@rubytech/create-maxy 1.0.648 → 1.0.650
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +65 -2
- package/package.json +1 -1
- package/payload/platform/plugins/docs/references/deployment.md +11 -0
- package/payload/platform/plugins/docs/references/getting-started.md +2 -0
- package/payload/platform/plugins/docs/references/memory-guide.md +2 -2
- package/payload/platform/plugins/docs/references/platform.md +6 -0
- package/payload/platform/plugins/docs/references/troubleshooting.md +16 -0
- package/payload/platform/plugins/memory/references/graph-primitives.md +5 -5
- package/payload/platform/templates/dotfiles/.tmux.conf +1 -0
- package/payload/platform/templates/systemd/maxy-ttyd.service +20 -0
- package/payload/server/public/assets/admin-S2KHPNe4.js +362 -0
- package/payload/server/public/assets/admin-kHJ-D0s7.css +1 -0
- package/payload/server/public/assets/{arc-DcrodP5U.js → arc-CRqJUbyK.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-CXIHKKCa.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Cvo_8X6C.js → architectureDiagram-Q4EWVU46-DtICG195.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CEK06TEn.js → blockDiagram-DXYQGD6D-nw1V7I38.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-QjYUSwTU.js → c4Diagram-AHTNJAMY-C1eEC43O.js} +1 -1
- package/payload/server/public/assets/channel-CA7njeKl.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-BBifNfFc.js → chunk-2KRD3SAO-Bybqj-wj.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-B-N_zWuf.js → chunk-336JU56O-Dszn2qEY.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-tWQOa3-I.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-CtDQKj9B.js → chunk-4BX2VUAB-DrKtrnWH.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-Dnu9n3p1.js → chunk-4TB4RGXK-CBFzVYqS.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-DSLoJJSj.js → chunk-55IACEB6-BNsOFSNf.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-rx-IvMNE.js → chunk-5FUZZQ4R-CXZykYh_.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-B9w9AKCS.js → chunk-5PVQY5BW-CLNppenz.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-C1yEjtiu.js → chunk-67CJDMHE-DFyE0-n0.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-C2f3zeVH.js → chunk-7N4EOEYR-BIKZD1_4.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-B_U-NsDK.js → chunk-AA7GKIK3-D4_g24le.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-DM6_wafW.js → chunk-BSJP7CBP-Cd9H-V61.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-DG7WkfNj.js → chunk-CIAEETIT-CAU9PIQi.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-aS3_rdwQ.js → chunk-EDXVE4YY-CR1JfOwe.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-r1I0uoCf.js → chunk-ENJZ2VHE-CuXW3Isg.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-CTm3YRE5.js → chunk-FMBD7UC4-BwGAtkIr.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-CAIttx3K.js → chunk-FOC6F5B3-Cn0552qP.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DQFV4c1l.js → chunk-ICPOFSXX-DEZT2XyQ.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-B2WCPQBa.js → chunk-K5T4RW27-KwBFTzJ9.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-C6w2sUOF.js → chunk-KGLVRYIC-1-3y582Z.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-BT1hcDTK.js → chunk-LIHQZDEY-DXIBsDHL.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-Brl32BSe.js → chunk-ORNJ4GCN-CRbOike7.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-BCO6n1CX.js → chunk-OYMX7WX6-CVT9itnY.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-BwkFBCAY.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BvgC0fpb.js → chunk-U2HBQHQK-BLgNHWFf.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-3yLdcqYf.js → chunk-X2U36JSP-DHYLiYqc.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-D876RWxK.js → chunk-XPW4576I-DBdiQ3Zy.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-CiuA4hOC.js → chunk-YZCP3GAM-DXaosB5Z.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-COjEBPzv.js → chunk-ZZ45TVLE-B5dCmOpH.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CUZ9BU_6.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BGYsCDux.js +1 -0
- package/payload/server/public/assets/clone-BjouONkW.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-Dap0yL0o.js → cose-bilkent-S5V4N54A-DaHtPQvk.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-DEia9UJj.js → dagre-KV5264BT-CAL9V_HR.js} +1 -1
- package/payload/server/public/assets/{dagre-DBbjK-Cf.js → dagre-N8C5Xujx.js} +1 -1
- package/payload/server/public/assets/data-BajF3t1i.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-CWSP9MzJ.js → diagram-5BDNPKRD-pzBSPqlM.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-DWRsfitL.js → diagram-G4DWMVQ6-DStdLqos.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-n-jyzS4D.js → diagram-MMDJMWI5-D-SfeX-6.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CLPTbfLq.js → diagram-TYMM5635-Cdr1DQ84.js} +1 -1
- package/payload/server/public/assets/{dist-Tkw8EOuG.js → dist-BKbAaes5.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-CmPC9Cnc.js → erDiagram-SMLLAGMA-CIg1dDZT.js} +1 -1
- package/payload/server/public/assets/file-CRrDfnO1.js +1 -0
- package/payload/server/public/assets/{flatten-Db2kUB5j.js → flatten-CpKIi5d2.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-DKMNmUbX.js → flowDiagram-DWJPFMVM-CJtU1T6d.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-C5-y3w-l.js → ganttDiagram-T4ZO3ILL-R4fuRAT1.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-tvzbaNdg.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-D9hG5kTg.js → gitGraphDiagram-UUTBAWPF-sifugSGn.js} +1 -1
- package/payload/server/public/assets/graph-CSBMZGGe.js +49 -0
- package/payload/server/public/assets/{graphlib-StP6GUhM.js → graphlib-DrlxPM8j.js} +1 -1
- package/payload/server/public/assets/house-D1CBraxB.js +1 -0
- package/payload/server/public/assets/info-OMHHGYJF-ByeBaFw5.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-CjgCxerY.js +2 -0
- package/payload/server/public/assets/{isEmpty-CXH_nKTs.js → isEmpty-C3Vxk1It.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A--K4KOS61.js → ishikawaDiagram-UXIWVN3A-CYUJOA2c.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DE-28YrW.js → journeyDiagram-VCZTEJTY-UltrLajs.js} +1 -1
- package/payload/server/public/assets/{jsx-runtime-BQmd8XDE.css → jsx-runtime-CZtLX8NN.css} +1 -1
- package/payload/server/public/assets/{jsx-runtime-DwoXvzmf.js → jsx-runtime-Cb-WunFZ.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CxSHjau2.js → kanban-definition-6JOO6SKY-BBaThtP3.js} +1 -1
- package/payload/server/public/assets/{line-DbcqYIG0.js → line-BhOwLD_o.js} +1 -1
- package/payload/server/public/assets/{linear-DXHoZSN3.js → linear-D76hoLvZ.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-CNGUA13J.js → mermaid-parser.core-D8n5xV7A.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-IQgx_upQ.js → mermaid.core-C3TZA9fX.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-BT9Up6-C.js → mindmap-definition-QFDTVHPH-Bv1kghvk.js} +1 -1
- package/payload/server/public/assets/{ordinal-CN3oz6oW.js → ordinal-BLrOss5K.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-Csybj5RO.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-Iw1du1Bn.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-BuewQTi6.js → pieDiagram-DEJITSTG-BSYldcKa.js} +1 -1
- package/payload/server/public/assets/public-ukz9-gSe.js +5 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-Cu8y2zQL.js → quadrantDiagram-34T5L4WZ-lCDshgz1.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-rEet4TBV.js +1 -0
- package/payload/server/public/assets/{reduce-SDh8_UdG.js → reduce-C5tBOlxC.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-DiT9bo27.js → requirementDiagram-MS252O5E-C7j42RrO.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-EkRnGTxM.js → sankeyDiagram-XADWPNL6-plPbHhuF.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BUiQA3SI.js → sequenceDiagram-FGHM5R23-D3Y8MXiX.js} +1 -1
- package/payload/server/public/assets/share-2-DkF8neDM.js +1 -0
- package/payload/server/public/assets/{src-DQQCRlaQ.js → src-BvrHnOMG.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-Fij35Tic.js → stateDiagram-FHFEXIEX-D4BdhMPy.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DhKxVkX3.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BMsyr7wU.js → timeline-definition-GMOUNBTQ-Lh9jrYCl.js} +1 -1
- package/payload/server/public/assets/trash-2-Dde58AAR.js +1 -0
- package/payload/server/public/assets/treeView-SZITEDCU-PaLYyjtc.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-CEhGYFbO.js +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-C0Fvv_Bt.js → useVoiceRecorder-Hz9X6luB.js} +3 -3
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-6fegYFB3.js → vennDiagram-DHZGUBPP-Cx0v19iv.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-xtJ4_o4d.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BtJ_B35h.js → wardleyDiagram-NUSXRM2D-BYewCTre.js} +1 -1
- package/payload/server/public/assets/x-D52qhSya.js +1 -0
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DJ20B4NY.js → xychartDiagram-5P7HB3ND-CkfIdbJu.js} +1 -1
- package/payload/server/public/data.html +8 -5
- package/payload/server/public/graph.html +19 -0
- package/payload/server/public/index.html +11 -7
- package/payload/server/public/public.html +7 -6
- package/payload/server/server.js +964 -737
- package/payload/server/public/assets/admin-uiqCo17I.js +0 -352
- package/payload/server/public/assets/architecture-YZFGNWBL-C38eyeNF.js +0 -1
- package/payload/server/public/assets/channel-D0dIwjlN.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-C8oXXITm.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-BE_lylks.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DH37CWIF.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DNJ7bv8r.js +0 -1
- package/payload/server/public/assets/clone-rrGuX3ZR.js +0 -1
- package/payload/server/public/assets/data-D8kol1ed.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CCjgA3FG.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-B65K6dQJ.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-DUJfTICr.js +0 -2
- package/payload/server/public/assets/packet-4T2RLAQJ-fp5ishAK.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-Bc3VMuuU.js +0 -1
- package/payload/server/public/assets/public-CWuf8cLU.js +0 -5
- package/payload/server/public/assets/radar-PYXPWWZC-D9jy5QAa.js +0 -1
- package/payload/server/public/assets/share-2-wGga_ldi.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DQzhSd8K.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CHyRL9e4.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-DpQ_FOO6.js +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-CWBIAatW.js +0 -1
- /package/payload/server/public/assets/{_baseFor-D71p92tl.js → _baseFor-WfS9pKAn.js} +0 -0
- /package/payload/server/public/assets/{array-Bs_owIvv.js → array-HeX70jSN.js} +0 -0
- /package/payload/server/public/assets/{chunk-lgnzUk6H.js → chunk-DD-I1_y5.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-DLG5qhup.js → cytoscape.esm-CDZo0kst.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-Du_2bjyv.js → defaultLocale-GJwWH1Jr.js} +0 -0
- /package/payload/server/public/assets/{init-BYLBkHX_.js → init-BPLPMQ3Y.js} +0 -0
- /package/payload/server/public/assets/{katex-lkho_UhZ.js → katex-CKZ-HWMQ.js} +0 -0
- /package/payload/server/public/assets/{path-BO54iFkf.js → path-YdFzr2W6.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-DWTEM3RW.js → preload-helper-BEFjQwLd.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-BCiZEpQC.js → rough.esm-HAx67Hnb.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -1201,14 +1201,14 @@ var Hono = class _Hono {
|
|
|
1201
1201
|
* app.route("/api", app2) // GET /api/user
|
|
1202
1202
|
* ```
|
|
1203
1203
|
*/
|
|
1204
|
-
route(path2,
|
|
1204
|
+
route(path2, app30) {
|
|
1205
1205
|
const subApp = this.basePath(path2);
|
|
1206
|
-
|
|
1206
|
+
app30.routes.map((r) => {
|
|
1207
1207
|
let handler;
|
|
1208
|
-
if (
|
|
1208
|
+
if (app30.errorHandler === errorHandler) {
|
|
1209
1209
|
handler = r.handler;
|
|
1210
1210
|
} else {
|
|
1211
|
-
handler = async (c, next) => (await compose([],
|
|
1211
|
+
handler = async (c, next) => (await compose([], app30.errorHandler)(c, () => r.handler(c, next))).res;
|
|
1212
1212
|
handler[COMPOSED_HANDLER] = r.handler;
|
|
1213
1213
|
}
|
|
1214
1214
|
subApp.#addRoute(r.method, r.path, handler);
|
|
@@ -2526,7 +2526,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
2526
2526
|
});
|
|
2527
2527
|
if (!chunk) {
|
|
2528
2528
|
if (i === 1) {
|
|
2529
|
-
await new Promise((
|
|
2529
|
+
await new Promise((resolve30) => setTimeout(resolve30));
|
|
2530
2530
|
maxReadCount = 3;
|
|
2531
2531
|
continue;
|
|
2532
2532
|
}
|
|
@@ -2892,8 +2892,8 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
2892
2892
|
};
|
|
2893
2893
|
|
|
2894
2894
|
// server/index.ts
|
|
2895
|
-
import { readFileSync as
|
|
2896
|
-
import { resolve as
|
|
2895
|
+
import { readFileSync as readFileSync25, existsSync as existsSync24, watchFile } from "fs";
|
|
2896
|
+
import { resolve as resolve29, join as join13, basename as basename7 } from "path";
|
|
2897
2897
|
import { homedir as homedir5 } from "os";
|
|
2898
2898
|
|
|
2899
2899
|
// app/lib/vnc-logger.ts
|
|
@@ -2982,10 +2982,10 @@ var SCRYPT_R = 8;
|
|
|
2982
2982
|
var SCRYPT_P = 1;
|
|
2983
2983
|
var SCRYPT_KEYLEN = 64;
|
|
2984
2984
|
function scryptAsync(password, salt) {
|
|
2985
|
-
return new Promise((
|
|
2985
|
+
return new Promise((resolve30, reject) => {
|
|
2986
2986
|
scrypt(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }, (err, key) => {
|
|
2987
2987
|
if (err) reject(err);
|
|
2988
|
-
else
|
|
2988
|
+
else resolve30(key);
|
|
2989
2989
|
});
|
|
2990
2990
|
});
|
|
2991
2991
|
}
|
|
@@ -3826,9 +3826,33 @@ Content-Length: 0\r
|
|
|
3826
3826
|
socket.destroy();
|
|
3827
3827
|
}
|
|
3828
3828
|
|
|
3829
|
-
// server/
|
|
3829
|
+
// server/ws-proxy-terminal.ts
|
|
3830
3830
|
import { createConnection as createConnection2 } from "net";
|
|
3831
|
-
|
|
3831
|
+
|
|
3832
|
+
// app/lib/terminal-logger.ts
|
|
3833
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
3834
|
+
import { resolve as resolve3 } from "path";
|
|
3835
|
+
var TERMINAL_LOG_FILE = resolve3(LOG_DIR, "terminal.log");
|
|
3836
|
+
try {
|
|
3837
|
+
mkdirSync2(LOG_DIR, { recursive: true });
|
|
3838
|
+
} catch (err) {
|
|
3839
|
+
console.error(`[terminal-log-fail] mkdir ${LOG_DIR} failed: ${err.message}`);
|
|
3840
|
+
}
|
|
3841
|
+
function terminalLog(phase, fields = {}) {
|
|
3842
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
3843
|
+
const kv = Object.entries(fields).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
|
|
3844
|
+
const line = kv.length > 0 ? `[${ts}] [terminal-${phase}] ${kv}
|
|
3845
|
+
` : `[${ts}] [terminal-${phase}]
|
|
3846
|
+
`;
|
|
3847
|
+
try {
|
|
3848
|
+
appendFileSync2(TERMINAL_LOG_FILE, line);
|
|
3849
|
+
} catch (err) {
|
|
3850
|
+
console.error(`[terminal-log-fail] ${err.message} \u2014 dropped: ${line.slice(0, 300).trim()}`);
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
|
|
3854
|
+
// server/ws-proxy-terminal.ts
|
|
3855
|
+
var WS_PATH2 = "/admin/terminal/ws";
|
|
3832
3856
|
var UPSTREAM_TIMEOUT_MS2 = 5e3;
|
|
3833
3857
|
var HOP_BY_HOP2 = /* @__PURE__ */ new Set([
|
|
3834
3858
|
"connection",
|
|
@@ -3840,194 +3864,225 @@ var HOP_BY_HOP2 = /* @__PURE__ */ new Set([
|
|
|
3840
3864
|
"transfer-encoding",
|
|
3841
3865
|
"upgrade"
|
|
3842
3866
|
]);
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
const uri = process.env.NEO4J_URI;
|
|
3847
|
-
if (!uri) {
|
|
3848
|
-
throw new Error(
|
|
3849
|
-
"[ui/graph-proxy] NEO4J_URI unset \u2014 refusing to default to bolt://localhost:7687 (Task 580)"
|
|
3850
|
-
);
|
|
3851
|
-
}
|
|
3852
|
-
const m = uri.match(/:(\d+)$/);
|
|
3853
|
-
const boltPort = m ? parseInt(m[1], 10) : 7687;
|
|
3854
|
-
cachedUpstreamPort = boltPort - 213;
|
|
3855
|
-
console.error(`[ui/graph-proxy] resolved neo4j_uri=${uri} http_port=${cachedUpstreamPort}`);
|
|
3856
|
-
return cachedUpstreamPort;
|
|
3857
|
-
}
|
|
3858
|
-
var UPSTREAM_HOST = "127.0.0.1";
|
|
3859
|
-
function attachGraphHttpRoutes(app29) {
|
|
3860
|
-
const handler = async (c) => {
|
|
3861
|
-
const raw2 = c.req.raw;
|
|
3862
|
-
const url = new URL(raw2.url);
|
|
3863
|
-
const upstreamPort = resolveUpstreamPort();
|
|
3864
|
-
const pathAfterPrefix = url.pathname.slice(GRAPH_PREFIX.length) || "/";
|
|
3865
|
-
const upstreamUrl = `http://${UPSTREAM_HOST}:${upstreamPort}${pathAfterPrefix}${url.search}`;
|
|
3866
|
-
const upstreamHeaders = new Headers(raw2.headers);
|
|
3867
|
-
for (const h of HOP_BY_HOP2) upstreamHeaders.delete(h);
|
|
3868
|
-
upstreamHeaders.set("host", `${UPSTREAM_HOST}:${upstreamPort}`);
|
|
3869
|
-
try {
|
|
3870
|
-
const upstream = await fetch(upstreamUrl, {
|
|
3871
|
-
method: raw2.method,
|
|
3872
|
-
headers: upstreamHeaders,
|
|
3873
|
-
body: raw2.body,
|
|
3874
|
-
// `duplex: 'half'` is required when forwarding a streaming body; TS
|
|
3875
|
-
// typings do not yet expose it but the undici runtime supports it.
|
|
3876
|
-
...raw2.body ? { duplex: "half" } : {},
|
|
3877
|
-
redirect: "manual"
|
|
3878
|
-
});
|
|
3879
|
-
const resHeaders = new Headers(upstream.headers);
|
|
3880
|
-
for (const h of HOP_BY_HOP2) resHeaders.delete(h);
|
|
3881
|
-
const loc = resHeaders.get("location");
|
|
3882
|
-
if (loc) {
|
|
3883
|
-
const rewritten = rewriteLocation(loc);
|
|
3884
|
-
if (rewritten !== loc) resHeaders.set("location", rewritten);
|
|
3885
|
-
}
|
|
3886
|
-
return new Response(upstream.body, {
|
|
3887
|
-
status: upstream.status,
|
|
3888
|
-
statusText: upstream.statusText,
|
|
3889
|
-
headers: resHeaders
|
|
3890
|
-
});
|
|
3891
|
-
} catch (err) {
|
|
3892
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3893
|
-
console.error(`[graph-proxy] upstream fetch failed for ${upstreamUrl}: ${msg}`);
|
|
3894
|
-
return c.text(`Graph proxy upstream unreachable: ${msg}`, 502);
|
|
3895
|
-
}
|
|
3896
|
-
};
|
|
3897
|
-
app29.all(GRAPH_PREFIX, handler);
|
|
3898
|
-
app29.all(`${GRAPH_PREFIX}/*`, handler);
|
|
3899
|
-
}
|
|
3900
|
-
function attachGraphWsProxy(server, opts) {
|
|
3867
|
+
function attachTerminalWsProxy(server, opts) {
|
|
3868
|
+
const upstreamHost = opts.upstreamHost ?? "127.0.0.1";
|
|
3869
|
+
const upstreamPort = opts.upstreamPort ?? 7681;
|
|
3901
3870
|
server.on("upgrade", (req, clientSocket, head) => {
|
|
3902
3871
|
try {
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
if (!isGraphPath) {
|
|
3908
|
-
if (pathname !== "/websockify") {
|
|
3909
|
-
clientSocket.destroy();
|
|
3910
|
-
}
|
|
3911
|
-
return;
|
|
3912
|
-
}
|
|
3913
|
-
const hostHeader = (req.headers.host ?? "").split(":")[0];
|
|
3914
|
-
const remote = req.socket.remoteAddress;
|
|
3915
|
-
const xff = headerString2(req.headers["x-forwarded-for"]);
|
|
3916
|
-
const cookie = headerString2(req.headers.cookie);
|
|
3917
|
-
const decision = opts.canAccessAdmin({
|
|
3918
|
-
host: hostHeader,
|
|
3919
|
-
remoteAddress: remote,
|
|
3920
|
-
xForwardedFor: xff,
|
|
3921
|
-
cookieHeader: cookie,
|
|
3922
|
-
isPublicHost: opts.isPublicHost
|
|
3923
|
-
});
|
|
3924
|
-
if (!decision.allow) {
|
|
3925
|
-
const status = decision.reason === "public-host" ? 404 : 401;
|
|
3926
|
-
writeStatusAndDestroy2(clientSocket, status, decision.reason === "public-host" ? "Not Found" : "Unauthorized");
|
|
3927
|
-
return;
|
|
3928
|
-
}
|
|
3929
|
-
const originHeader = headerString2(req.headers.origin);
|
|
3930
|
-
const originHost = parseOriginHost2(originHeader);
|
|
3931
|
-
if (!originHost || originHost !== hostHeader || opts.isPublicHost(originHost)) {
|
|
3932
|
-
writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
|
|
3933
|
-
return;
|
|
3934
|
-
}
|
|
3935
|
-
const rewrittenPath = pathname === GRAPH_PREFIX ? "/" : pathname.slice(GRAPH_PREFIX.length);
|
|
3936
|
-
const rewrittenUrl = qsIndex === -1 ? rewrittenPath : rewrittenPath + url.slice(qsIndex);
|
|
3937
|
-
const upstreamPort = resolveUpstreamPort();
|
|
3938
|
-
const upstream = createConnection2({ host: UPSTREAM_HOST, port: upstreamPort });
|
|
3939
|
-
upstream.setTimeout(UPSTREAM_TIMEOUT_MS2);
|
|
3940
|
-
upstream.once("connect", () => {
|
|
3941
|
-
upstream.setTimeout(0);
|
|
3942
|
-
const lines = [];
|
|
3943
|
-
lines.push(`${req.method ?? "GET"} ${rewrittenUrl} HTTP/${req.httpVersion}`);
|
|
3944
|
-
lines.push(`host: ${UPSTREAM_HOST}:${upstreamPort}`);
|
|
3945
|
-
for (const [name, value] of Object.entries(req.headers)) {
|
|
3946
|
-
if (name === "host") continue;
|
|
3947
|
-
if (HOP_BY_HOP2.has(name)) continue;
|
|
3948
|
-
if (value == null) continue;
|
|
3949
|
-
if (Array.isArray(value)) {
|
|
3950
|
-
for (const v of value) lines.push(`${name}: ${v}`);
|
|
3951
|
-
} else {
|
|
3952
|
-
lines.push(`${name}: ${value}`);
|
|
3953
|
-
}
|
|
3954
|
-
}
|
|
3955
|
-
const upgradeHeader = headerString2(req.headers.upgrade);
|
|
3956
|
-
const connectionHeader = headerString2(req.headers.connection);
|
|
3957
|
-
if (upgradeHeader) lines.push(`upgrade: ${upgradeHeader}`);
|
|
3958
|
-
if (connectionHeader) lines.push(`connection: ${connectionHeader}`);
|
|
3959
|
-
upstream.write(lines.join("\r\n") + "\r\n\r\n");
|
|
3960
|
-
if (head && head.length > 0) upstream.write(head);
|
|
3961
|
-
clientSocket.pipe(upstream);
|
|
3962
|
-
upstream.pipe(clientSocket);
|
|
3963
|
-
const teardown = () => {
|
|
3964
|
-
clientSocket.destroy();
|
|
3965
|
-
upstream.destroy();
|
|
3966
|
-
};
|
|
3967
|
-
clientSocket.once("close", teardown);
|
|
3968
|
-
upstream.once("close", teardown);
|
|
3969
|
-
clientSocket.once("error", teardown);
|
|
3970
|
-
upstream.once("error", teardown);
|
|
3971
|
-
});
|
|
3972
|
-
upstream.once("timeout", () => {
|
|
3973
|
-
writeStatusAndDestroy2(clientSocket, 504, "Gateway Timeout");
|
|
3974
|
-
upstream.destroy();
|
|
3975
|
-
});
|
|
3976
|
-
upstream.once("error", (err) => {
|
|
3977
|
-
console.error(`[graph-proxy] ws upstream error: ${err.message}`);
|
|
3978
|
-
writeStatusAndDestroy2(clientSocket, 502, "Bad Gateway");
|
|
3979
|
-
upstream.destroy();
|
|
3872
|
+
handleUpgrade2(req, clientSocket, head, {
|
|
3873
|
+
isPublicHost: opts.isPublicHost,
|
|
3874
|
+
upstreamHost,
|
|
3875
|
+
upstreamPort
|
|
3980
3876
|
});
|
|
3981
3877
|
} catch (err) {
|
|
3982
|
-
|
|
3878
|
+
terminalLog("ws-upgrade", {
|
|
3879
|
+
decision: "rejected",
|
|
3880
|
+
reason: "handler-exception",
|
|
3881
|
+
err: err.message
|
|
3882
|
+
});
|
|
3983
3883
|
clientSocket.destroy();
|
|
3984
3884
|
}
|
|
3985
3885
|
});
|
|
3986
3886
|
}
|
|
3987
|
-
function
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3887
|
+
function handleUpgrade2(req, clientSocket, head, opts) {
|
|
3888
|
+
const url = req.url ?? "";
|
|
3889
|
+
const qsIndex = url.indexOf("?");
|
|
3890
|
+
const pathname = qsIndex === -1 ? url : url.slice(0, qsIndex);
|
|
3891
|
+
if (pathname !== WS_PATH2) {
|
|
3892
|
+
return;
|
|
3893
|
+
}
|
|
3894
|
+
const corrId = newCorrId();
|
|
3895
|
+
const query = qsIndex === -1 ? "" : url.slice(qsIndex + 1);
|
|
3896
|
+
const rawClientCorrId = parseQueryParam2(query, "corrId");
|
|
3897
|
+
const clientCorrId = sanitizeClientCorrId(rawClientCorrId);
|
|
3898
|
+
const hostHeader = (req.headers.host ?? "").split(":")[0];
|
|
3899
|
+
const originHeader = headerString2(req.headers.origin);
|
|
3900
|
+
const remote = req.socket.remoteAddress;
|
|
3901
|
+
const xff = headerString2(req.headers["x-forwarded-for"]);
|
|
3902
|
+
const decision = canAccessAdmin({
|
|
3903
|
+
host: hostHeader,
|
|
3904
|
+
remoteAddress: remote,
|
|
3905
|
+
xForwardedFor: xff,
|
|
3906
|
+
cookieHeader: headerString2(req.headers.cookie),
|
|
3907
|
+
isPublicHost: opts.isPublicHost
|
|
3908
|
+
});
|
|
3909
|
+
if (!decision.allow) {
|
|
3910
|
+
const status = decision.reason === "public-host" ? 404 : 401;
|
|
3911
|
+
terminalLog("ws-upgrade", {
|
|
3912
|
+
corrId,
|
|
3913
|
+
clientCorrId: clientCorrId ?? null,
|
|
3914
|
+
decision: "rejected",
|
|
3915
|
+
reason: decision.reason,
|
|
3916
|
+
ip: remote,
|
|
3917
|
+
xff: xff ?? null,
|
|
3918
|
+
origin: originHeader ?? null,
|
|
3919
|
+
host: hostHeader
|
|
3920
|
+
});
|
|
3921
|
+
writeStatusAndDestroy2(clientSocket, status, decision.reason === "public-host" ? "Not Found" : "Unauthorized");
|
|
3922
|
+
return;
|
|
3923
|
+
}
|
|
3924
|
+
const originHost = parseOriginHost2(originHeader);
|
|
3925
|
+
if (!originHost) {
|
|
3926
|
+
terminalLog("ws-upgrade", {
|
|
3927
|
+
corrId,
|
|
3928
|
+
clientCorrId: clientCorrId ?? null,
|
|
3929
|
+
decision: "rejected",
|
|
3930
|
+
reason: "origin-missing-or-invalid",
|
|
3931
|
+
origin: originHeader ?? null,
|
|
3997
3932
|
host: hostHeader,
|
|
3998
|
-
|
|
3999
|
-
xForwardedFor: xff,
|
|
4000
|
-
cookieHeader: cookie,
|
|
4001
|
-
isPublicHost: opts.isPublicHost
|
|
3933
|
+
ip: remote
|
|
4002
3934
|
});
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
3935
|
+
writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
|
|
3936
|
+
return;
|
|
3937
|
+
}
|
|
3938
|
+
if (originHost !== hostHeader) {
|
|
3939
|
+
terminalLog("ws-upgrade", {
|
|
3940
|
+
corrId,
|
|
3941
|
+
clientCorrId: clientCorrId ?? null,
|
|
3942
|
+
decision: "rejected",
|
|
3943
|
+
reason: "origin-mismatch",
|
|
3944
|
+
origin_host: originHost,
|
|
3945
|
+
host: hostHeader,
|
|
3946
|
+
ip: remote
|
|
3947
|
+
});
|
|
3948
|
+
writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
|
|
3949
|
+
return;
|
|
3950
|
+
}
|
|
3951
|
+
if (opts.isPublicHost(originHost)) {
|
|
3952
|
+
terminalLog("ws-upgrade", {
|
|
3953
|
+
corrId,
|
|
3954
|
+
clientCorrId: clientCorrId ?? null,
|
|
3955
|
+
decision: "rejected",
|
|
3956
|
+
reason: "origin-public-host",
|
|
3957
|
+
origin_host: originHost,
|
|
3958
|
+
host: hostHeader,
|
|
3959
|
+
ip: remote
|
|
3960
|
+
});
|
|
3961
|
+
writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
|
|
3962
|
+
return;
|
|
3963
|
+
}
|
|
3964
|
+
terminalLog("ws-upgrade", {
|
|
3965
|
+
corrId,
|
|
3966
|
+
clientCorrId: clientCorrId ?? null,
|
|
3967
|
+
decision: "accepted",
|
|
3968
|
+
ip: remote,
|
|
3969
|
+
xff: xff ?? null,
|
|
3970
|
+
origin: originHeader ?? null,
|
|
3971
|
+
host: hostHeader,
|
|
3972
|
+
sec_ws_version: headerString2(req.headers["sec-websocket-version"]) ?? null,
|
|
3973
|
+
sec_ws_protocol: headerString2(req.headers["sec-websocket-protocol"]) ?? null
|
|
3974
|
+
});
|
|
3975
|
+
const upstream = createConnection2({ host: opts.upstreamHost, port: opts.upstreamPort });
|
|
3976
|
+
upstream.setTimeout(UPSTREAM_TIMEOUT_MS2);
|
|
3977
|
+
let bytesClientToUpstream = 0;
|
|
3978
|
+
let bytesUpstreamToClient = 0;
|
|
3979
|
+
let closedBy = null;
|
|
3980
|
+
let proxyOpened = false;
|
|
3981
|
+
const finish = (side, reason) => {
|
|
3982
|
+
if (closedBy) return;
|
|
3983
|
+
closedBy = side;
|
|
3984
|
+
if (proxyOpened) {
|
|
3985
|
+
terminalLog("proxy-close", {
|
|
3986
|
+
corrId,
|
|
3987
|
+
closedBy: side,
|
|
3988
|
+
reason,
|
|
3989
|
+
clientBytes: bytesClientToUpstream,
|
|
3990
|
+
upstreamBytes: bytesUpstreamToClient
|
|
3991
|
+
});
|
|
4008
3992
|
}
|
|
4009
|
-
|
|
3993
|
+
clientSocket.destroy();
|
|
3994
|
+
upstream.destroy();
|
|
4010
3995
|
};
|
|
3996
|
+
upstream.once("connect", () => {
|
|
3997
|
+
upstream.setTimeout(0);
|
|
3998
|
+
proxyOpened = true;
|
|
3999
|
+
terminalLog("proxy-open", {
|
|
4000
|
+
corrId,
|
|
4001
|
+
upstream: `${opts.upstreamHost}:${opts.upstreamPort}`
|
|
4002
|
+
});
|
|
4003
|
+
const lines = [];
|
|
4004
|
+
lines.push(`${req.method ?? "GET"} ${WS_PATH2} HTTP/${req.httpVersion}`);
|
|
4005
|
+
lines.push(`host: ${opts.upstreamHost}:${opts.upstreamPort}`);
|
|
4006
|
+
for (const [name, value] of Object.entries(req.headers)) {
|
|
4007
|
+
if (name === "host") continue;
|
|
4008
|
+
if (HOP_BY_HOP2.has(name)) continue;
|
|
4009
|
+
if (value == null) continue;
|
|
4010
|
+
if (Array.isArray(value)) {
|
|
4011
|
+
for (const v of value) lines.push(`${name}: ${v}`);
|
|
4012
|
+
} else {
|
|
4013
|
+
lines.push(`${name}: ${value}`);
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
const upgradeHeader = headerString2(req.headers.upgrade);
|
|
4017
|
+
const connectionHeader = headerString2(req.headers.connection);
|
|
4018
|
+
if (upgradeHeader) lines.push(`upgrade: ${upgradeHeader}`);
|
|
4019
|
+
if (connectionHeader) lines.push(`connection: ${connectionHeader}`);
|
|
4020
|
+
upstream.write(lines.join("\r\n") + "\r\n\r\n");
|
|
4021
|
+
if (head && head.length > 0) upstream.write(head);
|
|
4022
|
+
clientSocket.on("data", (chunk) => {
|
|
4023
|
+
bytesClientToUpstream += chunk.length;
|
|
4024
|
+
});
|
|
4025
|
+
upstream.on("data", (chunk) => {
|
|
4026
|
+
bytesUpstreamToClient += chunk.length;
|
|
4027
|
+
});
|
|
4028
|
+
clientSocket.pipe(upstream);
|
|
4029
|
+
upstream.pipe(clientSocket);
|
|
4030
|
+
clientSocket.once("close", (hadError) => {
|
|
4031
|
+
finish("client", hadError ? "error" : "normal");
|
|
4032
|
+
});
|
|
4033
|
+
upstream.once("close", (hadError) => {
|
|
4034
|
+
finish("upstream", hadError ? "error" : "normal");
|
|
4035
|
+
});
|
|
4036
|
+
clientSocket.once("error", (err) => {
|
|
4037
|
+
terminalLog("proxy-error", { corrId, side: "client", err: err.message });
|
|
4038
|
+
finish("client", "error");
|
|
4039
|
+
});
|
|
4040
|
+
upstream.once("error", (err) => {
|
|
4041
|
+
terminalLog("proxy-error", { corrId, side: "upstream", err: err.message });
|
|
4042
|
+
finish("upstream", "error");
|
|
4043
|
+
});
|
|
4044
|
+
});
|
|
4045
|
+
upstream.once("timeout", () => {
|
|
4046
|
+
if (proxyOpened) return;
|
|
4047
|
+
terminalLog("proxy-error", {
|
|
4048
|
+
corrId,
|
|
4049
|
+
side: "upstream-connect",
|
|
4050
|
+
err: "timeout",
|
|
4051
|
+
timeout_ms: UPSTREAM_TIMEOUT_MS2
|
|
4052
|
+
});
|
|
4053
|
+
writeStatusAndDestroy2(clientSocket, 504, "Gateway Timeout");
|
|
4054
|
+
upstream.destroy();
|
|
4055
|
+
});
|
|
4056
|
+
upstream.once("error", (err) => {
|
|
4057
|
+
if (proxyOpened) return;
|
|
4058
|
+
terminalLog("proxy-error", {
|
|
4059
|
+
corrId,
|
|
4060
|
+
side: "upstream-connect",
|
|
4061
|
+
err: err.message
|
|
4062
|
+
});
|
|
4063
|
+
writeStatusAndDestroy2(clientSocket, 502, "Bad Gateway");
|
|
4064
|
+
upstream.destroy();
|
|
4065
|
+
});
|
|
4066
|
+
}
|
|
4067
|
+
function parseQueryParam2(query, key) {
|
|
4068
|
+
if (!query) return null;
|
|
4069
|
+
for (const pair of query.split("&")) {
|
|
4070
|
+
const eq = pair.indexOf("=");
|
|
4071
|
+
const k = eq === -1 ? pair : pair.slice(0, eq);
|
|
4072
|
+
if (k !== key) continue;
|
|
4073
|
+
const v = eq === -1 ? "" : pair.slice(eq + 1);
|
|
4074
|
+
try {
|
|
4075
|
+
return decodeURIComponent(v);
|
|
4076
|
+
} catch {
|
|
4077
|
+
return null;
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
return null;
|
|
4011
4081
|
}
|
|
4012
4082
|
function headerString2(value) {
|
|
4013
4083
|
if (value == null) return void 0;
|
|
4014
4084
|
return Array.isArray(value) ? value[0] : value;
|
|
4015
4085
|
}
|
|
4016
|
-
function rewriteLocation(location) {
|
|
4017
|
-
try {
|
|
4018
|
-
const parsed = new URL(location);
|
|
4019
|
-
if (parsed.hostname === UPSTREAM_HOST && parsed.port === String(resolveUpstreamPort())) {
|
|
4020
|
-
const pathWithQuery = parsed.pathname + parsed.search + parsed.hash;
|
|
4021
|
-
return pathWithQuery.startsWith(GRAPH_PREFIX) ? pathWithQuery : `${GRAPH_PREFIX}${pathWithQuery}`;
|
|
4022
|
-
}
|
|
4023
|
-
return location;
|
|
4024
|
-
} catch {
|
|
4025
|
-
if (location.startsWith("/") && !location.startsWith(`${GRAPH_PREFIX}/`) && location !== GRAPH_PREFIX) {
|
|
4026
|
-
return `${GRAPH_PREFIX}${location}`;
|
|
4027
|
-
}
|
|
4028
|
-
return location;
|
|
4029
|
-
}
|
|
4030
|
-
}
|
|
4031
4086
|
function parseOriginHost2(origin) {
|
|
4032
4087
|
if (!origin) return null;
|
|
4033
4088
|
try {
|
|
@@ -4057,9 +4112,9 @@ var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
|
|
|
4057
4112
|
import Anthropic2 from "@anthropic-ai/sdk";
|
|
4058
4113
|
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
4059
4114
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4060
|
-
import { resolve as
|
|
4115
|
+
import { resolve as resolve7, join as join4 } from "path";
|
|
4061
4116
|
import { platform as osPlatform } from "os";
|
|
4062
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync6, mkdirSync as
|
|
4117
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync6, mkdirSync as mkdirSync5, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync2, cpSync, rmSync as rmSync2, appendFileSync as appendFileSync3, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
|
|
4063
4118
|
import { lookup as dnsLookup } from "dns/promises";
|
|
4064
4119
|
import { createConnection as netConnect } from "net";
|
|
4065
4120
|
import { StringDecoder } from "string_decoder";
|
|
@@ -4068,12 +4123,12 @@ import { StringDecoder } from "string_decoder";
|
|
|
4068
4123
|
var import_dist = __toESM(require_dist());
|
|
4069
4124
|
import {
|
|
4070
4125
|
existsSync as existsSync4,
|
|
4071
|
-
mkdirSync as
|
|
4126
|
+
mkdirSync as mkdirSync3,
|
|
4072
4127
|
readFileSync as readFileSync3,
|
|
4073
4128
|
unlinkSync,
|
|
4074
4129
|
writeFileSync as writeFileSync2
|
|
4075
4130
|
} from "fs";
|
|
4076
|
-
import { resolve as
|
|
4131
|
+
import { resolve as resolve4, join as join3, dirname } from "path";
|
|
4077
4132
|
import { homedir as homedir2 } from "os";
|
|
4078
4133
|
var cachedKeyFilePath = null;
|
|
4079
4134
|
function resolveKeyFilePath() {
|
|
@@ -4104,7 +4159,7 @@ function resolveKeyFilePath() {
|
|
|
4104
4159
|
throw err;
|
|
4105
4160
|
}
|
|
4106
4161
|
}
|
|
4107
|
-
cachedKeyFilePath =
|
|
4162
|
+
cachedKeyFilePath = resolve4(homedir2(), configDirName2, ".anthropic-api-key");
|
|
4108
4163
|
return cachedKeyFilePath;
|
|
4109
4164
|
}
|
|
4110
4165
|
function readKey() {
|
|
@@ -4391,10 +4446,10 @@ async function ensureAuth() {
|
|
|
4391
4446
|
// app/lib/vnc.ts
|
|
4392
4447
|
import { spawnSync, execFileSync } from "child_process";
|
|
4393
4448
|
import { createConnection as createConnection3 } from "net";
|
|
4394
|
-
import { mkdirSync as
|
|
4395
|
-
import { resolve as
|
|
4396
|
-
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
4397
|
-
var VNC_SCRIPT =
|
|
4449
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
4450
|
+
import { resolve as resolve5 } from "path";
|
|
4451
|
+
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
|
|
4452
|
+
var VNC_SCRIPT = resolve5(PLATFORM_ROOT2, "scripts/vnc.sh");
|
|
4398
4453
|
var displayMode = process.env.DISPLAY_MODE ?? "virtual";
|
|
4399
4454
|
if (displayMode === "native") {
|
|
4400
4455
|
console.log(`[vnc] DISPLAY_MODE=native \u2014 local requests use desktop display, remote requests use VNC`);
|
|
@@ -4506,10 +4561,10 @@ async function waitForPort(port2, timeoutMs = 12e3) {
|
|
|
4506
4561
|
return false;
|
|
4507
4562
|
}
|
|
4508
4563
|
function ensureLogDir() {
|
|
4509
|
-
|
|
4564
|
+
mkdirSync4(LOG_DIR, { recursive: true });
|
|
4510
4565
|
}
|
|
4511
4566
|
function logPath(name) {
|
|
4512
|
-
return
|
|
4567
|
+
return resolve5(LOG_DIR, `${name}.log`);
|
|
4513
4568
|
}
|
|
4514
4569
|
async function ensureVnc() {
|
|
4515
4570
|
const up = await waitForPort(5900, 1e3);
|
|
@@ -4587,8 +4642,8 @@ function killChromium() {
|
|
|
4587
4642
|
spawnSync("pkill", ["-f", "chromium"], { stdio: "pipe" });
|
|
4588
4643
|
}
|
|
4589
4644
|
function writeChromiumWrapper() {
|
|
4590
|
-
|
|
4591
|
-
const wrapperPath =
|
|
4645
|
+
mkdirSync4(BIN_DIR, { recursive: true });
|
|
4646
|
+
const wrapperPath = resolve5(BIN_DIR, "chromium");
|
|
4592
4647
|
writeFileSync4(wrapperPath, `#!/bin/bash
|
|
4593
4648
|
LOG="${LOG_DIR}/chromium.log"
|
|
4594
4649
|
echo "==== [$(date)] chromium wrapper ====" >> "$LOG"
|
|
@@ -4674,12 +4729,12 @@ import neo4j from "neo4j-driver";
|
|
|
4674
4729
|
import { randomUUID } from "crypto";
|
|
4675
4730
|
import { spawn } from "child_process";
|
|
4676
4731
|
import { readFileSync as readFileSync6, readdirSync, existsSync as existsSync5, openSync, readSync, closeSync, statSync as statSync2, rmSync } from "fs";
|
|
4677
|
-
import { resolve as
|
|
4678
|
-
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ??
|
|
4732
|
+
import { resolve as resolve6 } from "path";
|
|
4733
|
+
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
|
|
4679
4734
|
var driver = null;
|
|
4680
4735
|
function readPassword() {
|
|
4681
4736
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
4682
|
-
const passwordFile =
|
|
4737
|
+
const passwordFile = resolve6(PLATFORM_ROOT3, "config/.neo4j-password");
|
|
4683
4738
|
try {
|
|
4684
4739
|
return readFileSync6(passwordFile, "utf-8").trim();
|
|
4685
4740
|
} catch {
|
|
@@ -5355,7 +5410,7 @@ ${userContent}`;
|
|
|
5355
5410
|
"dontAsk",
|
|
5356
5411
|
prompt
|
|
5357
5412
|
];
|
|
5358
|
-
return new Promise((
|
|
5413
|
+
return new Promise((resolve30) => {
|
|
5359
5414
|
let stdout = "";
|
|
5360
5415
|
let stderr = "";
|
|
5361
5416
|
const spawnFn = _spawnOverride ?? spawn;
|
|
@@ -5373,35 +5428,35 @@ ${userContent}`;
|
|
|
5373
5428
|
const timer = setTimeout(() => {
|
|
5374
5429
|
proc.kill("SIGTERM");
|
|
5375
5430
|
console.error("[persist] autoLabel: haiku subprocess timed out");
|
|
5376
|
-
|
|
5431
|
+
resolve30(null);
|
|
5377
5432
|
}, SESSION_LABEL_TIMEOUT_MS);
|
|
5378
5433
|
proc.on("error", (err) => {
|
|
5379
5434
|
clearTimeout(timer);
|
|
5380
5435
|
console.error(`[persist] autoLabel: subprocess error \u2014 ${err.message}`);
|
|
5381
|
-
|
|
5436
|
+
resolve30(null);
|
|
5382
5437
|
});
|
|
5383
5438
|
proc.on("close", (code) => {
|
|
5384
5439
|
clearTimeout(timer);
|
|
5385
5440
|
if (code !== 0) {
|
|
5386
5441
|
console.error(`[persist] autoLabel: subprocess exited code=${code}${stderr ? ` stderr=${stderr.trim().slice(0, 200)}` : ""}`);
|
|
5387
|
-
|
|
5442
|
+
resolve30(null);
|
|
5388
5443
|
return;
|
|
5389
5444
|
}
|
|
5390
5445
|
const text = stdout.trim();
|
|
5391
5446
|
if (!text) {
|
|
5392
5447
|
console.error("[persist] autoLabel: haiku returned empty response");
|
|
5393
|
-
|
|
5448
|
+
resolve30(null);
|
|
5394
5449
|
return;
|
|
5395
5450
|
}
|
|
5396
5451
|
if (text === "SKIP") {
|
|
5397
5452
|
console.error("[persist] autoLabel: haiku returned SKIP \u2014 messages too vague");
|
|
5398
|
-
|
|
5453
|
+
resolve30(null);
|
|
5399
5454
|
return;
|
|
5400
5455
|
}
|
|
5401
5456
|
const words = text.split(/\s+/).slice(0, SESSION_LABEL_MAX_WORDS);
|
|
5402
5457
|
const label = words.join(" ");
|
|
5403
5458
|
console.error(`[persist] autoLabel: haiku response="${label}"`);
|
|
5404
|
-
|
|
5459
|
+
resolve30(label);
|
|
5405
5460
|
});
|
|
5406
5461
|
});
|
|
5407
5462
|
}
|
|
@@ -5673,9 +5728,9 @@ var MAX_RECENT_TOOL_FAILURES = 3;
|
|
|
5673
5728
|
var RECENT_FAILURES_TAIL_BYTES = 10 * 1024;
|
|
5674
5729
|
function readRecentToolFailures(accountId, conversationId) {
|
|
5675
5730
|
try {
|
|
5676
|
-
const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ??
|
|
5677
|
-
const logDir =
|
|
5678
|
-
const logPath2 =
|
|
5731
|
+
const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
|
|
5732
|
+
const logDir = resolve6(platformRoot3, "..", "data/accounts", accountId, "logs");
|
|
5733
|
+
const logPath2 = resolve6(logDir, `claude-agent-stream-${conversationId}.log`);
|
|
5679
5734
|
if (!existsSync5(logPath2)) {
|
|
5680
5735
|
console.error(`[review-tail-skip] path=${logPath2} reason=file-missing \u2014 first turn of conversation, subprocess not yet spawned, or log rotated`);
|
|
5681
5736
|
return [];
|
|
@@ -5832,13 +5887,13 @@ ${taskLines.join("\n")}`);
|
|
|
5832
5887
|
let pendingCount = 0;
|
|
5833
5888
|
let pendingLines = [];
|
|
5834
5889
|
try {
|
|
5835
|
-
const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ??
|
|
5836
|
-
const pendingDir =
|
|
5890
|
+
const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
|
|
5891
|
+
const pendingDir = resolve6(platformRoot3, "..", "data/accounts", accountId, "pending-actions");
|
|
5837
5892
|
if (existsSync5(pendingDir)) {
|
|
5838
5893
|
const files = readdirSync(pendingDir).filter((f) => f.endsWith(".json") && !f.startsWith("."));
|
|
5839
5894
|
for (const file of files) {
|
|
5840
5895
|
try {
|
|
5841
|
-
const raw2 = readFileSync6(
|
|
5896
|
+
const raw2 = readFileSync6(resolve6(pendingDir, file), "utf-8");
|
|
5842
5897
|
const action = JSON.parse(raw2);
|
|
5843
5898
|
if (action.state === "pending") {
|
|
5844
5899
|
const inputSummary = JSON.stringify(action.hookPayload?.tool_input ?? {}).slice(0, 150);
|
|
@@ -5905,8 +5960,8 @@ ${sections.join("\n\n")}
|
|
|
5905
5960
|
}
|
|
5906
5961
|
}
|
|
5907
5962
|
async function consumeStep7FlagUI(session, accountId) {
|
|
5908
|
-
const accountDir =
|
|
5909
|
-
const flagPath =
|
|
5963
|
+
const accountDir = resolve6(PLATFORM_ROOT3, "..", "data/accounts", accountId);
|
|
5964
|
+
const flagPath = resolve6(accountDir, "onboarding", "step7-complete");
|
|
5910
5965
|
if (!existsSync5(flagPath)) return false;
|
|
5911
5966
|
let completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5912
5967
|
try {
|
|
@@ -6487,20 +6542,20 @@ function agentLogStream(name, accountDir, conversationId) {
|
|
|
6487
6542
|
if (!conversationId) {
|
|
6488
6543
|
throw new Error(`agentLogStream: conversationId is required (name=${name}) \u2014 use preConversationLogStream for pre-session events`);
|
|
6489
6544
|
}
|
|
6490
|
-
const logDir =
|
|
6491
|
-
|
|
6545
|
+
const logDir = resolve7(accountDir, "logs");
|
|
6546
|
+
mkdirSync5(logDir, { recursive: true });
|
|
6492
6547
|
purgeOldLogs(logDir, `${name}-`);
|
|
6493
|
-
const logPath2 =
|
|
6548
|
+
const logPath2 = resolve7(logDir, `${name}-${conversationId}.log`);
|
|
6494
6549
|
const stream = createWriteStream(logPath2, { flags: "a" });
|
|
6495
6550
|
registerStreamLog(stream, { path: logPath2, conversationId, name });
|
|
6496
6551
|
return stream;
|
|
6497
6552
|
}
|
|
6498
6553
|
function preConversationLogStream(name, accountDir) {
|
|
6499
|
-
const logDir =
|
|
6500
|
-
|
|
6554
|
+
const logDir = resolve7(accountDir, "logs");
|
|
6555
|
+
mkdirSync5(logDir, { recursive: true });
|
|
6501
6556
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6502
6557
|
purgeOldLogs(logDir, `preconversation-${name}-`);
|
|
6503
|
-
const logPath2 =
|
|
6558
|
+
const logPath2 = resolve7(logDir, `preconversation-${name}-${date}.log`);
|
|
6504
6559
|
const stream = createWriteStream(logPath2, { flags: "a" });
|
|
6505
6560
|
registerStreamLog(stream, { path: logPath2, conversationId: null, name: `preconversation-${name}` });
|
|
6506
6561
|
return stream;
|
|
@@ -6519,7 +6574,7 @@ function sigtermFlushStreamLogs(reason, source) {
|
|
|
6519
6574
|
const line = `[${ts}] [server-sigterm] reason=${reason}${convPart} name=${entry.name} source=${source}
|
|
6520
6575
|
`;
|
|
6521
6576
|
try {
|
|
6522
|
-
|
|
6577
|
+
appendFileSync3(entry.path, line);
|
|
6523
6578
|
} catch (err) {
|
|
6524
6579
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6525
6580
|
console.error(`[server-sigterm-flush-err] path=${entry.path} reason=${msg}`);
|
|
@@ -6538,7 +6593,7 @@ function purgeOldLogs(logDir, prefix) {
|
|
|
6538
6593
|
}
|
|
6539
6594
|
for (const file of entries) {
|
|
6540
6595
|
if (!file.startsWith(prefix)) continue;
|
|
6541
|
-
const filePath =
|
|
6596
|
+
const filePath = resolve7(logDir, file);
|
|
6542
6597
|
try {
|
|
6543
6598
|
if (statSync3(filePath).mtimeMs < cutoff) unlinkSync2(filePath);
|
|
6544
6599
|
} catch (err) {
|
|
@@ -6656,8 +6711,8 @@ function sampleProcState(pid) {
|
|
|
6656
6711
|
return `proc_err=${JSON.stringify(msg.slice(0, 60))}`;
|
|
6657
6712
|
}
|
|
6658
6713
|
}
|
|
6659
|
-
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ??
|
|
6660
|
-
var ACCOUNTS_DIR =
|
|
6714
|
+
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve7(process.cwd(), "..");
|
|
6715
|
+
var ACCOUNTS_DIR = resolve7(PLATFORM_ROOT4, "..", "data/accounts");
|
|
6661
6716
|
if (!existsSync6(PLATFORM_ROOT4)) {
|
|
6662
6717
|
throw new Error(
|
|
6663
6718
|
`PLATFORM_ROOT does not exist: ${PLATFORM_ROOT4}
|
|
@@ -6666,7 +6721,7 @@ Set the MAXY_PLATFORM_ROOT environment variable to the absolute path of the plat
|
|
|
6666
6721
|
}
|
|
6667
6722
|
function resolveAccount() {
|
|
6668
6723
|
if (!existsSync6(ACCOUNTS_DIR)) return null;
|
|
6669
|
-
const usersFilePath =
|
|
6724
|
+
const usersFilePath = resolve7(PLATFORM_ROOT4, "config", "users.json");
|
|
6670
6725
|
let usersJsonUserId = null;
|
|
6671
6726
|
if (existsSync6(usersFilePath)) {
|
|
6672
6727
|
try {
|
|
@@ -6684,7 +6739,7 @@ function resolveAccount() {
|
|
|
6684
6739
|
let fallback = null;
|
|
6685
6740
|
for (const entry of entries) {
|
|
6686
6741
|
if (!entry.isDirectory()) continue;
|
|
6687
|
-
const configPath2 =
|
|
6742
|
+
const configPath2 = resolve7(ACCOUNTS_DIR, entry.name, "account.json");
|
|
6688
6743
|
if (!existsSync6(configPath2)) continue;
|
|
6689
6744
|
const raw2 = readFileSync7(configPath2, "utf-8");
|
|
6690
6745
|
let config;
|
|
@@ -6701,7 +6756,7 @@ function resolveAccount() {
|
|
|
6701
6756
|
}
|
|
6702
6757
|
const result = {
|
|
6703
6758
|
accountId: config.accountId,
|
|
6704
|
-
accountDir:
|
|
6759
|
+
accountDir: resolve7(ACCOUNTS_DIR, entry.name),
|
|
6705
6760
|
config
|
|
6706
6761
|
};
|
|
6707
6762
|
if (usersJsonUserId && config.admins?.some((a) => a.userId === usersJsonUserId)) {
|
|
@@ -6719,7 +6774,7 @@ function resolveAccount() {
|
|
|
6719
6774
|
return fallback;
|
|
6720
6775
|
}
|
|
6721
6776
|
function readAgentFile(accountDir, agentName, filename) {
|
|
6722
|
-
const filePath =
|
|
6777
|
+
const filePath = resolve7(accountDir, "agents", agentName, filename);
|
|
6723
6778
|
if (!existsSync6(filePath)) return null;
|
|
6724
6779
|
return readFileSync7(filePath, "utf-8");
|
|
6725
6780
|
}
|
|
@@ -6737,7 +6792,7 @@ function validateAgentSlug(slug) {
|
|
|
6737
6792
|
return true;
|
|
6738
6793
|
}
|
|
6739
6794
|
function resolveDefaultAgentSlug(accountDir) {
|
|
6740
|
-
const configPath2 =
|
|
6795
|
+
const configPath2 = resolve7(accountDir, "account.json");
|
|
6741
6796
|
if (!existsSync6(configPath2)) {
|
|
6742
6797
|
console.error("[agent-resolve] account.json not found \u2014 cannot resolve defaultAgent");
|
|
6743
6798
|
return null;
|
|
@@ -6753,7 +6808,7 @@ function resolveDefaultAgentSlug(accountDir) {
|
|
|
6753
6808
|
console.error("[agent-resolve] defaultAgent not configured in account.json \u2014 set it via the connect-whatsapp skill");
|
|
6754
6809
|
return null;
|
|
6755
6810
|
}
|
|
6756
|
-
const agentConfigPath =
|
|
6811
|
+
const agentConfigPath = resolve7(accountDir, "agents", config.defaultAgent, "config.json");
|
|
6757
6812
|
if (!existsSync6(agentConfigPath)) {
|
|
6758
6813
|
console.error(`[agent-resolve] defaultAgent="${config.defaultAgent}" has no config.json at ${agentConfigPath}`);
|
|
6759
6814
|
return null;
|
|
@@ -6826,9 +6881,9 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
6826
6881
|
}
|
|
6827
6882
|
let knowledge = null;
|
|
6828
6883
|
let knowledgeBaked = false;
|
|
6829
|
-
const agentDir =
|
|
6830
|
-
const knowledgePath =
|
|
6831
|
-
const summaryPath =
|
|
6884
|
+
const agentDir = resolve7(accountDir, "agents", agentName);
|
|
6885
|
+
const knowledgePath = resolve7(agentDir, "KNOWLEDGE.md");
|
|
6886
|
+
const summaryPath = resolve7(agentDir, "KNOWLEDGE-SUMMARY.md");
|
|
6832
6887
|
const hasKnowledge = existsSync6(knowledgePath);
|
|
6833
6888
|
const hasSummary = existsSync6(summaryPath);
|
|
6834
6889
|
if (hasKnowledge && hasSummary) {
|
|
@@ -6864,7 +6919,7 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
6864
6919
|
return { model, plugins, status, displayName, image, imageShape, showAgentName, knowledge, knowledgeBaked, liveMemory, knowledgeKeywords, budget, accessMode };
|
|
6865
6920
|
}
|
|
6866
6921
|
function parsePluginFrontmatter(pluginDir) {
|
|
6867
|
-
const pluginPath =
|
|
6922
|
+
const pluginPath = resolve7(PLATFORM_ROOT4, "plugins", pluginDir, "PLUGIN.md");
|
|
6868
6923
|
if (!existsSync6(pluginPath)) return null;
|
|
6869
6924
|
let raw2;
|
|
6870
6925
|
try {
|
|
@@ -6927,14 +6982,14 @@ function parsePluginFrontmatter(pluginDir) {
|
|
|
6927
6982
|
function autoDeliverPremiumPlugins(purchasedPlugins) {
|
|
6928
6983
|
if (!purchasedPlugins || purchasedPlugins.length === 0) return;
|
|
6929
6984
|
const TAG18 = "[premium-auto-deliver]";
|
|
6930
|
-
const stagingRoot =
|
|
6931
|
-
const pluginsDir =
|
|
6985
|
+
const stagingRoot = resolve7(PLATFORM_ROOT4, "../premium-plugins");
|
|
6986
|
+
const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
|
|
6932
6987
|
if (!existsSync6(stagingRoot)) {
|
|
6933
6988
|
console.log(`${TAG18} no staging directory \u2014 skipping`);
|
|
6934
6989
|
return;
|
|
6935
6990
|
}
|
|
6936
6991
|
for (const pluginName of purchasedPlugins) {
|
|
6937
|
-
const stagingDir =
|
|
6992
|
+
const stagingDir = resolve7(stagingRoot, pluginName);
|
|
6938
6993
|
if (!existsSync6(stagingDir)) {
|
|
6939
6994
|
console.log(`${TAG18} ${pluginName}: not in staging \u2014 skipping`);
|
|
6940
6995
|
continue;
|
|
@@ -6975,12 +7030,12 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
|
|
|
6975
7030
|
let delivered = 0;
|
|
6976
7031
|
let skipped = 0;
|
|
6977
7032
|
for (const sub of subPlugins) {
|
|
6978
|
-
const target =
|
|
6979
|
-
if (existsSync6(
|
|
7033
|
+
const target = resolve7(pluginsDir, sub);
|
|
7034
|
+
if (existsSync6(resolve7(target, "PLUGIN.md"))) {
|
|
6980
7035
|
skipped++;
|
|
6981
7036
|
continue;
|
|
6982
7037
|
}
|
|
6983
|
-
const source =
|
|
7038
|
+
const source = resolve7(stagingDir, "plugins", sub);
|
|
6984
7039
|
if (!existsSync6(source)) {
|
|
6985
7040
|
console.log(`${TAG18} ${pluginName}/${sub}: source missing in staging \u2014 skipping`);
|
|
6986
7041
|
continue;
|
|
@@ -6994,8 +7049,8 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
|
|
|
6994
7049
|
}
|
|
6995
7050
|
console.log(`${TAG18} ${pluginName} (bundle): ${delivered} delivered, ${skipped} already present`);
|
|
6996
7051
|
} else {
|
|
6997
|
-
const target =
|
|
6998
|
-
if (existsSync6(
|
|
7052
|
+
const target = resolve7(pluginsDir, pluginName);
|
|
7053
|
+
if (existsSync6(resolve7(target, "PLUGIN.md"))) {
|
|
6999
7054
|
console.log(`${TAG18} ${pluginName}: already present \u2014 skipping`);
|
|
7000
7055
|
continue;
|
|
7001
7056
|
}
|
|
@@ -7035,7 +7090,7 @@ function migratePluginRenames(accountDir, config) {
|
|
|
7035
7090
|
return name;
|
|
7036
7091
|
});
|
|
7037
7092
|
if (!changed) return;
|
|
7038
|
-
const configPath2 =
|
|
7093
|
+
const configPath2 = resolve7(accountDir, "account.json");
|
|
7039
7094
|
try {
|
|
7040
7095
|
const raw2 = readFileSync7(configPath2, "utf-8");
|
|
7041
7096
|
const parsed = JSON.parse(raw2);
|
|
@@ -7046,9 +7101,9 @@ function migratePluginRenames(accountDir, config) {
|
|
|
7046
7101
|
} catch (err) {
|
|
7047
7102
|
console.error(`${TAG18} failed to update account.json \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
7048
7103
|
}
|
|
7049
|
-
const pluginsDir =
|
|
7104
|
+
const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
|
|
7050
7105
|
for (const oldName of Object.keys(PLUGIN_RENAMES)) {
|
|
7051
|
-
const orphan =
|
|
7106
|
+
const orphan = resolve7(pluginsDir, oldName);
|
|
7052
7107
|
if (existsSync6(orphan)) {
|
|
7053
7108
|
try {
|
|
7054
7109
|
rmSync2(orphan, { recursive: true });
|
|
@@ -7062,13 +7117,13 @@ function migratePluginRenames(accountDir, config) {
|
|
|
7062
7117
|
function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
|
|
7063
7118
|
if (!purchasedPlugins || purchasedPlugins.length === 0) return;
|
|
7064
7119
|
const TAG18 = "[bundle-agent-deliver]";
|
|
7065
|
-
const stagingRoot =
|
|
7066
|
-
const specialistsDir =
|
|
7120
|
+
const stagingRoot = resolve7(PLATFORM_ROOT4, "../premium-plugins");
|
|
7121
|
+
const specialistsDir = resolve7(accountDir, "specialists", "agents");
|
|
7067
7122
|
if (!existsSync6(stagingRoot)) return;
|
|
7068
7123
|
if (!existsSync6(specialistsDir)) {
|
|
7069
|
-
|
|
7124
|
+
mkdirSync5(specialistsDir, { recursive: true });
|
|
7070
7125
|
}
|
|
7071
|
-
const agentsmdPath =
|
|
7126
|
+
const agentsmdPath = resolve7(accountDir, "agents", "admin", "AGENTS.md");
|
|
7072
7127
|
let agentsmd = "";
|
|
7073
7128
|
try {
|
|
7074
7129
|
agentsmd = existsSync6(agentsmdPath) ? readFileSync7(agentsmdPath, "utf-8") : "";
|
|
@@ -7076,7 +7131,7 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
|
|
|
7076
7131
|
}
|
|
7077
7132
|
let delivered = 0;
|
|
7078
7133
|
for (const pluginName of purchasedPlugins) {
|
|
7079
|
-
const bundleAgentsDir =
|
|
7134
|
+
const bundleAgentsDir = resolve7(stagingRoot, pluginName, "agents");
|
|
7080
7135
|
if (!existsSync6(bundleAgentsDir)) continue;
|
|
7081
7136
|
let entries;
|
|
7082
7137
|
try {
|
|
@@ -7085,9 +7140,9 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
|
|
|
7085
7140
|
continue;
|
|
7086
7141
|
}
|
|
7087
7142
|
for (const filename of entries) {
|
|
7088
|
-
const target =
|
|
7143
|
+
const target = resolve7(specialistsDir, filename);
|
|
7089
7144
|
if (existsSync6(target)) continue;
|
|
7090
|
-
const source =
|
|
7145
|
+
const source = resolve7(bundleAgentsDir, filename);
|
|
7091
7146
|
try {
|
|
7092
7147
|
cpSync(source, target);
|
|
7093
7148
|
} catch (err) {
|
|
@@ -7127,8 +7182,8 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
|
|
|
7127
7182
|
}
|
|
7128
7183
|
}
|
|
7129
7184
|
function assemblePublicPluginContent(pluginDir) {
|
|
7130
|
-
const pluginRoot =
|
|
7131
|
-
const pluginPath =
|
|
7185
|
+
const pluginRoot = resolve7(PLATFORM_ROOT4, "plugins", pluginDir);
|
|
7186
|
+
const pluginPath = resolve7(pluginRoot, "PLUGIN.md");
|
|
7132
7187
|
let raw2;
|
|
7133
7188
|
try {
|
|
7134
7189
|
raw2 = readFileSync7(pluginPath, "utf-8");
|
|
@@ -7140,7 +7195,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
7140
7195
|
const parts = [pluginBody];
|
|
7141
7196
|
let skillCount = 0;
|
|
7142
7197
|
let refCount = 0;
|
|
7143
|
-
const skillsDir =
|
|
7198
|
+
const skillsDir = resolve7(pluginRoot, "skills");
|
|
7144
7199
|
let skillDirs;
|
|
7145
7200
|
try {
|
|
7146
7201
|
skillDirs = readdirSync2(skillsDir).sort();
|
|
@@ -7148,8 +7203,8 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
7148
7203
|
return { body: pluginBody, skillCount: 0, refCount: 0 };
|
|
7149
7204
|
}
|
|
7150
7205
|
for (const skillName of skillDirs) {
|
|
7151
|
-
const skillDir =
|
|
7152
|
-
const skillMdPath =
|
|
7206
|
+
const skillDir = resolve7(skillsDir, skillName);
|
|
7207
|
+
const skillMdPath = resolve7(skillDir, "SKILL.md");
|
|
7153
7208
|
let skillRaw;
|
|
7154
7209
|
try {
|
|
7155
7210
|
skillRaw = readFileSync7(skillMdPath, "utf-8");
|
|
@@ -7192,7 +7247,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
7192
7247
|
parts.push(`
|
|
7193
7248
|
<!-- skill: ${skillName} -->`);
|
|
7194
7249
|
parts.push(skillBody);
|
|
7195
|
-
const refsDir =
|
|
7250
|
+
const refsDir = resolve7(skillDir, "references");
|
|
7196
7251
|
let refFiles;
|
|
7197
7252
|
try {
|
|
7198
7253
|
refFiles = readdirSync2(refsDir).filter((f) => f.endsWith(".md")).filter((f) => !publicExcludeReferences.includes(f)).sort();
|
|
@@ -7203,7 +7258,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
7203
7258
|
}
|
|
7204
7259
|
for (const refFile of refFiles) {
|
|
7205
7260
|
try {
|
|
7206
|
-
const refContent = readFileSync7(
|
|
7261
|
+
const refContent = readFileSync7(resolve7(refsDir, refFile), "utf-8").trim();
|
|
7207
7262
|
if (refContent) {
|
|
7208
7263
|
parts.push(`
|
|
7209
7264
|
<!-- reference: ${refFile} -->`);
|
|
@@ -7221,7 +7276,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
7221
7276
|
return { body: parts.join("\n"), skillCount, refCount };
|
|
7222
7277
|
}
|
|
7223
7278
|
function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
|
|
7224
|
-
const pluginsDir =
|
|
7279
|
+
const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
|
|
7225
7280
|
let dirs;
|
|
7226
7281
|
try {
|
|
7227
7282
|
dirs = readdirSync2(pluginsDir);
|
|
@@ -7274,7 +7329,7 @@ function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
|
|
|
7274
7329
|
console.log(`[plugins] loaded ${dir} for public (${assembled.body.length} chars, ${assembled.skillCount} skills, ${assembled.refCount} refs)`);
|
|
7275
7330
|
}
|
|
7276
7331
|
} else {
|
|
7277
|
-
const pluginPath =
|
|
7332
|
+
const pluginPath = resolve7(pluginsDir, dir, "PLUGIN.md");
|
|
7278
7333
|
let raw2;
|
|
7279
7334
|
try {
|
|
7280
7335
|
raw2 = readFileSync7(pluginPath, "utf-8");
|
|
@@ -7301,7 +7356,7 @@ var mcpToolsCache = /* @__PURE__ */ new Map();
|
|
|
7301
7356
|
function fetchMcpToolsList(pluginDir) {
|
|
7302
7357
|
const cached = mcpToolsCache.get(pluginDir);
|
|
7303
7358
|
if (cached) return Promise.resolve(cached);
|
|
7304
|
-
const serverPath =
|
|
7359
|
+
const serverPath = resolve7(PLATFORM_ROOT4, "plugins", pluginDir, "mcp/dist/index.js");
|
|
7305
7360
|
if (!existsSync6(serverPath)) return Promise.resolve([]);
|
|
7306
7361
|
const startMs = Date.now();
|
|
7307
7362
|
return new Promise((resolvePromise) => {
|
|
@@ -7414,7 +7469,7 @@ var SPECIALIST_PLUGIN_DOMAINS = {
|
|
|
7414
7469
|
// agent, so it retains a full manifest entry for routing clarity.
|
|
7415
7470
|
};
|
|
7416
7471
|
async function buildPluginManifest(enabledPlugins) {
|
|
7417
|
-
const pluginsDir =
|
|
7472
|
+
const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
|
|
7418
7473
|
let dirs;
|
|
7419
7474
|
try {
|
|
7420
7475
|
dirs = readdirSync2(pluginsDir);
|
|
@@ -7501,16 +7556,16 @@ ${specialist}: ${plugins.join(", ")}`);
|
|
|
7501
7556
|
for (let j = 0; j < adminPlugins.length; j++) {
|
|
7502
7557
|
const { dir, parsed } = adminPlugins[j];
|
|
7503
7558
|
const mcpTools = adminMcpResults[j];
|
|
7504
|
-
const pluginRoot =
|
|
7559
|
+
const pluginRoot = resolve7(pluginsDir, dir);
|
|
7505
7560
|
const skills = [];
|
|
7506
7561
|
const references = [];
|
|
7507
7562
|
const scanDir = (base, prefix, target) => {
|
|
7508
|
-
const scanPath =
|
|
7563
|
+
const scanPath = resolve7(pluginRoot, base);
|
|
7509
7564
|
if (!existsSync6(scanPath)) return;
|
|
7510
7565
|
try {
|
|
7511
7566
|
const walk = (current, rel) => {
|
|
7512
7567
|
for (const entry of readdirSync2(current)) {
|
|
7513
|
-
const full =
|
|
7568
|
+
const full = resolve7(current, entry);
|
|
7514
7569
|
try {
|
|
7515
7570
|
const stat5 = statSync3(full);
|
|
7516
7571
|
if (stat5.isDirectory()) {
|
|
@@ -7538,7 +7593,7 @@ ${specialist}: ${plugins.join(", ")}`);
|
|
|
7538
7593
|
toolLines.push(desc ? ` ${tool.name} \u2014 ${desc}` : ` ${tool.name}`);
|
|
7539
7594
|
}
|
|
7540
7595
|
} else if (parsed.tools.length > 0) {
|
|
7541
|
-
const serverPath =
|
|
7596
|
+
const serverPath = resolve7(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
|
|
7542
7597
|
if (existsSync6(serverPath)) {
|
|
7543
7598
|
fallbackSourced++;
|
|
7544
7599
|
console.error(`[plugin-manifest] ${dir}: tools/list empty \u2014 fallback to frontmatter (${parsed.tools.length} tools)`);
|
|
@@ -7619,7 +7674,7 @@ function resolveUserAccounts(userId) {
|
|
|
7619
7674
|
const entries = readdirSync2(ACCOUNTS_DIR, { withFileTypes: true });
|
|
7620
7675
|
for (const entry of entries) {
|
|
7621
7676
|
if (!entry.isDirectory()) continue;
|
|
7622
|
-
const configPath2 =
|
|
7677
|
+
const configPath2 = resolve7(ACCOUNTS_DIR, entry.name, "account.json");
|
|
7623
7678
|
if (!existsSync6(configPath2)) continue;
|
|
7624
7679
|
let config;
|
|
7625
7680
|
try {
|
|
@@ -7632,7 +7687,7 @@ function resolveUserAccounts(userId) {
|
|
|
7632
7687
|
if (adminEntry) {
|
|
7633
7688
|
results.push({
|
|
7634
7689
|
accountId: config.accountId,
|
|
7635
|
-
accountDir:
|
|
7690
|
+
accountDir: resolve7(ACCOUNTS_DIR, entry.name),
|
|
7636
7691
|
config,
|
|
7637
7692
|
role: adminEntry.role
|
|
7638
7693
|
});
|
|
@@ -7849,8 +7904,8 @@ function consumeStalledSubagents(sessionKey) {
|
|
|
7849
7904
|
return stalls && stalls.length > 0 ? stalls : void 0;
|
|
7850
7905
|
}
|
|
7851
7906
|
function streamLogPathFor(accountId, conversationId) {
|
|
7852
|
-
const logDir =
|
|
7853
|
-
const streamLogPath =
|
|
7907
|
+
const logDir = resolve7(ACCOUNTS_DIR, accountId, "logs");
|
|
7908
|
+
const streamLogPath = resolve7(logDir, `claude-agent-stream-${conversationId}.log`);
|
|
7854
7909
|
return { logDir, streamLogPath };
|
|
7855
7910
|
}
|
|
7856
7911
|
function buildSpawnEnv(accountId, accountDir, conversationId) {
|
|
@@ -7871,7 +7926,7 @@ var cachedBrandHostname = null;
|
|
|
7871
7926
|
function readBrandHostname() {
|
|
7872
7927
|
if (cachedBrandHostname !== null) return cachedBrandHostname;
|
|
7873
7928
|
try {
|
|
7874
|
-
const brandPath =
|
|
7929
|
+
const brandPath = resolve7(PLATFORM_ROOT4, "config", "brand.json");
|
|
7875
7930
|
const parsed = JSON.parse(readFileSync7(brandPath, "utf-8"));
|
|
7876
7931
|
cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
|
|
7877
7932
|
} catch {
|
|
@@ -7909,37 +7964,37 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7909
7964
|
const servers = {
|
|
7910
7965
|
"memory": {
|
|
7911
7966
|
command: "node",
|
|
7912
|
-
args: [
|
|
7967
|
+
args: [resolve7(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js")],
|
|
7913
7968
|
env: { ...baseEnv, ...userId ? { USER_ID: userId } : {} }
|
|
7914
7969
|
},
|
|
7915
7970
|
"contacts": {
|
|
7916
7971
|
command: "node",
|
|
7917
|
-
args: [
|
|
7972
|
+
args: [resolve7(PLATFORM_ROOT4, "plugins/contacts/mcp/dist/index.js")],
|
|
7918
7973
|
env: { ...baseEnv }
|
|
7919
7974
|
},
|
|
7920
7975
|
"whatsapp": {
|
|
7921
7976
|
command: "node",
|
|
7922
|
-
args: [
|
|
7977
|
+
args: [resolve7(PLATFORM_ROOT4, "plugins/whatsapp/mcp/dist/index.js")],
|
|
7923
7978
|
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
|
|
7924
7979
|
},
|
|
7925
7980
|
"admin": {
|
|
7926
7981
|
command: "node",
|
|
7927
|
-
args: [
|
|
7982
|
+
args: [resolve7(PLATFORM_ROOT4, "plugins/admin/mcp/dist/index.js")],
|
|
7928
7983
|
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200", ...userId ? { USER_ID: userId } : {} }
|
|
7929
7984
|
},
|
|
7930
7985
|
"scheduling": {
|
|
7931
7986
|
command: "node",
|
|
7932
|
-
args: [
|
|
7987
|
+
args: [resolve7(PLATFORM_ROOT4, "plugins/scheduling/mcp/dist/index.js")],
|
|
7933
7988
|
env: { ...baseEnv }
|
|
7934
7989
|
},
|
|
7935
7990
|
"tasks": {
|
|
7936
7991
|
command: "node",
|
|
7937
|
-
args: [
|
|
7992
|
+
args: [resolve7(PLATFORM_ROOT4, "plugins/tasks/mcp/dist/index.js")],
|
|
7938
7993
|
env: { ...baseEnv }
|
|
7939
7994
|
},
|
|
7940
7995
|
"email": {
|
|
7941
7996
|
command: "node",
|
|
7942
|
-
args: [
|
|
7997
|
+
args: [resolve7(PLATFORM_ROOT4, "plugins/email/mcp/dist/index.js")],
|
|
7943
7998
|
env: { ...baseEnv }
|
|
7944
7999
|
},
|
|
7945
8000
|
// Workflows MCP — persistent admin-session server for list/get/update/delete/
|
|
@@ -7950,7 +8005,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7950
8005
|
// ToolSearches fruitlessly before degrading to a task-create stand-in (Task 571).
|
|
7951
8006
|
"workflows": {
|
|
7952
8007
|
command: "node",
|
|
7953
|
-
args: [
|
|
8008
|
+
args: [resolve7(PLATFORM_ROOT4, "plugins/workflows/mcp/dist/index.js")],
|
|
7954
8009
|
env: { ...baseEnv }
|
|
7955
8010
|
},
|
|
7956
8011
|
// Playwright MCP server — browser automation for browser-specialist.
|
|
@@ -7972,7 +8027,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7972
8027
|
// MAXY-PRD.md:627, not in any application-layer filter).
|
|
7973
8028
|
"graph": {
|
|
7974
8029
|
command: "node",
|
|
7975
|
-
args: [
|
|
8030
|
+
args: [resolve7(PLATFORM_ROOT4, "lib/graph-mcp/dist/index.js")],
|
|
7976
8031
|
env: {
|
|
7977
8032
|
...baseEnv,
|
|
7978
8033
|
BRAND: readBrandHostname(),
|
|
@@ -7988,7 +8043,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7988
8043
|
if (tgBotToken) {
|
|
7989
8044
|
servers["telegram"] = {
|
|
7990
8045
|
command: "node",
|
|
7991
|
-
args: [
|
|
8046
|
+
args: [resolve7(PLATFORM_ROOT4, "plugins/telegram/mcp/dist/index.js")],
|
|
7992
8047
|
env: { ...baseEnv, TELEGRAM_BOT_TOKEN: tgBotToken }
|
|
7993
8048
|
};
|
|
7994
8049
|
} else {
|
|
@@ -7996,11 +8051,11 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7996
8051
|
}
|
|
7997
8052
|
servers["cloudflare"] = {
|
|
7998
8053
|
command: "node",
|
|
7999
|
-
args: [
|
|
8054
|
+
args: [resolve7(PLATFORM_ROOT4, "plugins/cloudflare/mcp/dist/index.js")],
|
|
8000
8055
|
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
|
|
8001
8056
|
};
|
|
8002
8057
|
if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
|
|
8003
|
-
const pluginsDir =
|
|
8058
|
+
const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
|
|
8004
8059
|
let dirs;
|
|
8005
8060
|
try {
|
|
8006
8061
|
dirs = readdirSync2(pluginsDir);
|
|
@@ -8023,7 +8078,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
8023
8078
|
continue;
|
|
8024
8079
|
}
|
|
8025
8080
|
}
|
|
8026
|
-
const mcpEntry =
|
|
8081
|
+
const mcpEntry = resolve7(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
|
|
8027
8082
|
if (!existsSync6(mcpEntry)) continue;
|
|
8028
8083
|
servers[dir] = {
|
|
8029
8084
|
command: "node",
|
|
@@ -8158,7 +8213,7 @@ var ADMIN_CORE_TOOLS = [
|
|
|
8158
8213
|
function getAdminAllowedTools(enabledPlugins) {
|
|
8159
8214
|
const tools = [...ADMIN_CORE_TOOLS];
|
|
8160
8215
|
if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
|
|
8161
|
-
const pluginsDir =
|
|
8216
|
+
const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
|
|
8162
8217
|
let dirs;
|
|
8163
8218
|
try {
|
|
8164
8219
|
dirs = readdirSync2(pluginsDir);
|
|
@@ -8266,13 +8321,13 @@ ${message.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`
|
|
|
8266
8321
|
}
|
|
8267
8322
|
}
|
|
8268
8323
|
async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
8269
|
-
const serverPath =
|
|
8324
|
+
const serverPath = resolve7(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
|
|
8270
8325
|
if (!existsSync6(serverPath)) {
|
|
8271
8326
|
console.error(`[fetchMemoryContext] MCP server not found: ${serverPath}`);
|
|
8272
8327
|
return null;
|
|
8273
8328
|
}
|
|
8274
8329
|
const startMs = Date.now();
|
|
8275
|
-
return new Promise((
|
|
8330
|
+
return new Promise((resolve30) => {
|
|
8276
8331
|
const proc = spawn2(process.execPath, [serverPath], {
|
|
8277
8332
|
env: {
|
|
8278
8333
|
...process.env,
|
|
@@ -8301,7 +8356,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
|
8301
8356
|
} else {
|
|
8302
8357
|
console.error(`[fetchMemoryContext] failed: ${reason} (${elapsed}ms)${stderrBuf ? ` stderr: ${stderrBuf.slice(0, 500)}` : ""}`);
|
|
8303
8358
|
}
|
|
8304
|
-
|
|
8359
|
+
resolve30(value);
|
|
8305
8360
|
};
|
|
8306
8361
|
proc.stdout.on("data", (chunk) => {
|
|
8307
8362
|
buffer += chunk.toString();
|
|
@@ -8364,7 +8419,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
|
8364
8419
|
}
|
|
8365
8420
|
async function compactTrimmedMessages(accountId, trimmedMessages) {
|
|
8366
8421
|
if (trimmedMessages.length === 0) return true;
|
|
8367
|
-
const serverPath =
|
|
8422
|
+
const serverPath = resolve7(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
|
|
8368
8423
|
if (!existsSync6(serverPath)) return false;
|
|
8369
8424
|
const briefing = trimmedMessages.map((m) => `[${m.role.toUpperCase()}] ${m.content}`).join("\n\n");
|
|
8370
8425
|
return new Promise((resolvePromise) => {
|
|
@@ -8682,7 +8737,7 @@ Then respond with only: [COMPACTED]`;
|
|
|
8682
8737
|
var COMPACTION_TIMEOUT_MS = 45e3;
|
|
8683
8738
|
async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, conversationId, enabledPlugins) {
|
|
8684
8739
|
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, conversationId, void 0, enabledPlugins) });
|
|
8685
|
-
const specialistsDir =
|
|
8740
|
+
const specialistsDir = resolve7(accountDir, "specialists");
|
|
8686
8741
|
if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir, conversationId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
8687
8742
|
`);
|
|
8688
8743
|
const args = [
|
|
@@ -8956,7 +9011,7 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId, a
|
|
|
8956
9011
|
const { logDir } = streamLogPathFor(accountId, conversationId);
|
|
8957
9012
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
8958
9013
|
for (const s of failed) {
|
|
8959
|
-
const stderrPath =
|
|
9014
|
+
const stderrPath = resolve7(logDir, `mcp-${s.name}-stderr-${date}.log`);
|
|
8960
9015
|
let tail = "(no stderr file)";
|
|
8961
9016
|
try {
|
|
8962
9017
|
const stats = statSync3(stderrPath);
|
|
@@ -9598,7 +9653,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9598
9653
|
}
|
|
9599
9654
|
const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
|
|
9600
9655
|
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, spawnConvId, ccUserId, enabledPlugins) });
|
|
9601
|
-
const specialistsDir =
|
|
9656
|
+
const specialistsDir = resolve7(accountDir, "specialists");
|
|
9602
9657
|
if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnConvId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9603
9658
|
`);
|
|
9604
9659
|
const args = [
|
|
@@ -9939,7 +9994,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9939
9994
|
`);
|
|
9940
9995
|
const managedUserId = getUserIdForSession(sessionKey);
|
|
9941
9996
|
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedConvId, managedUserId, enabledPlugins) });
|
|
9942
|
-
const specialistsDir =
|
|
9997
|
+
const specialistsDir = resolve7(accountDir, "specialists");
|
|
9943
9998
|
if (!existsSync6(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9944
9999
|
`);
|
|
9945
10000
|
const fullMessage = attachments.length > 0 ? message + buildAttachmentMetaText(attachments) : message;
|
|
@@ -10602,7 +10657,7 @@ ${sessionContext}`;
|
|
|
10602
10657
|
console.log(`[onboarding-inject] accountId=${accountId.slice(0, 8)}\u2026 error=neo4j-unreachable injected=false`);
|
|
10603
10658
|
} else if (onboardingStep < 8) {
|
|
10604
10659
|
const GENERIC_FALLBACK = "At every session start, call `onboarding-get`. If `currentStep` is less than 8, load the onboarding skill via `plugin-read` (find its path in the manifest under `admin`) and follow it \u2014 before any business setup. If `onboarding-get` fails (Neo4j unreachable), tell the user and skip onboarding for this session \u2014 it resumes automatically when the graph is available.";
|
|
10605
|
-
const skillPath =
|
|
10660
|
+
const skillPath = resolve7(PLATFORM_ROOT4, "plugins/admin/skills/onboarding/SKILL.md");
|
|
10606
10661
|
let skillContent = "";
|
|
10607
10662
|
try {
|
|
10608
10663
|
skillContent = readFileSync7(skillPath, "utf-8");
|
|
@@ -10655,7 +10710,7 @@ ${body}`;
|
|
|
10655
10710
|
|
|
10656
10711
|
${manifest}`;
|
|
10657
10712
|
}
|
|
10658
|
-
const graphRefPath =
|
|
10713
|
+
const graphRefPath = resolve7(PLATFORM_ROOT4, "plugins/memory/references/graph-primitives.md");
|
|
10659
10714
|
try {
|
|
10660
10715
|
const graphRef = readFileSync7(graphRefPath, "utf-8");
|
|
10661
10716
|
baseSystemPrompt += `
|
|
@@ -10883,8 +10938,8 @@ function getLanIp() {
|
|
|
10883
10938
|
import { basename as basename2 } from "path";
|
|
10884
10939
|
|
|
10885
10940
|
// app/lib/review-detector/rules.ts
|
|
10886
|
-
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, statSync as statSync4, mkdirSync as
|
|
10887
|
-
import { resolve as
|
|
10941
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, statSync as statSync4, mkdirSync as mkdirSync6, renameSync } from "fs";
|
|
10942
|
+
import { resolve as resolve8, dirname as dirname2 } from "path";
|
|
10888
10943
|
var DEFAULT_SCAN_INTERVAL_MS = 5e3;
|
|
10889
10944
|
var RATE_LIMIT_PATTERN = "rate[- ]?limit(?:ed| reached| hit)|(?:HTTP|status)[^a-z]{0,3}429|too many requests";
|
|
10890
10945
|
var RATE_LIMIT_PATTERN_V1 = "\\b429\\b|rate.?limit|too.?many.?requests";
|
|
@@ -11313,12 +11368,12 @@ function defaultRules() {
|
|
|
11313
11368
|
];
|
|
11314
11369
|
}
|
|
11315
11370
|
function rulesFilePath(configDir2) {
|
|
11316
|
-
return
|
|
11371
|
+
return resolve8(configDir2, "review-rules.json");
|
|
11317
11372
|
}
|
|
11318
11373
|
function ensureRulesFile(configDir2) {
|
|
11319
11374
|
const path2 = rulesFilePath(configDir2);
|
|
11320
11375
|
if (existsSync7(path2)) return { created: false, path: path2 };
|
|
11321
|
-
|
|
11376
|
+
mkdirSync6(dirname2(path2), { recursive: true });
|
|
11322
11377
|
const body = {
|
|
11323
11378
|
scanIntervalMs: DEFAULT_SCAN_INTERVAL_MS,
|
|
11324
11379
|
rules: defaultRules()
|
|
@@ -11496,10 +11551,10 @@ function validateRule(input, label, seenIds) {
|
|
|
11496
11551
|
}
|
|
11497
11552
|
|
|
11498
11553
|
// app/lib/review-detector/sources.ts
|
|
11499
|
-
import { existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync2, mkdirSync as
|
|
11500
|
-
import { resolve as
|
|
11554
|
+
import { existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync2, mkdirSync as mkdirSync7, openSync as openSync3, readSync as readSync3, closeSync as closeSync3, readFileSync as readFileSync9 } from "fs";
|
|
11555
|
+
import { resolve as resolve9, join as join5, basename, dirname as dirname3 } from "path";
|
|
11501
11556
|
function tailStatePath(configDir2) {
|
|
11502
|
-
return
|
|
11557
|
+
return resolve9(configDir2, "review-state.json");
|
|
11503
11558
|
}
|
|
11504
11559
|
function loadTailState(configDir2) {
|
|
11505
11560
|
const path2 = tailStatePath(configDir2);
|
|
@@ -11523,25 +11578,25 @@ function loadTailState(configDir2) {
|
|
|
11523
11578
|
}
|
|
11524
11579
|
function saveTailState(configDir2, state) {
|
|
11525
11580
|
const path2 = tailStatePath(configDir2);
|
|
11526
|
-
|
|
11581
|
+
mkdirSync7(dirname3(path2), { recursive: true });
|
|
11527
11582
|
const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
|
|
11528
11583
|
writeFileSync7(tmp, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
11529
11584
|
renameSync2(tmp, path2);
|
|
11530
11585
|
}
|
|
11531
11586
|
function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
|
|
11532
11587
|
if (logicalSource === "server") {
|
|
11533
|
-
const p =
|
|
11588
|
+
const p = resolve9(configDir2, "logs", "server.log");
|
|
11534
11589
|
return existsSync8(p) ? [{ logicalSource: "server", filepath: p }] : [];
|
|
11535
11590
|
}
|
|
11536
11591
|
if (logicalSource === "vnc") {
|
|
11537
|
-
const p =
|
|
11592
|
+
const p = resolve9(configDir2, "logs", "vnc-boot.log");
|
|
11538
11593
|
return existsSync8(p) ? [{ logicalSource: "vnc", filepath: p }] : [];
|
|
11539
11594
|
}
|
|
11540
11595
|
if (logicalSource === "cloudflared") {
|
|
11541
11596
|
const files2 = [];
|
|
11542
|
-
const daemon =
|
|
11597
|
+
const daemon = resolve9(configDir2, "logs", "cloudflared.log");
|
|
11543
11598
|
if (existsSync8(daemon)) files2.push({ logicalSource: "cloudflared", filepath: daemon });
|
|
11544
|
-
const login =
|
|
11599
|
+
const login = resolve9(configDir2, "logs", "cloudflared-login.log");
|
|
11545
11600
|
if (existsSync8(login)) files2.push({ logicalSource: "cloudflared", filepath: login });
|
|
11546
11601
|
return files2;
|
|
11547
11602
|
}
|
|
@@ -11668,31 +11723,31 @@ function fileLastWriteMs(path2) {
|
|
|
11668
11723
|
}
|
|
11669
11724
|
}
|
|
11670
11725
|
function accountLogDir(accountDir) {
|
|
11671
|
-
return
|
|
11726
|
+
return resolve9(accountDir, "logs");
|
|
11672
11727
|
}
|
|
11673
11728
|
function sourceKey(file) {
|
|
11674
11729
|
return `${file.logicalSource}:${basename(file.filepath)}`;
|
|
11675
11730
|
}
|
|
11676
11731
|
|
|
11677
11732
|
// app/lib/review-detector/writer.ts
|
|
11678
|
-
import { appendFileSync as
|
|
11679
|
-
import { resolve as
|
|
11733
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync8, renameSync as renameSync3, statSync as statSync6 } from "fs";
|
|
11734
|
+
import { resolve as resolve10, dirname as dirname4 } from "path";
|
|
11680
11735
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
11681
11736
|
function reviewLogPath(configDir2) {
|
|
11682
|
-
return
|
|
11737
|
+
return resolve10(configDir2, "logs", "review.log");
|
|
11683
11738
|
}
|
|
11684
11739
|
function pendingAlertsPath(configDir2) {
|
|
11685
|
-
return
|
|
11740
|
+
return resolve10(configDir2, "review-pending-alerts.jsonl");
|
|
11686
11741
|
}
|
|
11687
11742
|
function reviewLog(configDir2, event) {
|
|
11688
11743
|
const path2 = reviewLogPath(configDir2);
|
|
11689
11744
|
try {
|
|
11690
|
-
|
|
11745
|
+
mkdirSync8(dirname4(path2), { recursive: true });
|
|
11691
11746
|
const line = `${new Date(
|
|
11692
11747
|
typeof event.ts === "number" ? event.ts : Date.now()
|
|
11693
11748
|
).toISOString()} [review] ${JSON.stringify(event)}
|
|
11694
11749
|
`;
|
|
11695
|
-
|
|
11750
|
+
appendFileSync4(path2, line, "utf-8");
|
|
11696
11751
|
} catch (err) {
|
|
11697
11752
|
console.error(`[review] failed to write review log at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11698
11753
|
}
|
|
@@ -11804,9 +11859,9 @@ async function upsertReviewAlert(accountId, match2) {
|
|
|
11804
11859
|
function queueAlert(configDir2, accountId, match2) {
|
|
11805
11860
|
const path2 = pendingAlertsPath(configDir2);
|
|
11806
11861
|
try {
|
|
11807
|
-
|
|
11862
|
+
mkdirSync8(dirname4(path2), { recursive: true });
|
|
11808
11863
|
const line = JSON.stringify({ accountId, match: match2 }) + "\n";
|
|
11809
|
-
|
|
11864
|
+
appendFileSync4(path2, line, "utf-8");
|
|
11810
11865
|
} catch (err) {
|
|
11811
11866
|
console.error(`[review] failed to queue alert at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11812
11867
|
}
|
|
@@ -11937,7 +11992,7 @@ async function bootDetector() {
|
|
|
11937
11992
|
}
|
|
11938
11993
|
|
|
11939
11994
|
// app/lib/review-detector/scan-loop.ts
|
|
11940
|
-
import { resolve as
|
|
11995
|
+
import { resolve as resolve11 } from "path";
|
|
11941
11996
|
|
|
11942
11997
|
// app/lib/review-detector/evaluator.ts
|
|
11943
11998
|
var SAMPLE_MAX_CHARS = 500;
|
|
@@ -12231,14 +12286,14 @@ async function runScanCycle(runtime) {
|
|
|
12231
12286
|
match2 = result.match;
|
|
12232
12287
|
}
|
|
12233
12288
|
} else if (rule.type === "file-write-storm") {
|
|
12234
|
-
const dir =
|
|
12289
|
+
const dir = resolve11(runtime.configDir, rule.watchPath ?? "");
|
|
12235
12290
|
const sinceMs = cycleStart - rule.thresholdWindowMinutes * 6e4;
|
|
12236
12291
|
const count = countRecentWrites(dir, sinceMs);
|
|
12237
12292
|
const result = evaluateFileWriteStormRule(rule, count, state, cycleStart);
|
|
12238
12293
|
state = result.state;
|
|
12239
12294
|
match2 = result.match;
|
|
12240
12295
|
} else if (rule.type === "stale-log") {
|
|
12241
|
-
const trackedPath =
|
|
12296
|
+
const trackedPath = resolve11(runtime.configDir, rule.watchPath ?? "");
|
|
12242
12297
|
const lastMs = fileLastWriteMs(trackedPath);
|
|
12243
12298
|
const result = evaluateStaleLogRule(rule, lastMs, state, cycleStart);
|
|
12244
12299
|
state = result.state;
|
|
@@ -12478,10 +12533,10 @@ var WhatsAppConfigSchema = z.object({
|
|
|
12478
12533
|
|
|
12479
12534
|
// app/lib/whatsapp/config-persist.ts
|
|
12480
12535
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, existsSync as existsSync10 } from "fs";
|
|
12481
|
-
import { resolve as
|
|
12536
|
+
import { resolve as resolve12, join as join6 } from "path";
|
|
12482
12537
|
var TAG2 = "[whatsapp:config]";
|
|
12483
12538
|
function configPath(accountDir) {
|
|
12484
|
-
return
|
|
12539
|
+
return resolve12(accountDir, "account.json");
|
|
12485
12540
|
}
|
|
12486
12541
|
function readConfig(accountDir) {
|
|
12487
12542
|
const path2 = configPath(accountDir);
|
|
@@ -12936,7 +12991,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
12936
12991
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
12937
12992
|
console.error(`${TAG4} draining credential save queue\u2026`);
|
|
12938
12993
|
const timer = new Promise(
|
|
12939
|
-
(
|
|
12994
|
+
(resolve30) => setTimeout(() => resolve30("timeout"), timeoutMs)
|
|
12940
12995
|
);
|
|
12941
12996
|
const result = await Promise.race([
|
|
12942
12997
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -13064,11 +13119,11 @@ async function createWaSocket(opts) {
|
|
|
13064
13119
|
return sock;
|
|
13065
13120
|
}
|
|
13066
13121
|
async function waitForConnection(sock) {
|
|
13067
|
-
return new Promise((
|
|
13122
|
+
return new Promise((resolve30, reject) => {
|
|
13068
13123
|
const handler = (update) => {
|
|
13069
13124
|
if (update.connection === "open") {
|
|
13070
13125
|
sock.ev.off("connection.update", handler);
|
|
13071
|
-
|
|
13126
|
+
resolve30();
|
|
13072
13127
|
}
|
|
13073
13128
|
if (update.connection === "close") {
|
|
13074
13129
|
sock.ev.off("connection.update", handler);
|
|
@@ -13182,14 +13237,14 @@ ${inspected}`;
|
|
|
13182
13237
|
return inspect2(err, INSPECT_OPTS2);
|
|
13183
13238
|
}
|
|
13184
13239
|
function withTimeout(label, promise, timeoutMs) {
|
|
13185
|
-
return new Promise((
|
|
13240
|
+
return new Promise((resolve30, reject) => {
|
|
13186
13241
|
const timer = setTimeout(() => {
|
|
13187
13242
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
13188
13243
|
}, timeoutMs);
|
|
13189
13244
|
promise.then(
|
|
13190
13245
|
(value) => {
|
|
13191
13246
|
clearTimeout(timer);
|
|
13192
|
-
|
|
13247
|
+
resolve30(value);
|
|
13193
13248
|
},
|
|
13194
13249
|
(err) => {
|
|
13195
13250
|
clearTimeout(timer);
|
|
@@ -14389,11 +14444,11 @@ async function connectWithReconnect(conn) {
|
|
|
14389
14444
|
console.error(
|
|
14390
14445
|
`${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
14391
14446
|
);
|
|
14392
|
-
await new Promise((
|
|
14393
|
-
const timer = setTimeout(
|
|
14447
|
+
await new Promise((resolve30) => {
|
|
14448
|
+
const timer = setTimeout(resolve30, delay);
|
|
14394
14449
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
14395
14450
|
clearTimeout(timer);
|
|
14396
|
-
|
|
14451
|
+
resolve30();
|
|
14397
14452
|
}, { once: true });
|
|
14398
14453
|
});
|
|
14399
14454
|
}
|
|
@@ -14401,16 +14456,16 @@ async function connectWithReconnect(conn) {
|
|
|
14401
14456
|
}
|
|
14402
14457
|
}
|
|
14403
14458
|
function waitForDisconnectEvent(conn) {
|
|
14404
|
-
return new Promise((
|
|
14459
|
+
return new Promise((resolve30) => {
|
|
14405
14460
|
if (!conn.sock) {
|
|
14406
|
-
|
|
14461
|
+
resolve30();
|
|
14407
14462
|
return;
|
|
14408
14463
|
}
|
|
14409
14464
|
const sock = conn.sock;
|
|
14410
14465
|
const handler = (update) => {
|
|
14411
14466
|
if (update.connection === "close") {
|
|
14412
14467
|
sock.ev.off("connection.update", handler);
|
|
14413
|
-
|
|
14468
|
+
resolve30();
|
|
14414
14469
|
}
|
|
14415
14470
|
};
|
|
14416
14471
|
sock.ev.on("connection.update", handler);
|
|
@@ -14620,8 +14675,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
14620
14675
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
14621
14676
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
14622
14677
|
let resolvePending;
|
|
14623
|
-
const sttPending = new Promise((
|
|
14624
|
-
resolvePending =
|
|
14678
|
+
const sttPending = new Promise((resolve30) => {
|
|
14679
|
+
resolvePending = resolve30;
|
|
14625
14680
|
});
|
|
14626
14681
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
14627
14682
|
try {
|
|
@@ -14728,20 +14783,20 @@ async function probeApiKey() {
|
|
|
14728
14783
|
return result.status;
|
|
14729
14784
|
}
|
|
14730
14785
|
function checkPort(port2, timeoutMs = 500) {
|
|
14731
|
-
return new Promise((
|
|
14786
|
+
return new Promise((resolve30) => {
|
|
14732
14787
|
const socket = createConnection4(port2, "127.0.0.1");
|
|
14733
14788
|
socket.setTimeout(timeoutMs);
|
|
14734
14789
|
socket.once("connect", () => {
|
|
14735
14790
|
socket.destroy();
|
|
14736
|
-
|
|
14791
|
+
resolve30(true);
|
|
14737
14792
|
});
|
|
14738
14793
|
socket.once("error", () => {
|
|
14739
14794
|
socket.destroy();
|
|
14740
|
-
|
|
14795
|
+
resolve30(false);
|
|
14741
14796
|
});
|
|
14742
14797
|
socket.once("timeout", () => {
|
|
14743
14798
|
socket.destroy();
|
|
14744
|
-
|
|
14799
|
+
resolve30(false);
|
|
14745
14800
|
});
|
|
14746
14801
|
});
|
|
14747
14802
|
}
|
|
@@ -14838,14 +14893,14 @@ app.get("/", async (c) => {
|
|
|
14838
14893
|
var health_default = app;
|
|
14839
14894
|
|
|
14840
14895
|
// server/routes/session.ts
|
|
14841
|
-
import { resolve as
|
|
14842
|
-
import { existsSync as existsSync12, writeFileSync as writeFileSync10, mkdirSync as
|
|
14896
|
+
import { resolve as resolve13 } from "path";
|
|
14897
|
+
import { existsSync as existsSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9 } from "fs";
|
|
14843
14898
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14844
14899
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
14845
14900
|
try {
|
|
14846
|
-
const cacheDir =
|
|
14847
|
-
|
|
14848
|
-
writeFileSync10(
|
|
14901
|
+
const cacheDir = resolve13(MAXY_DIR, "branding-cache", accountId);
|
|
14902
|
+
mkdirSync9(cacheDir, { recursive: true });
|
|
14903
|
+
writeFileSync10(resolve13(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
14849
14904
|
} catch (err) {
|
|
14850
14905
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
14851
14906
|
}
|
|
@@ -14915,8 +14970,8 @@ app2.post("/", async (c) => {
|
|
|
14915
14970
|
}
|
|
14916
14971
|
let agentConfig = null;
|
|
14917
14972
|
if (account) {
|
|
14918
|
-
const agentDir =
|
|
14919
|
-
const agentConfigPath =
|
|
14973
|
+
const agentDir = resolve13(account.accountDir, "agents", agentSlug);
|
|
14974
|
+
const agentConfigPath = resolve13(agentDir, "config.json");
|
|
14920
14975
|
if (!existsSync12(agentDir) || !existsSync12(agentConfigPath)) {
|
|
14921
14976
|
return c.json({ error: "Agent not found" }, 404);
|
|
14922
14977
|
}
|
|
@@ -15163,9 +15218,9 @@ ${raw2}`;
|
|
|
15163
15218
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
15164
15219
|
import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
15165
15220
|
import { realpathSync } from "fs";
|
|
15166
|
-
import { resolve as
|
|
15167
|
-
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ??
|
|
15168
|
-
var ATTACHMENTS_ROOT =
|
|
15221
|
+
import { resolve as resolve14, extname, basename as basename3 } from "path";
|
|
15222
|
+
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve14(process.cwd(), "../platform");
|
|
15223
|
+
var ATTACHMENTS_ROOT = resolve14(PLATFORM_ROOT5, "..", "data/uploads");
|
|
15169
15224
|
var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
15170
15225
|
"image/jpeg",
|
|
15171
15226
|
"image/png",
|
|
@@ -15189,11 +15244,11 @@ function assertSupportedMime(mimeType) {
|
|
|
15189
15244
|
}
|
|
15190
15245
|
async function writeAttachment(scope, filename, mimeType, sizeBytes, buffer) {
|
|
15191
15246
|
const attachmentId = randomUUID6();
|
|
15192
|
-
const dir =
|
|
15247
|
+
const dir = resolve14(ATTACHMENTS_ROOT, scope, attachmentId);
|
|
15193
15248
|
await mkdir2(dir, { recursive: true });
|
|
15194
15249
|
const ext = extname(filename) || "";
|
|
15195
|
-
const storagePath =
|
|
15196
|
-
const metaPath =
|
|
15250
|
+
const storagePath = resolve14(dir, `${attachmentId}${ext}`);
|
|
15251
|
+
const metaPath = resolve14(dir, `${attachmentId}.meta.json`);
|
|
15197
15252
|
const meta = {
|
|
15198
15253
|
attachmentId,
|
|
15199
15254
|
scope,
|
|
@@ -15871,13 +15926,13 @@ var group_default = app4;
|
|
|
15871
15926
|
// app/lib/access-gate.ts
|
|
15872
15927
|
import neo4j2 from "neo4j-driver";
|
|
15873
15928
|
import { readFileSync as readFileSync13 } from "fs";
|
|
15874
|
-
import { resolve as
|
|
15929
|
+
import { resolve as resolve15 } from "path";
|
|
15875
15930
|
import { randomUUID as randomUUID7, randomInt } from "crypto";
|
|
15876
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ??
|
|
15931
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve15(process.cwd(), "..");
|
|
15877
15932
|
var driver2 = null;
|
|
15878
15933
|
function readPassword2() {
|
|
15879
15934
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
15880
|
-
const passwordFile =
|
|
15935
|
+
const passwordFile = resolve15(PLATFORM_ROOT6, "config/.neo4j-password");
|
|
15881
15936
|
try {
|
|
15882
15937
|
return readFileSync13(passwordFile, "utf-8").trim();
|
|
15883
15938
|
} catch {
|
|
@@ -16190,17 +16245,17 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
|
|
|
16190
16245
|
}
|
|
16191
16246
|
|
|
16192
16247
|
// app/lib/brevo-sms.ts
|
|
16193
|
-
import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, mkdirSync as
|
|
16248
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync13, chmodSync } from "fs";
|
|
16194
16249
|
import { dirname as dirname5 } from "path";
|
|
16195
|
-
import { resolve as
|
|
16196
|
-
var BREVO_API_KEY_FILE =
|
|
16250
|
+
import { resolve as resolve16 } from "path";
|
|
16251
|
+
var BREVO_API_KEY_FILE = resolve16(MAXY_DIR, ".brevo-api-key");
|
|
16197
16252
|
var BREVO_API_URL = "https://api.brevo.com/v3/transactionalSMS/sms";
|
|
16198
16253
|
var BREVO_TIMEOUT_MS = 1e4;
|
|
16199
16254
|
var BREVO_SENDER = "Maxy";
|
|
16200
16255
|
var platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
|
|
16201
16256
|
if (platformRoot2) {
|
|
16202
16257
|
try {
|
|
16203
|
-
const brandPath =
|
|
16258
|
+
const brandPath = resolve16(platformRoot2, "config", "brand.json");
|
|
16204
16259
|
if (existsSync13(brandPath)) {
|
|
16205
16260
|
const brand = JSON.parse(readFileSync14(brandPath, "utf-8"));
|
|
16206
16261
|
if (brand.productName) BREVO_SENDER = brand.productName;
|
|
@@ -16837,7 +16892,7 @@ app6.post("/webhook", async (c) => {
|
|
|
16837
16892
|
var telegram_default = app6;
|
|
16838
16893
|
|
|
16839
16894
|
// server/routes/whatsapp.ts
|
|
16840
|
-
import { join as join9, resolve as
|
|
16895
|
+
import { join as join9, resolve as resolve17, basename as basename4 } from "path";
|
|
16841
16896
|
import { readFile as readFile2, stat as stat3 } from "fs/promises";
|
|
16842
16897
|
import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync16, existsSync as existsSync15 } from "fs";
|
|
16843
16898
|
|
|
@@ -16945,8 +17000,8 @@ async function startLogin(opts) {
|
|
|
16945
17000
|
resetActiveLogin(accountId);
|
|
16946
17001
|
let resolveQr = null;
|
|
16947
17002
|
let rejectQr = null;
|
|
16948
|
-
const qrPromise = new Promise((
|
|
16949
|
-
resolveQr =
|
|
17003
|
+
const qrPromise = new Promise((resolve30, reject) => {
|
|
17004
|
+
resolveQr = resolve30;
|
|
16950
17005
|
rejectQr = reject;
|
|
16951
17006
|
});
|
|
16952
17007
|
const qrTimer = setTimeout(
|
|
@@ -17317,14 +17372,14 @@ app7.post("/config", async (c) => {
|
|
|
17317
17372
|
return c.json({ ok: true, slug: currentSlug });
|
|
17318
17373
|
}
|
|
17319
17374
|
case "list-public-agents": {
|
|
17320
|
-
const agentsDir =
|
|
17375
|
+
const agentsDir = resolve17(account.accountDir, "agents");
|
|
17321
17376
|
const agents = [];
|
|
17322
17377
|
if (existsSync15(agentsDir)) {
|
|
17323
17378
|
try {
|
|
17324
17379
|
const entries = readdirSync4(agentsDir, { withFileTypes: true });
|
|
17325
17380
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
17326
17381
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
17327
|
-
const configPath2 =
|
|
17382
|
+
const configPath2 = resolve17(agentsDir, entry.name, "config.json");
|
|
17328
17383
|
if (!existsSync15(configPath2)) continue;
|
|
17329
17384
|
try {
|
|
17330
17385
|
const config = JSON.parse(readFileSync16(configPath2, "utf-8"));
|
|
@@ -17402,7 +17457,7 @@ app7.post("/send-document", async (c) => {
|
|
|
17402
17457
|
if (!maxyAccountId || !PLATFORM_ROOT7) {
|
|
17403
17458
|
return c.json({ error: "Cannot validate file path: missing account or platform context" }, 400);
|
|
17404
17459
|
}
|
|
17405
|
-
const accountDir =
|
|
17460
|
+
const accountDir = resolve17(PLATFORM_ROOT7, "..", "data/accounts", maxyAccountId);
|
|
17406
17461
|
let resolvedPath;
|
|
17407
17462
|
try {
|
|
17408
17463
|
resolvedPath = realpathSync2(filePath);
|
|
@@ -17539,8 +17594,8 @@ var whatsapp_default = app7;
|
|
|
17539
17594
|
|
|
17540
17595
|
// server/routes/onboarding.ts
|
|
17541
17596
|
import { spawn as spawn3, execFileSync as execFileSync2 } from "child_process";
|
|
17542
|
-
import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync16, mkdirSync as
|
|
17543
|
-
import { resolve as
|
|
17597
|
+
import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync17, unlinkSync as unlinkSync3 } from "fs";
|
|
17598
|
+
import { resolve as resolve18, dirname as dirname6 } from "path";
|
|
17544
17599
|
import { createHash, randomUUID as randomUUID9 } from "crypto";
|
|
17545
17600
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
17546
17601
|
function hashPin(pin) {
|
|
@@ -17653,7 +17708,7 @@ app8.post("/set-pin", async (c) => {
|
|
|
17653
17708
|
}
|
|
17654
17709
|
const hash = hashPin(body.pin);
|
|
17655
17710
|
const userId = randomUUID9();
|
|
17656
|
-
|
|
17711
|
+
mkdirSync11(dirname6(USERS_FILE), { recursive: true });
|
|
17657
17712
|
writeFileSync12(USERS_FILE, JSON.stringify([{ userId, name: "Owner", pin: hash }]), { mode: 384 });
|
|
17658
17713
|
console.log(`[set-pin] created users.json: userId=${userId.slice(0, 8)}\u2026 hash=${hash.slice(0, 8)}\u2026`);
|
|
17659
17714
|
const account = resolveAccount();
|
|
@@ -17714,7 +17769,7 @@ app8.post("/skip", async (c) => {
|
|
|
17714
17769
|
}
|
|
17715
17770
|
const { accountId, accountDir } = account;
|
|
17716
17771
|
let agentName = "Maxy";
|
|
17717
|
-
const brandPath = PLATFORM_ROOT8 ?
|
|
17772
|
+
const brandPath = PLATFORM_ROOT8 ? resolve18(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
17718
17773
|
if (brandPath && existsSync16(brandPath)) {
|
|
17719
17774
|
try {
|
|
17720
17775
|
const brand = JSON.parse(readFileSync17(brandPath, "utf-8"));
|
|
@@ -17723,9 +17778,9 @@ app8.post("/skip", async (c) => {
|
|
|
17723
17778
|
console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
17724
17779
|
}
|
|
17725
17780
|
}
|
|
17726
|
-
const soulPath =
|
|
17781
|
+
const soulPath = resolve18(accountDir, "agents", "admin", "SOUL.md");
|
|
17727
17782
|
try {
|
|
17728
|
-
|
|
17783
|
+
mkdirSync11(dirname6(soulPath), { recursive: true });
|
|
17729
17784
|
writeFileSync12(soulPath, `You are ${agentName}, an AI operations manager.
|
|
17730
17785
|
`);
|
|
17731
17786
|
console.log(`[onboarding-skip] wrote SOUL.md: ${soulPath}`);
|
|
@@ -17764,7 +17819,7 @@ app8.post("/skip", async (c) => {
|
|
|
17764
17819
|
var onboarding_default = app8;
|
|
17765
17820
|
|
|
17766
17821
|
// server/routes/client-error.ts
|
|
17767
|
-
import { appendFileSync as
|
|
17822
|
+
import { appendFileSync as appendFileSync5, existsSync as existsSync17, renameSync as renameSync4, statSync as statSync7 } from "fs";
|
|
17768
17823
|
import { join as join10 } from "path";
|
|
17769
17824
|
var CLIENT_ERRORS_LOG = join10(LOG_DIR, "client-errors.log");
|
|
17770
17825
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
@@ -17892,7 +17947,7 @@ app9.post("/", async (c) => {
|
|
|
17892
17947
|
tag: typeof body.tag === "string" ? truncate(body.tag, 32) : void 0,
|
|
17893
17948
|
status: typeof body.status === "number" ? body.status : void 0
|
|
17894
17949
|
};
|
|
17895
|
-
|
|
17950
|
+
appendFileSync5(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
|
|
17896
17951
|
} catch (err) {
|
|
17897
17952
|
console.error(`[client-error] append failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
17898
17953
|
}
|
|
@@ -18004,12 +18059,14 @@ app10.post("/", async (c) => {
|
|
|
18004
18059
|
var session_default2 = app10;
|
|
18005
18060
|
|
|
18006
18061
|
// server/routes/admin/chat.ts
|
|
18007
|
-
import { resolve as
|
|
18062
|
+
import { resolve as resolve19 } from "path";
|
|
18008
18063
|
|
|
18009
18064
|
// app/lib/script-stream-tailer.ts
|
|
18010
|
-
import
|
|
18065
|
+
import * as childProcess from "child_process";
|
|
18066
|
+
import { appendFileSync as appendFileSync6, createReadStream as createReadStream2, mkdirSync as mkdirSync12, statSync as statSync8 } from "fs";
|
|
18067
|
+
import { dirname as dirname7 } from "path";
|
|
18011
18068
|
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
18012
|
-
var SCRIPT_STREAM_RE = /^\[([^\]]+)\] \[(
|
|
18069
|
+
var SCRIPT_STREAM_RE = /^\[([^\]]+)\] \[([a-z][a-z0-9-]*)((?::[a-z0-9:_-]+)?)\] (.*)$/;
|
|
18013
18070
|
function parseLine(line) {
|
|
18014
18071
|
const m = line.match(SCRIPT_STREAM_RE);
|
|
18015
18072
|
if (!m) return void 0;
|
|
@@ -18102,6 +18159,106 @@ function startScriptStreamTailer(opts) {
|
|
|
18102
18159
|
}
|
|
18103
18160
|
};
|
|
18104
18161
|
}
|
|
18162
|
+
var SCOPE_TOKEN_RE = /^[a-z][a-z0-9-]*$/;
|
|
18163
|
+
function writeRouteMilestone(streamLogPath, scope, line) {
|
|
18164
|
+
if (!SCOPE_TOKEN_RE.test(scope)) {
|
|
18165
|
+
throw new Error(
|
|
18166
|
+
`writeRouteMilestone: scope "${scope}" must match [a-z][a-z0-9-]* \u2014 the tailer regex won't match otherwise`
|
|
18167
|
+
);
|
|
18168
|
+
}
|
|
18169
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
18170
|
+
try {
|
|
18171
|
+
mkdirSync12(dirname7(streamLogPath), { recursive: true });
|
|
18172
|
+
appendFileSync6(streamLogPath, `[${ts}] [${scope}] ${line}
|
|
18173
|
+
`);
|
|
18174
|
+
} catch (err) {
|
|
18175
|
+
console.error(
|
|
18176
|
+
`[script-stream-tailer] writeRouteMilestone failed path=${streamLogPath} scope=${scope}: ${err instanceof Error ? err.message : String(err)}`
|
|
18177
|
+
);
|
|
18178
|
+
}
|
|
18179
|
+
}
|
|
18180
|
+
function runFormSpawn(opts) {
|
|
18181
|
+
const { scriptPath, args, streamLogPath, correlationId, timeoutMs, log, logErr, broadcast } = opts;
|
|
18182
|
+
return new Promise((resolveP) => {
|
|
18183
|
+
let tailer = null;
|
|
18184
|
+
try {
|
|
18185
|
+
tailer = startScriptStreamTailer({
|
|
18186
|
+
path: streamLogPath,
|
|
18187
|
+
onEvent: (event) => {
|
|
18188
|
+
try {
|
|
18189
|
+
broadcast(correlationId, event);
|
|
18190
|
+
} catch (e) {
|
|
18191
|
+
logErr(
|
|
18192
|
+
`phase=tailer-broadcast-failed detail="${e instanceof Error ? e.message : String(e)}"`
|
|
18193
|
+
);
|
|
18194
|
+
}
|
|
18195
|
+
},
|
|
18196
|
+
onError: (e) => {
|
|
18197
|
+
logErr(`phase=tailer-error message="${e.message.slice(0, 160).replace(/"/g, "'")}"`);
|
|
18198
|
+
}
|
|
18199
|
+
});
|
|
18200
|
+
log(`phase=tailer-started`);
|
|
18201
|
+
} catch (e) {
|
|
18202
|
+
logErr(`phase=tailer-start-failed detail="${e instanceof Error ? e.message : String(e)}"`);
|
|
18203
|
+
tailer = null;
|
|
18204
|
+
}
|
|
18205
|
+
const child = childProcess.spawn(scriptPath, args, {
|
|
18206
|
+
env: { ...process.env, STREAM_LOG_PATH: streamLogPath },
|
|
18207
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
18208
|
+
});
|
|
18209
|
+
log(`phase=script-spawn pid=${child.pid ?? "unknown"} args="${args.join(" ")}"`);
|
|
18210
|
+
let stdout = "";
|
|
18211
|
+
let stderr = "";
|
|
18212
|
+
let timedOut = false;
|
|
18213
|
+
const killer = setTimeout(() => {
|
|
18214
|
+
timedOut = true;
|
|
18215
|
+
try {
|
|
18216
|
+
child.kill("SIGTERM");
|
|
18217
|
+
} catch {
|
|
18218
|
+
}
|
|
18219
|
+
}, timeoutMs);
|
|
18220
|
+
child.stdout?.on("data", (chunk) => {
|
|
18221
|
+
stdout += chunk.toString("utf-8");
|
|
18222
|
+
});
|
|
18223
|
+
child.stderr?.on("data", (chunk) => {
|
|
18224
|
+
stderr += chunk.toString("utf-8");
|
|
18225
|
+
});
|
|
18226
|
+
let finalized = false;
|
|
18227
|
+
const finalize = async (result) => {
|
|
18228
|
+
if (finalized) return;
|
|
18229
|
+
finalized = true;
|
|
18230
|
+
if (tailer) {
|
|
18231
|
+
try {
|
|
18232
|
+
await tailer.stop();
|
|
18233
|
+
log(`phase=tailer-stopped`);
|
|
18234
|
+
} catch (e) {
|
|
18235
|
+
logErr(
|
|
18236
|
+
`phase=tailer-stop-failed detail="${e instanceof Error ? e.message : String(e)}"`
|
|
18237
|
+
);
|
|
18238
|
+
}
|
|
18239
|
+
}
|
|
18240
|
+
resolveP(result);
|
|
18241
|
+
};
|
|
18242
|
+
child.on("error", (e) => {
|
|
18243
|
+
clearTimeout(killer);
|
|
18244
|
+
void finalize({
|
|
18245
|
+
code: null,
|
|
18246
|
+
signal: null,
|
|
18247
|
+
stdout,
|
|
18248
|
+
stderr: `${stderr}
|
|
18249
|
+
${e.message}`,
|
|
18250
|
+
timedOut: false
|
|
18251
|
+
});
|
|
18252
|
+
});
|
|
18253
|
+
child.on("exit", (code, signal) => {
|
|
18254
|
+
clearTimeout(killer);
|
|
18255
|
+
log(
|
|
18256
|
+
`phase=script-exit code=${code ?? "null"} signal=${signal ?? "null"} timedOut=${timedOut}`
|
|
18257
|
+
);
|
|
18258
|
+
void finalize({ code, signal, stdout, stderr, timedOut });
|
|
18259
|
+
});
|
|
18260
|
+
});
|
|
18261
|
+
}
|
|
18105
18262
|
|
|
18106
18263
|
// app/lib/admin-sse-registry.ts
|
|
18107
18264
|
var activeAdminSSEControllers = /* @__PURE__ */ new Set();
|
|
@@ -18342,7 +18499,7 @@ app11.post("/", requireAdminSession, async (c) => {
|
|
|
18342
18499
|
try {
|
|
18343
18500
|
registerAdminSSE(sseEntry);
|
|
18344
18501
|
if (sseConvId) {
|
|
18345
|
-
const streamLogPath =
|
|
18502
|
+
const streamLogPath = resolve19(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
|
|
18346
18503
|
tailer = startScriptStreamTailer({
|
|
18347
18504
|
path: streamLogPath,
|
|
18348
18505
|
onEvent: (event) => {
|
|
@@ -18468,7 +18625,7 @@ var compact_default = app12;
|
|
|
18468
18625
|
|
|
18469
18626
|
// server/routes/admin/logs.ts
|
|
18470
18627
|
import { existsSync as existsSync19, readdirSync as readdirSync5, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
|
|
18471
|
-
import { resolve as
|
|
18628
|
+
import { resolve as resolve20, basename as basename5 } from "path";
|
|
18472
18629
|
var TAIL_BYTES = 8192;
|
|
18473
18630
|
var app13 = new Hono2();
|
|
18474
18631
|
app13.get("/", async (c) => {
|
|
@@ -18477,13 +18634,13 @@ app13.get("/", async (c) => {
|
|
|
18477
18634
|
const conversationIdParam = c.req.query("conversationId");
|
|
18478
18635
|
const download = c.req.query("download") === "1";
|
|
18479
18636
|
const account = resolveAccount();
|
|
18480
|
-
const accountLogDir2 = account ?
|
|
18637
|
+
const accountLogDir2 = account ? resolve20(account.accountDir, "logs") : null;
|
|
18481
18638
|
if (fileParam) {
|
|
18482
18639
|
const safe = basename5(fileParam);
|
|
18483
18640
|
const searched = [];
|
|
18484
18641
|
for (const dir of [accountLogDir2, LOG_DIR]) {
|
|
18485
18642
|
if (!dir) continue;
|
|
18486
|
-
const filePath =
|
|
18643
|
+
const filePath = resolve20(dir, safe);
|
|
18487
18644
|
searched.push(filePath);
|
|
18488
18645
|
try {
|
|
18489
18646
|
const content = readFileSync19(filePath, "utf-8");
|
|
@@ -18525,7 +18682,7 @@ app13.get("/", async (c) => {
|
|
|
18525
18682
|
const searched = [];
|
|
18526
18683
|
for (const dir of [accountLogDir2, LOG_DIR]) {
|
|
18527
18684
|
if (!dir) continue;
|
|
18528
|
-
const filePath =
|
|
18685
|
+
const filePath = resolve20(dir, fileName);
|
|
18529
18686
|
searched.push(filePath);
|
|
18530
18687
|
try {
|
|
18531
18688
|
const content = readFileSync19(filePath, "utf-8");
|
|
@@ -18552,10 +18709,10 @@ app13.get("/", async (c) => {
|
|
|
18552
18709
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
18553
18710
|
continue;
|
|
18554
18711
|
}
|
|
18555
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(
|
|
18712
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve20(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
18556
18713
|
seen.add(name);
|
|
18557
18714
|
try {
|
|
18558
|
-
const content = readFileSync19(
|
|
18715
|
+
const content = readFileSync19(resolve20(dir, name));
|
|
18559
18716
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
18560
18717
|
logs[name] = tail.trim() || "(empty)";
|
|
18561
18718
|
} catch (err) {
|
|
@@ -18596,7 +18753,7 @@ var claude_info_default = app14;
|
|
|
18596
18753
|
// server/routes/admin/attachment.ts
|
|
18597
18754
|
import { readFile as readFile3, readdir } from "fs/promises";
|
|
18598
18755
|
import { existsSync as existsSync20 } from "fs";
|
|
18599
|
-
import { resolve as
|
|
18756
|
+
import { resolve as resolve21 } from "path";
|
|
18600
18757
|
var app15 = new Hono2();
|
|
18601
18758
|
app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
18602
18759
|
const attachmentId = c.req.param("attachmentId");
|
|
@@ -18608,11 +18765,11 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
18608
18765
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
|
|
18609
18766
|
return new Response("Not found", { status: 404 });
|
|
18610
18767
|
}
|
|
18611
|
-
const dir =
|
|
18768
|
+
const dir = resolve21(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
18612
18769
|
if (!existsSync20(dir)) {
|
|
18613
18770
|
return new Response("Not found", { status: 404 });
|
|
18614
18771
|
}
|
|
18615
|
-
const metaPath =
|
|
18772
|
+
const metaPath = resolve21(dir, `${attachmentId}.meta.json`);
|
|
18616
18773
|
if (!existsSync20(metaPath)) {
|
|
18617
18774
|
return new Response("Not found", { status: 404 });
|
|
18618
18775
|
}
|
|
@@ -18627,7 +18784,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
18627
18784
|
if (!dataFile) {
|
|
18628
18785
|
return new Response("Not found", { status: 404 });
|
|
18629
18786
|
}
|
|
18630
|
-
const filePath =
|
|
18787
|
+
const filePath = resolve21(dir, dataFile);
|
|
18631
18788
|
const buffer = await readFile3(filePath);
|
|
18632
18789
|
return new Response(new Uint8Array(buffer), {
|
|
18633
18790
|
headers: {
|
|
@@ -18641,7 +18798,7 @@ var attachment_default = app15;
|
|
|
18641
18798
|
|
|
18642
18799
|
// server/routes/admin/account.ts
|
|
18643
18800
|
import { readFileSync as readFileSync20, writeFileSync as writeFileSync13 } from "fs";
|
|
18644
|
-
import { resolve as
|
|
18801
|
+
import { resolve as resolve22 } from "path";
|
|
18645
18802
|
var VALID_CONTEXT_MODES = ["managed", "claude-code"];
|
|
18646
18803
|
var app16 = new Hono2();
|
|
18647
18804
|
app16.patch("/", requireAdminSession, async (c) => {
|
|
@@ -18657,7 +18814,7 @@ app16.patch("/", requireAdminSession, async (c) => {
|
|
|
18657
18814
|
}
|
|
18658
18815
|
const account = resolveAccount();
|
|
18659
18816
|
if (!account) return c.json({ error: "No account configured" }, 500);
|
|
18660
|
-
const configPath2 =
|
|
18817
|
+
const configPath2 = resolve22(account.accountDir, "account.json");
|
|
18661
18818
|
try {
|
|
18662
18819
|
const raw2 = readFileSync20(configPath2, "utf-8");
|
|
18663
18820
|
const config = JSON.parse(raw2);
|
|
@@ -18673,13 +18830,13 @@ app16.patch("/", requireAdminSession, async (c) => {
|
|
|
18673
18830
|
var account_default = app16;
|
|
18674
18831
|
|
|
18675
18832
|
// server/routes/admin/agents.ts
|
|
18676
|
-
import { resolve as
|
|
18833
|
+
import { resolve as resolve23 } from "path";
|
|
18677
18834
|
import { readdirSync as readdirSync6, readFileSync as readFileSync21, existsSync as existsSync21, rmSync as rmSync3 } from "fs";
|
|
18678
18835
|
var app17 = new Hono2();
|
|
18679
18836
|
app17.get("/", (c) => {
|
|
18680
18837
|
const account = resolveAccount();
|
|
18681
18838
|
if (!account) return c.json({ agents: [] });
|
|
18682
|
-
const agentsDir =
|
|
18839
|
+
const agentsDir = resolve23(account.accountDir, "agents");
|
|
18683
18840
|
if (!existsSync21(agentsDir)) return c.json({ agents: [] });
|
|
18684
18841
|
const agents = [];
|
|
18685
18842
|
try {
|
|
@@ -18687,7 +18844,7 @@ app17.get("/", (c) => {
|
|
|
18687
18844
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
18688
18845
|
if (!entry.isDirectory()) continue;
|
|
18689
18846
|
if (entry.name === "admin") continue;
|
|
18690
|
-
const configPath2 =
|
|
18847
|
+
const configPath2 = resolve23(agentsDir, entry.name, "config.json");
|
|
18691
18848
|
if (!existsSync21(configPath2)) continue;
|
|
18692
18849
|
try {
|
|
18693
18850
|
const config = JSON.parse(readFileSync21(configPath2, "utf-8"));
|
|
@@ -18716,7 +18873,7 @@ app17.delete("/:slug", (c) => {
|
|
|
18716
18873
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
18717
18874
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
18718
18875
|
}
|
|
18719
|
-
const agentDir =
|
|
18876
|
+
const agentDir = resolve23(account.accountDir, "agents", slug);
|
|
18720
18877
|
if (!existsSync21(agentDir)) {
|
|
18721
18878
|
return c.json({ error: "Agent not found" }, 404);
|
|
18722
18879
|
}
|
|
@@ -18732,74 +18889,27 @@ app17.delete("/:slug", (c) => {
|
|
|
18732
18889
|
var agents_default = app17;
|
|
18733
18890
|
|
|
18734
18891
|
// server/routes/admin/version.ts
|
|
18735
|
-
import { spawn as spawn4 } from "child_process";
|
|
18736
|
-
import { existsSync as existsSync23, statSync as statSync10, writeFileSync as writeFileSync14, readFileSync as readFileSync23, openSync as openSync5, closeSync as closeSync5 } from "fs";
|
|
18737
|
-
import { resolve as resolve23, join as join11 } from "path";
|
|
18738
|
-
|
|
18739
|
-
// app/lib/upgrade-progress-parser.ts
|
|
18740
18892
|
import { existsSync as existsSync22, readFileSync as readFileSync22 } from "fs";
|
|
18741
|
-
|
|
18742
|
-
var
|
|
18743
|
-
var stripAnsi = (s) => s.replace(ANSI_RE, "").replace(/\r/g, "").trimEnd();
|
|
18744
|
-
var MAX_SUBSTEPS = 20;
|
|
18745
|
-
function parseUpgradeProgress(content) {
|
|
18746
|
-
const rawLines = content.split("\n");
|
|
18747
|
-
let step = 0;
|
|
18748
|
-
let total = 0;
|
|
18749
|
-
let label = "";
|
|
18750
|
-
let markerIdx = -1;
|
|
18751
|
-
for (let i = rawLines.length - 1; i >= 0; i--) {
|
|
18752
|
-
const match2 = rawLines[i].match(STEP_RE);
|
|
18753
|
-
if (match2) {
|
|
18754
|
-
step = parseInt(match2[1], 10);
|
|
18755
|
-
total = parseInt(match2[2], 10);
|
|
18756
|
-
label = match2[3].replace(/\.{3}$/, "").trim();
|
|
18757
|
-
markerIdx = i;
|
|
18758
|
-
break;
|
|
18759
|
-
}
|
|
18760
|
-
}
|
|
18761
|
-
const subSteps = markerIdx >= 0 ? rawLines.slice(markerIdx + 1).map(stripAnsi).filter((l) => l.length > 0).slice(-MAX_SUBSTEPS) : [];
|
|
18762
|
-
const tail = rawLines.slice(-20).join("\n");
|
|
18763
|
-
const finished = tail.includes("Open in your browser:");
|
|
18764
|
-
const failed = tail.includes("Setup failed:");
|
|
18765
|
-
return { step, total, label, started: true, finished, failed, subSteps };
|
|
18766
|
-
}
|
|
18767
|
-
function readProgressLog(path2) {
|
|
18768
|
-
if (!existsSync22(path2)) {
|
|
18769
|
-
return { step: 0, total: 0, label: "", started: false, subSteps: [] };
|
|
18770
|
-
}
|
|
18771
|
-
let content;
|
|
18772
|
-
try {
|
|
18773
|
-
content = readFileSync22(path2, "utf-8");
|
|
18774
|
-
} catch {
|
|
18775
|
-
return { step: 0, total: 0, label: "", started: false, subSteps: [] };
|
|
18776
|
-
}
|
|
18777
|
-
return parseUpgradeProgress(content);
|
|
18778
|
-
}
|
|
18779
|
-
|
|
18780
|
-
// server/routes/admin/version.ts
|
|
18781
|
-
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve23(process.cwd(), "..");
|
|
18893
|
+
import { resolve as resolve24, join as join11 } from "path";
|
|
18894
|
+
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
|
|
18782
18895
|
var brandHostname = "maxy";
|
|
18783
18896
|
var brandNpmPackage = "@rubytech/create-maxy";
|
|
18784
18897
|
var brandJsonPath = join11(PLATFORM_ROOT9, "config", "brand.json");
|
|
18785
|
-
if (
|
|
18898
|
+
if (existsSync22(brandJsonPath)) {
|
|
18786
18899
|
try {
|
|
18787
|
-
const brand = JSON.parse(
|
|
18900
|
+
const brand = JSON.parse(readFileSync22(brandJsonPath, "utf-8"));
|
|
18788
18901
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
18789
18902
|
if (brand.npm?.packageName) brandNpmPackage = brand.npm.packageName;
|
|
18790
18903
|
} catch {
|
|
18791
18904
|
}
|
|
18792
18905
|
}
|
|
18793
|
-
var VERSION_FILE =
|
|
18906
|
+
var VERSION_FILE = resolve24(PLATFORM_ROOT9, `config/.${brandHostname}-version`);
|
|
18794
18907
|
var NPM_PACKAGE = brandNpmPackage;
|
|
18795
18908
|
var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
|
|
18796
18909
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
18797
|
-
var LOCK_FILE = `/tmp/${brandHostname}-upgrade.lock`;
|
|
18798
|
-
var LOG_FILE = `/tmp/${brandHostname}-upgrade.log`;
|
|
18799
|
-
var LOCK_MAX_AGE_MS = 20 * 60 * 1e3;
|
|
18800
18910
|
function readInstalled() {
|
|
18801
|
-
if (!
|
|
18802
|
-
const content =
|
|
18911
|
+
if (!existsSync22(VERSION_FILE)) return "unknown";
|
|
18912
|
+
const content = readFileSync22(VERSION_FILE, "utf-8").trim();
|
|
18803
18913
|
return content || "unknown";
|
|
18804
18914
|
}
|
|
18805
18915
|
async function fetchLatest() {
|
|
@@ -18837,23 +18947,6 @@ function isNewer(latest, installed) {
|
|
|
18837
18947
|
}
|
|
18838
18948
|
return false;
|
|
18839
18949
|
}
|
|
18840
|
-
function isLockFresh() {
|
|
18841
|
-
if (!existsSync23(LOCK_FILE)) return false;
|
|
18842
|
-
try {
|
|
18843
|
-
const st = statSync10(LOCK_FILE);
|
|
18844
|
-
if (Date.now() - st.mtimeMs >= LOCK_MAX_AGE_MS) return false;
|
|
18845
|
-
if (existsSync23(LOG_FILE)) {
|
|
18846
|
-
const content = readFileSync23(LOG_FILE, "utf-8");
|
|
18847
|
-
const tail = content.slice(-4e3);
|
|
18848
|
-
if (tail.includes("Setup failed:") || tail.includes("Open in your browser:")) {
|
|
18849
|
-
return false;
|
|
18850
|
-
}
|
|
18851
|
-
}
|
|
18852
|
-
return true;
|
|
18853
|
-
} catch {
|
|
18854
|
-
return false;
|
|
18855
|
-
}
|
|
18856
|
-
}
|
|
18857
18950
|
var app18 = new Hono2();
|
|
18858
18951
|
app18.get("/", async (c) => {
|
|
18859
18952
|
const installed = readInstalled();
|
|
@@ -18866,56 +18959,6 @@ app18.get("/", async (c) => {
|
|
|
18866
18959
|
);
|
|
18867
18960
|
return c.json({ installed, latest, updateAvailable, clientErrors24h });
|
|
18868
18961
|
});
|
|
18869
|
-
app18.post("/upgrade", requireAdminSession, (c) => {
|
|
18870
|
-
if (isLockFresh()) {
|
|
18871
|
-
return c.json({ ok: false, error: "upgrade already in progress" }, 409);
|
|
18872
|
-
}
|
|
18873
|
-
const installerScope = `upgrade-${brandHostname}`;
|
|
18874
|
-
try {
|
|
18875
|
-
writeFileSync14(LOCK_FILE, String(Date.now()));
|
|
18876
|
-
} catch (err) {
|
|
18877
|
-
console.error("[admin/version/upgrade] failed to write lock file:", err);
|
|
18878
|
-
}
|
|
18879
|
-
try {
|
|
18880
|
-
writeFileSync14(LOG_FILE, "");
|
|
18881
|
-
} catch (err) {
|
|
18882
|
-
console.error("[admin/version/upgrade] failed to truncate upgrade log:", err);
|
|
18883
|
-
return c.json(
|
|
18884
|
-
{ ok: false, error: err instanceof Error ? err.message : "failed to prepare upgrade log" },
|
|
18885
|
-
500
|
|
18886
|
-
);
|
|
18887
|
-
}
|
|
18888
|
-
try {
|
|
18889
|
-
const logFd = openSync5(LOG_FILE, "a");
|
|
18890
|
-
const child = spawn4("systemd-run", [
|
|
18891
|
-
"--user",
|
|
18892
|
-
"--scope",
|
|
18893
|
-
`--unit=${installerScope}`,
|
|
18894
|
-
"--",
|
|
18895
|
-
"npx",
|
|
18896
|
-
"-y",
|
|
18897
|
-
`${NPM_PACKAGE}@latest`
|
|
18898
|
-
], {
|
|
18899
|
-
detached: true,
|
|
18900
|
-
stdio: ["ignore", logFd, logFd],
|
|
18901
|
-
env: { ...process.env, npm_config_yes: "true" },
|
|
18902
|
-
cwd: resolve23(process.cwd(), "..")
|
|
18903
|
-
});
|
|
18904
|
-
child.unref();
|
|
18905
|
-
closeSync5(logFd);
|
|
18906
|
-
console.log(`[admin/version/upgrade] spawned upgrade process (pid ${child.pid} scope ${installerScope})`);
|
|
18907
|
-
return c.json({ ok: true, started: true });
|
|
18908
|
-
} catch (err) {
|
|
18909
|
-
console.error("[admin/version/upgrade] failed to spawn upgrade process:", err);
|
|
18910
|
-
return c.json(
|
|
18911
|
-
{ ok: false, error: err instanceof Error ? err.message : "failed to start upgrade" },
|
|
18912
|
-
500
|
|
18913
|
-
);
|
|
18914
|
-
}
|
|
18915
|
-
});
|
|
18916
|
-
app18.get("/upgrade/progress", (c) => {
|
|
18917
|
-
return c.json(readProgressLog(LOG_FILE));
|
|
18918
|
-
});
|
|
18919
18962
|
app18.post("/alert-surfaced", async (c) => {
|
|
18920
18963
|
let installed = "unknown";
|
|
18921
18964
|
let latest = null;
|
|
@@ -19299,10 +19342,9 @@ app22.post("/", async (c) => {
|
|
|
19299
19342
|
var events_default = app22;
|
|
19300
19343
|
|
|
19301
19344
|
// server/routes/admin/cloudflare.ts
|
|
19302
|
-
import * as childProcess from "child_process";
|
|
19303
19345
|
import { homedir as homedir4 } from "os";
|
|
19304
|
-
import { resolve as
|
|
19305
|
-
import { readFileSync as
|
|
19346
|
+
import { resolve as resolve26 } from "path";
|
|
19347
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
19306
19348
|
|
|
19307
19349
|
// app/lib/dns-label.ts
|
|
19308
19350
|
var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
@@ -19318,14 +19360,14 @@ function isValidDomain(value) {
|
|
|
19318
19360
|
}
|
|
19319
19361
|
|
|
19320
19362
|
// app/lib/alias-domains.ts
|
|
19321
|
-
import { existsSync as
|
|
19322
|
-
import { dirname as
|
|
19323
|
-
import { resolve as
|
|
19324
|
-
var ALIAS_DOMAINS_PATH =
|
|
19363
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync14 } from "fs";
|
|
19364
|
+
import { dirname as dirname8 } from "path";
|
|
19365
|
+
import { resolve as resolve25 } from "path";
|
|
19366
|
+
var ALIAS_DOMAINS_PATH = resolve25(MAXY_DIR, "alias-domains.json");
|
|
19325
19367
|
function readExisting() {
|
|
19326
|
-
if (!
|
|
19368
|
+
if (!existsSync23(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
|
|
19327
19369
|
try {
|
|
19328
|
-
const parsed = JSON.parse(
|
|
19370
|
+
const parsed = JSON.parse(readFileSync23(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
19329
19371
|
if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
|
|
19330
19372
|
return new Set(parsed.filter((h) => typeof h === "string"));
|
|
19331
19373
|
} catch {
|
|
@@ -19336,17 +19378,18 @@ function addAliasDomain(hostname2) {
|
|
|
19336
19378
|
const existing = readExisting();
|
|
19337
19379
|
if (existing.has(hostname2)) return;
|
|
19338
19380
|
existing.add(hostname2);
|
|
19339
|
-
|
|
19340
|
-
|
|
19381
|
+
mkdirSync13(dirname8(ALIAS_DOMAINS_PATH), { recursive: true });
|
|
19382
|
+
writeFileSync14(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
|
|
19341
19383
|
}
|
|
19342
19384
|
|
|
19343
19385
|
// server/routes/admin/cloudflare.ts
|
|
19344
|
-
var
|
|
19386
|
+
var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
19387
|
+
var DOMAINS_TIMEOUT_MS = 40 * 1e3;
|
|
19345
19388
|
function loadBrandInfo() {
|
|
19346
|
-
const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ??
|
|
19347
|
-
const brandPath =
|
|
19389
|
+
const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
|
|
19390
|
+
const brandPath = resolve26(platformRoot3, "config", "brand.json");
|
|
19348
19391
|
try {
|
|
19349
|
-
const parsed = JSON.parse(
|
|
19392
|
+
const parsed = JSON.parse(readFileSync24(brandPath, "utf-8"));
|
|
19350
19393
|
const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
|
|
19351
19394
|
const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
|
|
19352
19395
|
return { hostname: hostname2, configDir: configDir2 };
|
|
@@ -19389,78 +19432,7 @@ function validateBody(body) {
|
|
|
19389
19432
|
}
|
|
19390
19433
|
return null;
|
|
19391
19434
|
}
|
|
19392
|
-
function runScript(scriptPath, args, streamLogPath, log) {
|
|
19393
|
-
return new Promise((resolveP) => {
|
|
19394
|
-
const child = childProcess.spawn(scriptPath, args, {
|
|
19395
|
-
env: { ...process.env, STREAM_LOG_PATH: streamLogPath },
|
|
19396
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
19397
|
-
});
|
|
19398
|
-
log(`phase=script-spawn pid=${child.pid ?? "unknown"} args="${args.join(" ")}"`);
|
|
19399
|
-
let stdout = "";
|
|
19400
|
-
let stderr = "";
|
|
19401
|
-
let timedOut = false;
|
|
19402
|
-
const killer = setTimeout(() => {
|
|
19403
|
-
timedOut = true;
|
|
19404
|
-
try {
|
|
19405
|
-
child.kill("SIGTERM");
|
|
19406
|
-
} catch {
|
|
19407
|
-
}
|
|
19408
|
-
}, SCRIPT_TIMEOUT_MS);
|
|
19409
|
-
child.stdout?.on("data", (chunk) => {
|
|
19410
|
-
stdout += chunk.toString("utf-8");
|
|
19411
|
-
});
|
|
19412
|
-
child.stderr?.on("data", (chunk) => {
|
|
19413
|
-
stderr += chunk.toString("utf-8");
|
|
19414
|
-
});
|
|
19415
|
-
child.on("error", (e) => {
|
|
19416
|
-
clearTimeout(killer);
|
|
19417
|
-
resolveP({ code: null, signal: null, stdout, stderr: `${stderr}
|
|
19418
|
-
${e.message}`, timedOut: false });
|
|
19419
|
-
});
|
|
19420
|
-
child.on("exit", (code, signal) => {
|
|
19421
|
-
clearTimeout(killer);
|
|
19422
|
-
log(`phase=script-exit code=${code ?? "null"} signal=${signal ?? "null"} timedOut=${timedOut}`);
|
|
19423
|
-
resolveP({ code, signal, stdout, stderr, timedOut });
|
|
19424
|
-
});
|
|
19425
|
-
});
|
|
19426
|
-
}
|
|
19427
19435
|
var app23 = new Hono2();
|
|
19428
|
-
var DOMAINS_TIMEOUT_MS = 40 * 1e3;
|
|
19429
|
-
function runListScript(scriptPath, args, streamLogPath, log) {
|
|
19430
|
-
return new Promise((resolveP) => {
|
|
19431
|
-
const child = childProcess.spawn(scriptPath, args, {
|
|
19432
|
-
env: { ...process.env, STREAM_LOG_PATH: streamLogPath },
|
|
19433
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
19434
|
-
});
|
|
19435
|
-
log(`phase=script-spawn pid=${child.pid ?? "unknown"} args="${args.join(" ")}"`);
|
|
19436
|
-
let stdout = "";
|
|
19437
|
-
let stderr = "";
|
|
19438
|
-
let timedOut = false;
|
|
19439
|
-
const killer = setTimeout(() => {
|
|
19440
|
-
timedOut = true;
|
|
19441
|
-
try {
|
|
19442
|
-
child.kill("SIGTERM");
|
|
19443
|
-
} catch {
|
|
19444
|
-
}
|
|
19445
|
-
}, DOMAINS_TIMEOUT_MS);
|
|
19446
|
-
child.stdout?.on("data", (chunk) => {
|
|
19447
|
-
stdout += chunk.toString("utf-8");
|
|
19448
|
-
});
|
|
19449
|
-
child.stderr?.on("data", (chunk) => {
|
|
19450
|
-
stderr += chunk.toString("utf-8");
|
|
19451
|
-
});
|
|
19452
|
-
child.on("error", (e) => {
|
|
19453
|
-
clearTimeout(killer);
|
|
19454
|
-
resolveP({ code: null, signal: null, stdout, stderr: `${stderr}
|
|
19455
|
-
${e.message}`, timedOut: false });
|
|
19456
|
-
});
|
|
19457
|
-
child.on("exit", (code, signal) => {
|
|
19458
|
-
clearTimeout(killer);
|
|
19459
|
-
log(`phase=script-exit code=${code ?? "null"} signal=${signal ?? "null"} timedOut=${timedOut}`);
|
|
19460
|
-
resolveP({ code, signal, stdout, stderr, timedOut });
|
|
19461
|
-
});
|
|
19462
|
-
});
|
|
19463
|
-
}
|
|
19464
19436
|
function fieldFromReason(reason) {
|
|
19465
19437
|
switch (reason) {
|
|
19466
19438
|
case "not-signed-in":
|
|
@@ -19495,6 +19467,13 @@ app23.get("/domains", requireAdminSession, async (c) => {
|
|
|
19495
19467
|
}
|
|
19496
19468
|
function err(field, message, output) {
|
|
19497
19469
|
logErr(`phase=error field=${field} reason="${message.slice(0, 160).replace(/"/g, "'")}"`);
|
|
19470
|
+
if (streamLogPath) {
|
|
19471
|
+
writeRouteMilestone(
|
|
19472
|
+
streamLogPath,
|
|
19473
|
+
"cloudflare-domains",
|
|
19474
|
+
`phase=error field=${field} reason="${message.slice(0, 160).replace(/"/g, "'")}"`
|
|
19475
|
+
);
|
|
19476
|
+
}
|
|
19498
19477
|
const body = {
|
|
19499
19478
|
ok: false,
|
|
19500
19479
|
field,
|
|
@@ -19513,8 +19492,17 @@ app23.get("/domains", requireAdminSession, async (c) => {
|
|
|
19513
19492
|
streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
|
|
19514
19493
|
log(`phase=stream-log-resolved path=${streamLogPath}`);
|
|
19515
19494
|
const brand = loadBrandInfo();
|
|
19516
|
-
const scriptPath =
|
|
19517
|
-
const result = await
|
|
19495
|
+
const scriptPath = resolve26(homedir4(), "list-cf-domains.sh");
|
|
19496
|
+
const result = await runFormSpawn({
|
|
19497
|
+
scriptPath,
|
|
19498
|
+
args: [brand.hostname],
|
|
19499
|
+
streamLogPath,
|
|
19500
|
+
correlationId,
|
|
19501
|
+
timeoutMs: DOMAINS_TIMEOUT_MS,
|
|
19502
|
+
log,
|
|
19503
|
+
logErr,
|
|
19504
|
+
broadcast: broadcastToConversation
|
|
19505
|
+
});
|
|
19518
19506
|
const combined = `${result.stdout}${result.stderr ? `
|
|
19519
19507
|
---
|
|
19520
19508
|
${result.stderr}` : ""}`;
|
|
@@ -19547,6 +19535,11 @@ ${result.stderr}` : ""}`;
|
|
|
19547
19535
|
};
|
|
19548
19536
|
const total = Date.now() - started;
|
|
19549
19537
|
log(`phase=response-sent total_ms=${total} count=${domains.length}`);
|
|
19538
|
+
writeRouteMilestone(
|
|
19539
|
+
streamLogPath,
|
|
19540
|
+
"cloudflare-domains",
|
|
19541
|
+
`phase=response-sent total_ms=${total} count=${domains.length}`
|
|
19542
|
+
);
|
|
19550
19543
|
return c.json(success, 200);
|
|
19551
19544
|
});
|
|
19552
19545
|
app23.post("/setup", requireAdminSession, async (c) => {
|
|
@@ -19567,6 +19560,13 @@ app23.post("/setup", requireAdminSession, async (c) => {
|
|
|
19567
19560
|
}
|
|
19568
19561
|
function err(field, message, output) {
|
|
19569
19562
|
logErr(`phase=error field=${field} reason="${message.slice(0, 160).replace(/"/g, "'")}"`);
|
|
19563
|
+
if (streamLogPath) {
|
|
19564
|
+
writeRouteMilestone(
|
|
19565
|
+
streamLogPath,
|
|
19566
|
+
"cloudflare-setup",
|
|
19567
|
+
`phase=error field=${field} reason="${message.slice(0, 160).replace(/"/g, "'")}"`
|
|
19568
|
+
);
|
|
19569
|
+
}
|
|
19570
19570
|
const body2 = {
|
|
19571
19571
|
ok: false,
|
|
19572
19572
|
field,
|
|
@@ -19617,47 +19617,25 @@ app23.post("/setup", requireAdminSession, async (c) => {
|
|
|
19617
19617
|
}
|
|
19618
19618
|
streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
|
|
19619
19619
|
log(`phase=stream-log-resolved path=${streamLogPath}`);
|
|
19620
|
-
|
|
19621
|
-
try {
|
|
19622
|
-
tailer = startScriptStreamTailer({
|
|
19623
|
-
path: streamLogPath,
|
|
19624
|
-
onEvent: (event) => {
|
|
19625
|
-
if (correlationId) broadcastToConversation(correlationId, event);
|
|
19626
|
-
},
|
|
19627
|
-
onError: (e) => {
|
|
19628
|
-
logErr(`phase=tailer-error message="${e.message.slice(0, 160).replace(/"/g, "'")}"`);
|
|
19629
|
-
}
|
|
19630
|
-
});
|
|
19631
|
-
log(`phase=tailer-started`);
|
|
19632
|
-
} catch (e) {
|
|
19633
|
-
logErr(`phase=tailer-start-failed detail="${e instanceof Error ? e.message : String(e)}"`);
|
|
19634
|
-
tailer = null;
|
|
19635
|
-
}
|
|
19636
|
-
const scriptPath = resolve25(homedir4(), "setup-tunnel.sh");
|
|
19620
|
+
const scriptPath = resolve26(homedir4(), "setup-tunnel.sh");
|
|
19637
19621
|
const args = [brand.hostname, String(port2), adminFqdn];
|
|
19638
19622
|
if (publicFqdn) args.push(publicFqdn);
|
|
19639
19623
|
if (apex) args.push(apex);
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
|
|
19645
|
-
|
|
19646
|
-
|
|
19647
|
-
|
|
19648
|
-
|
|
19649
|
-
|
|
19650
|
-
}
|
|
19651
|
-
}
|
|
19652
|
-
}
|
|
19653
|
-
if (!result) {
|
|
19654
|
-
return err("script", "Script spawn failed before producing a result");
|
|
19655
|
-
}
|
|
19624
|
+
const result = await runFormSpawn({
|
|
19625
|
+
scriptPath,
|
|
19626
|
+
args,
|
|
19627
|
+
streamLogPath,
|
|
19628
|
+
correlationId,
|
|
19629
|
+
timeoutMs: SETUP_TIMEOUT_MS,
|
|
19630
|
+
log,
|
|
19631
|
+
logErr,
|
|
19632
|
+
broadcast: broadcastToConversation
|
|
19633
|
+
});
|
|
19656
19634
|
const combined = `${result.stdout}${result.stderr ? `
|
|
19657
19635
|
---
|
|
19658
19636
|
${result.stderr}` : ""}`;
|
|
19659
19637
|
if (result.code !== 0) {
|
|
19660
|
-
const reason = result.timedOut ? `Timed out after ${
|
|
19638
|
+
const reason = result.timedOut ? `Timed out after ${SETUP_TIMEOUT_MS / 1e3}s` : `Exited with code ${result.code ?? "null"}${result.signal ? ` (signal ${result.signal})` : ""}`;
|
|
19661
19639
|
return err("script", reason, combined);
|
|
19662
19640
|
}
|
|
19663
19641
|
const candidates = [publicFqdn, apex].filter((h) => typeof h === "string" && h.length > 0);
|
|
@@ -19685,6 +19663,11 @@ ${result.stderr}` : ""}`;
|
|
|
19685
19663
|
}
|
|
19686
19664
|
const total = Date.now() - started;
|
|
19687
19665
|
log(`phase=done total_ms=${total} aliases=${aliasesWritten.length}`);
|
|
19666
|
+
writeRouteMilestone(
|
|
19667
|
+
streamLogPath,
|
|
19668
|
+
"cloudflare-setup",
|
|
19669
|
+
`phase=done total_ms=${total} aliases=${aliasesWritten.length}`
|
|
19670
|
+
);
|
|
19688
19671
|
const success = {
|
|
19689
19672
|
ok: true,
|
|
19690
19673
|
output: combined,
|
|
@@ -19701,19 +19684,19 @@ var cloudflare_default = app23;
|
|
|
19701
19684
|
|
|
19702
19685
|
// server/routes/admin/files.ts
|
|
19703
19686
|
import { createReadStream as createReadStream3 } from "fs";
|
|
19704
|
-
import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
|
|
19687
|
+
import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
19705
19688
|
import { realpathSync as realpathSync4 } from "fs";
|
|
19706
|
-
import { basename as basename6, join as join12, resolve as
|
|
19689
|
+
import { basename as basename6, dirname as dirname9, join as join12, resolve as resolve28, sep as sep2 } from "path";
|
|
19707
19690
|
import { Readable as Readable3 } from "stream";
|
|
19708
19691
|
|
|
19709
19692
|
// app/lib/data-path.ts
|
|
19710
19693
|
import { realpathSync as realpathSync3 } from "fs";
|
|
19711
|
-
import { resolve as
|
|
19712
|
-
var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT ??
|
|
19713
|
-
var DATA_ROOT =
|
|
19694
|
+
import { resolve as resolve27, normalize, sep, relative } from "path";
|
|
19695
|
+
var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT ?? resolve27(process.cwd(), "../platform");
|
|
19696
|
+
var DATA_ROOT = resolve27(PLATFORM_ROOT10, "..", "data");
|
|
19714
19697
|
function resolveDataPath(raw2) {
|
|
19715
19698
|
const cleaned = normalize("/" + (raw2 ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
19716
|
-
const absolute =
|
|
19699
|
+
const absolute = resolve27(DATA_ROOT, cleaned);
|
|
19717
19700
|
let dataRootReal;
|
|
19718
19701
|
try {
|
|
19719
19702
|
dataRootReal = realpathSync3(DATA_ROOT);
|
|
@@ -19761,12 +19744,44 @@ async function readMeta(absDir, baseName) {
|
|
|
19761
19744
|
}
|
|
19762
19745
|
return null;
|
|
19763
19746
|
}
|
|
19764
|
-
async function
|
|
19747
|
+
async function readAccountNames() {
|
|
19748
|
+
const map = /* @__PURE__ */ new Map();
|
|
19749
|
+
const accountsDir = resolve28(DATA_ROOT, "accounts");
|
|
19750
|
+
let names;
|
|
19751
|
+
try {
|
|
19752
|
+
names = await readdir2(accountsDir);
|
|
19753
|
+
} catch {
|
|
19754
|
+
return map;
|
|
19755
|
+
}
|
|
19756
|
+
for (const name of names) {
|
|
19757
|
+
if (!UUID_RE2.test(name)) continue;
|
|
19758
|
+
const configPath2 = resolve28(accountsDir, name, "account.json");
|
|
19759
|
+
try {
|
|
19760
|
+
const raw2 = await readFile4(configPath2, "utf8");
|
|
19761
|
+
const parsed = JSON.parse(raw2);
|
|
19762
|
+
if (typeof parsed?.name === "string" && parsed.name.length > 0) {
|
|
19763
|
+
map.set(name, parsed.name);
|
|
19764
|
+
} else {
|
|
19765
|
+
console.error(`[data] scope-resolve-miss uuid="${name}" reason="no-name"`);
|
|
19766
|
+
}
|
|
19767
|
+
} catch (err) {
|
|
19768
|
+
const code = err.code;
|
|
19769
|
+
console.error(`[data] scope-resolve-miss uuid="${name}" reason="${code === "ENOENT" ? "enoent" : "parse"}"`);
|
|
19770
|
+
}
|
|
19771
|
+
}
|
|
19772
|
+
return map;
|
|
19773
|
+
}
|
|
19774
|
+
async function enrich(absolute, entry, accountNames) {
|
|
19765
19775
|
if (entry.kind === "directory" && UUID_RE2.test(entry.name)) {
|
|
19766
19776
|
const meta = await readMeta(join12(absolute, entry.name), entry.name);
|
|
19767
19777
|
if (meta?.filename) {
|
|
19768
19778
|
entry.displayName = meta.filename;
|
|
19769
19779
|
entry.mimeType = meta.mimeType;
|
|
19780
|
+
return;
|
|
19781
|
+
}
|
|
19782
|
+
const accountName = accountNames.get(entry.name);
|
|
19783
|
+
if (accountName) {
|
|
19784
|
+
entry.displayName = accountName;
|
|
19770
19785
|
}
|
|
19771
19786
|
return;
|
|
19772
19787
|
}
|
|
@@ -19782,6 +19797,13 @@ async function enrich(absolute, entry) {
|
|
|
19782
19797
|
}
|
|
19783
19798
|
}
|
|
19784
19799
|
}
|
|
19800
|
+
function buildDisplayPath(relPath, accountNames) {
|
|
19801
|
+
if (relPath === "." || relPath === "") return [];
|
|
19802
|
+
return relPath.split("/").filter(Boolean).map((seg) => {
|
|
19803
|
+
const dn = UUID_RE2.test(seg) ? accountNames.get(seg) : void 0;
|
|
19804
|
+
return dn ? { name: seg, displayName: dn } : { name: seg };
|
|
19805
|
+
});
|
|
19806
|
+
}
|
|
19785
19807
|
var app24 = new Hono2();
|
|
19786
19808
|
app24.get("/", requireAdminSession, async (c) => {
|
|
19787
19809
|
const sessionKey = c.var.sessionKey;
|
|
@@ -19822,15 +19844,17 @@ app24.get("/", requireAdminSession, async (c) => {
|
|
|
19822
19844
|
entries.push({ name, kind: "other", sizeBytes: null, modifiedAt: (/* @__PURE__ */ new Date(0)).toISOString() });
|
|
19823
19845
|
}
|
|
19824
19846
|
}
|
|
19825
|
-
|
|
19847
|
+
const accountNames = await readAccountNames();
|
|
19848
|
+
await Promise.all(entries.map((e) => enrich(absolute, e, accountNames)));
|
|
19826
19849
|
entries.sort((a, b) => {
|
|
19827
19850
|
if (a.kind !== b.kind) return a.kind === "directory" ? -1 : 1;
|
|
19828
19851
|
const aKey = a.displayName ?? a.name;
|
|
19829
19852
|
const bKey = b.displayName ?? b.name;
|
|
19830
19853
|
return aKey.localeCompare(bKey);
|
|
19831
19854
|
});
|
|
19855
|
+
const displayPath = buildDisplayPath(relPath, accountNames);
|
|
19832
19856
|
console.error(`[data] file-list path="${relPath}" entries=${entries.length}`);
|
|
19833
|
-
return c.json({ path: relPath, entries });
|
|
19857
|
+
return c.json({ path: relPath, displayPath, entries });
|
|
19834
19858
|
} catch (err) {
|
|
19835
19859
|
const code = err.code;
|
|
19836
19860
|
if (code === "ENOENT") {
|
|
@@ -19922,8 +19946,8 @@ app24.post("/upload", requireAdminSession, async (c) => {
|
|
|
19922
19946
|
}
|
|
19923
19947
|
const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
|
|
19924
19948
|
const finalName = `${Date.now()}-${safeName}`;
|
|
19925
|
-
const destDir =
|
|
19926
|
-
const destPath =
|
|
19949
|
+
const destDir = resolve28(DATA_ROOT, "uploads", accountId);
|
|
19950
|
+
const destPath = resolve28(destDir, finalName);
|
|
19927
19951
|
try {
|
|
19928
19952
|
await mkdir3(destDir, { recursive: true });
|
|
19929
19953
|
const dataRootReal = realpathSync4(DATA_ROOT);
|
|
@@ -19948,6 +19972,59 @@ app24.post("/upload", requireAdminSession, async (c) => {
|
|
|
19948
19972
|
mimeType: file.type
|
|
19949
19973
|
});
|
|
19950
19974
|
});
|
|
19975
|
+
app24.delete("/", requireAdminSession, async (c) => {
|
|
19976
|
+
const sessionKey = c.var.sessionKey;
|
|
19977
|
+
if (!getAccountIdForSession(sessionKey)) {
|
|
19978
|
+
console.error(`[data] auth-rejected endpoint="DELETE /api/admin/files" reason="no account for session"`);
|
|
19979
|
+
return c.json({ error: "Account not found for session" }, 401);
|
|
19980
|
+
}
|
|
19981
|
+
const rawPath = c.req.query("path") ?? "";
|
|
19982
|
+
if (!rawPath) return c.json({ error: "path required" }, 400);
|
|
19983
|
+
const resolution = resolveDataPath(rawPath);
|
|
19984
|
+
if (!resolution.ok) {
|
|
19985
|
+
if (resolution.status === 403) {
|
|
19986
|
+
console.error(`[data] path-traversal-blocked requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
|
|
19987
|
+
}
|
|
19988
|
+
return c.json({ error: resolution.error }, resolution.status);
|
|
19989
|
+
}
|
|
19990
|
+
const { absolute, relative: relPath } = resolution;
|
|
19991
|
+
const base = basename6(absolute);
|
|
19992
|
+
const segments = relPath.split("/").filter(Boolean);
|
|
19993
|
+
if (base === "account.json" || segments.includes(".git")) {
|
|
19994
|
+
console.error(`[data] file-delete blocked path="${relPath}" reason="protected"`);
|
|
19995
|
+
return c.json({ error: "Protected file \u2014 refusing to delete" }, 403);
|
|
19996
|
+
}
|
|
19997
|
+
try {
|
|
19998
|
+
const info = await stat4(absolute);
|
|
19999
|
+
if (info.isDirectory()) {
|
|
20000
|
+
return c.json({ error: "Directory deletion not supported" }, 400);
|
|
20001
|
+
}
|
|
20002
|
+
if (!info.isFile()) {
|
|
20003
|
+
return c.json({ error: "Not a regular file" }, 400);
|
|
20004
|
+
}
|
|
20005
|
+
const dot = base.lastIndexOf(".");
|
|
20006
|
+
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
20007
|
+
const sidecarPath = UUID_RE2.test(stem) && base !== `${stem}.meta.json` ? join12(dirname9(absolute), `${stem}.meta.json`) : null;
|
|
20008
|
+
await unlink2(absolute);
|
|
20009
|
+
if (sidecarPath) {
|
|
20010
|
+
try {
|
|
20011
|
+
await unlink2(sidecarPath);
|
|
20012
|
+
} catch {
|
|
20013
|
+
}
|
|
20014
|
+
}
|
|
20015
|
+
console.error(`[data] file-delete path="${relPath}" bytes=${info.size}`);
|
|
20016
|
+
return c.json({ ok: true });
|
|
20017
|
+
} catch (err) {
|
|
20018
|
+
const code = err.code;
|
|
20019
|
+
if (code === "ENOENT") {
|
|
20020
|
+
console.error(`[data] file-delete not-found path="${relPath}"`);
|
|
20021
|
+
return c.json({ error: "Not found" }, 404);
|
|
20022
|
+
}
|
|
20023
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20024
|
+
console.error(`[data] file-delete error path="${relPath}" err="${message}"`);
|
|
20025
|
+
return c.json({ error: message }, 500);
|
|
20026
|
+
}
|
|
20027
|
+
});
|
|
19951
20028
|
var files_default = app24;
|
|
19952
20029
|
|
|
19953
20030
|
// server/routes/admin/graph-search.ts
|
|
@@ -19981,9 +20058,152 @@ app25.get("/", requireAdminSession, async (c) => {
|
|
|
19981
20058
|
});
|
|
19982
20059
|
var graph_search_default = app25;
|
|
19983
20060
|
|
|
19984
|
-
//
|
|
20061
|
+
// app/lib/graph-labels.ts
|
|
20062
|
+
var GRAPH_LABEL_COLOURS = {
|
|
20063
|
+
// Business identity
|
|
20064
|
+
LocalBusiness: "#2E5090",
|
|
20065
|
+
Service: "#3A6BB0",
|
|
20066
|
+
PriceSpecification: "#5B8DD0",
|
|
20067
|
+
OpeningHoursSpecification: "#7AAFE8",
|
|
20068
|
+
// People
|
|
20069
|
+
Person: "#C2410C",
|
|
20070
|
+
UserProfile: "#EA580C",
|
|
20071
|
+
Preference: "#F97316",
|
|
20072
|
+
AdminUser: "#9A3412",
|
|
20073
|
+
AccessGrant: "#7C2D12",
|
|
20074
|
+
// Knowledge
|
|
20075
|
+
KnowledgeDocument: "#16A34A",
|
|
20076
|
+
Section: "#22C55E",
|
|
20077
|
+
Chunk: "#4ADE80",
|
|
20078
|
+
DigitalDocument: "#15803D",
|
|
20079
|
+
CreativeWork: "#86EFAC",
|
|
20080
|
+
Question: "#A7F3D0",
|
|
20081
|
+
FAQPage: "#34D399",
|
|
20082
|
+
DefinedTerm: "#10B981",
|
|
20083
|
+
Review: "#059669",
|
|
20084
|
+
ImageObject: "#6EE7B7",
|
|
20085
|
+
// Conversational
|
|
20086
|
+
Conversation: "#7C3AED",
|
|
20087
|
+
Message: "#A78BFA",
|
|
20088
|
+
ToolCall: "#C4B5FD",
|
|
20089
|
+
// Tasks / projects / events
|
|
20090
|
+
Task: "#DB2777",
|
|
20091
|
+
Project: "#BE185D",
|
|
20092
|
+
Event: "#EC4899",
|
|
20093
|
+
// Workflows
|
|
20094
|
+
Workflow: "#0891B2",
|
|
20095
|
+
WorkflowStep: "#06B6D4",
|
|
20096
|
+
WorkflowRun: "#22D3EE",
|
|
20097
|
+
StepResult: "#67E8F9",
|
|
20098
|
+
// Onboarding
|
|
20099
|
+
OnboardingState: "#8B5CF6",
|
|
20100
|
+
// Email
|
|
20101
|
+
Email: "#65A30D",
|
|
20102
|
+
EmailAccount: "#84CC16"
|
|
20103
|
+
};
|
|
20104
|
+
var ALL_GRAPH_LABELS = Object.freeze(
|
|
20105
|
+
Object.keys(GRAPH_LABEL_COLOURS)
|
|
20106
|
+
);
|
|
20107
|
+
function isKnownLabel(label) {
|
|
20108
|
+
return Object.prototype.hasOwnProperty.call(GRAPH_LABEL_COLOURS, label);
|
|
20109
|
+
}
|
|
20110
|
+
|
|
20111
|
+
// server/routes/admin/graph-subgraph.ts
|
|
20112
|
+
var DEFAULT_LIMIT2 = 200;
|
|
20113
|
+
var MAX_LIMIT2 = 500;
|
|
20114
|
+
var STRIPPED_PROPERTIES = /* @__PURE__ */ new Set([
|
|
20115
|
+
"embedding",
|
|
20116
|
+
"passwordHash",
|
|
20117
|
+
"magicToken",
|
|
20118
|
+
"otpCode",
|
|
20119
|
+
"sessionKey"
|
|
20120
|
+
]);
|
|
19985
20121
|
var app26 = new Hono2();
|
|
19986
|
-
app26.
|
|
20122
|
+
app26.get("/", requireAdminSession, async (c) => {
|
|
20123
|
+
const sessionKey = c.var.sessionKey;
|
|
20124
|
+
const accountId = getAccountIdForSession(sessionKey);
|
|
20125
|
+
if (!accountId) {
|
|
20126
|
+
console.error('[graph-page] auth-rejected reason="no account for session"');
|
|
20127
|
+
return c.json({ error: "Account not found for session" }, 401);
|
|
20128
|
+
}
|
|
20129
|
+
const rawLimit = c.req.query("limit");
|
|
20130
|
+
const parsedLimit = rawLimit ? parseInt(rawLimit, 10) : DEFAULT_LIMIT2;
|
|
20131
|
+
const limit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, MAX_LIMIT2) : DEFAULT_LIMIT2;
|
|
20132
|
+
const rawLabels = c.req.query("labels");
|
|
20133
|
+
const labels = rawLabels ? rawLabels.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
20134
|
+
for (const label of labels) {
|
|
20135
|
+
if (!isKnownLabel(label)) {
|
|
20136
|
+
return c.json(
|
|
20137
|
+
{ error: `unknown label "${label}" \u2014 not registered in graph-labels.ts` },
|
|
20138
|
+
400
|
|
20139
|
+
);
|
|
20140
|
+
}
|
|
20141
|
+
}
|
|
20142
|
+
const started = Date.now();
|
|
20143
|
+
const session = getSession();
|
|
20144
|
+
try {
|
|
20145
|
+
const cypher = labels.length > 0 ? buildCypher(true) : buildCypher(false);
|
|
20146
|
+
const params = { accountId, limit };
|
|
20147
|
+
if (labels.length > 0) params.labels = labels;
|
|
20148
|
+
const result = await session.run(cypher, params);
|
|
20149
|
+
const record = result.records[0];
|
|
20150
|
+
const rawNodes = record?.get("nodes") ?? [];
|
|
20151
|
+
const rawEdges = record?.get("edges") ?? [];
|
|
20152
|
+
const nodes = rawNodes.map(pruneNode);
|
|
20153
|
+
const edges = rawEdges.filter((e) => e && e.id != null);
|
|
20154
|
+
const elapsed = Date.now() - started;
|
|
20155
|
+
console.error(
|
|
20156
|
+
`[graph-page] load account=${accountId} nodes=${nodes.length} edges=${edges.length} ms=${elapsed}`
|
|
20157
|
+
);
|
|
20158
|
+
return c.json({ nodes, edges });
|
|
20159
|
+
} catch (err) {
|
|
20160
|
+
const elapsed = Date.now() - started;
|
|
20161
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20162
|
+
console.error(`[graph-page] error stage=fetch reason="${message}" ms=${elapsed}`);
|
|
20163
|
+
return c.json({ error: `Graph data unavailable: ${message}` }, 503);
|
|
20164
|
+
} finally {
|
|
20165
|
+
try {
|
|
20166
|
+
await session.close();
|
|
20167
|
+
} catch {
|
|
20168
|
+
}
|
|
20169
|
+
}
|
|
20170
|
+
});
|
|
20171
|
+
function buildCypher(filtered) {
|
|
20172
|
+
const labelGuard = filtered ? "AND any(lbl IN labels(n) WHERE lbl IN $labels)" : "";
|
|
20173
|
+
return `
|
|
20174
|
+
MATCH (n)
|
|
20175
|
+
WHERE n.accountId = $accountId ${labelGuard}
|
|
20176
|
+
WITH n ORDER BY coalesce(n.updatedAt, n.createdAt, '') DESC
|
|
20177
|
+
LIMIT toInteger($limit)
|
|
20178
|
+
WITH collect(n) AS nodes, collect(elementId(n)) AS nodeIds
|
|
20179
|
+
UNWIND nodes AS n
|
|
20180
|
+
OPTIONAL MATCH (n)-[r]-(m)
|
|
20181
|
+
WHERE elementId(m) IN nodeIds
|
|
20182
|
+
WITH nodes,
|
|
20183
|
+
collect(DISTINCT CASE WHEN r IS NULL THEN null ELSE {
|
|
20184
|
+
id: elementId(r),
|
|
20185
|
+
from: elementId(startNode(r)),
|
|
20186
|
+
to: elementId(endNode(r)),
|
|
20187
|
+
type: type(r)
|
|
20188
|
+
} END) AS rawEdges
|
|
20189
|
+
RETURN
|
|
20190
|
+
[x IN nodes | {id: elementId(x), labels: labels(x), properties: properties(x)}] AS nodes,
|
|
20191
|
+
[e IN rawEdges WHERE e IS NOT NULL] AS edges
|
|
20192
|
+
`;
|
|
20193
|
+
}
|
|
20194
|
+
function pruneNode(node) {
|
|
20195
|
+
const properties = {};
|
|
20196
|
+
for (const [key, value] of Object.entries(node.properties ?? {})) {
|
|
20197
|
+
if (STRIPPED_PROPERTIES.has(key)) continue;
|
|
20198
|
+
properties[key] = value;
|
|
20199
|
+
}
|
|
20200
|
+
return { id: node.id, labels: node.labels, properties };
|
|
20201
|
+
}
|
|
20202
|
+
var graph_subgraph_default = app26;
|
|
20203
|
+
|
|
20204
|
+
// server/routes/admin/file-attach.ts
|
|
20205
|
+
var app27 = new Hono2();
|
|
20206
|
+
app27.post("/", async (c) => {
|
|
19987
20207
|
try {
|
|
19988
20208
|
const body = await c.req.json();
|
|
19989
20209
|
const { filePath, accountId } = body;
|
|
@@ -20006,39 +20226,40 @@ app26.post("/", async (c) => {
|
|
|
20006
20226
|
return c.json({ error: message }, 500);
|
|
20007
20227
|
}
|
|
20008
20228
|
});
|
|
20009
|
-
var file_attach_default =
|
|
20229
|
+
var file_attach_default = app27;
|
|
20010
20230
|
|
|
20011
20231
|
// server/routes/admin/index.ts
|
|
20012
|
-
var
|
|
20013
|
-
|
|
20014
|
-
|
|
20015
|
-
|
|
20016
|
-
|
|
20017
|
-
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
|
|
20024
|
-
|
|
20025
|
-
|
|
20026
|
-
|
|
20027
|
-
|
|
20028
|
-
|
|
20029
|
-
|
|
20030
|
-
|
|
20232
|
+
var app28 = new Hono2();
|
|
20233
|
+
app28.route("/session", session_default2);
|
|
20234
|
+
app28.route("/chat", chat_default2);
|
|
20235
|
+
app28.route("/compact", compact_default);
|
|
20236
|
+
app28.route("/logs", logs_default);
|
|
20237
|
+
app28.route("/claude-info", claude_info_default);
|
|
20238
|
+
app28.route("/attachment", attachment_default);
|
|
20239
|
+
app28.route("/account", account_default);
|
|
20240
|
+
app28.route("/agents", agents_default);
|
|
20241
|
+
app28.route("/version", version_default);
|
|
20242
|
+
app28.route("/sessions", sessions_default);
|
|
20243
|
+
app28.route("/browser", browser_default);
|
|
20244
|
+
app28.route("/device-browser", device_browser_default);
|
|
20245
|
+
app28.route("/events", events_default);
|
|
20246
|
+
app28.route("/cloudflare", cloudflare_default);
|
|
20247
|
+
app28.route("/files", files_default);
|
|
20248
|
+
app28.route("/graph-search", graph_search_default);
|
|
20249
|
+
app28.route("/graph-subgraph", graph_subgraph_default);
|
|
20250
|
+
app28.route("/file-attach", file_attach_default);
|
|
20251
|
+
var admin_default = app28;
|
|
20031
20252
|
|
|
20032
20253
|
// server/index.ts
|
|
20033
20254
|
var PLATFORM_ROOT11 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
20034
20255
|
var BRAND_JSON_PATH = PLATFORM_ROOT11 ? join13(PLATFORM_ROOT11, "config", "brand.json") : "";
|
|
20035
20256
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
|
|
20036
|
-
if (BRAND_JSON_PATH && !
|
|
20257
|
+
if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
|
|
20037
20258
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
20038
20259
|
}
|
|
20039
|
-
if (BRAND_JSON_PATH &&
|
|
20260
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
20040
20261
|
try {
|
|
20041
|
-
const parsed = JSON.parse(
|
|
20262
|
+
const parsed = JSON.parse(readFileSync25(BRAND_JSON_PATH, "utf-8"));
|
|
20042
20263
|
BRAND = { ...BRAND, ...parsed };
|
|
20043
20264
|
} catch (err) {
|
|
20044
20265
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -20060,8 +20281,8 @@ var brandLoginOpts = {
|
|
|
20060
20281
|
var ALIAS_DOMAINS_PATH2 = join13(homedir5(), BRAND.configDir, "alias-domains.json");
|
|
20061
20282
|
function loadAliasDomains() {
|
|
20062
20283
|
try {
|
|
20063
|
-
if (!
|
|
20064
|
-
const parsed = JSON.parse(
|
|
20284
|
+
if (!existsSync24(ALIAS_DOMAINS_PATH2)) return null;
|
|
20285
|
+
const parsed = JSON.parse(readFileSync25(ALIAS_DOMAINS_PATH2, "utf-8"));
|
|
20065
20286
|
if (!Array.isArray(parsed)) {
|
|
20066
20287
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
20067
20288
|
return null;
|
|
@@ -20085,9 +20306,9 @@ watchFile(ALIAS_DOMAINS_PATH2, { interval: 2e3 }, () => {
|
|
|
20085
20306
|
function isPublicHost(host) {
|
|
20086
20307
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
20087
20308
|
}
|
|
20088
|
-
var
|
|
20089
|
-
|
|
20090
|
-
|
|
20309
|
+
var app29 = new Hono2();
|
|
20310
|
+
app29.use("*", clientIpMiddleware);
|
|
20311
|
+
app29.use("*", async (c, next) => {
|
|
20091
20312
|
await next();
|
|
20092
20313
|
c.header("X-Content-Type-Options", "nosniff");
|
|
20093
20314
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
@@ -20110,7 +20331,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
|
|
|
20110
20331
|
"/g/"
|
|
20111
20332
|
];
|
|
20112
20333
|
var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
|
|
20113
|
-
|
|
20334
|
+
app29.use("*", async (c, next) => {
|
|
20114
20335
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
20115
20336
|
if (!isPublicHost(host)) {
|
|
20116
20337
|
await next();
|
|
@@ -20150,7 +20371,7 @@ function resolveRemoteAuthOpts() {
|
|
|
20150
20371
|
return brandLoginOpts;
|
|
20151
20372
|
}
|
|
20152
20373
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
20153
|
-
|
|
20374
|
+
app29.post("/__remote-auth/login", async (c) => {
|
|
20154
20375
|
const clientIp = c.var.clientIp || "unknown";
|
|
20155
20376
|
const rateLimited = checkRateLimit(clientIp);
|
|
20156
20377
|
if (rateLimited) {
|
|
@@ -20186,7 +20407,7 @@ app28.post("/__remote-auth/login", async (c) => {
|
|
|
20186
20407
|
}
|
|
20187
20408
|
});
|
|
20188
20409
|
});
|
|
20189
|
-
|
|
20410
|
+
app29.get("/__remote-auth/logout", (c) => {
|
|
20190
20411
|
const cookieHeader = c.req.header("cookie");
|
|
20191
20412
|
const token = parseCookie(cookieHeader, "__remote_session");
|
|
20192
20413
|
if (token) invalidateRemoteSession(token);
|
|
@@ -20199,7 +20420,7 @@ app28.get("/__remote-auth/logout", (c) => {
|
|
|
20199
20420
|
}
|
|
20200
20421
|
});
|
|
20201
20422
|
});
|
|
20202
|
-
|
|
20423
|
+
app29.post("/__remote-auth/change-password", async (c) => {
|
|
20203
20424
|
const clientIp = c.var.clientIp || "unknown";
|
|
20204
20425
|
const rateLimited = checkRateLimit(clientIp);
|
|
20205
20426
|
if (rateLimited) {
|
|
@@ -20248,13 +20469,13 @@ app28.post("/__remote-auth/change-password", async (c) => {
|
|
|
20248
20469
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
20249
20470
|
}
|
|
20250
20471
|
});
|
|
20251
|
-
|
|
20472
|
+
app29.get("/__remote-auth/setup", (c) => {
|
|
20252
20473
|
if (isRemoteAuthConfigured()) {
|
|
20253
20474
|
return c.redirect("/");
|
|
20254
20475
|
}
|
|
20255
20476
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
|
|
20256
20477
|
});
|
|
20257
|
-
|
|
20478
|
+
app29.post("/__remote-auth/set-initial-password", async (c) => {
|
|
20258
20479
|
if (isRemoteAuthConfigured()) {
|
|
20259
20480
|
return c.redirect("/");
|
|
20260
20481
|
}
|
|
@@ -20290,10 +20511,10 @@ app28.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
20290
20511
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
20291
20512
|
}
|
|
20292
20513
|
});
|
|
20293
|
-
|
|
20514
|
+
app29.get("/api/remote-auth/status", (c) => {
|
|
20294
20515
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
20295
20516
|
});
|
|
20296
|
-
|
|
20517
|
+
app29.post("/api/remote-auth/set-password", async (c) => {
|
|
20297
20518
|
let body;
|
|
20298
20519
|
try {
|
|
20299
20520
|
body = await c.req.json();
|
|
@@ -20323,9 +20544,9 @@ app28.post("/api/remote-auth/set-password", async (c) => {
|
|
|
20323
20544
|
return c.json({ error: "Failed to save password" }, 500);
|
|
20324
20545
|
}
|
|
20325
20546
|
});
|
|
20326
|
-
|
|
20547
|
+
app29.route("/api/_client-error", client_error_default);
|
|
20327
20548
|
console.log("[client-error-route] mounted");
|
|
20328
|
-
|
|
20549
|
+
app29.use("*", async (c, next) => {
|
|
20329
20550
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
20330
20551
|
const path2 = c.req.path;
|
|
20331
20552
|
if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
|
|
@@ -20365,15 +20586,15 @@ function parseCookie(cookieHeader, name) {
|
|
|
20365
20586
|
return null;
|
|
20366
20587
|
}
|
|
20367
20588
|
}
|
|
20368
|
-
|
|
20369
|
-
|
|
20370
|
-
|
|
20371
|
-
|
|
20372
|
-
|
|
20373
|
-
|
|
20374
|
-
|
|
20375
|
-
|
|
20376
|
-
|
|
20589
|
+
app29.route("/api/health", health_default);
|
|
20590
|
+
app29.route("/api/session", session_default);
|
|
20591
|
+
app29.route("/api/chat", chat_default);
|
|
20592
|
+
app29.route("/api/group", group_default);
|
|
20593
|
+
app29.route("/api/access", access_default);
|
|
20594
|
+
app29.route("/api/telegram", telegram_default);
|
|
20595
|
+
app29.route("/api/whatsapp", whatsapp_default);
|
|
20596
|
+
app29.route("/api/onboarding", onboarding_default);
|
|
20597
|
+
app29.route("/api/admin", admin_default);
|
|
20377
20598
|
var SAFE_SLUG_RE = /^[a-z][a-z0-9-]{2,49}$/;
|
|
20378
20599
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
20379
20600
|
var IMAGE_MIME = {
|
|
@@ -20385,7 +20606,7 @@ var IMAGE_MIME = {
|
|
|
20385
20606
|
".svg": "image/svg+xml",
|
|
20386
20607
|
".ico": "image/x-icon"
|
|
20387
20608
|
};
|
|
20388
|
-
|
|
20609
|
+
app29.get("/agent-assets/:slug/:filename", (c) => {
|
|
20389
20610
|
const slug = c.req.param("slug");
|
|
20390
20611
|
const filename = c.req.param("filename");
|
|
20391
20612
|
if (!SAFE_SLUG_RE.test(slug)) {
|
|
@@ -20401,26 +20622,26 @@ app28.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
20401
20622
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
20402
20623
|
return c.text("Not found", 404);
|
|
20403
20624
|
}
|
|
20404
|
-
const filePath =
|
|
20405
|
-
const expectedDir =
|
|
20625
|
+
const filePath = resolve29(account.accountDir, "agents", slug, "assets", filename);
|
|
20626
|
+
const expectedDir = resolve29(account.accountDir, "agents", slug, "assets");
|
|
20406
20627
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
20407
20628
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
20408
20629
|
return c.text("Forbidden", 403);
|
|
20409
20630
|
}
|
|
20410
|
-
if (!
|
|
20631
|
+
if (!existsSync24(filePath)) {
|
|
20411
20632
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
20412
20633
|
return c.text("Not found", 404);
|
|
20413
20634
|
}
|
|
20414
20635
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
20415
20636
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
20416
20637
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
20417
|
-
const body =
|
|
20638
|
+
const body = readFileSync25(filePath);
|
|
20418
20639
|
return c.body(body, 200, {
|
|
20419
20640
|
"Content-Type": contentType,
|
|
20420
20641
|
"Cache-Control": "public, max-age=3600"
|
|
20421
20642
|
});
|
|
20422
20643
|
});
|
|
20423
|
-
|
|
20644
|
+
app29.get("/generated/:filename", (c) => {
|
|
20424
20645
|
const filename = c.req.param("filename");
|
|
20425
20646
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
20426
20647
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -20431,20 +20652,20 @@ app28.get("/generated/:filename", (c) => {
|
|
|
20431
20652
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
20432
20653
|
return c.text("Not found", 404);
|
|
20433
20654
|
}
|
|
20434
|
-
const filePath =
|
|
20435
|
-
const expectedDir =
|
|
20655
|
+
const filePath = resolve29(account.accountDir, "generated", filename);
|
|
20656
|
+
const expectedDir = resolve29(account.accountDir, "generated");
|
|
20436
20657
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
20437
20658
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
20438
20659
|
return c.text("Forbidden", 403);
|
|
20439
20660
|
}
|
|
20440
|
-
if (!
|
|
20661
|
+
if (!existsSync24(filePath)) {
|
|
20441
20662
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
20442
20663
|
return c.text("Not found", 404);
|
|
20443
20664
|
}
|
|
20444
20665
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
20445
20666
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
20446
20667
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
20447
|
-
const body =
|
|
20668
|
+
const body = readFileSync25(filePath);
|
|
20448
20669
|
return c.body(body, 200, {
|
|
20449
20670
|
"Content-Type": contentType,
|
|
20450
20671
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -20453,9 +20674,9 @@ app28.get("/generated/:filename", (c) => {
|
|
|
20453
20674
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
20454
20675
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
20455
20676
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
20456
|
-
if (BRAND_JSON_PATH &&
|
|
20677
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
20457
20678
|
try {
|
|
20458
|
-
const fullBrand = JSON.parse(
|
|
20679
|
+
const fullBrand = JSON.parse(readFileSync25(BRAND_JSON_PATH, "utf-8"));
|
|
20459
20680
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
20460
20681
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
20461
20682
|
} catch {
|
|
@@ -20473,8 +20694,8 @@ function readInstalledVersion() {
|
|
|
20473
20694
|
try {
|
|
20474
20695
|
if (!PLATFORM_ROOT11) return "unknown";
|
|
20475
20696
|
const versionFile = join13(PLATFORM_ROOT11, "config", `.${BRAND.hostname}-version`);
|
|
20476
|
-
if (!
|
|
20477
|
-
const content =
|
|
20697
|
+
if (!existsSync24(versionFile)) return "unknown";
|
|
20698
|
+
const content = readFileSync25(versionFile, "utf-8").trim();
|
|
20478
20699
|
return content || "unknown";
|
|
20479
20700
|
} catch {
|
|
20480
20701
|
return "unknown";
|
|
@@ -20515,7 +20736,7 @@ var clientErrorReporterScript = `<script>
|
|
|
20515
20736
|
function cachedHtml(file) {
|
|
20516
20737
|
let html = htmlCache.get(file);
|
|
20517
20738
|
if (!html) {
|
|
20518
|
-
html =
|
|
20739
|
+
html = readFileSync25(resolve29(process.cwd(), "public", file), "utf-8");
|
|
20519
20740
|
html = html.replace("<title>Maxy</title>", `<title>${escapeHtml2(BRAND.productName)}</title>`);
|
|
20520
20741
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml2(brandFaviconPath)}"`);
|
|
20521
20742
|
const headInjection = file === "index.html" ? `${brandScript}
|
|
@@ -20533,13 +20754,13 @@ function loadBrandingCache(agentSlug) {
|
|
|
20533
20754
|
const configDir2 = join13(homedir5(), BRAND.configDir);
|
|
20534
20755
|
try {
|
|
20535
20756
|
const accountJsonPath = join13(configDir2, "account.json");
|
|
20536
|
-
if (!
|
|
20537
|
-
const account = JSON.parse(
|
|
20757
|
+
if (!existsSync24(accountJsonPath)) return null;
|
|
20758
|
+
const account = JSON.parse(readFileSync25(accountJsonPath, "utf-8"));
|
|
20538
20759
|
const accountId = account.accountId;
|
|
20539
20760
|
if (!accountId) return null;
|
|
20540
20761
|
const cachePath = join13(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
20541
|
-
if (!
|
|
20542
|
-
return JSON.parse(
|
|
20762
|
+
if (!existsSync24(cachePath)) return null;
|
|
20763
|
+
return JSON.parse(readFileSync25(cachePath, "utf-8"));
|
|
20543
20764
|
} catch {
|
|
20544
20765
|
return null;
|
|
20545
20766
|
}
|
|
@@ -20548,8 +20769,8 @@ function resolveDefaultSlug() {
|
|
|
20548
20769
|
try {
|
|
20549
20770
|
const configDir2 = join13(homedir5(), BRAND.configDir);
|
|
20550
20771
|
const accountJsonPath = join13(configDir2, "account.json");
|
|
20551
|
-
if (!
|
|
20552
|
-
const account = JSON.parse(
|
|
20772
|
+
if (!existsSync24(accountJsonPath)) return null;
|
|
20773
|
+
const account = JSON.parse(readFileSync25(accountJsonPath, "utf-8"));
|
|
20553
20774
|
return account.defaultAgent || null;
|
|
20554
20775
|
} catch {
|
|
20555
20776
|
return null;
|
|
@@ -20585,7 +20806,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
20585
20806
|
function escapeHtml2(s) {
|
|
20586
20807
|
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
20587
20808
|
}
|
|
20588
|
-
|
|
20809
|
+
app29.get("/", (c) => {
|
|
20589
20810
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
20590
20811
|
if (isPublicHost(host)) {
|
|
20591
20812
|
const defaultSlug = resolveDefaultSlug();
|
|
@@ -20593,12 +20814,12 @@ app28.get("/", (c) => {
|
|
|
20593
20814
|
}
|
|
20594
20815
|
return c.html(cachedHtml("index.html"));
|
|
20595
20816
|
});
|
|
20596
|
-
|
|
20817
|
+
app29.get("/public", (c) => {
|
|
20597
20818
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
20598
20819
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
20599
20820
|
return c.html(cachedHtml("public.html"));
|
|
20600
20821
|
});
|
|
20601
|
-
|
|
20822
|
+
app29.get("/chat", (c) => {
|
|
20602
20823
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
20603
20824
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
20604
20825
|
return c.html(cachedHtml("public.html"));
|
|
@@ -20617,12 +20838,12 @@ async function logViewerFetch(c, next) {
|
|
|
20617
20838
|
duration_ms: Date.now() - start
|
|
20618
20839
|
});
|
|
20619
20840
|
}
|
|
20620
|
-
|
|
20621
|
-
|
|
20622
|
-
|
|
20841
|
+
app29.use("/vnc-viewer.html", logViewerFetch);
|
|
20842
|
+
app29.use("/vnc-popout.html", logViewerFetch);
|
|
20843
|
+
app29.get("/vnc-popout.html", (c) => {
|
|
20623
20844
|
let html = htmlCache.get("vnc-popout.html");
|
|
20624
20845
|
if (!html) {
|
|
20625
|
-
html =
|
|
20846
|
+
html = readFileSync25(resolve29(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
20626
20847
|
const name = escapeHtml2(BRAND.productName);
|
|
20627
20848
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
20628
20849
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -20632,7 +20853,7 @@ app28.get("/vnc-popout.html", (c) => {
|
|
|
20632
20853
|
}
|
|
20633
20854
|
return c.html(html);
|
|
20634
20855
|
});
|
|
20635
|
-
|
|
20856
|
+
app29.post("/api/vnc/client-event", async (c) => {
|
|
20636
20857
|
let body;
|
|
20637
20858
|
try {
|
|
20638
20859
|
body = await c.req.json();
|
|
@@ -20653,18 +20874,20 @@ app28.post("/api/vnc/client-event", async (c) => {
|
|
|
20653
20874
|
});
|
|
20654
20875
|
return c.json({ ok: true });
|
|
20655
20876
|
});
|
|
20656
|
-
|
|
20877
|
+
app29.get("/g/:slug", (c) => {
|
|
20657
20878
|
return c.html(brandedPublicHtml());
|
|
20658
20879
|
});
|
|
20659
|
-
|
|
20660
|
-
|
|
20661
|
-
|
|
20662
|
-
|
|
20880
|
+
app29.get("/graph", (c) => {
|
|
20881
|
+
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
20882
|
+
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
20883
|
+
return c.html(cachedHtml("graph.html"));
|
|
20884
|
+
});
|
|
20885
|
+
app29.get("/data", (c) => {
|
|
20663
20886
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
20664
20887
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
20665
20888
|
return c.html(cachedHtml("data.html"));
|
|
20666
20889
|
});
|
|
20667
|
-
|
|
20890
|
+
app29.get("/:slug", async (c, next) => {
|
|
20668
20891
|
const slug = c.req.param("slug");
|
|
20669
20892
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
20670
20893
|
const branding = loadBrandingCache(slug);
|
|
@@ -20673,16 +20896,20 @@ app28.get("/:slug", async (c, next) => {
|
|
|
20673
20896
|
}
|
|
20674
20897
|
await next();
|
|
20675
20898
|
});
|
|
20676
|
-
|
|
20899
|
+
app29.use("/*", serveStatic({ root: "./public" }));
|
|
20677
20900
|
var port = parseInt(process.env.PORT ?? "19200", 10);
|
|
20678
20901
|
var hostname = process.env.HOSTNAME ?? "0.0.0.0";
|
|
20679
|
-
var httpServer = serve({ fetch:
|
|
20902
|
+
var httpServer = serve({ fetch: app29.fetch, port, hostname });
|
|
20680
20903
|
attachVncWsProxy(httpServer, {
|
|
20681
20904
|
isPublicHost,
|
|
20682
20905
|
upstreamHost: "127.0.0.1",
|
|
20683
20906
|
upstreamPort: 6080
|
|
20684
20907
|
});
|
|
20685
|
-
|
|
20908
|
+
attachTerminalWsProxy(httpServer, {
|
|
20909
|
+
isPublicHost,
|
|
20910
|
+
upstreamHost: "127.0.0.1",
|
|
20911
|
+
upstreamPort: 7681
|
|
20912
|
+
});
|
|
20686
20913
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
20687
20914
|
var SUBAPP_MANIFEST = [
|
|
20688
20915
|
{ prefix: "/api/health", file: "server/routes/health.ts", subapp: health_default },
|
|
@@ -20702,7 +20929,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
20702
20929
|
}
|
|
20703
20930
|
try {
|
|
20704
20931
|
const registered = [];
|
|
20705
|
-
for (const r of
|
|
20932
|
+
for (const r of app29.routes ?? []) {
|
|
20706
20933
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
20707
20934
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
20708
20935
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -20716,8 +20943,8 @@ try {
|
|
|
20716
20943
|
(async () => {
|
|
20717
20944
|
try {
|
|
20718
20945
|
let userId = "";
|
|
20719
|
-
if (
|
|
20720
|
-
const users = JSON.parse(
|
|
20946
|
+
if (existsSync24(USERS_FILE)) {
|
|
20947
|
+
const users = JSON.parse(readFileSync25(USERS_FILE, "utf-8").trim() || "[]");
|
|
20721
20948
|
userId = users[0]?.userId ?? "";
|
|
20722
20949
|
}
|
|
20723
20950
|
await backfillNullUserIdConversations(userId);
|
|
@@ -20743,7 +20970,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
20743
20970
|
}
|
|
20744
20971
|
init({
|
|
20745
20972
|
configDir: configDirForWhatsApp,
|
|
20746
|
-
platformRoot:
|
|
20973
|
+
platformRoot: resolve29(process.env.MAXY_PLATFORM_ROOT ?? join13(__dirname, "..")),
|
|
20747
20974
|
accountConfig: bootAccountConfig,
|
|
20748
20975
|
onMessage: async (msg) => {
|
|
20749
20976
|
try {
|