@rubytech/create-maxy-code 0.1.357 → 0.1.359
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/claude-ptys-slice.test.js +36 -1
- package/dist/__tests__/neo4j-pidfile-scrub.test.js +103 -0
- package/dist/index.js +42 -7
- package/dist/neo4j-pidfile-scrub.js +90 -0
- package/dist/port-resolution.js +18 -1
- package/package.json +1 -1
- package/payload/platform/package-lock.json +237 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.js +79 -1
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +17 -3
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts +16 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js +52 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js.map +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +61 -1
- package/payload/platform/plugins/cloudflare/PLUGIN.md +7 -1
- package/payload/platform/plugins/cloudflare/bin/__tests__/cf-token.test.sh +195 -0
- package/payload/platform/plugins/cloudflare/bin/cf-token.sh +157 -0
- package/payload/platform/plugins/cloudflare/references/api.md +2 -0
- package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +14 -0
- package/payload/platform/plugins/cloudflare/skills/site-deploy/SKILL.md +10 -1
- package/payload/platform/plugins/docs/PLUGIN.md +2 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +2 -0
- package/payload/platform/plugins/docs/references/claudeai-connectors.md +45 -0
- package/payload/platform/plugins/docs/references/cloudflare.md +6 -0
- package/payload/platform/plugins/docs/references/troubleshooting.md +2 -0
- package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/email/PLUGIN.md +7 -3
- package/payload/platform/plugins/email/mcp/dist/__tests__/body-extract.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/body-extract.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/body-extract.test.js +99 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/body-extract.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-draft-id.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-draft-id.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-draft-id.test.js +32 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-draft-id.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-edit.test.js +64 -6
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-edit.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft.test.js +12 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-status.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-status.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-status.test.js +58 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-status.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js +94 -9
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-probe.test.js +109 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-probe.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/mail-options.test.js +4 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/mail-options.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/index.js +39 -2
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.d.ts +5 -0
- package/payload/platform/plugins/email/mcp/dist/lib/compose.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.js +8 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts +13 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.js +35 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +83 -30
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js +327 -112
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/setup-probe.d.ts +19 -0
- package/payload/platform/plugins/email/mcp/dist/lib/setup-probe.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/setup-probe.js +33 -0
- package/payload/platform/plugins/email/mcp/dist/lib/setup-probe.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts +6 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +4 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-edit.d.ts +8 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-edit.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-edit.js +10 -6
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-edit.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft.js +4 -2
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-body.d.ts +15 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-body.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-body.js +29 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-body.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js +11 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-status.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-status.js +24 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-status.js.map +1 -1
- package/payload/platform/plugins/email/mcp/package.json +2 -0
- package/payload/platform/plugins/email/references/email-reference.md +30 -3
- package/payload/platform/scripts/rss-sampler.sh +21 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/connector-auth.d.ts +31 -0
- package/payload/platform/services/claude-session-manager/dist/connector-auth.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/connector-auth.js +49 -0
- package/payload/platform/services/claude-session-manager/dist/connector-auth.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +24 -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 +214 -17
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +13 -5
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +46 -11
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts +1 -0
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js +28 -0
- package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.js +75 -5
- package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-BW1UgfoI.js → AdminLoginScreens-tHyAPNnQ.js} +1 -1
- package/payload/server/public/assets/AdminShell-Ce9izNVZ.js +1 -0
- package/payload/server/public/assets/{Checkbox-O1qvMk_u.js → Checkbox-Cq6SJJlX.js} +1 -1
- package/payload/server/public/assets/OperatorConversations-BERW_hUa.css +1 -0
- package/payload/server/public/assets/OperatorConversations-m2lz6Ss7.js +9 -0
- package/payload/server/public/assets/admin-D9iMgdbN.js +1 -0
- package/payload/server/public/assets/{arc-DpkSQNto.js → arc-Crkz45LN.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Dma4Bgha.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Bw7Yq8O9.js → architectureDiagram-Q4EWVU46-C9JThHgP.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-BBtJQFSz.js → blockDiagram-DXYQGD6D-DpsV_HXT.js} +1 -1
- package/payload/server/public/assets/{browser-CySvj140.js → browser-CZWi0EAP.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-JAMgxzOr.js → c4Diagram-AHTNJAMY-CTm_6aKi.js} +1 -1
- package/payload/server/public/assets/calendar-DUAh-_kx.js +1 -0
- package/payload/server/public/assets/channel-B_-5bau1.js +1 -0
- package/payload/server/public/assets/chat-DHuibPfc.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-eDugsc2L.js → chunk-2KRD3SAO-1KSJW1q6.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-BXfECwdC.js → chunk-336JU56O-C3t08Ip-.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-Dirxdwjh.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-CByVeKCY.js → chunk-4BX2VUAB-kl1ze33n.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-CpY0DcUB.js → chunk-4TB4RGXK-DxpoDoHg.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-C3YxgyIb.js → chunk-55IACEB6-WM9VJzha.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-Bc0d7j8A.js → chunk-5FUZZQ4R-CLH63ZH3.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-CTUGMRIa.js → chunk-5PVQY5BW-DCTb8IAT.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-CbdPnpLd.js → chunk-67CJDMHE-BSYzyaoH.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-Cjl2kevz.js → chunk-7N4EOEYR-CarJ7O2P.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-BgozIh5p.js → chunk-AA7GKIK3-DTNLyBpH.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-6Ufq45NW.js → chunk-BSJP7CBP-BFmHflHY.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-BhhYuMnA.js → chunk-CIAEETIT-BpjG0IzU.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-BfaJ3yDD.js → chunk-EDXVE4YY-2fSowhlW.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-C7-hIGVC.js → chunk-ENJZ2VHE-ClcranGV.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-BQvvyocF.js → chunk-FMBD7UC4-DLq4BItQ.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-C6rBDOxH.js → chunk-FOC6F5B3-B9QI3Guz.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-BjOJvkVa.js → chunk-ICPOFSXX-BAr-Mv1Z.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-kKowRHhn.js → chunk-K5T4RW27-8PvAHasY.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-bAEV_5eH.js → chunk-KGLVRYIC--aGaDRH5.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-BLvsQAfg.js → chunk-LIHQZDEY-BrWj7mw2.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-BzYZfPZ7.js → chunk-ORNJ4GCN-kYQmkMTi.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-CL7WRw42.js → chunk-OYMX7WX6-CIsryCWH.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-Duipo1kI.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BdcVGjau.js → chunk-U2HBQHQK-68X653e3.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-C0CL_Jin.js → chunk-X2U36JSP-v3v9soX3.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-CsV7CUxH.js → chunk-XPW4576I-CW5DPpop.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-Cj0_fLkR.js → chunk-YZCP3GAM-B1VP7Rl2.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-DLuquyoY.js → chunk-ZZ45TVLE-DiJrujNY.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-B3kX5SuB.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-D761c7Lr.js +1 -0
- package/payload/server/public/assets/clone-BrhFv6Tb.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BU3EOLXX.js → cose-bilkent-S5V4N54A-BscXxLMc.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-CoCAYhFc.js → dagre-KV5264BT-DVgGlTQ1.js} +1 -1
- package/payload/server/public/assets/{dagre-BRq8C9NU.js → dagre-MBv6WlCS.js} +1 -1
- package/payload/server/public/assets/data-CoOcAR_2.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-DBy5GNaQ.js → diagram-5BDNPKRD-BmhCaO0a.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-l93MsBLv.js → diagram-G4DWMVQ6-ChU9L8vz.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-BIfXkVlL.js → diagram-MMDJMWI5-Oy409Zc_.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-BuTJLBny.js → diagram-TYMM5635-Cn1YByqk.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-BwPxWDbP.js → erDiagram-SMLLAGMA-e17yfLvr.js} +1 -1
- package/payload/server/public/assets/{flatten-DtM0XAMt.js → flatten-Cl1Bc3dR.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BhPtHXMR.js → flowDiagram-DWJPFMVM-Dqt7sCVZ.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BlXjprdD.js → ganttDiagram-T4ZO3ILL-B4IhwS2r.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-BCQAqtLy.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-BW36BDiu.js → gitGraphDiagram-UUTBAWPF-BDw-PKcI.js} +1 -1
- package/payload/server/public/assets/{graph-uFhGyOZR.js → graph-Dc_votDd.js} +3 -3
- package/payload/server/public/assets/{graph-labels-BNnhRQqM.js → graph-labels-jqagxwhW.js} +1 -1
- package/payload/server/public/assets/{graphlib-Bjg3Tq-k.js → graphlib-DPIYk_sg.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-qU7hK50T.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-KGdcJeTs.js +2 -0
- package/payload/server/public/assets/{isEmpty-D__iH1WQ.js → isEmpty-D1pGOD1J.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CmB-bA_O.js → ishikawaDiagram-UXIWVN3A-BRIHsB3G.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-Bt1fJKmc.js → journeyDiagram-VCZTEJTY-aKNgmoHd.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DVbksz_J.js → kanban-definition-6JOO6SKY-cwBwT-nm.js} +1 -1
- package/payload/server/public/assets/{line-BX2Ugx3B.js → line-DVtVRzUA.js} +1 -1
- package/payload/server/public/assets/{linear-Bdc-QJWI.js → linear-DtoOFPO2.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-qv1e-J5x.js → mermaid-parser.core-G7YD_zO3.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-C_d9YQX_.js → mermaid.core-DpFyndHm.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-DwKGvF14.js → mindmap-definition-QFDTVHPH-CYXK_Pvz.js} +1 -1
- package/payload/server/public/assets/operator-KYXcuQ4Y.js +1 -0
- package/payload/server/public/assets/{ordinal-BRQ5CzZY.js → ordinal-CBZeH4Yp.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-DiU2yS9m.js +1 -0
- package/payload/server/public/assets/page-Cfsv97nq.js +32 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-zHUKKlVv.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-dUvQC_4S.js → pieDiagram-DEJITSTG-DFo4N6WH.js} +1 -1
- package/payload/server/public/assets/{public-DgltXhnD.js → public-49guEjCh.js} +1 -1
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CebX_oPa.js → quadrantDiagram-34T5L4WZ-BlH5iIhl.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-BUnVoaPg.js +1 -0
- package/payload/server/public/assets/{reduce-CkcXOmmJ.js → reduce-PuPlFPRX.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-h2euIqGj.js → requirementDiagram-MS252O5E-D5WLty8A.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-CyR5o3Nq.js +1 -0
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DCii3fkF.js → sankeyDiagram-XADWPNL6-vB85IxHG.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-CQSGYmlq.js → sequenceDiagram-FGHM5R23-Cxraa0Ee.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CVtU0iXH.js → stateDiagram-FHFEXIEX-BBPssYBx.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BWmmUwDt.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-ChvsBltI.js → timeline-definition-GMOUNBTQ-Bgk6PT62.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-C3Y-Q52h.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-Dk0qZpAg.js +1 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-e-EZiiba.js → vennDiagram-DHZGUBPP-B-APTs3L.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-BAWI02OA.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-4JaRn1s5.js → wardleyDiagram-NUSXRM2D-CkXJSs_i.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-kxwmCNuM.js → xychartDiagram-5P7HB3ND-BkyshPMV.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +4 -4
- package/payload/server/public/chat.html +7 -6
- package/payload/server/public/data.html +4 -4
- package/payload/server/public/graph.html +7 -6
- package/payload/server/public/index.html +6 -6
- package/payload/server/public/operator.html +9 -8
- package/payload/server/public/public.html +7 -6
- package/payload/server/server.js +244 -24
- package/payload/server/public/assets/AdminShell-CDQDkT1R.js +0 -1
- package/payload/server/public/assets/OperatorConversations-CVH-FoJo.css +0 -1
- package/payload/server/public/assets/OperatorConversations-jqaPCHwB.js +0 -9
- package/payload/server/public/assets/admin-BkYc54bi.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Csj53u5T.js +0 -1
- package/payload/server/public/assets/calendar-DgP7lal2.js +0 -1
- package/payload/server/public/assets/channel-BUQUAUts.js +0 -1
- package/payload/server/public/assets/chat-gls5aV50.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-CdknReB2.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-8tHoEDE9.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BEGZ3DLg.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DsLxnm5U.js +0 -1
- package/payload/server/public/assets/clone-uiXbH_kh.js +0 -1
- package/payload/server/public/assets/data-CbI8-CX3.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-D_2KrZoP.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-BP2TNwQ4.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-D1kDtBOY.js +0 -2
- package/payload/server/public/assets/operator-DeCk_qYw.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-BDg8fde8.js +0 -1
- package/payload/server/public/assets/page-jGwq-DpW.js +0 -32
- package/payload/server/public/assets/pie-ZZUOXDRM-CXDyYMkZ.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-xen_zv8F.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BywcXjtb.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-2FBf_1It.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-BDdb30P-.js +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-BLq5MxjR.js +0 -1
- /package/payload/server/public/assets/{_baseFor-SRtUHe7G.js → _baseFor-brRjpUOX.js} +0 -0
- /package/payload/server/public/assets/{array-BpsnM-Py.js → array-BGFCBI0e.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-D4Rl1H5h.js → cytoscape.esm-TvRJ2tGX.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-C7sQPl-O.js → defaultLocale-CRZydyG6.js} +0 -0
- /package/payload/server/public/assets/{dist-CtUNt3w8.js → dist-B0CkUodR.js} +0 -0
- /package/payload/server/public/assets/{init-BydxaDEV.js → init-B8gtcn7T.js} +0 -0
- /package/payload/server/public/assets/{katex-B0fVeDx4.js → katex-RxgSu_dy.js} +0 -0
- /package/payload/server/public/assets/{path-jlWYQ2i9.js → path-DZF-JdEe.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-uTix4PVD.js → preload-helper-CQ36nxok.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-c4PR5shF.js → rough.esm-6CnTHTkH.js} +0 -0
- /package/payload/server/public/assets/{src-48kCpScq.js → src-CR_MU8uy.js} +0 -0
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
// slice un-enabled after install).
|
|
7
7
|
import test from "node:test";
|
|
8
8
|
import assert from "node:assert/strict";
|
|
9
|
-
import { buildClaudePtysSliceUnitFile } from "../port-resolution.js";
|
|
9
|
+
import { buildClaudePtysSliceUnitFile, resolveClaudePtysSliceMemoryHigh } from "../port-resolution.js";
|
|
10
|
+
const GiB = 1024 ** 3;
|
|
11
|
+
const memHighMiB = (unit) => {
|
|
12
|
+
const m = unit.match(/^MemoryHigh=(\d+)M$/m);
|
|
13
|
+
assert.ok(m, "unit must carry a MemoryHigh=<n>M line");
|
|
14
|
+
return Number(m[1]);
|
|
15
|
+
};
|
|
10
16
|
test("buildClaudePtysSliceUnitFile emits a valid systemd slice unit", () => {
|
|
11
17
|
const unit = buildClaudePtysSliceUnitFile();
|
|
12
18
|
assert.match(unit, /^\[Unit\]$/m);
|
|
@@ -23,3 +29,32 @@ test("buildClaudePtysSliceUnitFile output has no per-brand or per-install templa
|
|
|
23
29
|
assert.doesNotMatch(unit, /\$\{/);
|
|
24
30
|
assert.doesNotMatch(unit, /__[A-Z_]+__/);
|
|
25
31
|
});
|
|
32
|
+
// Task 1176 — the slice carries a soft MemoryHigh sized to the host so the
|
|
33
|
+
// session cohort absorbs reclaim pressure (throttle/swap within the slice)
|
|
34
|
+
// before the box exhausts RAM and the kernel kills the brand server. The
|
|
35
|
+
// value is total RAM minus a ~2.7GB fixed floor (neo4j + server +
|
|
36
|
+
// edge/manager/chrome), clamped to a 2GiB minimum. MemoryHigh (soft), never
|
|
37
|
+
// MemoryMax (hard — would just relocate the OOM kill inside the slice).
|
|
38
|
+
test("resolveClaudePtysSliceMemoryHigh scales with host RAM and clamps tiny hosts", () => {
|
|
39
|
+
const low = resolveClaudePtysSliceMemoryHigh(8 * GiB);
|
|
40
|
+
const high = resolveClaudePtysSliceMemoryHigh(16 * GiB);
|
|
41
|
+
assert.match(low, /^\d+M$/);
|
|
42
|
+
assert.match(high, /^\d+M$/);
|
|
43
|
+
// 8GB host: total minus 2.7GB floor lands in the 4-6 GiB band.
|
|
44
|
+
const lowMiB = Number(low.replace("M", ""));
|
|
45
|
+
assert.ok(lowMiB > 4000 && lowMiB < 6000, `8GB MemoryHigh out of band: ${low}`);
|
|
46
|
+
// A 16GB host gets a strictly larger ceiling than an 8GB host.
|
|
47
|
+
assert.ok(Number(high.replace("M", "")) > lowMiB);
|
|
48
|
+
// Tiny host clamps to the 2GiB floor rather than emitting a near-zero or
|
|
49
|
+
// negative MemoryHigh.
|
|
50
|
+
assert.equal(resolveClaudePtysSliceMemoryHigh(1 * GiB), "2048M");
|
|
51
|
+
});
|
|
52
|
+
test("buildClaudePtysSliceUnitFile carries a MemoryHigh line under [Slice] that scales with RAM", () => {
|
|
53
|
+
const low = buildClaudePtysSliceUnitFile(8 * GiB);
|
|
54
|
+
const high = buildClaudePtysSliceUnitFile(16 * GiB);
|
|
55
|
+
// MemoryHigh appears, and after the [Slice] header.
|
|
56
|
+
assert.match(low, /^MemoryHigh=\d+M$/m);
|
|
57
|
+
assert.ok(low.indexOf("[Slice]") < low.indexOf("MemoryHigh="), "MemoryHigh must be inside [Slice]");
|
|
58
|
+
// The 16GB unit's ceiling exceeds the 8GB unit's.
|
|
59
|
+
assert.ok(memHighMiB(high) > memHighMiB(low));
|
|
60
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Contract grid for resolveNeo4jPidfileScrub (the pure decision behind the
|
|
2
|
+
// install-time per-boot scrub provisioning) plus a behaviour grid for the
|
|
3
|
+
// shipped SCRUB_SCRIPT run against crafted PIDs. The wrapper in index.ts owns
|
|
4
|
+
// the privileged fs/systemctl; this suite is decision + script only, no sudo.
|
|
5
|
+
//
|
|
6
|
+
// The load-bearing guarantee: the scrub NEVER removes a pidfile that names a
|
|
7
|
+
// live Neo4j process, and ALWAYS removes one whose PID is dead or reused by an
|
|
8
|
+
// unrelated process. Runs via Node's built-in test runner (codebase convention,
|
|
9
|
+
// see neo4j-teardown.test.ts).
|
|
10
|
+
import test from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { mkdtempSync, writeFileSync, existsSync, rmSync } from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { spawnSync, spawn } from "node:child_process";
|
|
16
|
+
import { resolveNeo4jPidfileScrub, SCRUB_SCRIPT, SCRUB_SCRIPT_PATH } from "../neo4j-pidfile-scrub.js";
|
|
17
|
+
// ---- resolve grid (pure) ----
|
|
18
|
+
test("default brand (port 7687) → apt neo4j unit + /var/lib/neo4j/run pidfile", () => {
|
|
19
|
+
const plan = resolveNeo4jPidfileScrub({ brandHostname: "maxy-code", neo4jPort: 7687 });
|
|
20
|
+
assert.notEqual(plan, null);
|
|
21
|
+
assert.equal(plan.unit, "neo4j");
|
|
22
|
+
assert.equal(plan.pidfile, "/var/lib/neo4j/run/neo4j.pid");
|
|
23
|
+
assert.equal(plan.dropinPath, "/etc/systemd/system/neo4j.service.d/10-maxy-pidfile-scrub.conf");
|
|
24
|
+
assert.match(plan.dropinContent, /ExecStartPre=\+\/usr\/local\/lib\/maxy-neo4j-pidfile-scrub\.sh \/var\/lib\/neo4j\/run\/neo4j\.pid/);
|
|
25
|
+
assert.equal(plan.scriptPath, SCRUB_SCRIPT_PATH);
|
|
26
|
+
});
|
|
27
|
+
test("dedicated brand (custom port) → neo4j-<host> unit + per-brand run pidfile", () => {
|
|
28
|
+
const plan = resolveNeo4jPidfileScrub({ brandHostname: "sitedesk-code", neo4jPort: 7689 });
|
|
29
|
+
assert.notEqual(plan, null);
|
|
30
|
+
assert.equal(plan.unit, "neo4j-sitedesk-code");
|
|
31
|
+
assert.equal(plan.pidfile, "/var/lib/neo4j-sitedesk-code/run/neo4j.pid");
|
|
32
|
+
assert.equal(plan.dropinPath, "/etc/systemd/system/neo4j-sitedesk-code.service.d/10-maxy-pidfile-scrub.conf");
|
|
33
|
+
// dedicated drop-in never names the shared run-dir pidfile
|
|
34
|
+
assert.doesNotMatch(plan.dropinContent, /\/var\/lib\/neo4j\/run\//);
|
|
35
|
+
});
|
|
36
|
+
test("unreadable port (undefined) → null (never guess the unit)", () => {
|
|
37
|
+
assert.equal(resolveNeo4jPidfileScrub({ brandHostname: "sitedesk-code", neo4jPort: undefined }), null);
|
|
38
|
+
});
|
|
39
|
+
test("dedicated port with empty hostname → null (cannot form a unit)", () => {
|
|
40
|
+
assert.equal(resolveNeo4jPidfileScrub({ brandHostname: "", neo4jPort: 7689 }), null);
|
|
41
|
+
});
|
|
42
|
+
// ---- script behaviour (writes SCRUB_SCRIPT to disk, runs against crafted PIDs) ----
|
|
43
|
+
function runScrub(pidfile) {
|
|
44
|
+
const dir = mkdtempSync(join(tmpdir(), "scrub-"));
|
|
45
|
+
const script = join(dir, "scrub.sh");
|
|
46
|
+
writeFileSync(script, SCRUB_SCRIPT, { mode: 0o755 });
|
|
47
|
+
const r = spawnSync("sh", [script, pidfile], { encoding: "utf8" });
|
|
48
|
+
rmSync(dir, { recursive: true, force: true });
|
|
49
|
+
return { stdout: r.stdout ?? "", status: r.status ?? -1 };
|
|
50
|
+
}
|
|
51
|
+
test("absent pidfile → no-op, exit 0", () => {
|
|
52
|
+
const r = runScrub("/no/such/pidfile");
|
|
53
|
+
assert.equal(r.status, 0);
|
|
54
|
+
assert.match(r.stdout, /no-op .*reason=absent/);
|
|
55
|
+
});
|
|
56
|
+
test("unparseable pidfile → removed, exit 0", () => {
|
|
57
|
+
const dir = mkdtempSync(join(tmpdir(), "scrub-pf-"));
|
|
58
|
+
const pf = join(dir, "neo4j.pid");
|
|
59
|
+
writeFileSync(pf, "not-a-number\n");
|
|
60
|
+
const r = runScrub(pf);
|
|
61
|
+
assert.equal(r.status, 0);
|
|
62
|
+
assert.match(r.stdout, /removed .*reason=unparseable/);
|
|
63
|
+
assert.equal(existsSync(pf), false);
|
|
64
|
+
rmSync(dir, { recursive: true, force: true });
|
|
65
|
+
});
|
|
66
|
+
test("dead PID → removed, exit 0", () => {
|
|
67
|
+
const dir = mkdtempSync(join(tmpdir(), "scrub-pf-"));
|
|
68
|
+
const pf = join(dir, "neo4j.pid");
|
|
69
|
+
// 999999 is above the default pid_max on Linux and unused on darwin — dead.
|
|
70
|
+
writeFileSync(pf, "999999\n");
|
|
71
|
+
const r = runScrub(pf);
|
|
72
|
+
assert.equal(r.status, 0);
|
|
73
|
+
assert.match(r.stdout, /removed .*reason=dead/);
|
|
74
|
+
assert.equal(existsSync(pf), false);
|
|
75
|
+
rmSync(dir, { recursive: true, force: true });
|
|
76
|
+
});
|
|
77
|
+
test("alive non-neo4j PID → removed, exit 0", async () => {
|
|
78
|
+
const dir = mkdtempSync(join(tmpdir(), "scrub-pf-"));
|
|
79
|
+
const pf = join(dir, "neo4j.pid");
|
|
80
|
+
const proc = spawn("sleep", ["30"]); // argv has no 'neo4j'
|
|
81
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
82
|
+
writeFileSync(pf, String(proc.pid) + "\n");
|
|
83
|
+
const r = runScrub(pf);
|
|
84
|
+
proc.kill("SIGKILL");
|
|
85
|
+
assert.equal(r.status, 0);
|
|
86
|
+
assert.match(r.stdout, /removed .*reason=alive-other/);
|
|
87
|
+
assert.equal(existsSync(pf), false);
|
|
88
|
+
rmSync(dir, { recursive: true, force: true });
|
|
89
|
+
});
|
|
90
|
+
test("alive Neo4j-cmdline PID → no-op, exit 0 (Linux only — needs /proc)", { skip: process.platform !== "linux" }, async () => {
|
|
91
|
+
const dir = mkdtempSync(join(tmpdir(), "scrub-pf-"));
|
|
92
|
+
const pf = join(dir, "neo4j.pid");
|
|
93
|
+
// a long-lived process whose argv contains 'neo4j' ($0 = the stub name)
|
|
94
|
+
const proc = spawn("sh", ["-c", "sleep 30", "neo4j-jvm-stub"]);
|
|
95
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
96
|
+
writeFileSync(pf, String(proc.pid) + "\n");
|
|
97
|
+
const r = runScrub(pf);
|
|
98
|
+
proc.kill("SIGKILL");
|
|
99
|
+
assert.equal(r.status, 0);
|
|
100
|
+
assert.match(r.stdout, /no-op .*reason=live-neo4j/);
|
|
101
|
+
assert.equal(existsSync(pf), true); // never removes a live neo4j's pidfile
|
|
102
|
+
rmSync(dir, { recursive: true, force: true });
|
|
103
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { resolveInstallPortFromFs, buildMaxyUnitFile, buildClaudeSessionManagerU
|
|
|
9
9
|
import { validateTierFlag, validateEntitlementBase64, applyTierToAccountConfig, entitlementPath } from "./tier-flag.js";
|
|
10
10
|
import { parseOsRelease, isUbuntuLike as isUbuntuLikePure, parseAptCacheCandidate, decideAptResolution, } from "./apt-resolve.js";
|
|
11
11
|
import { findPeerBrandOnDefaultNeo4jPort } from "./peer-brand-detect.js";
|
|
12
|
+
import { resolveNeo4jPidfileScrub } from "./neo4j-pidfile-scrub.js";
|
|
12
13
|
import { runInitLogging } from "./init-logging.js";
|
|
13
14
|
import { requireSupportedPlatform, detectPlatform } from "./platform-detect.js";
|
|
14
15
|
import { protectCommands, unprotectCommands, lsattrShowsImmutable } from "./install-immutability.js";
|
|
@@ -1515,13 +1516,16 @@ WantedBy=multi-user.target
|
|
|
1515
1516
|
// ============================================================================
|
|
1516
1517
|
// unified state remediation + start + verify.
|
|
1517
1518
|
//
|
|
1518
|
-
// Runs on both fresh and recovery paths. The
|
|
1519
|
-
//
|
|
1520
|
-
// NEO4J_HOME
|
|
1521
|
-
//
|
|
1522
|
-
//
|
|
1523
|
-
//
|
|
1524
|
-
//
|
|
1519
|
+
// Runs on both fresh and recovery paths. The apt system unit leaves
|
|
1520
|
+
// NEO4J_HOME unset so its run dir is /var/lib/neo4j/run; the dedicated unit
|
|
1521
|
+
// sets NEO4J_HOME=<per-brand data dir> so server.directories.run resolves
|
|
1522
|
+
// per-brand to <data dir>/run — no collision with the system unit's pidfile.
|
|
1523
|
+
// Either way the launcher refuses with "Neo4j is already running (pid:N)" if
|
|
1524
|
+
// a stale run-dir pidfile names a still-live PID. Stopping the system unit
|
|
1525
|
+
// first frees its run-state; disabling prevents it returning at next boot.
|
|
1526
|
+
// reset-failed clears any prior start-limit-hit from a half-installed Pi.
|
|
1527
|
+
// (The per-boot scrub from provisionNeo4jPidfileScrub auto-clears the
|
|
1528
|
+
// reused-PID case on every subsequent boot.)
|
|
1525
1529
|
// ============================================================================
|
|
1526
1530
|
spawnSync("sudo", ["systemctl", "daemon-reload"], { stdio: "inherit" });
|
|
1527
1531
|
console.log(" [privileged] systemctl enable");
|
|
@@ -1599,6 +1603,36 @@ WantedBy=multi-user.target
|
|
|
1599
1603
|
logFile(` Neo4j dedicated: config=${confDir} data=${dataDir} service=${serviceName} bolt=:${NEO4J_PORT} http=:${httpPort}`);
|
|
1600
1604
|
console.log(` Dedicated Neo4j instance ready on bolt://localhost:${NEO4J_PORT}`);
|
|
1601
1605
|
}
|
|
1606
|
+
// Provision the per-boot stale-pidfile scrub drop-in for whichever Neo4j unit
|
|
1607
|
+
// this brand uses. The drop-in's ExecStartPre runs the host-shared scrub script
|
|
1608
|
+
// before each unit start, removing a stale run-dir pidfile whose PID is not a
|
|
1609
|
+
// live Neo4j (the reused-PID case that browns-out a Pi into a non-starting
|
|
1610
|
+
// Neo4j after an unclean reboot). Idempotent: fixed paths, overwritten each
|
|
1611
|
+
// install, never appended. Linux-only — a no-op on darwin/dev hosts.
|
|
1612
|
+
function provisionNeo4jPidfileScrub() {
|
|
1613
|
+
if (process.platform !== "linux")
|
|
1614
|
+
return;
|
|
1615
|
+
const plan = resolveNeo4jPidfileScrub({ brandHostname: BRAND.hostname, neo4jPort: NEO4J_PORT });
|
|
1616
|
+
if (plan === null) {
|
|
1617
|
+
logFile(` [neo4j] pidfile-scrub: no plan (port=${NEO4J_PORT} hostname=${BRAND.hostname}) — skipped`);
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
// pid-namespaced staging paths so two co-resident brand installs never race
|
|
1621
|
+
// on a shared /tmp file (matches the file's other tmp-staging idioms).
|
|
1622
|
+
const tmpScript = `/tmp/maxy-neo4j-pidfile-scrub.${process.pid}.sh`;
|
|
1623
|
+
writeFileSync(tmpScript, plan.scriptContent);
|
|
1624
|
+
shell("cp", [tmpScript, plan.scriptPath], { sudo: true });
|
|
1625
|
+
shell("chmod", ["0755", plan.scriptPath], { sudo: true });
|
|
1626
|
+
spawnSync("rm", ["-f", tmpScript]);
|
|
1627
|
+
shell("mkdir", ["-p", plan.dropinDir], { sudo: true });
|
|
1628
|
+
const tmpDropin = `/tmp/10-maxy-pidfile-scrub.${process.pid}.conf`;
|
|
1629
|
+
writeFileSync(tmpDropin, plan.dropinContent);
|
|
1630
|
+
shell("cp", [tmpDropin, plan.dropinPath], { sudo: true });
|
|
1631
|
+
spawnSync("rm", ["-f", tmpDropin]);
|
|
1632
|
+
spawnSync("sudo", ["systemctl", "daemon-reload"], { stdio: "inherit" });
|
|
1633
|
+
console.log(` [neo4j] pidfile-scrub armed for ${plan.unit} (ExecStartPre scrubs ${plan.pidfile})`);
|
|
1634
|
+
logFile(` [neo4j] pidfile-scrub armed unit=${plan.unit} pidfile=${plan.pidfile} dropin=${plan.dropinPath} script=${plan.scriptPath}`);
|
|
1635
|
+
}
|
|
1602
1636
|
function ensureOllamaServing() {
|
|
1603
1637
|
const check = () => spawnSync("curl", ["-sf", "http://localhost:11434/api/tags"], {
|
|
1604
1638
|
stdio: "pipe", timeout: 5_000,
|
|
@@ -4347,6 +4381,7 @@ try {
|
|
|
4347
4381
|
installClaudeCode();
|
|
4348
4382
|
installNeo4j();
|
|
4349
4383
|
setupDedicatedNeo4j();
|
|
4384
|
+
provisionNeo4jPidfileScrub();
|
|
4350
4385
|
installOllama(EMBED_MODEL);
|
|
4351
4386
|
installUv();
|
|
4352
4387
|
installCloudflared();
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Pure per-boot Neo4j stale-pidfile scrub resolution + the scrub script body.
|
|
2
|
+
// Mirrors neo4j-teardown.ts: inputs in, plan out, no fs/systemctl. The installer
|
|
3
|
+
// wrapper (provisionNeo4jPidfileScrub in index.ts) owns the privileged writes.
|
|
4
|
+
//
|
|
5
|
+
// Why: after an unclean reboot the neo4j launcher refuses to start with
|
|
6
|
+
// "Neo4j is already running (pid:N)" when the run-dir pidfile names a PID that
|
|
7
|
+
// is alive but reused by an unrelated process (the launcher's kill -0 liveness
|
|
8
|
+
// check false-positives). The scrub runs as the unit's ExecStartPre and removes
|
|
9
|
+
// the pidfile IFF the PID it names is not a live Neo4j process, so the next boot
|
|
10
|
+
// recovers with no human action. It never removes a pidfile naming a live Neo4j.
|
|
11
|
+
import { SHARED_NEO4J_PORT, resolveDedicatedNeo4jTeardown } from "./neo4j-teardown.js";
|
|
12
|
+
/** Host-shared scrub script path. Stateless and parameterised by the pidfile
|
|
13
|
+
* path, so one copy serves the apt unit and any dedicated unit. */
|
|
14
|
+
export const SCRUB_SCRIPT_PATH = "/usr/local/lib/maxy-neo4j-pidfile-scrub.sh";
|
|
15
|
+
const DROPIN_BASENAME = "10-maxy-pidfile-scrub.conf";
|
|
16
|
+
/** The scrub script body. POSIX sh. $1 = the run-dir pidfile path. Always exits
|
|
17
|
+
* 0 — an ExecStartPre that fails would block the unit from starting. Emits one
|
|
18
|
+
* line per run naming the action, pidfile, PID, and what the PID was. */
|
|
19
|
+
export const SCRUB_SCRIPT = `#!/bin/sh
|
|
20
|
+
# Maxy per-boot Neo4j stale-pidfile scrub. ExecStartPre for the brand's Neo4j
|
|
21
|
+
# unit. Removes the run-dir pidfile IFF the PID it names is not a live Neo4j
|
|
22
|
+
# process, so an unclean reboot that left a stale pidfile (whose PID may have
|
|
23
|
+
# been reused by an unrelated process) does not make the launcher false-positive
|
|
24
|
+
# "Neo4j is already running". Never removes a pidfile naming a live Neo4j.
|
|
25
|
+
# Always exits 0 so it cannot block unit start.
|
|
26
|
+
set -u
|
|
27
|
+
PIDFILE="\${1:-}"
|
|
28
|
+
emit() { echo "neo4j-pidfile-scrub $1 pidfile=\${PIDFILE:-<none>} pid=$2 reason=$3"; }
|
|
29
|
+
|
|
30
|
+
if [ -z "$PIDFILE" ]; then emit no-op '<none>' no-pidfile-arg; exit 0; fi
|
|
31
|
+
if [ ! -e "$PIDFILE" ]; then emit no-op '<none>' absent; exit 0; fi
|
|
32
|
+
|
|
33
|
+
PID="$(tr -d '[:space:]' < "$PIDFILE" 2>/dev/null)"
|
|
34
|
+
case "$PID" in
|
|
35
|
+
''|*[!0-9]*) emit removed "\${PID:-<empty>}" unparseable; rm -f "$PIDFILE"; exit 0 ;;
|
|
36
|
+
esac
|
|
37
|
+
|
|
38
|
+
if ! kill -0 "$PID" 2>/dev/null; then
|
|
39
|
+
emit removed "$PID" dead
|
|
40
|
+
rm -f "$PIDFILE"
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
CMD="$(tr '\\0' ' ' < "/proc/$PID/cmdline" 2>/dev/null)"
|
|
45
|
+
case "$CMD" in
|
|
46
|
+
*neo4j*) emit no-op "$PID" live-neo4j; exit 0 ;;
|
|
47
|
+
*) emit removed "$PID" alive-other; rm -f "$PIDFILE"; exit 0 ;;
|
|
48
|
+
esac
|
|
49
|
+
`;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the per-boot scrub plan for a brand's Neo4j unit, or `null` when no
|
|
52
|
+
* unit can be safely targeted:
|
|
53
|
+
* - `neo4jPort === undefined`: unreadable port — never guess the unit.
|
|
54
|
+
* - dedicated port with empty `brandHostname`: no `neo4j-<host>` unit exists.
|
|
55
|
+
* A default-brand port (7687) targets the shared apt `neo4j` unit; any other
|
|
56
|
+
* port targets the brand-dedicated `neo4j-<hostname>` unit, whose run dir lives
|
|
57
|
+
* under its per-brand `NEO4J_HOME` (`<dataDir>/run`). The dedicated unit name +
|
|
58
|
+
* data dir are taken from `resolveDedicatedNeo4jTeardown` so install, uninstall,
|
|
59
|
+
* and this scrub share one derivation and never drift.
|
|
60
|
+
*/
|
|
61
|
+
export function resolveNeo4jPidfileScrub(args) {
|
|
62
|
+
const { brandHostname, neo4jPort } = args;
|
|
63
|
+
if (neo4jPort === undefined)
|
|
64
|
+
return null;
|
|
65
|
+
let unit;
|
|
66
|
+
let pidfile;
|
|
67
|
+
if (neo4jPort === SHARED_NEO4J_PORT) {
|
|
68
|
+
unit = "neo4j";
|
|
69
|
+
pidfile = "/var/lib/neo4j/run/neo4j.pid";
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Dedicated brand: reuse the teardown resolver's unit/dataDir derivation
|
|
73
|
+
// (returns null on empty hostname) rather than re-deriving the names here.
|
|
74
|
+
const dedicated = resolveDedicatedNeo4jTeardown({ brandHostname, neo4jPort });
|
|
75
|
+
if (dedicated === null)
|
|
76
|
+
return null;
|
|
77
|
+
unit = dedicated.service;
|
|
78
|
+
pidfile = `${dedicated.dataDir}/run/neo4j.pid`;
|
|
79
|
+
}
|
|
80
|
+
const dropinDir = `/etc/systemd/system/${unit}.service.d`;
|
|
81
|
+
return {
|
|
82
|
+
unit,
|
|
83
|
+
pidfile,
|
|
84
|
+
dropinDir,
|
|
85
|
+
dropinPath: `${dropinDir}/${DROPIN_BASENAME}`,
|
|
86
|
+
dropinContent: `[Service]\nExecStartPre=+${SCRUB_SCRIPT_PATH} ${pidfile}\n`,
|
|
87
|
+
scriptPath: SCRUB_SCRIPT_PATH,
|
|
88
|
+
scriptContent: SCRUB_SCRIPT,
|
|
89
|
+
};
|
|
90
|
+
}
|
package/dist/port-resolution.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
3
4
|
const PORT_RE = /^PORT=(\d+)/m;
|
|
4
5
|
const ENV_PORT_RE = /^Environment=PORT=(\d+)/m;
|
|
5
6
|
const ENV_EDGE_PORT_RE = /^Environment=EDGE_PORT=(\d+)/m;
|
|
@@ -174,13 +175,29 @@ WantedBy=default.target
|
|
|
174
175
|
// `~/.config/systemd/user/claude-ptys.slice` and runs
|
|
175
176
|
// `systemctl --user daemon-reload` so the new unit is picked up.
|
|
176
177
|
// ---------------------------------------------------------------------------
|
|
177
|
-
|
|
178
|
+
// Task 1176 — the slice carries a soft MemoryHigh so the session cohort
|
|
179
|
+
// absorbs reclaim pressure (throttle/swap within the slice) before the box
|
|
180
|
+
// exhausts RAM and the kernel kills the brand server. The value is total
|
|
181
|
+
// RAM minus a fixed ~2.7GB floor (neo4j + brand server + edge/manager/chrome
|
|
182
|
+
// must stay resident), clamped to a 2GiB minimum so a small host still emits
|
|
183
|
+
// a valid, non-trivial ceiling. MemoryHigh is a SOFT reclaim threshold —
|
|
184
|
+
// never MemoryMax, which would hard-kill within the slice and just relocate
|
|
185
|
+
// the failure.
|
|
186
|
+
const SLICE_FLOOR_BYTES = Math.round(2.7 * 1024 ** 3);
|
|
187
|
+
const SLICE_MIN_HIGH_BYTES = 2 * 1024 ** 3;
|
|
188
|
+
export function resolveClaudePtysSliceMemoryHigh(totalMemBytes) {
|
|
189
|
+
const high = Math.max(totalMemBytes - SLICE_FLOOR_BYTES, SLICE_MIN_HIGH_BYTES);
|
|
190
|
+
return `${Math.round(high / 1024 ** 2)}M`;
|
|
191
|
+
}
|
|
192
|
+
export function buildClaudePtysSliceUnitFile(totalMemBytes = os.totalmem()) {
|
|
193
|
+
const memoryHigh = resolveClaudePtysSliceMemoryHigh(totalMemBytes);
|
|
178
194
|
return `[Unit]
|
|
179
195
|
Description=Claude Code PTY sessions (Task 250)
|
|
180
196
|
Documentation=https://github.com/rubytech/maxy-code/blob/main/.tasks/archive/250-claude-ptys-survive-manager-restart-via-systemd-scope-per-pty.md
|
|
181
197
|
Before=shutdown.target
|
|
182
198
|
|
|
183
199
|
[Slice]
|
|
200
|
+
MemoryHigh=${memoryHigh}
|
|
184
201
|
|
|
185
202
|
[Install]
|
|
186
203
|
WantedBy=default.target
|