@rubytech/create-maxy-code 0.1.305 → 0.1.307
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/package.json +1 -1
- package/payload/platform/lib/models/dist/index.d.ts +0 -1
- package/payload/platform/lib/models/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/models/dist/index.js +2 -5
- package/payload/platform/lib/models/dist/index.js.map +1 -1
- package/payload/platform/lib/models/src/index.ts +1 -4
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +46 -12
- package/payload/platform/plugins/docs/references/admin-ui.md +32 -11
- package/payload/platform/plugins/docs/references/getting-started.md +13 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +14 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +8 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +13 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +18 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.js +34 -10
- package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.d.ts +9 -0
- package/payload/platform/services/whatsapp-channel/dist/targets.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.js +13 -0
- package/payload/platform/services/whatsapp-channel/dist/targets.js.map +1 -1
- package/payload/platform/templates/account.json +1 -1
- package/payload/server/{chunk-TUY3RB5G.js → chunk-4QFPGAUZ.js} +27 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/AdminLoginScreens-BsC-EwAn.js +1 -0
- package/payload/server/public/assets/AdminShell-CiKKN22G.js +1 -0
- package/payload/server/public/assets/Checkbox-DoPOlZTV.js +1 -0
- package/payload/server/public/assets/admin-DSwmdCKp.js +1 -0
- package/payload/server/public/assets/{arc-BrTRjBIM.js → arc-DxSm8tli.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-6g9jRVgc.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DJ9on7sZ.js → architectureDiagram-Q4EWVU46-7JSAh0vL.js} +1 -1
- package/payload/server/public/assets/{audio-attachment-mime-j2VvxEtx.js → audio-attachment-mime-BA10IzqQ.js} +4 -4
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CIcyQn0q.js → blockDiagram-DXYQGD6D-DLoVmHKD.js} +1 -1
- package/payload/server/public/assets/brand-BaRMO3mZ.js +9 -0
- package/payload/server/public/assets/{jsx-runtime-DZkld3Wk.css → brand-C001cin3.css} +1 -1
- package/payload/server/public/assets/browser-BKR1eJJb.js +1 -0
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Dt5dh_GR.js → c4Diagram-AHTNJAMY-Nv1G0o9P.js} +1 -1
- package/payload/server/public/assets/channel-BgQLJanG.js +1 -0
- package/payload/server/public/assets/chat-DZXNRiy-.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-BUq3MqUy.js → chunk-2KRD3SAO-BpqPMhoA.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-CUzJH5Qt.js → chunk-336JU56O-DgXHlVU_.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-Dr_XVuUq.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-B4JUc3UA.js → chunk-4BX2VUAB-nwyZSZH8.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-HRO9fIIs.js → chunk-4TB4RGXK-Tfxz9LHX.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-MZdrLBuw.js → chunk-55IACEB6-6FMJQGXG.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-XcAN5zlM.js → chunk-5FUZZQ4R-g2FgZF3D.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-PQ1GEsxo.js → chunk-5PVQY5BW-DtLwXsPH.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-BVvCaqGd.js → chunk-67CJDMHE-K91CP5ZU.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BlBbZdpM.js → chunk-7N4EOEYR-BGpCQhqK.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-Ct-PgkGa.js → chunk-AA7GKIK3-jkDbInck.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-DZRRfCSz.js → chunk-BSJP7CBP-mDosdzGs.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-BoGEJiXe.js → chunk-CIAEETIT-DhBxewpQ.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-DiSmN31f.js → chunk-EDXVE4YY-B7c-9Emg.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-uhhokIvs.js → chunk-ENJZ2VHE-Cqhhncox.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-CfCx5WI7.js → chunk-FMBD7UC4-BpDq6wj1.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-Dj9E9gTu.js → chunk-FOC6F5B3-Ds02-ktu.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DcBs_FNy.js → chunk-ICPOFSXX-BTX5POFr.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-7-VefyJD.js → chunk-K5T4RW27-Du1PXXBU.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-CqgTEVxm.js → chunk-KGLVRYIC-siasOAf8.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-C0gzDXha.js → chunk-LIHQZDEY-xf1rbZtf.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-JXz23R1H.js → chunk-ORNJ4GCN-DeTLQ_LD.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-C2snCNq9.js → chunk-OYMX7WX6-DQ7_oVJn.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-DF4o9HRJ.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-CXCU7M-M.js → chunk-U2HBQHQK-CXmFUKgn.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-CEdcSb28.js → chunk-X2U36JSP-Bj8-8Wkz.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-Cckc75YS.js → chunk-XPW4576I-Con3TXqt.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-4nN8AO7H.js → chunk-YZCP3GAM-Dx8BHGWf.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-DcoBc8-7.js → chunk-ZZ45TVLE-Dp4Q5Y-f.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DAbPKU6u.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BKuFQLcv.js +1 -0
- package/payload/server/public/assets/clone-DnNHGhNe.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BFFro_t9.js → cose-bilkent-S5V4N54A-pZiCv69E.js} +1 -1
- package/payload/server/public/assets/{dagre-CN5to_JM.js → dagre-CLqhEgbL.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-WYrPwDg0.js → dagre-KV5264BT-BCxE8iyk.js} +1 -1
- package/payload/server/public/assets/data-CuVmW1WN.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-Dz2CiboN.js → diagram-5BDNPKRD-slUEdZzz.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-r8h3Ip9O.js → diagram-G4DWMVQ6-DPcraMfu.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-CECcpGtr.js → diagram-MMDJMWI5-DR6luhGK.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CHOcAyIP.js → diagram-TYMM5635-Jap1B4wA.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-YIqKdPbS.js → erDiagram-SMLLAGMA-DJKUs2hO.js} +1 -1
- package/payload/server/public/assets/file-download-4_hQmMwU.js +1 -0
- package/payload/server/public/assets/{flatten-DhYYI_lo.js → flatten-Cl1Bc3dR.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-DaEvQWpj.js → flowDiagram-DWJPFMVM-C8W-ZZ9W.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-x1hClEOB.js → ganttDiagram-T4ZO3ILL-DYCX4Xu2.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-g7mvu6IY.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-C-sZsh6Z.js → gitGraphDiagram-UUTBAWPF-CZu_fpgS.js} +1 -1
- package/payload/server/public/assets/{graph-Uaa5heM5.js → graph-D29p1FJH.js} +3 -3
- package/payload/server/public/assets/graph-labels-DF3QVHkH.js +1 -0
- package/payload/server/public/assets/{graphlib-CNkAqm2w.js → graphlib-Bdp7TA4y.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-DZqJuIkB.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-cGfGccHz.js +2 -0
- package/payload/server/public/assets/{isEmpty-DGhq_DBP.js → isEmpty-D1pGOD1J.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-wHE-RwzZ.js → ishikawaDiagram-UXIWVN3A-B7KfIX6z.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DQB6iThz.js → journeyDiagram-VCZTEJTY-CSDwBfhF.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-Ck08vXSA.js → kanban-definition-6JOO6SKY-CWzcEKTr.js} +1 -1
- package/payload/server/public/assets/{line-Cdc2FBEU.js → line-BNQSlbYn.js} +1 -1
- package/payload/server/public/assets/{linear-DhtUm5H7.js → linear-BN6kGN93.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-CSYulPIL.js → mermaid-parser.core-CcFhkElq.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-CP-hOmMh.js → mermaid.core-BsIeJ7Cc.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-GxlkvV1Z.js → mindmap-definition-QFDTVHPH-CQVzj6D_.js} +1 -1
- package/payload/server/public/assets/operator-CQRaBNN7.js +1 -0
- package/payload/server/public/assets/{ordinal-CibZU3PM.js → ordinal-CBZeH4Yp.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-Hn8rlHpy.js +1 -0
- package/payload/server/public/assets/page-Dz2F3QtB.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-BPGWVqBm.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-Di6kWh9c.js → pieDiagram-DEJITSTG-DnrQ9fyn.js} +1 -1
- package/payload/server/public/assets/{public-DBXfM02B.js → public-B7BQwWpP.js} +3 -3
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-iNHC5bkO.js → quadrantDiagram-34T5L4WZ-BQWlCyWi.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DMdLrj3E.js +1 -0
- package/payload/server/public/assets/{reduce-Da0RBva0.js → reduce-PuPlFPRX.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-BBlLREXo.js → requirementDiagram-MS252O5E-Du_vZhU1.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-qyDkP-Ca.js → sankeyDiagram-XADWPNL6-Cf6Z8GPQ.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-C0MJuS0m.js → sequenceDiagram-FGHM5R23-CS8GVol8.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-aHTrlosa.js → stateDiagram-FHFEXIEX-Bv1NW2F1.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BGUCxPmp.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BlRZIxEp.js → timeline-definition-GMOUNBTQ-ClGYAsr0.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CWxofUbI.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-Cfgb_ZG0.js +1 -0
- package/payload/server/public/assets/useSelectionMode-ghXuZicl.js +5 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-s76adysx.js → vennDiagram-DHZGUBPP-C2wTcxQT.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-B45olYjj.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-xC8t2QGW.js → wardleyDiagram-NUSXRM2D-D86-ivEx.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DRcHg-Wj.js → xychartDiagram-5P7HB3ND-Ba5WAN8P.js} +1 -1
- package/payload/server/public/browser.html +6 -6
- package/payload/server/public/chat.html +9 -10
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +8 -8
- package/payload/server/public/index.html +8 -9
- package/payload/server/public/operator.html +11 -12
- package/payload/server/public/public.html +7 -7
- package/payload/server/public/sw.js +24 -0
- package/payload/server/server.js +637 -226
- package/payload/server/public/assets/AdminLoginScreens-Cw882rmm.js +0 -1
- package/payload/server/public/assets/AdminShell-DpWl1uoY.js +0 -1
- package/payload/server/public/assets/Checkbox-DAWZItdK.js +0 -1
- package/payload/server/public/assets/Transcript-DbK2YA3K.js +0 -1
- package/payload/server/public/assets/admin-nQRdn79w.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-DqQB3T6j.js +0 -1
- package/payload/server/public/assets/browser-CHWZDCXt.js +0 -1
- package/payload/server/public/assets/channel-NZeJdLVK.js +0 -1
- package/payload/server/public/assets/chat-BBK6ulFy.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-DFKX5l0L.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-BWCCN0AM.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DPlCQCX2.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-CqkQq74h.js +0 -1
- package/payload/server/public/assets/clone-CSykdD1A.js +0 -1
- package/payload/server/public/assets/data-33mdnHv8.js +0 -1
- package/payload/server/public/assets/file-download-P_qlMCg5.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-V7Tx7_eY.js +0 -1
- package/payload/server/public/assets/graph-labels-rG63rHvl.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-D3DqVPGn.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-B_O-R0vN.js +0 -2
- package/payload/server/public/assets/jsx-runtime-sFVKdwlP.js +0 -9
- package/payload/server/public/assets/operator-CiDwt5I9.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-BY7mgCTQ.js +0 -1
- package/payload/server/public/assets/page-BvkaS_tu.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-B9f6jN54.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-BKS8JOUe.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-V2vo4BjY.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-bIKzyIdw.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-CQycQ4H9.js +0 -1
- package/payload/server/public/assets/useSelectionMode-Dky-Stnf.js +0 -5
- package/payload/server/public/assets/wardley-RL74JXVD-COr9o-nG.js +0 -1
- /package/payload/server/public/assets/{_baseFor-BFYpZGkN.js → _baseFor-brRjpUOX.js} +0 -0
- /package/payload/server/public/assets/{array-CYkMkqnU.js → array-BGFCBI0e.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-DqlHsaog.js → cytoscape.esm-TvRJ2tGX.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-Beh6XjaL.js → defaultLocale-CRZydyG6.js} +0 -0
- /package/payload/server/public/assets/{dist-t9p-NJH2.js → dist-Dbh7HrZX.js} +0 -0
- /package/payload/server/public/assets/{init-Rr1s_RiX.js → init-B8gtcn7T.js} +0 -0
- /package/payload/server/public/assets/{katex-pVJiI_rc.js → katex-RxgSu_dy.js} +0 -0
- /package/payload/server/public/assets/{path-BAQ3hXlG.js → path-DZF-JdEe.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-B_l5jfgx.js → preload-helper-CQ36nxok.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-nHaDi0Kw.js → rough.esm-6CnTHTkH.js} +0 -0
- /package/payload/server/public/assets/{src-DuPQVw-H.js → src-BoaldmnP.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -69,6 +69,7 @@ import {
|
|
|
69
69
|
listAdminSessions,
|
|
70
70
|
listValidAccounts,
|
|
71
71
|
loadAdminUserName,
|
|
72
|
+
loadPersonNameByElementId,
|
|
72
73
|
loadUserProfile,
|
|
73
74
|
loadWhatsappSenderName,
|
|
74
75
|
persistMessage,
|
|
@@ -105,7 +106,7 @@ import {
|
|
|
105
106
|
vncLog,
|
|
106
107
|
walkPremiumBundles,
|
|
107
108
|
writeAdminUserAndPerson
|
|
108
|
-
} from "./chunk-
|
|
109
|
+
} from "./chunk-4QFPGAUZ.js";
|
|
109
110
|
import {
|
|
110
111
|
__commonJS,
|
|
111
112
|
__toESM
|
|
@@ -1223,10 +1224,93 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1223
1224
|
|
|
1224
1225
|
// server/index.ts
|
|
1225
1226
|
import { readFileSync as readFileSync26, existsSync as existsSync27, watchFile } from "fs";
|
|
1226
|
-
import { resolve as
|
|
1227
|
+
import { resolve as resolve30, join as join22, basename as basename8 } from "path";
|
|
1227
1228
|
import { homedir as homedir3 } from "os";
|
|
1228
1229
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
1229
1230
|
|
|
1231
|
+
// app/lib/pwa.ts
|
|
1232
|
+
var PWA_SURFACES = [
|
|
1233
|
+
{
|
|
1234
|
+
surface: "admin",
|
|
1235
|
+
shellFile: "index.html",
|
|
1236
|
+
manifestPath: "/manifest-admin.webmanifest",
|
|
1237
|
+
nameSuffix: "",
|
|
1238
|
+
startUrl: "/",
|
|
1239
|
+
scope: "/",
|
|
1240
|
+
id: "/?pwa=admin"
|
|
1241
|
+
},
|
|
1242
|
+
{
|
|
1243
|
+
surface: "operator",
|
|
1244
|
+
shellFile: "operator.html",
|
|
1245
|
+
manifestPath: "/manifest-operator.webmanifest",
|
|
1246
|
+
nameSuffix: " Operator",
|
|
1247
|
+
startUrl: "/",
|
|
1248
|
+
scope: "/",
|
|
1249
|
+
id: "/?pwa=operator"
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
surface: "chat",
|
|
1253
|
+
shellFile: "chat.html",
|
|
1254
|
+
manifestPath: "/manifest-chat.webmanifest",
|
|
1255
|
+
nameSuffix: " Chat",
|
|
1256
|
+
startUrl: "/chat",
|
|
1257
|
+
scope: "/chat",
|
|
1258
|
+
id: "/chat?pwa=chat"
|
|
1259
|
+
}
|
|
1260
|
+
];
|
|
1261
|
+
var MANIFEST_CONTENT_TYPE = "application/manifest+json";
|
|
1262
|
+
var SW_CONTENT_TYPE = "application/javascript";
|
|
1263
|
+
function pwaSurfaceForShell(file) {
|
|
1264
|
+
return PWA_SURFACES.find((s) => s.shellFile === file);
|
|
1265
|
+
}
|
|
1266
|
+
function buildManifest(s, b) {
|
|
1267
|
+
const name = `${b.productName}${s.nameSuffix}`;
|
|
1268
|
+
return {
|
|
1269
|
+
id: s.id,
|
|
1270
|
+
name,
|
|
1271
|
+
short_name: name,
|
|
1272
|
+
start_url: s.startUrl,
|
|
1273
|
+
scope: s.scope,
|
|
1274
|
+
display: "standalone",
|
|
1275
|
+
theme_color: b.themeColor,
|
|
1276
|
+
background_color: b.backgroundColor,
|
|
1277
|
+
icons: [{ src: b.iconPath, sizes: "any", purpose: "any" }]
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
function escAttr(s) {
|
|
1281
|
+
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1282
|
+
}
|
|
1283
|
+
function swRegisterScript(s) {
|
|
1284
|
+
return [
|
|
1285
|
+
"<script>",
|
|
1286
|
+
"if('serviceWorker' in navigator){navigator.serviceWorker.register('/sw.js').catch(function(e){",
|
|
1287
|
+
`try{fetch('/api/_client-error',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({kind:'event',source:'pwa',op:'sw-register-fail',surface:'${s.surface}',scope:'${s.scope}',err:String((e&&e.message)||e)}),keepalive:true}).catch(function(){});}catch(_){}`,
|
|
1288
|
+
"});}",
|
|
1289
|
+
"</script>"
|
|
1290
|
+
].join("\n");
|
|
1291
|
+
}
|
|
1292
|
+
function pwaHeadTags(s, b) {
|
|
1293
|
+
return [
|
|
1294
|
+
`<link rel="manifest" href="${escAttr(s.manifestPath)}">`,
|
|
1295
|
+
`<link rel="apple-touch-icon" href="${escAttr(b.iconPath)}">`,
|
|
1296
|
+
// Both the standards-based and the legacy Apple capable meta: Chromium
|
|
1297
|
+
// deprecates `apple-mobile-web-app-capable` in favour of
|
|
1298
|
+
// `mobile-web-app-capable`, while iOS Safari still reads the apple one.
|
|
1299
|
+
'<meta name="mobile-web-app-capable" content="yes">',
|
|
1300
|
+
'<meta name="apple-mobile-web-app-capable" content="yes">',
|
|
1301
|
+
`<meta name="theme-color" content="${escAttr(b.themeColor)}">`,
|
|
1302
|
+
swRegisterScript(s)
|
|
1303
|
+
].join("\n");
|
|
1304
|
+
}
|
|
1305
|
+
function pwaCensus(d) {
|
|
1306
|
+
const manifestLinked = PWA_SURFACES.filter((s) => {
|
|
1307
|
+
const html = d.shellHtml(s.shellFile);
|
|
1308
|
+
return html != null && html.includes(`"${s.manifestPath}"`);
|
|
1309
|
+
}).length;
|
|
1310
|
+
const iconsPresent = d.iconExists() ? PWA_SURFACES.length : 0;
|
|
1311
|
+
return { surfaces: PWA_SURFACES.length, manifestLinked, iconsPresent };
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1230
1314
|
// server/webchat-descriptor.ts
|
|
1231
1315
|
import { resolve, join as join2 } from "path";
|
|
1232
1316
|
function webchatGatewayUrl() {
|
|
@@ -1788,7 +1872,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
1788
1872
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
1789
1873
|
console.error(`${TAG2} draining credential save queue\u2026`);
|
|
1790
1874
|
const timer2 = new Promise(
|
|
1791
|
-
(
|
|
1875
|
+
(resolve31) => setTimeout(() => resolve31("timeout"), timeoutMs)
|
|
1792
1876
|
);
|
|
1793
1877
|
const result = await Promise.race([
|
|
1794
1878
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -1916,11 +2000,11 @@ async function createWaSocket(opts) {
|
|
|
1916
2000
|
return sock;
|
|
1917
2001
|
}
|
|
1918
2002
|
async function waitForConnection(sock) {
|
|
1919
|
-
return new Promise((
|
|
2003
|
+
return new Promise((resolve31, reject) => {
|
|
1920
2004
|
const handler = (update) => {
|
|
1921
2005
|
if (update.connection === "open") {
|
|
1922
2006
|
sock.ev.off("connection.update", handler);
|
|
1923
|
-
|
|
2007
|
+
resolve31();
|
|
1924
2008
|
}
|
|
1925
2009
|
if (update.connection === "close") {
|
|
1926
2010
|
sock.ev.off("connection.update", handler);
|
|
@@ -2034,14 +2118,14 @@ ${inspected}`;
|
|
|
2034
2118
|
return inspect2(err, INSPECT_OPTS2);
|
|
2035
2119
|
}
|
|
2036
2120
|
function withTimeout(label, promise, timeoutMs) {
|
|
2037
|
-
return new Promise((
|
|
2121
|
+
return new Promise((resolve31, reject) => {
|
|
2038
2122
|
const timer2 = setTimeout(() => {
|
|
2039
2123
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
2040
2124
|
}, timeoutMs);
|
|
2041
2125
|
promise.then(
|
|
2042
2126
|
(value) => {
|
|
2043
2127
|
clearTimeout(timer2);
|
|
2044
|
-
|
|
2128
|
+
resolve31(value);
|
|
2045
2129
|
},
|
|
2046
2130
|
(err) => {
|
|
2047
2131
|
clearTimeout(timer2);
|
|
@@ -2925,8 +3009,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
2925
3009
|
const { givenName, familyName } = splitName(input.pushName);
|
|
2926
3010
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
2927
3011
|
let release;
|
|
2928
|
-
const mine = new Promise((
|
|
2929
|
-
release =
|
|
3012
|
+
const mine = new Promise((resolve31) => {
|
|
3013
|
+
release = resolve31;
|
|
2930
3014
|
});
|
|
2931
3015
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
2932
3016
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -4006,11 +4090,11 @@ async function connectWithReconnect(conn) {
|
|
|
4006
4090
|
console.error(
|
|
4007
4091
|
`${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
4008
4092
|
);
|
|
4009
|
-
await new Promise((
|
|
4010
|
-
const timer2 = setTimeout(
|
|
4093
|
+
await new Promise((resolve31) => {
|
|
4094
|
+
const timer2 = setTimeout(resolve31, delay);
|
|
4011
4095
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
4012
4096
|
clearTimeout(timer2);
|
|
4013
|
-
|
|
4097
|
+
resolve31();
|
|
4014
4098
|
}, { once: true });
|
|
4015
4099
|
});
|
|
4016
4100
|
}
|
|
@@ -4018,16 +4102,16 @@ async function connectWithReconnect(conn) {
|
|
|
4018
4102
|
}
|
|
4019
4103
|
}
|
|
4020
4104
|
function waitForDisconnectEvent(conn) {
|
|
4021
|
-
return new Promise((
|
|
4105
|
+
return new Promise((resolve31) => {
|
|
4022
4106
|
if (!conn.sock) {
|
|
4023
|
-
|
|
4107
|
+
resolve31();
|
|
4024
4108
|
return;
|
|
4025
4109
|
}
|
|
4026
4110
|
const sock = conn.sock;
|
|
4027
4111
|
const handler = (update) => {
|
|
4028
4112
|
if (update.connection === "close") {
|
|
4029
4113
|
sock.ev.off("connection.update", handler);
|
|
4030
|
-
|
|
4114
|
+
resolve31();
|
|
4031
4115
|
}
|
|
4032
4116
|
};
|
|
4033
4117
|
sock.ev.on("connection.update", handler);
|
|
@@ -4287,8 +4371,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4287
4371
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
4288
4372
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
4289
4373
|
let resolvePending;
|
|
4290
|
-
const sttPending = new Promise((
|
|
4291
|
-
resolvePending =
|
|
4374
|
+
const sttPending = new Promise((resolve31) => {
|
|
4375
|
+
resolvePending = resolve31;
|
|
4292
4376
|
});
|
|
4293
4377
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
4294
4378
|
try {
|
|
@@ -4682,20 +4766,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
4682
4766
|
|
|
4683
4767
|
// server/routes/health.ts
|
|
4684
4768
|
function checkPort(port2, timeoutMs = 500) {
|
|
4685
|
-
return new Promise((
|
|
4769
|
+
return new Promise((resolve31) => {
|
|
4686
4770
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
4687
4771
|
socket.setTimeout(timeoutMs);
|
|
4688
4772
|
socket.once("connect", () => {
|
|
4689
4773
|
socket.destroy();
|
|
4690
|
-
|
|
4774
|
+
resolve31(true);
|
|
4691
4775
|
});
|
|
4692
4776
|
socket.once("error", () => {
|
|
4693
4777
|
socket.destroy();
|
|
4694
|
-
|
|
4778
|
+
resolve31(false);
|
|
4695
4779
|
});
|
|
4696
4780
|
socket.once("timeout", () => {
|
|
4697
4781
|
socket.destroy();
|
|
4698
|
-
|
|
4782
|
+
resolve31(false);
|
|
4699
4783
|
});
|
|
4700
4784
|
});
|
|
4701
4785
|
}
|
|
@@ -4764,10 +4848,75 @@ var health_default = app;
|
|
|
4764
4848
|
// app/lib/attachments.ts
|
|
4765
4849
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
4766
4850
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
4851
|
+
import { realpathSync as realpathSync2 } from "fs";
|
|
4852
|
+
import { resolve as resolve6, extname } from "path";
|
|
4853
|
+
|
|
4854
|
+
// app/lib/data-path.ts
|
|
4767
4855
|
import { realpathSync } from "fs";
|
|
4768
|
-
import { resolve as resolve5,
|
|
4856
|
+
import { resolve as resolve5, normalize, sep as sep2, relative } from "path";
|
|
4769
4857
|
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "../platform");
|
|
4770
|
-
var
|
|
4858
|
+
var DATA_ROOT = resolve5(PLATFORM_ROOT4, "..", "data");
|
|
4859
|
+
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve5(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
4860
|
+
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["accounts"]);
|
|
4861
|
+
var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4862
|
+
function crossesForeignAccountPartition(relPath, accountId) {
|
|
4863
|
+
const segs = relPath.split("/").filter(Boolean);
|
|
4864
|
+
if (segs.length < 2) return false;
|
|
4865
|
+
if (!ACCOUNT_PARTITION_DIRS.has(segs[0])) return false;
|
|
4866
|
+
const owner = segs[1];
|
|
4867
|
+
if (!ACCOUNT_UUID_RE.test(owner)) return false;
|
|
4868
|
+
return owner !== accountId;
|
|
4869
|
+
}
|
|
4870
|
+
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
4871
|
+
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
4872
|
+
const absolute = resolve5(rootAbs, cleaned);
|
|
4873
|
+
let rootReal;
|
|
4874
|
+
try {
|
|
4875
|
+
rootReal = realpathSync(rootAbs);
|
|
4876
|
+
} catch (err) {
|
|
4877
|
+
return {
|
|
4878
|
+
ok: false,
|
|
4879
|
+
status: 500,
|
|
4880
|
+
error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
|
|
4881
|
+
};
|
|
4882
|
+
}
|
|
4883
|
+
let resolvedReal;
|
|
4884
|
+
try {
|
|
4885
|
+
resolvedReal = realpathSync(absolute);
|
|
4886
|
+
} catch (err) {
|
|
4887
|
+
const code = err.code;
|
|
4888
|
+
if (code === "ENOENT") {
|
|
4889
|
+
if (absolute !== rootReal && !absolute.startsWith(rootReal + sep2)) {
|
|
4890
|
+
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
|
|
4891
|
+
}
|
|
4892
|
+
return { ok: false, status: 404, error: "Not found" };
|
|
4893
|
+
}
|
|
4894
|
+
return {
|
|
4895
|
+
ok: false,
|
|
4896
|
+
status: 500,
|
|
4897
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4898
|
+
};
|
|
4899
|
+
}
|
|
4900
|
+
if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep2)) {
|
|
4901
|
+
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
|
|
4902
|
+
}
|
|
4903
|
+
const relPath = relative(rootReal, resolvedReal) || ".";
|
|
4904
|
+
return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath };
|
|
4905
|
+
}
|
|
4906
|
+
function resolveDataPath(raw) {
|
|
4907
|
+
return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
|
|
4908
|
+
}
|
|
4909
|
+
function resolveClaudeUploadsPath(raw) {
|
|
4910
|
+
if (!CLAUDE_UPLOADS_ROOT) {
|
|
4911
|
+
return { ok: false, status: 404, error: "Not found" };
|
|
4912
|
+
}
|
|
4913
|
+
return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
|
|
4914
|
+
}
|
|
4915
|
+
|
|
4916
|
+
// app/lib/attachments.ts
|
|
4917
|
+
function uploadsDirFor(accountId, subPath, attachmentId) {
|
|
4918
|
+
return subPath ? resolve6(DATA_ROOT, "accounts", accountId, "uploads", subPath, attachmentId) : resolve6(DATA_ROOT, "accounts", accountId, "uploads", attachmentId);
|
|
4919
|
+
}
|
|
4771
4920
|
var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
4772
4921
|
"image/jpeg",
|
|
4773
4922
|
"image/png",
|
|
@@ -4792,16 +4941,16 @@ function assertSupportedMime(mimeType) {
|
|
|
4792
4941
|
);
|
|
4793
4942
|
}
|
|
4794
4943
|
}
|
|
4795
|
-
async function writeAttachment(
|
|
4944
|
+
async function writeAttachment(accountId, subPath, filename, mimeType, sizeBytes, buffer) {
|
|
4796
4945
|
const attachmentId = randomUUID4();
|
|
4797
|
-
const dir =
|
|
4946
|
+
const dir = uploadsDirFor(accountId, subPath, attachmentId);
|
|
4798
4947
|
await mkdir2(dir, { recursive: true });
|
|
4799
4948
|
const ext = extname(filename) || "";
|
|
4800
|
-
const storagePath =
|
|
4801
|
-
const metaPath =
|
|
4949
|
+
const storagePath = resolve6(dir, `${attachmentId}${ext}`);
|
|
4950
|
+
const metaPath = resolve6(dir, `${attachmentId}.meta.json`);
|
|
4802
4951
|
const meta = {
|
|
4803
4952
|
attachmentId,
|
|
4804
|
-
scope,
|
|
4953
|
+
scope: subPath || accountId,
|
|
4805
4954
|
filename,
|
|
4806
4955
|
mimeType,
|
|
4807
4956
|
sizeBytes,
|
|
@@ -4819,16 +4968,16 @@ async function storeAttachment(accountId, file) {
|
|
|
4819
4968
|
);
|
|
4820
4969
|
}
|
|
4821
4970
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
4822
|
-
return writeAttachment(accountId, file.name, file.type, file.size, buffer);
|
|
4971
|
+
return writeAttachment(accountId, "", file.name, file.type, file.size, buffer);
|
|
4823
4972
|
}
|
|
4824
|
-
async function storePublicAttachment(sessionId, file, buffer) {
|
|
4973
|
+
async function storePublicAttachment(accountId, sessionId, file, buffer) {
|
|
4825
4974
|
assertSupportedMime(file.type);
|
|
4826
4975
|
if (file.size > MAX_FILE_SIZE_BYTES) {
|
|
4827
4976
|
throw new Error(
|
|
4828
4977
|
`File "${file.name}" exceeds the 50 MB limit (${(file.size / 1024 / 1024).toFixed(1)} MB).`
|
|
4829
4978
|
);
|
|
4830
4979
|
}
|
|
4831
|
-
return writeAttachment(`public/${sessionId}`, file.name, file.type, file.size, buffer);
|
|
4980
|
+
return writeAttachment(accountId, `public/${sessionId}`, file.name, file.type, file.size, buffer);
|
|
4832
4981
|
}
|
|
4833
4982
|
var MIME_BY_EXT = {
|
|
4834
4983
|
".pdf": "application/pdf",
|
|
@@ -4858,8 +5007,8 @@ function detectMimeType(filePath) {
|
|
|
4858
5007
|
return MIME_BY_EXT[ext] ?? "application/octet-stream";
|
|
4859
5008
|
}
|
|
4860
5009
|
function validateFilePathInAccount(filePath, accountDir) {
|
|
4861
|
-
const resolved =
|
|
4862
|
-
const accountResolved =
|
|
5010
|
+
const resolved = realpathSync2(filePath);
|
|
5011
|
+
const accountResolved = realpathSync2(accountDir);
|
|
4863
5012
|
if (!resolved.startsWith(accountResolved + "/")) {
|
|
4864
5013
|
throw new Error(`File path is outside the account directory`);
|
|
4865
5014
|
}
|
|
@@ -4872,11 +5021,11 @@ async function storeComponentArtefact(accountId, attachmentId, mimeType, content
|
|
|
4872
5021
|
`Component artefact exceeds the 50 MB limit (${(buffer.byteLength / 1024 / 1024).toFixed(1)} MB).`
|
|
4873
5022
|
);
|
|
4874
5023
|
}
|
|
4875
|
-
const dir =
|
|
5024
|
+
const dir = uploadsDirFor(accountId, "", attachmentId);
|
|
4876
5025
|
await mkdir2(dir, { recursive: true });
|
|
4877
5026
|
const ext = extname(filename) || (mimeType === "text/html" ? ".html" : ".md");
|
|
4878
|
-
const storagePath =
|
|
4879
|
-
const metaPath =
|
|
5027
|
+
const storagePath = resolve6(dir, `${attachmentId}${ext}`);
|
|
5028
|
+
const metaPath = resolve6(dir, `${attachmentId}.meta.json`);
|
|
4880
5029
|
const meta = {
|
|
4881
5030
|
attachmentId,
|
|
4882
5031
|
scope: accountId,
|
|
@@ -4980,7 +5129,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
4980
5129
|
}
|
|
4981
5130
|
|
|
4982
5131
|
// app/lib/channel-pty-bridge/bridge.ts
|
|
4983
|
-
import { resolve as
|
|
5132
|
+
import { resolve as resolve8 } from "path";
|
|
4984
5133
|
|
|
4985
5134
|
// app/lib/channel-pty-bridge/manager-client.ts
|
|
4986
5135
|
function managerBase() {
|
|
@@ -5431,9 +5580,9 @@ async function fanOut(subscribers, text, onError, tag) {
|
|
|
5431
5580
|
}
|
|
5432
5581
|
|
|
5433
5582
|
// app/lib/whatsapp/outbound/send-document.ts
|
|
5434
|
-
import { realpathSync as
|
|
5583
|
+
import { realpathSync as realpathSync3 } from "fs";
|
|
5435
5584
|
import { readFile, stat as stat2 } from "fs/promises";
|
|
5436
|
-
import { resolve as
|
|
5585
|
+
import { resolve as resolve7, basename } from "path";
|
|
5437
5586
|
var TAG16 = "[whatsapp:outbound]";
|
|
5438
5587
|
var WHATSAPP_DOCUMENT_MAX_BYTES = 100 * 1024 * 1024;
|
|
5439
5588
|
var lastDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
@@ -5464,11 +5613,11 @@ async function sendWhatsAppDocument(input) {
|
|
|
5464
5613
|
if (!maxyAccountId || !platformRoot2) {
|
|
5465
5614
|
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
5466
5615
|
}
|
|
5467
|
-
const accountDir =
|
|
5616
|
+
const accountDir = resolve7(platformRoot2, "..", "data/accounts", maxyAccountId);
|
|
5468
5617
|
let resolvedPath;
|
|
5469
5618
|
try {
|
|
5470
|
-
resolvedPath =
|
|
5471
|
-
const accountResolved =
|
|
5619
|
+
resolvedPath = realpathSync3(filePath);
|
|
5620
|
+
const accountResolved = realpathSync3(accountDir);
|
|
5472
5621
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
5473
5622
|
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
5474
5623
|
console.error(`${TAG16} document REJECTED path=${sanitised} reason=outside_account_directory`);
|
|
@@ -5674,7 +5823,7 @@ async function ensureEntry(input) {
|
|
|
5674
5823
|
});
|
|
5675
5824
|
} else {
|
|
5676
5825
|
console.error(`${tag} route role=${input.role} target=public-spawn senderId=${input.senderId}`);
|
|
5677
|
-
const attachmentDir =
|
|
5826
|
+
const attachmentDir = resolve8(DATA_ROOT, "accounts", input.accountId, "uploads", "public", input.senderId);
|
|
5678
5827
|
spawned = await managerSpawn({
|
|
5679
5828
|
senderId: input.senderId,
|
|
5680
5829
|
role: input.role,
|
|
@@ -5806,12 +5955,12 @@ async function dispatchOnce(input) {
|
|
|
5806
5955
|
});
|
|
5807
5956
|
if (!entry) return { error: "spawn-failed" };
|
|
5808
5957
|
entry.lastInboundAt = Date.now();
|
|
5809
|
-
let
|
|
5958
|
+
let resolve31;
|
|
5810
5959
|
const turnPromise = new Promise((r) => {
|
|
5811
|
-
|
|
5960
|
+
resolve31 = r;
|
|
5812
5961
|
});
|
|
5813
5962
|
const listener = (text) => {
|
|
5814
|
-
|
|
5963
|
+
resolve31(text);
|
|
5815
5964
|
};
|
|
5816
5965
|
entry.subscribers.add(listener);
|
|
5817
5966
|
const writeAtMs = Date.now();
|
|
@@ -5960,6 +6109,10 @@ var app2 = new Hono();
|
|
|
5960
6109
|
app2.post("/", async (c) => {
|
|
5961
6110
|
console.log(`[chat-route] entered route=public method=POST`);
|
|
5962
6111
|
const contentType = c.req.header("content-type") ?? "";
|
|
6112
|
+
const account = resolveAccount();
|
|
6113
|
+
if (!account) {
|
|
6114
|
+
return c.json({ error: "No account configured" }, 500);
|
|
6115
|
+
}
|
|
5963
6116
|
let message;
|
|
5964
6117
|
let session_key;
|
|
5965
6118
|
let userTimestamp;
|
|
@@ -6020,7 +6173,7 @@ ${result.result.text}` : result.result.text;
|
|
|
6020
6173
|
const stored = [];
|
|
6021
6174
|
for (const file of regularFiles) {
|
|
6022
6175
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
6023
|
-
const att = await storePublicAttachment(session_key, file, buffer);
|
|
6176
|
+
const att = await storePublicAttachment(account.accountId, session_key, file, buffer);
|
|
6024
6177
|
stored.push({ name: file.name, path: att.storagePath, mime: att.mimeType, sizeBytes: att.sizeBytes });
|
|
6025
6178
|
}
|
|
6026
6179
|
attachmentCount = stored.length;
|
|
@@ -6066,10 +6219,6 @@ ${result.result.text}` : result.result.text;
|
|
|
6066
6219
|
);
|
|
6067
6220
|
const fullMessage = message + uploadNote;
|
|
6068
6221
|
const encoder = new TextEncoder();
|
|
6069
|
-
const account = resolveAccount();
|
|
6070
|
-
if (!account) {
|
|
6071
|
-
return c.json({ error: "No account configured" }, 500);
|
|
6072
|
-
}
|
|
6073
6222
|
let agentSlug;
|
|
6074
6223
|
let resolveSource;
|
|
6075
6224
|
if (requestedSlug) {
|
|
@@ -6181,7 +6330,7 @@ ${result.result.text}` : result.result.text;
|
|
|
6181
6330
|
var chat_default = app2;
|
|
6182
6331
|
|
|
6183
6332
|
// server/routes/whatsapp.ts
|
|
6184
|
-
import { join as join7, resolve as
|
|
6333
|
+
import { join as join7, resolve as resolve10 } from "path";
|
|
6185
6334
|
import { readdirSync as readdirSync3, readFileSync as readFileSync8, existsSync as existsSync5 } from "fs";
|
|
6186
6335
|
|
|
6187
6336
|
// app/lib/whatsapp/login.ts
|
|
@@ -6189,10 +6338,10 @@ import { randomUUID as randomUUID7 } from "crypto";
|
|
|
6189
6338
|
|
|
6190
6339
|
// app/lib/whatsapp/config-persist.ts
|
|
6191
6340
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "fs";
|
|
6192
|
-
import { resolve as
|
|
6341
|
+
import { resolve as resolve9, join as join5 } from "path";
|
|
6193
6342
|
var TAG18 = "[whatsapp:config]";
|
|
6194
6343
|
function configPath(accountDir) {
|
|
6195
|
-
return
|
|
6344
|
+
return resolve9(accountDir, "account.json");
|
|
6196
6345
|
}
|
|
6197
6346
|
function readConfig(accountDir) {
|
|
6198
6347
|
const path2 = configPath(accountDir);
|
|
@@ -6599,8 +6748,8 @@ async function startLogin(opts) {
|
|
|
6599
6748
|
await clearAuth(authDir);
|
|
6600
6749
|
let resolveCode = null;
|
|
6601
6750
|
let rejectCode = null;
|
|
6602
|
-
const codePromise = new Promise((
|
|
6603
|
-
resolveCode =
|
|
6751
|
+
const codePromise = new Promise((resolve31, reject) => {
|
|
6752
|
+
resolveCode = resolve31;
|
|
6604
6753
|
rejectCode = reject;
|
|
6605
6754
|
});
|
|
6606
6755
|
const codeTimer = setTimeout(
|
|
@@ -6997,14 +7146,14 @@ app3.post("/config", async (c) => {
|
|
|
6997
7146
|
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
6998
7147
|
}
|
|
6999
7148
|
case "list-public-agents": {
|
|
7000
|
-
const agentsDir =
|
|
7149
|
+
const agentsDir = resolve10(account.accountDir, "agents");
|
|
7001
7150
|
const agents = [];
|
|
7002
7151
|
if (existsSync5(agentsDir)) {
|
|
7003
7152
|
try {
|
|
7004
7153
|
const entries = readdirSync3(agentsDir, { withFileTypes: true });
|
|
7005
7154
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
7006
7155
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
7007
|
-
const configPath2 =
|
|
7156
|
+
const configPath2 = resolve10(agentsDir, entry.name, "config.json");
|
|
7008
7157
|
if (!existsSync5(configPath2)) continue;
|
|
7009
7158
|
try {
|
|
7010
7159
|
const config = JSON.parse(readFileSync8(configPath2, "utf-8"));
|
|
@@ -7277,15 +7426,17 @@ var whatsapp_default = app3;
|
|
|
7277
7426
|
|
|
7278
7427
|
// server/routes/whatsapp-reader.ts
|
|
7279
7428
|
import { readFileSync as readFileSync11, watch, statSync as statSync4, openSync, readSync, closeSync, existsSync as existsSync7, readdirSync as readdirSync6 } from "fs";
|
|
7280
|
-
import { basename as basename2, dirname, join as join10, resolve as
|
|
7429
|
+
import { basename as basename2, dirname, join as join10, resolve as resolve12, sep as sep3 } from "path";
|
|
7281
7430
|
|
|
7282
7431
|
// server/routes/admin/sidebar-sessions.ts
|
|
7283
7432
|
import { readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
|
|
7284
|
-
import { join as join8, resolve as
|
|
7433
|
+
import { join as join8, resolve as resolve11 } from "path";
|
|
7285
7434
|
var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
|
|
7286
7435
|
var CLI_MARKER_PREFIXES = ["<local-command-", "<command-name>", "<command-message>"];
|
|
7287
7436
|
var PUBLIC_ROLE = "public";
|
|
7437
|
+
var ADMIN_ROLE = "admin";
|
|
7288
7438
|
var WEBCHAT_CHANNEL = "webchat";
|
|
7439
|
+
var WHATSAPP_CHANNEL = "whatsapp";
|
|
7289
7440
|
var app4 = new Hono();
|
|
7290
7441
|
function claudeConfigDir() {
|
|
7291
7442
|
return process.env.CLAUDE_CONFIG_DIR ?? null;
|
|
@@ -7398,7 +7549,7 @@ function loadUserTitles(accountDir) {
|
|
|
7398
7549
|
return out;
|
|
7399
7550
|
}
|
|
7400
7551
|
function readSidecarMeta(metaPath) {
|
|
7401
|
-
const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null };
|
|
7552
|
+
const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null };
|
|
7402
7553
|
let raw;
|
|
7403
7554
|
try {
|
|
7404
7555
|
raw = readFileSync9(metaPath, "utf8");
|
|
@@ -7419,7 +7570,9 @@ function readSidecarMeta(metaPath) {
|
|
|
7419
7570
|
role: typeof r.role === "string" ? r.role : null,
|
|
7420
7571
|
channel: typeof r.channel === "string" ? r.channel : null,
|
|
7421
7572
|
senderId: typeof r.senderId === "string" ? r.senderId : null,
|
|
7422
|
-
startedAt: typeof r.startedAt === "string" ? r.startedAt : null
|
|
7573
|
+
startedAt: typeof r.startedAt === "string" ? r.startedAt : null,
|
|
7574
|
+
personId: typeof r.personId === "string" ? r.personId : null,
|
|
7575
|
+
adminUserId: typeof r.adminUserId === "string" ? r.adminUserId : null
|
|
7423
7576
|
};
|
|
7424
7577
|
}
|
|
7425
7578
|
function startsWithCliMarker(s) {
|
|
@@ -7429,9 +7582,29 @@ function startsWithCliMarker(s) {
|
|
|
7429
7582
|
function trimToTitle(raw) {
|
|
7430
7583
|
return raw.trim().replace(/\s+/g, " ");
|
|
7431
7584
|
}
|
|
7585
|
+
var CHANNEL_WRAPPER = /^<channel\b([^>]*)>\n?([\s\S]*?)\n?<\/channel>$/;
|
|
7586
|
+
var SOURCE_ATTR = /\bsource="([^"]+)"/;
|
|
7587
|
+
var CHANNEL_SOURCE_MAP = {
|
|
7588
|
+
whatsapp: "whatsapp",
|
|
7589
|
+
"whatsapp-channel": "whatsapp",
|
|
7590
|
+
webchat: "webchat",
|
|
7591
|
+
"webchat-channel": "webchat"
|
|
7592
|
+
};
|
|
7593
|
+
function stripChannelEnvelope(text) {
|
|
7594
|
+
const m = text.match(CHANNEL_WRAPPER);
|
|
7595
|
+
if (!m) return { inner: text, channel: null };
|
|
7596
|
+
let raw = m[1].match(SOURCE_ATTR)?.[1] ?? "";
|
|
7597
|
+
let inner = m[2];
|
|
7598
|
+
const im = inner.match(CHANNEL_WRAPPER);
|
|
7599
|
+
const innerRaw = im?.[1].match(SOURCE_ATTR)?.[1];
|
|
7600
|
+
if (im && innerRaw) {
|
|
7601
|
+
raw = innerRaw;
|
|
7602
|
+
inner = im[2];
|
|
7603
|
+
}
|
|
7604
|
+
return { inner, channel: CHANNEL_SOURCE_MAP[raw] ?? (raw || null) };
|
|
7605
|
+
}
|
|
7432
7606
|
function resolveTitle(body, sessionId, userTitles) {
|
|
7433
7607
|
const userTitle = userTitles.get(sessionId);
|
|
7434
|
-
if (userTitle) return { title: trimToTitle(userTitle), source: "user" };
|
|
7435
7608
|
let aiTitle = null;
|
|
7436
7609
|
let firstUserMessage = null;
|
|
7437
7610
|
for (const line of body.split("\n")) {
|
|
@@ -7458,9 +7631,12 @@ function resolveTitle(body, sessionId, userTitles) {
|
|
|
7458
7631
|
firstUserMessage = trimmed;
|
|
7459
7632
|
}
|
|
7460
7633
|
}
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
return { title:
|
|
7634
|
+
const stripped = firstUserMessage !== null ? stripChannelEnvelope(firstUserMessage) : { inner: null, channel: null };
|
|
7635
|
+
const channel = stripped.channel;
|
|
7636
|
+
if (userTitle) return { title: trimToTitle(userTitle), source: "user", channel };
|
|
7637
|
+
if (aiTitle) return { title: trimToTitle(aiTitle), source: "ai", channel };
|
|
7638
|
+
if (stripped.inner) return { title: trimToTitle(stripped.inner), source: "first-message", channel };
|
|
7639
|
+
return { title: sessionId.slice(0, 8), source: "prefix", channel };
|
|
7464
7640
|
}
|
|
7465
7641
|
app4.get("/", requireAdminSession, async (c) => {
|
|
7466
7642
|
const accountId = process.env.ACCOUNT_ID ?? null;
|
|
@@ -7472,7 +7648,7 @@ app4.get("/", requireAdminSession, async (c) => {
|
|
|
7472
7648
|
const projectsRoot = join8(configDir2, "projects");
|
|
7473
7649
|
const jsonls = enumerateJsonls(projectsRoot);
|
|
7474
7650
|
const liveSessions = await fetchLiveSessions();
|
|
7475
|
-
const accountDir = accountId ?
|
|
7651
|
+
const accountDir = accountId ? resolve11(ACCOUNTS_DIR, accountId) : null;
|
|
7476
7652
|
const userTitles = loadUserTitles(accountDir);
|
|
7477
7653
|
const rows = [];
|
|
7478
7654
|
const seenIds = /* @__PURE__ */ new Set();
|
|
@@ -7481,6 +7657,8 @@ app4.get("/", requireAdminSession, async (c) => {
|
|
|
7481
7657
|
let nonResumableCount = 0;
|
|
7482
7658
|
let archivedCount = 0;
|
|
7483
7659
|
const titleSourceCounts = { user: 0, ai: 0, "first-message": 0, prefix: 0 };
|
|
7660
|
+
const personIdBySession = /* @__PURE__ */ new Map();
|
|
7661
|
+
const adminUserIdBySession = /* @__PURE__ */ new Map();
|
|
7484
7662
|
for (const { path: path2, isSubagent, archived } of jsonls) {
|
|
7485
7663
|
const lastSlash = path2.lastIndexOf("/");
|
|
7486
7664
|
const base = path2.slice(lastSlash + 1);
|
|
@@ -7504,8 +7682,12 @@ app4.get("/", requireAdminSession, async (c) => {
|
|
|
7504
7682
|
const resolved = resolveTitle(body, sessionId, userTitles);
|
|
7505
7683
|
titleSourceCounts[resolved.source] += 1;
|
|
7506
7684
|
const metaPath = join8(projectDir, `${sessionId}.meta.json`);
|
|
7507
|
-
const { bridgeIds, role, channel } = readSidecarMeta(metaPath);
|
|
7508
|
-
const resumable = !(role === PUBLIC_ROLE &&
|
|
7685
|
+
const { bridgeIds, role, channel: sidecarChannel, personId, adminUserId } = readSidecarMeta(metaPath);
|
|
7686
|
+
const resumable = !(role === PUBLIC_ROLE && (sidecarChannel === WEBCHAT_CHANNEL || sidecarChannel === WHATSAPP_CHANNEL));
|
|
7687
|
+
if (personId) personIdBySession.set(sessionId, personId);
|
|
7688
|
+
if (adminUserId && role === ADMIN_ROLE && sidecarChannel === WEBCHAT_CHANNEL) {
|
|
7689
|
+
adminUserIdBySession.set(sessionId, adminUserId);
|
|
7690
|
+
}
|
|
7509
7691
|
rows.push({
|
|
7510
7692
|
sessionId,
|
|
7511
7693
|
title: resolved.title,
|
|
@@ -7517,30 +7699,59 @@ app4.get("/", requireAdminSession, async (c) => {
|
|
|
7517
7699
|
projectDir,
|
|
7518
7700
|
bridgeIds,
|
|
7519
7701
|
resumable,
|
|
7520
|
-
archived
|
|
7702
|
+
archived,
|
|
7703
|
+
channel: resolved.channel,
|
|
7704
|
+
personName: null
|
|
7521
7705
|
});
|
|
7522
7706
|
if (live) liveCount += 1;
|
|
7523
7707
|
if (isSubagent) subagentCount += 1;
|
|
7524
7708
|
if (!resumable) nonResumableCount += 1;
|
|
7525
7709
|
if (archived) archivedCount += 1;
|
|
7526
7710
|
}
|
|
7711
|
+
let personNameCount = 0;
|
|
7712
|
+
let adminNameCount = 0;
|
|
7713
|
+
if (accountId) {
|
|
7714
|
+
await Promise.all(
|
|
7715
|
+
rows.map(async (row) => {
|
|
7716
|
+
if (row.titleSource !== "first-message" && row.titleSource !== "prefix") return;
|
|
7717
|
+
const pid = personIdBySession.get(row.sessionId);
|
|
7718
|
+
if (pid) {
|
|
7719
|
+
const name = await loadPersonNameByElementId(accountId, pid);
|
|
7720
|
+
if (name) {
|
|
7721
|
+
row.personName = name;
|
|
7722
|
+
personNameCount += 1;
|
|
7723
|
+
}
|
|
7724
|
+
return;
|
|
7725
|
+
}
|
|
7726
|
+
const auid = adminUserIdBySession.get(row.sessionId);
|
|
7727
|
+
if (auid) {
|
|
7728
|
+
const resolved = await loadAdminUserName(accountId, auid);
|
|
7729
|
+
if (resolved.source === "neo4j") {
|
|
7730
|
+
row.personName = resolved.joined;
|
|
7731
|
+
adminNameCount += 1;
|
|
7732
|
+
}
|
|
7733
|
+
}
|
|
7734
|
+
})
|
|
7735
|
+
);
|
|
7736
|
+
}
|
|
7527
7737
|
rows.sort((a, b) => a.startedAt < b.startedAt ? 1 : -1);
|
|
7528
7738
|
console.log(
|
|
7529
|
-
`[admin-sessions-list] rows=${rows.length} live=${liveCount} subagents=${subagentCount} archived=${archivedCount} nonResumable=${nonResumableCount} titles user=${titleSourceCounts.user} ai=${titleSourceCounts.ai} first=${titleSourceCounts["first-message"]} prefix=${titleSourceCounts.prefix} accountId=${accountId ? accountId.slice(0, 8) : "unset"}`
|
|
7739
|
+
`[admin-sessions-list] rows=${rows.length} live=${liveCount} subagents=${subagentCount} archived=${archivedCount} nonResumable=${nonResumableCount} titles user=${titleSourceCounts.user} ai=${titleSourceCounts.ai} first=${titleSourceCounts["first-message"]} prefix=${titleSourceCounts.prefix} personNames=${personNameCount} adminNames=${adminNameCount} accountId=${accountId ? accountId.slice(0, 8) : "unset"}`
|
|
7530
7740
|
);
|
|
7531
7741
|
return c.json({ sessions: rows, accountId });
|
|
7532
7742
|
});
|
|
7533
7743
|
var sidebar_sessions_default = app4;
|
|
7534
7744
|
|
|
7535
7745
|
// app/lib/whatsapp-reader/select-sessions.ts
|
|
7536
|
-
function
|
|
7537
|
-
return rows.filter((r) => r.role === "admin" && (r.channel === "whatsapp" || r.channel === "telegram")).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).map((r) => ({
|
|
7746
|
+
function selectReaderChannelSessions(rows) {
|
|
7747
|
+
return rows.filter((r) => r.role === "admin" && (r.channel === "whatsapp" || r.channel === "telegram") || r.role === "public" && (r.channel === "whatsapp" || r.channel === "webchat")).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).map((r) => ({
|
|
7538
7748
|
sessionId: r.sessionId,
|
|
7539
7749
|
projectDir: r.projectDir,
|
|
7540
7750
|
title: r.title,
|
|
7541
7751
|
senderId: r.senderId,
|
|
7542
7752
|
startedAt: r.startedAt,
|
|
7543
7753
|
channel: r.channel,
|
|
7754
|
+
role: r.role,
|
|
7544
7755
|
operatorName: null,
|
|
7545
7756
|
whatsappName: null,
|
|
7546
7757
|
lastMessageAt: r.lastMessageAt
|
|
@@ -7644,14 +7855,26 @@ function resolveOperatorDisplay(userIdRes, nameRes) {
|
|
|
7644
7855
|
return { name: null, reason: "no-person-name" };
|
|
7645
7856
|
}
|
|
7646
7857
|
|
|
7858
|
+
// app/lib/agent-errors.ts
|
|
7859
|
+
function classifyAgentError(err) {
|
|
7860
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7861
|
+
if (/\b(401|authentication_error|OAuth token has expired)\b/i.test(msg)) return "auth_expired";
|
|
7862
|
+
if (/\b(529|overloaded)\b/i.test(msg)) return "claude_overloaded";
|
|
7863
|
+
if (/\b(404|model_not_found)\b/i.test(msg) || /issue with the selected model|may not have access to it/i.test(msg))
|
|
7864
|
+
return "model_unavailable";
|
|
7865
|
+
if (/\b(500|api_error|internal server error)\b/i.test(msg)) return "claude_down";
|
|
7866
|
+
return "agent_error";
|
|
7867
|
+
}
|
|
7868
|
+
|
|
7647
7869
|
// app/lib/whatsapp-reader/parse-transcript.ts
|
|
7648
7870
|
var WA_REPLY_TOOL = "mcp__whatsapp-channel__reply";
|
|
7649
7871
|
var WA_REPLY_DOCUMENT_TOOL = "mcp__whatsapp-channel__reply-document";
|
|
7650
7872
|
var WEBCHAT_REPLY_TOOL = "mcp__webchat-channel__reply";
|
|
7651
7873
|
var CHANNEL_REPLY_TOOL = "mcp__channel__reply";
|
|
7874
|
+
var CHANNEL_REPLY_DOCUMENT_TOOL = "mcp__channel__reply-document";
|
|
7652
7875
|
var CLI_MARKER_PREFIXES2 = ["<local-command-", "<command-name>", "<command-message>"];
|
|
7653
|
-
var
|
|
7654
|
-
var
|
|
7876
|
+
var CHANNEL_WRAPPER2 = /^<channel\b([^>]*)>\n?([\s\S]*?)\n?<\/channel>$/;
|
|
7877
|
+
var SOURCE_ATTR2 = /\bsource="([^"]+)"/;
|
|
7655
7878
|
var SOURCE_MAP = {
|
|
7656
7879
|
whatsapp: "whatsapp",
|
|
7657
7880
|
"whatsapp-channel": "whatsapp",
|
|
@@ -7659,13 +7882,13 @@ var SOURCE_MAP = {
|
|
|
7659
7882
|
"webchat-channel": "webchat"
|
|
7660
7883
|
};
|
|
7661
7884
|
function unwrapChannel(text) {
|
|
7662
|
-
const m = text.match(
|
|
7885
|
+
const m = text.match(CHANNEL_WRAPPER2);
|
|
7663
7886
|
if (!m) return { text };
|
|
7664
|
-
let source = SOURCE_MAP[m[1].match(
|
|
7887
|
+
let source = SOURCE_MAP[m[1].match(SOURCE_ATTR2)?.[1] ?? ""];
|
|
7665
7888
|
let inner = m[2];
|
|
7666
|
-
const im = inner.match(
|
|
7889
|
+
const im = inner.match(CHANNEL_WRAPPER2);
|
|
7667
7890
|
if (im) {
|
|
7668
|
-
const innerSource = SOURCE_MAP[im[1].match(
|
|
7891
|
+
const innerSource = SOURCE_MAP[im[1].match(SOURCE_ATTR2)?.[1] ?? ""];
|
|
7669
7892
|
if (innerSource) {
|
|
7670
7893
|
source = innerSource;
|
|
7671
7894
|
inner = im[2];
|
|
@@ -7679,6 +7902,17 @@ function asString(content) {
|
|
|
7679
7902
|
function tsOf(row) {
|
|
7680
7903
|
return typeof row.timestamp === "string" ? row.timestamp : null;
|
|
7681
7904
|
}
|
|
7905
|
+
function joinTextBlocks(content) {
|
|
7906
|
+
if (!Array.isArray(content)) return "";
|
|
7907
|
+
const parts = [];
|
|
7908
|
+
for (const block of content) {
|
|
7909
|
+
if (block && typeof block === "object") {
|
|
7910
|
+
const b = block;
|
|
7911
|
+
if (b.type === "text" && typeof b.text === "string") parts.push(b.text);
|
|
7912
|
+
}
|
|
7913
|
+
}
|
|
7914
|
+
return parts.join("");
|
|
7915
|
+
}
|
|
7682
7916
|
function assistantTurns(content, ts) {
|
|
7683
7917
|
if (!Array.isArray(content)) return [];
|
|
7684
7918
|
const out = [];
|
|
@@ -7690,7 +7924,7 @@ function assistantTurns(content, ts) {
|
|
|
7690
7924
|
} else if (b.type === "tool_use" && (b.name === WA_REPLY_TOOL || b.name === WEBCHAT_REPLY_TOOL || b.name === CHANNEL_REPLY_TOOL)) {
|
|
7691
7925
|
const text = b.input?.text;
|
|
7692
7926
|
out.push({ kind: "agent-reply", text: typeof text === "string" ? text : "", ts });
|
|
7693
|
-
} else if (b.type === "tool_use" && b.name === WA_REPLY_DOCUMENT_TOOL) {
|
|
7927
|
+
} else if (b.type === "tool_use" && (b.name === WA_REPLY_DOCUMENT_TOOL || b.name === CHANNEL_REPLY_DOCUMENT_TOOL)) {
|
|
7694
7928
|
const input = b.input ?? {};
|
|
7695
7929
|
const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
|
|
7696
7930
|
const caption = typeof input.caption === "string" ? input.caption : null;
|
|
@@ -7736,6 +7970,14 @@ function parseTranscript(lines) {
|
|
|
7736
7970
|
const ts = tsOf(row);
|
|
7737
7971
|
const msg = row.message;
|
|
7738
7972
|
if (row.type === "assistant") {
|
|
7973
|
+
if (row.isApiErrorMessage === true) {
|
|
7974
|
+
const raw = joinTextBlocks(msg?.content);
|
|
7975
|
+
const status = typeof row.apiErrorStatus === "number" ? String(row.apiErrorStatus) : "";
|
|
7976
|
+
const errorSlug = typeof row.error === "string" ? row.error : "";
|
|
7977
|
+
const code = classifyAgentError(`${status} ${errorSlug} ${raw}`.trim());
|
|
7978
|
+
out.push({ kind: "agent-error", code, raw, ts });
|
|
7979
|
+
continue;
|
|
7980
|
+
}
|
|
7739
7981
|
out.push(...assistantTurns(msg?.content, ts));
|
|
7740
7982
|
continue;
|
|
7741
7983
|
}
|
|
@@ -7799,7 +8041,9 @@ app5.get("/conversations", requireAdminSession, async (c) => {
|
|
|
7799
8041
|
const sessionId = basename2(path2).replace(/\.jsonl$/, "");
|
|
7800
8042
|
const projectDir = dirname(path2);
|
|
7801
8043
|
const meta = readSidecarMeta(join10(projectDir, `${sessionId}.meta.json`));
|
|
7802
|
-
|
|
8044
|
+
const isAdminChannel = meta.role === "admin" && (meta.channel === "whatsapp" || meta.channel === "telegram");
|
|
8045
|
+
const isPublicChannel = meta.role === "public" && (meta.channel === "whatsapp" || meta.channel === "webchat");
|
|
8046
|
+
if (!isAdminChannel && !isPublicChannel) continue;
|
|
7803
8047
|
let body = "";
|
|
7804
8048
|
try {
|
|
7805
8049
|
body = readFileSync11(path2, "utf8");
|
|
@@ -7818,7 +8062,7 @@ app5.get("/conversations", requireAdminSession, async (c) => {
|
|
|
7818
8062
|
lastMessageAt
|
|
7819
8063
|
});
|
|
7820
8064
|
}
|
|
7821
|
-
const selected =
|
|
8065
|
+
const selected = selectReaderChannelSessions(rows);
|
|
7822
8066
|
const account = resolveAccount();
|
|
7823
8067
|
let users = [];
|
|
7824
8068
|
if (account) {
|
|
@@ -7885,12 +8129,12 @@ app5.get("/stream", requireAdminSession, (c) => {
|
|
|
7885
8129
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
7886
8130
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
7887
8131
|
const cfg = claudeConfigDir();
|
|
7888
|
-
const projectsRoot = cfg ?
|
|
8132
|
+
const projectsRoot = cfg ? resolve12(join10(cfg, "projects")) : null;
|
|
7889
8133
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
7890
8134
|
return c.json({ error: "bad session reference" }, 400);
|
|
7891
8135
|
}
|
|
7892
|
-
const jsonlPath =
|
|
7893
|
-
if (!jsonlPath.startsWith(projectsRoot +
|
|
8136
|
+
const jsonlPath = resolve12(join10(projectDir, `${sessionId}.jsonl`));
|
|
8137
|
+
if (!jsonlPath.startsWith(projectsRoot + sep3)) {
|
|
7894
8138
|
return c.json({ error: "bad session reference" }, 400);
|
|
7895
8139
|
}
|
|
7896
8140
|
const encoder = new TextEncoder();
|
|
@@ -7921,6 +8165,9 @@ data: ${JSON.stringify(turn)}
|
|
|
7921
8165
|
let n = 0;
|
|
7922
8166
|
for (const turn of parseTranscript(lines)) {
|
|
7923
8167
|
send(controller, turn, offset);
|
|
8168
|
+
if (turn.kind === "agent-error") {
|
|
8169
|
+
console.log(`[wa-stream] op=agent-error conn=${connId} sessionId=${sessionId} code=${turn.code} source=jsonl`);
|
|
8170
|
+
}
|
|
7924
8171
|
n++;
|
|
7925
8172
|
}
|
|
7926
8173
|
if (n > 0) console.log(`[wa-stream] op=drain conn=${connId} turns=${n}`);
|
|
@@ -7934,6 +8181,9 @@ data: ${JSON.stringify(turn)}
|
|
|
7934
8181
|
let n = 0;
|
|
7935
8182
|
for (const turn of parseTranscript(buf.toString("utf8").split("\n"))) {
|
|
7936
8183
|
send(controller, turn, offset);
|
|
8184
|
+
if (turn.kind === "agent-error") {
|
|
8185
|
+
console.log(`[wa-stream] op=agent-error conn=${connId} sessionId=${sessionId} code=${turn.code} source=jsonl`);
|
|
8186
|
+
}
|
|
7937
8187
|
n++;
|
|
7938
8188
|
}
|
|
7939
8189
|
console.log(`[wa-stream] op=backlog conn=${connId} turns=${n} bytes=${end}`);
|
|
@@ -7976,7 +8226,7 @@ var DIRECTIVE_NAME_RE = /^\d+-\d+\.txt$/;
|
|
|
7976
8226
|
function directiveStoreDir(sessionId) {
|
|
7977
8227
|
const account = resolveAccount();
|
|
7978
8228
|
if (!account) return null;
|
|
7979
|
-
return
|
|
8229
|
+
return resolve12(account.accountDir, "logs", "prompt-optimiser-directives", sessionId);
|
|
7980
8230
|
}
|
|
7981
8231
|
app5.get("/directives", requireAdminSession, (c) => {
|
|
7982
8232
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
@@ -8010,8 +8260,8 @@ app5.get("/directive", requireAdminSession, (c) => {
|
|
|
8010
8260
|
}
|
|
8011
8261
|
const dir = directiveStoreDir(sessionId);
|
|
8012
8262
|
if (!dir) return c.json({ error: "no account" }, 404);
|
|
8013
|
-
const path2 =
|
|
8014
|
-
if (!path2.startsWith(dir +
|
|
8263
|
+
const path2 = resolve12(join10(dir, name));
|
|
8264
|
+
if (!path2.startsWith(dir + sep3)) return c.json({ error: "bad reference" }, 400);
|
|
8015
8265
|
let body;
|
|
8016
8266
|
try {
|
|
8017
8267
|
body = readFileSync11(path2, "utf8");
|
|
@@ -8347,7 +8597,7 @@ ${note}` : note;
|
|
|
8347
8597
|
}
|
|
8348
8598
|
|
|
8349
8599
|
// server/routes/webchat-greeting.ts
|
|
8350
|
-
import { resolve as
|
|
8600
|
+
import { resolve as resolve13 } from "path";
|
|
8351
8601
|
|
|
8352
8602
|
// app/lib/claude-agent/specialist-roster.ts
|
|
8353
8603
|
import { existsSync as existsSync9, readFileSync as readFileSync13, readdirSync as readdirSync8 } from "fs";
|
|
@@ -8452,7 +8702,7 @@ app6.get("/", requireAdminSession, async (c) => {
|
|
|
8452
8702
|
let specialists = [];
|
|
8453
8703
|
try {
|
|
8454
8704
|
const roster = readSpecialistRoster(
|
|
8455
|
-
|
|
8705
|
+
resolve13(ACCOUNTS_DIR, accountId, "specialists", "agents")
|
|
8456
8706
|
);
|
|
8457
8707
|
specialists = roster.specialists;
|
|
8458
8708
|
for (const s of roster.skipped) {
|
|
@@ -8487,7 +8737,7 @@ var webchat_greeting_default = app6;
|
|
|
8487
8737
|
|
|
8488
8738
|
// server/routes/telegram.ts
|
|
8489
8739
|
import { homedir } from "os";
|
|
8490
|
-
import { resolve as
|
|
8740
|
+
import { resolve as resolve14, join as join13 } from "path";
|
|
8491
8741
|
import { existsSync as existsSync10, readFileSync as readFileSync14 } from "fs";
|
|
8492
8742
|
|
|
8493
8743
|
// app/lib/telegram/access-control.ts
|
|
@@ -8538,7 +8788,7 @@ function routeTelegramUpdate(input) {
|
|
|
8538
8788
|
var TAG22 = "[telegram-inbound]";
|
|
8539
8789
|
var DISPATCH_TIMEOUT_MS = 12e4;
|
|
8540
8790
|
function configDirName() {
|
|
8541
|
-
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
8791
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve14(process.cwd(), "..");
|
|
8542
8792
|
const brandPath = join13(platformRoot2, "config", "brand.json");
|
|
8543
8793
|
if (existsSync10(brandPath)) {
|
|
8544
8794
|
try {
|
|
@@ -8550,7 +8800,7 @@ function configDirName() {
|
|
|
8550
8800
|
}
|
|
8551
8801
|
function secretPath(botType) {
|
|
8552
8802
|
const filename = botType === "admin" ? ".telegram-admin-webhook-secret" : ".telegram-webhook-secret";
|
|
8553
|
-
return
|
|
8803
|
+
return resolve14(homedir(), configDirName(), filename);
|
|
8554
8804
|
}
|
|
8555
8805
|
async function sendTelegram(botToken, chatId, text) {
|
|
8556
8806
|
try {
|
|
@@ -9153,7 +9403,7 @@ app9.post("/", async (c) => {
|
|
|
9153
9403
|
const safe = typeof v === "string" ? truncate(v, 200) : typeof v === "number" || typeof v === "boolean" ? String(v) : JSON.stringify(v).slice(0, 200);
|
|
9154
9404
|
return `${k}=${safe}`;
|
|
9155
9405
|
}).join(" ");
|
|
9156
|
-
const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set(["stream-log-click"]);
|
|
9406
|
+
const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set(["stream-log-click", "pwa"]);
|
|
9157
9407
|
if (TAGGED_PREFIX_SOURCES.has(source)) {
|
|
9158
9408
|
console.log(
|
|
9159
9409
|
`[${source}] ts=${ts} ip=${ip} version=${version || "unknown"}${extra ? " " + extra : ""}`
|
|
@@ -9365,7 +9615,7 @@ var session_default = app10;
|
|
|
9365
9615
|
|
|
9366
9616
|
// server/routes/admin/logs.ts
|
|
9367
9617
|
import { existsSync as existsSync15, readdirSync as readdirSync10, readFileSync as readFileSync17, statSync as statSync7 } from "fs";
|
|
9368
|
-
import { resolve as
|
|
9618
|
+
import { resolve as resolve15, basename as basename4 } from "path";
|
|
9369
9619
|
|
|
9370
9620
|
// app/lib/logs-read-resolve.ts
|
|
9371
9621
|
import { existsSync as existsSync14 } from "fs";
|
|
@@ -9392,7 +9642,7 @@ app11.get("/", async (c) => {
|
|
|
9392
9642
|
const cacheKeyParam = c.req.query("cacheKey");
|
|
9393
9643
|
const download = c.req.query("download") === "1";
|
|
9394
9644
|
const account = resolveAccount();
|
|
9395
|
-
const accountLogDir = account ?
|
|
9645
|
+
const accountLogDir = account ? resolve15(account.accountDir, "logs") : null;
|
|
9396
9646
|
const logDirs = [];
|
|
9397
9647
|
if (accountLogDir) logDirs.push(accountLogDir);
|
|
9398
9648
|
logDirs.push(LOG_DIR);
|
|
@@ -9400,7 +9650,7 @@ app11.get("/", async (c) => {
|
|
|
9400
9650
|
const safe = basename4(fileParam);
|
|
9401
9651
|
const searched = [];
|
|
9402
9652
|
for (const dir of logDirs) {
|
|
9403
|
-
const filePath =
|
|
9653
|
+
const filePath = resolve15(dir, safe);
|
|
9404
9654
|
searched.push(filePath);
|
|
9405
9655
|
try {
|
|
9406
9656
|
const buffer = readFileSync17(filePath);
|
|
@@ -9512,10 +9762,10 @@ app11.get("/", async (c) => {
|
|
|
9512
9762
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
9513
9763
|
continue;
|
|
9514
9764
|
}
|
|
9515
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync7(
|
|
9765
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync7(resolve15(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
9516
9766
|
seen.add(name);
|
|
9517
9767
|
try {
|
|
9518
|
-
const content = readFileSync17(
|
|
9768
|
+
const content = readFileSync17(resolve15(dir, name));
|
|
9519
9769
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
9520
9770
|
logs[name] = tail.trim() || "(empty)";
|
|
9521
9771
|
} catch (err) {
|
|
@@ -9555,7 +9805,7 @@ var claude_info_default = app12;
|
|
|
9555
9805
|
// server/routes/admin/attachment.ts
|
|
9556
9806
|
import { readFile as readFile2, readdir } from "fs/promises";
|
|
9557
9807
|
import { existsSync as existsSync16 } from "fs";
|
|
9558
|
-
import { resolve as
|
|
9808
|
+
import { resolve as resolve16 } from "path";
|
|
9559
9809
|
var app13 = new Hono();
|
|
9560
9810
|
app13.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
9561
9811
|
const attachmentId = c.req.param("attachmentId");
|
|
@@ -9567,11 +9817,11 @@ app13.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
9567
9817
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
|
|
9568
9818
|
return new Response("Not found", { status: 404 });
|
|
9569
9819
|
}
|
|
9570
|
-
const dir =
|
|
9820
|
+
const dir = resolve16(DATA_ROOT, "accounts", accountId, "uploads", attachmentId);
|
|
9571
9821
|
if (!existsSync16(dir)) {
|
|
9572
9822
|
return new Response("Not found", { status: 404 });
|
|
9573
9823
|
}
|
|
9574
|
-
const metaPath =
|
|
9824
|
+
const metaPath = resolve16(dir, `${attachmentId}.meta.json`);
|
|
9575
9825
|
if (!existsSync16(metaPath)) {
|
|
9576
9826
|
return new Response("Not found", { status: 404 });
|
|
9577
9827
|
}
|
|
@@ -9586,7 +9836,7 @@ app13.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
9586
9836
|
if (!dataFile) {
|
|
9587
9837
|
return new Response("Not found", { status: 404 });
|
|
9588
9838
|
}
|
|
9589
|
-
const filePath =
|
|
9839
|
+
const filePath = resolve16(dir, dataFile);
|
|
9590
9840
|
const buffer = await readFile2(filePath);
|
|
9591
9841
|
return new Response(new Uint8Array(buffer), {
|
|
9592
9842
|
headers: {
|
|
@@ -9599,13 +9849,13 @@ app13.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
9599
9849
|
var attachment_default = app13;
|
|
9600
9850
|
|
|
9601
9851
|
// server/routes/admin/agents.ts
|
|
9602
|
-
import { resolve as
|
|
9852
|
+
import { resolve as resolve17 } from "path";
|
|
9603
9853
|
import { readdirSync as readdirSync11, readFileSync as readFileSync18, existsSync as existsSync17, rmSync } from "fs";
|
|
9604
9854
|
var app14 = new Hono();
|
|
9605
9855
|
app14.get("/", (c) => {
|
|
9606
9856
|
const account = resolveAccount();
|
|
9607
9857
|
if (!account) return c.json({ agents: [] });
|
|
9608
|
-
const agentsDir =
|
|
9858
|
+
const agentsDir = resolve17(account.accountDir, "agents");
|
|
9609
9859
|
if (!existsSync17(agentsDir)) return c.json({ agents: [] });
|
|
9610
9860
|
const agents = [];
|
|
9611
9861
|
try {
|
|
@@ -9613,7 +9863,7 @@ app14.get("/", (c) => {
|
|
|
9613
9863
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
9614
9864
|
if (!entry.isDirectory()) continue;
|
|
9615
9865
|
if (entry.name === "admin") continue;
|
|
9616
|
-
const configPath2 =
|
|
9866
|
+
const configPath2 = resolve17(agentsDir, entry.name, "config.json");
|
|
9617
9867
|
if (!existsSync17(configPath2)) continue;
|
|
9618
9868
|
try {
|
|
9619
9869
|
const config = JSON.parse(readFileSync18(configPath2, "utf-8"));
|
|
@@ -9642,7 +9892,7 @@ app14.delete("/:slug", async (c) => {
|
|
|
9642
9892
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
9643
9893
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
9644
9894
|
}
|
|
9645
|
-
const agentDir =
|
|
9895
|
+
const agentDir = resolve17(account.accountDir, "agents", slug);
|
|
9646
9896
|
if (!existsSync17(agentDir)) {
|
|
9647
9897
|
return c.json({ error: "Agent not found" }, 404);
|
|
9648
9898
|
}
|
|
@@ -9672,7 +9922,7 @@ app14.post("/:slug/project", async (c) => {
|
|
|
9672
9922
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
9673
9923
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
9674
9924
|
}
|
|
9675
|
-
const agentDir =
|
|
9925
|
+
const agentDir = resolve17(account.accountDir, "agents", slug);
|
|
9676
9926
|
if (!existsSync17(agentDir)) {
|
|
9677
9927
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
9678
9928
|
}
|
|
@@ -10376,7 +10626,7 @@ var sessions_default = app15;
|
|
|
10376
10626
|
|
|
10377
10627
|
// app/lib/claude-agent/spawn-context.ts
|
|
10378
10628
|
import { existsSync as existsSync19, readFileSync as readFileSync19, readdirSync as readdirSync12, statSync as statSync8 } from "fs";
|
|
10379
|
-
import { dirname as dirname4, resolve as
|
|
10629
|
+
import { dirname as dirname4, resolve as resolve18, join as join17 } from "path";
|
|
10380
10630
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
10381
10631
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
10382
10632
|
try {
|
|
@@ -10461,7 +10711,7 @@ function parseSkillPathsFromFrontmatter(fm) {
|
|
|
10461
10711
|
}
|
|
10462
10712
|
function listPluginDirs() {
|
|
10463
10713
|
const out = /* @__PURE__ */ new Map();
|
|
10464
|
-
const platformPlugins =
|
|
10714
|
+
const platformPlugins = resolve18(PLATFORM_ROOT, "plugins");
|
|
10465
10715
|
if (existsSync19(platformPlugins)) {
|
|
10466
10716
|
for (const name of readdirSync12(platformPlugins)) {
|
|
10467
10717
|
const dir = join17(platformPlugins, name);
|
|
@@ -10471,7 +10721,7 @@ function listPluginDirs() {
|
|
|
10471
10721
|
}
|
|
10472
10722
|
}
|
|
10473
10723
|
}
|
|
10474
|
-
const premiumRoot =
|
|
10724
|
+
const premiumRoot = resolve18(dirname4(PLATFORM_ROOT), "premium-plugins");
|
|
10475
10725
|
if (existsSync19(premiumRoot)) {
|
|
10476
10726
|
for (const bundle of readdirSync12(premiumRoot)) {
|
|
10477
10727
|
const bundlePlugins = join17(premiumRoot, bundle, "plugins");
|
|
@@ -10491,7 +10741,7 @@ function listPluginDirs() {
|
|
|
10491
10741
|
return out;
|
|
10492
10742
|
}
|
|
10493
10743
|
function readEnabledPlugins(accountId) {
|
|
10494
|
-
const accountFile =
|
|
10744
|
+
const accountFile = resolve18(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
10495
10745
|
if (!existsSync19(accountFile)) return /* @__PURE__ */ new Set();
|
|
10496
10746
|
try {
|
|
10497
10747
|
const parsed = JSON.parse(readFileSync19(accountFile, "utf-8"));
|
|
@@ -10564,7 +10814,7 @@ ${skillFm}
|
|
|
10564
10814
|
return out;
|
|
10565
10815
|
}
|
|
10566
10816
|
function computeSpecialistDomains(accountId) {
|
|
10567
|
-
const specialistsDir =
|
|
10817
|
+
const specialistsDir = resolve18(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
|
|
10568
10818
|
if (!existsSync19(specialistsDir)) return [];
|
|
10569
10819
|
let entries;
|
|
10570
10820
|
try {
|
|
@@ -10610,7 +10860,7 @@ ${fm}
|
|
|
10610
10860
|
return out;
|
|
10611
10861
|
}
|
|
10612
10862
|
function computeDormantPlugins(accountId) {
|
|
10613
|
-
const accountFile =
|
|
10863
|
+
const accountFile = resolve18(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
10614
10864
|
if (!existsSync19(accountFile)) return [];
|
|
10615
10865
|
let enabled;
|
|
10616
10866
|
try {
|
|
@@ -10625,7 +10875,7 @@ function computeDormantPlugins(accountId) {
|
|
|
10625
10875
|
);
|
|
10626
10876
|
return [];
|
|
10627
10877
|
}
|
|
10628
|
-
const pluginsDir =
|
|
10878
|
+
const pluginsDir = resolve18(PLATFORM_ROOT, "plugins");
|
|
10629
10879
|
if (!existsSync19(pluginsDir)) return [];
|
|
10630
10880
|
let entries;
|
|
10631
10881
|
try {
|
|
@@ -10636,7 +10886,7 @@ function computeDormantPlugins(accountId) {
|
|
|
10636
10886
|
const out = [];
|
|
10637
10887
|
for (const name of entries) {
|
|
10638
10888
|
if (enabled.has(name)) continue;
|
|
10639
|
-
const manifestPath =
|
|
10889
|
+
const manifestPath = resolve18(pluginsDir, name, "PLUGIN.md");
|
|
10640
10890
|
if (!existsSync19(manifestPath)) continue;
|
|
10641
10891
|
let manifest;
|
|
10642
10892
|
try {
|
|
@@ -10655,7 +10905,7 @@ function computeDormantPlugins(accountId) {
|
|
|
10655
10905
|
|
|
10656
10906
|
// server/routes/admin/claude-sessions.ts
|
|
10657
10907
|
import { existsSync as existsSync20, readFileSync as readFileSync20 } from "fs";
|
|
10658
|
-
import { resolve as
|
|
10908
|
+
import { resolve as resolve19 } from "path";
|
|
10659
10909
|
function readTunnelState(brandConfigDir) {
|
|
10660
10910
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
10661
10911
|
if (!existsSync20(statePath)) return null;
|
|
@@ -10671,10 +10921,10 @@ function readTunnelState(brandConfigDir) {
|
|
|
10671
10921
|
}
|
|
10672
10922
|
}
|
|
10673
10923
|
function resolveTunnelUrl() {
|
|
10674
|
-
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ??
|
|
10924
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
|
|
10675
10925
|
let brand;
|
|
10676
10926
|
try {
|
|
10677
|
-
brand = JSON.parse(readFileSync20(
|
|
10927
|
+
brand = JSON.parse(readFileSync20(resolve19(platformRoot2, "config", "brand.json"), "utf-8"));
|
|
10678
10928
|
} catch {
|
|
10679
10929
|
return null;
|
|
10680
10930
|
}
|
|
@@ -10922,68 +11172,6 @@ import { realpathSync as realpathSync4 } from "fs";
|
|
|
10922
11172
|
import { basename as basename6, dirname as dirname5, join as join19, resolve as resolve21, sep as sep5 } from "path";
|
|
10923
11173
|
import { Readable as Readable2 } from "stream";
|
|
10924
11174
|
|
|
10925
|
-
// app/lib/data-path.ts
|
|
10926
|
-
import { realpathSync as realpathSync3 } from "fs";
|
|
10927
|
-
import { resolve as resolve19, normalize, sep as sep3, relative } from "path";
|
|
10928
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "../platform");
|
|
10929
|
-
var DATA_ROOT = resolve19(PLATFORM_ROOT6, "..", "data");
|
|
10930
|
-
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve19(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
10931
|
-
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["uploads", "accounts"]);
|
|
10932
|
-
var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10933
|
-
function crossesForeignAccountPartition(relPath, accountId) {
|
|
10934
|
-
const segs = relPath.split("/").filter(Boolean);
|
|
10935
|
-
if (segs.length < 2) return false;
|
|
10936
|
-
if (!ACCOUNT_PARTITION_DIRS.has(segs[0])) return false;
|
|
10937
|
-
const owner = segs[1];
|
|
10938
|
-
if (!ACCOUNT_UUID_RE.test(owner)) return false;
|
|
10939
|
-
return owner !== accountId;
|
|
10940
|
-
}
|
|
10941
|
-
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
10942
|
-
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
10943
|
-
const absolute = resolve19(rootAbs, cleaned);
|
|
10944
|
-
let rootReal;
|
|
10945
|
-
try {
|
|
10946
|
-
rootReal = realpathSync3(rootAbs);
|
|
10947
|
-
} catch (err) {
|
|
10948
|
-
return {
|
|
10949
|
-
ok: false,
|
|
10950
|
-
status: 500,
|
|
10951
|
-
error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
|
|
10952
|
-
};
|
|
10953
|
-
}
|
|
10954
|
-
let resolvedReal;
|
|
10955
|
-
try {
|
|
10956
|
-
resolvedReal = realpathSync3(absolute);
|
|
10957
|
-
} catch (err) {
|
|
10958
|
-
const code = err.code;
|
|
10959
|
-
if (code === "ENOENT") {
|
|
10960
|
-
if (absolute !== rootReal && !absolute.startsWith(rootReal + sep3)) {
|
|
10961
|
-
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
|
|
10962
|
-
}
|
|
10963
|
-
return { ok: false, status: 404, error: "Not found" };
|
|
10964
|
-
}
|
|
10965
|
-
return {
|
|
10966
|
-
ok: false,
|
|
10967
|
-
status: 500,
|
|
10968
|
-
error: err instanceof Error ? err.message : String(err)
|
|
10969
|
-
};
|
|
10970
|
-
}
|
|
10971
|
-
if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep3)) {
|
|
10972
|
-
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
|
|
10973
|
-
}
|
|
10974
|
-
const relPath = relative(rootReal, resolvedReal) || ".";
|
|
10975
|
-
return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath };
|
|
10976
|
-
}
|
|
10977
|
-
function resolveDataPath(raw) {
|
|
10978
|
-
return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
|
|
10979
|
-
}
|
|
10980
|
-
function resolveClaudeUploadsPath(raw) {
|
|
10981
|
-
if (!CLAUDE_UPLOADS_ROOT) {
|
|
10982
|
-
return { ok: false, status: 404, error: "Not found" };
|
|
10983
|
-
}
|
|
10984
|
-
return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
|
|
10985
|
-
}
|
|
10986
|
-
|
|
10987
11175
|
// ../lib/graph-trash/src/index.ts
|
|
10988
11176
|
var UNIQUE_KEYS_BY_LABEL = {
|
|
10989
11177
|
Person: ["email", "telephone"],
|
|
@@ -11221,11 +11409,11 @@ async function restoreNode(params) {
|
|
|
11221
11409
|
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
11222
11410
|
function parseAttachmentPath(relPath) {
|
|
11223
11411
|
const segments = relPath.split("/").filter(Boolean);
|
|
11224
|
-
if (segments.length !==
|
|
11225
|
-
if (segments[0] !== "uploads") return null;
|
|
11412
|
+
if (segments.length !== 5) return null;
|
|
11413
|
+
if (segments[0] !== "accounts" || segments[2] !== "uploads") return null;
|
|
11226
11414
|
const accountId = segments[1];
|
|
11227
|
-
const attachmentId = segments[
|
|
11228
|
-
const filename = segments[
|
|
11415
|
+
const attachmentId = segments[3];
|
|
11416
|
+
const filename = segments[4];
|
|
11229
11417
|
if (!UUID_RE2.test(accountId) || !UUID_RE2.test(attachmentId)) return null;
|
|
11230
11418
|
const dot = filename.lastIndexOf(".");
|
|
11231
11419
|
if (dot === -1) return null;
|
|
@@ -11303,6 +11491,17 @@ import { resolve as resolve20, relative as relative2, join as join18, basename a
|
|
|
11303
11491
|
import { tmpdir as tmpdir2 } from "os";
|
|
11304
11492
|
import { execFile as execFile2 } from "child_process";
|
|
11305
11493
|
import { promisify as promisify2 } from "util";
|
|
11494
|
+
|
|
11495
|
+
// app/lib/migration-state.ts
|
|
11496
|
+
var inProgress = false;
|
|
11497
|
+
function setUploadsMigrationInProgress(value) {
|
|
11498
|
+
inProgress = value;
|
|
11499
|
+
}
|
|
11500
|
+
function uploadsMigrationInProgress() {
|
|
11501
|
+
return inProgress;
|
|
11502
|
+
}
|
|
11503
|
+
|
|
11504
|
+
// app/lib/file-index.ts
|
|
11306
11505
|
var execFileAsync2 = promisify2(execFile2);
|
|
11307
11506
|
var CONTENT_CAP = 1e5;
|
|
11308
11507
|
var OCR_TIMEOUT_MS = 3e4;
|
|
@@ -11607,7 +11806,6 @@ async function reconcileFileIndex(accountId, opts) {
|
|
|
11607
11806
|
return withSession(opts, async (session) => {
|
|
11608
11807
|
const walked = [];
|
|
11609
11808
|
const subtreeRoots = [
|
|
11610
|
-
resolve20(dataRoot, "uploads", accountId),
|
|
11611
11809
|
resolve20(dataRoot, "accounts", accountId)
|
|
11612
11810
|
];
|
|
11613
11811
|
let clean = true;
|
|
@@ -11644,7 +11842,9 @@ async function reconcileFileIndex(accountId, opts) {
|
|
|
11644
11842
|
}
|
|
11645
11843
|
let pruned = 0;
|
|
11646
11844
|
let cascadeKds = 0, cascadeSections = 0, cascadeChunks = 0;
|
|
11647
|
-
if (clean) {
|
|
11845
|
+
if (clean && uploadsMigrationInProgress()) {
|
|
11846
|
+
console.error(`[file-index] reconcile account=${accountId} prune-skipped reason="uploads-migration-in-progress"`);
|
|
11847
|
+
} else if (clean) {
|
|
11648
11848
|
const foundPaths = walked.map((w) => w.relativePath);
|
|
11649
11849
|
const goneRes = await session.run(
|
|
11650
11850
|
`MATCH (f:FileArtifact {accountId: $accountId})
|
|
@@ -11804,6 +12004,32 @@ function sortDirEntries(entries) {
|
|
|
11804
12004
|
return aKey.localeCompare(bKey);
|
|
11805
12005
|
});
|
|
11806
12006
|
}
|
|
12007
|
+
var PROTECTED_DELETE_SEGMENTS = {
|
|
12008
|
+
".git": "git",
|
|
12009
|
+
".claude": "claude",
|
|
12010
|
+
secrets: "secrets",
|
|
12011
|
+
agents: "agents",
|
|
12012
|
+
specialists: "specialists",
|
|
12013
|
+
state: "state"
|
|
12014
|
+
};
|
|
12015
|
+
var PROTECTED_DELETE_BASENAMES = {
|
|
12016
|
+
"account.json": "account-json",
|
|
12017
|
+
"wa-channel-bindings.json": "channel-bindings",
|
|
12018
|
+
"webchat-channel-bindings.json": "channel-bindings"
|
|
12019
|
+
};
|
|
12020
|
+
function isProtectedFromDeletion(relPath) {
|
|
12021
|
+
const segments = relPath.split("/").filter(Boolean);
|
|
12022
|
+
for (const seg of segments) {
|
|
12023
|
+
if (Object.hasOwn(PROTECTED_DELETE_SEGMENTS, seg)) {
|
|
12024
|
+
return { protected: true, rule: PROTECTED_DELETE_SEGMENTS[seg] };
|
|
12025
|
+
}
|
|
12026
|
+
}
|
|
12027
|
+
const base = segments[segments.length - 1];
|
|
12028
|
+
if (base && Object.hasOwn(PROTECTED_DELETE_BASENAMES, base)) {
|
|
12029
|
+
return { protected: true, rule: PROTECTED_DELETE_BASENAMES[base] };
|
|
12030
|
+
}
|
|
12031
|
+
return { protected: false };
|
|
12032
|
+
}
|
|
11807
12033
|
async function knowledgeDocOwnedByAccount(accountId, attachmentId) {
|
|
11808
12034
|
if (!attachmentId) return false;
|
|
11809
12035
|
const session = getSession();
|
|
@@ -11849,7 +12075,7 @@ app19.get("/", requireAdminSession, async (c) => {
|
|
|
11849
12075
|
}
|
|
11850
12076
|
const names = await readdir3(absolute);
|
|
11851
12077
|
const segs = relPath.split("/").filter(Boolean);
|
|
11852
|
-
const isAccountParent = segs.length === 1 &&
|
|
12078
|
+
const isAccountParent = segs.length === 1 && segs[0] === "accounts";
|
|
11853
12079
|
const entries = [];
|
|
11854
12080
|
for (const name of names) {
|
|
11855
12081
|
if (UUID_RE3.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
|
|
@@ -11858,6 +12084,8 @@ app19.get("/", requireAdminSession, async (c) => {
|
|
|
11858
12084
|
if (isAccountParent && UUID_RE3.test(name) && name !== accountId) {
|
|
11859
12085
|
continue;
|
|
11860
12086
|
}
|
|
12087
|
+
const childRel = relPath === "" || relPath === "." ? name : `${relPath}/${name}`;
|
|
12088
|
+
const protectedEntry = isProtectedFromDeletion(childRel).protected;
|
|
11861
12089
|
try {
|
|
11862
12090
|
const entryPath = join19(absolute, name);
|
|
11863
12091
|
const s = await stat4(entryPath);
|
|
@@ -11865,10 +12093,11 @@ app19.get("/", requireAdminSession, async (c) => {
|
|
|
11865
12093
|
name,
|
|
11866
12094
|
kind: s.isDirectory() ? "directory" : s.isFile() ? "file" : "other",
|
|
11867
12095
|
sizeBytes: s.isDirectory() ? null : s.size,
|
|
11868
|
-
modifiedAt: s.mtime.toISOString()
|
|
12096
|
+
modifiedAt: s.mtime.toISOString(),
|
|
12097
|
+
protected: protectedEntry
|
|
11869
12098
|
});
|
|
11870
12099
|
} catch {
|
|
11871
|
-
entries.push({ name, kind: "other", sizeBytes: null, modifiedAt: (/* @__PURE__ */ new Date(0)).toISOString() });
|
|
12100
|
+
entries.push({ name, kind: "other", sizeBytes: null, modifiedAt: (/* @__PURE__ */ new Date(0)).toISOString(), protected: protectedEntry });
|
|
11872
12101
|
}
|
|
11873
12102
|
}
|
|
11874
12103
|
const accountNames = await readAccountNames();
|
|
@@ -11984,14 +12213,14 @@ app19.post("/upload", requireAdminSession, async (c) => {
|
|
|
11984
12213
|
}
|
|
11985
12214
|
const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
|
|
11986
12215
|
const finalName = `${Date.now()}-${safeName}`;
|
|
11987
|
-
const destDir = resolve21(DATA_ROOT, "
|
|
12216
|
+
const destDir = resolve21(DATA_ROOT, "accounts", accountId, "uploads");
|
|
11988
12217
|
const destPath = resolve21(destDir, finalName);
|
|
11989
12218
|
try {
|
|
11990
12219
|
await mkdir3(destDir, { recursive: true });
|
|
11991
12220
|
const dataRootReal = realpathSync4(DATA_ROOT);
|
|
11992
12221
|
const destDirReal = realpathSync4(destDir);
|
|
11993
12222
|
if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal + sep5)) {
|
|
11994
|
-
console.error(`[data] path-traversal-blocked requested="
|
|
12223
|
+
console.error(`[data] path-traversal-blocked requested="accounts/${accountId}/uploads" resolved="${destDirReal}"`);
|
|
11995
12224
|
return c.json({ error: "Upload destination escapes DATA_ROOT" }, 403);
|
|
11996
12225
|
}
|
|
11997
12226
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
@@ -12001,7 +12230,7 @@ app19.post("/upload", requireAdminSession, async (c) => {
|
|
|
12001
12230
|
console.error(`[data] file-upload error filename="${safeName}" err="${message}"`);
|
|
12002
12231
|
return c.json({ error: message }, 500);
|
|
12003
12232
|
}
|
|
12004
|
-
const relativeDest = `
|
|
12233
|
+
const relativeDest = `accounts/${accountId}/uploads/${finalName}`;
|
|
12005
12234
|
console.error(`[data] file-upload filename="${safeName}" size=${file.size} dest="${relativeDest}"`);
|
|
12006
12235
|
void indexFile(accountId, relativeDest).catch((err) => {
|
|
12007
12236
|
console.error(`[data] file-upload index-failed dest="${relativeDest}" err="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -12035,9 +12264,9 @@ app19.delete("/", requireAdminSession, async (c) => {
|
|
|
12035
12264
|
return c.json({ error: "Not found" }, 404);
|
|
12036
12265
|
}
|
|
12037
12266
|
const base = basename6(absolute);
|
|
12038
|
-
const
|
|
12039
|
-
if (
|
|
12040
|
-
console.error(`[data] file-delete blocked path="${relPath}" reason="protected"`);
|
|
12267
|
+
const protection = isProtectedFromDeletion(relPath);
|
|
12268
|
+
if (protection.protected) {
|
|
12269
|
+
console.error(`[data] file-delete blocked path="${relPath}" reason="protected" rule="${protection.rule}"`);
|
|
12041
12270
|
return c.json({ error: "Protected file \u2014 refusing to delete" }, 403);
|
|
12042
12271
|
}
|
|
12043
12272
|
try {
|
|
@@ -14136,13 +14365,14 @@ async function fetchAccountFileArtefacts(accountId) {
|
|
|
14136
14365
|
const result = await session.run(
|
|
14137
14366
|
`MATCH (f:FileArtifact { accountId: $accountId })
|
|
14138
14367
|
WHERE f.relativePath STARTS WITH 'accounts/'
|
|
14368
|
+
AND NOT f.relativePath STARTS WITH $uploadsPrefix
|
|
14139
14369
|
RETURN f.relativePath AS relativePath,
|
|
14140
14370
|
f.displayName AS displayName,
|
|
14141
14371
|
f.mimeType AS mimeType,
|
|
14142
14372
|
f.modifiedAt AS modifiedAt
|
|
14143
14373
|
ORDER BY f.modifiedAt DESC
|
|
14144
14374
|
LIMIT 50`,
|
|
14145
|
-
{ accountId }
|
|
14375
|
+
{ accountId, uploadsPrefix: `accounts/${accountId}/uploads/` }
|
|
14146
14376
|
);
|
|
14147
14377
|
return result.records.map((r) => {
|
|
14148
14378
|
const relativePath = r.get("relativePath");
|
|
@@ -14398,7 +14628,7 @@ var session_archive_default = app29;
|
|
|
14398
14628
|
|
|
14399
14629
|
// server/routes/admin/session-rc-spawn.ts
|
|
14400
14630
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
14401
|
-
function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID10) {
|
|
14631
|
+
function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID10, adminUserId) {
|
|
14402
14632
|
const operator = isOperatorHost(host, operatorDomains);
|
|
14403
14633
|
const resumeId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : void 0;
|
|
14404
14634
|
const name = typeof body.name === "string" && body.name.length > 0 ? body.name : void 0;
|
|
@@ -14416,6 +14646,7 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID10) {
|
|
|
14416
14646
|
payload.senderId = key;
|
|
14417
14647
|
payload.webchatChannel = webchatChannelDescriptor(key);
|
|
14418
14648
|
if (name) payload.name = name;
|
|
14649
|
+
if (adminUserId) payload.adminUserId = adminUserId;
|
|
14419
14650
|
} else {
|
|
14420
14651
|
sessionId = resumeId;
|
|
14421
14652
|
payload.sessionId = resumeId;
|
|
@@ -14436,7 +14667,9 @@ app30.post("/", requireAdminSession, async (c) => {
|
|
|
14436
14667
|
body = {};
|
|
14437
14668
|
}
|
|
14438
14669
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
14439
|
-
const
|
|
14670
|
+
const cacheKey = c.get("cacheKey");
|
|
14671
|
+
const adminUserId = cacheKey ? getUserIdForSession(cacheKey) : void 0;
|
|
14672
|
+
const plan = resolveSpawnPlan(host, getOperatorDomains(), body, randomUUID10, adminUserId);
|
|
14440
14673
|
const spawnReqId = randomUUID10();
|
|
14441
14674
|
console.log(
|
|
14442
14675
|
`[chat-spawn] op=request spawnReqId=${spawnReqId} origin=${plan.origin} kind=${plan.kind} sessionId=${plan.kind === "new" ? "new" : plan.sessionId}`
|
|
@@ -14605,9 +14838,9 @@ var system_stats_default = app31;
|
|
|
14605
14838
|
// server/routes/admin/health.ts
|
|
14606
14839
|
import { existsSync as existsSync22, readFileSync as readFileSync21 } from "fs";
|
|
14607
14840
|
import { resolve as resolve23, join as join20 } from "path";
|
|
14608
|
-
var
|
|
14841
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve23(process.cwd(), "..");
|
|
14609
14842
|
var brandHostname = "maxy";
|
|
14610
|
-
var brandJsonPath = join20(
|
|
14843
|
+
var brandJsonPath = join20(PLATFORM_ROOT6, "config", "brand.json");
|
|
14611
14844
|
if (existsSync22(brandJsonPath)) {
|
|
14612
14845
|
try {
|
|
14613
14846
|
const brand = JSON.parse(readFileSync21(brandJsonPath, "utf-8"));
|
|
@@ -14615,7 +14848,7 @@ if (existsSync22(brandJsonPath)) {
|
|
|
14615
14848
|
} catch {
|
|
14616
14849
|
}
|
|
14617
14850
|
}
|
|
14618
|
-
var VERSION_FILE = resolve23(
|
|
14851
|
+
var VERSION_FILE = resolve23(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
14619
14852
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
14620
14853
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
14621
14854
|
function readVersion() {
|
|
@@ -15325,9 +15558,9 @@ var verify_token_default = app41;
|
|
|
15325
15558
|
// app/lib/access-email.ts
|
|
15326
15559
|
import { spawn as spawn2 } from "child_process";
|
|
15327
15560
|
import { resolve as resolve24 } from "path";
|
|
15328
|
-
var
|
|
15561
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
|
|
15329
15562
|
var SEND_SCRIPT = resolve24(
|
|
15330
|
-
|
|
15563
|
+
PLATFORM_ROOT7,
|
|
15331
15564
|
"plugins",
|
|
15332
15565
|
"email",
|
|
15333
15566
|
"mcp",
|
|
@@ -16563,7 +16796,7 @@ import * as fsp2 from "fs/promises";
|
|
|
16563
16796
|
import { resolve as resolve27, sep as sep7 } from "path";
|
|
16564
16797
|
var ACCOUNT_UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
16565
16798
|
var DEFAULT_COALESCE_MS = 500;
|
|
16566
|
-
var ROOTS = ["
|
|
16799
|
+
var ROOTS = ["accounts"];
|
|
16567
16800
|
function _routeEvent(rootName, filename) {
|
|
16568
16801
|
if (!filename) return null;
|
|
16569
16802
|
const segs = filename.split(sep7).filter(Boolean);
|
|
@@ -16661,6 +16894,129 @@ async function startFileWatcher(opts = {}) {
|
|
|
16661
16894
|
return { stop };
|
|
16662
16895
|
}
|
|
16663
16896
|
|
|
16897
|
+
// app/lib/migrate-uploads.ts
|
|
16898
|
+
import { mkdir as mkdir5, readdir as readdir5, rename, rm as rm3 } from "fs/promises";
|
|
16899
|
+
import { dirname as dirname7, relative as relative4, resolve as resolve28 } from "path";
|
|
16900
|
+
var ACCOUNT_UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
16901
|
+
async function walkFiles(dir) {
|
|
16902
|
+
const out = [];
|
|
16903
|
+
let entries;
|
|
16904
|
+
try {
|
|
16905
|
+
entries = await readdir5(dir, { withFileTypes: true });
|
|
16906
|
+
} catch {
|
|
16907
|
+
return out;
|
|
16908
|
+
}
|
|
16909
|
+
for (const e of entries) {
|
|
16910
|
+
const abs = resolve28(dir, e.name);
|
|
16911
|
+
if (e.isDirectory()) {
|
|
16912
|
+
out.push(...await walkFiles(abs));
|
|
16913
|
+
} else if (e.isFile()) {
|
|
16914
|
+
out.push(abs);
|
|
16915
|
+
}
|
|
16916
|
+
}
|
|
16917
|
+
return out;
|
|
16918
|
+
}
|
|
16919
|
+
function toNumber2(v) {
|
|
16920
|
+
if (v && typeof v.toNumber === "function") {
|
|
16921
|
+
return v.toNumber();
|
|
16922
|
+
}
|
|
16923
|
+
return Number(v ?? 0);
|
|
16924
|
+
}
|
|
16925
|
+
async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
16926
|
+
const files = await walkFiles(srcDir);
|
|
16927
|
+
let moved = 0;
|
|
16928
|
+
for (const oldAbs of files) {
|
|
16929
|
+
const suffix = relative4(srcDir, oldAbs);
|
|
16930
|
+
const newAbs = resolve28(destDir, suffix);
|
|
16931
|
+
await mkdir5(dirname7(newAbs), { recursive: true });
|
|
16932
|
+
await rename(oldAbs, newAbs);
|
|
16933
|
+
moved++;
|
|
16934
|
+
const oldRel = relative4(dataRoot, oldAbs);
|
|
16935
|
+
const newRel = relative4(dataRoot, newAbs);
|
|
16936
|
+
let linked = false;
|
|
16937
|
+
if (accountId && session) {
|
|
16938
|
+
const fa = await session.run(
|
|
16939
|
+
`MATCH (f:FileArtifact {accountId: $accountId, relativePath: $oldRel})
|
|
16940
|
+
SET f.relativePath = $newRel RETURN count(f) AS n`,
|
|
16941
|
+
{ accountId, oldRel, newRel }
|
|
16942
|
+
);
|
|
16943
|
+
const kd = await session.run(
|
|
16944
|
+
`MATCH (d:KnowledgeDocument {accountId: $accountId, sourcePath: $oldAbs})
|
|
16945
|
+
SET d.sourcePath = $newAbs RETURN count(d) AS n`,
|
|
16946
|
+
{ accountId, oldAbs, newAbs }
|
|
16947
|
+
);
|
|
16948
|
+
linked = toNumber2(fa.records[0]?.get("n")) + toNumber2(kd.records[0]?.get("n")) > 0;
|
|
16949
|
+
}
|
|
16950
|
+
console.error(`[uploads-migrate] from=${oldRel} to=${newRel} linked=${linked}`);
|
|
16951
|
+
}
|
|
16952
|
+
await rm3(srcDir, { recursive: true, force: true });
|
|
16953
|
+
return moved;
|
|
16954
|
+
}
|
|
16955
|
+
async function migrateUploads(opts = {}) {
|
|
16956
|
+
const dataRoot = opts.dataRoot ?? DATA_ROOT;
|
|
16957
|
+
const oldRoot = resolve28(dataRoot, "uploads");
|
|
16958
|
+
let topEntries;
|
|
16959
|
+
try {
|
|
16960
|
+
topEntries = await readdir5(oldRoot, { withFileTypes: true });
|
|
16961
|
+
} catch (err) {
|
|
16962
|
+
if (err.code === "ENOENT") {
|
|
16963
|
+
return { moved: 0, skipped: 0, removedRoot: false };
|
|
16964
|
+
}
|
|
16965
|
+
throw err;
|
|
16966
|
+
}
|
|
16967
|
+
const installAccountId = opts.installAccountId ?? resolveAccount()?.accountId;
|
|
16968
|
+
const session = opts.session ?? getSession();
|
|
16969
|
+
const ownSession = !opts.session;
|
|
16970
|
+
setUploadsMigrationInProgress(true);
|
|
16971
|
+
let moved = 0;
|
|
16972
|
+
let skipped = 0;
|
|
16973
|
+
try {
|
|
16974
|
+
for (const entry of topEntries) {
|
|
16975
|
+
const name = entry.name;
|
|
16976
|
+
if (ACCOUNT_UUID_RE3.test(name)) {
|
|
16977
|
+
moved += await relocateTree(
|
|
16978
|
+
resolve28(oldRoot, name),
|
|
16979
|
+
resolve28(dataRoot, "accounts", name, "uploads"),
|
|
16980
|
+
dataRoot,
|
|
16981
|
+
name,
|
|
16982
|
+
session
|
|
16983
|
+
);
|
|
16984
|
+
} else if (name === "public") {
|
|
16985
|
+
if (!installAccountId) {
|
|
16986
|
+
console.error(`[uploads-migrate] skip entry="public" reason="no-install-account"`);
|
|
16987
|
+
skipped++;
|
|
16988
|
+
continue;
|
|
16989
|
+
}
|
|
16990
|
+
moved += await relocateTree(
|
|
16991
|
+
resolve28(oldRoot, "public"),
|
|
16992
|
+
resolve28(dataRoot, "accounts", installAccountId, "uploads", "public"),
|
|
16993
|
+
dataRoot,
|
|
16994
|
+
null,
|
|
16995
|
+
// public uploads carry no graph nodes
|
|
16996
|
+
session
|
|
16997
|
+
);
|
|
16998
|
+
} else {
|
|
16999
|
+
console.error(`[uploads-migrate] skip entry="${name}" reason="not-account-or-public"`);
|
|
17000
|
+
skipped++;
|
|
17001
|
+
}
|
|
17002
|
+
}
|
|
17003
|
+
let removedRoot = false;
|
|
17004
|
+
try {
|
|
17005
|
+
const remaining = await readdir5(oldRoot);
|
|
17006
|
+
if (remaining.length === 0) {
|
|
17007
|
+
await rm3(oldRoot, { recursive: true, force: true });
|
|
17008
|
+
removedRoot = true;
|
|
17009
|
+
}
|
|
17010
|
+
} catch {
|
|
17011
|
+
}
|
|
17012
|
+
console.error(`[uploads-migrate] done moved=${moved} skipped=${skipped} removedRoot=${removedRoot}`);
|
|
17013
|
+
return { moved, skipped, removedRoot };
|
|
17014
|
+
} finally {
|
|
17015
|
+
setUploadsMigrationInProgress(false);
|
|
17016
|
+
if (ownSession) await session.close();
|
|
17017
|
+
}
|
|
17018
|
+
}
|
|
17019
|
+
|
|
16664
17020
|
// app/lib/whatsapp/gateway/inbound-hub.ts
|
|
16665
17021
|
var InboundHub = class {
|
|
16666
17022
|
senders = /* @__PURE__ */ new Map();
|
|
@@ -17572,7 +17928,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
17572
17928
|
// ../lib/entitlement/src/index.ts
|
|
17573
17929
|
import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
|
|
17574
17930
|
import { existsSync as existsSync26, readFileSync as readFileSync25, statSync as statSync10 } from "fs";
|
|
17575
|
-
import { resolve as
|
|
17931
|
+
import { resolve as resolve29 } from "path";
|
|
17576
17932
|
|
|
17577
17933
|
// ../lib/entitlement/src/canonicalize.ts
|
|
17578
17934
|
function canonicalize(value) {
|
|
@@ -17607,7 +17963,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
17607
17963
|
var GRACE_DAYS = 7;
|
|
17608
17964
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
17609
17965
|
function pubkeyPath(brand) {
|
|
17610
|
-
return
|
|
17966
|
+
return resolve29(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
17611
17967
|
}
|
|
17612
17968
|
var memo = null;
|
|
17613
17969
|
function memoKey(mtimeMs, account) {
|
|
@@ -17619,7 +17975,7 @@ function resolveEntitlement(brand, account) {
|
|
|
17619
17975
|
if (brand.commercialMode !== true) {
|
|
17620
17976
|
return logResolved(implicitTrust(account), null);
|
|
17621
17977
|
}
|
|
17622
|
-
const entitlementPath =
|
|
17978
|
+
const entitlementPath = resolve29(brand.configDir, "entitlement.json");
|
|
17623
17979
|
if (!existsSync26(entitlementPath)) {
|
|
17624
17980
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
17625
17981
|
}
|
|
@@ -17809,8 +18165,8 @@ function clientFrom(c) {
|
|
|
17809
18165
|
c.req.header("x-forwarded-for")
|
|
17810
18166
|
);
|
|
17811
18167
|
}
|
|
17812
|
-
var
|
|
17813
|
-
var BRAND_JSON_PATH =
|
|
18168
|
+
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
18169
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join22(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
17814
18170
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
17815
18171
|
if (BRAND_JSON_PATH && !existsSync27(BRAND_JSON_PATH)) {
|
|
17816
18172
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
@@ -17868,7 +18224,7 @@ var app49 = new Hono();
|
|
|
17868
18224
|
var nativeFileFollowers = /* @__PURE__ */ new Map();
|
|
17869
18225
|
var waGateway = new WaGateway({
|
|
17870
18226
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
17871
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ??
|
|
18227
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve30(process.env.MAXY_PLATFORM_ROOT ?? join22(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
17872
18228
|
// Task 751 — file delivery on the native channel: resolve the platform
|
|
17873
18229
|
// account + path validation here and funnel through the shared send core.
|
|
17874
18230
|
sendDocument: async ({ senderId, accountId, filePath, caption }) => {
|
|
@@ -17884,7 +18240,7 @@ var waGateway = new WaGateway({
|
|
|
17884
18240
|
caption,
|
|
17885
18241
|
accountId,
|
|
17886
18242
|
maxyAccountId,
|
|
17887
|
-
platformRoot:
|
|
18243
|
+
platformRoot: resolve30(process.env.MAXY_PLATFORM_ROOT ?? join22(__dirname, ".."))
|
|
17888
18244
|
});
|
|
17889
18245
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
17890
18246
|
},
|
|
@@ -18188,10 +18544,11 @@ app49.post("/api/remote-auth/set-password", async (c) => {
|
|
|
18188
18544
|
});
|
|
18189
18545
|
app49.route("/api/_client-error", client_error_default);
|
|
18190
18546
|
console.log("[client-error-route] mounted");
|
|
18547
|
+
var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
|
|
18191
18548
|
app49.use("*", async (c, next) => {
|
|
18192
18549
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
18193
18550
|
const path2 = c.req.path;
|
|
18194
|
-
if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
|
|
18551
|
+
if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/") || PWA_PUBLIC_PATHS.has(path2)) {
|
|
18195
18552
|
await next();
|
|
18196
18553
|
return;
|
|
18197
18554
|
}
|
|
@@ -18266,8 +18623,8 @@ app49.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
18266
18623
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
18267
18624
|
return c.text("Not found", 404);
|
|
18268
18625
|
}
|
|
18269
|
-
const filePath =
|
|
18270
|
-
const expectedDir =
|
|
18626
|
+
const filePath = resolve30(account.accountDir, "agents", slug, "assets", filename);
|
|
18627
|
+
const expectedDir = resolve30(account.accountDir, "agents", slug, "assets");
|
|
18271
18628
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
18272
18629
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
18273
18630
|
return c.text("Forbidden", 403);
|
|
@@ -18296,8 +18653,8 @@ app49.get("/generated/:filename", (c) => {
|
|
|
18296
18653
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
18297
18654
|
return c.text("Not found", 404);
|
|
18298
18655
|
}
|
|
18299
|
-
const filePath =
|
|
18300
|
-
const expectedDir =
|
|
18656
|
+
const filePath = resolve30(account.accountDir, "generated", filename);
|
|
18657
|
+
const expectedDir = resolve30(account.accountDir, "generated");
|
|
18301
18658
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
18302
18659
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
18303
18660
|
return c.text("Forbidden", 403);
|
|
@@ -18338,10 +18695,21 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
18338
18695
|
icon: brandIconPath,
|
|
18339
18696
|
logoContainsName: !!BRAND.logoContainsName
|
|
18340
18697
|
})}</script>`;
|
|
18698
|
+
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
18699
|
+
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
18700
|
+
var brandIconFsPath = resolve30(process.cwd(), "public", brandIconPath.replace(/^\//, ""));
|
|
18701
|
+
var brandIconExists = existsSync27(brandIconFsPath);
|
|
18702
|
+
var SW_SOURCE = (() => {
|
|
18703
|
+
try {
|
|
18704
|
+
return readFileSync26(resolve30(process.cwd(), "public", "sw.js"), "utf-8");
|
|
18705
|
+
} catch {
|
|
18706
|
+
return null;
|
|
18707
|
+
}
|
|
18708
|
+
})();
|
|
18341
18709
|
function readInstalledVersion() {
|
|
18342
18710
|
try {
|
|
18343
|
-
if (!
|
|
18344
|
-
const versionFile = join22(
|
|
18711
|
+
if (!PLATFORM_ROOT8) return "unknown";
|
|
18712
|
+
const versionFile = join22(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
18345
18713
|
if (!existsSync27(versionFile)) return "unknown";
|
|
18346
18714
|
const content = readFileSync26(versionFile, "utf-8").trim();
|
|
18347
18715
|
return content || "unknown";
|
|
@@ -18384,15 +18752,17 @@ var clientErrorReporterScript = `<script>
|
|
|
18384
18752
|
function cachedHtml(file) {
|
|
18385
18753
|
let html = htmlCache.get(file);
|
|
18386
18754
|
if (!html) {
|
|
18387
|
-
html = readFileSync26(
|
|
18755
|
+
html = readFileSync26(resolve30(process.cwd(), "public", file), "utf-8");
|
|
18388
18756
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
18389
18757
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
18390
18758
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
18391
|
-
const
|
|
18759
|
+
const pwaSurface = pwaSurfaceForShell(file);
|
|
18760
|
+
const pwaHead = pwaSurface ? "\n" + pwaHeadTags(pwaSurface, { iconPath: brandIconPath, themeColor: brandThemeColor }) + "\n" : "";
|
|
18761
|
+
const headInjection = (file === "index.html" ? `${brandScript}
|
|
18392
18762
|
${versionScript}
|
|
18393
18763
|
${clientErrorReporterScript}
|
|
18394
18764
|
` : `${brandScript}
|
|
18395
|
-
|
|
18765
|
+
`) + pwaHead;
|
|
18396
18766
|
html = html.replace("</head>", `${headInjection}</head>`);
|
|
18397
18767
|
htmlCache.set(file, html);
|
|
18398
18768
|
}
|
|
@@ -18500,7 +18870,7 @@ app49.use("/vnc-popout.html", logViewerFetch);
|
|
|
18500
18870
|
app49.get("/vnc-popout.html", (c) => {
|
|
18501
18871
|
let html = htmlCache.get("vnc-popout.html");
|
|
18502
18872
|
if (!html) {
|
|
18503
|
-
html = readFileSync26(
|
|
18873
|
+
html = readFileSync26(resolve30(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
18504
18874
|
const name = escapeHtml(BRAND.productName);
|
|
18505
18875
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
18506
18876
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -18534,6 +18904,30 @@ app49.post("/api/vnc/client-event", async (c) => {
|
|
|
18534
18904
|
app49.get("/g/:slug", (c) => {
|
|
18535
18905
|
return c.html(brandedPublicHtml());
|
|
18536
18906
|
});
|
|
18907
|
+
for (const pwa of PWA_SURFACES) {
|
|
18908
|
+
app49.get(pwa.manifestPath, (c) => {
|
|
18909
|
+
const manifest = buildManifest(pwa, {
|
|
18910
|
+
productName: BRAND.productName,
|
|
18911
|
+
iconPath: brandIconPath,
|
|
18912
|
+
themeColor: brandThemeColor,
|
|
18913
|
+
backgroundColor: brandBackgroundColor
|
|
18914
|
+
});
|
|
18915
|
+
c.header("Content-Type", MANIFEST_CONTENT_TYPE);
|
|
18916
|
+
console.log(
|
|
18917
|
+
`[pwa] op=manifest surface=${pwa.surface} brand=${BRAND.productName} status=200 ct=${MANIFEST_CONTENT_TYPE} icon=${brandIconPath} iconExists=${brandIconExists}`
|
|
18918
|
+
);
|
|
18919
|
+
return c.body(JSON.stringify(manifest));
|
|
18920
|
+
});
|
|
18921
|
+
}
|
|
18922
|
+
app49.get("/sw.js", (c) => {
|
|
18923
|
+
if (SW_SOURCE == null) {
|
|
18924
|
+
console.error("[pwa] op=sw status=500 reason=sw-source-missing");
|
|
18925
|
+
return c.text("Service worker unavailable", 500);
|
|
18926
|
+
}
|
|
18927
|
+
c.header("Content-Type", SW_CONTENT_TYPE);
|
|
18928
|
+
console.log(`[pwa] op=sw status=200 ct=${SW_CONTENT_TYPE}`);
|
|
18929
|
+
return c.body(SW_SOURCE);
|
|
18930
|
+
});
|
|
18537
18931
|
app49.get("/graph", (c) => {
|
|
18538
18932
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
18539
18933
|
if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
|
|
@@ -18591,6 +18985,21 @@ var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
|
18591
18985
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
18592
18986
|
var httpServer = serve({ fetch: app49.fetch, port, hostname });
|
|
18593
18987
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
18988
|
+
{
|
|
18989
|
+
const census = pwaCensus({
|
|
18990
|
+
shellHtml: (file) => {
|
|
18991
|
+
try {
|
|
18992
|
+
return cachedHtml(file);
|
|
18993
|
+
} catch {
|
|
18994
|
+
return null;
|
|
18995
|
+
}
|
|
18996
|
+
},
|
|
18997
|
+
iconExists: () => brandIconExists
|
|
18998
|
+
});
|
|
18999
|
+
console.log(
|
|
19000
|
+
`[pwa-census] surfaces=${census.surfaces} manifest-linked=${census.manifestLinked} icons-present=${census.iconsPresent}`
|
|
19001
|
+
);
|
|
19002
|
+
}
|
|
18594
19003
|
startAuthHealthHeartbeat();
|
|
18595
19004
|
{
|
|
18596
19005
|
const loopHist = monitorEventLoopDelay({ resolution: 50 });
|
|
@@ -18687,8 +19096,10 @@ var adminUserReconcileTimer = setInterval(() => {
|
|
|
18687
19096
|
}, ADMINUSER_RECONCILE_INTERVAL_MS);
|
|
18688
19097
|
if (typeof adminUserReconcileTimer.unref === "function") adminUserReconcileTimer.unref();
|
|
18689
19098
|
startGraphHealthTimer();
|
|
18690
|
-
void
|
|
18691
|
-
|
|
19099
|
+
void migrateUploads().catch((err) => console.error(`[uploads-migrate] failed err="${err instanceof Error ? err.message : String(err)}"`)).finally(() => {
|
|
19100
|
+
void startFileWatcher().catch((err) => {
|
|
19101
|
+
console.error(`[file-watcher] start-failed err="${err instanceof Error ? err.message : String(err)}"`);
|
|
19102
|
+
});
|
|
18692
19103
|
});
|
|
18693
19104
|
try {
|
|
18694
19105
|
const accounts = enumerateValidAccountIds(getAccountsDirFromEnv());
|
|
@@ -18770,7 +19181,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
18770
19181
|
}
|
|
18771
19182
|
init({
|
|
18772
19183
|
configDir: configDirForWhatsApp,
|
|
18773
|
-
platformRoot:
|
|
19184
|
+
platformRoot: resolve30(process.env.MAXY_PLATFORM_ROOT ?? join22(__dirname, "..")),
|
|
18774
19185
|
accountConfig: bootAccountConfig,
|
|
18775
19186
|
onMessage: async (msg) => {
|
|
18776
19187
|
if (msg.isOwnerMirror) {
|