@rubytech/create-sitedesk-code 0.1.508 → 0.1.510
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +53 -0
- package/package.json +1 -1
- package/payload/platform/docs/superpowers/plans/2026-07-26-task-1902-account-owned-entry-declarations.md +555 -0
- package/payload/platform/docs/superpowers/plans/2026-07-26-task-1926-portal-push-concurrency-guard.md +482 -0
- package/payload/platform/docs/superpowers/plans/2026-07-26-task-2023-sidebar-sessions-async-reads.md +394 -0
- package/payload/platform/docs/superpowers/specs/2026-07-26-task-1902-account-owned-entry-declarations-design.md +232 -0
- package/payload/platform/docs/superpowers/specs/2026-07-26-task-1926-portal-push-concurrency-guard-design.md +200 -0
- package/payload/platform/docs/superpowers/specs/2026-07-26-task-2014-ui-suite-nondeterminism-design.md +169 -0
- package/payload/platform/plugins/admin/PLUGIN.md +1 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +12 -7
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
- package/payload/platform/plugins/business-assistant/PLUGIN.md +1 -0
- package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -0
- package/payload/platform/plugins/cloudflare/bin/portal-index-push.mjs +68 -28
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-push.test.ts +271 -29
- package/payload/platform/plugins/cloudflare/mcp/__tests__/schema-exposed-dirs.test.ts +21 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +5 -4
- package/payload/platform/plugins/docs/references/admin-ui.md +10 -5
- package/payload/platform/plugins/docs/references/troubleshooting.md +1 -1
- package/payload/platform/plugins/scheduling/PLUGIN.md +1 -0
- package/payload/platform/scripts/__tests__/account-schema-owned-dirs.test.sh +112 -4
- package/payload/platform/scripts/__tests__/check-brand-cache.test.sh +231 -0
- package/payload/platform/scripts/check-brand-cache.sh +138 -0
- package/payload/platform/scripts/lib/account-schema-owned-dirs.py +61 -32
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js +17 -30
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/config.d.ts +50 -4
- package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/config.js +58 -15
- package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
- package/payload/server/maxy-edge.js +26 -11
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/AdminLoginScreens-CWMpccrR.css.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-CWMpccrR.css.gz +0 -0
- package/payload/server/public/assets/AdminLoginScreens-bksN0XPe.js +1 -0
- package/payload/server/public/assets/AdminLoginScreens-bksN0XPe.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-bksN0XPe.js.gz +0 -0
- package/payload/server/public/assets/{AdminShell-HeELCCjJ.js → AdminShell-BMPEWNZk.js} +1 -1
- package/payload/server/public/assets/AdminShell-BMPEWNZk.js.br +0 -0
- package/payload/server/public/assets/AdminShell-BMPEWNZk.js.gz +0 -0
- package/payload/server/public/assets/_baseFor-Be3EKK3K.js.br +0 -0
- package/payload/server/public/assets/_baseFor-Be3EKK3K.js.gz +0 -0
- package/payload/server/public/assets/{activity-BEIDAxDA.js → activity-ByWYF5dC.js} +1 -1
- package/payload/server/public/assets/activity-ByWYF5dC.js.br +0 -0
- package/payload/server/public/assets/activity-ByWYF5dC.js.gz +0 -0
- package/payload/server/public/assets/admin-DGr1HuPg.js +1 -0
- package/payload/server/public/assets/admin-DGr1HuPg.js.br +0 -0
- package/payload/server/public/assets/admin-DGr1HuPg.js.gz +0 -0
- package/payload/server/public/assets/agents-C0rTfrYd.js +1 -0
- package/payload/server/public/assets/agents-C0rTfrYd.js.br +0 -0
- package/payload/server/public/assets/agents-C0rTfrYd.js.gz +0 -0
- package/payload/server/public/assets/arc-DaI5MLTq.js.br +0 -0
- package/payload/server/public/assets/arc-DaI5MLTq.js.gz +0 -0
- package/payload/server/public/assets/architecture-YZFGNWBL-DWZIX5aV.js.br +0 -0
- package/payload/server/public/assets/architectureDiagram-Q4EWVU46-CBGVUEN9.js.br +0 -0
- package/payload/server/public/assets/architectureDiagram-Q4EWVU46-CBGVUEN9.js.gz +0 -0
- package/payload/server/public/assets/array-X0JlPOfd.js.br +0 -0
- package/payload/server/public/assets/blockDiagram-DXYQGD6D-DS6dS4GZ.js.br +0 -0
- package/payload/server/public/assets/blockDiagram-DXYQGD6D-DS6dS4GZ.js.gz +0 -0
- package/payload/server/public/assets/{browser-GkVTGqlb.js → browser-4tTaem8N.js} +1 -1
- package/payload/server/public/assets/browser-4tTaem8N.js.br +0 -0
- package/payload/server/public/assets/browser-4tTaem8N.js.gz +0 -0
- package/payload/server/public/assets/c4Diagram-AHTNJAMY-hBSBUZPt.js.br +0 -0
- package/payload/server/public/assets/c4Diagram-AHTNJAMY-hBSBUZPt.js.gz +0 -0
- package/payload/server/public/assets/{calendar-CSqJeFEg.js → calendar-BEjnkKG1.js} +1 -1
- package/payload/server/public/assets/calendar-BEjnkKG1.js.br +0 -0
- package/payload/server/public/assets/calendar-BEjnkKG1.js.gz +0 -0
- package/payload/server/public/assets/channel-BEokjAeE.js.br +0 -0
- package/payload/server/public/assets/chat-DBgzjDIE.js +1 -0
- package/payload/server/public/assets/chat-DBgzjDIE.js.br +0 -0
- package/payload/server/public/assets/chat-DBgzjDIE.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-DqYv3oFh.js +1 -0
- package/payload/server/public/assets/chevron-right-CQfPGsFb.js +1 -0
- package/payload/server/public/assets/chevron-right-CQfPGsFb.js.br +0 -0
- package/payload/server/public/assets/chunk-2KRD3SAO-B6koUoB7.js.br +0 -0
- package/payload/server/public/assets/chunk-2KRD3SAO-B6koUoB7.js.gz +0 -0
- package/payload/server/public/assets/chunk-336JU56O-BF6BRh8u.js.br +0 -0
- package/payload/server/public/assets/chunk-336JU56O-BF6BRh8u.js.gz +0 -0
- package/payload/server/public/assets/chunk-426QAEUC-pbQ4PWBU.js.br +0 -0
- package/payload/server/public/assets/chunk-426QAEUC-pbQ4PWBU.js.gz +0 -0
- package/payload/server/public/assets/chunk-4BX2VUAB-7sO95xR2.js.br +0 -0
- package/payload/server/public/assets/chunk-4BX2VUAB-7sO95xR2.js.gz +0 -0
- package/payload/server/public/assets/chunk-4TB4RGXK-GCTndJYx.js.br +0 -0
- package/payload/server/public/assets/chunk-4TB4RGXK-GCTndJYx.js.gz +0 -0
- package/payload/server/public/assets/chunk-55IACEB6-ggIJf_TC.js.br +0 -0
- package/payload/server/public/assets/chunk-55IACEB6-ggIJf_TC.js.gz +0 -0
- package/payload/server/public/assets/chunk-5FUZZQ4R-DeGmkimt.js.br +0 -0
- package/payload/server/public/assets/chunk-5FUZZQ4R-DeGmkimt.js.gz +0 -0
- package/payload/server/public/assets/chunk-5PVQY5BW-aFIQKk_U.js.br +0 -0
- package/payload/server/public/assets/chunk-5PVQY5BW-aFIQKk_U.js.gz +0 -0
- package/payload/server/public/assets/chunk-67CJDMHE-LS8rUbii.js.br +2 -0
- package/payload/server/public/assets/chunk-67CJDMHE-LS8rUbii.js.gz +0 -0
- package/payload/server/public/assets/chunk-7N4EOEYR-JKOVngS0.js.br +2 -0
- package/payload/server/public/assets/chunk-7N4EOEYR-JKOVngS0.js.gz +0 -0
- package/payload/server/public/assets/chunk-AA7GKIK3-CoXnTVK6.js.br +0 -0
- package/payload/server/public/assets/chunk-AA7GKIK3-CoXnTVK6.js.gz +0 -0
- package/payload/server/public/assets/chunk-BSJP7CBP-CcsqObFg.js.br +0 -0
- package/payload/server/public/assets/chunk-BSJP7CBP-CcsqObFg.js.gz +0 -0
- package/payload/server/public/assets/chunk-CIAEETIT-CpsybR7q.js.br +0 -0
- package/payload/server/public/assets/chunk-CIAEETIT-CpsybR7q.js.gz +0 -0
- package/payload/server/public/assets/chunk-CltuBf4Z.js.br +0 -0
- package/payload/server/public/assets/chunk-CltuBf4Z.js.gz +0 -0
- package/payload/server/public/assets/chunk-EDXVE4YY-BZ2I9PSq.js.br +0 -0
- package/payload/server/public/assets/chunk-EDXVE4YY-BZ2I9PSq.js.gz +0 -0
- package/payload/server/public/assets/chunk-ENJZ2VHE-DbTwt6OZ.js.br +0 -0
- package/payload/server/public/assets/chunk-ENJZ2VHE-DbTwt6OZ.js.gz +0 -0
- package/payload/server/public/assets/chunk-FMBD7UC4-D27VnSCa.js.br +0 -0
- package/payload/server/public/assets/chunk-FMBD7UC4-D27VnSCa.js.gz +0 -0
- package/payload/server/public/assets/chunk-FOC6F5B3-C0tU2UFl.js.br +0 -0
- package/payload/server/public/assets/chunk-FOC6F5B3-C0tU2UFl.js.gz +0 -0
- package/payload/server/public/assets/chunk-ICPOFSXX-UPPu2OIq.js.br +0 -0
- package/payload/server/public/assets/chunk-ICPOFSXX-UPPu2OIq.js.gz +0 -0
- package/payload/server/public/assets/chunk-K5T4RW27-DIKxqyYw.js.br +0 -0
- package/payload/server/public/assets/chunk-K5T4RW27-DIKxqyYw.js.gz +0 -0
- package/payload/server/public/assets/chunk-KGLVRYIC-CiL4YVLo.js.br +0 -0
- package/payload/server/public/assets/chunk-KGLVRYIC-CiL4YVLo.js.gz +0 -0
- package/payload/server/public/assets/chunk-LIHQZDEY-ejKezVlD.js.br +0 -0
- package/payload/server/public/assets/chunk-LIHQZDEY-ejKezVlD.js.gz +0 -0
- package/payload/server/public/assets/chunk-ORNJ4GCN-Cc0vqc-D.js.br +0 -0
- package/payload/server/public/assets/chunk-ORNJ4GCN-Cc0vqc-D.js.gz +0 -0
- package/payload/server/public/assets/chunk-OYMX7WX6-Drxn1qG0.js.br +0 -0
- package/payload/server/public/assets/chunk-OYMX7WX6-Drxn1qG0.js.gz +0 -0
- package/payload/server/public/assets/chunk-QZHKN3VN-Dhlh7Pkk.js.br +0 -0
- package/payload/server/public/assets/chunk-QZHKN3VN-Dhlh7Pkk.js.gz +0 -0
- package/payload/server/public/assets/chunk-U2HBQHQK-DnbQIkRP.js.br +0 -0
- package/payload/server/public/assets/chunk-U2HBQHQK-DnbQIkRP.js.gz +0 -0
- package/payload/server/public/assets/chunk-X2U36JSP-CauTSVWg.js.br +0 -0
- package/payload/server/public/assets/chunk-X2U36JSP-CauTSVWg.js.gz +0 -0
- package/payload/server/public/assets/chunk-XPW4576I-DZqqB9Zi.js.br +0 -0
- package/payload/server/public/assets/chunk-XPW4576I-DZqqB9Zi.js.gz +0 -0
- package/payload/server/public/assets/chunk-YZCP3GAM-BGHo4Li-.js.br +0 -0
- package/payload/server/public/assets/chunk-YZCP3GAM-BGHo4Li-.js.gz +0 -0
- package/payload/server/public/assets/chunk-ZZ45TVLE-DWGJKPd7.js.br +0 -0
- package/payload/server/public/assets/chunk-ZZ45TVLE-DWGJKPd7.js.gz +0 -0
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-D2z_ZV1x.js.br +0 -0
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-D2z_ZV1x.js.gz +0 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BJwJoNEK.js.br +0 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BJwJoNEK.js.gz +0 -0
- package/payload/server/public/assets/clock-Dn6FHB51.js +1 -0
- package/payload/server/public/assets/clock-Dn6FHB51.js.br +0 -0
- package/payload/server/public/assets/clock-Dn6FHB51.js.gz +0 -0
- package/payload/server/public/assets/clone-Bn7XXVhr.js.br +0 -0
- package/payload/server/public/assets/cose-bilkent-S5V4N54A-D-95BzXL.js.br +0 -0
- package/payload/server/public/assets/cose-bilkent-S5V4N54A-D-95BzXL.js.gz +0 -0
- package/payload/server/public/assets/cytoscape.esm-CI2IWMLB.js.br +0 -0
- package/payload/server/public/assets/cytoscape.esm-CI2IWMLB.js.gz +0 -0
- package/payload/server/public/assets/dagre-BZYNBHvT.js.br +0 -0
- package/payload/server/public/assets/dagre-BZYNBHvT.js.gz +0 -0
- package/payload/server/public/assets/dagre-KV5264BT-DyU4HQLQ.js.br +0 -0
- package/payload/server/public/assets/dagre-KV5264BT-DyU4HQLQ.js.gz +0 -0
- package/payload/server/public/assets/data-D_e6Vdfd.js +1 -0
- package/payload/server/public/assets/data-D_e6Vdfd.js.br +0 -0
- package/payload/server/public/assets/data-D_e6Vdfd.js.gz +0 -0
- package/payload/server/public/assets/defaultLocale-B6RGN4id.js.br +0 -0
- package/payload/server/public/assets/defaultLocale-B6RGN4id.js.gz +0 -0
- package/payload/server/public/assets/diagram-5BDNPKRD-CCttJ9XA.js.br +0 -0
- package/payload/server/public/assets/diagram-5BDNPKRD-CCttJ9XA.js.gz +0 -0
- package/payload/server/public/assets/diagram-G4DWMVQ6-DvkpgSLO.js.br +0 -0
- package/payload/server/public/assets/diagram-G4DWMVQ6-DvkpgSLO.js.gz +0 -0
- package/payload/server/public/assets/diagram-MMDJMWI5-DHmW5Y9k.js.br +0 -0
- package/payload/server/public/assets/diagram-MMDJMWI5-DHmW5Y9k.js.gz +0 -0
- package/payload/server/public/assets/diagram-TYMM5635-C6T4hwzO.js.br +0 -0
- package/payload/server/public/assets/diagram-TYMM5635-C6T4hwzO.js.gz +0 -0
- package/payload/server/public/assets/dist-QvbmK1IP.js.br +0 -0
- package/payload/server/public/assets/dist-QvbmK1IP.js.gz +0 -0
- package/payload/server/public/assets/erDiagram-SMLLAGMA-5v_o7s3T.js.br +0 -0
- package/payload/server/public/assets/erDiagram-SMLLAGMA-5v_o7s3T.js.gz +0 -0
- package/payload/server/public/assets/{file-text-DiscM2pP.js → file-text-DMKw3nIk.js} +1 -1
- package/payload/server/public/assets/file-text-DMKw3nIk.js.br +0 -0
- package/payload/server/public/assets/file-text-DMKw3nIk.js.gz +0 -0
- package/payload/server/public/assets/flatten-BtIc9jX7.js.br +0 -0
- package/payload/server/public/assets/flatten-BtIc9jX7.js.gz +0 -0
- package/payload/server/public/assets/flowDiagram-DWJPFMVM-3UhCDsjt.js.br +0 -0
- package/payload/server/public/assets/flowDiagram-DWJPFMVM-3UhCDsjt.js.gz +0 -0
- package/payload/server/public/assets/ganttDiagram-T4ZO3ILL-F2zgHIIQ.js.br +0 -0
- package/payload/server/public/assets/ganttDiagram-T4ZO3ILL-F2zgHIIQ.js.gz +0 -0
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-C-FvNTHZ.js.br +0 -0
- package/payload/server/public/assets/gitGraphDiagram-UUTBAWPF-oQc1N3dT.js.br +0 -0
- package/payload/server/public/assets/gitGraphDiagram-UUTBAWPF-oQc1N3dT.js.gz +0 -0
- package/payload/server/public/assets/{graph-CRP5DM8C.js → graph-C3vW8lvw.js} +1 -1
- package/payload/server/public/assets/graph-C3vW8lvw.js.br +0 -0
- package/payload/server/public/assets/graph-C3vW8lvw.js.gz +0 -0
- package/payload/server/public/assets/{graph-labels-Du-6KoBE.js → graph-labels-BZgd0L6n.js} +1 -1
- package/payload/server/public/assets/graph-labels-BZgd0L6n.js.br +0 -0
- package/payload/server/public/assets/graph-labels-BZgd0L6n.js.gz +0 -0
- package/payload/server/public/assets/graphlib-DY4-AT2Q.js.br +0 -0
- package/payload/server/public/assets/graphlib-DY4-AT2Q.js.gz +0 -0
- package/payload/server/public/assets/info-OMHHGYJF-BjLPgHAC.js.br +0 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-CXNKLTui.js.br +0 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-CXNKLTui.js.gz +0 -0
- package/payload/server/public/assets/init-BmUWJJHz.js.br +0 -0
- package/payload/server/public/assets/init-BmUWJJHz.js.gz +0 -0
- package/payload/server/public/assets/isEmpty-Bu4fZ8Kf.js.br +0 -0
- package/payload/server/public/assets/isEmpty-Bu4fZ8Kf.js.gz +0 -0
- package/payload/server/public/assets/ishikawaDiagram-UXIWVN3A-wJhSOkuw.js.br +0 -0
- package/payload/server/public/assets/ishikawaDiagram-UXIWVN3A-wJhSOkuw.js.gz +0 -0
- package/payload/server/public/assets/journeyDiagram-VCZTEJTY-DgadC6Pz.js.br +0 -0
- package/payload/server/public/assets/journeyDiagram-VCZTEJTY-DgadC6Pz.js.gz +0 -0
- package/payload/server/public/assets/kanban-definition-6JOO6SKY-Bs4nQqXI.js.br +0 -0
- package/payload/server/public/assets/kanban-definition-6JOO6SKY-Bs4nQqXI.js.gz +0 -0
- package/payload/server/public/assets/katex-BjZuzSQn.js.br +0 -0
- package/payload/server/public/assets/katex-BjZuzSQn.js.gz +0 -0
- package/payload/server/public/assets/line-Bnmxy1hO.js.br +0 -0
- package/payload/server/public/assets/line-Bnmxy1hO.js.gz +0 -0
- package/payload/server/public/assets/linear-RvwZxFzh.js.br +0 -0
- package/payload/server/public/assets/linear-RvwZxFzh.js.gz +0 -0
- package/payload/server/public/assets/{maximize-2-D-H_uIV-.js → maximize-2-la3IBA3M.js} +1 -1
- package/payload/server/public/assets/maximize-2-la3IBA3M.js.br +0 -0
- package/payload/server/public/assets/maximize-2-la3IBA3M.js.gz +0 -0
- package/payload/server/public/assets/mermaid-parser.core-DeNV8RtA.js.br +0 -0
- package/payload/server/public/assets/mermaid-parser.core-DeNV8RtA.js.gz +0 -0
- package/payload/server/public/assets/mermaid.core-BU9IiSCP.js.br +0 -0
- package/payload/server/public/assets/mermaid.core-BU9IiSCP.js.gz +0 -0
- package/payload/server/public/assets/mindmap-definition-QFDTVHPH-D1PMUxxm.js.br +0 -0
- package/payload/server/public/assets/mindmap-definition-QFDTVHPH-D1PMUxxm.js.gz +0 -0
- package/payload/server/public/assets/{operator-D5rA76YY.js → operator-D9NgVL9_.js} +1 -1
- package/payload/server/public/assets/operator-D9NgVL9_.js.br +0 -0
- package/payload/server/public/assets/operator-D9NgVL9_.js.gz +0 -0
- package/payload/server/public/assets/ordinal-C2TEWKNu.js.br +0 -0
- package/payload/server/public/assets/ordinal-C2TEWKNu.js.gz +0 -0
- package/payload/server/public/assets/packet-4T2RLAQJ-BBAZASfb.js.br +0 -0
- package/payload/server/public/assets/{page-CHK1415C.js → page-BWFHRIAH.js} +1 -1
- package/payload/server/public/assets/page-BWFHRIAH.js.br +0 -0
- package/payload/server/public/assets/page-BWFHRIAH.js.gz +0 -0
- package/payload/server/public/assets/{page-DZE3H_n4.js → page-DC11gesX.js} +1 -1
- package/payload/server/public/assets/page-DC11gesX.js.br +0 -0
- package/payload/server/public/assets/page-DC11gesX.js.gz +0 -0
- package/payload/server/public/assets/path-CoPyR7c2.js.br +0 -0
- package/payload/server/public/assets/path-CoPyR7c2.js.gz +0 -0
- package/payload/server/public/assets/pdf-render-C257cidb.js.br +0 -0
- package/payload/server/public/assets/pdf-render-C257cidb.js.gz +0 -0
- package/payload/server/public/assets/pdf.worker.min-yatZIOMy.mjs.br +0 -0
- package/payload/server/public/assets/pdf.worker.min-yatZIOMy.mjs.gz +0 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-CCJUnMeG.js.br +0 -0
- package/payload/server/public/assets/pieDiagram-DEJITSTG-BzdNzoKA.js.br +0 -0
- package/payload/server/public/assets/pieDiagram-DEJITSTG-BzdNzoKA.js.gz +0 -0
- package/payload/server/public/assets/preload-helper-BQrMkyGX.js.br +0 -0
- package/payload/server/public/assets/preload-helper-BQrMkyGX.js.gz +0 -0
- package/payload/server/public/assets/{public-DFHXVkdD.js → public-DQbbLHQN.js} +1 -1
- package/payload/server/public/assets/public-DQbbLHQN.js.br +0 -0
- package/payload/server/public/assets/public-DQbbLHQN.js.gz +0 -0
- package/payload/server/public/assets/quadrantDiagram-34T5L4WZ-oFr1MJRO.js.br +0 -0
- package/payload/server/public/assets/quadrantDiagram-34T5L4WZ-oFr1MJRO.js.gz +0 -0
- package/payload/server/public/assets/radar-PYXPWWZC-BsRU8Aep.js.br +0 -0
- package/payload/server/public/assets/reduce-BGpN5lgn.js.br +0 -0
- package/payload/server/public/assets/reduce-BGpN5lgn.js.gz +0 -0
- package/payload/server/public/assets/requirementDiagram-MS252O5E-C9QBQxDh.js.br +0 -0
- package/payload/server/public/assets/requirementDiagram-MS252O5E-C9QBQxDh.js.gz +0 -0
- package/payload/server/public/assets/{rotate-ccw-CesEJsuO.js → rotate-ccw-OQD5si8N.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-OQD5si8N.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-OQD5si8N.js.gz +0 -0
- package/payload/server/public/assets/rough.esm-jSt7UMyi.js.br +0 -0
- package/payload/server/public/assets/rough.esm-jSt7UMyi.js.gz +0 -0
- package/payload/server/public/assets/{routines-DYMYY_r8.js → routines-asrBGZUR.js} +1 -1
- package/payload/server/public/assets/routines-asrBGZUR.js.br +0 -0
- package/payload/server/public/assets/routines-asrBGZUR.js.gz +0 -0
- package/payload/server/public/assets/sankeyDiagram-XADWPNL6-c_y3bvVv.js.br +0 -0
- package/payload/server/public/assets/sankeyDiagram-XADWPNL6-c_y3bvVv.js.gz +0 -0
- package/payload/server/public/assets/sequenceDiagram-FGHM5R23-YwlCBPHE.js.br +0 -0
- package/payload/server/public/assets/sequenceDiagram-FGHM5R23-YwlCBPHE.js.gz +0 -0
- package/payload/server/public/assets/{skills-67jtJ16D.js → skills-D4ECwoxa.js} +1 -1
- package/payload/server/public/assets/skills-D4ECwoxa.js.br +0 -0
- package/payload/server/public/assets/skills-D4ECwoxa.js.gz +0 -0
- package/payload/server/public/assets/src-C2WwTWSY.js.br +0 -0
- package/payload/server/public/assets/src-C2WwTWSY.js.gz +0 -0
- package/payload/server/public/assets/stateDiagram-FHFEXIEX-BFE3e-FM.js.br +0 -0
- package/payload/server/public/assets/stateDiagram-FHFEXIEX-BFE3e-FM.js.gz +0 -0
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CpjB4ykI.js.br +4 -0
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CpjB4ykI.js.gz +0 -0
- package/payload/server/public/assets/{tasks-D2C2a-uk.js → tasks-U3QdbUsO.js} +1 -1
- package/payload/server/public/assets/tasks-U3QdbUsO.js.br +0 -0
- package/payload/server/public/assets/tasks-U3QdbUsO.js.gz +0 -0
- package/payload/server/public/assets/{time-entry-format-BixTGQAd.js → time-entry-format-BgdTKgYr.js} +1 -1
- package/payload/server/public/assets/time-entry-format-BgdTKgYr.js.br +0 -0
- package/payload/server/public/assets/time-entry-format-BgdTKgYr.js.gz +0 -0
- package/payload/server/public/assets/timeline-definition-GMOUNBTQ-azqhmRwZ.js.br +0 -0
- package/payload/server/public/assets/timeline-definition-GMOUNBTQ-azqhmRwZ.js.gz +0 -0
- package/payload/server/public/assets/treeView-SZITEDCU-BDzhKA6v.js.br +0 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-epHpdeWa.js.br +0 -0
- package/payload/server/public/assets/{triangle-alert-Co9RWb9q.js → triangle-alert-Ck_3VhT9.js} +1 -1
- package/payload/server/public/assets/triangle-alert-Ck_3VhT9.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-Ck_3VhT9.js.gz +0 -0
- package/payload/server/public/assets/{useCopyFeedback-7zwf9_w4.js → useCopyFeedback-BdUwyNpa.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-BdUwyNpa.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-BdUwyNpa.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-CJKjEPNN.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-CJKjEPNN.js.gz +0 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-DE4v5_Fz.css → useSubAccountSwitcher-DOWQXDT8.css} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-DOWQXDT8.css.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-DOWQXDT8.css.gz +0 -0
- package/payload/server/public/assets/{useVoiceRecorder-CZDDiwMD.js → useVoiceRecorder-ChnBrzbZ.js} +1 -1
- package/payload/server/public/assets/useVoiceRecorder-ChnBrzbZ.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-ChnBrzbZ.js.gz +0 -0
- package/payload/server/public/assets/vennDiagram-DHZGUBPP-Da2RfFs8.js.br +0 -0
- package/payload/server/public/assets/vennDiagram-DHZGUBPP-Da2RfFs8.js.gz +0 -0
- package/payload/server/public/assets/wardley-RL74JXVD-DNSZCrxV.js.br +0 -0
- package/payload/server/public/assets/wardleyDiagram-NUSXRM2D-HdrdBYES.js.br +0 -0
- package/payload/server/public/assets/wardleyDiagram-NUSXRM2D-HdrdBYES.js.gz +0 -0
- package/payload/server/public/assets/{wrench-CjQqdj4P.js → wrench-Cpee3C3J.js} +1 -1
- package/payload/server/public/assets/wrench-Cpee3C3J.js.br +2 -0
- package/payload/server/public/assets/wrench-Cpee3C3J.js.gz +0 -0
- package/payload/server/public/assets/xychartDiagram-5P7HB3ND-DMk6rTcQ.js.br +0 -0
- package/payload/server/public/assets/xychartDiagram-5P7HB3ND-DMk6rTcQ.js.gz +0 -0
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +7 -7
- package/payload/server/public/chat.html +13 -13
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +9 -9
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +13 -13
- package/payload/server/public/routines.html +6 -6
- package/payload/server/public/skills.html +5 -5
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +817 -715
- package/payload/server/public/assets/AdminLoginScreens-DKeCt1uf.js +0 -1
- package/payload/server/public/assets/admin-C0-WU1sf.js +0 -1
- package/payload/server/public/assets/agents-DjUvMFTj.js +0 -1
- package/payload/server/public/assets/chat-C0Ps84U5.js +0 -1
- package/payload/server/public/assets/chevron-left-DQ2uNlu9.js +0 -1
- package/payload/server/public/assets/chevron-right-C4QCiRGD.js +0 -1
- package/payload/server/public/assets/clock-BEeQ7JxX.js +0 -1
- package/payload/server/public/assets/data-CGuNfym7.js +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-Bg72hdQ8.js → useSubAccountSwitcher-CJKjEPNN.js} +0 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Tests for check-brand-cache.sh (Task 2019). Fixture-driven: builds a throwaway
|
|
3
|
+
# install dir + config dir per case and stubs the fetch command, because the real
|
|
4
|
+
# one crosses a Cloudflare edge that no test can hold still. sha256 and the
|
|
5
|
+
# ingress parse are the real code paths — those are what this asserts.
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
CHECK="$SCRIPT_DIR/../check-brand-cache.sh"
|
|
10
|
+
|
|
11
|
+
PASS=0
|
|
12
|
+
FAIL=0
|
|
13
|
+
|
|
14
|
+
run_case() {
|
|
15
|
+
local name="$1"; shift
|
|
16
|
+
echo ""
|
|
17
|
+
echo "=== CASE: $name ==="
|
|
18
|
+
if "$@"; then
|
|
19
|
+
PASS=$((PASS + 1)); echo "PASS: $name"
|
|
20
|
+
else
|
|
21
|
+
FAIL=$((FAIL + 1)); echo "FAIL: $name"
|
|
22
|
+
fi
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Build install dir with two brand files and brand-defaults.css. Echoes its path.
|
|
26
|
+
mk_install() {
|
|
27
|
+
local d
|
|
28
|
+
d="$(mktemp -d "${TMPDIR:-/tmp}/brand-cache-install.XXXXXX")"
|
|
29
|
+
mkdir -p "$d/server/public/brand"
|
|
30
|
+
printf 'lime-logo-bytes' > "$d/server/public/brand/logo.png"
|
|
31
|
+
printf 'lime-icon-bytes' > "$d/server/public/brand/icon.png"
|
|
32
|
+
printf ':root{--brand:#c3f53c}' > "$d/server/public/brand-defaults.css"
|
|
33
|
+
echo "$d"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Build a config dir whose cloudflared/config.yml has the given ingress hosts.
|
|
37
|
+
mk_config_with_hosts() {
|
|
38
|
+
local d; d="$(mktemp -d "${TMPDIR:-/tmp}/brand-cache-config.XXXXXX")"
|
|
39
|
+
mkdir -p "$d/cloudflared"
|
|
40
|
+
{ echo "ingress:"; for h in "$@"; do echo " - hostname: $h"; done; echo " - service: http_status:404"; } \
|
|
41
|
+
> "$d/cloudflared/config.yml"
|
|
42
|
+
echo "$d"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
mk_config_no_tunnel() {
|
|
46
|
+
local d; d="$(mktemp -d "${TMPDIR:-/tmp}/brand-cache-config.XXXXXX")"
|
|
47
|
+
echo "$d"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# A config dir with no cloudflared ingress but an alias-domains.json, the
|
|
51
|
+
# second source resolvePublicHostname reads.
|
|
52
|
+
mk_config_alias_only() {
|
|
53
|
+
local d; d="$(mktemp -d "${TMPDIR:-/tmp}/brand-cache-config.XXXXXX")"
|
|
54
|
+
printf '["public.realagent.app"]' > "$d/alias-domains.json"
|
|
55
|
+
echo "$d"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Stub curl. $1 = dir whose files it serves as the "edge", keyed by basename.
|
|
59
|
+
# Writes headers to the -D path and the body to the -o path, exit 0.
|
|
60
|
+
mk_curl_stub() {
|
|
61
|
+
local serve_dir="$1" stub
|
|
62
|
+
stub="$(mktemp "${TMPDIR:-/tmp}/brand-cache-curl.XXXXXX")"
|
|
63
|
+
cat > "$stub" <<STUB
|
|
64
|
+
#!/usr/bin/env bash
|
|
65
|
+
hdr=""; out=""; url=""
|
|
66
|
+
while [ \$# -gt 0 ]; do
|
|
67
|
+
case "\$1" in
|
|
68
|
+
-D) hdr="\$2"; shift 2 ;;
|
|
69
|
+
-o) out="\$2"; shift 2 ;;
|
|
70
|
+
-*) shift ;;
|
|
71
|
+
*) url="\$1"; shift ;;
|
|
72
|
+
esac
|
|
73
|
+
done
|
|
74
|
+
name="\${url##*/}"
|
|
75
|
+
printf 'HTTP/2 200\r\ncf-cache-status: HIT\r\nage: 5352\r\n\r\n' > "\$hdr"
|
|
76
|
+
cat "$serve_dir/\$name" > "\$out"
|
|
77
|
+
STUB
|
|
78
|
+
chmod +x "$stub"
|
|
79
|
+
echo "$stub"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
case_clean_install_reports_clean() {
|
|
83
|
+
local i c stub edge out rc
|
|
84
|
+
i="$(mk_install)"; c="$(mk_config_with_hosts operations.realagent.network)"
|
|
85
|
+
# Edge serves exactly what is on disk.
|
|
86
|
+
edge="$(mktemp -d "${TMPDIR:-/tmp}/brand-cache-edge.XXXXXX")"
|
|
87
|
+
cp "$i/server/public/brand/"* "$edge/"
|
|
88
|
+
cp "$i/server/public/brand-defaults.css" "$edge/"
|
|
89
|
+
stub="$(mk_curl_stub "$edge")"
|
|
90
|
+
out="$(BRAND_CACHE_CURL="$stub" "$CHECK" --install-dir "$i" --config-dir "$c" 2>/dev/null)"
|
|
91
|
+
rc=$?
|
|
92
|
+
[ "$rc" -eq 0 ] || { echo " expected exit 0, got $rc"; rm -rf "$i" "$c" "$edge" "$stub"; return 1; }
|
|
93
|
+
# files=2 pinned as an integer: a \d+ match also accepts files=0, and a
|
|
94
|
+
# hostname resolving to nothing would then read as a pass.
|
|
95
|
+
echo "$out" | grep -qx "\[brand-cache\] clean host=operations.realagent.network files=2" \
|
|
96
|
+
|| { echo " no clean line with files=2; got: $out"; rm -rf "$i" "$c" "$edge" "$stub"; return 1; }
|
|
97
|
+
rm -rf "$i" "$c" "$edge" "$stub"; return 0
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case_stale_edge_reports_stale_on_stderr() {
|
|
101
|
+
local i c stub edge err rc
|
|
102
|
+
i="$(mk_install)"; c="$(mk_config_with_hosts operations.realagent.network)"
|
|
103
|
+
edge="$(mktemp -d "${TMPDIR:-/tmp}/brand-cache-edge.XXXXXX")"
|
|
104
|
+
cp "$i/server/public/brand/"* "$edge/"
|
|
105
|
+
cp "$i/server/public/brand-defaults.css" "$edge/"
|
|
106
|
+
# The measured symptom: the edge holds the retired mark, disk holds the new one.
|
|
107
|
+
printf 'black-square-bytes' > "$edge/logo.png"
|
|
108
|
+
stub="$(mk_curl_stub "$edge")"
|
|
109
|
+
err="$(BRAND_CACHE_CURL="$stub" "$CHECK" --install-dir "$i" --config-dir "$c" 2>&1 >/dev/null)"
|
|
110
|
+
rc=$?
|
|
111
|
+
[ "$rc" -eq 1 ] || { echo " expected exit 1, got $rc"; rm -rf "$i" "$c" "$edge" "$stub"; return 1; }
|
|
112
|
+
echo "$err" | grep -qE "^\[brand-cache\] STALE host=operations\.realagent\.network path=/brand/logo\.png edge=[0-9a-f]{7} disk=[0-9a-f]{7} cf-cache-status=HIT age=5352$" \
|
|
113
|
+
|| { echo " no STALE line for /brand/logo.png; got: $err"; rm -rf "$i" "$c" "$edge" "$stub"; return 1; }
|
|
114
|
+
rm -rf "$i" "$c" "$edge" "$stub"; return 0
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
case_no_tunnel_reports_skipped() {
|
|
118
|
+
local i c out rc
|
|
119
|
+
i="$(mk_install)"; c="$(mk_config_no_tunnel)"
|
|
120
|
+
out="$("$CHECK" --install-dir "$i" --config-dir "$c" 2>/dev/null)"
|
|
121
|
+
rc=$?
|
|
122
|
+
[ "$rc" -eq 0 ] || { echo " expected exit 0, got $rc"; rm -rf "$i" "$c"; return 1; }
|
|
123
|
+
echo "$out" | grep -qx "\[brand-cache\] skipped host=none reason=no-tunnel-ingress" \
|
|
124
|
+
|| { echo " no skipped line; got: $out"; rm -rf "$i" "$c"; return 1; }
|
|
125
|
+
rm -rf "$i" "$c"; return 0
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
case_localhost_only_ingress_is_no_tunnel() {
|
|
129
|
+
local i c out rc
|
|
130
|
+
i="$(mk_install)"; c="$(mk_config_with_hosts localhost muvin.local)"
|
|
131
|
+
out="$("$CHECK" --install-dir "$i" --config-dir "$c" 2>/dev/null)"
|
|
132
|
+
rc=$?
|
|
133
|
+
[ "$rc" -eq 0 ] || { echo " expected exit 0, got $rc"; rm -rf "$i" "$c"; return 1; }
|
|
134
|
+
echo "$out" | grep -qx "\[brand-cache\] skipped host=none reason=no-tunnel-ingress" \
|
|
135
|
+
|| { echo " localhost/.local counted as ingress; got: $out"; rm -rf "$i" "$c"; return 1; }
|
|
136
|
+
rm -rf "$i" "$c"; return 0
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
case_unfetchable_asset_is_not_silently_clean() {
|
|
140
|
+
local i c stub err rc
|
|
141
|
+
i="$(mk_install)"; c="$(mk_config_with_hosts operations.realagent.network)"
|
|
142
|
+
# Stub that answers 502 with an empty body for every path.
|
|
143
|
+
stub="$(mktemp "${TMPDIR:-/tmp}/brand-cache-curl.XXXXXX")"
|
|
144
|
+
cat > "$stub" <<'STUB'
|
|
145
|
+
#!/usr/bin/env bash
|
|
146
|
+
hdr=""; out=""
|
|
147
|
+
while [ $# -gt 0 ]; do
|
|
148
|
+
case "$1" in
|
|
149
|
+
-D) hdr="$2"; shift 2 ;;
|
|
150
|
+
-o) out="$2"; shift 2 ;;
|
|
151
|
+
-*) shift ;;
|
|
152
|
+
*) shift ;;
|
|
153
|
+
esac
|
|
154
|
+
done
|
|
155
|
+
printf 'HTTP/2 502\r\n\r\n' > "$hdr"
|
|
156
|
+
: > "$out"
|
|
157
|
+
STUB
|
|
158
|
+
chmod +x "$stub"
|
|
159
|
+
err="$(BRAND_CACHE_CURL="$stub" "$CHECK" --install-dir "$i" --config-dir "$c" 2>&1 >/dev/null)"
|
|
160
|
+
rc=$?
|
|
161
|
+
[ "$rc" -eq 1 ] || { echo " expected exit 1, got $rc"; rm -rf "$i" "$c" "$stub"; return 1; }
|
|
162
|
+
echo "$err" | grep -qE "^\[brand-cache\] UNREACHABLE host=operations\.realagent\.network path=/brand/logo\.png http=502$" \
|
|
163
|
+
|| { echo " no UNREACHABLE line; got: $err"; rm -rf "$i" "$c" "$stub"; return 1; }
|
|
164
|
+
rm -rf "$i" "$c" "$stub"; return 0
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
case_no_assets_is_a_named_skip_not_a_clean() {
|
|
168
|
+
local i c out rc
|
|
169
|
+
i="$(mktemp -d "${TMPDIR:-/tmp}/brand-cache-install.XXXXXX")"; mkdir -p "$i/server/public"
|
|
170
|
+
c="$(mk_config_with_hosts operations.realagent.network)"
|
|
171
|
+
# bash 3.2 (macOS) errors on "${PATHS[@]}" for an empty array under `set -u`,
|
|
172
|
+
# so this also pins that the loop is never entered with nothing to compare.
|
|
173
|
+
out="$(/bin/bash "$CHECK" --install-dir "$i" --config-dir "$c" 2>&1)"
|
|
174
|
+
rc=$?
|
|
175
|
+
[ "$rc" -eq 0 ] || { echo " expected exit 0, got $rc; got: $out"; rm -rf "$i" "$c"; return 1; }
|
|
176
|
+
echo "$out" | grep -qx "\[brand-cache\] skipped host=operations.realagent.network reason=no-brand-assets" \
|
|
177
|
+
|| { echo " no named skip for an empty asset list; got: $out"; rm -rf "$i" "$c"; return 1; }
|
|
178
|
+
# files=0 must never be emitted: the task's own pinning exists so an empty
|
|
179
|
+
# comparison cannot read as a pass.
|
|
180
|
+
echo "$out" | grep -q "files=0" \
|
|
181
|
+
&& { echo " emitted files=0, which reads as a pass"; rm -rf "$i" "$c"; return 1; }
|
|
182
|
+
rm -rf "$i" "$c"; return 0
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
case_alias_only_install_is_checked_not_skipped() {
|
|
186
|
+
local i c stub edge out rc
|
|
187
|
+
i="$(mk_install)"; c="$(mk_config_alias_only)"
|
|
188
|
+
edge="$(mktemp -d "${TMPDIR:-/tmp}/brand-cache-edge.XXXXXX")"
|
|
189
|
+
cp "$i/server/public/brand/"* "$edge/"
|
|
190
|
+
stub="$(mk_curl_stub "$edge")"
|
|
191
|
+
out="$(BRAND_CACHE_CURL="$stub" "$CHECK" --install-dir "$i" --config-dir "$c" 2>/dev/null)"
|
|
192
|
+
rc=$?
|
|
193
|
+
[ "$rc" -eq 0 ] || { echo " expected exit 0, got $rc"; rm -rf "$i" "$c" "$edge" "$stub"; return 1; }
|
|
194
|
+
# An install with an alias edge and no ingress row does have an edge that can
|
|
195
|
+
# diverge, so reporting `skipped` there would read as nothing-to-check on the
|
|
196
|
+
# one state the skip wording promises it is not.
|
|
197
|
+
echo "$out" | grep -qx "\[brand-cache\] clean host=public.realagent.app files=2" \
|
|
198
|
+
|| { echo " alias-only install was not checked; got: $out"; rm -rf "$i" "$c" "$edge" "$stub"; return 1; }
|
|
199
|
+
rm -rf "$i" "$c" "$edge" "$stub"; return 0
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
case_brand_defaults_css_is_not_fetched() {
|
|
203
|
+
local i c stub edge out rc
|
|
204
|
+
i="$(mk_install)"; c="$(mk_config_with_hosts admin.realagent.network public.realagent.network)"
|
|
205
|
+
edge="$(mktemp -d "${TMPDIR:-/tmp}/brand-cache-edge.XXXXXX")"
|
|
206
|
+
cp "$i/server/public/brand/"* "$edge/"
|
|
207
|
+
# No brand-defaults.css at the edge. The gate at server/index.ts:1311-1322
|
|
208
|
+
# exempts /brand/ but not /brand-defaults.css, so through an admin-gated host
|
|
209
|
+
# that path answers with a login page at status 200 — hashing that against
|
|
210
|
+
# the stylesheet would be a permanent false STALE with no cache involved.
|
|
211
|
+
stub="$(mk_curl_stub "$edge")"
|
|
212
|
+
out="$(BRAND_CACHE_CURL="$stub" "$CHECK" --install-dir "$i" --config-dir "$c" 2>/dev/null)"
|
|
213
|
+
rc=$?
|
|
214
|
+
[ "$rc" -eq 0 ] || { echo " expected exit 0, got $rc"; rm -rf "$i" "$c" "$edge" "$stub"; return 1; }
|
|
215
|
+
echo "$out" | grep -qx "\[brand-cache\] clean host=admin.realagent.network files=2" \
|
|
216
|
+
|| { echo " expected only the two /brand/ files; got: $out"; rm -rf "$i" "$c" "$edge" "$stub"; return 1; }
|
|
217
|
+
rm -rf "$i" "$c" "$edge" "$stub"; return 0
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
run_case "clean install reports clean with a pinned file count" case_clean_install_reports_clean
|
|
221
|
+
run_case "stale edge reports STALE on stderr and exits 1" case_stale_edge_reports_stale_on_stderr
|
|
222
|
+
run_case "no tunnel ingress reports skipped and exits 0" case_no_tunnel_reports_skipped
|
|
223
|
+
run_case "localhost/.local-only ingress counts as no tunnel" case_localhost_only_ingress_is_no_tunnel
|
|
224
|
+
run_case "an unfetchable asset is reported, never counted clean" case_unfetchable_asset_is_not_silently_clean
|
|
225
|
+
run_case "an empty asset list is a named skip, never files=0" case_no_assets_is_a_named_skip_not_a_clean
|
|
226
|
+
run_case "an alias-only install is checked, not skipped" case_alias_only_install_is_checked_not_skipped
|
|
227
|
+
run_case "brand-defaults.css is not fetched through a gated host" case_brand_defaults_css_is_not_fetched
|
|
228
|
+
|
|
229
|
+
echo ""
|
|
230
|
+
echo "PASS=$PASS FAIL=$FAIL"
|
|
231
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-brand-cache.sh — reconcile the bytes the public hostname returns
|
|
3
|
+
# against the bytes on disk (Task 2019).
|
|
4
|
+
#
|
|
5
|
+
# The failure this exists for emits no event at either end. The installer
|
|
6
|
+
# writes the new brand bytes and logs a successful upgrade; the origin serves
|
|
7
|
+
# them correctly to anyone asking it directly; Cloudflare's edge serves the
|
|
8
|
+
# pre-upgrade ones and reports `HIT` to nobody looking. There is no action to
|
|
9
|
+
# log and re-running the upgrade does not surface it, so only a reconciliation
|
|
10
|
+
# of served bytes against on-disk bytes reveals the state. That is the
|
|
11
|
+
# condition behind the 2026-07-26 report, where the regenerated Real Agent logo
|
|
12
|
+
# was correct on disk and invisible through the hostname for four hours.
|
|
13
|
+
#
|
|
14
|
+
# $ check-brand-cache.sh --install-dir <dir> --config-dir <dir>
|
|
15
|
+
#
|
|
16
|
+
# stdout, exit 0: [brand-cache] clean host=<h> files=<n>
|
|
17
|
+
# stdout, exit 0: [brand-cache] skipped host=none reason=no-tunnel-ingress
|
|
18
|
+
# stderr, exit 1: [brand-cache] STALE host=<h> path=<p> edge=<sha7> disk=<sha7> cf-cache-status=<s> age=<n>
|
|
19
|
+
# stderr, exit 1: [brand-cache] UNREACHABLE host=<h> path=<p> http=<code>
|
|
20
|
+
#
|
|
21
|
+
# No ingress hostname and no alias domain means no edge and therefore no
|
|
22
|
+
# possible divergence, so that is a skip, not a pass — a caller must never read
|
|
23
|
+
# the two as the same.
|
|
24
|
+
#
|
|
25
|
+
# Scope: the files under server/public/brand/ only. See the PATHS block below.
|
|
26
|
+
#
|
|
27
|
+
# BRAND_CACHE_CURL overrides the fetch command (tests stub it).
|
|
28
|
+
set -uo pipefail
|
|
29
|
+
|
|
30
|
+
INSTALL_DIR=""
|
|
31
|
+
CONFIG_DIR=""
|
|
32
|
+
while [ $# -gt 0 ]; do
|
|
33
|
+
case "$1" in
|
|
34
|
+
--install-dir) INSTALL_DIR="$2"; shift 2 ;;
|
|
35
|
+
--config-dir) CONFIG_DIR="$2"; shift 2 ;;
|
|
36
|
+
*) echo "[brand-cache] unknown argument: $1" >&2; exit 2 ;;
|
|
37
|
+
esac
|
|
38
|
+
done
|
|
39
|
+
if [ -z "$INSTALL_DIR" ] || [ -z "$CONFIG_DIR" ]; then
|
|
40
|
+
echo "[brand-cache] usage: check-brand-cache.sh --install-dir <dir> --config-dir <dir>" >&2
|
|
41
|
+
exit 2
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
CURL="${BRAND_CACHE_CURL:-curl}"
|
|
45
|
+
|
|
46
|
+
# sha256sum is GNU; darwin ships shasum. Both print "<digest> <path>".
|
|
47
|
+
sha256() {
|
|
48
|
+
if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | awk '{print $1}'
|
|
49
|
+
else shasum -a 256 "$1" | awk '{print $1}'; fi
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# The hostname to probe, read from the same two sources and with the same
|
|
53
|
+
# exclusions as platform/plugins/admin/mcp/src/lib/public-hostname.ts:
|
|
54
|
+
# cloudflared/config.yml first, then alias-domains.json. First non-excluded
|
|
55
|
+
# wins within each source.
|
|
56
|
+
#
|
|
57
|
+
# No apex tiebreak, and no attempt to pick the *public* host over the admin
|
|
58
|
+
# one: only /brand/ assets are fetched, and the admin gate exempts /brand/ on
|
|
59
|
+
# every host class (server/index.ts:1311-1322), so any ingress host serves them
|
|
60
|
+
# identically. The alias source is read because an install whose edge is an
|
|
61
|
+
# alias domain with no ingress row does have an edge that can diverge, and
|
|
62
|
+
# reporting `skipped` there would read as nothing-to-check.
|
|
63
|
+
resolve_host() {
|
|
64
|
+
local cfg="$CONFIG_DIR/cloudflared/config.yml" alias="$CONFIG_DIR/alias-domains.json" h
|
|
65
|
+
if [ -f "$cfg" ]; then
|
|
66
|
+
h="$(sed -n 's/^[[:space:]]*-[[:space:]]*hostname:[[:space:]]*\([^[:space:]]*\).*/\1/p' "$cfg" \
|
|
67
|
+
| grep -v '^localhost$' | grep -v '\.local$' | head -1)"
|
|
68
|
+
if [ -n "$h" ]; then echo "$h"; return 0; fi
|
|
69
|
+
fi
|
|
70
|
+
[ -f "$alias" ] || return 0
|
|
71
|
+
tr ',' '\n' < "$alias" | sed -n 's/.*"\([^"]*\)".*/\1/p' \
|
|
72
|
+
| grep -v '^localhost$' | grep -v '\.local$' | head -1
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
HOST="$(resolve_host)"
|
|
76
|
+
if [ -z "$HOST" ]; then
|
|
77
|
+
echo "[brand-cache] skipped host=none reason=no-tunnel-ingress"
|
|
78
|
+
exit 0
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# /brand/ only. /brand-defaults.css is NOT exempt from the admin gate
|
|
82
|
+
# (server/index.ts:1311-1322 lists /favicon.ico, /assets/, /brand/,
|
|
83
|
+
# /api/calendar/free-busy and the PWA paths, and nothing else), so through an
|
|
84
|
+
# admin-gated or operator host it answers with a login page at status 200
|
|
85
|
+
# (index.ts:1379). Hashing that against the stylesheet on disk would be a
|
|
86
|
+
# permanent STALE with no cache involved, which would both bury a real
|
|
87
|
+
# divergence and make the clean line unreachable. Covering it, and the page
|
|
88
|
+
# shells, needs a resolved public hostname — Task 2026.
|
|
89
|
+
PUBLIC_DIR="$INSTALL_DIR/server/public"
|
|
90
|
+
PATHS=()
|
|
91
|
+
if [ -d "$PUBLIC_DIR/brand" ]; then
|
|
92
|
+
while IFS= read -r f; do PATHS+=("/brand/$(basename "$f")"); done \
|
|
93
|
+
< <(find "$PUBLIC_DIR/brand" -maxdepth 1 -type f | sort)
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# Nothing to compare is a skip with its own reason, not a clean run. Emitting
|
|
97
|
+
# `files=0` here would read as a pass on an install whose payload never
|
|
98
|
+
# deployed, and that is the dead signal the file count is pinned against.
|
|
99
|
+
# Also the guard for bash 3.2 (darwin), where "${PATHS[@]}" on an empty array
|
|
100
|
+
# is an unbound-variable error under `set -u`.
|
|
101
|
+
if [ "${#PATHS[@]}" -eq 0 ]; then
|
|
102
|
+
echo "[brand-cache] skipped host=$HOST reason=no-brand-assets"
|
|
103
|
+
exit 0
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
TMP="$(mktemp -d "${TMPDIR:-/tmp}/brand-cache.XXXXXX")"
|
|
107
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
108
|
+
|
|
109
|
+
FAILED=0
|
|
110
|
+
CHECKED=0
|
|
111
|
+
for p in "${PATHS[@]}"; do
|
|
112
|
+
body="$TMP/body"; hdr="$TMP/hdr"
|
|
113
|
+
"$CURL" -sS -L -D "$hdr" -o "$body" "https://$HOST$p" >/dev/null 2>&1
|
|
114
|
+
code="$(awk 'tolower($0) ~ /^http\// {c=$2} END{print c+0}' "$hdr" 2>/dev/null)"
|
|
115
|
+
if [ "$code" != "200" ]; then
|
|
116
|
+
echo "[brand-cache] UNREACHABLE host=$HOST path=$p http=$code" >&2
|
|
117
|
+
FAILED=1
|
|
118
|
+
continue
|
|
119
|
+
fi
|
|
120
|
+
edge="$(sha256 "$body")"
|
|
121
|
+
disk="$(sha256 "$PUBLIC_DIR$p")"
|
|
122
|
+
if [ "$edge" != "$disk" ]; then
|
|
123
|
+
# tail -1, not head -1: -L means $hdr can hold several header blocks, and
|
|
124
|
+
# the cache state that matters is the one on the response the bytes came
|
|
125
|
+
# from. head -1 would name a redirect hop's cache state instead, which is
|
|
126
|
+
# the field an operator reads to tell this fault from an origin fault.
|
|
127
|
+
status="$(awk 'tolower($1) == "cf-cache-status:" {print $2}' "$hdr" | tr -d '\r' | tail -1)"
|
|
128
|
+
age="$(awk 'tolower($1) == "age:" {print $2}' "$hdr" | tr -d '\r' | tail -1)"
|
|
129
|
+
echo "[brand-cache] STALE host=$HOST path=$p edge=${edge:0:7} disk=${disk:0:7} cf-cache-status=${status:-none} age=${age:-0}" >&2
|
|
130
|
+
FAILED=1
|
|
131
|
+
continue
|
|
132
|
+
fi
|
|
133
|
+
CHECKED=$((CHECKED + 1))
|
|
134
|
+
done
|
|
135
|
+
|
|
136
|
+
[ "$FAILED" -eq 1 ] && exit 1
|
|
137
|
+
echo "[brand-cache] clean host=$HOST files=$CHECKED"
|
|
138
|
+
exit 0
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Resolve plugin-declared account-owned top-level
|
|
3
|
-
them into an account's SCHEMA.md.
|
|
2
|
+
"""Resolve plugin-declared account-owned top-level entries for the brand and
|
|
3
|
+
merge them into an account's SCHEMA.md.
|
|
4
4
|
|
|
5
|
-
A plugin declares owned top-level account
|
|
5
|
+
A plugin declares owned top-level account entries in its PLUGIN.md frontmatter,
|
|
6
|
+
by kind:
|
|
6
7
|
|
|
7
|
-
account-owned-dirs:
|
|
8
|
+
account-owned-dirs: [{"dir": "quoting", "description": "..."}]
|
|
9
|
+
account-owned-files: [{"file": "data-portal.json", "description": "..."}]
|
|
8
10
|
|
|
9
|
-
The owned-
|
|
11
|
+
The owned-entry set for a brand is the union declared by every platform plugin
|
|
10
12
|
($PROJECT_DIR/plugins/*/PLUGIN.md) and every premium sub-plugin of a bundle the
|
|
11
13
|
brand ships ($PREMIUM_ROOT/<bundle>/plugins/*/PLUGIN.md, gated by brand.json
|
|
12
14
|
shipsPremiumBundles). Reads PROJECT_DIR from the environment.
|
|
13
15
|
|
|
16
|
+
A declaration reaches an account only on a brand whose payload carries the
|
|
17
|
+
declaring plugin (brand.json plugins.excluded drops the directory entirely), so
|
|
18
|
+
an entry written by platform code that runs on every brand belongs on a plugin
|
|
19
|
+
no brand excludes, not on the plugin its feature is named after.
|
|
20
|
+
|
|
14
21
|
The account SCHEMA.md also gains one domain-entity bucket per top-level
|
|
15
22
|
operator-entry node type of the brand's business ontology. That set is derived
|
|
16
23
|
from the brand vertical's schema reference (brand.json#vertical ->
|
|
@@ -102,7 +109,17 @@ def _read_vertical(project_dir):
|
|
|
102
109
|
|
|
103
110
|
|
|
104
111
|
def _parse_plugin_md(path):
|
|
105
|
-
"""Return (name, [ {
|
|
112
|
+
"""Return (name, [ {name, kind, description} ]) from a PLUGIN.md, or (None, []).
|
|
113
|
+
Two frontmatter keys, one list: `account-owned-dirs` yields kind='dir',
|
|
114
|
+
`account-owned-files` yields kind='file'. A key whose value is not a JSON
|
|
115
|
+
array of objects is skipped WHOLE: entries are staged and committed only once
|
|
116
|
+
every element parses, so a bad element cannot half-merge a declaration and
|
|
117
|
+
leave the rest reporting as strays. The other key and every other plugin
|
|
118
|
+
still merge, because one hand-edited manifest must never empty the set. A
|
|
119
|
+
name carrying a path separator is dropped: a fence entry has to match one
|
|
120
|
+
readdir name, and the region tells a bucket from a file by the trailing slash
|
|
121
|
+
it adds itself, so a declared `foo/` would render as a bucket that matches
|
|
122
|
+
nothing."""
|
|
106
123
|
try:
|
|
107
124
|
with open(path) as f:
|
|
108
125
|
text = f.read()
|
|
@@ -117,16 +134,21 @@ def _parse_plugin_md(path):
|
|
|
117
134
|
if nm:
|
|
118
135
|
name = nm.group(1).strip().strip('"').strip("'")
|
|
119
136
|
owned = []
|
|
120
|
-
|
|
121
|
-
|
|
137
|
+
for key, field, kind in (("account-owned-dirs", "dir", "dir"),
|
|
138
|
+
("account-owned-files", "file", "file")):
|
|
139
|
+
om = re.search(r"^" + key + r":\s*(.+)$", fm, re.MULTILINE)
|
|
140
|
+
if not om:
|
|
141
|
+
continue
|
|
142
|
+
staged = []
|
|
122
143
|
try:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
144
|
+
for e in json.loads(om.group(1).strip()):
|
|
145
|
+
n = (e.get(field) or "").strip()
|
|
146
|
+
if n and "/" not in n:
|
|
147
|
+
staged.append({"name": n, "kind": kind,
|
|
148
|
+
"description": (e.get("description") or "").strip()})
|
|
128
149
|
except Exception:
|
|
129
|
-
|
|
150
|
+
continue
|
|
151
|
+
owned.extend(staged)
|
|
130
152
|
return name, owned
|
|
131
153
|
|
|
132
154
|
|
|
@@ -158,18 +180,19 @@ def _plugin_md_paths(project_dir):
|
|
|
158
180
|
|
|
159
181
|
|
|
160
182
|
def resolve(project_dir):
|
|
161
|
-
"""Union of owned-
|
|
162
|
-
label + description."""
|
|
183
|
+
"""Union of owned-entry declarations. First declarer of a name wins its
|
|
184
|
+
owner label + description, whichever kind it declared."""
|
|
163
185
|
seen = {}
|
|
164
186
|
order = []
|
|
165
187
|
for path in _plugin_md_paths(project_dir):
|
|
166
188
|
name, owned = _parse_plugin_md(path)
|
|
167
189
|
for e in owned:
|
|
168
|
-
if e["
|
|
190
|
+
if e["name"] in seen:
|
|
169
191
|
continue
|
|
170
|
-
seen[e["
|
|
171
|
-
|
|
172
|
-
|
|
192
|
+
seen[e["name"]] = {"name": e["name"], "kind": e["kind"],
|
|
193
|
+
"description": e["description"], "plugin": name or "unknown"}
|
|
194
|
+
order.append(e["name"])
|
|
195
|
+
return [seen[n] for n in order]
|
|
173
196
|
|
|
174
197
|
|
|
175
198
|
def _top_level_labels(schema_path):
|
|
@@ -304,7 +327,7 @@ def merge(project_dir, account_dir):
|
|
|
304
327
|
# entity (non-blank Owner) nests under its owner and is not a target,
|
|
305
328
|
# so a pre-fix account's empty owned bucket prunes while a populated one
|
|
306
329
|
# is kept-and-logged like any other stale bucket.
|
|
307
|
-
target = FIXED_SEED | {e["
|
|
330
|
+
target = FIXED_SEED | {e["name"] for e in owned} | {b["dir"] for b in domain if _is_root(b["owner"])}
|
|
308
331
|
# Reconcile existing entries: keep in-target; keep populated strays
|
|
309
332
|
# (logged); drop empty/absent strays (logged).
|
|
310
333
|
new_entries = []
|
|
@@ -319,9 +342,9 @@ def merge(project_dir, account_dir):
|
|
|
319
342
|
# Append newly-projected owned then domain dirs not already present.
|
|
320
343
|
insert = []
|
|
321
344
|
for e in owned:
|
|
322
|
-
if e["
|
|
323
|
-
insert.append(e["
|
|
324
|
-
added.append(e["
|
|
345
|
+
if e["name"] not in present and e["name"] not in insert:
|
|
346
|
+
insert.append(e["name"])
|
|
347
|
+
added.append(e["name"])
|
|
325
348
|
for b in domain:
|
|
326
349
|
if not _is_root(b["owner"]):
|
|
327
350
|
continue # contained entity: no root bucket, nests under its owner
|
|
@@ -336,16 +359,21 @@ def merge(project_dir, account_dir):
|
|
|
336
359
|
text = _strip_region(text, MARK_START, MARK_END)
|
|
337
360
|
text = _strip_region(text, ONT_START, ONT_END)
|
|
338
361
|
if owned:
|
|
339
|
-
region = [MARK_START, "## Plugin-owned top-level
|
|
340
|
-
"A plugin
|
|
341
|
-
"structure
|
|
342
|
-
"write
|
|
362
|
+
region = [MARK_START, "## Plugin-owned top-level entries", "",
|
|
363
|
+
"A plugin declares the account-root entries its feature owns. A",
|
|
364
|
+
"directory's internal structure belongs to that plugin's tools and is",
|
|
365
|
+
"recreated on the next write, so never reorganise it; a declared file",
|
|
366
|
+
"is written whole by its owner. Neither is operator data.", ""]
|
|
343
367
|
for e in owned:
|
|
344
368
|
desc = e["description"] or f"Owned by the {e['plugin']} plugin."
|
|
345
|
-
|
|
369
|
+
# The trailing slash marks a bucket. Three readers anchor on it to
|
|
370
|
+
# tell a directory from a declared file (account-schema-regions,
|
|
371
|
+
# schema-exposed-dirs.mjs), so a file must never gain one.
|
|
372
|
+
head = f"{e['name']}/" if e["kind"] == "dir" else e["name"]
|
|
373
|
+
region.append(f"- `{head}` — {desc}")
|
|
346
374
|
region.append(MARK_END)
|
|
347
375
|
text = text.rstrip("\n") + "\n\n" + "\n".join(region) + "\n"
|
|
348
|
-
owned_dirs = {e["
|
|
376
|
+
owned_dirs = {e["name"] for e in owned if e["kind"] == "dir"}
|
|
349
377
|
# A dir already claimed by a plugin (described in the plugin-owned region)
|
|
350
378
|
# is not also described here, so a name collision cannot double-describe.
|
|
351
379
|
described = [b for b in domain if b["dir"] not in owned_dirs]
|
|
@@ -387,8 +415,9 @@ def reconcile(project_dir, account_dir):
|
|
|
387
415
|
entries, _s, _e = _allowed_block(f.read())
|
|
388
416
|
present = set(entries or [])
|
|
389
417
|
for e in owned:
|
|
390
|
-
is_present = "true" if e["
|
|
391
|
-
|
|
418
|
+
is_present = "true" if e["name"] in present else "false"
|
|
419
|
+
label = "ownedDir" if e["kind"] == "dir" else "ownedFile"
|
|
420
|
+
print(f"brand={brand} plugin={e['plugin']} {label}={e['name']} present={is_present}")
|
|
392
421
|
vertical = _read_vertical(project_dir)
|
|
393
422
|
domain = resolve_domain_buckets(project_dir)
|
|
394
423
|
if vertical and not domain:
|
package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-dir-schema-reconcile.d.ts","sourceRoot":"","sources":["../src/account-dir-schema-reconcile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"account-dir-schema-reconcile.d.ts","sourceRoot":"","sources":["../src/account-dir-schema-reconcile.ts"],"names":[],"mappings":"AA8EA,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD;;;;;0CAKsC;IACtC,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAED;;+DAE+D;AAC/D,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,eAAe,EAClB,WAAW,SAAI,EACf,aAAa,SAAI,EACjB,UAAU,SAAI,GACb,MAAM,CAGR;AAED;;8EAE8E;AAC9E,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAcpE;AA+ED;;;8EAG8E;AAC9E,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,CAoCpE;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC/B;AA0DD;;;;;;;;;;;;;iCAaiC;AACjC,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CA8DtD;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,IAAI,IAAI,CAAA;IACb,IAAI,IAAI,IAAI,CAAA;CACb;AAED;oDACoD;AACpD,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,aAAa,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAC3C,yBAAyB,CAiB3B"}
|
package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js
CHANGED
|
@@ -31,26 +31,24 @@ import { join, relative, sep } from 'node:path';
|
|
|
31
31
|
import { parseSchemaRegions, HOME_FIXED } from '../../../lib/account-schema-regions/dist/index.js';
|
|
32
32
|
const TAG = '[fs-reconcile]';
|
|
33
33
|
const QUARANTINE_DIR = '.quarantine';
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
* the account dir a git root (`.git`) and writes its hook-enforcement
|
|
37
|
-
* into `.claude
|
|
38
|
-
* refreshed on every provision
|
|
39
|
-
* hand-edited
|
|
40
|
-
*
|
|
34
|
+
/** The account's own machinery, which the reconcile NEVER counts as a stray
|
|
35
|
+
* whatever that account's SCHEMA.md allowed set says. provision-account-dir.sh
|
|
36
|
+
* makes the account dir a git root (`.git`) and writes its hook-enforcement
|
|
37
|
+
* settings into `.claude/`; the account's identity lives in account.json;
|
|
38
|
+
* AGENTS.md and SCHEMA.md are refreshed on every provision. An account carrying
|
|
39
|
+
* a stale, hand-edited or older-template schema must not be reported every run
|
|
40
|
+
* for machinery it is required to have.
|
|
41
41
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* because nothing moves. It keeps the account's own machinery out of the stray
|
|
53
|
-
* count so the standing op=strays line names only what an operator must act on. */
|
|
42
|
+
* Feature files at the account root are NOT here. Each is declared by the plugin
|
|
43
|
+
* whose feature owns it (Task 1902, `account-owned-files` in PLUGIN.md
|
|
44
|
+
* frontmatter), unioned into the account's allowed block by
|
|
45
|
+
* platform/scripts/lib/account-schema-owned-dirs.py, and read from there. This
|
|
46
|
+
* constant used to hold seven of them and was extended once per casualty
|
|
47
|
+
* (calendar-availability.json was Task 1877, after quarantining it froze a live
|
|
48
|
+
* booking page; agents-disabled.json was Task 1996), which is the growth the
|
|
49
|
+
* declarations replace. setup-account.sh runs the all-accounts merge on every
|
|
50
|
+
* install, so a declaration reaches existing accounts and not only fresh ones,
|
|
51
|
+
* and the reconcile's ownedFile= lines name any account it has not reached. */
|
|
54
52
|
const PROTECTED_TOP_LEVEL = new Set([
|
|
55
53
|
'.git',
|
|
56
54
|
'.claude',
|
|
@@ -58,17 +56,6 @@ const PROTECTED_TOP_LEVEL = new Set([
|
|
|
58
56
|
'SCHEMA.md',
|
|
59
57
|
'account.json',
|
|
60
58
|
'AGENTS.md',
|
|
61
|
-
'calendar-availability.json',
|
|
62
|
-
'wa-channel-bindings.json',
|
|
63
|
-
'webchat-channel-bindings.json',
|
|
64
|
-
'telegram-channel-bindings.json',
|
|
65
|
-
'canonical-webchat-session.json',
|
|
66
|
-
'session-titles.json',
|
|
67
|
-
// Task 1996 — names the agents the operator switched off. Platform-written at
|
|
68
|
-
// the account root every disable, declared by no plugin, so without this every
|
|
69
|
-
// account with a disabled agent reports it as a stray forever and the standing
|
|
70
|
-
// line stops naming only what an operator must act on.
|
|
71
|
-
'agents-disabled.json',
|
|
72
59
|
]);
|
|
73
60
|
/** Buckets whose depth fs-schema-guard.sh checks, with the same 3-segment cap.
|
|
74
61
|
* Every other top-level bucket is tool-owned and unbounded — not walked. This
|