@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
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export type ClaudeAiConnectorState = "connected" | "needs-auth" | "unreachable";
|
|
2
|
+
export interface ClaudeAiConnector {
|
|
3
|
+
/** The connector's claude.ai display name, prefix included, e.g.
|
|
4
|
+
* "claude.ai Zoom for Claude" — the exact string `claude mcp login` expects. */
|
|
5
|
+
name: string;
|
|
6
|
+
state: ClaudeAiConnectorState;
|
|
7
|
+
}
|
|
1
8
|
export interface CapabilitiesHereInput {
|
|
2
9
|
accountId: string;
|
|
3
10
|
accountConfigPath: string;
|
|
@@ -5,6 +12,13 @@ export interface CapabilitiesHereInput {
|
|
|
5
12
|
platformRoot: string;
|
|
6
13
|
premiumPluginsRoot: string;
|
|
7
14
|
claudeConfigDir: string;
|
|
15
|
+
/** Task 1160 — returns the raw combined output of `claude mcp list` run under
|
|
16
|
+
* the brand config dir. Injected by the adapter (index.ts) so this function
|
|
17
|
+
* stays a pure resolver and so the subprocess never runs in unit tests. When
|
|
18
|
+
* omitted, no connectors are enumerated and nothing is spawned. The provider
|
|
19
|
+
* itself must never throw; this resolver also guards it, degrading to an empty
|
|
20
|
+
* connector list on any failure. */
|
|
21
|
+
listConnectorsRaw?: () => Promise<string>;
|
|
8
22
|
}
|
|
9
23
|
export interface CapabilitiesHereResult {
|
|
10
24
|
accountId: string;
|
|
@@ -18,11 +32,13 @@ export interface CapabilitiesHereResult {
|
|
|
18
32
|
name: string;
|
|
19
33
|
}>;
|
|
20
34
|
registeredAgentTypes: string[];
|
|
35
|
+
claudeAiConnectors: ClaudeAiConnector[];
|
|
21
36
|
brand: {
|
|
22
37
|
productName: string | null;
|
|
23
38
|
shipsPremiumBundles: string[];
|
|
24
39
|
};
|
|
25
40
|
capturedAt: string;
|
|
26
41
|
}
|
|
42
|
+
export declare function parseClaudeAiConnectors(mcpListOutput: string): ClaudeAiConnector[];
|
|
27
43
|
export declare function resolveCapabilitiesHere(input: CapabilitiesHereInput, nowIso: string): Promise<CapabilitiesHereResult>;
|
|
28
44
|
//# sourceMappingURL=capabilities-here.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities-here.d.ts","sourceRoot":"","sources":["../../src/tools/capabilities-here.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"capabilities-here.d.ts","sourceRoot":"","sources":["../../src/tools/capabilities-here.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,sBAAsB,GAAG,WAAW,GAAG,YAAY,GAAG,aAAa,CAAC;AAEhF,MAAM,WAAW,iBAAiB;IAChC;qFACiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,sBAAsB,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;yCAKqC;IACrC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,4BAA4B,EAAE,MAAM,EAAE,CAAC;IACvC,2BAA2B,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;IACxC,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,mBAAmB,EAAE,MAAM,EAAE,CAAC;KAC/B,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;CACpB;AASD,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAwBlF;AAuCD,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,sBAAsB,CAAC,CAmDjC"}
|
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
3
3
|
import { resolve, join } from "node:path";
|
|
4
|
+
// Task 1160 — parse the claude.ai connectors out of `claude mcp list` output.
|
|
5
|
+
// claude.ai connectors are account-synced (not stably enumerable on disk), so the
|
|
6
|
+
// CLI's list is the canonical source of which connectors exist for this client and
|
|
7
|
+
// their per-client auth state. Each connector row has the shape
|
|
8
|
+
// `claude.ai <Display Name>: <url> - <status>`; non-claude.ai rows are ignored.
|
|
9
|
+
// Status is matched on words, not glyphs, because the glyph varies (✔ vs ✓) across
|
|
10
|
+
// CLI builds. Only names and status words are read — no token material. Pure.
|
|
11
|
+
export function parseClaudeAiConnectors(mcpListOutput) {
|
|
12
|
+
const out = [];
|
|
13
|
+
for (const rawLine of mcpListOutput.split("\n")) {
|
|
14
|
+
const line = rawLine.trim();
|
|
15
|
+
if (!line.startsWith("claude.ai "))
|
|
16
|
+
continue;
|
|
17
|
+
const nameEnd = line.indexOf(": ");
|
|
18
|
+
const statusStart = line.lastIndexOf(" - ");
|
|
19
|
+
if (nameEnd < 0 || statusStart < 0 || statusStart < nameEnd)
|
|
20
|
+
continue;
|
|
21
|
+
const name = line.slice(0, nameEnd).trim();
|
|
22
|
+
// Skip a row with no display component (`claude.ai : ...`): the bare prefix is
|
|
23
|
+
// not a connector `claude mcp login` could target, so it must not be surfaced.
|
|
24
|
+
if (name === "claude.ai")
|
|
25
|
+
continue;
|
|
26
|
+
const status = line.slice(statusStart + 3);
|
|
27
|
+
let state;
|
|
28
|
+
// Match the whole word "connected" so "Disconnected" / "Failed to connect" do
|
|
29
|
+
// not read as connected; anything that is neither needs-auth nor connected is
|
|
30
|
+
// not usable, so it maps to unreachable.
|
|
31
|
+
if (/needs authentication/i.test(status))
|
|
32
|
+
state = "needs-auth";
|
|
33
|
+
else if (/\bconnected\b/i.test(status))
|
|
34
|
+
state = "connected";
|
|
35
|
+
else
|
|
36
|
+
state = "unreachable";
|
|
37
|
+
out.push({ name, state });
|
|
38
|
+
}
|
|
39
|
+
out.sort((a, b) => a.name.localeCompare(b.name));
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
async function resolveClaudeAiConnectors(listConnectorsRaw) {
|
|
43
|
+
if (!listConnectorsRaw)
|
|
44
|
+
return [];
|
|
45
|
+
try {
|
|
46
|
+
return parseClaudeAiConnectors(await listConnectorsRaw());
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// The provider should never throw, but a connector-blind capabilities result
|
|
50
|
+
// is far better than a thrown discovery call. Degrade to empty.
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
4
54
|
function listSpecialistFiles(dir) {
|
|
5
55
|
if (!existsSync(dir))
|
|
6
56
|
return [];
|
|
@@ -52,6 +102,7 @@ export async function resolveCapabilitiesHere(input, nowIso) {
|
|
|
52
102
|
installedPremiumSpecialists.push({ bundle, name });
|
|
53
103
|
}
|
|
54
104
|
const registeredAgentTypes = listAgentRegistry(resolve(input.claudeConfigDir, "agents"));
|
|
105
|
+
const claudeAiConnectors = await resolveClaudeAiConnectors(input.listConnectorsRaw);
|
|
55
106
|
return {
|
|
56
107
|
accountId: input.accountId,
|
|
57
108
|
tier,
|
|
@@ -61,6 +112,7 @@ export async function resolveCapabilitiesHere(input, nowIso) {
|
|
|
61
112
|
installedPlatformSpecialists,
|
|
62
113
|
installedPremiumSpecialists,
|
|
63
114
|
registeredAgentTypes,
|
|
115
|
+
claudeAiConnectors,
|
|
64
116
|
brand: { productName, shipsPremiumBundles },
|
|
65
117
|
capturedAt: nowIso,
|
|
66
118
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities-here.js","sourceRoot":"","sources":["../../src/tools/capabilities-here.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"capabilities-here.js","sourceRoot":"","sources":["../../src/tools/capabilities-here.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:6f90b00dd8971be316fca8e109765ec840a220d06f1cb4f1aabecefb9da17916
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -1123,6 +1123,12 @@ The most common cause is wrong nameservers on the domain. The domain must use Cl
|
|
|
1123
1123
|
- 5 failed login attempts → 15-minute lockout — wait for expiry.
|
|
1124
1124
|
- The remote password is set during Cloudflare Tunnel onboarding — the agent asks for one in chat and stores it deterministically. The browser form at `/__remote-auth/setup` remains available for resets on the local network.
|
|
1125
1125
|
|
|
1126
|
+
### Large `/data` upload fails with a connection error
|
|
1127
|
+
|
|
1128
|
+
- Cloudflare proxies a request body of at most ~100 MB on the current plan. A single upload larger than that is cut at the edge before it reaches the server, so the browser sees a connection error rather than a clean size rejection.
|
|
1129
|
+
- The `/data` page handles this itself: files over 95 MB are split into ~48 MB parts and sent one at a time under the cap, and the server reassembles them. No plan change or dashboard action is needed for uploads up to the 2 GB app ceiling.
|
|
1130
|
+
- Raising the proxied body limit is a paid-plan change made on the Cloudflare dashboard, not something the agent does.
|
|
1131
|
+
|
|
1126
1132
|
## What the agent does and does not do
|
|
1127
1133
|
|
|
1128
1134
|
**Does:** invokes `cloudflared` directly via Bash, following `plugins/cloudflare/references/manual-setup.md` step by step; quotes click-paths from the reference files verbatim; verifies external reachability with `curl -I` and surfaces the response.
|
|
@@ -1709,6 +1715,56 @@ A 25-component design system (title slides, metric cards, quotes, comparison tab
|
|
|
1709
1715
|
|
|
1710
1716
|
This is a frozen, vendored copy of the open-source Slides™ framework (MIT). It ships installed and available on every account; enable it per session like any other plugin if it is not already active.
|
|
1711
1717
|
|
|
1718
|
+
---
|
|
1719
|
+
# Connectors
|
|
1720
|
+
Source: https://docs.getmaxy.com/connectors.md
|
|
1721
|
+
|
|
1722
|
+
# claude.ai connectors
|
|
1723
|
+
|
|
1724
|
+
claude.ai connectors (Zoom, Dropbox, GitHub, Google Calendar, and the rest you add on claude.ai) work
|
|
1725
|
+
inside your admin chat once they are authorised for this Maxy client. This page explains why a
|
|
1726
|
+
connector you already added can still say it needs authentication, and how to fix it without leaving
|
|
1727
|
+
the chat.
|
|
1728
|
+
|
|
1729
|
+
## Asking for a connector by name
|
|
1730
|
+
|
|
1731
|
+
You can ask the agent to use a connector you added on claude.ai by name — "can we connect my Zoom
|
|
1732
|
+
account?", "save this to Dropbox", "check my Microsoft 365 calendar". The agent knows which connectors
|
|
1733
|
+
are loaded for this client and whether each one is ready or still needs the one-off authentication
|
|
1734
|
+
below, so it will reach for the connector rather than telling you it has no such integration. If a
|
|
1735
|
+
connector you added is not yet authenticated for this client, the agent offers you the authentication
|
|
1736
|
+
step straight away instead of dead-ending.
|
|
1737
|
+
|
|
1738
|
+
## Why a connector can still need authentication
|
|
1739
|
+
|
|
1740
|
+
A claude.ai connector is authorised per client, not per account. Approving a connector in the
|
|
1741
|
+
claude.ai web app authorises the web app only. Your Maxy admin chat is a separate client, so the first
|
|
1742
|
+
time it uses a connector that client has to be authorised too. This is a one-off step per connector.
|
|
1743
|
+
|
|
1744
|
+
That is why some connectors work straight away while a newly added one does not. A connector you used
|
|
1745
|
+
before was already authorised for this client; a brand new one has only been approved in the web app.
|
|
1746
|
+
|
|
1747
|
+
## How to authenticate one from the chat
|
|
1748
|
+
|
|
1749
|
+
When the agent tries a connector that has not been authorised for this client, it will tell you the
|
|
1750
|
+
connector needs authentication and hand you a link. You do not need a terminal and you do not need to
|
|
1751
|
+
run anything yourself.
|
|
1752
|
+
|
|
1753
|
+
1. The agent gives you an authorisation link.
|
|
1754
|
+
2. Open the link and approve the connector on claude.ai (sign in with the same account you use for
|
|
1755
|
+
your connectors).
|
|
1756
|
+
3. Tell the agent you have approved it.
|
|
1757
|
+
4. The agent reconnects your chat session. Send your next message and the connector is ready to use.
|
|
1758
|
+
|
|
1759
|
+
If a connector still reports that it needs authentication after you have approved it and the session
|
|
1760
|
+
has reconnected, that is unexpected. Tell your installer so they can look into it.
|
|
1761
|
+
|
|
1762
|
+
## What this is not
|
|
1763
|
+
|
|
1764
|
+
This is about Anthropic's claude.ai connectors, which authenticate per client. It is separate from the
|
|
1765
|
+
Maxy connector plugin, which registers ordinary REST APIs with credentials Maxy holds. The two are
|
|
1766
|
+
unrelated.
|
|
1767
|
+
|
|
1712
1768
|
---
|
|
1713
1769
|
# Telegram
|
|
1714
1770
|
Source: https://docs.getmaxy.com/telegram-guide.md
|
|
@@ -2466,6 +2522,8 @@ either is a regression.
|
|
|
2466
2522
|
|
|
2467
2523
|
**`/chat` empty-state greeting panel.** While the admin webchat has no conversation JSONL yet (`GET /api/webchat/session` → `projectDir:null`), `app/chat/page.tsx` renders a "What's up next, {givenName}?" greeting (given name = first whitespace token of the admin session's `userName`) above two read-only lists from `GET /api/webchat/greeting` (`server/routes/webchat-greeting.ts`, `requireAdminSession`-gated, `accountId`-scoped): the next 5 upcoming `:Event` rows (`eventStatus IN ['scheduled','due']`, future `COALESCE(nextRun, startDate)`, soonest first) and the outstanding `:Task` rows (`status IN ['pending','active','running']`, 5 shown + `+N more`). Read failure returns HTTP 200 `{ ok: false }` and the page shows the headline alone — the panel never blocks the composer. A third section, **Your specialists**, shows the account's installed roster — every `*.md` under `data/accounts/<accountId>/specialists/agents/`, core and `--`-prefixed premium files alike — shown by frontmatter `name` + `summary` (fallback `description`), empty dir → "No specialists installed". The roster read (`app/lib/claude-agent/specialist-roster.ts`) is isolated from the graph fallback: malformed files are skipped (`op=specialist-skipped file=<name> reason=<…>`), a whole-roster failure degrades to `specialists: []` inside the `ok: true` payload (`op=specialists-read-failed`). Logs: `[webchat:greeting] op=read account=<id8> events=<n> tasks=<n> specialists=<n> ms=<n>` / `op=read-failed reason=…`; client warns `[admin-ui] greeting-unavailable …`. The full webchat architecture lives in `.docs/admin-webchat-native-channel.md`.
|
|
2468
2524
|
|
|
2525
|
+
**`/chat` Conversations flyout + zero-sessions splash.** The admin `/chat` `HeaderMenu` carries a **Conversations** item (rendered only when `onOpenConversations` is wired, which scopes it to `/chat`) that opens an in-chat session-management pane (`app/chat/SessionList.tsx`) hosted by `chat/page.tsx` — distinct from, and sharing endpoints with, the `Sidebar` Sessions list (which stays hidden on narrow viewports). It enumerates the admin's own webchat sessions via `GET /api/admin/sidebar-sessions` (carries the per-row `live` marker and `archived` flag, install-wide), lists them newest-first with the live one marked and archived rows folded under a collapsible subsection, and offers per-row resume (`/chat?session=<id>`), rename, archive/unarchive, two-tap delete, an **End** control on the live row (`session-stop`), plus a New-session control (`session-rc-spawn`) and a copyable full id. When the canonical pointer is `known:false` (`canonical-empty`) and enumeration returns zero rows, the surface renders a splash (brand logo + New session) instead of a dead bootstrap thread; a freshly-spawned New session is `known:true`, so its greeting is preserved. Client breadcrumbs: `[chat-conversations] op=enumerate owned=<n>` and `op=action name=<open|rename|archive|delete|stop> sessionId=<id8>` (`op=action-failed … status=…` on a non-2xx). Detail lives in `.docs/admin-webchat-native-channel.md` ("Conversations flyout + zero-sessions splash").
|
|
2526
|
+
|
|
2469
2527
|
**`/chat` Claude-desktop transcript presentation.** The admin webchat keeps the shared `Transcript` shell (stream, follow-tail) but injects a /chat-only item renderer via the component's optional `renderItems` prop: `renderChatTimeline` in `app/chat/transcript-render.tsx`. Presentation: operator turns render as a right-aligned grey bubble showing the message text and, beneath it, a subtle always-visible time-of-day stamp (no label); delivered agent replies render as plain prose with the same time-of-day stamp beneath (the reply-document filename line stays, as prose); the stamp is HH:MM from the turn's `ts` (locale-formatted, right-aligned inside the operator bubble, left-aligned under agent prose), and a turn whose `ts` is null/unparseable shows no stamp, never an empty line — so only delivered operator and agent-reply turns are stamped, while tool runs, the collapsed "Thinking" block, and the agent-error banner stay time-free; a maximal consecutive run of `tool-call`/`tool-result` turns renders as one collapsed grey one-liner ("Used N tools ›") whose expansion shows every call and result payload — a lone call still gets the one-liner, and prose or a directive row ends a run. The WhatsApp reader (`/whatsapp`) omits the prop and keeps the default `renderTimeline`, so its rendering is unchanged; both presentations are test-pinned (`app/whatsapp/__tests__/Transcript-*.test.tsx`, `app/chat/__tests__/transcript-render.test.tsx`). **Day-divider:** both renderers insert a centered `.day-divider` row between two consecutive timeline items on different local calendar days (label `Today`/`Yesterday`, else `Sat 14 Jun 2026`), so a thread spanning midnight is never an ambiguous run of HH:MM; the first dated item gets a leading divider, a null/unparseable `ts` marks no boundary, dividers are computed over the filtered `visibleItems`, and in `/chat` a day crossover flushes any open tool/think run first so a collapsed run never spans it. Per-bubble HH:MM stamps are unchanged. Shared helpers `dayKey`/`dayLabel`/`itemTs`/`DayDivider` live in `app/whatsapp/Transcript.tsx`.
|
|
2470
2528
|
|
|
2471
2529
|
**`/chat` live activity line.** While a turn is in flight the transcript tail shows one ephemeral `ChatActivity` line (mounted only while `busy`, admin/operator only). It tracks real activity, not a timer: while a `Task` subagent runs it shows that subagent's headline (`agentType · description`, prefixed `N agents ·` when ≥2 are concurrent), sourced from `agent-<hex>.meta.json` via named `activity` SSE events the admin reader pushes from the session's `subagents/` dir; with no subagent active it shows a neutral word that advances only on a real turn arrival. The line carries a turn-elapsed clock and flips to a `stalled` state once nothing has been written for 5 minutes (`now − lastEmitAt`), so the operator can tell a wedge from progress without SSH. It is never added to the persisted timeline. Detail and the `[webchat-activity]` observability live in [`admin-webchat-native-channel.md`](../../../.docs/admin-webchat-native-channel.md).
|
|
@@ -4179,6 +4237,8 @@ tail -200 ~/.maxy/logs/maxy-ui.log | rg '\[remote-auth\].*resolvedKind='
|
|
|
4179
4237
|
|
|
4180
4238
|
**If a background task goes silent and the chat shows "A background task went silent — K of M completed":** Maxy's subagent stopped emitting progress for over 2 minutes. Tap "Continue" — the next turn resumes the prior session and reads a synthetic tool_result describing what completed before the pause, so the agent re-plans without losing the work it had done. Most stalls are upstream API latency rather than the subagent's approach failing — the resume-first path treats both correctly. Greppable post-deploy invariants: `[stall-recovery] kind=subagent_stalled … completed=<K>/? handoff=resume-first` followed by `[stall-resume] consumed kind=subagent_stalled toolUseId=<8>` on the next turn. If the button reads "Start over" instead, the parent's pending tool_use_id was not captured — the fallback path took over; the prior conversation is preserved as a `<recovery-context>` block in the cold-started session.
|
|
4181
4239
|
|
|
4240
|
+
**The stop button is stuck and the spinner never clears.** If the chat shows a stop button and a working spinner that never resolves, and the session dot is grey (not live), the agent process for that session was killed mid-turn (most often the device ran low on memory). There is no live turn to interrupt, so the old behaviour left you stranded. Now it self-heals: within a few seconds the spinner clears on its own and the composer shows "The agent stopped. Send a message to resume." Pressing stop clears it immediately. Either way, just send your next message — the session resumes from where it left off. Greppable post-deploy invariants: the manager logs `[rc-life] op=turn-aborted reason=child-exit-mid-turn` on the death and `[webchat-interrupt] op=dead-child-recovered` if you press stop; the browser console logs `[admin-ui] op=phantom-turn-cleared` (self-heal) or `[admin-ui] op=stop-dead-child` (stop press). If a session keeps dying mid-turn, the device is memory-constrained — that is a capacity concern, separate from this recovery.
|
|
4241
|
+
|
|
4182
4242
|
**Agent searches the filesystem after uploading a zip.** If you uploaded a zip and the agent burns several turns running `find` / `Glob` instead of unzipping, that is the symptom of the recovery-retry attachment-context regression (now closed by the recovery context preservation contract in `.docs/agents.md`). Greppable confirmation is the `[context-overflow-recovery] retry … attachmentsCarried=<n>` line in the conversation stream log. If you see `[context-overflow-recovery] WARN attachment-context-lost`, the regression has returned — surface to support.
|
|
4183
4243
|
|
|
4184
4244
|
**Turn budget exhausted with a horizontal rule separating two assistant turns.** When Maxy reaches its turn budget and the doubled retry also runs out, the chat now shows a one-paragraph assistant message that opens with `error_max_turns turns=A→B` (initial budget → final budget) followed by the recovery copy: "I reached my turn budget of N before I could finish this request. Try sending a smaller or more focused request, or ask me to use higher effort." That message is persisted to the graph, so the next page-refresh still shows it. The thin horizontal rule labelled "Session restored after timeout." that appears above your following turn signals that the prior turn forced a cold SDK-session restart inside the same conversation (pool eviction) — the agent's response after the rule is from a fresh SDK session even though the conversation thread is unchanged. Greppable post-deploy invariants: `[context-overflow-recovery] exhausted cause=max-turns-interrupted` count equals `[admin-persist] writer=persistMessageExhaust outcome=ok` count for the same sessionId window, and one `[session-store] storeAgentSessionId` line marks the cold-restart that drove the on-screen rule.
|
|
@@ -25,7 +25,7 @@ Each installation has its own Cloudflare account. Two auth paths serve two opera
|
|
|
25
25
|
|
|
26
26
|
## Operator-facing surface
|
|
27
27
|
|
|
28
|
-
The plugin registers no agent-facing MCP tools. Every Cloudflare operation is the agent invoking `cloudflared` or `wrangler` directly via Bash, or calling the Cloudflare API with a reused per-scope narrow token, with stdout/stderr streamed verbatim into the PTY chat (secrets redacted). The OAuth URL printed by `cloudflared tunnel login` is linkified by the native PTY; the operator clicks it in their own browser.
|
|
28
|
+
The plugin registers no agent-facing MCP tools. Every Cloudflare operation is the agent invoking `cloudflared` or `wrangler` directly via Bash, or calling the Cloudflare API with a reused per-scope narrow token, with stdout/stderr streamed verbatim into the PTY chat (secrets redacted). The OAuth URL printed by `cloudflared tunnel login` is linkified by the native PTY; the operator clicks it in their own browser. The one shell helper is [bin/cf-token.sh](bin/cf-token.sh), a deterministic per-scope token resolver that faithfully relays the Cloudflare token API (it invents no flags and drives no dashboard); there is no wrapper around `cloudflared` or `wrangler`, no orchestrator state machine, no MCP-tool surface.
|
|
29
29
|
|
|
30
30
|
### Skills
|
|
31
31
|
|
|
@@ -48,6 +48,12 @@ The plugin registers no agent-facing MCP tools. Every Cloudflare operation is th
|
|
|
48
48
|
|
|
49
49
|
The agent loads these references on demand via `plugin-read` as the conversation requires. They are not auto-injected into the system prompt.
|
|
50
50
|
|
|
51
|
+
### Helper
|
|
52
|
+
|
|
53
|
+
| Helper | Purpose |
|
|
54
|
+
|---|---|
|
|
55
|
+
| [bin/cf-token.sh](bin/cf-token.sh) | Deterministic per-scope token resolver. `bash bin/cf-token.sh <scope> <secrets-file>` (scope: `pages`, `d1`, `dns`, `access`) reads the master's `cfat_`/`cfut_` prefix, routes verify/`permission_groups`/mint to the matching endpoint family, reuses a persisted token or mints once if absent (no expiry), verifies through the transient post-mint window, persists to the secrets file, and prints only the secrets key name on stdout (never a token value). It removes the endpoint-routing decision from the agent, which is what the false-negative `Invalid API Token` / `9109` failures turned on. curl + jq only; exit-code gated. |
|
|
56
|
+
|
|
51
57
|
### Success contract
|
|
52
58
|
|
|
53
59
|
The setup-done claim only fires when `curl -I https://<hostname>` issued from outside the local network returns `HTTP/2 200` (or `HTTP/1.1 200 OK`) and the response is surfaced verbatim in chat. No state file, no service-active claim, no `cloudflared` exit code substitutes for the live HTTP response. When the curl returns anything else, the agent diagnoses with `cloudflared tunnel info <tunnelId>` and `systemctl --user status cloudflared-${BRAND}.service`, cites the relevant step in `references/reset-guide.md`, and stops.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Regression test for cf-token.sh. Stubs curl via a PATH shim backed by a state
|
|
3
|
+
# file so mint -> verify sequencing and the post-mint 10000 window are simulated
|
|
4
|
+
# offline. No live Cloudflare account is touched.
|
|
5
|
+
#
|
|
6
|
+
# Covers:
|
|
7
|
+
# 1. cfat_ master mints pages-d1, routes verify/perm/mint to /accounts/...
|
|
8
|
+
# 2. cfut_ master routes to /user/... and never to /accounts/...
|
|
9
|
+
# 3. an already-persisted canonical key is reused with no mint call
|
|
10
|
+
# 4. a legacy key (CF_PAGES_TOKEN) is reused and reported as the used key
|
|
11
|
+
# 5. the post-mint 10000 window is retried, then verify passes
|
|
12
|
+
# 6. access poison duplicate-id: first id 10000 on functional read -> alt id
|
|
13
|
+
# 7. an unrecognised master prefix fails closed
|
|
14
|
+
# 8. a real failure (non-transient verify code) exits non-zero, code surfaced
|
|
15
|
+
# 9/10. no token value ever reaches stdout/stderr; stdout is exactly the key
|
|
16
|
+
set -u
|
|
17
|
+
|
|
18
|
+
HELPER="$(cd "$(dirname "$0")/.." && pwd)/cf-token.sh"
|
|
19
|
+
[ -x "$HELPER" ] || { echo "FAIL: $HELPER not executable" >&2; exit 1; }
|
|
20
|
+
|
|
21
|
+
PASS=0; FAIL=0
|
|
22
|
+
SECRET_VALUE="cfat_minted_SECRET_do_not_leak_0123456789" # fake minted token value
|
|
23
|
+
|
|
24
|
+
ok() { echo "PASS: $1"; PASS=$((PASS+1)); }
|
|
25
|
+
bad() { echo "FAIL: $1" >&2; FAIL=$((FAIL+1)); }
|
|
26
|
+
|
|
27
|
+
# Build a scratch sandbox: a fake-curl dir on PATH + an account-style secrets file.
|
|
28
|
+
# $1 = master token value (its prefix selects cfat_/cfut_ routing).
|
|
29
|
+
setup() {
|
|
30
|
+
SANDBOX=$(mktemp -d)
|
|
31
|
+
BIN="$SANDBOX/bin"; mkdir -p "$BIN"
|
|
32
|
+
SDIR="$SANDBOX/acme-code/data/accounts/acc123/secrets"; mkdir -p "$SDIR"
|
|
33
|
+
SECRETS="$SDIR/cloudflare.env"
|
|
34
|
+
( umask 077; printf 'CLOUDFLARE_API_TOKEN=%s\nCLOUDFLARE_ACCOUNT_ID=%s\n' "$1" "acc123" > "$SECRETS" )
|
|
35
|
+
: > "$SANDBOX/curl.log"
|
|
36
|
+
install_default_curl
|
|
37
|
+
PATH="$BIN:$PATH"; export PATH
|
|
38
|
+
}
|
|
39
|
+
teardown() { rm -rf "$SANDBOX"; }
|
|
40
|
+
|
|
41
|
+
# Default fake curl: a healthy account with all permission groups; mint -> value;
|
|
42
|
+
# verify -> active; functional access read -> ok.
|
|
43
|
+
install_default_curl() {
|
|
44
|
+
cat > "$BIN/curl" <<CURL
|
|
45
|
+
#!/usr/bin/env bash
|
|
46
|
+
SB="\$(cd "\$(dirname "\$0")/.." && pwd)"
|
|
47
|
+
echo "\$@" >> "\$SB/curl.log"
|
|
48
|
+
url=""; for a in "\$@"; do case "\$a" in https://*) url="\$a";; esac; done
|
|
49
|
+
case "\$url" in
|
|
50
|
+
*"/tokens/permission_groups"*)
|
|
51
|
+
echo '{"success":true,"result":[{"id":"pg_pages","name":"Cloudflare Pages Write"},{"id":"pg_d1","name":"D1 Write"},{"id":"pg_dns","name":"DNS Write"},{"id":"pg_acc1","name":"Access: Apps and Policies Write"},{"id":"pg_acc2","name":"Access: Apps and Policies Write"}]}'
|
|
52
|
+
;;
|
|
53
|
+
*"/tokens/verify"*) echo '{"success":true,"result":{"status":"active"}}';;
|
|
54
|
+
*"/tokens"*) echo '{"success":true,"result":{"id":"tok1","value":"'"$SECRET_VALUE"'"}}';;
|
|
55
|
+
*"/access/apps"*) echo '{"success":true,"result":[]}';;
|
|
56
|
+
*) echo '{"success":false,"errors":[{"code":9999,"message":"unmapped"}]}';;
|
|
57
|
+
esac
|
|
58
|
+
CURL
|
|
59
|
+
chmod +x "$BIN/curl"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Run the helper. $1 name $2 scope $3 expected_exit. Sets OUT/ERR/RC.
|
|
63
|
+
run() {
|
|
64
|
+
local name="$1" scope="$2" exp="$3" of ef
|
|
65
|
+
of=$(mktemp); ef=$(mktemp)
|
|
66
|
+
CF_BACKOFF_SECONDS=0 bash "$HELPER" "$scope" "$SECRETS" >"$of" 2>"$ef"
|
|
67
|
+
RC=$?
|
|
68
|
+
OUT=$(cat "$of"); ERR=$(cat "$ef"); rm -f "$of" "$ef"
|
|
69
|
+
[ "$RC" -eq "$exp" ] && ok "$name (exit=$RC)" || { bad "$name (exit=$RC want $exp)"; echo " stderr: $ERR" >&2; }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
no_leak() { # assert no fake token value appears in OUT or ERR
|
|
73
|
+
if printf '%s%s' "$OUT" "$ERR" | grep -qE "SECRET|BADtoken|GOODtoken"; then bad "$1 leaked a token value"
|
|
74
|
+
else ok "$1 no token leak"; fi
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# 1. cfat_ master mints pages-d1, routes to /accounts/...
|
|
79
|
+
setup "cfat_master_abc"
|
|
80
|
+
run "cfat pages mint" pages 0
|
|
81
|
+
grep -q "/accounts/acc123/tokens" "$SANDBOX/curl.log" && ok "cfat routes accounts" || bad "cfat routing"
|
|
82
|
+
[ "$OUT" = "CF_PAGES_D1_TOKEN" ] && ok "stdout is canonical key" || bad "stdout='$OUT'"
|
|
83
|
+
grep -q "^CF_PAGES_D1_TOKEN=" "$SECRETS" && ok "persisted canonical key" || bad "not persisted"
|
|
84
|
+
echo "$ERR" | grep -q "op=resolve scope=pages master=cfat action=mint result=active code=0" && ok "mint lifeline verbatim" || bad "lifeline: $ERR"
|
|
85
|
+
no_leak "cfat mint"
|
|
86
|
+
teardown
|
|
87
|
+
|
|
88
|
+
# 2. cfut_ master routes to /user/... only
|
|
89
|
+
setup "cfut_master_xyz"
|
|
90
|
+
run "cfut pages mint" pages 0
|
|
91
|
+
grep -q "/user/tokens" "$SANDBOX/curl.log" && ok "cfut routes user" || bad "cfut routing"
|
|
92
|
+
grep -q "/accounts/acc123/tokens" "$SANDBOX/curl.log" && bad "cfut hit account endpoint" || ok "no account route for cfut"
|
|
93
|
+
echo "$ERR" | grep -q "master=cfut" && ok "cfut lifeline tag" || bad "lifeline tag: $ERR"
|
|
94
|
+
teardown
|
|
95
|
+
|
|
96
|
+
# 3. reuse: pre-persist canonical key -> no mint POST
|
|
97
|
+
setup "cfat_master_abc"
|
|
98
|
+
( umask 077; printf 'CF_PAGES_D1_TOKEN=%s\n' "cfat_already_here" >> "$SECRETS" )
|
|
99
|
+
run "reuse no mint" pages 0
|
|
100
|
+
grep -q -- "-X POST" "$SANDBOX/curl.log" && bad "reuse path minted" || ok "reuse no mint call"
|
|
101
|
+
echo "$ERR" | grep -q "action=reuse result=active" && ok "reuse lifeline" || bad "reuse lifeline: $ERR"
|
|
102
|
+
[ "$OUT" = "CF_PAGES_D1_TOKEN" ] && ok "reuse reports canonical key" || bad "reuse out='$OUT'"
|
|
103
|
+
teardown
|
|
104
|
+
|
|
105
|
+
# 4. legacy key reuse: CF_PAGES_TOKEN present -> reused, reported as legacy key
|
|
106
|
+
setup "cfat_master_abc"
|
|
107
|
+
( umask 077; printf 'CF_PAGES_TOKEN=%s\n' "cfat_legacy_here" >> "$SECRETS" )
|
|
108
|
+
run "legacy reuse" pages 0
|
|
109
|
+
grep -q -- "-X POST" "$SANDBOX/curl.log" && bad "legacy reuse minted" || ok "legacy reuse no mint"
|
|
110
|
+
[ "$OUT" = "CF_PAGES_TOKEN" ] && ok "reports legacy key" || bad "legacy out='$OUT'"
|
|
111
|
+
teardown
|
|
112
|
+
|
|
113
|
+
# 5. post-mint 10000 window then active
|
|
114
|
+
setup "cfat_master_abc"
|
|
115
|
+
cat > "$BIN/curl" <<'CURL'
|
|
116
|
+
#!/usr/bin/env bash
|
|
117
|
+
SB="$(cd "$(dirname "$0")/.." && pwd)"
|
|
118
|
+
echo "$@" >> "$SB/curl.log"
|
|
119
|
+
url=""; for a in "$@"; do case "$a" in https://*) url="$a";; esac; done
|
|
120
|
+
case "$url" in
|
|
121
|
+
*"/permission_groups"*) echo '{"success":true,"result":[{"id":"pg_pages","name":"Pages Write"},{"id":"pg_d1","name":"D1 Write"}]}';;
|
|
122
|
+
*"/tokens/verify"*)
|
|
123
|
+
n=$(cat "$SB/vcount" 2>/dev/null || echo 0); n=$((n+1)); echo "$n" > "$SB/vcount"
|
|
124
|
+
if [ "$n" -ge 3 ]; then echo '{"success":true,"result":{"status":"active"}}'
|
|
125
|
+
else echo '{"success":false,"errors":[{"code":10000,"message":"Authentication error"}]}'; fi;;
|
|
126
|
+
*"/tokens"*) echo '{"success":true,"result":{"value":"cfat_minted_SECRET_do_not_leak_0123456789"}}';;
|
|
127
|
+
*) echo '{"success":false,"errors":[{"code":9999}]}';;
|
|
128
|
+
esac
|
|
129
|
+
CURL
|
|
130
|
+
chmod +x "$BIN/curl"; : > "$SANDBOX/vcount"
|
|
131
|
+
run "10000 retry then active" pages 0
|
|
132
|
+
no_leak "10000 retry"
|
|
133
|
+
teardown
|
|
134
|
+
|
|
135
|
+
# 6. access poison id: first candidate's functional read returns 10000, second is fine
|
|
136
|
+
setup "cfat_master_abc"
|
|
137
|
+
cat > "$BIN/curl" <<'CURL'
|
|
138
|
+
#!/usr/bin/env bash
|
|
139
|
+
SB="$(cd "$(dirname "$0")/.." && pwd)"
|
|
140
|
+
echo "$@" >> "$SB/curl.log"
|
|
141
|
+
url=""; data=""; hdr=""; prev=""
|
|
142
|
+
for a in "$@"; do
|
|
143
|
+
case "$a" in https://*) url="$a";; esac
|
|
144
|
+
[ "$prev" = "--data" ] && data="$a"
|
|
145
|
+
[ "$prev" = "-H" ] && hdr="$hdr $a"
|
|
146
|
+
prev="$a"
|
|
147
|
+
done
|
|
148
|
+
case "$url" in
|
|
149
|
+
*"/permission_groups"*) echo '{"success":true,"result":[{"id":"acc_bad","name":"Access: Apps and Policies Write"},{"id":"acc_good","name":"Access: Apps and Policies Write"}]}';;
|
|
150
|
+
*"/tokens/verify"*) echo '{"success":true,"result":{"status":"active"}}';;
|
|
151
|
+
*"/tokens"*)
|
|
152
|
+
case "$data" in
|
|
153
|
+
*acc_bad*) echo '{"success":true,"result":{"value":"cfat_BADtoken_0"}}';;
|
|
154
|
+
*acc_good*) echo '{"success":true,"result":{"value":"cfat_GOODtoken_1"}}';;
|
|
155
|
+
*) echo '{"success":true,"result":{"value":"cfat_x"}}';; esac;;
|
|
156
|
+
*"/access/apps"*)
|
|
157
|
+
case "$hdr" in *BADtoken*) echo '{"success":false,"errors":[{"code":10000}]}';;
|
|
158
|
+
*) echo '{"success":true,"result":[]}';; esac;;
|
|
159
|
+
*) echo '{"success":false,"errors":[{"code":9999}]}';;
|
|
160
|
+
esac
|
|
161
|
+
CURL
|
|
162
|
+
chmod +x "$BIN/curl"
|
|
163
|
+
run "access poison retried to good id" access 0
|
|
164
|
+
grep -q "acc_good" "$SANDBOX/curl.log" && ok "minted with good id" || bad "never tried good id"
|
|
165
|
+
no_leak "access poison"
|
|
166
|
+
teardown
|
|
167
|
+
|
|
168
|
+
# 7. unknown prefix -> fail closed
|
|
169
|
+
setup "v1.0-legacyapikeystyle"
|
|
170
|
+
run "unknown prefix fail closed" pages 1
|
|
171
|
+
grep -q -- "-X POST" "$SANDBOX/curl.log" && bad "unknown prefix still minted" || ok "unknown prefix no mint"
|
|
172
|
+
teardown
|
|
173
|
+
|
|
174
|
+
# 8. real failure: verify returns a non-transient code -> non-zero, surfaced, masked
|
|
175
|
+
setup "cfat_master_abc"
|
|
176
|
+
cat > "$BIN/curl" <<'CURL'
|
|
177
|
+
#!/usr/bin/env bash
|
|
178
|
+
SB="$(cd "$(dirname "$0")/.." && pwd)"
|
|
179
|
+
echo "$@" >> "$SB/curl.log"
|
|
180
|
+
url=""; for a in "$@"; do case "$a" in https://*) url="$a";; esac; done
|
|
181
|
+
case "$url" in
|
|
182
|
+
*"/permission_groups"*) echo '{"success":true,"result":[{"id":"pg_pages","name":"Pages Write"},{"id":"pg_d1","name":"D1 Write"}]}';;
|
|
183
|
+
*"/tokens/verify"*) echo '{"success":false,"errors":[{"code":1001,"message":"bad"}]}';;
|
|
184
|
+
*"/tokens"*) echo '{"success":true,"result":{"value":"cfat_minted_SECRET_do_not_leak_0123456789"}}';;
|
|
185
|
+
*) echo '{"success":false,"errors":[{"code":9999}]}';;
|
|
186
|
+
esac
|
|
187
|
+
CURL
|
|
188
|
+
chmod +x "$BIN/curl"
|
|
189
|
+
run "real failure non-zero" pages 1
|
|
190
|
+
echo "$ERR" | grep -q "code=1001" && ok "failure code surfaced" || bad "code not surfaced: $ERR"
|
|
191
|
+
no_leak "real failure"
|
|
192
|
+
teardown
|
|
193
|
+
|
|
194
|
+
echo "----"; echo "PASS=$PASS FAIL=$FAIL"
|
|
195
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# cf-token.sh - deterministic Cloudflare per-scope token resolver.
|
|
3
|
+
#
|
|
4
|
+
# Owns the cfat_/cfut_ prefix decision and the mint-or-reuse state machine that
|
|
5
|
+
# api.md "Token type - route endpoints by prefix" and "Provisioning and reusing
|
|
6
|
+
# a stable per-scope token" describe, so the agent never guesses a token
|
|
7
|
+
# endpoint. curl + jq only (the cloudflare skill binds API calls to curl/jq).
|
|
8
|
+
#
|
|
9
|
+
# Usage: cf-token.sh <scope> <secrets-file>
|
|
10
|
+
# scope in {pages, d1, dns, access}
|
|
11
|
+
# Output: stdout = the secrets key name holding the active token (never a value).
|
|
12
|
+
# stderr = a [cf-token] lifeline and, on failure, the endpoint + code.
|
|
13
|
+
# Exit: 0 when the scope token is active/reused and persisted; non-zero else.
|
|
14
|
+
set -u
|
|
15
|
+
|
|
16
|
+
API="https://api.cloudflare.com/client/v4"
|
|
17
|
+
BACKOFF="${CF_BACKOFF_SECONDS:-3}"
|
|
18
|
+
RETRIES="${CF_VERIFY_RETRIES:-10}"
|
|
19
|
+
|
|
20
|
+
die() { echo "[cf-token] error: $*" >&2; exit 1; }
|
|
21
|
+
|
|
22
|
+
scope="${1:-}"; secrets="${2:-}"
|
|
23
|
+
case "$scope" in pages|d1|dns|access) ;; *) die "unknown scope '${scope}'; use pages|d1|dns|access";; esac
|
|
24
|
+
[ -n "$secrets" ] && [ -r "$secrets" ] || die "secrets file not readable: '${secrets}'"
|
|
25
|
+
|
|
26
|
+
# Load credentials (api.md load-credentials pre-flight). :? aborts if unset/empty,
|
|
27
|
+
# so a credential-unloaded call can never reach the API.
|
|
28
|
+
set -a; . "$secrets"; set +a
|
|
29
|
+
: "${CLOUDFLARE_API_TOKEN:?credentials not loaded - source the secrets file before any call}"
|
|
30
|
+
: "${CLOUDFLARE_ACCOUNT_ID:?account id not loaded - source the secrets file before any call}"
|
|
31
|
+
ACC="$CLOUDFLARE_ACCOUNT_ID"
|
|
32
|
+
|
|
33
|
+
# Brand for the minted token name: CF_TOKEN_BRAND overrides; else the <brand>-code
|
|
34
|
+
# path segment of the secrets file; else fail closed (no guessed name).
|
|
35
|
+
brand="${CF_TOKEN_BRAND:-}"
|
|
36
|
+
if [ -z "$brand" ]; then
|
|
37
|
+
case "$secrets" in
|
|
38
|
+
*/*-code/*) brand="$(printf '%s\n' "$secrets" | tr '/' '\n' | grep -E -- '-code$' | head -1)";;
|
|
39
|
+
esac
|
|
40
|
+
fi
|
|
41
|
+
[ -n "$brand" ] || die "cannot derive brand from path; set CF_TOKEN_BRAND"
|
|
42
|
+
|
|
43
|
+
# Scope table - resolved by case, never inferred.
|
|
44
|
+
legacy=""
|
|
45
|
+
case "$scope" in
|
|
46
|
+
pages|d1) suffix="pages-d1"; key="CF_PAGES_D1_TOKEN"; legacy="CF_PAGES_TOKEN"
|
|
47
|
+
# per-group regexes separated by ';' (each may use '|' internally)
|
|
48
|
+
regexes="pages.*(write|edit);d1.*(write|edit)"; res='{"com.cloudflare.api.account.'"$ACC"'":"*"}';;
|
|
49
|
+
dns) suffix="dns"; key="CF_DNS_TOKEN"
|
|
50
|
+
regexes="dns.*(write|edit)"; res='{"com.cloudflare.api.account.zone.*":"*"}';;
|
|
51
|
+
access) suffix="access"; key="CF_ACCESS_TOKEN"
|
|
52
|
+
regexes="access.*apps.*polic.*(write|edit)"; res='{"com.cloudflare.api.account.'"$ACC"'":"*"}';;
|
|
53
|
+
esac
|
|
54
|
+
name="${brand}-${suffix}"
|
|
55
|
+
|
|
56
|
+
read_key() { grep -m1 "^$1=" "$secrets" 2>/dev/null | cut -d= -f2-; }
|
|
57
|
+
prefix_tag() { case "$CLOUDFLARE_API_TOKEN" in cfat_*) echo cfat;; cfut_*) echo cfut;; *) echo other;; esac; }
|
|
58
|
+
|
|
59
|
+
# --- Reuse path: a persisted token is already active (api.md). No verify, no mint.
|
|
60
|
+
existing="$(read_key "$key")"; used_key="$key"
|
|
61
|
+
if [ -z "$existing" ] && [ -n "$legacy" ]; then
|
|
62
|
+
existing="$(read_key "$legacy")"; [ -n "$existing" ] && used_key="$legacy"
|
|
63
|
+
fi
|
|
64
|
+
if [ -n "$existing" ]; then
|
|
65
|
+
echo "[cf-token] op=resolve scope=${scope} master=$(prefix_tag) action=reuse result=active code=0" >&2
|
|
66
|
+
printf '%s\n' "$used_key"; exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# --- Mint path: route strictly by master prefix. Any other prefix fails closed.
|
|
70
|
+
case "$CLOUDFLARE_API_TOKEN" in
|
|
71
|
+
cfat_*) base="$API/accounts/$ACC"; mtag=cfat;;
|
|
72
|
+
cfut_*) base="$API/user"; mtag=cfut;;
|
|
73
|
+
*) echo "[cf-token] op=resolve scope=${scope} master=other action=mint result=error code=0" >&2
|
|
74
|
+
die "unrecognised master token prefix (not cfat_/cfut_); refusing to guess an endpoint";;
|
|
75
|
+
esac
|
|
76
|
+
|
|
77
|
+
api_get() { curl -sS -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" "$1"; }
|
|
78
|
+
|
|
79
|
+
# Resolve permission-group id(s) by name regex (case-insensitive).
|
|
80
|
+
pg_json="$(api_get "${base}/tokens/permission_groups")"
|
|
81
|
+
echo "$pg_json" | jq -e '.success==true' >/dev/null 2>&1 || {
|
|
82
|
+
code=$(echo "$pg_json" | jq -r '.errors[0].code // "?"')
|
|
83
|
+
echo "[cf-token] op=resolve scope=${scope} master=${mtag} action=mint result=error code=${code}" >&2
|
|
84
|
+
die "permission_groups failed at ${base}/tokens/permission_groups code=${code}"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
collect_ids() { echo "$pg_json" | jq -r --arg re "$1" '.result[] | select(.name|test($re;"i")) | .id'; }
|
|
88
|
+
|
|
89
|
+
mint_with() { # $1 = json permission_groups array -> echoes minted token value or empty
|
|
90
|
+
local body
|
|
91
|
+
body=$(jq -cn --arg n "$name" --argjson res "$res" --argjson pg "$1" \
|
|
92
|
+
'{name:$n, policies:[{effect:"allow", resources:$res, permission_groups:$pg}]}')
|
|
93
|
+
curl -sS -X POST -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
|
|
94
|
+
-H "Content-Type: application/json" "${base}/tokens" --data "$body" \
|
|
95
|
+
| jq -r '.result.value // empty'
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
verify_token() { # $1 token -> echoes "active" or the failing code; retries transient 10000
|
|
99
|
+
local i s
|
|
100
|
+
s=""
|
|
101
|
+
for i in $(seq 1 "$RETRIES"); do
|
|
102
|
+
s=$(curl -sS -H "Authorization: Bearer $1" "${base}/tokens/verify" \
|
|
103
|
+
| jq -r '.result.status // (.errors[0].code | tostring) // "error"')
|
|
104
|
+
[ "$s" = "active" ] && { echo active; return 0; }
|
|
105
|
+
[ "$s" = "10000" ] && { sleep "$BACKOFF"; continue; }
|
|
106
|
+
echo "$s"; return 1
|
|
107
|
+
done
|
|
108
|
+
echo "$s"; return 1
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
finish() { # $1 token value -> persist under canonical key, report, exit 0
|
|
112
|
+
( umask 077; printf '%s=%s\n' "$key" "$1" >> "$secrets" )
|
|
113
|
+
echo "[cf-token] op=resolve scope=${scope} master=${mtag} action=mint result=active code=0" >&2
|
|
114
|
+
printf '%s\n' "$key"; exit 0
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if [ "$scope" = "access" ]; then
|
|
118
|
+
# The "Access: Apps and Policies Write" group can resolve to more than one id;
|
|
119
|
+
# the wrong one yields a token that returns 10000 on every call, reads included.
|
|
120
|
+
# Try each candidate, mint+verify, then a functional read; keep the one that works.
|
|
121
|
+
candidates="$(collect_ids "$regexes")"
|
|
122
|
+
[ -n "$candidates" ] || {
|
|
123
|
+
echo "[cf-token] op=resolve scope=access master=${mtag} action=mint result=error code=0" >&2
|
|
124
|
+
die "no permission group matched /${regexes}/"; }
|
|
125
|
+
for cid in $candidates; do
|
|
126
|
+
tok=$(mint_with "$(jq -cn --arg id "$cid" '[{id:$id}]')")
|
|
127
|
+
[ -n "$tok" ] || continue
|
|
128
|
+
st=$(verify_token "$tok"); [ "$st" = "active" ] || continue
|
|
129
|
+
fn=$(curl -sS -H "Authorization: Bearer $tok" "${base}/access/apps" \
|
|
130
|
+
| jq -r '.success // (.errors[0].code|tostring)')
|
|
131
|
+
[ "$fn" = "true" ] && finish "$tok"
|
|
132
|
+
# else poison id - try the next candidate
|
|
133
|
+
done
|
|
134
|
+
echo "[cf-token] op=resolve scope=access master=${mtag} action=mint result=error code=10000" >&2
|
|
135
|
+
die "every Access permission-group candidate returned 10000 on a functional read (poison id)"
|
|
136
|
+
else
|
|
137
|
+
pg_array='[]'
|
|
138
|
+
OLDIFS=$IFS; IFS=';'
|
|
139
|
+
for rx in $regexes; do
|
|
140
|
+
id=$(collect_ids "$rx" | head -1)
|
|
141
|
+
[ -n "$id" ] || {
|
|
142
|
+
IFS=$OLDIFS
|
|
143
|
+
echo "[cf-token] op=resolve scope=${scope} master=${mtag} action=mint result=error code=0" >&2
|
|
144
|
+
die "no permission group matched /${rx}/"; }
|
|
145
|
+
pg_array=$(echo "$pg_array" | jq -c --arg id "$id" '. + [{id:$id}]')
|
|
146
|
+
done
|
|
147
|
+
IFS=$OLDIFS
|
|
148
|
+
tok=$(mint_with "$pg_array")
|
|
149
|
+
[ -n "$tok" ] || {
|
|
150
|
+
echo "[cf-token] op=resolve scope=${scope} master=${mtag} action=mint result=error code=0" >&2
|
|
151
|
+
die "mint returned no token value at ${base}/tokens"; }
|
|
152
|
+
st=$(verify_token "$tok")
|
|
153
|
+
[ "$st" = "active" ] || {
|
|
154
|
+
echo "[cf-token] op=resolve scope=${scope} master=${mtag} action=mint result=error code=${st}" >&2
|
|
155
|
+
die "verify failed code=${st} at ${base}/tokens/verify"; }
|
|
156
|
+
finish "$tok"
|
|
157
|
+
fi
|