@rubytech/create-maxy-code 0.1.306 → 0.1.308
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/lib/admin-access-password/__tests__/index.test.ts +114 -0
- package/payload/platform/lib/admin-access-password/dist/index.d.ts +40 -0
- package/payload/platform/lib/admin-access-password/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/admin-access-password/dist/index.js +172 -0
- package/payload/platform/lib/admin-access-password/dist/index.js.map +1 -0
- package/payload/platform/lib/admin-access-password/src/index.ts +152 -0
- package/payload/platform/lib/admin-access-password/tsconfig.json +8 -0
- package/payload/platform/lib/models/dist/index.d.ts +6 -1
- package/payload/platform/lib/models/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/models/dist/index.js +11 -5
- package/payload/platform/lib/models/dist/index.js.map +1 -1
- package/payload/platform/lib/models/src/index.ts +10 -4
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/PLUGIN.md +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +60 -19
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/admin-user-management/SKILL.md +8 -2
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +35 -14
- package/payload/platform/plugins/docs/references/admin-ui.md +33 -12
- package/payload/platform/plugins/docs/references/internals.md +1 -1
- package/payload/platform/scripts/check-no-esm-require.mjs +4 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +3 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +196 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +12 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +60 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +13 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +27 -3
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts +8 -0
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js +7 -0
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts +8 -0
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -0
- package/payload/platform/services/webchat-channel/dist/instructions.js +44 -0
- package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -0
- package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.js +11 -18
- package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.js +34 -10
- package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.d.ts +9 -0
- package/payload/platform/services/whatsapp-channel/dist/targets.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.js +13 -0
- package/payload/platform/services/whatsapp-channel/dist/targets.js.map +1 -1
- package/payload/platform/templates/account.json +1 -1
- package/payload/server/{chunk-TUY3RB5G.js → chunk-FBGIHWWM.js} +197 -82
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/AdminLoginScreens-BZIA9Z6o.js +1 -0
- package/payload/server/public/assets/AdminShell-iazLHsk7.js +1 -0
- package/payload/server/public/assets/Checkbox-8elc7oXU.js +1 -0
- package/payload/server/public/assets/admin-BEx6o3fa.js +1 -0
- package/payload/server/public/assets/{arc-BrTRjBIM.js → arc-DxSm8tli.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-6g9jRVgc.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DJ9on7sZ.js → architectureDiagram-Q4EWVU46-7JSAh0vL.js} +1 -1
- package/payload/server/public/assets/{audio-attachment-mime-j2VvxEtx.js → audio-attachment-mime-DzbKNqbH.js} +4 -4
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CIcyQn0q.js → blockDiagram-DXYQGD6D-DLoVmHKD.js} +1 -1
- package/payload/server/public/assets/{jsx-runtime-DZkld3Wk.css → brand-CmMZe4RK.css} +1 -1
- package/payload/server/public/assets/brand-Dh5UlVO2.js +9 -0
- package/payload/server/public/assets/browser-D16sPVkI.js +1 -0
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Dt5dh_GR.js → c4Diagram-AHTNJAMY-Nv1G0o9P.js} +1 -1
- package/payload/server/public/assets/channel-BgQLJanG.js +1 -0
- package/payload/server/public/assets/chat-x4PnXhkd.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-BUq3MqUy.js → chunk-2KRD3SAO-BpqPMhoA.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-CUzJH5Qt.js → chunk-336JU56O-DgXHlVU_.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-Dr_XVuUq.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-B4JUc3UA.js → chunk-4BX2VUAB-nwyZSZH8.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-HRO9fIIs.js → chunk-4TB4RGXK-Tfxz9LHX.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-MZdrLBuw.js → chunk-55IACEB6-6FMJQGXG.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-XcAN5zlM.js → chunk-5FUZZQ4R-g2FgZF3D.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-PQ1GEsxo.js → chunk-5PVQY5BW-DtLwXsPH.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-BVvCaqGd.js → chunk-67CJDMHE-K91CP5ZU.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BlBbZdpM.js → chunk-7N4EOEYR-BGpCQhqK.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-Ct-PgkGa.js → chunk-AA7GKIK3-jkDbInck.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-DZRRfCSz.js → chunk-BSJP7CBP-mDosdzGs.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-BoGEJiXe.js → chunk-CIAEETIT-DhBxewpQ.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-DiSmN31f.js → chunk-EDXVE4YY-B7c-9Emg.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-uhhokIvs.js → chunk-ENJZ2VHE-Cqhhncox.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-CfCx5WI7.js → chunk-FMBD7UC4-BpDq6wj1.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-Dj9E9gTu.js → chunk-FOC6F5B3-Ds02-ktu.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DcBs_FNy.js → chunk-ICPOFSXX-BTX5POFr.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-7-VefyJD.js → chunk-K5T4RW27-Du1PXXBU.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-CqgTEVxm.js → chunk-KGLVRYIC-siasOAf8.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-C0gzDXha.js → chunk-LIHQZDEY-xf1rbZtf.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-JXz23R1H.js → chunk-ORNJ4GCN-DeTLQ_LD.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-C2snCNq9.js → chunk-OYMX7WX6-DQ7_oVJn.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-DF4o9HRJ.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-CXCU7M-M.js → chunk-U2HBQHQK-CXmFUKgn.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-CEdcSb28.js → chunk-X2U36JSP-Bj8-8Wkz.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-Cckc75YS.js → chunk-XPW4576I-Con3TXqt.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-4nN8AO7H.js → chunk-YZCP3GAM-Dx8BHGWf.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-DcoBc8-7.js → chunk-ZZ45TVLE-Dp4Q5Y-f.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DAbPKU6u.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BKuFQLcv.js +1 -0
- package/payload/server/public/assets/clone-DnNHGhNe.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BFFro_t9.js → cose-bilkent-S5V4N54A-pZiCv69E.js} +1 -1
- package/payload/server/public/assets/{dagre-CN5to_JM.js → dagre-CLqhEgbL.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-WYrPwDg0.js → dagre-KV5264BT-BCxE8iyk.js} +1 -1
- package/payload/server/public/assets/data-DqG45sq0.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-Dz2CiboN.js → diagram-5BDNPKRD-slUEdZzz.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-r8h3Ip9O.js → diagram-G4DWMVQ6-DPcraMfu.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-CECcpGtr.js → diagram-MMDJMWI5-DR6luhGK.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CHOcAyIP.js → diagram-TYMM5635-Jap1B4wA.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-YIqKdPbS.js → erDiagram-SMLLAGMA-DJKUs2hO.js} +1 -1
- package/payload/server/public/assets/file-download-BvGS7Qmi.js +1 -0
- package/payload/server/public/assets/{flatten-DhYYI_lo.js → flatten-Cl1Bc3dR.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-DaEvQWpj.js → flowDiagram-DWJPFMVM-C8W-ZZ9W.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-x1hClEOB.js → ganttDiagram-T4ZO3ILL-DYCX4Xu2.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-g7mvu6IY.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-C-sZsh6Z.js → gitGraphDiagram-UUTBAWPF-CZu_fpgS.js} +1 -1
- package/payload/server/public/assets/{graph-Uaa5heM5.js → graph-DKSjcpmR.js} +3 -3
- package/payload/server/public/assets/graph-labels-CVWPQgBV.js +1 -0
- package/payload/server/public/assets/{graphlib-CNkAqm2w.js → graphlib-Bdp7TA4y.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-DZqJuIkB.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-cGfGccHz.js +2 -0
- package/payload/server/public/assets/{isEmpty-DGhq_DBP.js → isEmpty-D1pGOD1J.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-wHE-RwzZ.js → ishikawaDiagram-UXIWVN3A-B7KfIX6z.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DQB6iThz.js → journeyDiagram-VCZTEJTY-CSDwBfhF.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-Ck08vXSA.js → kanban-definition-6JOO6SKY-CWzcEKTr.js} +1 -1
- package/payload/server/public/assets/{line-Cdc2FBEU.js → line-BNQSlbYn.js} +1 -1
- package/payload/server/public/assets/{linear-DhtUm5H7.js → linear-BN6kGN93.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-CSYulPIL.js → mermaid-parser.core-CcFhkElq.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-CP-hOmMh.js → mermaid.core-BsIeJ7Cc.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-GxlkvV1Z.js → mindmap-definition-QFDTVHPH-CQVzj6D_.js} +1 -1
- package/payload/server/public/assets/operator-Cnzbhdh2.js +1 -0
- package/payload/server/public/assets/{ordinal-CibZU3PM.js → ordinal-CBZeH4Yp.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-Hn8rlHpy.js +1 -0
- package/payload/server/public/assets/page-CH8JQkQN.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-BPGWVqBm.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-Di6kWh9c.js → pieDiagram-DEJITSTG-DnrQ9fyn.js} +1 -1
- package/payload/server/public/assets/{public-DBXfM02B.js → public-CA90VhI9.js} +3 -3
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-iNHC5bkO.js → quadrantDiagram-34T5L4WZ-BQWlCyWi.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DMdLrj3E.js +1 -0
- package/payload/server/public/assets/{reduce-Da0RBva0.js → reduce-PuPlFPRX.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-BBlLREXo.js → requirementDiagram-MS252O5E-Du_vZhU1.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-qyDkP-Ca.js → sankeyDiagram-XADWPNL6-Cf6Z8GPQ.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-C0MJuS0m.js → sequenceDiagram-FGHM5R23-CS8GVol8.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-aHTrlosa.js → stateDiagram-FHFEXIEX-Bv1NW2F1.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BGUCxPmp.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BlRZIxEp.js → timeline-definition-GMOUNBTQ-ClGYAsr0.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CWxofUbI.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-Cfgb_ZG0.js +1 -0
- package/payload/server/public/assets/useSelectionMode-FIodJKzS.js +5 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-s76adysx.js → vennDiagram-DHZGUBPP-C2wTcxQT.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-B45olYjj.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-xC8t2QGW.js → wardleyDiagram-NUSXRM2D-D86-ivEx.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DRcHg-Wj.js → xychartDiagram-5P7HB3ND-Ba5WAN8P.js} +1 -1
- package/payload/server/public/browser.html +6 -6
- package/payload/server/public/chat.html +9 -10
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +8 -8
- package/payload/server/public/index.html +9 -10
- package/payload/server/public/operator.html +11 -12
- package/payload/server/public/public.html +7 -7
- package/payload/server/server.js +4235 -3522
- package/payload/server/public/assets/AdminLoginScreens-Cw882rmm.js +0 -1
- package/payload/server/public/assets/AdminShell-DpWl1uoY.js +0 -1
- package/payload/server/public/assets/Checkbox-DAWZItdK.js +0 -1
- package/payload/server/public/assets/Transcript-DbK2YA3K.js +0 -1
- package/payload/server/public/assets/admin-nQRdn79w.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-DqQB3T6j.js +0 -1
- package/payload/server/public/assets/browser-CHWZDCXt.js +0 -1
- package/payload/server/public/assets/channel-NZeJdLVK.js +0 -1
- package/payload/server/public/assets/chat-BBK6ulFy.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-DFKX5l0L.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-BWCCN0AM.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DPlCQCX2.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-CqkQq74h.js +0 -1
- package/payload/server/public/assets/clone-CSykdD1A.js +0 -1
- package/payload/server/public/assets/data-33mdnHv8.js +0 -1
- package/payload/server/public/assets/file-download-P_qlMCg5.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-V7Tx7_eY.js +0 -1
- package/payload/server/public/assets/graph-labels-rG63rHvl.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-D3DqVPGn.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-B_O-R0vN.js +0 -2
- package/payload/server/public/assets/jsx-runtime-sFVKdwlP.js +0 -9
- package/payload/server/public/assets/operator-CiDwt5I9.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-BY7mgCTQ.js +0 -1
- package/payload/server/public/assets/page-BvkaS_tu.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-B9f6jN54.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-BKS8JOUe.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-V2vo4BjY.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-bIKzyIdw.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-CQycQ4H9.js +0 -1
- package/payload/server/public/assets/useSelectionMode-Dky-Stnf.js +0 -5
- package/payload/server/public/assets/wardley-RL74JXVD-COr9o-nG.js +0 -1
- /package/payload/server/public/assets/{_baseFor-BFYpZGkN.js → _baseFor-brRjpUOX.js} +0 -0
- /package/payload/server/public/assets/{array-CYkMkqnU.js → array-BGFCBI0e.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-DqlHsaog.js → cytoscape.esm-TvRJ2tGX.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-Beh6XjaL.js → defaultLocale-CRZydyG6.js} +0 -0
- /package/payload/server/public/assets/{dist-t9p-NJH2.js → dist-Dbh7HrZX.js} +0 -0
- /package/payload/server/public/assets/{init-Rr1s_RiX.js → init-B8gtcn7T.js} +0 -0
- /package/payload/server/public/assets/{katex-pVJiI_rc.js → katex-RxgSu_dy.js} +0 -0
- /package/payload/server/public/assets/{path-BAQ3hXlG.js → path-DZF-JdEe.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-B_l5jfgx.js → preload-helper-CQ36nxok.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-nHaDi0Kw.js → rough.esm-6CnTHTkH.js} +0 -0
- /package/payload/server/public/assets/{src-DuPQVw-H.js → src-BoaldmnP.js} +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mkdtempSync, writeFileSync, readFileSync, existsSync } from 'node:fs'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import {
|
|
6
|
+
hashAccessPassword,
|
|
7
|
+
setAccessPassword,
|
|
8
|
+
clearAccessPassword,
|
|
9
|
+
verifyAccessPassword,
|
|
10
|
+
anyAccessPasswordConfigured,
|
|
11
|
+
accessStoreUnreadable,
|
|
12
|
+
migrateLegacyRemotePassword,
|
|
13
|
+
} from '../src/index'
|
|
14
|
+
|
|
15
|
+
function tmpUsers(content: unknown): string {
|
|
16
|
+
const dir = mkdtempSync(join(tmpdir(), 'aap-'))
|
|
17
|
+
const f = join(dir, 'users.json')
|
|
18
|
+
writeFileSync(f, JSON.stringify(content, null, 2))
|
|
19
|
+
return f
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('admin-access-password', () => {
|
|
23
|
+
it('sets accessHash on the target record only, preserving siblings', async () => {
|
|
24
|
+
const f = tmpUsers([
|
|
25
|
+
{ userId: 'a', pin: 'pinA', phone: '+1' },
|
|
26
|
+
{ userId: 'b', pin: 'pinB' },
|
|
27
|
+
])
|
|
28
|
+
await setAccessPassword('a', 'Sw0rdfish!', f)
|
|
29
|
+
const users = JSON.parse(readFileSync(f, 'utf-8'))
|
|
30
|
+
expect(users[0].accessHash).toMatch(/^[0-9a-f]+:[0-9a-f]+$/)
|
|
31
|
+
expect(users[0].pin).toBe('pinA')
|
|
32
|
+
expect(users[0].phone).toBe('+1')
|
|
33
|
+
expect(users[1].accessHash).toBeUndefined()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('verifyAccessPassword returns the matching userId, null on miss', async () => {
|
|
37
|
+
const f = tmpUsers([{ userId: 'a', pin: 'x' }, { userId: 'b', pin: 'y' }])
|
|
38
|
+
await setAccessPassword('b', 'Sw0rdfish!', f)
|
|
39
|
+
expect(await verifyAccessPassword('Sw0rdfish!', f)).toBe('b')
|
|
40
|
+
expect(await verifyAccessPassword('wrong', f)).toBeNull()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it("clearAccessPassword removes only that record's hash", async () => {
|
|
44
|
+
const f = tmpUsers([{ userId: 'a', pin: 'x' }])
|
|
45
|
+
await setAccessPassword('a', 'Sw0rdfish!', f)
|
|
46
|
+
clearAccessPassword('a', f)
|
|
47
|
+
const users = JSON.parse(readFileSync(f, 'utf-8'))
|
|
48
|
+
expect(users[0].accessHash).toBeUndefined()
|
|
49
|
+
expect(users[0].pin).toBe('x')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('anyAccessPasswordConfigured reflects presence', async () => {
|
|
53
|
+
const f = tmpUsers([{ userId: 'a', pin: 'x' }])
|
|
54
|
+
expect(anyAccessPasswordConfigured(f)).toBe(false)
|
|
55
|
+
await setAccessPassword('a', 'Sw0rdfish!', f)
|
|
56
|
+
expect(anyAccessPasswordConfigured(f)).toBe(true)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('verifyAccessPassword returns null when no record has accessHash', async () => {
|
|
60
|
+
const f = tmpUsers([{ userId: 'a', pin: 'x' }])
|
|
61
|
+
expect(await verifyAccessPassword('anything', f)).toBeNull()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('accessStoreUnreadable: false for absent/clean, true for corrupt (fail-closed signal)', () => {
|
|
65
|
+
const dir = mkdtempSync(join(tmpdir(), 'aapu-'))
|
|
66
|
+
const absent = join(dir, 'users.json')
|
|
67
|
+
expect(accessStoreUnreadable(absent)).toBe(false) // absent → not unreadable
|
|
68
|
+
writeFileSync(absent, JSON.stringify([{ userId: 'a', pin: 'x' }]))
|
|
69
|
+
expect(accessStoreUnreadable(absent)).toBe(false) // clean → readable
|
|
70
|
+
writeFileSync(absent, '{ this is not json')
|
|
71
|
+
expect(accessStoreUnreadable(absent)).toBe(true) // corrupt → unreadable → caller fails closed
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('migrateLegacyRemotePassword', () => {
|
|
76
|
+
function setup(usersContent: unknown, legacy?: string): { usersFile: string; legacyFile: string } {
|
|
77
|
+
const dir = mkdtempSync(join(tmpdir(), 'aapm-'))
|
|
78
|
+
const usersFile = join(dir, 'users.json')
|
|
79
|
+
const legacyFile = join(dir, '.remote-password')
|
|
80
|
+
writeFileSync(usersFile, JSON.stringify(usersContent, null, 2))
|
|
81
|
+
if (legacy !== undefined) writeFileSync(legacyFile, legacy)
|
|
82
|
+
return { usersFile, legacyFile }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
it('moves the legacy hash into the owner record and deletes the file', async () => {
|
|
86
|
+
const legacyHash = await hashAccessPassword('OwnerPass1!')
|
|
87
|
+
const { usersFile, legacyFile } = setup([{ userId: 'owner', pin: 'x' }], legacyHash)
|
|
88
|
+
const r = migrateLegacyRemotePassword(usersFile, legacyFile, 'owner')
|
|
89
|
+
expect(r).toBe('migrated')
|
|
90
|
+
expect(existsSync(legacyFile)).toBe(false)
|
|
91
|
+
const users = JSON.parse(readFileSync(usersFile, 'utf-8'))
|
|
92
|
+
expect(users[0].accessHash).toBe(legacyHash)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('is idempotent — second run is noop', async () => {
|
|
96
|
+
const legacyHash = await hashAccessPassword('OwnerPass1!')
|
|
97
|
+
const { usersFile, legacyFile } = setup([{ userId: 'owner', pin: 'x' }], legacyHash)
|
|
98
|
+
migrateLegacyRemotePassword(usersFile, legacyFile, 'owner')
|
|
99
|
+
expect(migrateLegacyRemotePassword(usersFile, legacyFile, 'owner')).toBe('noop-no-legacy')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('no owner record → leaves the legacy file in place', async () => {
|
|
103
|
+
const legacyHash = await hashAccessPassword('OwnerPass1!')
|
|
104
|
+
const { usersFile, legacyFile } = setup([{ userId: 'someoneelse', pin: 'x' }], legacyHash)
|
|
105
|
+
expect(migrateLegacyRemotePassword(usersFile, legacyFile, 'owner')).toBe('noop-no-owner')
|
|
106
|
+
expect(existsSync(legacyFile)).toBe(true)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('owner already has accessHash → deletes stale legacy file, noop', async () => {
|
|
110
|
+
const { usersFile, legacyFile } = setup([{ userId: 'owner', pin: 'x', accessHash: 'aa:bb' }], 'cc:dd')
|
|
111
|
+
expect(migrateLegacyRemotePassword(usersFile, legacyFile, 'owner')).toBe('noop-owner-has-hash')
|
|
112
|
+
expect(existsSync(legacyFile)).toBe(false)
|
|
113
|
+
})
|
|
114
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface AccessUserEntry {
|
|
2
|
+
userId: string;
|
|
3
|
+
pin: string;
|
|
4
|
+
phone?: string;
|
|
5
|
+
accessHash?: string;
|
|
6
|
+
[k: string]: unknown;
|
|
7
|
+
}
|
|
8
|
+
export declare function hashAccessPassword(password: string): Promise<string>;
|
|
9
|
+
declare function matchesHash(password: string, stored: string): Promise<boolean>;
|
|
10
|
+
/** Write `accessHash` onto the userId's record. Throws if the record is absent. */
|
|
11
|
+
export declare function setAccessPassword(userId: string, password: string, usersFile: string): Promise<void>;
|
|
12
|
+
/** Remove `accessHash` from the userId's record. No-op if absent. */
|
|
13
|
+
export declare function clearAccessPassword(userId: string, usersFile: string): void;
|
|
14
|
+
/** Return the userId whose accessHash matches, or null. */
|
|
15
|
+
export declare function verifyAccessPassword(password: string, usersFile: string): Promise<string | null>;
|
|
16
|
+
/** True when at least one record carries an accessHash. */
|
|
17
|
+
export declare function anyAccessPasswordConfigured(usersFile: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* True when users.json is present on disk but cannot be read or parsed. The
|
|
20
|
+
* gate's `isRemoteAuthConfigured` checks this so a transient read fault on a
|
|
21
|
+
* configured install fails CLOSED (stays "configured" → login screen, not the
|
|
22
|
+
* setup flow) instead of downgrading to "unconfigured". A genuinely absent or
|
|
23
|
+
* cleanly-empty store is NOT unreadable.
|
|
24
|
+
*/
|
|
25
|
+
export declare function accessStoreUnreadable(usersFile: string): boolean;
|
|
26
|
+
export type MigrateResult = 'migrated' | 'noop-no-legacy' | 'noop-no-owner' | 'noop-owner-has-hash';
|
|
27
|
+
/**
|
|
28
|
+
* One-time, idempotent. If the legacy single-password file exists and the
|
|
29
|
+
* owner record has no accessHash, copy the legacy hash (already scrypt
|
|
30
|
+
* "salt:hash") onto the owner and delete the legacy file. If the owner already
|
|
31
|
+
* has an accessHash, the legacy file is stale → delete it. If no owner record
|
|
32
|
+
* exists yet, leave the legacy file untouched (verify still reads it as a
|
|
33
|
+
* candidate during this window — see readLegacyRemoteHash).
|
|
34
|
+
*/
|
|
35
|
+
export declare function migrateLegacyRemotePassword(usersFile: string, legacyFile: string, ownerUserId: string): MigrateResult;
|
|
36
|
+
/** Read a still-present legacy single-password hash, or null. Used by verify
|
|
37
|
+
* as one additional candidate before the boot migration runs. */
|
|
38
|
+
export declare function readLegacyRemoteHash(legacyFile: string): string | null;
|
|
39
|
+
export { matchesHash as verifyHashCandidate };
|
|
40
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB;AAWD,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI1E;AAED,iBAAe,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAS7E;AAiBD,mFAAmF;AACnF,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM1G;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAQ3E;AAED,2DAA2D;AAC3D,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAStG;AAED,2DAA2D;AAC3D,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAItE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAGhE;AAED,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,gBAAgB,GAAG,eAAe,GAAG,qBAAqB,CAAA;AAEnG;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,aAAa,CAgBf;AAED;kEACkE;AAClE,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAItE;AAED,OAAO,EAAE,WAAW,IAAI,mBAAmB,EAAE,CAAA"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hashAccessPassword = hashAccessPassword;
|
|
4
|
+
exports.setAccessPassword = setAccessPassword;
|
|
5
|
+
exports.clearAccessPassword = clearAccessPassword;
|
|
6
|
+
exports.verifyAccessPassword = verifyAccessPassword;
|
|
7
|
+
exports.anyAccessPasswordConfigured = anyAccessPasswordConfigured;
|
|
8
|
+
exports.accessStoreUnreadable = accessStoreUnreadable;
|
|
9
|
+
exports.migrateLegacyRemotePassword = migrateLegacyRemotePassword;
|
|
10
|
+
exports.readLegacyRemoteHash = readLegacyRemoteHash;
|
|
11
|
+
exports.verifyHashCandidate = matchesHash;
|
|
12
|
+
// Per-admin device access passwords. Single owner of the `accessHash` field
|
|
13
|
+
// on users.json admin records. Imported by both platform/ui (src) and the
|
|
14
|
+
// admin MCP plugin (dist) — mirrors platform/lib/admins-write.
|
|
15
|
+
const node_crypto_1 = require("node:crypto");
|
|
16
|
+
const node_fs_1 = require("node:fs");
|
|
17
|
+
const SCRYPT_N = 16384;
|
|
18
|
+
const SCRYPT_R = 8;
|
|
19
|
+
const SCRYPT_P = 1;
|
|
20
|
+
const SCRYPT_KEYLEN = 64;
|
|
21
|
+
function scryptAsync(password, salt) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
(0, node_crypto_1.scrypt)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }, (err, key) => {
|
|
24
|
+
if (err)
|
|
25
|
+
reject(err);
|
|
26
|
+
else
|
|
27
|
+
resolve(key);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function hashAccessPassword(password) {
|
|
32
|
+
const salt = (0, node_crypto_1.randomBytes)(32);
|
|
33
|
+
const hash = await scryptAsync(password, salt);
|
|
34
|
+
return `${salt.toString('hex')}:${hash.toString('hex')}`;
|
|
35
|
+
}
|
|
36
|
+
async function matchesHash(password, stored) {
|
|
37
|
+
const colon = stored.indexOf(':');
|
|
38
|
+
if (colon === -1)
|
|
39
|
+
return false;
|
|
40
|
+
const salt = Buffer.from(stored.slice(0, colon), 'hex');
|
|
41
|
+
const storedHash = Buffer.from(stored.slice(colon + 1), 'hex');
|
|
42
|
+
if (storedHash.length !== SCRYPT_KEYLEN)
|
|
43
|
+
return false;
|
|
44
|
+
let computed;
|
|
45
|
+
try {
|
|
46
|
+
computed = await scryptAsync(password, salt);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return (0, node_crypto_1.timingSafeEqual)(computed, storedHash);
|
|
52
|
+
}
|
|
53
|
+
function readUsers(usersFile) {
|
|
54
|
+
if (!(0, node_fs_1.existsSync)(usersFile))
|
|
55
|
+
return [];
|
|
56
|
+
const raw = (0, node_fs_1.readFileSync)(usersFile, 'utf-8').trim();
|
|
57
|
+
if (!raw)
|
|
58
|
+
return [];
|
|
59
|
+
const parsed = JSON.parse(raw);
|
|
60
|
+
if (!Array.isArray(parsed))
|
|
61
|
+
throw new Error('users.json is not an array');
|
|
62
|
+
return parsed;
|
|
63
|
+
}
|
|
64
|
+
function writeUsersAtomic(usersFile, users) {
|
|
65
|
+
const tmp = `${usersFile}.tmp-${process.pid}`;
|
|
66
|
+
(0, node_fs_1.writeFileSync)(tmp, JSON.stringify(users, null, 2) + '\n', { mode: 0o600 });
|
|
67
|
+
(0, node_fs_1.renameSync)(tmp, usersFile);
|
|
68
|
+
}
|
|
69
|
+
/** Write `accessHash` onto the userId's record. Throws if the record is absent. */
|
|
70
|
+
async function setAccessPassword(userId, password, usersFile) {
|
|
71
|
+
const users = readUsers(usersFile);
|
|
72
|
+
const idx = users.findIndex(u => u.userId === userId);
|
|
73
|
+
if (idx === -1)
|
|
74
|
+
throw new Error(`no users.json record for userId=${userId}`);
|
|
75
|
+
users[idx] = { ...users[idx], accessHash: await hashAccessPassword(password) };
|
|
76
|
+
writeUsersAtomic(usersFile, users);
|
|
77
|
+
}
|
|
78
|
+
/** Remove `accessHash` from the userId's record. No-op if absent. */
|
|
79
|
+
function clearAccessPassword(userId, usersFile) {
|
|
80
|
+
const users = readUsers(usersFile);
|
|
81
|
+
const idx = users.findIndex(u => u.userId === userId);
|
|
82
|
+
if (idx === -1)
|
|
83
|
+
return;
|
|
84
|
+
const { accessHash, ...rest } = users[idx];
|
|
85
|
+
void accessHash;
|
|
86
|
+
users[idx] = rest;
|
|
87
|
+
writeUsersAtomic(usersFile, users);
|
|
88
|
+
}
|
|
89
|
+
/** Return the userId whose accessHash matches, or null. */
|
|
90
|
+
async function verifyAccessPassword(password, usersFile) {
|
|
91
|
+
let users;
|
|
92
|
+
try {
|
|
93
|
+
users = readUsers(usersFile);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
for (const u of users) {
|
|
99
|
+
if (typeof u.accessHash === 'string' && (await matchesHash(password, u.accessHash))) {
|
|
100
|
+
return u.userId;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
/** True when at least one record carries an accessHash. */
|
|
106
|
+
function anyAccessPasswordConfigured(usersFile) {
|
|
107
|
+
let users;
|
|
108
|
+
try {
|
|
109
|
+
users = readUsers(usersFile);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
return users.some(u => typeof u.accessHash === 'string' && u.accessHash.length > 0);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* True when users.json is present on disk but cannot be read or parsed. The
|
|
118
|
+
* gate's `isRemoteAuthConfigured` checks this so a transient read fault on a
|
|
119
|
+
* configured install fails CLOSED (stays "configured" → login screen, not the
|
|
120
|
+
* setup flow) instead of downgrading to "unconfigured". A genuinely absent or
|
|
121
|
+
* cleanly-empty store is NOT unreadable.
|
|
122
|
+
*/
|
|
123
|
+
function accessStoreUnreadable(usersFile) {
|
|
124
|
+
if (!(0, node_fs_1.existsSync)(usersFile))
|
|
125
|
+
return false;
|
|
126
|
+
try {
|
|
127
|
+
readUsers(usersFile);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* One-time, idempotent. If the legacy single-password file exists and the
|
|
136
|
+
* owner record has no accessHash, copy the legacy hash (already scrypt
|
|
137
|
+
* "salt:hash") onto the owner and delete the legacy file. If the owner already
|
|
138
|
+
* has an accessHash, the legacy file is stale → delete it. If no owner record
|
|
139
|
+
* exists yet, leave the legacy file untouched (verify still reads it as a
|
|
140
|
+
* candidate during this window — see readLegacyRemoteHash).
|
|
141
|
+
*/
|
|
142
|
+
function migrateLegacyRemotePassword(usersFile, legacyFile, ownerUserId) {
|
|
143
|
+
if (!(0, node_fs_1.existsSync)(legacyFile))
|
|
144
|
+
return 'noop-no-legacy';
|
|
145
|
+
const legacy = (0, node_fs_1.readFileSync)(legacyFile, 'utf-8').trim();
|
|
146
|
+
const users = readUsers(usersFile);
|
|
147
|
+
const idx = users.findIndex(u => u.userId === ownerUserId);
|
|
148
|
+
if (idx === -1)
|
|
149
|
+
return 'noop-no-owner';
|
|
150
|
+
const existing = users[idx].accessHash;
|
|
151
|
+
if (typeof existing === 'string' && existing.length > 0) {
|
|
152
|
+
(0, node_fs_1.unlinkSync)(legacyFile);
|
|
153
|
+
return 'noop-owner-has-hash';
|
|
154
|
+
}
|
|
155
|
+
if (!legacy.includes(':')) {
|
|
156
|
+
(0, node_fs_1.unlinkSync)(legacyFile);
|
|
157
|
+
return 'noop-no-legacy';
|
|
158
|
+
}
|
|
159
|
+
users[idx] = { ...users[idx], accessHash: legacy };
|
|
160
|
+
writeUsersAtomic(usersFile, users);
|
|
161
|
+
(0, node_fs_1.unlinkSync)(legacyFile);
|
|
162
|
+
return 'migrated';
|
|
163
|
+
}
|
|
164
|
+
/** Read a still-present legacy single-password hash, or null. Used by verify
|
|
165
|
+
* as one additional candidate before the boot migration runs. */
|
|
166
|
+
function readLegacyRemoteHash(legacyFile) {
|
|
167
|
+
if (!(0, node_fs_1.existsSync)(legacyFile))
|
|
168
|
+
return null;
|
|
169
|
+
const raw = (0, node_fs_1.readFileSync)(legacyFile, 'utf-8').trim();
|
|
170
|
+
return raw.includes(':') ? raw : null;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Per-admin device access passwords. Single owner of the `accessHash` field
|
|
2
|
+
// on users.json admin records. Imported by both platform/ui (src) and the
|
|
3
|
+
// admin MCP plugin (dist) — mirrors platform/lib/admins-write.
|
|
4
|
+
import { scrypt, randomBytes, timingSafeEqual } from 'node:crypto'
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync, renameSync, unlinkSync } from 'node:fs'
|
|
6
|
+
|
|
7
|
+
const SCRYPT_N = 16384
|
|
8
|
+
const SCRYPT_R = 8
|
|
9
|
+
const SCRYPT_P = 1
|
|
10
|
+
const SCRYPT_KEYLEN = 64
|
|
11
|
+
|
|
12
|
+
export interface AccessUserEntry {
|
|
13
|
+
userId: string
|
|
14
|
+
pin: string
|
|
15
|
+
phone?: string
|
|
16
|
+
accessHash?: string
|
|
17
|
+
[k: string]: unknown
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function scryptAsync(password: string, salt: Buffer): Promise<Buffer> {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
scrypt(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }, (err, key) => {
|
|
23
|
+
if (err) reject(err)
|
|
24
|
+
else resolve(key)
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function hashAccessPassword(password: string): Promise<string> {
|
|
30
|
+
const salt = randomBytes(32)
|
|
31
|
+
const hash = await scryptAsync(password, salt)
|
|
32
|
+
return `${salt.toString('hex')}:${hash.toString('hex')}`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function matchesHash(password: string, stored: string): Promise<boolean> {
|
|
36
|
+
const colon = stored.indexOf(':')
|
|
37
|
+
if (colon === -1) return false
|
|
38
|
+
const salt = Buffer.from(stored.slice(0, colon), 'hex')
|
|
39
|
+
const storedHash = Buffer.from(stored.slice(colon + 1), 'hex')
|
|
40
|
+
if (storedHash.length !== SCRYPT_KEYLEN) return false
|
|
41
|
+
let computed: Buffer
|
|
42
|
+
try { computed = await scryptAsync(password, salt) } catch { return false }
|
|
43
|
+
return timingSafeEqual(computed, storedHash)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readUsers(usersFile: string): AccessUserEntry[] {
|
|
47
|
+
if (!existsSync(usersFile)) return []
|
|
48
|
+
const raw = readFileSync(usersFile, 'utf-8').trim()
|
|
49
|
+
if (!raw) return []
|
|
50
|
+
const parsed = JSON.parse(raw)
|
|
51
|
+
if (!Array.isArray(parsed)) throw new Error('users.json is not an array')
|
|
52
|
+
return parsed as AccessUserEntry[]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function writeUsersAtomic(usersFile: string, users: AccessUserEntry[]): void {
|
|
56
|
+
const tmp = `${usersFile}.tmp-${process.pid}`
|
|
57
|
+
writeFileSync(tmp, JSON.stringify(users, null, 2) + '\n', { mode: 0o600 })
|
|
58
|
+
renameSync(tmp, usersFile)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Write `accessHash` onto the userId's record. Throws if the record is absent. */
|
|
62
|
+
export async function setAccessPassword(userId: string, password: string, usersFile: string): Promise<void> {
|
|
63
|
+
const users = readUsers(usersFile)
|
|
64
|
+
const idx = users.findIndex(u => u.userId === userId)
|
|
65
|
+
if (idx === -1) throw new Error(`no users.json record for userId=${userId}`)
|
|
66
|
+
users[idx] = { ...users[idx], accessHash: await hashAccessPassword(password) }
|
|
67
|
+
writeUsersAtomic(usersFile, users)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Remove `accessHash` from the userId's record. No-op if absent. */
|
|
71
|
+
export function clearAccessPassword(userId: string, usersFile: string): void {
|
|
72
|
+
const users = readUsers(usersFile)
|
|
73
|
+
const idx = users.findIndex(u => u.userId === userId)
|
|
74
|
+
if (idx === -1) return
|
|
75
|
+
const { accessHash, ...rest } = users[idx]
|
|
76
|
+
void accessHash
|
|
77
|
+
users[idx] = rest as AccessUserEntry
|
|
78
|
+
writeUsersAtomic(usersFile, users)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Return the userId whose accessHash matches, or null. */
|
|
82
|
+
export async function verifyAccessPassword(password: string, usersFile: string): Promise<string | null> {
|
|
83
|
+
let users: AccessUserEntry[]
|
|
84
|
+
try { users = readUsers(usersFile) } catch { return null }
|
|
85
|
+
for (const u of users) {
|
|
86
|
+
if (typeof u.accessHash === 'string' && (await matchesHash(password, u.accessHash))) {
|
|
87
|
+
return u.userId
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** True when at least one record carries an accessHash. */
|
|
94
|
+
export function anyAccessPasswordConfigured(usersFile: string): boolean {
|
|
95
|
+
let users: AccessUserEntry[]
|
|
96
|
+
try { users = readUsers(usersFile) } catch { return false }
|
|
97
|
+
return users.some(u => typeof u.accessHash === 'string' && u.accessHash.length > 0)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* True when users.json is present on disk but cannot be read or parsed. The
|
|
102
|
+
* gate's `isRemoteAuthConfigured` checks this so a transient read fault on a
|
|
103
|
+
* configured install fails CLOSED (stays "configured" → login screen, not the
|
|
104
|
+
* setup flow) instead of downgrading to "unconfigured". A genuinely absent or
|
|
105
|
+
* cleanly-empty store is NOT unreadable.
|
|
106
|
+
*/
|
|
107
|
+
export function accessStoreUnreadable(usersFile: string): boolean {
|
|
108
|
+
if (!existsSync(usersFile)) return false
|
|
109
|
+
try { readUsers(usersFile); return false } catch { return true }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type MigrateResult = 'migrated' | 'noop-no-legacy' | 'noop-no-owner' | 'noop-owner-has-hash'
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* One-time, idempotent. If the legacy single-password file exists and the
|
|
116
|
+
* owner record has no accessHash, copy the legacy hash (already scrypt
|
|
117
|
+
* "salt:hash") onto the owner and delete the legacy file. If the owner already
|
|
118
|
+
* has an accessHash, the legacy file is stale → delete it. If no owner record
|
|
119
|
+
* exists yet, leave the legacy file untouched (verify still reads it as a
|
|
120
|
+
* candidate during this window — see readLegacyRemoteHash).
|
|
121
|
+
*/
|
|
122
|
+
export function migrateLegacyRemotePassword(
|
|
123
|
+
usersFile: string,
|
|
124
|
+
legacyFile: string,
|
|
125
|
+
ownerUserId: string,
|
|
126
|
+
): MigrateResult {
|
|
127
|
+
if (!existsSync(legacyFile)) return 'noop-no-legacy'
|
|
128
|
+
const legacy = readFileSync(legacyFile, 'utf-8').trim()
|
|
129
|
+
const users = readUsers(usersFile)
|
|
130
|
+
const idx = users.findIndex(u => u.userId === ownerUserId)
|
|
131
|
+
if (idx === -1) return 'noop-no-owner'
|
|
132
|
+
const existing = users[idx].accessHash
|
|
133
|
+
if (typeof existing === 'string' && existing.length > 0) {
|
|
134
|
+
unlinkSync(legacyFile)
|
|
135
|
+
return 'noop-owner-has-hash'
|
|
136
|
+
}
|
|
137
|
+
if (!legacy.includes(':')) { unlinkSync(legacyFile); return 'noop-no-legacy' }
|
|
138
|
+
users[idx] = { ...users[idx], accessHash: legacy }
|
|
139
|
+
writeUsersAtomic(usersFile, users)
|
|
140
|
+
unlinkSync(legacyFile)
|
|
141
|
+
return 'migrated'
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Read a still-present legacy single-password hash, or null. Used by verify
|
|
145
|
+
* as one additional candidate before the boot migration runs. */
|
|
146
|
+
export function readLegacyRemoteHash(legacyFile: string): string | null {
|
|
147
|
+
if (!existsSync(legacyFile)) return null
|
|
148
|
+
const raw = readFileSync(legacyFile, 'utf-8').trim()
|
|
149
|
+
return raw.includes(':') ? raw : null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { matchesHash as verifyHashCandidate }
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export declare const FABLE_MODEL = "claude-fable-5[1m]";
|
|
2
1
|
export declare const OPUS_MODEL = "claude-opus-4-8[1m]";
|
|
3
2
|
export declare const SONNET_MODEL = "claude-sonnet-4-6";
|
|
4
3
|
export declare const HAIKU_MODEL = "claude-haiku-4-5";
|
|
@@ -12,4 +11,10 @@ export declare const MODEL_DISPLAY_NAMES: Record<string, string>;
|
|
|
12
11
|
/** The dropdown label for a model id: friendly name when canonical, else the
|
|
13
12
|
* raw id (never misrepresents an off-map persisted lever). */
|
|
14
13
|
export declare function modelDisplayName(id: string): string;
|
|
14
|
+
/** The exact selectable model ids (no aliases). Server-side re-seat validation
|
|
15
|
+
* (Task 876) uses this so a body-supplied model can never reach the spawn argv
|
|
16
|
+
* unchecked — a removed model (e.g. claude-fable-5) or a bare alias ('opus')
|
|
17
|
+
* is rejected before the fork. */
|
|
18
|
+
export declare const SELECTABLE_MODELS: readonly string[];
|
|
19
|
+
export declare function isSelectableModel(id: string): boolean;
|
|
15
20
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,UAAU,wBAAwB,CAAC;AAChD,eAAO,MAAM,YAAY,sBAAsB,CAAC;AAChD,eAAO,MAAM,WAAW,qBAAqB,CAAC;AAE9C,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAIvD,CAAC;AAEF,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;qEAEqE;AACrE,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAItD,CAAC;AAEF;+DAC+D;AAC/D,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;mCAGmC;AACnC,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAA4C,CAAC;AAC5F,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAErD"}
|
|
@@ -3,18 +3,17 @@
|
|
|
3
3
|
// Templates, docs, and skills reference these values as strings;
|
|
4
4
|
// all TypeScript consumers import from here.
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.MODEL_DISPLAY_NAMES = exports.MODEL_CONTEXT_WINDOW = exports.HAIKU_MODEL = exports.SONNET_MODEL = exports.OPUS_MODEL =
|
|
6
|
+
exports.SELECTABLE_MODELS = exports.MODEL_DISPLAY_NAMES = exports.MODEL_CONTEXT_WINDOW = exports.HAIKU_MODEL = exports.SONNET_MODEL = exports.OPUS_MODEL = void 0;
|
|
7
7
|
exports.contextWindow = contextWindow;
|
|
8
8
|
exports.modelDisplayName = modelDisplayName;
|
|
9
|
-
|
|
9
|
+
exports.isSelectableModel = isSelectableModel;
|
|
10
|
+
// Opus defaults to the 1M-context variant ([1m] is Claude Code's
|
|
10
11
|
// context-extension suffix, valid in settings.json "model" and agent
|
|
11
12
|
// frontmatter); Sonnet/Haiku stay on the standard 200k window.
|
|
12
|
-
exports.FABLE_MODEL = "claude-fable-5[1m]";
|
|
13
13
|
exports.OPUS_MODEL = "claude-opus-4-8[1m]";
|
|
14
14
|
exports.SONNET_MODEL = "claude-sonnet-4-6";
|
|
15
15
|
exports.HAIKU_MODEL = "claude-haiku-4-5";
|
|
16
16
|
exports.MODEL_CONTEXT_WINDOW = {
|
|
17
|
-
[exports.FABLE_MODEL]: 1_000_000,
|
|
18
17
|
[exports.OPUS_MODEL]: 1_000_000,
|
|
19
18
|
[exports.SONNET_MODEL]: 200_000,
|
|
20
19
|
[exports.HAIKU_MODEL]: 200_000,
|
|
@@ -27,7 +26,6 @@ function contextWindow(model) {
|
|
|
27
26
|
* native-CC model dropdown (Task 820). A lever id outside this map renders
|
|
28
27
|
* with its raw id as the label (the composer's append-fallback). */
|
|
29
28
|
exports.MODEL_DISPLAY_NAMES = {
|
|
30
|
-
[exports.FABLE_MODEL]: "Fable 5 (1M context)",
|
|
31
29
|
[exports.OPUS_MODEL]: "Opus 4.8 (1M context)",
|
|
32
30
|
[exports.SONNET_MODEL]: "Sonnet 4.6",
|
|
33
31
|
[exports.HAIKU_MODEL]: "Haiku 4.5",
|
|
@@ -37,4 +35,12 @@ exports.MODEL_DISPLAY_NAMES = {
|
|
|
37
35
|
function modelDisplayName(id) {
|
|
38
36
|
return exports.MODEL_DISPLAY_NAMES[id] ?? id;
|
|
39
37
|
}
|
|
38
|
+
/** The exact selectable model ids (no aliases). Server-side re-seat validation
|
|
39
|
+
* (Task 876) uses this so a body-supplied model can never reach the spawn argv
|
|
40
|
+
* unchecked — a removed model (e.g. claude-fable-5) or a bare alias ('opus')
|
|
41
|
+
* is rejected before the fork. */
|
|
42
|
+
exports.SELECTABLE_MODELS = [exports.OPUS_MODEL, exports.SONNET_MODEL, exports.HAIKU_MODEL];
|
|
43
|
+
function isSelectableModel(id) {
|
|
44
|
+
return exports.SELECTABLE_MODELS.includes(id);
|
|
45
|
+
}
|
|
40
46
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,0DAA0D;AAC1D,iEAAiE;AACjE,6CAA6C;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,0DAA0D;AAC1D,iEAAiE;AACjE,6CAA6C;;;AAgB7C,sCAEC;AAaD,4CAEC;AAOD,8CAEC;AAxCD,iEAAiE;AACjE,qEAAqE;AACrE,+DAA+D;AAClD,QAAA,UAAU,GAAG,qBAAqB,CAAC;AACnC,QAAA,YAAY,GAAG,mBAAmB,CAAC;AACnC,QAAA,WAAW,GAAG,kBAAkB,CAAC;AAEjC,QAAA,oBAAoB,GAA2B;IAC1D,CAAC,kBAAU,CAAC,EAAE,SAAS;IACvB,CAAC,oBAAY,CAAC,EAAE,OAAO;IACvB,CAAC,mBAAW,CAAC,EAAE,OAAO;CACvB,CAAC;AAEF,wEAAwE;AACxE,SAAgB,aAAa,CAAC,KAAa;IACzC,OAAO,4BAAoB,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC;AAChD,CAAC;AAED;;qEAEqE;AACxD,QAAA,mBAAmB,GAA2B;IACzD,CAAC,kBAAU,CAAC,EAAE,uBAAuB;IACrC,CAAC,oBAAY,CAAC,EAAE,YAAY;IAC5B,CAAC,mBAAW,CAAC,EAAE,WAAW;CAC3B,CAAC;AAEF;+DAC+D;AAC/D,SAAgB,gBAAgB,CAAC,EAAU;IACzC,OAAO,2BAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;AACvC,CAAC;AAED;;;mCAGmC;AACtB,QAAA,iBAAiB,GAAsB,CAAC,kBAAU,EAAE,oBAAY,EAAE,mBAAW,CAAC,CAAC;AAC5F,SAAgB,iBAAiB,CAAC,EAAU;IAC1C,OAAO,yBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -2,16 +2,14 @@
|
|
|
2
2
|
// Templates, docs, and skills reference these values as strings;
|
|
3
3
|
// all TypeScript consumers import from here.
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Opus defaults to the 1M-context variant ([1m] is Claude Code's
|
|
6
6
|
// context-extension suffix, valid in settings.json "model" and agent
|
|
7
7
|
// frontmatter); Sonnet/Haiku stay on the standard 200k window.
|
|
8
|
-
export const FABLE_MODEL = "claude-fable-5[1m]";
|
|
9
8
|
export const OPUS_MODEL = "claude-opus-4-8[1m]";
|
|
10
9
|
export const SONNET_MODEL = "claude-sonnet-4-6";
|
|
11
10
|
export const HAIKU_MODEL = "claude-haiku-4-5";
|
|
12
11
|
|
|
13
12
|
export const MODEL_CONTEXT_WINDOW: Record<string, number> = {
|
|
14
|
-
[FABLE_MODEL]: 1_000_000,
|
|
15
13
|
[OPUS_MODEL]: 1_000_000,
|
|
16
14
|
[SONNET_MODEL]: 200_000,
|
|
17
15
|
[HAIKU_MODEL]: 200_000,
|
|
@@ -26,7 +24,6 @@ export function contextWindow(model: string): number {
|
|
|
26
24
|
* native-CC model dropdown (Task 820). A lever id outside this map renders
|
|
27
25
|
* with its raw id as the label (the composer's append-fallback). */
|
|
28
26
|
export const MODEL_DISPLAY_NAMES: Record<string, string> = {
|
|
29
|
-
[FABLE_MODEL]: "Fable 5 (1M context)",
|
|
30
27
|
[OPUS_MODEL]: "Opus 4.8 (1M context)",
|
|
31
28
|
[SONNET_MODEL]: "Sonnet 4.6",
|
|
32
29
|
[HAIKU_MODEL]: "Haiku 4.5",
|
|
@@ -37,3 +34,12 @@ export const MODEL_DISPLAY_NAMES: Record<string, string> = {
|
|
|
37
34
|
export function modelDisplayName(id: string): string {
|
|
38
35
|
return MODEL_DISPLAY_NAMES[id] ?? id;
|
|
39
36
|
}
|
|
37
|
+
|
|
38
|
+
/** The exact selectable model ids (no aliases). Server-side re-seat validation
|
|
39
|
+
* (Task 876) uses this so a body-supplied model can never reach the spawn argv
|
|
40
|
+
* unchecked — a removed model (e.g. claude-fable-5) or a bare alias ('opus')
|
|
41
|
+
* is rejected before the fork. */
|
|
42
|
+
export const SELECTABLE_MODELS: readonly string[] = [OPUS_MODEL, SONNET_MODEL, HAIKU_MODEL];
|
|
43
|
+
export function isSelectableModel(id: string): boolean {
|
|
44
|
+
return SELECTABLE_MODELS.includes(id);
|
|
45
|
+
}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
"services/*"
|
|
7
7
|
],
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && tsc -p services/webchat-channel/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
|
|
10
|
-
"build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json",
|
|
9
|
+
"build": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && tsc -p services/webchat-channel/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
|
|
10
|
+
"build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json",
|
|
11
11
|
"gen:canonical-tools": "node scripts/generate-canonical-tool-names.mjs",
|
|
12
12
|
"build:memory": "tsc -p plugins/memory/mcp/tsconfig.json",
|
|
13
13
|
"build:contacts": "tsc -p plugins/contacts/mcp/tsconfig.json",
|