@datasynx/agentic-crm 0.1.0 → 1.0.0
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/README.md +264 -670
- package/dist/{approvals-DpjxGHFp.js → approvals-CmDT2eUg.js} +7 -24
- package/dist/approvals-CmDT2eUg.js.map +1 -0
- package/dist/{ask-CID3jnuL.js → ask-D8iYqDAr.js} +6 -6
- package/dist/{ask-CID3jnuL.js.map → ask-D8iYqDAr.js.map} +1 -1
- package/dist/atomic-write-8yjqqLtS.js +29 -0
- package/dist/atomic-write-8yjqqLtS.js.map +1 -0
- package/dist/atomic-write-BYmF-ThH.cjs +37 -0
- package/dist/atomic-write-BYmF-ThH.cjs.map +1 -0
- package/dist/auth-B5DcjJ_6.js +2 -0
- package/dist/{auth-DFWwWcYD.js → auth-DDXZTwS0.js} +4 -13
- package/dist/auth-DDXZTwS0.js.map +1 -0
- package/dist/{autofill-Di_-SP7t.js → autofill-B9VtlR2j.js} +2 -2
- package/dist/{autofill-Di_-SP7t.js.map → autofill-B9VtlR2j.js.map} +1 -1
- package/dist/{backup-CeMk9z86.js → backup-CTlIxUdO.js} +5 -7
- package/dist/backup-CTlIxUdO.js.map +1 -0
- package/dist/backup-LFnC09oV.js +2 -0
- package/dist/{churn-C28IgnAj.js → churn-DN9WDGNM.js} +3 -3
- package/dist/{churn-C28IgnAj.js.map → churn-DN9WDGNM.js.map} +1 -1
- package/dist/cli.js +282 -184
- package/dist/cli.js.map +1 -1
- package/dist/{compliance-CKSBoQUe.js → compliance-Bc12Hn9a.js} +3 -3
- package/dist/{compliance-CKSBoQUe.js.map → compliance-Bc12Hn9a.js.map} +1 -1
- package/dist/{compliance-CujOqAKk.js → compliance-TqYQXhBj.js} +1 -1
- package/dist/{compliance-B1kk5-YS.js → compliance-kq0xHRw3.js} +3 -3
- package/dist/{compliance-B1kk5-YS.js.map → compliance-kq0xHRw3.js.map} +1 -1
- package/dist/{compliance-B91zNvCR.cjs → compliance-pAj9FcGI.cjs} +3 -3
- package/dist/{compliance-B91zNvCR.cjs.map → compliance-pAj9FcGI.cjs.map} +1 -1
- package/dist/{context-builder-BzWAp3Zs.js → context-builder-7Uab5-G4.js} +3 -2
- package/dist/context-builder-7Uab5-G4.js.map +1 -0
- package/dist/context-builder-hmOPvgso.js +2 -0
- package/dist/{custom-fields-CzNeD3_v.js → custom-fields-BMyz5Ruh.js} +1 -1
- package/dist/{custom-fields-Pl2t9xzp.js → custom-fields-GzpOHW_2.js} +4 -13
- package/dist/custom-fields-GzpOHW_2.js.map +1 -0
- package/dist/{custom-objects-CIFrmQ2V.js → custom-objects-BNy-ayR-.js} +1 -1
- package/dist/{custom-objects-BHgn1GEX.js → custom-objects-CxW1gHwJ.js} +10 -25
- package/dist/custom-objects-CxW1gHwJ.js.map +1 -0
- package/dist/{customer-dir-DIylZ8Q6.js → customer-dir-CkMMXhb0.js} +9 -4
- package/dist/customer-dir-CkMMXhb0.js.map +1 -0
- package/dist/daemon/worker.js +66 -40
- package/dist/daemon/worker.js.map +1 -1
- package/dist/doctor-C14-vnJ1.js +103 -0
- package/dist/doctor-C14-vnJ1.js.map +1 -0
- package/dist/{enrichment-3XvgGDfB.js → enrichment-CDFdWmvD.js} +3 -3
- package/dist/{enrichment-3XvgGDfB.js.map → enrichment-CDFdWmvD.js.map} +1 -1
- package/dist/{file-lock-B_zi7NQl.js → file-lock-CcHotQkZ.js} +3 -4
- package/dist/file-lock-CcHotQkZ.js.map +1 -0
- package/dist/{gmail-sync-rQaVqKWd.js → gmail-sync-C-NmibzS.js} +13 -9
- package/dist/gmail-sync-C-NmibzS.js.map +1 -0
- package/dist/{gmail-sync-DIaxInDT.js → gmail-sync-DueE6tl5.js} +14 -10
- package/dist/gmail-sync-DueE6tl5.js.map +1 -0
- package/dist/{gmail-sync-hHm9gaWd.cjs → gmail-sync-GEy3oVvw.cjs} +13 -9
- package/dist/gmail-sync-GEy3oVvw.cjs.map +1 -0
- package/dist/{gmail-webhook-handler-DS7OlRPX.js → gmail-webhook-handler-B26COilD.js} +2 -2
- package/dist/{gmail-webhook-handler-e5Od25FX.js → gmail-webhook-handler-kGKpbY9h.js} +4 -4
- package/dist/{gmail-webhook-handler-e5Od25FX.js.map → gmail-webhook-handler-kGKpbY9h.js.map} +1 -1
- package/dist/{goal-engine-KpBftn4V.js → goal-engine-BbroPhqm.js} +10 -11
- package/dist/goal-engine-BbroPhqm.js.map +1 -0
- package/dist/{goal-engine-CUZSpERI.js → goal-engine-CfDAJTFt.js} +1 -1
- package/dist/{google-drive-sync-DEPcqFca.js → google-drive-sync-D1n7WKZn.js} +3 -3
- package/dist/{google-drive-sync-DEPcqFca.js.map → google-drive-sync-D1n7WKZn.js.map} +1 -1
- package/dist/{hygiene-DZqfYpFf.js → hygiene-DzQPnc6P.js} +3 -3
- package/dist/{hygiene-DZqfYpFf.js.map → hygiene-DzQPnc6P.js.map} +1 -1
- package/dist/identity-CB7j-Zr1.js +2 -0
- package/dist/{identity-CI6olMNm.js → identity-_uZ3Lbr2.js} +2 -2
- package/dist/{identity-CI6olMNm.js.map → identity-_uZ3Lbr2.js.map} +1 -1
- package/dist/{import-hubspot-BaK71U_K.js → import-hubspot-DB4n89jy.js} +51 -45
- package/dist/import-hubspot-DB4n89jy.js.map +1 -0
- package/dist/{index-V8BFaH-b.d.ts → index-B0IMMrp_.d.ts} +8 -4
- package/dist/{index-V8BFaH-b.d.ts.map → index-B0IMMrp_.d.ts.map} +1 -1
- package/dist/{index-YqwMd6aQ.d.cts → index-pY7tYXwH.d.cts} +8 -4
- package/dist/{index-YqwMd6aQ.d.cts.map → index-pY7tYXwH.d.cts.map} +1 -1
- package/dist/index.cjs +19 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -21
- package/dist/index.js.map +1 -1
- package/dist/{interactions-writer-DO3KcSR3.js → interactions-writer-BZzUIgJd.js} +5 -2
- package/dist/interactions-writer-BZzUIgJd.js.map +1 -0
- package/dist/{interactions-writer-SLHnoEeE.js → interactions-writer-DbSyI2rt.js} +32 -3
- package/dist/interactions-writer-DbSyI2rt.js.map +1 -0
- package/dist/interactions-writer-RJB8SWf2.js +2 -0
- package/dist/{interactions-writer-CrPStUll.cjs → interactions-writer-a2yzBd7T.cjs} +5 -2
- package/dist/interactions-writer-a2yzBd7T.cjs.map +1 -0
- package/dist/json-store-WWsFzXub.js +43 -0
- package/dist/json-store-WWsFzXub.js.map +1 -0
- package/dist/{knowledge-base-D0Fh40kc.js → knowledge-base-DHNc4hVj.js} +43 -16
- package/dist/knowledge-base-DHNc4hVj.js.map +1 -0
- package/dist/{lancedb-CCBbpulq.js → lancedb-CswQEE5K.js} +1 -1
- package/dist/{lancedb-rlvWoPwl.js → lancedb-CuHKNsNZ.js} +4 -3
- package/dist/lancedb-CuHKNsNZ.js.map +1 -0
- package/dist/{lead-model-BCFzyktm.js → lead-model-CEmx7te7.js} +6 -14
- package/dist/lead-model-CEmx7te7.js.map +1 -0
- package/dist/{llm-Z8RIYkpF.js → llm-BnSUBisu.js} +2 -2
- package/dist/{llm-Z8RIYkpF.js.map → llm-BnSUBisu.js.map} +1 -1
- package/dist/{llm-iijeXmgq.cjs → llm-CXycmEl9.cjs} +2 -2
- package/dist/{llm-iijeXmgq.cjs.map → llm-CXycmEl9.cjs.map} +1 -1
- package/dist/{llm-DEjWcqmW.js → llm-DSX1-wFu.js} +1 -1
- package/dist/{llm-DvzZqva0.js → llm-PZzgPphl.js} +3 -3
- package/dist/{llm-DvzZqva0.js.map → llm-PZzgPphl.js.map} +1 -1
- package/dist/logger-BkInaGoV.cjs +167 -0
- package/dist/logger-BkInaGoV.cjs.map +1 -0
- package/dist/logger-Dyl4VcLO.js +147 -0
- package/dist/logger-Dyl4VcLO.js.map +1 -0
- package/dist/logger-UaF5p9d1.js +147 -0
- package/dist/logger-UaF5p9d1.js.map +1 -0
- package/dist/logger-vKQS34w9.js +2 -0
- package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
- package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
- package/dist/mcp.cjs +327 -303
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.d.cts.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +327 -303
- package/dist/mcp.js.map +1 -1
- package/dist/{memory-Cy6-Tbyl.js → memory-D8hmgD9d.js} +1 -1
- package/dist/{memory-Bb6ky3kb.js → memory-Dzr9dXSM.js} +4 -11
- package/dist/memory-Dzr9dXSM.js.map +1 -0
- package/dist/{microsoft-calendar-B6MMtUQK.js → microsoft-calendar-jIu9K5zX.js} +4 -4
- package/dist/{microsoft-calendar-B6MMtUQK.js.map → microsoft-calendar-jIu9K5zX.js.map} +1 -1
- package/dist/{microsoft-sync-CpZVoSuq.js → microsoft-sync-R_r8HL-B.js} +5 -5
- package/dist/{microsoft-sync-CpZVoSuq.js.map → microsoft-sync-R_r8HL-B.js.map} +1 -1
- package/dist/{nba-3wanmJ0U.js → nba-mTJ4yEqD.js} +3 -3
- package/dist/{nba-3wanmJ0U.js.map → nba-mTJ4yEqD.js.map} +1 -1
- package/dist/{notification-dispatcher-0vYNngWe.js → notification-dispatcher-inpKyuBz.js} +7 -3
- package/dist/notification-dispatcher-inpKyuBz.js.map +1 -0
- package/dist/{pipeline-writer-BqBrYrQc.js → pipeline-writer-0LJ6Qkat.js} +1 -1
- package/dist/{pipeline-writer-N2omexxp.cjs → pipeline-writer-B1tRAhuD.cjs} +11 -3
- package/dist/pipeline-writer-B1tRAhuD.cjs.map +1 -0
- package/dist/{pipeline-writer-BvVquKIe.js → pipeline-writer-CIllfnZl.js} +5 -3
- package/dist/pipeline-writer-CIllfnZl.js.map +1 -0
- package/dist/{pipeline-writer-eufx_0o1.js → pipeline-writer-rDj-ni6q.js} +6 -4
- package/dist/pipeline-writer-rDj-ni6q.js.map +1 -0
- package/dist/{proactive-agent-BgQXw3ac.js → proactive-agent-B7u3Bj_l.js} +6 -6
- package/dist/{proactive-agent-BgQXw3ac.js.map → proactive-agent-B7u3Bj_l.js.map} +1 -1
- package/dist/{proactive-worker-BrLHNhjH.js → proactive-worker-1zkm6aJD.js} +7 -8
- package/dist/proactive-worker-1zkm6aJD.js.map +1 -0
- package/dist/{push-manager-CowY-0IK.js → push-manager-BXM-IHfP.js} +1 -1
- package/dist/{push-manager-CdqIIkuh.js → push-manager-C0ECQgva.js} +4 -4
- package/dist/push-manager-C0ECQgva.js.map +1 -0
- package/dist/{quote-generator-OhSFsi3x.js → quote-generator-ByUyIYtw.js} +1 -1
- package/dist/{quote-generator-BfwENXzg.js → quote-generator-CTdR8eEI.js} +5 -5
- package/dist/quote-generator-CTdR8eEI.js.map +1 -0
- package/dist/rbac-DzbyFhVH.js +2 -0
- package/dist/{rbac-CTIktZaC.js → rbac-msmBc_tK.js} +19 -12
- package/dist/rbac-msmBc_tK.js.map +1 -0
- package/dist/regex-Jt5DatPi.js +13 -0
- package/dist/regex-Jt5DatPi.js.map +1 -0
- package/dist/{relationship-health-odxEoQdJ.js → relationship-health-ZZNXR1RZ.js} +8 -16
- package/dist/relationship-health-ZZNXR1RZ.js.map +1 -0
- package/dist/{revenue-simulation-Bqf2DLVB.js → revenue-simulation-D8f_YkUY.js} +9 -19
- package/dist/revenue-simulation-D8f_YkUY.js.map +1 -0
- package/dist/{revenue-simulation-BJdRTEHc.js → revenue-simulation-njJZlTqm.js} +1 -1
- package/dist/safe-path-mpp0dKtO.js +18 -0
- package/dist/safe-path-mpp0dKtO.js.map +1 -0
- package/dist/{segments-BqcD5HIl.js → segments-DI3LOQNe.js} +5 -14
- package/dist/segments-DI3LOQNe.js.map +1 -0
- package/dist/sequence-engine-C6nnewHX.js +2 -0
- package/dist/{sequence-engine-J1lTW_in.js → sequence-engine-DNTVLq7o.js} +15 -8
- package/dist/sequence-engine-DNTVLq7o.js.map +1 -0
- package/dist/{sequence-store-DaaWr0Os.js → sequence-store-CmYb6s0g.js} +6 -5
- package/dist/sequence-store-CmYb6s0g.js.map +1 -0
- package/dist/{server-Dyva03K8.js → server-DqSMYhSA.js} +278 -220
- package/dist/server-DqSMYhSA.js.map +1 -0
- package/dist/{session-D9ub6Wl1.js → session-B6XaP83h.js} +3 -3
- package/dist/session-B6XaP83h.js.map +1 -0
- package/dist/{session-B9AilxOE.js → session-BgGDyP2C.js} +3 -3
- package/dist/session-BgGDyP2C.js.map +1 -0
- package/dist/session-Bp4zTh4l.js +2 -0
- package/dist/{session-D0qFkBla.cjs → session-Mm7GQbSH.cjs} +3 -3
- package/dist/session-Mm7GQbSH.cjs.map +1 -0
- package/dist/{session-store-C8tEvMPw.js → session-store-DWxJ5Pof.js} +79 -17
- package/dist/session-store-DWxJ5Pof.js.map +1 -0
- package/dist/{session-store-B0QZE8Bx.cjs → session-store-yfwnj0OC.cjs} +126 -16
- package/dist/session-store-yfwnj0OC.cjs.map +1 -0
- package/dist/{sla-engine-5IhTsBUR.js → sla-engine-CP2KiKDS.js} +1 -1
- package/dist/{sla-engine-BqX-7u-7.js → sla-engine-O-A1ntu_.js} +2 -2
- package/dist/{sla-engine-BqX-7u-7.js.map → sla-engine-O-A1ntu_.js.map} +1 -1
- package/dist/{sop-Vp0UPWFW.js → sop-BV7ICAFR.js} +4 -11
- package/dist/sop-BV7ICAFR.js.map +1 -0
- package/dist/{sop-DkhVChGy.js → sop-D33qTHUb.js} +1 -1
- package/dist/survey-engine-DKctGcLQ.js +2 -0
- package/dist/{survey-engine-DBjCYqCv.js → survey-engine-DngXBv47.js} +5 -4
- package/dist/survey-engine-DngXBv47.js.map +1 -0
- package/dist/{sync-state-CwLSt_1m.js → sync-state-BaA8LbTI.js} +1 -1
- package/dist/{sync-state-ChaLbamC.js → sync-state-DMZgzpez.js} +4 -12
- package/dist/sync-state-DMZgzpez.js.map +1 -0
- package/dist/{ticket-writer-CjqKeIRD.js → ticket-writer-DsfpeLGZ.js} +1 -1
- package/dist/{ticket-writer-j2oX_Wal.js → ticket-writer-a9on36Wb.js} +12 -24
- package/dist/ticket-writer-a9on36Wb.js.map +1 -0
- package/dist/{tone-Bdm5uaht.js → tone-C7bqK69y.js} +5 -12
- package/dist/tone-C7bqK69y.js.map +1 -0
- package/dist/{tone-DRKlZgPr.cjs → tone-Cmc7O2Fx.cjs} +3 -9
- package/dist/tone-Cmc7O2Fx.cjs.map +1 -0
- package/dist/{tone-vNb2DAAD.js → tone-mXSftvTn.js} +3 -8
- package/dist/tone-mXSftvTn.js.map +1 -0
- package/dist/{transcript-watcher-CL2QUygI.js → transcript-watcher-0mh2ZhmH.js} +18 -11
- package/dist/transcript-watcher-0mh2ZhmH.js.map +1 -0
- package/dist/unmatched-transcripts-C92zAoM4.js +2 -0
- package/dist/unmatched-transcripts-DC-VQ9YS.js +16 -0
- package/dist/unmatched-transcripts-DC-VQ9YS.js.map +1 -0
- package/dist/update-deal-CWy1eLJI.js +2 -0
- package/dist/{update-deal-DKC79skb.js → update-deal-DSzr_Aau.js} +3 -3
- package/dist/{update-deal-DKC79skb.js.map → update-deal-DSzr_Aau.js.map} +1 -1
- package/dist/{usage-D0-TYJkw.js → usage-BVlFlKW_.js} +8 -6
- package/dist/usage-BVlFlKW_.js.map +1 -0
- package/dist/usage-CClTf5e6.cjs.map +1 -1
- package/dist/usage-D0u9a-lV.js.map +1 -1
- package/dist/{vault-DXCg29W-.js → vault-CfwZdNzC.js} +3 -4
- package/dist/vault-CfwZdNzC.js.map +1 -0
- package/dist/{vault-C1D3zScD.js → vault-DxKP4_R2.js} +1 -1
- package/dist/{webhooks-Xn6zO6kd.cjs → webhooks-CwW-3kvG.cjs} +5 -19
- package/dist/webhooks-CwW-3kvG.cjs.map +1 -0
- package/dist/{webhooks-7EpA05Qr.js → webhooks-DXr1IoKn.js} +8 -21
- package/dist/webhooks-DXr1IoKn.js.map +1 -0
- package/dist/{webhooks-BO2UAnmn.js → webhooks-sWZ8CJtR.js} +5 -18
- package/dist/webhooks-sWZ8CJtR.js.map +1 -0
- package/package.json +11 -2
- package/dist/approvals-DpjxGHFp.js.map +0 -1
- package/dist/auth-CyFuu9X_.js +0 -2
- package/dist/auth-DFWwWcYD.js.map +0 -1
- package/dist/backup-CeMk9z86.js.map +0 -1
- package/dist/backup-f_hC7rBV.js +0 -2
- package/dist/context-builder-BzWAp3Zs.js.map +0 -1
- package/dist/context-builder-DlrRcqmJ.js +0 -2
- package/dist/custom-fields-Pl2t9xzp.js.map +0 -1
- package/dist/custom-objects-BHgn1GEX.js.map +0 -1
- package/dist/customer-dir-DIylZ8Q6.js.map +0 -1
- package/dist/file-lock-B_zi7NQl.js.map +0 -1
- package/dist/gmail-sync-DIaxInDT.js.map +0 -1
- package/dist/gmail-sync-hHm9gaWd.cjs.map +0 -1
- package/dist/gmail-sync-rQaVqKWd.js.map +0 -1
- package/dist/goal-engine-KpBftn4V.js.map +0 -1
- package/dist/identity-gyfWdrcX.js +0 -2
- package/dist/import-hubspot-BaK71U_K.js.map +0 -1
- package/dist/interactions-writer-CrPStUll.cjs.map +0 -1
- package/dist/interactions-writer-DO3KcSR3.js.map +0 -1
- package/dist/interactions-writer-SLHnoEeE.js.map +0 -1
- package/dist/interactions-writer-dSPy1XfO.js +0 -2
- package/dist/knowledge-base-D0Fh40kc.js.map +0 -1
- package/dist/lancedb-rlvWoPwl.js.map +0 -1
- package/dist/lead-model-BCFzyktm.js.map +0 -1
- package/dist/memory-Bb6ky3kb.js.map +0 -1
- package/dist/notification-dispatcher-0vYNngWe.js.map +0 -1
- package/dist/pipeline-writer-BvVquKIe.js.map +0 -1
- package/dist/pipeline-writer-N2omexxp.cjs.map +0 -1
- package/dist/pipeline-writer-eufx_0o1.js.map +0 -1
- package/dist/proactive-worker-BrLHNhjH.js.map +0 -1
- package/dist/push-manager-CdqIIkuh.js.map +0 -1
- package/dist/quote-generator-BfwENXzg.js.map +0 -1
- package/dist/rbac-C7c8tcES.js +0 -2
- package/dist/rbac-CTIktZaC.js.map +0 -1
- package/dist/relationship-health-odxEoQdJ.js.map +0 -1
- package/dist/revenue-simulation-Bqf2DLVB.js.map +0 -1
- package/dist/segments-BqcD5HIl.js.map +0 -1
- package/dist/sequence-engine-CCTHEBgi.js +0 -2
- package/dist/sequence-engine-J1lTW_in.js.map +0 -1
- package/dist/sequence-store-DaaWr0Os.js.map +0 -1
- package/dist/server-Dyva03K8.js.map +0 -1
- package/dist/session-B9AilxOE.js.map +0 -1
- package/dist/session-D0qFkBla.cjs.map +0 -1
- package/dist/session-D9ub6Wl1.js.map +0 -1
- package/dist/session-mWHA71Lw.js +0 -2
- package/dist/session-store-B0QZE8Bx.cjs.map +0 -1
- package/dist/session-store-C8tEvMPw.js.map +0 -1
- package/dist/sop-Vp0UPWFW.js.map +0 -1
- package/dist/survey-engine-C06hcQt3.js +0 -2
- package/dist/survey-engine-DBjCYqCv.js.map +0 -1
- package/dist/sync-state-ChaLbamC.js.map +0 -1
- package/dist/ticket-writer-j2oX_Wal.js.map +0 -1
- package/dist/tone-Bdm5uaht.js.map +0 -1
- package/dist/tone-DRKlZgPr.cjs.map +0 -1
- package/dist/tone-vNb2DAAD.js.map +0 -1
- package/dist/transcript-watcher-CL2QUygI.js.map +0 -1
- package/dist/unmatched-transcripts-BsH5bhkU.js +0 -26
- package/dist/unmatched-transcripts-BsH5bhkU.js.map +0 -1
- package/dist/unmatched-transcripts-D0PrJ9iz.js +0 -2
- package/dist/update-deal-BNwPGaTV.js +0 -2
- package/dist/usage-D0-TYJkw.js.map +0 -1
- package/dist/vault-DXCg29W-.js.map +0 -1
- package/dist/webhooks-7EpA05Qr.js.map +0 -1
- package/dist/webhooks-BO2UAnmn.js.map +0 -1
- package/dist/webhooks-Xn6zO6kd.cjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { E as customerExists, _ as error, k as readMainFacts, n as getSession, r as setSession, t as clearSession, v as info, y as success } from "./session-store-DWxJ5Pof.js";
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import fs from "fs";
|
|
@@ -41,7 +41,7 @@ function readAllSessions(dataDir) {
|
|
|
41
41
|
}
|
|
42
42
|
}).filter((s) => s !== null);
|
|
43
43
|
}
|
|
44
|
-
const sessionCommand = new Command("session");
|
|
44
|
+
const sessionCommand = new Command("session").description("Manage the active customer session");
|
|
45
45
|
sessionCommand.command("open <slug>").option("--owner <owner>", "Set the owner of this session").action(async (slug, opts) => {
|
|
46
46
|
const dataDir = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
47
47
|
if (!customerExists(dataDir, slug)) {
|
|
@@ -76,4 +76,4 @@ sessionCommand.command("status").action(() => {
|
|
|
76
76
|
//#endregion
|
|
77
77
|
export { readAllSessions };
|
|
78
78
|
|
|
79
|
-
//# sourceMappingURL=session-
|
|
79
|
+
//# sourceMappingURL=session-B6XaP83h.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-B6XaP83h.js","names":[],"sources":["../src/commands/session.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { setSession, getSession, clearSession } from \"../core/session-store.js\";\nimport { readMainFacts, customerExists } from \"../fs/customer-dir.js\";\nimport { success, error, info } from \"../ui/colors.js\";\n\nfunction sessionsDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sessions\");\n}\n\nexport function persistSession(\n dataDir: string,\n session: {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n }\n): void {\n const dir = sessionsDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n const key = (session.owner ?? `pid-${process.pid}`).replace(/[^a-zA-Z0-9_-]/g, \"_\");\n fs.writeFileSync(path.join(dir, `${key}.json`), JSON.stringify({ ...session, pid: process.pid }));\n}\n\nexport function clearPersistedSession(dataDir: string, owner?: string): void {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return;\n if (owner !== undefined) {\n const key = owner.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n const file = path.join(dir, `${key}.json`);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n return;\n }\n // No specific owner — clear all session files\n try {\n for (const f of fs.readdirSync(dir).filter((n) => n.endsWith(\".json\"))) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n } catch {\n /* ignore */\n }\n}\n\nexport function readAllSessions(\n dataDir: string\n): Array<{ customerSlug: string; customerName: string; startedAt: string; owner?: string }> {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => {\n try {\n return JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\")) as {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n };\n } catch {\n return null;\n }\n })\n .filter((s): s is NonNullable<typeof s> => s !== null);\n}\n\nexport const sessionCommand = new Command(\"session\").description(\n \"Manage the active customer session\"\n);\n\nsessionCommand\n .command(\"open <slug>\")\n .option(\"--owner <owner>\", \"Set the owner of this session\")\n .action(async (slug: string, opts: { owner?: string }) => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n if (!customerExists(dataDir, slug)) {\n console.error(error(`✗ Customer not found: ${slug}`));\n process.exit(1);\n }\n const facts = await readMainFacts(dataDir, slug);\n const owner = opts.owner ?? process.env[\"DXCRM_ACTOR\"];\n const session = {\n customerSlug: slug,\n customerName: facts.name,\n startedAt: new Date().toISOString(),\n ...(owner !== undefined ? { owner } : {}),\n };\n setSession(session);\n persistSession(dataDir, session);\n console.log(success(`✓ Session opened: ${facts.name}`));\n });\n\nsessionCommand.command(\"close\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n clearSession();\n clearPersistedSession(dataDir, s?.owner);\n console.log(success(\"✓ Session closed.\"));\n});\n\nsessionCommand.command(\"status\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n if (!s) {\n console.log(info(\"No active session.\"));\n } else {\n console.log(info(`Active: ${s.customerName} (${s.customerSlug}) since ${s.startedAt}`));\n }\n});\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as readMainFacts, n as customerExists } from "./customer-dir-CkMMXhb0.js";
|
|
2
2
|
import { i as success, n as error, r as info } from "./colors-BG07TZQz.js";
|
|
3
3
|
import { n as getSession, r as setSession, t as clearSession } from "./session-store-CEa39Dxs.js";
|
|
4
4
|
import { Command } from "commander";
|
|
@@ -43,7 +43,7 @@ function readAllSessions(dataDir) {
|
|
|
43
43
|
}
|
|
44
44
|
}).filter((s) => s !== null);
|
|
45
45
|
}
|
|
46
|
-
const sessionCommand = new Command("session");
|
|
46
|
+
const sessionCommand = new Command("session").description("Manage the active customer session");
|
|
47
47
|
sessionCommand.command("open <slug>").option("--owner <owner>", "Set the owner of this session").action(async (slug, opts) => {
|
|
48
48
|
const dataDir = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
49
49
|
if (!customerExists(dataDir, slug)) {
|
|
@@ -78,4 +78,4 @@ sessionCommand.command("status").action(() => {
|
|
|
78
78
|
//#endregion
|
|
79
79
|
export { sessionCommand as i, persistSession as n, readAllSessions as r, clearPersistedSession as t };
|
|
80
80
|
|
|
81
|
-
//# sourceMappingURL=session-
|
|
81
|
+
//# sourceMappingURL=session-BgGDyP2C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-BgGDyP2C.js","names":[],"sources":["../src/commands/session.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { setSession, getSession, clearSession } from \"../core/session-store.js\";\nimport { readMainFacts, customerExists } from \"../fs/customer-dir.js\";\nimport { success, error, info } from \"../ui/colors.js\";\n\nfunction sessionsDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sessions\");\n}\n\nexport function persistSession(\n dataDir: string,\n session: {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n }\n): void {\n const dir = sessionsDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n const key = (session.owner ?? `pid-${process.pid}`).replace(/[^a-zA-Z0-9_-]/g, \"_\");\n fs.writeFileSync(path.join(dir, `${key}.json`), JSON.stringify({ ...session, pid: process.pid }));\n}\n\nexport function clearPersistedSession(dataDir: string, owner?: string): void {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return;\n if (owner !== undefined) {\n const key = owner.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n const file = path.join(dir, `${key}.json`);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n return;\n }\n // No specific owner — clear all session files\n try {\n for (const f of fs.readdirSync(dir).filter((n) => n.endsWith(\".json\"))) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n } catch {\n /* ignore */\n }\n}\n\nexport function readAllSessions(\n dataDir: string\n): Array<{ customerSlug: string; customerName: string; startedAt: string; owner?: string }> {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => {\n try {\n return JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\")) as {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n };\n } catch {\n return null;\n }\n })\n .filter((s): s is NonNullable<typeof s> => s !== null);\n}\n\nexport const sessionCommand = new Command(\"session\").description(\n \"Manage the active customer session\"\n);\n\nsessionCommand\n .command(\"open <slug>\")\n .option(\"--owner <owner>\", \"Set the owner of this session\")\n .action(async (slug: string, opts: { owner?: string }) => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n if (!customerExists(dataDir, slug)) {\n console.error(error(`✗ Customer not found: ${slug}`));\n process.exit(1);\n }\n const facts = await readMainFacts(dataDir, slug);\n const owner = opts.owner ?? process.env[\"DXCRM_ACTOR\"];\n const session = {\n customerSlug: slug,\n customerName: facts.name,\n startedAt: new Date().toISOString(),\n ...(owner !== undefined ? { owner } : {}),\n };\n setSession(session);\n persistSession(dataDir, session);\n console.log(success(`✓ Session opened: ${facts.name}`));\n });\n\nsessionCommand.command(\"close\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n clearSession();\n clearPersistedSession(dataDir, s?.owner);\n console.log(success(\"✓ Session closed.\"));\n});\n\nsessionCommand.command(\"status\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n if (!s) {\n console.log(info(\"No active session.\"));\n } else {\n console.log(info(`Active: ${s.customerName} (${s.customerSlug}) since ${s.startedAt}`));\n }\n});\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
-
const require_session_store = require("./session-store-
|
|
2
|
+
const require_session_store = require("./session-store-yfwnj0OC.cjs");
|
|
3
3
|
let commander = require("commander");
|
|
4
4
|
let path = require("path");
|
|
5
5
|
path = require_chunk.__toESM(path, 1);
|
|
@@ -44,7 +44,7 @@ function readAllSessions(dataDir) {
|
|
|
44
44
|
}
|
|
45
45
|
}).filter((s) => s !== null);
|
|
46
46
|
}
|
|
47
|
-
const sessionCommand = new commander.Command("session");
|
|
47
|
+
const sessionCommand = new commander.Command("session").description("Manage the active customer session");
|
|
48
48
|
sessionCommand.command("open <slug>").option("--owner <owner>", "Set the owner of this session").action(async (slug, opts) => {
|
|
49
49
|
const dataDir = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
50
50
|
if (!require_session_store.customerExists(dataDir, slug)) {
|
|
@@ -79,4 +79,4 @@ sessionCommand.command("status").action(() => {
|
|
|
79
79
|
//#endregion
|
|
80
80
|
exports.readAllSessions = readAllSessions;
|
|
81
81
|
|
|
82
|
-
//# sourceMappingURL=session-
|
|
82
|
+
//# sourceMappingURL=session-Mm7GQbSH.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-Mm7GQbSH.cjs","names":["Command","customerExists","error","readMainFacts","success","getSession","info"],"sources":["../src/commands/session.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { setSession, getSession, clearSession } from \"../core/session-store.js\";\nimport { readMainFacts, customerExists } from \"../fs/customer-dir.js\";\nimport { success, error, info } from \"../ui/colors.js\";\n\nfunction sessionsDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sessions\");\n}\n\nexport function persistSession(\n dataDir: string,\n session: {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n }\n): void {\n const dir = sessionsDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n const key = (session.owner ?? `pid-${process.pid}`).replace(/[^a-zA-Z0-9_-]/g, \"_\");\n fs.writeFileSync(path.join(dir, `${key}.json`), JSON.stringify({ ...session, pid: process.pid }));\n}\n\nexport function clearPersistedSession(dataDir: string, owner?: string): void {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return;\n if (owner !== undefined) {\n const key = owner.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n const file = path.join(dir, `${key}.json`);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n return;\n }\n // No specific owner — clear all session files\n try {\n for (const f of fs.readdirSync(dir).filter((n) => n.endsWith(\".json\"))) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n } catch {\n /* ignore */\n }\n}\n\nexport function readAllSessions(\n dataDir: string\n): Array<{ customerSlug: string; customerName: string; startedAt: string; owner?: string }> {\n const dir = sessionsDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => {\n try {\n return JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\")) as {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n };\n } catch {\n return null;\n }\n })\n .filter((s): s is NonNullable<typeof s> => s !== null);\n}\n\nexport const sessionCommand = new Command(\"session\").description(\n \"Manage the active customer session\"\n);\n\nsessionCommand\n .command(\"open <slug>\")\n .option(\"--owner <owner>\", \"Set the owner of this session\")\n .action(async (slug: string, opts: { owner?: string }) => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n if (!customerExists(dataDir, slug)) {\n console.error(error(`✗ Customer not found: ${slug}`));\n process.exit(1);\n }\n const facts = await readMainFacts(dataDir, slug);\n const owner = opts.owner ?? process.env[\"DXCRM_ACTOR\"];\n const session = {\n customerSlug: slug,\n customerName: facts.name,\n startedAt: new Date().toISOString(),\n ...(owner !== undefined ? { owner } : {}),\n };\n setSession(session);\n persistSession(dataDir, session);\n console.log(success(`✓ Session opened: ${facts.name}`));\n });\n\nsessionCommand.command(\"close\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n clearSession();\n clearPersistedSession(dataDir, s?.owner);\n console.log(success(\"✓ Session closed.\"));\n});\n\nsessionCommand.command(\"status\").action(() => {\n const dataDir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const s = getSession() ?? readAllSessions(dataDir)[0] ?? null;\n if (!s) {\n console.log(info(\"No active session.\"));\n } else {\n console.log(info(`Active: ${s.customerName} (${s.customerSlug}) since ${s.startedAt}`));\n }\n});\n"],"mappings":"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"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
|
|
1
2
|
import { Command } from "commander";
|
|
2
3
|
import path from "path";
|
|
3
4
|
import fs from "fs";
|
|
4
5
|
import matter from "gray-matter";
|
|
5
6
|
import { fromZodError } from "zod-validation-error";
|
|
6
7
|
import { z } from "zod";
|
|
8
|
+
import { createHash } from "crypto";
|
|
7
9
|
import ansis from "ansis";
|
|
8
10
|
import { execSync } from "child_process";
|
|
9
|
-
import { createHash } from "crypto";
|
|
10
11
|
//#region src/schemas/main-facts.ts
|
|
11
12
|
const MainFactsSchema = z.object({
|
|
12
13
|
name: z.string().min(1),
|
|
@@ -29,8 +30,27 @@ const MainFactsSchema = z.object({
|
|
|
29
30
|
updated: z.preprocess((v) => v instanceof Date ? v.toISOString().slice(0, 10) : v, z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD required"))
|
|
30
31
|
});
|
|
31
32
|
//#endregion
|
|
33
|
+
//#region src/fs/safe-path.ts
|
|
34
|
+
/**
|
|
35
|
+
* Filesystem path-segment safety. A segment (customer slug, custom-object name,
|
|
36
|
+
* KB article id/category, …) is safe iff it cannot escape its parent directory:
|
|
37
|
+
* no path separators, no `..`, no NUL, not "." or empty, and bounded in length.
|
|
38
|
+
* Enforced wherever an untrusted value (from an MCP tool, API, or import) is used
|
|
39
|
+
* to build a file path, to prevent path-traversal (arbitrary read/write).
|
|
40
|
+
*/
|
|
41
|
+
function isSafePathSegment(segment) {
|
|
42
|
+
return typeof segment === "string" && segment.length > 0 && segment.length <= 128 && segment !== "." && !segment.includes("/") && !segment.includes("\\") && !segment.includes("\0") && !segment.includes("..");
|
|
43
|
+
}
|
|
44
|
+
function assertSafePathSegment(segment, kind = "path segment") {
|
|
45
|
+
if (!isSafePathSegment(segment)) throw new Error(`Invalid ${kind}: ${JSON.stringify(segment)}`);
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
32
48
|
//#region src/fs/customer-dir.ts
|
|
49
|
+
function assertSafeSlug(slug) {
|
|
50
|
+
assertSafePathSegment(slug, "customer slug");
|
|
51
|
+
}
|
|
33
52
|
function getCustomerDir(dataDir, slug) {
|
|
53
|
+
assertSafeSlug(slug);
|
|
34
54
|
return path.join(dataDir, "customers", slug);
|
|
35
55
|
}
|
|
36
56
|
function customerExists(dataDir, slug) {
|
|
@@ -57,8 +77,7 @@ async function ensureCustomerDir(dataDir, slug) {
|
|
|
57
77
|
async function writeMainFacts(dataDir, slug, facts) {
|
|
58
78
|
const filePath = path.join(getCustomerDir(dataDir, slug), "main_facts.md");
|
|
59
79
|
const clean = Object.fromEntries(Object.entries(facts).filter(([, v]) => v !== void 0));
|
|
60
|
-
|
|
61
|
-
fs.writeFileSync(filePath, content, "utf-8");
|
|
80
|
+
writeFileAtomic(filePath, matter.stringify("", clean));
|
|
62
81
|
}
|
|
63
82
|
async function readMainFacts(dataDir, slug) {
|
|
64
83
|
const filePath = path.join(getCustomerDir(dataDir, slug), "main_facts.md");
|
|
@@ -74,6 +93,44 @@ async function readMainFacts(dataDir, slug) {
|
|
|
74
93
|
return result.data;
|
|
75
94
|
}
|
|
76
95
|
//#endregion
|
|
96
|
+
//#region src/fs/json-store.ts
|
|
97
|
+
/**
|
|
98
|
+
* Small shared JSON persistence helpers. Many modules independently reimplemented
|
|
99
|
+
* the same "read a JSON file, fall back to a default on missing/parse-error" and
|
|
100
|
+
* "write a `{ key: items }` array store" logic — this centralizes both so the
|
|
101
|
+
* behavior (and the silent-fallback semantics) is defined in exactly one place.
|
|
102
|
+
*
|
|
103
|
+
* Writes go through writeFileAtomic, so a crash mid-write can never leave a
|
|
104
|
+
* half-written (corrupt) JSON file — readers always see either the old or the
|
|
105
|
+
* new complete document. This matters for the config, audit, and state files
|
|
106
|
+
* the whole product depends on.
|
|
107
|
+
*/
|
|
108
|
+
/** Read and parse a JSON file, returning `fallback` if it is missing or invalid. */
|
|
109
|
+
function readJsonFile(filePath, fallback) {
|
|
110
|
+
if (!fs.existsSync(filePath)) return fallback;
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
113
|
+
} catch {
|
|
114
|
+
return fallback;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Atomically write `value` as pretty-printed JSON, creating parent dirs as needed. */
|
|
118
|
+
function writeJsonFile(filePath, value) {
|
|
119
|
+
writeFileAtomic(filePath, JSON.stringify(value, null, 2));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Read an array stored under `key` in a `{ [key]: T[] }` JSON document. Returns
|
|
123
|
+
* an empty array if the file is missing, unparsable, or the key is not an array.
|
|
124
|
+
*/
|
|
125
|
+
function readJsonArray(filePath, key) {
|
|
126
|
+
const arr = readJsonFile(filePath, {})[key];
|
|
127
|
+
return Array.isArray(arr) ? arr : [];
|
|
128
|
+
}
|
|
129
|
+
/** Write an array under `key` as a `{ [key]: items }` JSON document. */
|
|
130
|
+
function writeJsonArray(filePath, key, items) {
|
|
131
|
+
writeJsonFile(filePath, { [key]: items });
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
77
134
|
//#region src/ui/colors.ts
|
|
78
135
|
const success = (s) => ansis.green(s);
|
|
79
136
|
const error = (s) => ansis.red(s);
|
|
@@ -95,9 +152,7 @@ function readAgenticConfig(dataDir) {
|
|
|
95
152
|
}
|
|
96
153
|
}
|
|
97
154
|
function writeAgenticConfig(dataDir, config) {
|
|
98
|
-
|
|
99
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
100
|
-
fs.writeFileSync(filePath, JSON.stringify(config, null, 2), "utf-8");
|
|
155
|
+
writeJsonFile(getConfigPath(dataDir), config);
|
|
101
156
|
}
|
|
102
157
|
function countDir(dir) {
|
|
103
158
|
let files = 0;
|
|
@@ -179,8 +234,7 @@ function appendBackupLog(dataDir, entry) {
|
|
|
179
234
|
entries = entries.filter((e) => e.filename !== entry.filename);
|
|
180
235
|
entries.unshift(entry);
|
|
181
236
|
if (entries.length > 100) entries = entries.slice(0, 100);
|
|
182
|
-
|
|
183
|
-
fs.writeFileSync(logPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
237
|
+
writeJsonFile(logPath, entries);
|
|
184
238
|
}
|
|
185
239
|
function readBackupLog(dataDir) {
|
|
186
240
|
const logPath = path.join(dataDir, ".agentic", "backup-log.json");
|
|
@@ -491,13 +545,7 @@ function rbacPath(dataDir) {
|
|
|
491
545
|
return path.join(dataDir, ".agentic", "rbac.json");
|
|
492
546
|
}
|
|
493
547
|
function getRbacConfig(dataDir) {
|
|
494
|
-
|
|
495
|
-
if (!fs.existsSync(p)) return { actors: {} };
|
|
496
|
-
try {
|
|
497
|
-
return JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
498
|
-
} catch {
|
|
499
|
-
return { actors: {} };
|
|
500
|
-
}
|
|
548
|
+
return readJsonFile(rbacPath(dataDir), { actors: {} });
|
|
501
549
|
}
|
|
502
550
|
function getRole(dataDir, actor) {
|
|
503
551
|
const config = getRbacConfig(dataDir);
|
|
@@ -525,6 +573,20 @@ function canSeeCustomer(dataDir, actor, slug) {
|
|
|
525
573
|
if (!owned) return false;
|
|
526
574
|
return (owned[actor] ?? []).includes(slug);
|
|
527
575
|
}
|
|
576
|
+
/**
|
|
577
|
+
* Build a once-loaded predicate for which customers `actor` may see. Equivalent
|
|
578
|
+
* to calling canSeeCustomer per slug, but reads/parses rbac.json a single time
|
|
579
|
+
* (and uses O(1) Set membership) — for hot loops like list_customers.
|
|
580
|
+
*/
|
|
581
|
+
function customerVisibility(dataDir, actor) {
|
|
582
|
+
if (!fs.existsSync(rbacPath(dataDir))) return () => true;
|
|
583
|
+
if (actor === "system") return () => true;
|
|
584
|
+
const config = getRbacConfig(dataDir);
|
|
585
|
+
const role = config.actors[actor] ?? config.default ?? "rep";
|
|
586
|
+
if (role === "admin" || role === "manager") return () => true;
|
|
587
|
+
const owned = new Set(config.owned_customers?.[actor] ?? []);
|
|
588
|
+
return (slug) => owned.has(slug);
|
|
589
|
+
}
|
|
528
590
|
//#endregion
|
|
529
591
|
//#region src/core/session-store.ts
|
|
530
592
|
let activeSession = null;
|
|
@@ -538,6 +600,6 @@ function clearSession() {
|
|
|
538
600
|
activeSession = null;
|
|
539
601
|
}
|
|
540
602
|
//#endregion
|
|
541
|
-
export {
|
|
603
|
+
export { writeMainFacts as A, writeJsonArray as C, ensureCustomerDir as D, customerExists as E, isSafePathSegment as M, MainFactsSchema as N, listCustomerSlugs as O, readJsonFile as S, assertSafeSlug as T, error as _, customerVisibility as a, warning as b, getRole as c, readAuditLog as d, writeAuditEntry as f, bold as g, runBackup as h, canSeeCustomer as i, assertSafePathSegment as j, readMainFacts as k, filterAuditLog as l, readBackupLog as m, getSession as n, enforceRbac as o, listBackupsInDir as p, setSession as r, getRbacConfig as s, clearSession as t, getActor as u, info as v, writeJsonFile as w, readJsonArray as x, success as y };
|
|
542
604
|
|
|
543
|
-
//# sourceMappingURL=session-store-
|
|
605
|
+
//# sourceMappingURL=session-store-DWxJ5Pof.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store-DWxJ5Pof.js","names":[],"sources":["../src/schemas/main-facts.ts","../src/fs/safe-path.ts","../src/fs/customer-dir.ts","../src/fs/json-store.ts","../src/ui/colors.ts","../src/commands/backup.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const MainFactsSchema = z.object({\n name: z.string().min(1),\n domain: z.string().optional(),\n email: z.string().optional(),\n phone: z.string().optional(),\n industry: z.string().optional(),\n relationship_stage: z.enum([\"prospect\", \"active\", \"churned\", \"paused\"]),\n deal_value: z.number().optional(),\n currency: z.string().default(\"EUR\"),\n primary_contact: z.string().optional(),\n timezone: z.string().optional(),\n tags: z.array(z.string()).default([]),\n created: z.preprocess(\n (v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n ),\n updated: z.preprocess(\n (v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n ),\n});\n\nexport type MainFacts = z.infer<typeof MainFactsSchema>;\n","/**\n * Filesystem path-segment safety. A segment (customer slug, custom-object name,\n * KB article id/category, …) is safe iff it cannot escape its parent directory:\n * no path separators, no `..`, no NUL, not \".\" or empty, and bounded in length.\n * Enforced wherever an untrusted value (from an MCP tool, API, or import) is used\n * to build a file path, to prevent path-traversal (arbitrary read/write).\n */\nexport function isSafePathSegment(segment: unknown): segment is string {\n return (\n typeof segment === \"string\" &&\n segment.length > 0 &&\n segment.length <= 128 &&\n segment !== \".\" &&\n !segment.includes(\"/\") &&\n !segment.includes(\"\\\\\") &&\n !segment.includes(\"\\0\") &&\n !segment.includes(\"..\")\n );\n}\n\nexport function assertSafePathSegment(segment: string, kind = \"path segment\"): void {\n if (!isSafePathSegment(segment)) {\n throw new Error(`Invalid ${kind}: ${JSON.stringify(segment)}`);\n }\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { fromZodError } from \"zod-validation-error\";\nimport { MainFactsSchema, type MainFacts } from \"../schemas/main-facts.js\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport { isSafePathSegment, assertSafePathSegment } from \"./safe-path.js\";\n\n/** A customer slug is safe iff it is a safe filesystem path segment. */\nexport function isSafeSlug(slug: unknown): slug is string {\n return isSafePathSegment(slug);\n}\n\nexport function assertSafeSlug(slug: string): void {\n assertSafePathSegment(slug, \"customer slug\");\n}\n\nexport function getCustomerDir(dataDir: string, slug: string): string {\n assertSafeSlug(slug);\n return path.join(dataDir, \"customers\", slug);\n}\n\nexport function customerExists(dataDir: string, slug: string): boolean {\n return fs.existsSync(getCustomerDir(dataDir, slug));\n}\n\n/** List all customer slugs (immediate subdirectories of customers/). */\nexport function listCustomerSlugs(dataDir: string): string[] {\n const dir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(dir)) return [];\n return fs.readdirSync(dir).filter((s) => {\n try {\n return fs.statSync(path.join(dir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n}\n\nexport async function ensureCustomerDir(dataDir: string, slug: string): Promise<void> {\n const customerDir = getCustomerDir(dataDir, slug);\n fs.mkdirSync(customerDir, { recursive: true });\n fs.mkdirSync(path.join(customerDir, \"attachments\"), { recursive: true });\n fs.mkdirSync(path.join(customerDir, \"transcripts\"), { recursive: true });\n}\n\nexport async function writeMainFacts(\n dataDir: string,\n slug: string,\n facts: MainFacts\n): Promise<void> {\n const filePath = path.join(getCustomerDir(dataDir, slug), \"main_facts.md\");\n // Strip undefined values — gray-matter YAML serializer rejects them\n const clean = Object.fromEntries(\n Object.entries(facts as Record<string, unknown>).filter(([, v]) => v !== undefined)\n );\n const content = matter.stringify(\"\", clean);\n writeFileAtomic(filePath, content);\n}\n\nexport async function readMainFacts(dataDir: string, slug: string): Promise<MainFacts> {\n const filePath = path.join(getCustomerDir(dataDir, slug), \"main_facts.md\");\n if (!fs.existsSync(filePath)) {\n throw new Error(`main_facts.md not found for customer '${slug}'`);\n }\n // Use fs.readFileSync so the memfs mock is respected in tests,\n // then parse the string with matter.\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n const raw = matter(content);\n // gray-matter parses YYYY-MM-DD as Date objects; coerce back to strings for Zod\n const data = raw.data as Record<string, unknown>;\n for (const key of [\"created\", \"updated\"] as const) {\n if (data[key] instanceof Date) {\n data[key] = (data[key] as Date).toISOString().slice(0, 10);\n }\n }\n const result = MainFactsSchema.safeParse(data);\n if (!result.success) {\n throw new Error(\n fromZodError(result.error, {\n prefix: `Schema error in ${filePath}`,\n prefixSeparator: \":\\n - \",\n issueSeparator: \"\\n - \",\n }).message\n );\n }\n return result.data;\n}\n","import fs from \"fs\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\n\n/**\n * Small shared JSON persistence helpers. Many modules independently reimplemented\n * the same \"read a JSON file, fall back to a default on missing/parse-error\" and\n * \"write a `{ key: items }` array store\" logic — this centralizes both so the\n * behavior (and the silent-fallback semantics) is defined in exactly one place.\n *\n * Writes go through writeFileAtomic, so a crash mid-write can never leave a\n * half-written (corrupt) JSON file — readers always see either the old or the\n * new complete document. This matters for the config, audit, and state files\n * the whole product depends on.\n */\n\n/** Read and parse a JSON file, returning `fallback` if it is missing or invalid. */\nexport function readJsonFile<T>(filePath: string, fallback: T): T {\n if (!fs.existsSync(filePath)) return fallback;\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as T;\n } catch {\n return fallback;\n }\n}\n\n/** Atomically write `value` as pretty-printed JSON, creating parent dirs as needed. */\nexport function writeJsonFile(filePath: string, value: unknown): void {\n writeFileAtomic(filePath, JSON.stringify(value, null, 2));\n}\n\n/**\n * Read an array stored under `key` in a `{ [key]: T[] }` JSON document. Returns\n * an empty array if the file is missing, unparsable, or the key is not an array.\n */\nexport function readJsonArray<T>(filePath: string, key: string): T[] {\n const data = readJsonFile<Record<string, unknown>>(filePath, {});\n const arr = data[key];\n return Array.isArray(arr) ? (arr as T[]) : [];\n}\n\n/** Write an array under `key` as a `{ [key]: items }` JSON document. */\nexport function writeJsonArray<T>(filePath: string, key: string, items: T[]): void {\n writeJsonFile(filePath, { [key]: items });\n}\n","import ansis from \"ansis\";\n\nexport const success = (s: string): string => ansis.green(s);\nexport const error = (s: string): string => ansis.red(s);\nexport const warning = (s: string): string => ansis.yellow(s);\nexport const info = (s: string): string => ansis.cyan(s);\nexport const muted = (s: string): string => ansis.gray(s);\nexport const bold = (s: string): string => ansis.bold(s);\n","import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { execSync } from \"child_process\";\nimport { createHash } from \"crypto\";\nimport { success, error, info, bold } from \"../ui/colors.js\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\n\nexport interface BackupManifest {\n version: \"1\";\n createdAt: string;\n dxcrmVersion: string;\n directories: string[];\n customerCount: number;\n fileCount: number;\n totalBytes: number;\n sha256: string;\n encrypted: boolean;\n retentionTier?: \"daily\" | \"weekly\" | \"monthly\";\n}\n\nexport interface BackupScheduleConfig {\n every: string;\n keep: number;\n weekly?: number;\n monthly?: number;\n lastBackup: string | null;\n remote?: string;\n}\n\nexport interface AgenticConfig {\n backupSchedule?: BackupScheduleConfig;\n}\n\nexport interface BackupEntry {\n filename: string;\n path: string;\n createdAt: string;\n sizeBytes: number;\n verified: boolean;\n encrypted: boolean;\n customerCount: number;\n fileCount: number;\n}\n\n// ─── Config helpers ────────────────────────────────────────────────────────────\n\nfunction getConfigPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"config.json\");\n}\n\nfunction readAgenticConfig(dataDir: string): AgenticConfig {\n const filePath = getConfigPath(dataDir);\n if (!fs.existsSync(filePath)) return {};\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as AgenticConfig;\n } catch {\n return {};\n }\n}\n\nfunction writeAgenticConfig(dataDir: string, config: AgenticConfig): void {\n writeJsonFile(getConfigPath(dataDir), config);\n}\n\n// ─── Manifest ─────────────────────────────────────────────────────────────────\n\nfunction countDir(dir: string): { files: number; bytes: number } {\n let files = 0;\n let bytes = 0;\n if (!fs.existsSync(dir)) return { files, bytes };\n const walk = (d: string) => {\n try {\n for (const entry of fs.readdirSync(d)) {\n const full = path.join(d, entry);\n try {\n const stat = fs.statSync(full);\n if (stat.isDirectory()) walk(full);\n else {\n files++;\n bytes += stat.size;\n }\n } catch {\n /* skip */\n }\n }\n } catch {\n /* skip */\n }\n };\n walk(dir);\n return { files, bytes };\n}\n\nfunction countCustomers(dataDir: string): number {\n const dir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(dir)) return 0;\n try {\n return fs.readdirSync(dir).filter((f) => {\n try {\n return fs.statSync(path.join(dir, f)).isDirectory();\n } catch {\n return false;\n }\n }).length;\n } catch {\n return 0;\n }\n}\n\nfunction sha256File(filePath: string): string {\n if (!fs.existsSync(filePath)) return \"\";\n const hash = createHash(\"sha256\");\n hash.update(fs.readFileSync(filePath));\n return hash.digest(\"hex\");\n}\n\nfunction buildManifest(\n dataDir: string,\n dirs: string[],\n zipPath: string,\n encrypted: boolean\n): BackupManifest {\n let totalFiles = 0;\n let totalBytes = 0;\n for (const d of dirs) {\n const full = path.join(dataDir, d);\n const { files, bytes } = countDir(full);\n totalFiles += files;\n totalBytes += bytes;\n }\n return {\n version: \"1\",\n createdAt: new Date().toISOString(),\n dxcrmVersion: \"0.1.0\",\n directories: dirs,\n customerCount: countCustomers(dataDir),\n fileCount: totalFiles,\n totalBytes,\n sha256: sha256File(zipPath),\n encrypted,\n };\n}\n\n// ─── Manifest log ──────────────────────────────────────────────────────────────\n\nfunction appendBackupLog(dataDir: string, entry: BackupEntry): void {\n const logPath = path.join(dataDir, \".agentic\", \"backup-log.json\");\n let entries: BackupEntry[] = [];\n if (fs.existsSync(logPath)) {\n try {\n entries = JSON.parse(fs.readFileSync(logPath, \"utf-8\") as string) as BackupEntry[];\n } catch {\n entries = [];\n }\n }\n // Deduplicate by filename — update existing entry if same file backed up again\n entries = entries.filter((e) => e.filename !== entry.filename);\n entries.unshift(entry);\n // Keep last 100 entries\n if (entries.length > 100) entries = entries.slice(0, 100);\n writeJsonFile(logPath, entries);\n}\n\nexport function readBackupLog(dataDir: string): BackupEntry[] {\n const logPath = path.join(dataDir, \".agentic\", \"backup-log.json\");\n if (!fs.existsSync(logPath)) return [];\n try {\n return JSON.parse(fs.readFileSync(logPath, \"utf-8\") as string) as BackupEntry[];\n } catch {\n return [];\n }\n}\n\n// ─── runBackup ────────────────────────────────────────────────────────────────\n\nexport async function runBackup(\n output?: string,\n dataDir?: string,\n opts: { encrypt?: boolean; remote?: string } = {}\n): Promise<BackupManifest | null> {\n const dir = dataDir ?? process.cwd();\n const customersDir = path.join(dir, \"customers\");\n\n if (!fs.existsSync(customersDir)) {\n console.error(error(\"✗ No customers directory found.\"));\n process.exit(1);\n }\n\n const zipPath =\n output ?? path.join(dir, `dxcrm-backup-${new Date().toISOString().slice(0, 10)}.zip`);\n\n // Determine which directories to include\n const includeDirs = [\"customers/\"];\n if (fs.existsSync(path.join(dir, \".agentic\"))) {\n includeDirs.push(\".agentic/\");\n }\n\n try {\n execSync(`zip -r \"${zipPath}\" ${includeDirs.join(\" \")}`, { cwd: dir });\n\n // Build manifest and append to zip\n const manifest = buildManifest(dir, includeDirs, zipPath, opts.encrypt ?? false);\n const manifestPath = path.join(dir, \".dxcrm-manifest-tmp.json\");\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), \"utf-8\");\n try {\n execSync(`zip -j \"${zipPath}\" \"${manifestPath}\"`, { cwd: dir });\n } catch {\n /* non-fatal */\n }\n fs.unlinkSync(manifestPath);\n\n // Verify integrity\n const verified = verifyBackupFile(zipPath);\n\n const entry: BackupEntry = {\n filename: path.basename(zipPath),\n path: zipPath,\n createdAt: manifest.createdAt,\n sizeBytes: fs.existsSync(zipPath) ? fs.statSync(zipPath).size : 0,\n verified,\n encrypted: opts.encrypt ?? false,\n customerCount: manifest.customerCount,\n fileCount: manifest.fileCount,\n };\n appendBackupLog(dir, entry);\n\n // Remote upload\n if (opts.remote) {\n await uploadBackup(zipPath, opts.remote);\n }\n\n console.log(success(`✓ Backup saved: ${zipPath}`));\n console.log(\n info(\n ` Customers: ${manifest.customerCount} Files: ${manifest.fileCount} Size: ${(manifest.totalBytes / 1024 / 1024).toFixed(1)} MB`\n )\n );\n if (!verified) console.log(info(\" ⚠ Integrity check failed — backup may be incomplete\"));\n\n return manifest;\n } catch (err) {\n console.error(error(`✗ Backup failed: ${(err as Error).message}`));\n process.exit(1);\n }\n}\n\n// ─── Verify ───────────────────────────────────────────────────────────────────\n\nexport function verifyBackupFile(zipPath: string): boolean {\n if (!fs.existsSync(zipPath)) return false;\n try {\n execSync(`unzip -t \"${zipPath}\"`, { stdio: \"pipe\" });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function runVerify(zipPath: string): Promise<void> {\n if (!fs.existsSync(zipPath)) {\n console.error(error(`✗ File not found: ${zipPath}`));\n process.exit(1);\n }\n\n console.log(info(`Verifying ${path.basename(zipPath)}...`));\n const ok = verifyBackupFile(zipPath);\n\n if (ok) {\n const size = fs.statSync(zipPath).size;\n const sha = sha256File(zipPath);\n console.log(success(\"✓ ZIP integrity OK\"));\n console.log(info(` Size: ${(size / 1024 / 1024).toFixed(1)} MB`));\n console.log(info(` SHA-256: ${sha}`));\n } else {\n console.error(error(\"✗ Integrity check failed\"));\n process.exit(1);\n }\n}\n\n// ─── Remote Upload ────────────────────────────────────────────────────────────\n\nexport async function uploadBackup(localPath: string, remote: string): Promise<void> {\n if (remote.startsWith(\"s3://\")) {\n // Requires AWS CLI or @aws-sdk/client-s3 to be installed\n try {\n execSync(`aws s3 cp \"${localPath}\" \"${remote}${path.basename(localPath)}\"`, {\n stdio: \"pipe\",\n });\n console.log(info(` ✓ Uploaded to ${remote}${path.basename(localPath)}`));\n } catch (err) {\n console.error(\n error(\n ` ✗ S3 upload failed (install aws-cli or @aws-sdk/client-s3): ${(err as Error).message}`\n )\n );\n }\n } else if (remote.startsWith(\"rsync://\")) {\n const dest = remote.replace(\"rsync://\", \"\");\n try {\n execSync(`rsync -az \"${localPath}\" \"${dest}\"`, { stdio: \"pipe\" });\n console.log(info(` ✓ Synced to ${dest}`));\n } catch (err) {\n console.error(error(` ✗ rsync failed: ${(err as Error).message}`));\n }\n } else {\n // Local directory copy\n try {\n const destPath = path.join(remote, path.basename(localPath));\n fs.mkdirSync(remote, { recursive: true });\n fs.copyFileSync(localPath, destPath);\n console.log(info(` ✓ Copied to ${destPath}`));\n } catch (err) {\n console.error(error(` ✗ Copy failed: ${(err as Error).message}`));\n }\n }\n}\n\n// ─── List Backups ─────────────────────────────────────────────────────────────\n\nexport function listBackupsInDir(dir: string): BackupEntry[] {\n if (!fs.existsSync(dir)) return [];\n try {\n return fs\n .readdirSync(dir)\n .filter((f) => f.match(/^dxcrm-backup-.*\\.(zip|dxbak)$/))\n .map((f) => {\n const fullPath = path.join(dir, f);\n const stat = fs.statSync(fullPath);\n return {\n filename: f,\n path: fullPath,\n createdAt: stat.mtime.toISOString(),\n sizeBytes: stat.size,\n verified: false,\n encrypted: f.endsWith(\".dxbak\"),\n customerCount: 0,\n fileCount: 0,\n } satisfies BackupEntry;\n })\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt));\n } catch {\n return [];\n }\n}\n\n// ─── Retention Policy ─────────────────────────────────────────────────────────\n\nexport interface RetentionConfig {\n daily?: number;\n weekly?: number;\n monthly?: number;\n}\n\nexport function pruneOldBackups(dir: string, keep: number, retention?: RetentionConfig): void {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.match(/^dxcrm-backup-\\d{4}-\\d{2}-\\d{2}.*\\.(zip|dxbak)$/))\n .sort();\n\n if (!retention) {\n // Legacy: keep last N\n const toDelete = files.slice(0, Math.max(0, files.length - keep));\n for (const f of toDelete) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n return;\n }\n\n // Grandfathering: daily → weekly → monthly\n const daily = retention.daily ?? keep;\n const weekly = retention.weekly ?? 0;\n const monthly = retention.monthly ?? 0;\n\n const kept = new Set<string>();\n\n // Keep last N daily\n for (const f of files.slice(-daily)) kept.add(f);\n\n // Keep last backup of each week (up to 'weekly' weeks)\n if (weekly > 0) {\n const byWeek = new Map<string, string>();\n for (const f of files) {\n const dateMatch = f.match(/dxcrm-backup-(\\d{4}-\\d{2}-\\d{2})/);\n if (!dateMatch?.[1]) continue;\n const d = new Date(dateMatch[1]);\n // ISO week: year + week number\n const week = `${d.getFullYear()}-W${String(Math.ceil((d.getDate() + new Date(d.getFullYear(), 0, 1).getDay()) / 7)).padStart(2, \"0\")}`;\n byWeek.set(week, f); // last backup of the week wins\n }\n Array.from(byWeek.values())\n .slice(-weekly)\n .forEach((f) => kept.add(f));\n }\n\n // Keep last backup of each month (up to 'monthly' months)\n if (monthly > 0) {\n const byMonth = new Map<string, string>();\n for (const f of files) {\n const dateMatch = f.match(/dxcrm-backup-(\\d{4}-\\d{2})/);\n if (!dateMatch?.[1]) continue;\n byMonth.set(dateMatch[1], f); // last backup of the month wins\n }\n Array.from(byMonth.values())\n .slice(-monthly)\n .forEach((f) => kept.add(f));\n }\n\n for (const f of files) {\n if (!kept.has(f)) {\n try {\n fs.unlinkSync(path.join(dir, f));\n } catch {\n /* ignore */\n }\n }\n }\n}\n\n// ─── Schedule ─────────────────────────────────────────────────────────────────\n\nexport async function runBackupSchedule(\n opts: {\n every?: string;\n keep?: string;\n weekly?: string;\n monthly?: string;\n remote?: string;\n status?: boolean;\n clear?: boolean;\n },\n dataDir?: string\n): Promise<void> {\n const dir = dataDir ?? process.cwd();\n\n if (opts.clear) {\n const config = readAgenticConfig(dir);\n delete config.backupSchedule;\n writeAgenticConfig(dir, config);\n console.log(success(\"✓ Backup schedule cleared.\"));\n return;\n }\n\n if (!opts.every && !opts.status) {\n console.error(error(\"✗ --every is required (e.g. --every day)\"));\n process.exit(1);\n return;\n }\n\n if (opts.every) {\n const keep = opts.keep ? parseInt(opts.keep, 10) : 7;\n const config = readAgenticConfig(dir);\n config.backupSchedule = {\n every: opts.every,\n keep,\n ...(opts.weekly ? { weekly: parseInt(opts.weekly, 10) } : {}),\n ...(opts.monthly ? { monthly: parseInt(opts.monthly, 10) } : {}),\n ...(opts.remote ? { remote: opts.remote } : {}),\n lastBackup: null,\n };\n writeAgenticConfig(dir, config);\n if (!opts.status) {\n console.log(\n success(\n `✓ Backup schedule set: every ${opts.every}, keep ${keep} daily${opts.weekly ? ` / ${opts.weekly} weekly` : \"\"}${opts.monthly ? ` / ${opts.monthly} monthly` : \"\"}.`\n )\n );\n }\n }\n\n if (opts.status) {\n const config = readAgenticConfig(dir);\n const sched = config.backupSchedule;\n if (!sched) {\n console.log(info(\"No backup schedule configured.\"));\n } else {\n console.log(bold(\"Backup Schedule:\"));\n console.log(` every: ${sched.every}`);\n console.log(` keep: ${sched.keep} daily backups`);\n if (sched.weekly) console.log(` weekly: ${sched.weekly} weekly backups`);\n if (sched.monthly) console.log(` monthly: ${sched.monthly} monthly backups`);\n if (sched.remote) console.log(` remote: ${sched.remote}`);\n console.log(` lastBackup: ${sched.lastBackup ?? \"never\"}`);\n }\n }\n}\n\nexport function shouldRunScheduledBackup(dataDir: string): boolean {\n const config = readAgenticConfig(dataDir);\n const sched = config.backupSchedule;\n if (!sched) return false;\n if (!sched.lastBackup) return true;\n const last = new Date(sched.lastBackup).getTime();\n const oneDayMs = 24 * 60 * 60 * 1000;\n return Date.now() - last >= oneDayMs;\n}\n\nexport async function runScheduledBackupIfDue(dataDir: string): Promise<void> {\n if (!shouldRunScheduledBackup(dataDir)) return;\n const config = readAgenticConfig(dataDir);\n const sched = config.backupSchedule!;\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) return;\n\n const zipPath = path.join(dataDir, `dxcrm-backup-${new Date().toISOString().slice(0, 10)}.zip`);\n const includeDirs = [\"customers/\"];\n if (fs.existsSync(path.join(dataDir, \".agentic\"))) includeDirs.push(\".agentic/\");\n\n try {\n execSync(`zip -r \"${zipPath}\" ${includeDirs.join(\" \")}`, { cwd: dataDir });\n\n const retention: RetentionConfig | undefined =\n (sched.weekly ?? sched.monthly)\n ? {\n daily: sched.keep,\n ...(sched.weekly ? { weekly: sched.weekly } : {}),\n ...(sched.monthly ? { monthly: sched.monthly } : {}),\n }\n : undefined;\n pruneOldBackups(dataDir, sched.keep, retention);\n\n if (sched.remote) {\n await uploadBackup(zipPath, sched.remote).catch(() => {\n /* non-fatal */\n });\n }\n\n config.backupSchedule!.lastBackup = new Date().toISOString();\n writeAgenticConfig(dataDir, config);\n process.stderr.write(`[daemon] Scheduled backup saved: ${zipPath}\\n`);\n } catch (err) {\n process.stderr.write(`[daemon] Scheduled backup failed: ${(err as Error).message}\\n`);\n }\n}\n\n// ─── Restore ──────────────────────────────────────────────────────────────────\n\nexport async function runRestore(zipPath: string, dataDir?: string): Promise<void> {\n const dir = dataDir ?? process.cwd();\n try {\n execSync(`unzip -o \"${path.resolve(zipPath)}\" -d \"${dir}\"`, { cwd: dir });\n console.log(success(\"✓ Restore complete.\"));\n } catch (err) {\n console.error(error(`✗ Restore failed: ${(err as Error).message}`));\n process.exit(1);\n }\n}\n\nexport interface RestoreDrillReport {\n ok: boolean;\n verified: boolean;\n hasCustomers: boolean;\n hasAgentic: boolean;\n reason?: string;\n}\n\n/**\n * Restore-drill: verify a backup is actually restorable WITHOUT touching live\n * data — checks integrity (unzip -t) and that the archive contains the expected\n * top-level state (customers/, .agentic/). Returns a report for monitoring.\n */\nexport async function runRestoreDrill(\n zipPath: string,\n opts: { silent?: boolean } = {}\n): Promise<RestoreDrillReport> {\n const resolved = path.resolve(zipPath);\n if (!fs.existsSync(resolved)) {\n if (!opts.silent) console.error(error(`✗ File not found: ${zipPath}`));\n return {\n ok: false,\n verified: false,\n hasCustomers: false,\n hasAgentic: false,\n reason: \"not_found\",\n };\n }\n\n const verified = verifyBackupFile(resolved);\n let hasCustomers = false;\n let hasAgentic = false;\n if (verified) {\n try {\n const listing = execSync(`unzip -l \"${resolved}\"`, { stdio: \"pipe\" }).toString();\n hasCustomers = listing.includes(\"customers/\");\n hasAgentic = listing.includes(\".agentic/\");\n } catch {\n /* listing failed — treated as incomplete */\n }\n }\n\n const ok = verified && hasCustomers;\n if (!opts.silent) {\n if (ok) {\n console.log(\n success(\n `✓ Restore drill OK — integrity verified; customers/${hasAgentic ? \" + .agentic/\" : \"\"} present`\n )\n );\n } else {\n console.error(\n error(`✗ Restore drill failed (verified=${verified}, customers=${hasCustomers})`)\n );\n }\n }\n return { ok, verified, hasCustomers, hasAgentic };\n}\n\n// ─── Commands ─────────────────────────────────────────────────────────────────\n\nconst scheduleSubCommand = new Command(\"schedule\")\n .description(\"Configure automatic backup schedule\")\n .option(\"--every <interval>\", \"Backup interval (e.g. day)\")\n .option(\"--keep <n>\", \"Daily backups to keep (default: 7)\")\n .option(\"--weekly <n>\", \"Weekly backups to keep (e.g. 4)\")\n .option(\"--monthly <n>\", \"Monthly backups to keep (e.g. 12)\")\n .option(\"--remote <url>\", \"Remote destination (s3://, rsync://, or local path)\")\n .option(\"--status\", \"Show current schedule\")\n .option(\"--clear\", \"Remove backup schedule\")\n .action((opts) => runBackupSchedule(opts, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd()));\n\nconst verifySubCommand = new Command(\"verify\")\n .argument(\"<path>\", \"Path to backup zip\")\n .description(\"Verify backup integrity (SHA-256 + zip test)\")\n .action((zipPath: string) => runVerify(zipPath));\n\nconst drillSubCommand = new Command(\"drill\")\n .argument(\"<path>\", \"Path to backup zip\")\n .description(\"Restore-drill: verify a backup is restorable without touching live data\")\n .action(async (zipPath: string) => {\n const report = await runRestoreDrill(zipPath);\n if (!report.ok) process.exitCode = 1;\n });\n\nconst listSubCommand = new Command(\"list\").description(\"List available backups\").action(() => {\n const dir = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n const entries = readBackupLog(dir);\n const fileEntries = listBackupsInDir(dir);\n const combined = entries.length > 0 ? entries : fileEntries;\n if (combined.length === 0) {\n console.log(info(\"No backups found.\"));\n return;\n }\n for (const e of combined) {\n const enc = e.encrypted ? \" [encrypted]\" : \"\";\n const ver = e.verified ? \" ✓\" : \"\";\n const mb = e.sizeBytes > 0 ? ` ${(e.sizeBytes / 1024 / 1024).toFixed(1)} MB` : \"\";\n console.log(` ${bold(e.filename)}${enc}${ver}${mb} ${e.createdAt.slice(0, 10)}`);\n }\n});\n\nexport const backupCommand = new Command(\"backup\")\n .argument(\"[output]\", \"Output path for backup zip\")\n .description(\"Backup customers/ + .agentic/ directories\")\n .option(\"--encrypt\", \"Encrypt the backup (AES-256-GCM)\")\n .option(\"--remote <url>\", \"Also upload to remote (s3://, rsync://, or path)\")\n .action((output?: string, opts?: { encrypt?: boolean; remote?: string }) => {\n void runBackup(output, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd(), opts ?? {});\n });\n\nbackupCommand.addCommand(scheduleSubCommand);\nbackupCommand.addCommand(verifySubCommand);\nbackupCommand.addCommand(drillSubCommand);\nbackupCommand.addCommand(listSubCommand);\n\nexport const restoreCommand = new Command(\"restore\")\n .argument(\"<path>\", \"Path to backup zip\")\n .description(\"Restore from backup zip\")\n .action((zipPath: string) => runRestore(zipPath, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd()));\n","import fs from \"fs\";\nimport path from \"path\";\n\nexport interface AuditEntry {\n timestamp: string; // ISO 8601\n actor: string; // DXCRM_ACTOR env var, or \"system\"\n tool: string; // \"log_interaction\" | \"update_deal\" | \"update_customer_facts\" | etc.\n slug: string; // customer slug\n summary: string; // short description (first 120 chars of summary/deal name)\n}\n\n// File format (one line per entry, append-only):\n// 2026-06-01T09:14:00Z | alice | log_interaction | acme-corp | Called about Q3 renewal...\n\nconst AUDIT_LOG_PATH = \".agentic/audit.log\";\n\nexport function getActor(): string {\n const actor = process.env[\"DXCRM_ACTOR\"];\n return actor && actor.trim().length > 0 ? actor.trim() : \"system\";\n}\n\nexport function writeAuditEntry(dataDir: string, entry: AuditEntry): void {\n const logPath = path.join(dataDir, AUDIT_LOG_PATH);\n const logDir = path.dirname(logPath);\n\n if (!fs.existsSync(logDir)) {\n fs.mkdirSync(logDir, { recursive: true });\n }\n\n const truncatedSummary = entry.summary.slice(0, 120);\n const line = `${entry.timestamp} | ${entry.actor} | ${entry.tool} | ${entry.slug} | ${truncatedSummary}\\n`;\n\n fs.appendFileSync(logPath, line, \"utf-8\");\n}\n\nexport function readAuditLog(dataDir: string): AuditEntry[] {\n const logPath = path.join(dataDir, AUDIT_LOG_PATH);\n\n if (!fs.existsSync(logPath)) {\n return [];\n }\n\n const content = fs.readFileSync(logPath, \"utf-8\") as string;\n const lines = content.split(\"\\n\");\n\n const entries: AuditEntry[] = [];\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n const parts = trimmed.split(\" | \");\n if (parts.length < 5) continue;\n\n const [timestamp, actor, tool, slug, ...summaryParts] = parts;\n const summary = summaryParts.join(\" | \");\n\n if (timestamp && actor && tool && slug) {\n entries.push({ timestamp, actor, tool, slug, summary: summary ?? \"\" });\n }\n }\n\n return entries;\n}\n\nexport function filterAuditLog(\n entries: AuditEntry[],\n opts: { slug?: string; actor?: string; limit?: number }\n): AuditEntry[] {\n let filtered = entries;\n\n if (opts.slug !== undefined) {\n filtered = filtered.filter((e) => e.slug === opts.slug);\n }\n\n if (opts.actor !== undefined) {\n filtered = filtered.filter((e) => e.actor === opts.actor);\n }\n\n if (opts.limit !== undefined) {\n filtered = filtered.slice(-opts.limit);\n }\n\n return filtered;\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { readJsonFile, writeJsonFile } from \"../fs/json-store.js\";\n\nexport type Role = \"admin\" | \"manager\" | \"rep\";\n\nexport interface RbacConfig {\n actors: Record<string, Role>;\n default?: Role;\n owned_customers?: Record<string, string[]>;\n /** Field-level ACL: field name → roles allowed to see it. Others get it redacted. */\n field_acl?: Record<string, Role[]>;\n}\n\nconst ALLOWED_TOOLS: Record<Role, string[]> = {\n admin: [\n \"log_interaction\",\n \"update_deal\",\n \"update_customer_facts\",\n \"export_customer\",\n \"pursue_goal\",\n \"register_push_subscription\",\n \"define_custom_object\",\n \"create_record\",\n ],\n manager: [\"log_interaction\", \"update_deal\", \"pursue_goal\", \"create_record\"],\n rep: [\"log_interaction\", \"update_deal\", \"create_record\"],\n};\n\nfunction rbacPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"rbac.json\");\n}\n\nexport function getRbacConfig(dataDir: string): RbacConfig {\n return readJsonFile<RbacConfig>(rbacPath(dataDir), { actors: {} });\n}\n\nexport function setActorRole(dataDir: string, actor: string, role: Role): void {\n const config = getRbacConfig(dataDir);\n config.actors[actor] = role;\n writeJsonFile(rbacPath(dataDir), config);\n}\n\nexport function getRole(dataDir: string, actor: string): Role {\n const config = getRbacConfig(dataDir);\n return config.actors[actor] ?? config.default ?? \"rep\";\n}\n\nexport function canWrite(role: Role, tool: string): boolean {\n return ALLOWED_TOOLS[role]?.includes(tool) ?? false;\n}\n\nexport function assertCanWrite(role: Role, tool: string, actor: string): void {\n if (!canWrite(role, tool)) {\n throw new Error(`Access denied: '${actor}' (role: ${role}) cannot use tool '${tool}'`);\n }\n}\n\nexport function enforceRbac(dataDir: string, tool: string): void {\n if (!fs.existsSync(rbacPath(dataDir))) return; // no rbac.json = open access\n const actor = process.env[\"DXCRM_ACTOR\"] ?? \"system\";\n if (actor === \"system\") return; // internal system actor bypasses RBAC\n const role = getRole(dataDir, actor);\n assertCanWrite(role, tool, actor);\n}\n\nexport function canSeeCustomer(dataDir: string, actor: string, slug: string): boolean {\n if (!fs.existsSync(rbacPath(dataDir))) return true; // open access\n if (actor === \"system\") return true; // internal system actor always has full access\n const config = getRbacConfig(dataDir);\n const role = config.actors[actor] ?? config.default ?? \"rep\";\n if (role === \"admin\" || role === \"manager\") return true;\n // rep: only sees customers listed in owned_customers[actor]\n const owned = config.owned_customers;\n if (!owned) return false;\n return (owned[actor] ?? []).includes(slug);\n}\n\n/**\n * Build a once-loaded predicate for which customers `actor` may see. Equivalent\n * to calling canSeeCustomer per slug, but reads/parses rbac.json a single time\n * (and uses O(1) Set membership) — for hot loops like list_customers.\n */\nexport function customerVisibility(dataDir: string, actor: string): (slug: string) => boolean {\n if (!fs.existsSync(rbacPath(dataDir))) return () => true; // open access\n if (actor === \"system\") return () => true;\n const config = getRbacConfig(dataDir);\n const role = config.actors[actor] ?? config.default ?? \"rep\";\n if (role === \"admin\" || role === \"manager\") return () => true;\n const owned = new Set(config.owned_customers?.[actor] ?? []);\n return (slug: string) => owned.has(slug);\n}\n\n/** Load the field-level ACL (field → allowed roles) from rbac.json. */\nexport function loadFieldAcl(dataDir: string): Record<string, Role[]> {\n return getRbacConfig(dataDir).field_acl ?? {};\n}\n\n/** Whether a role may see a field given the ACL (fields not in the ACL are public). */\nexport function canSeeField(field: string, role: Role, acl: Record<string, Role[]>): boolean {\n const allowed = acl[field];\n if (!allowed) return true;\n return allowed.includes(role);\n}\n\n/** Return a copy of `values` with fields the role may not see removed. */\nexport function redactFields<T extends Record<string, unknown>>(\n values: T,\n role: Role,\n acl: Record<string, Role[]>\n): Partial<T> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(values)) {\n if (canSeeField(k, role, acl)) out[k] = v;\n }\n return out as Partial<T>;\n}\n","export interface Session {\n customerSlug: string;\n customerName: string;\n startedAt: string;\n owner?: string;\n}\n\nlet activeSession: Session | null = null;\n\nexport function setSession(s: Session): void {\n activeSession = s;\n}\n\nexport function getSession(): Session | null {\n return activeSession;\n}\n\nexport function clearSession(): void {\n activeSession = null;\n}\n"],"mappings":"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"}
|