@rubytech/create-maxy-code 0.1.383 → 0.1.386
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +37 -9
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +48 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +51 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +37 -18
- package/payload/platform/plugins/cloudflare/references/hosting-sites.md +7 -4
- package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +9 -6
- package/payload/platform/plugins/docs/references/admin-ui.md +36 -17
- package/payload/platform/plugins/email/skills/email-composition/SKILL.md +1 -1
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +2 -0
- package/payload/platform/plugins/telegram/skills/configure/SKILL.md +2 -0
- package/payload/platform/scripts/__tests__/check-plugin-references.test.sh +83 -0
- package/payload/platform/scripts/check-plugin-references.mjs +60 -11
- package/payload/platform/services/claude-session-manager/dist/index.js +27 -2
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rootless-client-audit.d.ts +35 -0
- package/payload/platform/services/claude-session-manager/dist/rootless-client-audit.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rootless-client-audit.js +114 -0
- package/payload/platform/services/claude-session-manager/dist/rootless-client-audit.js.map +1 -0
- package/payload/platform/services/claude-session-manager/package.json +2 -1
- package/payload/server/public/assets/{AdminLoginScreens-C32Kg8HN.js → AdminLoginScreens-oxqHKL3C.js} +1 -1
- package/payload/server/public/assets/AdminShell-DBD6ruS1.js +1 -0
- package/payload/server/public/assets/{Checkbox-C-dvkjKb.js → Checkbox-yr8-ftrE.js} +1 -1
- package/payload/server/public/assets/Transcript-FX_w6no6.js +2 -0
- package/payload/server/public/assets/admin-CQfZnuFy.js +1 -0
- package/payload/server/public/assets/{arc-JgkiMDpu.js → arc-C6-nr5UV.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-DdgmZ93B.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-CyIbl1bD.js → architectureDiagram-Q4EWVU46-DIdho_T6.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CkcZVblf.js → blockDiagram-DXYQGD6D-D5rTTBSy.js} +1 -1
- package/payload/server/public/assets/{browser-BUuA2PPl.js → browser-DbdPeDK1.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-VByNuobb.js → c4Diagram-AHTNJAMY-pfUVHdmy.js} +1 -1
- package/payload/server/public/assets/calendar-CFdOhe7Q.js +1 -0
- package/payload/server/public/assets/channel-Da-UUyqx.js +1 -0
- package/payload/server/public/assets/chat-BunxcNDk.js +1 -0
- package/payload/server/public/assets/chevron-left-Cngmu0Py.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-BFwiGcqo.js → chunk-2KRD3SAO-EF6cdj8U.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-BMCBU-vf.js → chunk-336JU56O-DE-lcZAc.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-B1kFAxo1.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-RfOeo8_F.js → chunk-4BX2VUAB--ANCzzyn.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-B5xbgonc.js → chunk-4TB4RGXK-DTO_X80J.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-K8U55ikJ.js → chunk-55IACEB6-4J3nrz44.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-DgmmsrAc.js → chunk-5FUZZQ4R-jH_eqAHL.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-DQ6rtehF.js → chunk-5PVQY5BW-C-qU6p6k.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-CC2kfpmQ.js → chunk-67CJDMHE-DXxH9Snb.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BClZ5bzw.js → chunk-7N4EOEYR-CCkxuP7A.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-adtwAFKX.js → chunk-AA7GKIK3-CyItuM8d.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-fKY1UWEd.js → chunk-BSJP7CBP-Bg7gxYNf.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-knPAMmjG.js → chunk-CIAEETIT-BAXbOqv5.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-C7FRKZ5e.js → chunk-EDXVE4YY-vdoErQoc.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-0cgFfv3z.js → chunk-ENJZ2VHE-Cz_z_6GK.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-Dm_Hcblb.js → chunk-FMBD7UC4-Ck2jvOgW.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-CfCJNXmK.js → chunk-FOC6F5B3-DucR0Qo8.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-xeu6qrZj.js → chunk-ICPOFSXX-DolYDD7g.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-BQDCSuTh.js → chunk-K5T4RW27-FRzx7E5I.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-D6fFAgbM.js → chunk-KGLVRYIC-Bb5S4Bmc.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-Dskwlc-K.js → chunk-LIHQZDEY-c8VZJ-lU.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-CnHIrJeA.js → chunk-ORNJ4GCN-BAeh-LZ1.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-B4bnsPkv.js → chunk-OYMX7WX6-BUg7Kysh.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-BjE-Glwf.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BeEXzOqV.js → chunk-U2HBQHQK-K4r17UTk.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-D2rpgOqd.js → chunk-X2U36JSP-Byh-CyxC.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-D-yT82Xf.js → chunk-XPW4576I-DC7lhr93.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-DqxlLBm1.js → chunk-YZCP3GAM-B2cu7AEP.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-CA_Ep0OM.js → chunk-ZZ45TVLE-DH_p8sFX.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-Dol3Ogeh.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-VUMj7gWH.js +1 -0
- package/payload/server/public/assets/clone-m7oGrdBC.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-DWb9mxTa.js → cose-bilkent-S5V4N54A-DeM6JI00.js} +1 -1
- package/payload/server/public/assets/{dagre-B3K7jxOZ.js → dagre-3drs3e4F.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-BhiTISRq.js → dagre-KV5264BT-C6S-VUtN.js} +1 -1
- package/payload/server/public/assets/data-IqZlMSwG.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-D5yBIDpm.js → diagram-5BDNPKRD-BkvA32I3.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-CSTE1S4d.js → diagram-G4DWMVQ6-CVwtRMPB.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-D8llv4XY.js → diagram-MMDJMWI5-DJ1JVMbd.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CITQ7aDM.js → diagram-TYMM5635-rfj-crtX.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-BjR5nlmv.js → erDiagram-SMLLAGMA-TJPuweS5.js} +1 -1
- package/payload/server/public/assets/{flatten-DhYYI_lo.js → flatten-BZkoyJ4e.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-Dl6e-E0f.js → flowDiagram-DWJPFMVM-LQWEUjbV.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CppkrFAl.js → ganttDiagram-T4ZO3ILL-DdYsxKRw.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-cJEaiQq4.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-clV_tnwa.js → gitGraphDiagram-UUTBAWPF-sx8-fokM.js} +1 -1
- package/payload/server/public/assets/{graph-CvQZVxwZ.js → graph-Bluocsf_.js} +3 -3
- package/payload/server/public/assets/{graph-labels-C7bJMcXs.js → graph-labels-BcLJmY-v.js} +1 -1
- package/payload/server/public/assets/{graphlib-Cnk6WL-8.js → graphlib-DixeJdCf.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-BUxfOTKa.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-CD-d3Rgf.js +2 -0
- package/payload/server/public/assets/{isEmpty-DGhq_DBP.js → isEmpty-VRKFml5R.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-DUqusmP1.js → ishikawaDiagram-UXIWVN3A-BKUCuKhZ.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-RMEvGtDJ.js → journeyDiagram-VCZTEJTY-BafZP6pM.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-BxkwQPCP.js → kanban-definition-6JOO6SKY-DQ4c6wRv.js} +1 -1
- package/payload/server/public/assets/{line-CiOuSBlJ.js → line-0_x1Wivu.js} +1 -1
- package/payload/server/public/assets/{linear-D22sItNm.js → linear-CZrU-J2V.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-kvN8ajT-.js → mermaid-parser.core-DwJ1cyfO.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-DKHTTwbg.js → mermaid.core-DnUst0He.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CKUJxwdG.js → mindmap-definition-QFDTVHPH-oc-GwerE.js} +1 -1
- package/payload/server/public/assets/operator-DjK3MBSS.js +1 -0
- package/payload/server/public/assets/{ordinal-CibZU3PM.js → ordinal-v4prQcuq.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-B9U_2639.js +1 -0
- package/payload/server/public/assets/page-DQNYTb2d.js +1 -0
- package/payload/server/public/assets/page-zJQTRXB2.js +32 -0
- package/payload/server/public/assets/pdf-render-NQmflZ7K.js +11 -0
- package/payload/server/public/assets/pdf.worker.min-yatZIOMy.mjs +21 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-Glh-lJOT.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-CRMabeJN.js → pieDiagram-DEJITSTG-BnBtQNHM.js} +1 -1
- package/payload/server/public/assets/public-CHCniosp.js +1 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-DcrlhDuj.js → quadrantDiagram-34T5L4WZ-CkNqtzHI.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-CTSgob8w.js +1 -0
- package/payload/server/public/assets/{reduce-Da0RBva0.js → reduce-0Mm1v1Rr.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-DP231rM_.js → requirementDiagram-MS252O5E-D7qc27KT.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-DPCP-Lmz.js → rotate-ccw-JDfwjfIL.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6--HM5_dC8.js → sankeyDiagram-XADWPNL6-2Y4sqRY7.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BsiVmDR9.js → sequenceDiagram-FGHM5R23-hXOiSHuT.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-Cjdhqdjh.js → stateDiagram-FHFEXIEX-Dq4hhkLA.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DTj9o2vZ.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-Cf5xPmSA.js → timeline-definition-GMOUNBTQ-Dn9wBUEw.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CXApkW1l.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-DzvTXAcT.js +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-BpQ3oKiJ.js +9 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-DqxG4I5C.css → useSubAccountSwitcher-CfI3J2IX.css} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-PCor55Sw.js → vennDiagram-DHZGUBPP-BKVHJiIt.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-BExXV4oI.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-bokCHsim.js → wardleyDiagram-NUSXRM2D-Bn9vvpdK.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-B2YnsUoQ.js → xychartDiagram-5P7HB3ND-FIoMdPB3.js} +1 -1
- package/payload/server/public/browser.html +5 -4
- package/payload/server/public/calendar.html +6 -4
- package/payload/server/public/chat.html +10 -7
- package/payload/server/public/data.html +9 -5
- package/payload/server/public/graph.html +9 -7
- package/payload/server/public/index.html +11 -8
- package/payload/server/public/operator.html +12 -9
- package/payload/server/public/public.html +9 -7
- package/payload/server/server.js +5 -2
- package/payload/server/public/assets/AdminShell-DizHgGBB.js +0 -1
- package/payload/server/public/assets/admin-CSBsndZ4.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Dh5BzVzk.js +0 -1
- package/payload/server/public/assets/calendar-CGlyx54B.js +0 -1
- package/payload/server/public/assets/channel-CFHc3PQ7.js +0 -1
- package/payload/server/public/assets/chat-DT1PbRkD.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-De9_O1hi.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-COwe7oXR.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-wjL65qaU.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-D_EIplVd.js +0 -1
- package/payload/server/public/assets/clone-CEczy_Ef.js +0 -1
- package/payload/server/public/assets/data-BC4KTIX4.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-KHAc7IxG.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-C6nm3sYf.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-DhwCejsr.js +0 -2
- package/payload/server/public/assets/operator-4skjNtEa.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-z-APhyNH.js +0 -1
- package/payload/server/public/assets/page-B0zvkWaG.js +0 -1
- package/payload/server/public/assets/page-BYlBTwqJ.js +0 -32
- package/payload/server/public/assets/pie-ZZUOXDRM-D_HVZjir.js +0 -1
- package/payload/server/public/assets/public-ClYyeEA1.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DtKBIb3o.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-6v4alr7n.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-DbXWs029.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-DUGgqAs9.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-B8FEcQNM.js +0 -9
- package/payload/server/public/assets/wardley-RL74JXVD-Cjn85RON.js +0 -1
- /package/payload/server/public/assets/{_baseFor-BFYpZGkN.js → _baseFor-C0Y1mc3g.js} +0 -0
- /package/payload/server/public/assets/{array-CYkMkqnU.js → array-DqLCdDFv.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-DqlHsaog.js → cytoscape.esm-Cn5b4Cdu.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-Beh6XjaL.js → defaultLocale-CrJzLgRD.js} +0 -0
- /package/payload/server/public/assets/{dist-BMqkacmt.js → dist-Dlks0Bfw.js} +0 -0
- /package/payload/server/public/assets/{init-Rr1s_RiX.js → init-C0r9Gk5G.js} +0 -0
- /package/payload/server/public/assets/{katex-pVJiI_rc.js → katex-BFwFoEts.js} +0 -0
- /package/payload/server/public/assets/{path-BAQ3hXlG.js → path-DIKpVbHL.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-B_l5jfgx.js → preload-helper-C5gCWwwF.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-nHaDi0Kw.js → rough.esm-DsHEmXUZ.js} +0 -0
- /package/payload/server/public/assets/{src-CsEKjNXD.js → src-Bv7ZWAzF.js} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CreatedBy } from "../../../../../lib/graph-write/dist/index.js";
|
|
1
2
|
export interface AccountSummary {
|
|
2
3
|
accountId: string;
|
|
3
4
|
role: string;
|
|
@@ -43,6 +44,53 @@ export declare function provisionClientAccount(opts: {
|
|
|
43
44
|
accountId: string;
|
|
44
45
|
usersFile: string;
|
|
45
46
|
}): string;
|
|
47
|
+
/** Minimal Neo4j session surface the root seed needs — the admin plugin's
|
|
48
|
+
* `getSession()` satisfies it; tests pass an in-memory fake. */
|
|
49
|
+
export interface SeedSessionLike {
|
|
50
|
+
run(query: string, params?: Record<string, unknown>): Promise<{
|
|
51
|
+
records: Array<{
|
|
52
|
+
get(key: string): unknown;
|
|
53
|
+
}>;
|
|
54
|
+
}>;
|
|
55
|
+
}
|
|
56
|
+
/** The provenance the root seed represents — matches the properties the cypher
|
|
57
|
+
* below stamps (`createdByAgent='system'`, `createdBySource='installer'`), the
|
|
58
|
+
* same shape `seed-neo4j.sh` writes for the house root. `agent:"system"` is the
|
|
59
|
+
* convention `checkLabelOriginGate` exempts. The seed runs as raw cypher through
|
|
60
|
+
* the admin session (not the memory-write chokepoint), so the gate never
|
|
61
|
+
* actually evaluates it in production; this constant is not the write's
|
|
62
|
+
* provenance carrier — it exists so a unit test can assert the gate *would*
|
|
63
|
+
* permit this origin. */
|
|
64
|
+
export declare const SEED_ORIGIN: CreatedBy;
|
|
65
|
+
/** Task 1359 — seed a client account's graph root: a bare :LocalBusiness{accountId}
|
|
66
|
+
* plus its owner :AdminUser (fresh per-account userId) joined by :ADMIN_OF, in
|
|
67
|
+
* one statement, then read both node existences back. Without this a created
|
|
68
|
+
* client account is rootless: `checkGraphWriteGate` refuses every non-root
|
|
69
|
+
* write and `checkLabelOriginGate` refuses the agent's attempt to create the
|
|
70
|
+
* root, so the graph is unpopulatable.
|
|
71
|
+
*
|
|
72
|
+
* Raw cypher through the admin session bypasses the memory-write gates by
|
|
73
|
+
* construction (they guard only the memory-write chokepoint), mirroring
|
|
74
|
+
* `seed-neo4j.sh`. The owner AdminUser carries a fresh `ownerUserId` because
|
|
75
|
+
* AdminUser is MERGE-keyed on `userId` and COALESCEs `accountId`; reusing the
|
|
76
|
+
* install owner's userId would keep `accountId=house` and leave the gate's
|
|
77
|
+
* `MATCH (au:AdminUser {accountId: <client>})` empty. Single-shot per account:
|
|
78
|
+
* MERGE on `LocalBusiness.accountId` (a UNIQUE key) makes a replay against the
|
|
79
|
+
* same accountId safe, but it is NOT replay-idempotent under a different
|
|
80
|
+
* `ownerUserId` (that would MERGE a second owner AdminUser); the sole caller
|
|
81
|
+
* mints a new accountId per create and never replays. Returns the two
|
|
82
|
+
* read-back booleans; the caller treats `businessSeeded && adminSeeded` as the
|
|
83
|
+
* verified post-condition. */
|
|
84
|
+
export declare function seedAccountGraphRoot(args: {
|
|
85
|
+
session: SeedSessionLike;
|
|
86
|
+
accountId: string;
|
|
87
|
+
ownerUserId: string;
|
|
88
|
+
ownerName: string;
|
|
89
|
+
createdAt: string;
|
|
90
|
+
}): Promise<{
|
|
91
|
+
businessSeeded: boolean;
|
|
92
|
+
adminSeeded: boolean;
|
|
93
|
+
}>;
|
|
46
94
|
/** Cypher for the account-scoped node count and the irreversible purge. The
|
|
47
95
|
* purge deletes every node carrying the accountId (account scope is a property,
|
|
48
96
|
* never an edge — see CLAUDE.md), detaching its relationships. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-lifecycle.d.ts","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"account-lifecycle.d.ts","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8CAA8C,CAAC;AAI9E,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;oCAEoC;AACpC,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,EAAE,CAoBzE;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEvE;AAED;;+DAE+D;AAC/D,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAE3E;AAED;;;;;+DAK+D;AAC/D,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU7E;AAED;;;;0EAI0E;AAC1E,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAO7F;AAED;;iFAEiF;AACjF,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAO3C;AAED;;;yBAGyB;AACzB,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAoBT;AAED;iEACiE;AACjE,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAClH;AAED;;;;;;;0BAO0B;AAC1B,eAAO,MAAM,WAAW,EAAE,SAAoD,CAAC;AAE/E;;;;;;;;;;;;;;;;;;+BAkB+B;AAC/B,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,cAAc,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAyB7D;AAED;;mEAEmE;AACnE,eAAO,MAAM,yBAAyB,2DAA2D,CAAC;AAClG,eAAO,MAAM,oBAAoB,sDAAsD,CAAC"}
|
|
@@ -109,6 +109,57 @@ export function provisionClientAccount(opts) {
|
|
|
109
109
|
});
|
|
110
110
|
return accountDir;
|
|
111
111
|
}
|
|
112
|
+
/** The provenance the root seed represents — matches the properties the cypher
|
|
113
|
+
* below stamps (`createdByAgent='system'`, `createdBySource='installer'`), the
|
|
114
|
+
* same shape `seed-neo4j.sh` writes for the house root. `agent:"system"` is the
|
|
115
|
+
* convention `checkLabelOriginGate` exempts. The seed runs as raw cypher through
|
|
116
|
+
* the admin session (not the memory-write chokepoint), so the gate never
|
|
117
|
+
* actually evaluates it in production; this constant is not the write's
|
|
118
|
+
* provenance carrier — it exists so a unit test can assert the gate *would*
|
|
119
|
+
* permit this origin. */
|
|
120
|
+
export const SEED_ORIGIN = { agent: "system", source: "installer" };
|
|
121
|
+
/** Task 1359 — seed a client account's graph root: a bare :LocalBusiness{accountId}
|
|
122
|
+
* plus its owner :AdminUser (fresh per-account userId) joined by :ADMIN_OF, in
|
|
123
|
+
* one statement, then read both node existences back. Without this a created
|
|
124
|
+
* client account is rootless: `checkGraphWriteGate` refuses every non-root
|
|
125
|
+
* write and `checkLabelOriginGate` refuses the agent's attempt to create the
|
|
126
|
+
* root, so the graph is unpopulatable.
|
|
127
|
+
*
|
|
128
|
+
* Raw cypher through the admin session bypasses the memory-write gates by
|
|
129
|
+
* construction (they guard only the memory-write chokepoint), mirroring
|
|
130
|
+
* `seed-neo4j.sh`. The owner AdminUser carries a fresh `ownerUserId` because
|
|
131
|
+
* AdminUser is MERGE-keyed on `userId` and COALESCEs `accountId`; reusing the
|
|
132
|
+
* install owner's userId would keep `accountId=house` and leave the gate's
|
|
133
|
+
* `MATCH (au:AdminUser {accountId: <client>})` empty. Single-shot per account:
|
|
134
|
+
* MERGE on `LocalBusiness.accountId` (a UNIQUE key) makes a replay against the
|
|
135
|
+
* same accountId safe, but it is NOT replay-idempotent under a different
|
|
136
|
+
* `ownerUserId` (that would MERGE a second owner AdminUser); the sole caller
|
|
137
|
+
* mints a new accountId per create and never replays. Returns the two
|
|
138
|
+
* read-back booleans; the caller treats `businessSeeded && adminSeeded` as the
|
|
139
|
+
* verified post-condition. */
|
|
140
|
+
export async function seedAccountGraphRoot(args) {
|
|
141
|
+
const { session, accountId, ownerUserId, ownerName, createdAt } = args;
|
|
142
|
+
const res = await session.run(`MERGE (lb:LocalBusiness {accountId: $accountId})
|
|
143
|
+
ON CREATE SET lb.createdBySource = 'installer',
|
|
144
|
+
lb.createdByAgent = 'system',
|
|
145
|
+
lb.createdAt = datetime()
|
|
146
|
+
MERGE (au:AdminUser {userId: $ownerUserId})
|
|
147
|
+
ON CREATE SET au.accountId = $accountId,
|
|
148
|
+
au.role = 'owner',
|
|
149
|
+
au.name = $ownerName,
|
|
150
|
+
au.createdAt = $createdAt
|
|
151
|
+
ON MATCH SET au.accountId = COALESCE(au.accountId, $accountId)
|
|
152
|
+
MERGE (au)-[r:ADMIN_OF]->(lb)
|
|
153
|
+
ON CREATE SET r.role = 'owner', r.grantedAt = $createdAt
|
|
154
|
+
WITH lb.accountId AS acct
|
|
155
|
+
RETURN COUNT { MATCH (b:LocalBusiness {accountId: acct}) } > 0 AS hasBusiness,
|
|
156
|
+
COUNT { MATCH (a:AdminUser {accountId: acct}) } > 0 AS hasAdmin`, { accountId, ownerUserId, ownerName, createdAt });
|
|
157
|
+
const rec = res.records[0];
|
|
158
|
+
return {
|
|
159
|
+
businessSeeded: Boolean(rec?.get("hasBusiness")),
|
|
160
|
+
adminSeeded: Boolean(rec?.get("hasAdmin")),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
112
163
|
/** Cypher for the account-scoped node count and the irreversible purge. The
|
|
113
164
|
* purge deletes every node carrying the accountId (account scope is a property,
|
|
114
165
|
* never an edge — see CLAUDE.md), detaching its relationships. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-lifecycle.js","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,sEAAsE;AACtE,gFAAgF;AAChF,+EAA+E;AAC/E,uEAAuE;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"account-lifecycle.js","sourceRoot":"","sources":["../../src/tools/account-lifecycle.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:6a2a7d9672033ab7af53bc389dfd77c9462949665ee53614374147811c0921ef
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -2553,7 +2553,7 @@ either is a regression.
|
|
|
2553
2553
|
|
|
2554
2554
|
**`/chat` header always shows the account name.** The admin/operator `HeaderMenu` centre always renders the active account's name alongside the brand logo, on every chat surface, regardless of the active conversation — `headerTitle = businessName || brand.productName`. There is no per-conversation header title: the active conversation's label stays in the sidebar and the Conversations flyout (each resolves its own per-session title independently), never in the header. The account name is `LocalBusiness.name`, resolved by `fetchAccountName(accountId)` in `neo4j-store.ts` with **no theme gate** — unlike `fetchBranding` (which returns null unless a colour/logo property is also set, and stays the source for actual theming). The house account is seeded with `LocalBusiness.name = <brand productName>` (`seed-neo4j.sh`, coalesced ON MATCH so a re-seed backfills a name-less node and never clobbers a name the business-profile recorder set later), so the sub-account picker's house row shows the product name (e.g. "SiteDesk"), not the raw accountId UUID. When a name is absent the resolver emits `[branding] account-name-fallback accountId=<id8>… reason=no-name`, and the picker falls back to the accountId; the seed + backfill is the name source. The lite variant header is unchanged.
|
|
2555
2555
|
|
|
2556
|
-
**`/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
|
|
2556
|
+
**`/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/operator reader (`/whatsapp`, `OperatorConversations`) omits the prop and keeps the default `renderTimeline`, so its bubble/DM chrome is unchanged — except that since the attachment-render extraction the default renderer draws inbound `operator-inbound` uploads and `agent-file` deliveries inline (image thumbnail / pdf.js preview / audio player / download chip) via the attachment renderer shared with `/chat`, extracted to `app/chat/attachment-render.tsx` to break the cycle with `transcript-render.tsx`; `renderChatTimeline` is untouched. Both presentations are test-pinned (`app/whatsapp/__tests__/Transcript-*.test.tsx`, `app/chat/__tests__/transcript-render.test.tsx`, `app/chat/__tests__/pdf-preview.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`.
|
|
2557
2557
|
|
|
2558
2558
|
**`/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).
|
|
2559
2559
|
|
|
@@ -2803,13 +2803,19 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
2803
2803
|
> **Admin variant only.** Every surface below belongs to the `admin` shell
|
|
2804
2804
|
> variant. The **operator** variant (`operator.<host>`) mounts no sidebar at
|
|
2805
2805
|
> all: its dashboard is the persistent admin webchat at `/` and the data
|
|
2806
|
-
> browser at `/data`, plus a
|
|
2807
|
-
>
|
|
2808
|
-
>
|
|
2809
|
-
> the
|
|
2810
|
-
>
|
|
2811
|
-
>
|
|
2812
|
-
>
|
|
2806
|
+
> browser at `/data`, plus a **Conversations** item reached from a dynamic
|
|
2807
|
+
> burger item — header order Chat → Conversations → Data → Log out, in a
|
|
2808
|
+
> single-column `.platform.platform-operator` grid. On the operator **chat**
|
|
2809
|
+
> surface the Conversations item opens the same in-chat session
|
|
2810
|
+
> switcher the admin `/chat` surface uses (`ChatSurface`'s `SessionList` flyout):
|
|
2811
|
+
> the operator's own admin webchat conversations, newest-first, each resuming
|
|
2812
|
+
> live via `/chat?session=<id>`, with rename / archive / delete / end / new — not
|
|
2813
|
+
> a read-only reader. The item gates on the flyout handler being wired, exactly
|
|
2814
|
+
> like the admin and lite items. The read-only inbound-channel **Conversations**
|
|
2815
|
+
> reader (WhatsApp, Telegram, public webchat, via the admin-gated
|
|
2816
|
+
> `/api/whatsapp-reader/conversations` route) remains on `/data`'s Conversations
|
|
2817
|
+
> item and in the admin Sidebar; it is no longer reachable from the operator chat
|
|
2818
|
+
> surface. The operator surface is documented in full in the internal doc
|
|
2813
2819
|
> `maxy-code/.docs/operator-dashboard.md`.
|
|
2814
2820
|
>
|
|
2815
2821
|
> **Operator `/chat`.** The reduced operator chrome renders on
|
|
@@ -2819,6 +2825,17 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
2819
2825
|
> operator shell. Unlike the operator root — which forces the canonical session
|
|
2820
2826
|
> — operator `/chat` honors `?session=`, opening a specific session inside the
|
|
2821
2827
|
> reduced shell. Admin-host `/chat` is unchanged (full developer shell).
|
|
2828
|
+
> **Sub-account switch resets the session target.** Because `/chat` honors
|
|
2829
|
+
> `?session=`, the header sub-account switcher (`useSubAccountSwitcher`) drops
|
|
2830
|
+
> the `session` query param when it navigates after a switch, rather than
|
|
2831
|
+
> reloading verbatim. A verbatim reload would keep the *previous* account's
|
|
2832
|
+
> `?session=<id>`, which the reloaded surface re-pins — stranding the operator
|
|
2833
|
+
> on the wrong account's session . Dropping the param lands on the
|
|
2834
|
+
> account-scoped canonical surface, so `resolveCanonical` resolves the switched
|
|
2835
|
+
> account's latest owned session, or the new-session splash when it has none.
|
|
2836
|
+
> The pathname is preserved (a switch on `/chat` stays on `/chat`); the operator
|
|
2837
|
+
> root `/` and bare `/chat` were always correct (they force-ignore / carry no
|
|
2838
|
+
> `?session=`).
|
|
2822
2839
|
>
|
|
2823
2840
|
> **Width.** The operator header, the operator chat column, and the `/data`
|
|
2824
2841
|
> header+toolbar+body all render in one centred column bounded to
|
|
@@ -2833,15 +2850,17 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
2833
2850
|
> surface; the rest of the admin shell stays edge-to-edge. The public webchat
|
|
2834
2851
|
> (`.public-surface`) inherits the same value.
|
|
2835
2852
|
>
|
|
2836
|
-
> **Conversations item.** The dynamic Conversations burger item
|
|
2837
|
-
>
|
|
2838
|
-
>
|
|
2839
|
-
>
|
|
2840
|
-
>
|
|
2841
|
-
>
|
|
2842
|
-
>
|
|
2843
|
-
>
|
|
2844
|
-
>
|
|
2853
|
+
> **Conversations item.** The dynamic Conversations burger item gates on the
|
|
2854
|
+
> host wiring its open-handler (like the admin and lite items), not on a channel
|
|
2855
|
+
> count. On the operator **chat** surface the handler is always wired, so the
|
|
2856
|
+
> item is always present and opens the live session switcher (above). On `/data`
|
|
2857
|
+
> the handler is wired only when ≥1 reviewable inbound conversation exists
|
|
2858
|
+
> (fetched from `/whatsapp-reader/conversations` with the operator's session
|
|
2859
|
+
> key), and selecting it renders the read-only reader in place (the file browser
|
|
2860
|
+
> is swapped for it; Back returns) — so `/data` keeps its hide-when-empty
|
|
2861
|
+
> behaviour. The flyout-open signal `[operator-ui] op=conversations-menu
|
|
2862
|
+
> present=<bool>` fires for the operator variant on both surfaces; `present=false`
|
|
2863
|
+
> while the operator expects the item is the handler-not-wired signature.
|
|
2845
2864
|
|
|
2846
2865
|
| Surface | What it does | Backed by |
|
|
2847
2866
|
|---|---|---|
|
|
@@ -33,15 +33,18 @@ If the site captures form data, add the `[[d1_databases]]` binding from `d1-data
|
|
|
33
33
|
|
|
34
34
|
## 3. Load the reused deploy token
|
|
35
35
|
|
|
36
|
-
A Pages deploy needs **Account · Cloudflare Pages · Edit**. If the site has Pages Functions hitting D1, the token also needs **Account · D1 · Edit** (the single most common breakage — see `d1-data-capture.md`).
|
|
36
|
+
A Pages deploy needs **Account · Cloudflare Pages · Edit**. If the site has Pages Functions hitting D1, the token also needs **Account · D1 · Edit** (the single most common breakage — see `d1-data-capture.md`). The `pages` scope resolves the stable `<brand>-pages-d1` token, which carries both. Resolve it with the deterministic helper, which mints it once only if absent and reuses it thereafter. This is the one command; do not source the secrets file and hand the resulting `CLOUDFLARE_API_TOKEN` to `wrangler` — that variable holds the **master**, not the per-scope token, and `wrangler pages` then returns `Authentication error [code: 10000]`:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
+
KEY=$(bash platform/plugins/cloudflare/bin/cf-token.sh pages "${SECRETS_DIR}/cloudflare.env")
|
|
39
40
|
set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
|
|
40
|
-
: "${
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
: "${CLOUDFLARE_ACCOUNT_ID:?account id not loaded}"
|
|
42
|
+
PAGES_D1="${!KEY}" # the active Pages(+D1) per-scope token — NOT the sourced CLOUDFLARE_API_TOKEN (that is the master)
|
|
43
|
+
: "${PAGES_D1:?per-scope token not resolved — re-run cf-token.sh and read its output}"
|
|
43
44
|
```
|
|
44
45
|
|
|
46
|
+
A `10000` here is a token-selection symptom, not a permissions wall: it means the master reached `wrangler`. Re-run the helper line and retry with `${PAGES_D1}`; never fall back to a manual step on the strength of a `10000`. The mint-once, reuse, prefix-routing, and redaction details live in `api.md` § Provisioning and reusing a stable per-scope token; this command is self-contained and does not depend on reading that section first.
|
|
47
|
+
|
|
45
48
|
## 4. Deploy
|
|
46
49
|
|
|
47
50
|
```bash
|
|
@@ -9,7 +9,9 @@ Run by the admin/main session, the operator-infra surface that holds the `Bash`
|
|
|
9
9
|
|
|
10
10
|
This is the entry point for every Cloudflare task on the install. Pick the operation class, read the matching reference, run the command via Bash, relay the literal output, and prove the outcome with a live signal. There are no Cloudflare MCP tools; the plugin registers none. The agent never browser-automates the dashboard — the operator clicks where a click is genuinely required.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**How to open every `references/…` file named in this skill.** These reference files live at the plugin root (`platform/plugins/cloudflare/references/`), one directory above this skill. They are not children of the directory this skill announces as its base. Read each one with the `plugin-read` tool, which resolves the path plugin-root-relative: `plugin-read` with `file="references/api.md"` returns the file. A plain `Read` of `references/api.md` against this skill's announced base directory resolves to `.../skills/cloudflare/references/api.md`, which does not exist and returns "File does not exist". That miss is not a reason to skip the reference, to substitute a non-canonical file, or to improvise the command from memory. Use `plugin-read` for every `references/…` mention below, the same way the sibling `site-deploy` and `calendar-site` skills do.
|
|
13
|
+
|
|
14
|
+
| You want to… | Read via `plugin-read` | Outcome contract |
|
|
13
15
|
|---|---|---|
|
|
14
16
|
| Stand up / diagnose / reset a tunnel so a hostname is reachable | `references/manual-setup.md` | external `curl -I https://<hostname>` → `200` |
|
|
15
17
|
| Switch accounts, edit an apex CNAME by hand, or other dashboard-only clicks | `references/dashboard-guide.md` | the click-path completed in the operator's browser |
|
|
@@ -27,19 +29,20 @@ This is the entry point for every Cloudflare task on the install. Pick the opera
|
|
|
27
29
|
|
|
28
30
|
Neither the master token nor any per-scope token is ever written into a project tree, committed, or echoed into chat — the per-scope tokens persist only in the account-scoped secrets file, never in a deployable project tree.
|
|
29
31
|
|
|
30
|
-
###
|
|
32
|
+
### Resolve the per-scope token before any `wrangler` / API call (binding)
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
This binding governs **every** Pages, D1, DNS, or Access operation, not only calls you think of as "token operations." Before the first `wrangler pages …`, `wrangler d1 …`, or Cloudflare-API `curl` of a task, resolve the per-scope token with the deterministic helper and pass **that** token to the command:
|
|
33
35
|
|
|
34
36
|
```bash
|
|
35
37
|
KEY=$(bash platform/plugins/cloudflare/bin/cf-token.sh <scope> <secrets-file>) # scope: pages | d1 | dns | access
|
|
36
38
|
set -a; . <secrets-file>; set +a
|
|
37
|
-
# the active, persisted per-scope token
|
|
39
|
+
# ${!KEY} now holds the active, persisted per-scope token. Pass it explicitly:
|
|
40
|
+
CLOUDFLARE_API_TOKEN="${!KEY}" wrangler pages project list
|
|
38
41
|
```
|
|
39
42
|
|
|
40
|
-
The
|
|
43
|
+
**The antipattern this exists to stop.** Sourcing the secrets file loads the **master** token into `CLOUDFLARE_API_TOKEN`. If you then run `wrangler pages …` (or a Cloudflare-API `curl`) with the environment as-sourced, you hand the **master** to the tool, not the per-scope token. The master is the wrong token for these endpoints, so Cloudflare rejects the call, and the rejection reads like a permissions wall when it is really a token-selection mistake. `. <secrets-file>` immediately followed by `wrangler pages …` is the exact sequence that trips this; the helper line above is what prevents it. Never let `CLOUDFLARE_API_TOKEN` reach `wrangler`/`curl` still holding the sourced master.
|
|
41
44
|
|
|
42
|
-
A
|
|
45
|
+
**A `10000` / `Invalid API Token` / `9109` from a `wrangler` or API call is a token-selection symptom, never "this token lacks permission."** The recovery is always the same: run `cf-token.sh <scope> <secrets-file>` and re-issue the command with the resolved per-scope token. Concretely: a just-sourced master handed to `wrangler pages …` returns `Authentication error [code: 10000]`; a `cfat_` master at `/user/tokens/verify` returns `Invalid API Token`; a `cfut_` master at any `/accounts/{id}/...` endpoint returns `9109`. None of these means "cannot deploy" or "lacks Pages/DNS permission." Do not fall back to a manual DNS record, a tunnel workaround, or an operator ask on the strength of one of these codes — run the helper and retry. The helper reads the master's `cfat_`/`cfut_` prefix, routes verify/`permission_groups`/mint to the matching endpoint family, 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; it never prints a token value. Doing verify or mint by hand is permitted only after reading the `references/api.md` section on routing token endpoints by prefix, and routing by the established prefix.
|
|
43
46
|
|
|
44
47
|
## Outcome contracts — what "done" means
|
|
45
48
|
|
|
@@ -73,7 +73,7 @@ either is a regression.
|
|
|
73
73
|
|
|
74
74
|
**`/chat` header always shows the account name.** The admin/operator `HeaderMenu` centre always renders the active account's name alongside the brand logo, on every chat surface, regardless of the active conversation — `headerTitle = businessName || brand.productName`. There is no per-conversation header title: the active conversation's label stays in the sidebar and the Conversations flyout (each resolves its own per-session title independently), never in the header. The account name is `LocalBusiness.name`, resolved by `fetchAccountName(accountId)` in `neo4j-store.ts` with **no theme gate** — unlike `fetchBranding` (which returns null unless a colour/logo property is also set, and stays the source for actual theming). The house account is seeded with `LocalBusiness.name = <brand productName>` (`seed-neo4j.sh`, coalesced ON MATCH so a re-seed backfills a name-less node and never clobbers a name the business-profile recorder set later), so the sub-account picker's house row shows the product name (e.g. "SiteDesk"), not the raw accountId UUID. When a name is absent the resolver emits `[branding] account-name-fallback accountId=<id8>… reason=no-name`, and the picker falls back to the accountId; the seed + backfill is the name source. The lite variant header is unchanged.
|
|
75
75
|
|
|
76
|
-
**`/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
|
|
76
|
+
**`/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/operator reader (`/whatsapp`, `OperatorConversations`) omits the prop and keeps the default `renderTimeline`, so its bubble/DM chrome is unchanged — except that since the attachment-render extraction the default renderer draws inbound `operator-inbound` uploads and `agent-file` deliveries inline (image thumbnail / pdf.js preview / audio player / download chip) via the attachment renderer shared with `/chat`, extracted to `app/chat/attachment-render.tsx` to break the cycle with `transcript-render.tsx`; `renderChatTimeline` is untouched. Both presentations are test-pinned (`app/whatsapp/__tests__/Transcript-*.test.tsx`, `app/chat/__tests__/transcript-render.test.tsx`, `app/chat/__tests__/pdf-preview.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`.
|
|
77
77
|
|
|
78
78
|
**`/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).
|
|
79
79
|
|
|
@@ -323,13 +323,19 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
323
323
|
> **Admin variant only.** Every surface below belongs to the `admin` shell
|
|
324
324
|
> variant. The **operator** variant (`operator.<host>`) mounts no sidebar at
|
|
325
325
|
> all: its dashboard is the persistent admin webchat at `/` and the data
|
|
326
|
-
> browser at `/data`, plus a
|
|
327
|
-
>
|
|
328
|
-
>
|
|
329
|
-
> the
|
|
330
|
-
>
|
|
331
|
-
>
|
|
332
|
-
>
|
|
326
|
+
> browser at `/data`, plus a **Conversations** item reached from a dynamic
|
|
327
|
+
> burger item — header order Chat → Conversations → Data → Log out, in a
|
|
328
|
+
> single-column `.platform.platform-operator` grid. On the operator **chat**
|
|
329
|
+
> surface the Conversations item opens the same in-chat session
|
|
330
|
+
> switcher the admin `/chat` surface uses (`ChatSurface`'s `SessionList` flyout):
|
|
331
|
+
> the operator's own admin webchat conversations, newest-first, each resuming
|
|
332
|
+
> live via `/chat?session=<id>`, with rename / archive / delete / end / new — not
|
|
333
|
+
> a read-only reader. The item gates on the flyout handler being wired, exactly
|
|
334
|
+
> like the admin and lite items. The read-only inbound-channel **Conversations**
|
|
335
|
+
> reader (WhatsApp, Telegram, public webchat, via the admin-gated
|
|
336
|
+
> `/api/whatsapp-reader/conversations` route) remains on `/data`'s Conversations
|
|
337
|
+
> item and in the admin Sidebar; it is no longer reachable from the operator chat
|
|
338
|
+
> surface. The operator surface is documented in full in the internal doc
|
|
333
339
|
> `maxy-code/.docs/operator-dashboard.md`.
|
|
334
340
|
>
|
|
335
341
|
> **Operator `/chat`.** The reduced operator chrome renders on
|
|
@@ -339,6 +345,17 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
339
345
|
> operator shell. Unlike the operator root — which forces the canonical session
|
|
340
346
|
> — operator `/chat` honors `?session=`, opening a specific session inside the
|
|
341
347
|
> reduced shell. Admin-host `/chat` is unchanged (full developer shell).
|
|
348
|
+
> **Sub-account switch resets the session target.** Because `/chat` honors
|
|
349
|
+
> `?session=`, the header sub-account switcher (`useSubAccountSwitcher`) drops
|
|
350
|
+
> the `session` query param when it navigates after a switch, rather than
|
|
351
|
+
> reloading verbatim. A verbatim reload would keep the *previous* account's
|
|
352
|
+
> `?session=<id>`, which the reloaded surface re-pins — stranding the operator
|
|
353
|
+
> on the wrong account's session . Dropping the param lands on the
|
|
354
|
+
> account-scoped canonical surface, so `resolveCanonical` resolves the switched
|
|
355
|
+
> account's latest owned session, or the new-session splash when it has none.
|
|
356
|
+
> The pathname is preserved (a switch on `/chat` stays on `/chat`); the operator
|
|
357
|
+
> root `/` and bare `/chat` were always correct (they force-ignore / carry no
|
|
358
|
+
> `?session=`).
|
|
342
359
|
>
|
|
343
360
|
> **Width.** The operator header, the operator chat column, and the `/data`
|
|
344
361
|
> header+toolbar+body all render in one centred column bounded to
|
|
@@ -353,15 +370,17 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
353
370
|
> surface; the rest of the admin shell stays edge-to-edge. The public webchat
|
|
354
371
|
> (`.public-surface`) inherits the same value.
|
|
355
372
|
>
|
|
356
|
-
> **Conversations item.** The dynamic Conversations burger item
|
|
357
|
-
>
|
|
358
|
-
>
|
|
359
|
-
>
|
|
360
|
-
>
|
|
361
|
-
>
|
|
362
|
-
>
|
|
363
|
-
>
|
|
364
|
-
>
|
|
373
|
+
> **Conversations item.** The dynamic Conversations burger item gates on the
|
|
374
|
+
> host wiring its open-handler (like the admin and lite items), not on a channel
|
|
375
|
+
> count. On the operator **chat** surface the handler is always wired, so the
|
|
376
|
+
> item is always present and opens the live session switcher (above). On `/data`
|
|
377
|
+
> the handler is wired only when ≥1 reviewable inbound conversation exists
|
|
378
|
+
> (fetched from `/whatsapp-reader/conversations` with the operator's session
|
|
379
|
+
> key), and selecting it renders the read-only reader in place (the file browser
|
|
380
|
+
> is swapped for it; Back returns) — so `/data` keeps its hide-when-empty
|
|
381
|
+
> behaviour. The flyout-open signal `[operator-ui] op=conversations-menu
|
|
382
|
+
> present=<bool>` fires for the operator variant on both surfaces; `present=false`
|
|
383
|
+
> while the operator expects the item is the handler-not-wired signature.
|
|
365
384
|
|
|
366
385
|
| Surface | What it does | Backed by |
|
|
367
386
|
|---|---|---|
|
|
@@ -21,7 +21,7 @@ Activate when the operator asks for any of:
|
|
|
21
21
|
Do **not** activate for:
|
|
22
22
|
|
|
23
23
|
- Listing or searching emails as the end-goal (no draft intent) — let `email-search` / `email-graph-query` answer directly.
|
|
24
|
-
- Setup, status checks, OTP extraction — those are direct tool calls. (For Google/Workspace setup the agent must require an app password, never the regular password — see the email plugin's `references/email-reference.md` § Setup → Google. For Microsoft / Microsoft 365 addresses there is no email-plugin path at all — register via the `outlook` Graph plugin's `outlook-account-register`, not `email-setup` — see § Setup → Microsoft.)
|
|
24
|
+
- Setup, status checks, OTP extraction — those are direct tool calls. (For Google/Workspace setup the agent must require an app password, never the regular password — see the email plugin's `references/email-reference.md` § Setup → Google, opened with the `plugin-read` tool (`file="references/email-reference.md"`), which resolves it at the plugin root; a plain `Read` against this skill's base directory misses. For Microsoft / Microsoft 365 addresses there is no email-plugin path at all — register via the `outlook` Graph plugin's `outlook-account-register`, not `email-setup` — see § Setup → Microsoft.)
|
|
25
25
|
- Any automated send loop. If the operator asks for drip / scheduled / broadcast sends, decline and explain that this skill is human-in-the-loop only.
|
|
26
26
|
|
|
27
27
|
## Flow — reply to an existing thread
|
|
@@ -63,5 +63,7 @@ Subsequent tool calls use the persisted refresh token to mint access tokens tran
|
|
|
63
63
|
|
|
64
64
|
## Reference files
|
|
65
65
|
|
|
66
|
+
Open each with the `plugin-read` tool (`file="references/auth.md"`), which resolves the path at the plugin root. A plain `Read` of `references/auth.md` against this skill's base directory misses, because these files live one level up at the plugin root, not under the skill.
|
|
67
|
+
|
|
66
68
|
- [`references/auth.md`](references/auth.md) — Entra app registration + PKCE flow + re-vendor procedure
|
|
67
69
|
- [`references/graph-surfaces.md`](references/graph-surfaces.md) — Graph endpoint and response shape per tool
|
|
@@ -22,6 +22,8 @@ A finished Telegram setup has all of these, and the skill is not done until each
|
|
|
22
22
|
|
|
23
23
|
## Flow
|
|
24
24
|
|
|
25
|
+
Read `references/setup-guide.md` (cited throughout this flow) with the `plugin-read` tool (`file="references/setup-guide.md"`), which resolves it at the plugin root. A plain `Read` against this skill's base directory misses, because the file lives one level up at the plugin root, not under the skill.
|
|
26
|
+
|
|
25
27
|
### Step 1 — Get the bot token
|
|
26
28
|
|
|
27
29
|
Follow `references/setup-guide.md` "Creating a Bot": the operator creates the bot via @BotFather and gives you the token (e.g. `123456:ABC-DEF…`). Confirm you have it before continuing.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Task 1362 — the reference-reachability guard.
|
|
3
|
+
# A SKILL.md's plugin-root-only bare `references/…` citation is reachable ONLY
|
|
4
|
+
# when the SKILL.md declares the `plugin-read` convention. Without it, a plain
|
|
5
|
+
# Read against the announced skill base dir misses, so the citation is a
|
|
6
|
+
# violation. Skill-local references, full `plugins/<name>/references/…` paths,
|
|
7
|
+
# and PLUGIN.md plugin-root references are unaffected.
|
|
8
|
+
set -u
|
|
9
|
+
GATE="$(cd "$(dirname "$0")/.." && pwd)/check-plugin-references.mjs"
|
|
10
|
+
[[ -f "$GATE" ]] || { echo "FAIL: gate script missing at $GATE" >&2; exit 1; }
|
|
11
|
+
PASS=0; FAIL=0
|
|
12
|
+
|
|
13
|
+
ROOT=$(mktemp -d)
|
|
14
|
+
PREMIUM="$ROOT/premium-empty"
|
|
15
|
+
mkdir -p "$PREMIUM"
|
|
16
|
+
trap 'rm -rf "$ROOT"' EXIT
|
|
17
|
+
|
|
18
|
+
reset_root() { rm -rf "$ROOT/platform"; }
|
|
19
|
+
|
|
20
|
+
# Write a SKILL.md for plugin "$1" (skill slug == plugin name) with body "$2".
|
|
21
|
+
make_skill() {
|
|
22
|
+
local plugin="$1" body="$2"
|
|
23
|
+
local dir="$ROOT/platform/plugins/$plugin/skills/$plugin"
|
|
24
|
+
mkdir -p "$dir"
|
|
25
|
+
{ printf -- '---\nname: %s\ndescription: test skill\n---\n\n' "$plugin"; printf '%s\n' "$body"; } > "$dir/SKILL.md"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Create a reference file at an arbitrary path under $ROOT.
|
|
29
|
+
make_ref() { local rel="$1"; mkdir -p "$ROOT/$(dirname "$rel")"; printf 'ref body\n' > "$ROOT/$rel"; }
|
|
30
|
+
|
|
31
|
+
check() {
|
|
32
|
+
local desc="$1" expected="$2"
|
|
33
|
+
node "$GATE" "$ROOT/platform" "$PREMIUM" >/dev/null 2>&1
|
|
34
|
+
local got=$?
|
|
35
|
+
if [[ "$got" -eq "$expected" ]]; then echo "PASS: $desc"; ((PASS++))
|
|
36
|
+
else echo "FAIL: $desc (expected exit $expected, got $got)"; ((FAIL++)); fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# CASE 1: plugin-root-only ref, NO plugin-read declared → violation.
|
|
40
|
+
reset_root
|
|
41
|
+
make_ref "platform/plugins/foo/references/api.md"
|
|
42
|
+
make_skill "foo" 'Read \`references/api.md\` before the API call.'
|
|
43
|
+
check "plugin-root-only ref without plugin-read fails" 1
|
|
44
|
+
|
|
45
|
+
# CASE 2: same ref, plugin-read declared → passes.
|
|
46
|
+
reset_root
|
|
47
|
+
make_ref "platform/plugins/foo/references/api.md"
|
|
48
|
+
make_skill "foo" 'Open \`references/api.md\` with the \`plugin-read\` tool before the API call.'
|
|
49
|
+
check "plugin-root-only ref with plugin-read passes" 0
|
|
50
|
+
|
|
51
|
+
# CASE 3: skill-local ref, NO plugin-read → passes (plain Read resolves it).
|
|
52
|
+
reset_root
|
|
53
|
+
make_ref "platform/plugins/foo/skills/foo/references/local.md"
|
|
54
|
+
make_skill "foo" 'Read \`references/local.md\` for the details.'
|
|
55
|
+
check "skill-local ref without plugin-read passes" 0
|
|
56
|
+
|
|
57
|
+
# CASE 4: full plugins/<name>/references/... path, NO plugin-read → passes.
|
|
58
|
+
reset_root
|
|
59
|
+
make_ref "platform/plugins/foo/references/api.md"
|
|
60
|
+
make_skill "foo" 'Read \`plugins/foo/references/api.md\` before the API call.'
|
|
61
|
+
check "full plugin-path ref without plugin-read passes" 0
|
|
62
|
+
|
|
63
|
+
# CASE 5: genuinely missing ref (neither skill-local nor plugin-root) → violation.
|
|
64
|
+
reset_root
|
|
65
|
+
make_skill "foo" 'Open \`references/ghost.md\` with \`plugin-read\`.'
|
|
66
|
+
check "missing ref fails even with plugin-read" 1
|
|
67
|
+
|
|
68
|
+
# CASE 6: a MISSING full plugins/<name>/references/... path from a SKILL.md without
|
|
69
|
+
# plugin-read → violation, and the plugin-root "declare plugin-read" hint must NOT
|
|
70
|
+
# fire (the hint is scoped to the bare references/ shape only; a full path is not
|
|
71
|
+
# reachability-limited by the missing convention). Guards the Task 1362 hint-scope fix.
|
|
72
|
+
reset_root
|
|
73
|
+
make_skill "foo" 'Read \`plugins/foo/references/ghost.md\` before the API call.'
|
|
74
|
+
OUT=$(node "$GATE" "$ROOT/platform" "$PREMIUM" 2>&1); GOT=$?
|
|
75
|
+
if [[ "$GOT" -eq 1 ]] && ! grep -q "exists at plugin root" <<<"$OUT"; then
|
|
76
|
+
echo "PASS: missing full-path ref fails without a false plugin-read hint"; ((PASS++))
|
|
77
|
+
else
|
|
78
|
+
echo "FAIL: missing full-path ref (exit=$GOT, hint present=$(grep -qc 'exists at plugin root' <<<"$OUT" && echo yes || echo no))"; ((FAIL++))
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
echo ""
|
|
82
|
+
echo "Results: $PASS passed, $FAIL failed"
|
|
83
|
+
[[ "$FAIL" -eq 0 ]] && exit 0 || exit 1
|