@rubytech/create-maxy-code 0.1.383 → 0.1.384
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 +25 -17
- 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 +24 -16
- 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-C_h8V0Xs.js} +1 -1
- package/payload/server/public/assets/AdminShell-Dqnxpn7U.js +1 -0
- package/payload/server/public/assets/{Checkbox-C-dvkjKb.js → Checkbox-Bl1WRVGb.js} +1 -1
- package/payload/server/public/assets/Transcript-C93Lifnb.js +1 -0
- package/payload/server/public/assets/admin-ByoqNaSf.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-BXY1sNxv.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-VByNuobb.js → c4Diagram-AHTNJAMY-pfUVHdmy.js} +1 -1
- package/payload/server/public/assets/calendar-CObRTPpU.js +1 -0
- package/payload/server/public/assets/channel-Da-UUyqx.js +1 -0
- package/payload/server/public/assets/chat-Dq-VHp7a.js +1 -0
- package/payload/server/public/assets/chevron-left-DjbK_wdQ.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-BXOoK8Ci.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-BCL0fVDH.js} +3 -3
- package/payload/server/public/assets/{graph-labels-C7bJMcXs.js → graph-labels-C0nn9nQi.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-B_1qYO7m.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-BQEl7aZO.js +1 -0
- package/payload/server/public/assets/page-Dck23z9U.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-BzvwPHM0.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-CHep9vl5.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-B1Kf32uG.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 +8 -5
- package/payload/server/public/graph.html +9 -7
- package/payload/server/public/index.html +10 -8
- package/payload/server/public/operator.html +12 -9
- package/payload/server/public/public.html +9 -7
- 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":"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;AAG1C,MAAM,OAAO,GAAG,iEAAiE,CAAC;AAQlF;;oCAEoC;AACpC,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACrD,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAuB,CAAC;YAChF,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QACjE,CAAC;QAAC,MAAM,CAAC;YACP,8DAA8D;QAChE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,WAAmB,EAAE,EAAU;IAC5D,OAAO,mBAAmB,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;AACvF,CAAC;AAED;;+DAE+D;AAC/D,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,OAAO,mBAAmB,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;AACzE,CAAC;AAED;;;;;+DAK+D;AAC/D,MAAM,UAAU,0BAA0B,CAAC,WAAmB;IAC5D,MAAM,KAAK,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACzE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAA0C,CAAC;QACnG,OAAO,OAAO,GAAG,CAAC,uBAAuB,KAAK,QAAQ,IAAI,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7H,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;0EAI0E;AAC1E,MAAM,UAAU,0BAA0B,CAAC,WAAmB,EAAE,eAAuB;IACrF,MAAM,KAAK,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IAClF,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACzE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAA4B,CAAC;IACrF,GAAG,CAAC,uBAAuB,GAAG,eAAe,CAAC;IAC9C,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;iFAEiF;AACjF,MAAM,UAAU,iBAAiB,CAC/B,WAAmB,EACnB,EAAU,EACV,EAAU;IAEV,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC9C,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACjD,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC/B,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;AACxD,CAAC;AAED;;;yBAGyB;AACzB,MAAM,UAAU,sBAAsB,CAAC,IAKtC;IACC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1D,YAAY,CACV,MAAM,EACN,CAAC,IAAI,EAAE,mGAAmG,CAAC,EAC3G;QACE,GAAG,EAAE;YACH,GAAG,OAAO,CAAC,GAAG;YACd,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC;YACnD,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC;YAC9C,UAAU,EAAE,IAAI,CAAC,SAAS;YAC1B,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,IAAI,CAAC,SAAS;SACxB;QACD,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;KAClC,CACF,CAAC;IACF,OAAO,UAAU,CAAC;AACpB,CAAC;AAQD;;;;;;;0BAO0B;AAC1B,MAAM,CAAC,MAAM,WAAW,GAAc,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AAE/E;;;;;;;;;;;;;;;;;;+BAkB+B;AAC/B,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAM1C;IACC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IACvE,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAC3B;;;;;;;;;;;;;;4EAcwE,EACxE,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,CACjD,CAAC;IACF,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO;QACL,cAAc,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;QAChD,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;KAC3C,CAAC;AACJ,CAAC;AAED;;mEAEmE;AACnE,MAAM,CAAC,MAAM,yBAAyB,GAAG,wDAAwD,CAAC;AAClG,MAAM,CAAC,MAAM,oBAAoB,GAAG,mDAAmD,CAAC"}
|
|
@@ -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:6ab87c68af715d29cae2a3fa1273c434fb8c88cdbe96eb86e0c7216895625085
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -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
|
|
@@ -2833,15 +2839,17 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
2833
2839
|
> surface; the rest of the admin shell stays edge-to-edge. The public webchat
|
|
2834
2840
|
> (`.public-surface`) inherits the same value.
|
|
2835
2841
|
>
|
|
2836
|
-
> **Conversations item.** The dynamic Conversations burger item
|
|
2837
|
-
>
|
|
2838
|
-
>
|
|
2839
|
-
>
|
|
2840
|
-
>
|
|
2841
|
-
>
|
|
2842
|
-
>
|
|
2843
|
-
>
|
|
2844
|
-
>
|
|
2842
|
+
> **Conversations item.** The dynamic Conversations burger item gates on the
|
|
2843
|
+
> host wiring its open-handler (like the admin and lite items), not on a channel
|
|
2844
|
+
> count. On the operator **chat** surface the handler is always wired, so the
|
|
2845
|
+
> item is always present and opens the live session switcher (above). On `/data`
|
|
2846
|
+
> the handler is wired only when ≥1 reviewable inbound conversation exists
|
|
2847
|
+
> (fetched from `/whatsapp-reader/conversations` with the operator's session
|
|
2848
|
+
> key), and selecting it renders the read-only reader in place (the file browser
|
|
2849
|
+
> is swapped for it; Back returns) — so `/data` keeps its hide-when-empty
|
|
2850
|
+
> behaviour. The flyout-open signal `[operator-ui] op=conversations-menu
|
|
2851
|
+
> present=<bool>` fires for the operator variant on both surfaces; `present=false`
|
|
2852
|
+
> while the operator expects the item is the handler-not-wired signature.
|
|
2845
2853
|
|
|
2846
2854
|
| Surface | What it does | Backed by |
|
|
2847
2855
|
|---|---|---|
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -353,15 +359,17 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
353
359
|
> surface; the rest of the admin shell stays edge-to-edge. The public webchat
|
|
354
360
|
> (`.public-surface`) inherits the same value.
|
|
355
361
|
>
|
|
356
|
-
> **Conversations item.** The dynamic Conversations burger item
|
|
357
|
-
>
|
|
358
|
-
>
|
|
359
|
-
>
|
|
360
|
-
>
|
|
361
|
-
>
|
|
362
|
-
>
|
|
363
|
-
>
|
|
364
|
-
>
|
|
362
|
+
> **Conversations item.** The dynamic Conversations burger item gates on the
|
|
363
|
+
> host wiring its open-handler (like the admin and lite items), not on a channel
|
|
364
|
+
> count. On the operator **chat** surface the handler is always wired, so the
|
|
365
|
+
> item is always present and opens the live session switcher (above). On `/data`
|
|
366
|
+
> the handler is wired only when ≥1 reviewable inbound conversation exists
|
|
367
|
+
> (fetched from `/whatsapp-reader/conversations` with the operator's session
|
|
368
|
+
> key), and selecting it renders the read-only reader in place (the file browser
|
|
369
|
+
> is swapped for it; Back returns) — so `/data` keeps its hide-when-empty
|
|
370
|
+
> behaviour. The flyout-open signal `[operator-ui] op=conversations-menu
|
|
371
|
+
> present=<bool>` fires for the operator variant on both surfaces; `present=false`
|
|
372
|
+
> while the operator expects the item is the handler-not-wired signature.
|
|
365
373
|
|
|
366
374
|
| Surface | What it does | Backed by |
|
|
367
375
|
|---|---|---|
|
|
@@ -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
|
|
@@ -17,6 +17,21 @@
|
|
|
17
17
|
// 2. `plugins/<name>/references/<file>.md` (optionally prefixed by
|
|
18
18
|
// `platform/`) — resolves at `<platformRoot>/plugins/<name>/references/
|
|
19
19
|
// <file>.md`.
|
|
20
|
+
//
|
|
21
|
+
// Task 1362 — access-method tightening for SKILL.md bare references.
|
|
22
|
+
// A SKILL.md announces its own skill directory as the base dir. An agent that
|
|
23
|
+
// reads "references/foo.md" with a plain file `Read` resolves it against that
|
|
24
|
+
// base dir → `<skill-dir>/references/foo.md`. When the reference actually lives
|
|
25
|
+
// at the plugin root (`<plugin-root>/references/foo.md`, the shared layout the
|
|
26
|
+
// cloudflare/outlook/telegram/email plugins use) that plain read MISSES, and the
|
|
27
|
+
// agent gets "File does not exist" — the reliability trap Task 1362 removes.
|
|
28
|
+
// The plugin-root path is only reachable if the SKILL.md tells the agent to read
|
|
29
|
+
// via the `plugin-read` tool (which resolves plugin-root-relative). So for a bare
|
|
30
|
+
// reference cited from a SKILL.md, the plugin-root candidate counts ONLY when that
|
|
31
|
+
// SKILL.md declares the `plugin-read` convention; without it, the skill-local path
|
|
32
|
+
// is the sole candidate and a plugin-root-only reference is a violation. PLUGIN.md
|
|
33
|
+
// is itself a plugin-root doc always read via plugin-read, so its plugin-root
|
|
34
|
+
// candidate is unconditional (unchanged).
|
|
20
35
|
|
|
21
36
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
22
37
|
import { resolve, relative, join, dirname } from "node:path";
|
|
@@ -85,23 +100,25 @@ function owningPluginRoot(filePath) {
|
|
|
85
100
|
|
|
86
101
|
// Directories to resolve a bare `references/<file>.md` citation against.
|
|
87
102
|
// Returns an array of candidate directories; the citation passes if the file
|
|
88
|
-
// exists under ANY of them.
|
|
89
|
-
// via `plugin-read` — the agent may construct `file=references/foo.md`
|
|
90
|
-
// (plugin-root form) or `file=skills/<slug>/references/foo.md` (skill-local
|
|
91
|
-
// form), depending on how the plugin organises its references.
|
|
103
|
+
// exists under ANY of them.
|
|
92
104
|
//
|
|
93
105
|
// PLUGIN.md — plugin-root only. → [<plugin-root>]
|
|
94
|
-
// SKILL.md — skill-local
|
|
95
|
-
//
|
|
106
|
+
// SKILL.md — skill-local always; plugin-root ONLY when the SKILL.md
|
|
107
|
+
// declares the `plugin-read` convention (Task 1362). A plain
|
|
108
|
+
// `Read` from a SKILL.md resolves against the announced skill
|
|
109
|
+
// dir, so a plugin-root-only reference is unreachable unless
|
|
110
|
+
// the SKILL.md routes the read through `plugin-read`.
|
|
111
|
+
// → [<skill-dir>] or
|
|
112
|
+
// [<skill-dir>, <plugin-root>]
|
|
96
113
|
// specialist template — no owning context. → []
|
|
97
|
-
function bareReferenceContextDirs(filePath) {
|
|
114
|
+
function bareReferenceContextDirs(filePath, declaresPluginRead) {
|
|
98
115
|
if (filePath.endsWith("/PLUGIN.md")) {
|
|
99
116
|
return [dirname(filePath)];
|
|
100
117
|
}
|
|
101
118
|
if (filePath.endsWith("/SKILL.md")) {
|
|
102
119
|
const skillDir = dirname(filePath);
|
|
103
120
|
const pluginRoot = owningPluginRoot(filePath);
|
|
104
|
-
return pluginRoot ? [skillDir, pluginRoot] : [skillDir];
|
|
121
|
+
return pluginRoot && declaresPluginRead ? [skillDir, pluginRoot] : [skillDir];
|
|
105
122
|
}
|
|
106
123
|
return [];
|
|
107
124
|
}
|
|
@@ -110,7 +127,7 @@ function bareReferenceContextDirs(filePath) {
|
|
|
110
127
|
// citation passes the existence check if ANY candidate exists. Returns an
|
|
111
128
|
// empty array if the citation has no resolvable shape (bare reference cited
|
|
112
129
|
// from a non-plugin file).
|
|
113
|
-
function candidateTargets({ raw, sourcePath, platformRoot }) {
|
|
130
|
+
function candidateTargets({ raw, sourcePath, platformRoot, declaresPluginRead }) {
|
|
114
131
|
// Strip an optional leading `platform/` so `platform/plugins/<name>/...`
|
|
115
132
|
// and `plugins/<name>/...` resolve identically.
|
|
116
133
|
const normalised = raw.replace(/^platform\//, "");
|
|
@@ -130,7 +147,7 @@ function candidateTargets({ raw, sourcePath, platformRoot }) {
|
|
|
130
147
|
}
|
|
131
148
|
|
|
132
149
|
// Bare `references/<file>.md`.
|
|
133
|
-
const ctxDirs = bareReferenceContextDirs(sourcePath);
|
|
150
|
+
const ctxDirs = bareReferenceContextDirs(sourcePath, declaresPluginRead);
|
|
134
151
|
return ctxDirs.map(d => resolve(d, normalised));
|
|
135
152
|
}
|
|
136
153
|
|
|
@@ -141,6 +158,10 @@ function scanFile(filePath, { platformRoot, repoRoot }) {
|
|
|
141
158
|
} catch {
|
|
142
159
|
return { violations: [], citations: 0 };
|
|
143
160
|
}
|
|
161
|
+
// Whether this file routes its bare references through the `plugin-read` tool.
|
|
162
|
+
// Governs whether a SKILL.md's plugin-root reference candidate is reachable
|
|
163
|
+
// (Task 1362 — see bareReferenceContextDirs). PLUGIN.md ignores this.
|
|
164
|
+
const declaresPluginRead = /plugin-read/.test(body);
|
|
144
165
|
const violations = [];
|
|
145
166
|
let citations = 0;
|
|
146
167
|
const lines = body.split("\n");
|
|
@@ -153,7 +174,7 @@ function scanFile(filePath, { platformRoot, repoRoot }) {
|
|
|
153
174
|
// 1-based column, pointing at the start of the captured citation.
|
|
154
175
|
const column = (m.index + m[0].indexOf(raw)) + 1;
|
|
155
176
|
citations++;
|
|
156
|
-
const candidates = candidateTargets({ raw, sourcePath: filePath, platformRoot });
|
|
177
|
+
const candidates = candidateTargets({ raw, sourcePath: filePath, platformRoot, declaresPluginRead });
|
|
157
178
|
if (candidates.length === 0) {
|
|
158
179
|
violations.push({
|
|
159
180
|
source: relative(repoRoot, filePath),
|
|
@@ -171,12 +192,39 @@ function scanFile(filePath, { platformRoot, repoRoot }) {
|
|
|
171
192
|
const targetMsg = candidates.length === 1
|
|
172
193
|
? candidates[0]
|
|
173
194
|
: `none of [${candidates.join(", ")}]`;
|
|
195
|
+
// Task 1362 hint: a SKILL.md BARE `references/<file>.md` citation that
|
|
196
|
+
// misses skill-local but DOES exist at the plugin root is an access-method
|
|
197
|
+
// defect, not a missing file. The fix is to declare `plugin-read` (or write
|
|
198
|
+
// the full `plugins/<name>/references/...` path), not to create a skill-local
|
|
199
|
+
// copy. Restrict the hint to the bare shape: a citation already written as a
|
|
200
|
+
// full `plugins/.../references/...` (or `skills/.../references/...`) path is
|
|
201
|
+
// resolved elsewhere and is not reachability-limited by a missing convention,
|
|
202
|
+
// so it gets no hint (and its `raw` would resolve to a bogus nested path here).
|
|
203
|
+
let hint;
|
|
204
|
+
const isBareRef = raw.replace(/^platform\//, "").startsWith("references/");
|
|
205
|
+
if (
|
|
206
|
+
isBareRef &&
|
|
207
|
+
filePath.endsWith("/SKILL.md") &&
|
|
208
|
+
!declaresPluginRead &&
|
|
209
|
+
owningPluginRoot(filePath)
|
|
210
|
+
) {
|
|
211
|
+
const rootCandidate = resolve(
|
|
212
|
+
owningPluginRoot(filePath),
|
|
213
|
+
raw.replace(/^platform\//, ""),
|
|
214
|
+
);
|
|
215
|
+
if (existsSync(rootCandidate)) {
|
|
216
|
+
hint =
|
|
217
|
+
`exists at plugin root (${relative(repoRoot, rootCandidate)}) but this SKILL.md does not declare the \`plugin-read\` convention, ` +
|
|
218
|
+
`so a plain Read misses it — declare \`plugin-read\` or cite the full \`plugins/<name>/references/...\` path`;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
174
221
|
violations.push({
|
|
175
222
|
source: relative(repoRoot, filePath),
|
|
176
223
|
line: i + 1,
|
|
177
224
|
column,
|
|
178
225
|
target: targetMsg,
|
|
179
226
|
raw,
|
|
227
|
+
...(hint ? { hint } : {}),
|
|
180
228
|
});
|
|
181
229
|
}
|
|
182
230
|
}
|
|
@@ -244,6 +292,7 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
|
|
|
244
292
|
console.error(` source: ${v.source}:${v.line}:${v.column}`);
|
|
245
293
|
console.error(` cites: ${v.raw}`);
|
|
246
294
|
console.error(` target: ${v.target}`);
|
|
295
|
+
if (v.hint) console.error(` hint: ${v.hint}`);
|
|
247
296
|
}
|
|
248
297
|
console.error(
|
|
249
298
|
`FATAL: ${r.violations.length} broken reference citation(s) across ${r.scanned} scanned file(s) (${r.citations} citations).`,
|