@rubytech/create-maxy-code 0.1.307 → 0.1.309
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/claude-ptys-slice.test.js +1 -1
- package/dist/__tests__/cloudflared-slice.test.js +1 -1
- package/dist/__tests__/macos-darwin-branch.test.js +4 -6
- package/package.json +1 -1
- package/payload/platform/lib/admin-access-password/__tests__/index.test.ts +142 -0
- package/payload/platform/lib/admin-access-password/dist/index.d.ts +49 -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 +198 -0
- package/payload/platform/lib/admin-access-password/dist/index.js.map +1 -0
- package/payload/platform/lib/admin-access-password/src/index.ts +176 -0
- package/payload/platform/lib/admin-access-password/tsconfig.json +8 -0
- package/payload/platform/lib/models/dist/index.d.ts +13 -0
- package/payload/platform/lib/models/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/models/dist/index.js +20 -1
- package/payload/platform/lib/models/dist/index.js.map +1 -1
- package/payload/platform/lib/models/src/index.ts +19 -0
- 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 +63 -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 +23 -3
- package/payload/platform/plugins/docs/references/admin-ui.md +21 -1
- 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 +4 -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 +193 -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 +52 -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.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +9 -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/server/{chunk-4QFPGAUZ.js → chunk-J6WDAWFJ.js} +190 -82
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/AccessGate-CU4Qw6M6.js +1 -0
- package/payload/server/public/assets/AdminLoginScreens-DUpRIM1h.js +1 -0
- package/payload/server/public/assets/AdminShell-DkoQSsqo.js +1 -0
- package/payload/server/public/assets/{Checkbox-DoPOlZTV.js → Checkbox-DMA1hVhn.js} +1 -1
- package/payload/server/public/assets/OperatorConversations-BIHiyyxU.js +1 -0
- package/payload/server/public/assets/admin-BHvOMcCh.js +1 -0
- package/payload/server/public/assets/{arc-DxSm8tli.js → arc-Dl9QFDgW.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Crq5RSDG.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-7JSAh0vL.js → architectureDiagram-Q4EWVU46-CNLQUVi9.js} +1 -1
- package/payload/server/public/assets/{audio-attachment-mime-BA10IzqQ.js → audio-attachment-mime-DOBET-W1.js} +3 -3
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DLoVmHKD.js → blockDiagram-DXYQGD6D-B-QxfVjk.js} +1 -1
- package/payload/server/public/assets/brand-4F9ZXBF8.css +1 -0
- package/payload/server/public/assets/{brand-BaRMO3mZ.js → brand-C7kj0USi.js} +1 -1
- package/payload/server/public/assets/browser-eLSiC7_b.js +1 -0
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Nv1G0o9P.js → c4Diagram-AHTNJAMY-NuYFvPDU.js} +1 -1
- package/payload/server/public/assets/channel-KT66MFN2.js +1 -0
- package/payload/server/public/assets/chat-DedpVKUl.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-BpqPMhoA.js → chunk-2KRD3SAO-l5VL1KQm.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-DgXHlVU_.js → chunk-336JU56O-C5gVznC2.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-CWMiOm8Y.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-nwyZSZH8.js → chunk-4BX2VUAB-BtMVz_Sq.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-Tfxz9LHX.js → chunk-4TB4RGXK-C8-dmCuT.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-6FMJQGXG.js → chunk-55IACEB6-C5gWvEJW.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-g2FgZF3D.js → chunk-5FUZZQ4R-BZ7VZoAC.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-DtLwXsPH.js → chunk-5PVQY5BW-C_ajQOGC.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-K91CP5ZU.js → chunk-67CJDMHE-BJOR2dwK.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BGpCQhqK.js → chunk-7N4EOEYR-DvZLpquX.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-jkDbInck.js → chunk-AA7GKIK3-h5tVsPlW.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-mDosdzGs.js → chunk-BSJP7CBP-DLOF2-m3.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-DhBxewpQ.js → chunk-CIAEETIT-CQRQMPCf.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-B7c-9Emg.js → chunk-EDXVE4YY-K9-EfuM2.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-Cqhhncox.js → chunk-ENJZ2VHE-DVoy6NvU.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-BpDq6wj1.js → chunk-FMBD7UC4-CQn_2o2b.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-Ds02-ktu.js → chunk-FOC6F5B3-CuN73goS.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-BTX5POFr.js → chunk-ICPOFSXX-CKhl9J-H.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-Du1PXXBU.js → chunk-K5T4RW27-CBskuQmq.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-siasOAf8.js → chunk-KGLVRYIC-Chtjt8xK.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-xf1rbZtf.js → chunk-LIHQZDEY-EOBiBNSH.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-DeTLQ_LD.js → chunk-ORNJ4GCN-uPPq-5MN.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-DQ7_oVJn.js → chunk-OYMX7WX6-BRzbxJOT.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-DCzpF46A.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-CXmFUKgn.js → chunk-U2HBQHQK-Bndyn5nF.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-Bj8-8Wkz.js → chunk-X2U36JSP-BspGBHXW.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-Con3TXqt.js → chunk-XPW4576I-HgXQ2k9h.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-Dx8BHGWf.js → chunk-YZCP3GAM-C7oTyCnw.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-Dp4Q5Y-f.js → chunk-ZZ45TVLE-CufWa7QL.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-QOU4HzJJ.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DcOUai3Z.js +1 -0
- package/payload/server/public/assets/clone-CHh05KN-.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-pZiCv69E.js → cose-bilkent-S5V4N54A-BM3yEbF-.js} +1 -1
- package/payload/server/public/assets/{dagre-CLqhEgbL.js → dagre-C7WHNZO8.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-BCxE8iyk.js → dagre-KV5264BT-bcZx3l06.js} +1 -1
- package/payload/server/public/assets/data-BcoBY39U.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-slUEdZzz.js → diagram-5BDNPKRD-B7VotaMF.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-DPcraMfu.js → diagram-G4DWMVQ6-CWWvEsFx.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DR6luhGK.js → diagram-MMDJMWI5-DYHVkTYJ.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-Jap1B4wA.js → diagram-TYMM5635-CHeB6Ama.js} +1 -1
- package/payload/server/public/assets/{dist-Dbh7HrZX.js → dist-CjjtmoWL.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-DJKUs2hO.js → erDiagram-SMLLAGMA-C9gog8QP.js} +1 -1
- package/payload/server/public/assets/{flatten-Cl1Bc3dR.js → flatten-QcJDm7yK.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-C8W-ZZ9W.js → flowDiagram-DWJPFMVM-B2O9lKTh.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-DYCX4Xu2.js → ganttDiagram-T4ZO3ILL-DEa4AwbR.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-BC-u4jgy.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CZu_fpgS.js → gitGraphDiagram-UUTBAWPF-BjXPQpBL.js} +1 -1
- package/payload/server/public/assets/{graph-D29p1FJH.js → graph-C7Hye_vp.js} +2 -2
- package/payload/server/public/assets/{graph-labels-DF3QVHkH.js → graph-labels-YYgGsEVd.js} +1 -1
- package/payload/server/public/assets/{graphlib-Bdp7TA4y.js → graphlib-CBevn2DP.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-DPUetTz-.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-q2dl09-M.js +2 -0
- package/payload/server/public/assets/{isEmpty-D1pGOD1J.js → isEmpty-BO6BAEn7.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-B7KfIX6z.js → ishikawaDiagram-UXIWVN3A-CPLfpvqv.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CSDwBfhF.js → journeyDiagram-VCZTEJTY-CTHe8MiD.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CWzcEKTr.js → kanban-definition-6JOO6SKY-Bm9h1O3Y.js} +1 -1
- package/payload/server/public/assets/{line-BNQSlbYn.js → line-C31b0rtY.js} +1 -1
- package/payload/server/public/assets/{linear-BN6kGN93.js → linear-Cfm_8_pU.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-CcFhkElq.js → mermaid-parser.core-B3tGwOcE.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-BsIeJ7Cc.js → mermaid.core-BM3s85ZW.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CQVzj6D_.js → mindmap-definition-QFDTVHPH-CaGv_mIB.js} +1 -1
- package/payload/server/public/assets/operator-WsXhcbBF.js +1 -0
- package/payload/server/public/assets/{ordinal-CBZeH4Yp.js → ordinal-jLCwuvYg.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-BcfR-ZwA.js +1 -0
- package/payload/server/public/assets/page-BhHTnqv4.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-BiKgZdNB.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-DnrQ9fyn.js → pieDiagram-DEJITSTG-BhAXps4u.js} +1 -1
- package/payload/server/public/assets/public-ViXjC_dA.js +6 -0
- package/payload/server/public/assets/public-next-BKECyMz2.js +1 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-BQWlCyWi.js → quadrantDiagram-34T5L4WZ-BWtTdNs8.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-nvCjiW_J.js +1 -0
- package/payload/server/public/assets/{reduce-PuPlFPRX.js → reduce-Dmi_NdVH.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-Du_vZhU1.js → requirementDiagram-MS252O5E-CWU0qCcP.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-Cf6Z8GPQ.js → sankeyDiagram-XADWPNL6-DYvutQld.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-CS8GVol8.js → sequenceDiagram-FGHM5R23-Cir0hBWS.js} +1 -1
- package/payload/server/public/assets/{src-BoaldmnP.js → src-CwB968DO.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-Bv1NW2F1.js → stateDiagram-FHFEXIEX-Be-h8YSS.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CAnmjSj7.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-ClGYAsr0.js → timeline-definition-GMOUNBTQ-C3C4Wuyk.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-BinXCVyM.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-CSmh20Ze.js +1 -0
- package/payload/server/public/assets/{useSelectionMode-ghXuZicl.js → useSelectionMode-CG_Iel5F.js} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-C2wTcxQT.js → vennDiagram-DHZGUBPP-mDclw0Sa.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-BiCmRKWx.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-D86-ivEx.js → wardleyDiagram-NUSXRM2D-mvk-YA9g.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-Ba5WAN8P.js → xychartDiagram-5P7HB3ND-9M7k1hmD.js} +1 -1
- package/payload/server/public/browser.html +8 -8
- package/payload/server/public/chat.html +11 -11
- package/payload/server/public/data.html +7 -7
- package/payload/server/public/graph.html +10 -10
- package/payload/server/public/index.html +10 -10
- package/payload/server/public/operator.html +13 -13
- package/payload/server/public/public-next.html +24 -0
- package/payload/server/public/public.html +9 -8
- package/payload/server/server.js +4450 -3781
- package/dist/__tests__/onboarding-state-readback.test.js +0 -61
- package/dist/__tests__/rss-sampler-installer.test.js +0 -27
- package/dist/onboarding-readback.js +0 -27
- package/payload/server/public/assets/AdminLoginScreens-BsC-EwAn.js +0 -1
- package/payload/server/public/assets/AdminShell-CiKKN22G.js +0 -1
- package/payload/server/public/assets/admin-DSwmdCKp.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-6g9jRVgc.js +0 -1
- package/payload/server/public/assets/brand-C001cin3.css +0 -1
- package/payload/server/public/assets/browser-BKR1eJJb.js +0 -1
- package/payload/server/public/assets/channel-BgQLJanG.js +0 -1
- package/payload/server/public/assets/chat-DZXNRiy-.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-Dr_XVuUq.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-DF4o9HRJ.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DAbPKU6u.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BKuFQLcv.js +0 -1
- package/payload/server/public/assets/clone-DnNHGhNe.js +0 -1
- package/payload/server/public/assets/data-CuVmW1WN.js +0 -1
- package/payload/server/public/assets/file-download-4_hQmMwU.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-g7mvu6IY.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-DZqJuIkB.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-cGfGccHz.js +0 -2
- package/payload/server/public/assets/operator-CQRaBNN7.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-Hn8rlHpy.js +0 -1
- package/payload/server/public/assets/page-Dz2F3QtB.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-BPGWVqBm.js +0 -1
- package/payload/server/public/assets/public-B7BQwWpP.js +0 -6
- package/payload/server/public/assets/radar-PYXPWWZC-DMdLrj3E.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BGUCxPmp.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CWxofUbI.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-Cfgb_ZG0.js +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-B45olYjj.js +0 -1
- /package/payload/server/public/assets/{file-download-qc_xy7Az.css → OperatorConversations-qc_xy7Az.css} +0 -0
- /package/payload/server/public/assets/{_baseFor-brRjpUOX.js → _baseFor-D8P3gUw3.js} +0 -0
- /package/payload/server/public/assets/{array-BGFCBI0e.js → array-CLwNaqU1.js} +0 -0
- /package/payload/server/public/assets/{chunk-Pqm5yXtL.js → chunk-CAM3fms7.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-TvRJ2tGX.js → cytoscape.esm-mZ4wTuB7.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-CRZydyG6.js → defaultLocale-D_VMtRaY.js} +0 -0
- /package/payload/server/public/assets/{init-B8gtcn7T.js → init-vVpfz1D6.js} +0 -0
- /package/payload/server/public/assets/{katex-RxgSu_dy.js → katex-DaHGEgm7.js} +0 -0
- /package/payload/server/public/assets/{path-DZF-JdEe.js → path-CuyvWNAH.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-CQ36nxok.js → preload-helper-TKOBYGYD.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-6CnTHTkH.js → rough.esm-JX0wREDd.js} +0 -0
|
@@ -11,4 +11,17 @@ export declare const MODEL_DISPLAY_NAMES: Record<string, string>;
|
|
|
11
11
|
/** The dropdown label for a model id: friendly name when canonical, else the
|
|
12
12
|
* raw id (never misrepresents an off-map persisted lever). */
|
|
13
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;
|
|
20
|
+
/** Strip Claude Code's `[1m]` context-extension suffix so a selectable id
|
|
21
|
+
* (`claude-opus-4-8[1m]`) compares equal to the live API/JSONL `message.model`
|
|
22
|
+
* (suffix stripped upstream → `claude-opus-4-8`). Ids without the suffix pass
|
|
23
|
+
* through. The /chat picker uses this to decide whether a chosen model is a
|
|
24
|
+
* real change from the running one — a raw `selected !== live` compare is
|
|
25
|
+
* always true for `[1m]` models. */
|
|
26
|
+
export declare function baseModelId(id: string): string;
|
|
14
27
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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"}
|
|
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;AAED;;;;;qCAKqC;AACrC,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE9C"}
|
|
@@ -3,9 +3,11 @@
|
|
|
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 = void 0;
|
|
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
|
+
exports.isSelectableModel = isSelectableModel;
|
|
10
|
+
exports.baseModelId = baseModelId;
|
|
9
11
|
// Opus defaults to the 1M-context variant ([1m] is Claude Code's
|
|
10
12
|
// context-extension suffix, valid in settings.json "model" and agent
|
|
11
13
|
// frontmatter); Sonnet/Haiku stay on the standard 200k window.
|
|
@@ -34,4 +36,21 @@ exports.MODEL_DISPLAY_NAMES = {
|
|
|
34
36
|
function modelDisplayName(id) {
|
|
35
37
|
return exports.MODEL_DISPLAY_NAMES[id] ?? id;
|
|
36
38
|
}
|
|
39
|
+
/** The exact selectable model ids (no aliases). Server-side re-seat validation
|
|
40
|
+
* (Task 876) uses this so a body-supplied model can never reach the spawn argv
|
|
41
|
+
* unchecked — a removed model (e.g. claude-fable-5) or a bare alias ('opus')
|
|
42
|
+
* is rejected before the fork. */
|
|
43
|
+
exports.SELECTABLE_MODELS = [exports.OPUS_MODEL, exports.SONNET_MODEL, exports.HAIKU_MODEL];
|
|
44
|
+
function isSelectableModel(id) {
|
|
45
|
+
return exports.SELECTABLE_MODELS.includes(id);
|
|
46
|
+
}
|
|
47
|
+
/** Strip Claude Code's `[1m]` context-extension suffix so a selectable id
|
|
48
|
+
* (`claude-opus-4-8[1m]`) compares equal to the live API/JSONL `message.model`
|
|
49
|
+
* (suffix stripped upstream → `claude-opus-4-8`). Ids without the suffix pass
|
|
50
|
+
* through. The /chat picker uses this to decide whether a chosen model is a
|
|
51
|
+
* real change from the running one — a raw `selected !== live` compare is
|
|
52
|
+
* always true for `[1m]` models. */
|
|
53
|
+
function baseModelId(id) {
|
|
54
|
+
return id.replace(/\[1m\]$/, "");
|
|
55
|
+
}
|
|
37
56
|
//# 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;;;AAgB7C,sCAEC;AAaD,4CAEC;
|
|
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;AAQD,kCAEC;AAlDD,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;AAED;;;;;qCAKqC;AACrC,SAAgB,WAAW,CAAC,EAAU;IACpC,OAAO,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -34,3 +34,22 @@ export const MODEL_DISPLAY_NAMES: Record<string, string> = {
|
|
|
34
34
|
export function modelDisplayName(id: string): string {
|
|
35
35
|
return MODEL_DISPLAY_NAMES[id] ?? id;
|
|
36
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
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Strip Claude Code's `[1m]` context-extension suffix so a selectable id
|
|
48
|
+
* (`claude-opus-4-8[1m]`) compares equal to the live API/JSONL `message.model`
|
|
49
|
+
* (suffix stripped upstream → `claude-opus-4-8`). Ids without the suffix pass
|
|
50
|
+
* through. The /chat picker uses this to decide whether a chosen model is a
|
|
51
|
+
* real change from the running one — a raw `selected !== live` compare is
|
|
52
|
+
* always true for `[1m]` models. */
|
|
53
|
+
export function baseModelId(id: string): string {
|
|
54
|
+
return id.replace(/\[1m\]$/, "");
|
|
55
|
+
}
|
|
@@ -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",
|
|
@@ -9,6 +9,7 @@ import { resolve, join } from "node:path";
|
|
|
9
9
|
import { execFileSync } from "node:child_process";
|
|
10
10
|
import { appendFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { writeAdminEntry, removeAdminFromAccount } from "../../../../lib/admins-write/dist/index.js";
|
|
12
|
+
import { setAccessPassword as setAccessPasswordHash, clearAccessPassword as clearAccessPasswordHash, accessPasswordCollides } from "../../../../lib/admin-access-password/dist/index.js";
|
|
12
13
|
import { embed } from "../../../../lib/embed-client/dist/index.js";
|
|
13
14
|
import { requirePortEnv } from "../../../../lib/require-port-env/dist/index.js";
|
|
14
15
|
import { deviceUrlBlock } from "../../../../lib/device-url/dist/index.js";
|
|
@@ -181,7 +182,6 @@ async function currentEntitlement() {
|
|
|
181
182
|
: undefined,
|
|
182
183
|
});
|
|
183
184
|
}
|
|
184
|
-
const REMOTE_PASSWORD_FILE = resolve(CONFIG_DIR, ".remote-password");
|
|
185
185
|
// Resolve account directory
|
|
186
186
|
function getAccountDir() {
|
|
187
187
|
if (!ACCOUNT_ID) {
|
|
@@ -2034,12 +2034,30 @@ eagerTool(server, "session-resume", "Resume a previous session. Loads the select
|
|
|
2034
2034
|
return refuseNoAccount("session-resume");
|
|
2035
2035
|
return { content: [{ type: "text", text: "resumed" }] };
|
|
2036
2036
|
});
|
|
2037
|
-
server.tool("remote-auth-set-password", "Set
|
|
2038
|
-
"
|
|
2039
|
-
"
|
|
2040
|
-
"
|
|
2037
|
+
server.tool("remote-auth-set-password", "Set an admin's remote access password — the outer password the tunnel asks for before the PIN screen. " +
|
|
2038
|
+
"Each admin has their own; setting one does not affect any other admin's password. " +
|
|
2039
|
+
"Defaults to the calling admin; pass userId (from admin-list) to set another admin's. " +
|
|
2040
|
+
"Hashes with scrypt at mode 0600. Validates strength (8+ chars, digit, special character, no spaces anywhere). " +
|
|
2041
|
+
"Protects the admin interface when exposed over the tunnel — has no effect on the public endpoint or the tunnel itself.", {
|
|
2042
|
+
password: z.string(),
|
|
2043
|
+
userId: z.string().optional().describe("The admin to set the password for. Defaults to the calling admin. Use admin-list to find userIds."),
|
|
2044
|
+
}, async ({ password, userId }) => {
|
|
2041
2045
|
if (!ACCOUNT_ID)
|
|
2042
2046
|
return refuseNoAccount("remote-auth-set-password");
|
|
2047
|
+
const targetUserId = userId ?? process.env.USER_ID;
|
|
2048
|
+
if (!targetUserId) {
|
|
2049
|
+
return { content: [{ type: "text", text: "No target admin: pass userId, or run as an identified admin." }], isError: true };
|
|
2050
|
+
}
|
|
2051
|
+
let users;
|
|
2052
|
+
try {
|
|
2053
|
+
users = readUsersJson();
|
|
2054
|
+
}
|
|
2055
|
+
catch (err) {
|
|
2056
|
+
return { content: [{ type: "text", text: `${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
2057
|
+
}
|
|
2058
|
+
if (!users.some((u) => u.userId === targetUserId)) {
|
|
2059
|
+
return { content: [{ type: "text", text: `No admin with userId ${targetUserId} on this device.` }], isError: true };
|
|
2060
|
+
}
|
|
2043
2061
|
// Validate strength — same rules as the web server's validatePasswordStrength
|
|
2044
2062
|
const checks = [
|
|
2045
2063
|
{ label: "at least 8 characters", met: password.length >= 8 },
|
|
@@ -2054,21 +2072,13 @@ server.tool("remote-auth-set-password", "Set the remote access password. Hashes
|
|
|
2054
2072
|
isError: true,
|
|
2055
2073
|
};
|
|
2056
2074
|
}
|
|
2075
|
+
if (await accessPasswordCollides(password, USERS_FILE, targetUserId)) {
|
|
2076
|
+
return { content: [{ type: "text", text: `That password is already in use by another admin. Choose a different one.` }], isError: true };
|
|
2077
|
+
}
|
|
2057
2078
|
try {
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
scrypt(password, salt, 64, { N: 16384, r: 8, p: 1 }, (err, key) => {
|
|
2062
|
-
if (err)
|
|
2063
|
-
rej(err);
|
|
2064
|
-
else
|
|
2065
|
-
res(key);
|
|
2066
|
-
});
|
|
2067
|
-
});
|
|
2068
|
-
const stored = `${salt.toString("hex")}:${hash.toString("hex")}`;
|
|
2069
|
-
const pwPath = REMOTE_PASSWORD_FILE;
|
|
2070
|
-
await writeFile(pwPath, stored, { mode: 0o600 });
|
|
2071
|
-
return { content: [{ type: "text", text: "Remote access password set." }] };
|
|
2079
|
+
await setAccessPasswordHash(targetUserId, password, USERS_FILE);
|
|
2080
|
+
console.error(`[admin] remote-access-password set userId=${targetUserId.slice(0, 8)}`);
|
|
2081
|
+
return { content: [{ type: "text", text: `Remote access password set for userId ${targetUserId}.` }] };
|
|
2072
2082
|
}
|
|
2073
2083
|
catch (err) {
|
|
2074
2084
|
return {
|
|
@@ -2077,6 +2087,40 @@ server.tool("remote-auth-set-password", "Set the remote access password. Hashes
|
|
|
2077
2087
|
};
|
|
2078
2088
|
}
|
|
2079
2089
|
});
|
|
2090
|
+
server.tool("remote-auth-revoke-password", "Revoke an admin's remote access password so they can no longer obtain a NEW remote session (an already-open session lapses within 24h). " +
|
|
2091
|
+
"Does not affect any other admin's password and does not touch their PIN. Pass userId (from admin-list).", { userId: z.string().describe("The admin whose remote access password to revoke. Use admin-list to find userIds.") }, async ({ userId }) => {
|
|
2092
|
+
if (!ACCOUNT_ID)
|
|
2093
|
+
return refuseNoAccount("remote-auth-revoke-password");
|
|
2094
|
+
let users;
|
|
2095
|
+
try {
|
|
2096
|
+
users = readUsersJson();
|
|
2097
|
+
}
|
|
2098
|
+
catch (err) {
|
|
2099
|
+
return { content: [{ type: "text", text: `${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
2100
|
+
}
|
|
2101
|
+
const target = users.find((u) => u.userId === userId);
|
|
2102
|
+
if (!target) {
|
|
2103
|
+
return { content: [{ type: "text", text: `No admin with userId ${userId} on this device.` }], isError: true };
|
|
2104
|
+
}
|
|
2105
|
+
if (!target.accessHash) {
|
|
2106
|
+
return { content: [{ type: "text", text: `userId ${userId} has no remote access password to revoke.` }] };
|
|
2107
|
+
}
|
|
2108
|
+
const withHash = users.filter((u) => typeof u.accessHash === "string" && u.accessHash.length > 0);
|
|
2109
|
+
if (withHash.length <= 1) {
|
|
2110
|
+
return { content: [{ type: "text", text: "Refusing to revoke the last remaining remote access password — the install would have no remote access. Set another admin's password first." }], isError: true };
|
|
2111
|
+
}
|
|
2112
|
+
try {
|
|
2113
|
+
clearAccessPasswordHash(userId, USERS_FILE);
|
|
2114
|
+
console.error(`[admin] remote-access-password revoked userId=${userId.slice(0, 8)}`);
|
|
2115
|
+
return { content: [{ type: "text", text: `Remote access password revoked for userId ${userId}.` }] };
|
|
2116
|
+
}
|
|
2117
|
+
catch (err) {
|
|
2118
|
+
return {
|
|
2119
|
+
content: [{ type: "text", text: `Failed to revoke: ${err instanceof Error ? err.message : String(err)}` }],
|
|
2120
|
+
isError: true,
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
});
|
|
2080
2124
|
// ===================================================================
|
|
2081
2125
|
// Utility tools
|
|
2082
2126
|
// ===================================================================
|