@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,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
+
const require_atomic_write = require("./atomic-write-BYmF-ThH.cjs");
|
|
2
3
|
let commander = require("commander");
|
|
3
4
|
let path = require("path");
|
|
4
5
|
path = require_chunk.__toESM(path, 1);
|
|
@@ -8,10 +9,10 @@ let gray_matter = require("gray-matter");
|
|
|
8
9
|
gray_matter = require_chunk.__toESM(gray_matter, 1);
|
|
9
10
|
let zod_validation_error = require("zod-validation-error");
|
|
10
11
|
let zod = require("zod");
|
|
12
|
+
let crypto = require("crypto");
|
|
11
13
|
let ansis = require("ansis");
|
|
12
14
|
ansis = require_chunk.__toESM(ansis, 1);
|
|
13
15
|
let child_process = require("child_process");
|
|
14
|
-
let crypto = require("crypto");
|
|
15
16
|
//#region src/schemas/main-facts.ts
|
|
16
17
|
const MainFactsSchema = zod.z.object({
|
|
17
18
|
name: zod.z.string().min(1),
|
|
@@ -34,8 +35,27 @@ const MainFactsSchema = zod.z.object({
|
|
|
34
35
|
updated: zod.z.preprocess((v) => v instanceof Date ? v.toISOString().slice(0, 10) : v, zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD required"))
|
|
35
36
|
});
|
|
36
37
|
//#endregion
|
|
38
|
+
//#region src/fs/safe-path.ts
|
|
39
|
+
/**
|
|
40
|
+
* Filesystem path-segment safety. A segment (customer slug, custom-object name,
|
|
41
|
+
* KB article id/category, …) is safe iff it cannot escape its parent directory:
|
|
42
|
+
* no path separators, no `..`, no NUL, not "." or empty, and bounded in length.
|
|
43
|
+
* Enforced wherever an untrusted value (from an MCP tool, API, or import) is used
|
|
44
|
+
* to build a file path, to prevent path-traversal (arbitrary read/write).
|
|
45
|
+
*/
|
|
46
|
+
function isSafePathSegment(segment) {
|
|
47
|
+
return typeof segment === "string" && segment.length > 0 && segment.length <= 128 && segment !== "." && !segment.includes("/") && !segment.includes("\\") && !segment.includes("\0") && !segment.includes("..");
|
|
48
|
+
}
|
|
49
|
+
function assertSafePathSegment(segment, kind = "path segment") {
|
|
50
|
+
if (!isSafePathSegment(segment)) throw new Error(`Invalid ${kind}: ${JSON.stringify(segment)}`);
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
37
53
|
//#region src/fs/customer-dir.ts
|
|
54
|
+
function assertSafeSlug(slug) {
|
|
55
|
+
assertSafePathSegment(slug, "customer slug");
|
|
56
|
+
}
|
|
38
57
|
function getCustomerDir(dataDir, slug) {
|
|
58
|
+
assertSafeSlug(slug);
|
|
39
59
|
return path.default.join(dataDir, "customers", slug);
|
|
40
60
|
}
|
|
41
61
|
function customerExists(dataDir, slug) {
|
|
@@ -62,8 +82,7 @@ async function ensureCustomerDir(dataDir, slug) {
|
|
|
62
82
|
async function writeMainFacts(dataDir, slug, facts) {
|
|
63
83
|
const filePath = path.default.join(getCustomerDir(dataDir, slug), "main_facts.md");
|
|
64
84
|
const clean = Object.fromEntries(Object.entries(facts).filter(([, v]) => v !== void 0));
|
|
65
|
-
|
|
66
|
-
fs.default.writeFileSync(filePath, content, "utf-8");
|
|
85
|
+
require_atomic_write.writeFileAtomic(filePath, gray_matter.default.stringify("", clean));
|
|
67
86
|
}
|
|
68
87
|
async function readMainFacts(dataDir, slug) {
|
|
69
88
|
const filePath = path.default.join(getCustomerDir(dataDir, slug), "main_facts.md");
|
|
@@ -79,6 +98,44 @@ async function readMainFacts(dataDir, slug) {
|
|
|
79
98
|
return result.data;
|
|
80
99
|
}
|
|
81
100
|
//#endregion
|
|
101
|
+
//#region src/fs/json-store.ts
|
|
102
|
+
/**
|
|
103
|
+
* Small shared JSON persistence helpers. Many modules independently reimplemented
|
|
104
|
+
* the same "read a JSON file, fall back to a default on missing/parse-error" and
|
|
105
|
+
* "write a `{ key: items }` array store" logic — this centralizes both so the
|
|
106
|
+
* behavior (and the silent-fallback semantics) is defined in exactly one place.
|
|
107
|
+
*
|
|
108
|
+
* Writes go through writeFileAtomic, so a crash mid-write can never leave a
|
|
109
|
+
* half-written (corrupt) JSON file — readers always see either the old or the
|
|
110
|
+
* new complete document. This matters for the config, audit, and state files
|
|
111
|
+
* the whole product depends on.
|
|
112
|
+
*/
|
|
113
|
+
/** Read and parse a JSON file, returning `fallback` if it is missing or invalid. */
|
|
114
|
+
function readJsonFile(filePath, fallback) {
|
|
115
|
+
if (!fs.default.existsSync(filePath)) return fallback;
|
|
116
|
+
try {
|
|
117
|
+
return JSON.parse(fs.default.readFileSync(filePath, "utf-8"));
|
|
118
|
+
} catch {
|
|
119
|
+
return fallback;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Atomically write `value` as pretty-printed JSON, creating parent dirs as needed. */
|
|
123
|
+
function writeJsonFile(filePath, value) {
|
|
124
|
+
require_atomic_write.writeFileAtomic(filePath, JSON.stringify(value, null, 2));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Read an array stored under `key` in a `{ [key]: T[] }` JSON document. Returns
|
|
128
|
+
* an empty array if the file is missing, unparsable, or the key is not an array.
|
|
129
|
+
*/
|
|
130
|
+
function readJsonArray(filePath, key) {
|
|
131
|
+
const arr = readJsonFile(filePath, {})[key];
|
|
132
|
+
return Array.isArray(arr) ? arr : [];
|
|
133
|
+
}
|
|
134
|
+
/** Write an array under `key` as a `{ [key]: items }` JSON document. */
|
|
135
|
+
function writeJsonArray(filePath, key, items) {
|
|
136
|
+
writeJsonFile(filePath, { [key]: items });
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
82
139
|
//#region src/ui/colors.ts
|
|
83
140
|
const success = (s) => ansis.default.green(s);
|
|
84
141
|
const error = (s) => ansis.default.red(s);
|
|
@@ -100,9 +157,7 @@ function readAgenticConfig(dataDir) {
|
|
|
100
157
|
}
|
|
101
158
|
}
|
|
102
159
|
function writeAgenticConfig(dataDir, config) {
|
|
103
|
-
|
|
104
|
-
fs.default.mkdirSync(path.default.dirname(filePath), { recursive: true });
|
|
105
|
-
fs.default.writeFileSync(filePath, JSON.stringify(config, null, 2), "utf-8");
|
|
160
|
+
writeJsonFile(getConfigPath(dataDir), config);
|
|
106
161
|
}
|
|
107
162
|
function countDir(dir) {
|
|
108
163
|
let files = 0;
|
|
@@ -184,8 +239,7 @@ function appendBackupLog(dataDir, entry) {
|
|
|
184
239
|
entries = entries.filter((e) => e.filename !== entry.filename);
|
|
185
240
|
entries.unshift(entry);
|
|
186
241
|
if (entries.length > 100) entries = entries.slice(0, 100);
|
|
187
|
-
|
|
188
|
-
fs.default.writeFileSync(logPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
242
|
+
writeJsonFile(logPath, entries);
|
|
189
243
|
}
|
|
190
244
|
function readBackupLog(dataDir) {
|
|
191
245
|
const logPath = path.default.join(dataDir, ".agentic", "backup-log.json");
|
|
@@ -496,13 +550,7 @@ function rbacPath(dataDir) {
|
|
|
496
550
|
return path.default.join(dataDir, ".agentic", "rbac.json");
|
|
497
551
|
}
|
|
498
552
|
function getRbacConfig(dataDir) {
|
|
499
|
-
|
|
500
|
-
if (!fs.default.existsSync(p)) return { actors: {} };
|
|
501
|
-
try {
|
|
502
|
-
return JSON.parse(fs.default.readFileSync(p, "utf-8"));
|
|
503
|
-
} catch {
|
|
504
|
-
return { actors: {} };
|
|
505
|
-
}
|
|
553
|
+
return readJsonFile(rbacPath(dataDir), { actors: {} });
|
|
506
554
|
}
|
|
507
555
|
function getRole(dataDir, actor) {
|
|
508
556
|
const config = getRbacConfig(dataDir);
|
|
@@ -530,6 +578,20 @@ function canSeeCustomer(dataDir, actor, slug) {
|
|
|
530
578
|
if (!owned) return false;
|
|
531
579
|
return (owned[actor] ?? []).includes(slug);
|
|
532
580
|
}
|
|
581
|
+
/**
|
|
582
|
+
* Build a once-loaded predicate for which customers `actor` may see. Equivalent
|
|
583
|
+
* to calling canSeeCustomer per slug, but reads/parses rbac.json a single time
|
|
584
|
+
* (and uses O(1) Set membership) — for hot loops like list_customers.
|
|
585
|
+
*/
|
|
586
|
+
function customerVisibility(dataDir, actor) {
|
|
587
|
+
if (!fs.default.existsSync(rbacPath(dataDir))) return () => true;
|
|
588
|
+
if (actor === "system") return () => true;
|
|
589
|
+
const config = getRbacConfig(dataDir);
|
|
590
|
+
const role = config.actors[actor] ?? config.default ?? "rep";
|
|
591
|
+
if (role === "admin" || role === "manager") return () => true;
|
|
592
|
+
const owned = new Set(config.owned_customers?.[actor] ?? []);
|
|
593
|
+
return (slug) => owned.has(slug);
|
|
594
|
+
}
|
|
533
595
|
//#endregion
|
|
534
596
|
//#region src/core/session-store.ts
|
|
535
597
|
let activeSession = null;
|
|
@@ -549,6 +611,18 @@ Object.defineProperty(exports, "MainFactsSchema", {
|
|
|
549
611
|
return MainFactsSchema;
|
|
550
612
|
}
|
|
551
613
|
});
|
|
614
|
+
Object.defineProperty(exports, "assertSafePathSegment", {
|
|
615
|
+
enumerable: true,
|
|
616
|
+
get: function() {
|
|
617
|
+
return assertSafePathSegment;
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
Object.defineProperty(exports, "assertSafeSlug", {
|
|
621
|
+
enumerable: true,
|
|
622
|
+
get: function() {
|
|
623
|
+
return assertSafeSlug;
|
|
624
|
+
}
|
|
625
|
+
});
|
|
552
626
|
Object.defineProperty(exports, "bold", {
|
|
553
627
|
enumerable: true,
|
|
554
628
|
get: function() {
|
|
@@ -573,6 +647,12 @@ Object.defineProperty(exports, "customerExists", {
|
|
|
573
647
|
return customerExists;
|
|
574
648
|
}
|
|
575
649
|
});
|
|
650
|
+
Object.defineProperty(exports, "customerVisibility", {
|
|
651
|
+
enumerable: true,
|
|
652
|
+
get: function() {
|
|
653
|
+
return customerVisibility;
|
|
654
|
+
}
|
|
655
|
+
});
|
|
576
656
|
Object.defineProperty(exports, "enforceRbac", {
|
|
577
657
|
enumerable: true,
|
|
578
658
|
get: function() {
|
|
@@ -627,6 +707,12 @@ Object.defineProperty(exports, "info", {
|
|
|
627
707
|
return info;
|
|
628
708
|
}
|
|
629
709
|
});
|
|
710
|
+
Object.defineProperty(exports, "isSafePathSegment", {
|
|
711
|
+
enumerable: true,
|
|
712
|
+
get: function() {
|
|
713
|
+
return isSafePathSegment;
|
|
714
|
+
}
|
|
715
|
+
});
|
|
630
716
|
Object.defineProperty(exports, "listBackupsInDir", {
|
|
631
717
|
enumerable: true,
|
|
632
718
|
get: function() {
|
|
@@ -651,6 +737,18 @@ Object.defineProperty(exports, "readBackupLog", {
|
|
|
651
737
|
return readBackupLog;
|
|
652
738
|
}
|
|
653
739
|
});
|
|
740
|
+
Object.defineProperty(exports, "readJsonArray", {
|
|
741
|
+
enumerable: true,
|
|
742
|
+
get: function() {
|
|
743
|
+
return readJsonArray;
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
Object.defineProperty(exports, "readJsonFile", {
|
|
747
|
+
enumerable: true,
|
|
748
|
+
get: function() {
|
|
749
|
+
return readJsonFile;
|
|
750
|
+
}
|
|
751
|
+
});
|
|
654
752
|
Object.defineProperty(exports, "readMainFacts", {
|
|
655
753
|
enumerable: true,
|
|
656
754
|
get: function() {
|
|
@@ -687,6 +785,18 @@ Object.defineProperty(exports, "writeAuditEntry", {
|
|
|
687
785
|
return writeAuditEntry;
|
|
688
786
|
}
|
|
689
787
|
});
|
|
788
|
+
Object.defineProperty(exports, "writeJsonArray", {
|
|
789
|
+
enumerable: true,
|
|
790
|
+
get: function() {
|
|
791
|
+
return writeJsonArray;
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
Object.defineProperty(exports, "writeJsonFile", {
|
|
795
|
+
enumerable: true,
|
|
796
|
+
get: function() {
|
|
797
|
+
return writeJsonFile;
|
|
798
|
+
}
|
|
799
|
+
});
|
|
690
800
|
Object.defineProperty(exports, "writeMainFacts", {
|
|
691
801
|
enumerable: true,
|
|
692
802
|
get: function() {
|
|
@@ -694,4 +804,4 @@ Object.defineProperty(exports, "writeMainFacts", {
|
|
|
694
804
|
}
|
|
695
805
|
});
|
|
696
806
|
|
|
697
|
-
//# sourceMappingURL=session-store-
|
|
807
|
+
//# sourceMappingURL=session-store-yfwnj0OC.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store-yfwnj0OC.cjs","names":["z","matter","Command"],"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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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as loadSlaRules, n as checkSlaBreaches, t as calcSlaDue } from "./sla-engine-
|
|
1
|
+
import { i as loadSlaRules, n as checkSlaBreaches, t as calcSlaDue } from "./sla-engine-O-A1ntu_.js";
|
|
2
2
|
export { calcSlaDue, checkSlaBreaches, loadSlaRules };
|
|
@@ -44,10 +44,10 @@ function isSlaBreach(ticket, today) {
|
|
|
44
44
|
return ticket.slaDue < today;
|
|
45
45
|
}
|
|
46
46
|
async function checkSlaBreaches(dataDir, today) {
|
|
47
|
-
const { listAllTickets } = await import("./ticket-writer-
|
|
47
|
+
const { listAllTickets } = await import("./ticket-writer-DsfpeLGZ.js");
|
|
48
48
|
return (await listAllTickets(dataDir)).filter(({ ticket }) => isSlaBreach(ticket, today));
|
|
49
49
|
}
|
|
50
50
|
//#endregion
|
|
51
51
|
export { loadSlaRules as i, checkSlaBreaches as n, isSlaBreach as r, calcSlaDue as t };
|
|
52
52
|
|
|
53
|
-
//# sourceMappingURL=sla-engine-
|
|
53
|
+
//# sourceMappingURL=sla-engine-O-A1ntu_.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sla-engine-
|
|
1
|
+
{"version":3,"file":"sla-engine-O-A1ntu_.js","names":[],"sources":["../src/core/sla-engine.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\nimport type { Ticket } from \"../schemas/ticket.js\";\n\nexport interface SlaRule {\n priority: Ticket[\"priority\"];\n resolveDays: number;\n}\n\nconst DEFAULT_RULES: SlaRule[] = [\n { priority: \"urgent\", resolveDays: 1 },\n { priority: \"high\", resolveDays: 2 },\n { priority: \"normal\", resolveDays: 5 },\n { priority: \"low\", resolveDays: 10 },\n];\n\nexport function loadSlaRules(dataDir: string): SlaRule[] {\n const p = path.join(dataDir, \".agentic\", \"sla-rules.yaml\");\n if (!fs.existsSync(p)) return DEFAULT_RULES;\n try {\n const raw = yaml.load(fs.readFileSync(p, \"utf-8\") as string) as { rules?: SlaRule[] };\n return raw?.rules ?? DEFAULT_RULES;\n } catch {\n return DEFAULT_RULES;\n }\n}\n\nfunction addDaysToDate(isoDate: string, days: number): string {\n const [year, month, day] = isoDate.split(\"-\").map(Number) as [number, number, number];\n const d = new Date(Date.UTC(year, month - 1, day));\n d.setUTCDate(d.getUTCDate() + days);\n return d.toISOString().slice(0, 10);\n}\n\nexport function calcSlaDue(\n createdDate: string,\n priority: Ticket[\"priority\"],\n rules: SlaRule[]\n): string {\n const rule = rules.find((r) => r.priority === priority) ?? { resolveDays: 5 };\n return addDaysToDate(createdDate, rule.resolveDays);\n}\n\nexport function isSlaBreach(ticket: Ticket, today: string): boolean {\n if (ticket.status === \"resolved\" || ticket.status === \"closed\") return false;\n if (!ticket.slaDue) return false;\n return ticket.slaDue < today;\n}\n\nexport async function checkSlaBreaches(\n dataDir: string,\n today: string\n): Promise<Array<{ slug: string; ticket: Ticket }>> {\n const { listAllTickets } = await import(\"../fs/ticket-writer.js\");\n const all = await listAllTickets(dataDir);\n return all.filter(({ ticket }) => isSlaBreach(ticket, today));\n}\n"],"mappings":";;;;AAUA,MAAM,gBAA2B;CAC/B;EAAE,UAAU;EAAU,aAAa;CAAE;CACrC;EAAE,UAAU;EAAQ,aAAa;CAAE;CACnC;EAAE,UAAU;EAAU,aAAa;CAAE;CACrC;EAAE,UAAU;EAAO,aAAa;CAAG;AACrC;AAEA,SAAgB,aAAa,SAA4B;CACvD,MAAM,IAAI,KAAK,KAAK,SAAS,YAAY,gBAAgB;CACzD,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO;CAC9B,IAAI;EAEF,OADY,KAAK,KAAK,GAAG,aAAa,GAAG,OAAO,CACvC,GAAG,SAAS;CACvB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,cAAc,SAAiB,MAAsB;CAC5D,MAAM,CAAC,MAAM,OAAO,OAAO,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;CACxD,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC;CACjD,EAAE,WAAW,EAAE,WAAW,IAAI,IAAI;CAClC,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACpC;AAEA,SAAgB,WACd,aACA,UACA,OACQ;CAER,OAAO,cAAc,cADR,MAAM,MAAM,MAAM,EAAE,aAAa,QAAQ,KAAK,EAAE,aAAa,EAAE,GACrC,WAAW;AACpD;AAEA,SAAgB,YAAY,QAAgB,OAAwB;CAClE,IAAI,OAAO,WAAW,cAAc,OAAO,WAAW,UAAU,OAAO;CACvE,IAAI,CAAC,OAAO,QAAQ,OAAO;CAC3B,OAAO,OAAO,SAAS;AACzB;AAEA,eAAsB,iBACpB,SACA,OACkD;CAClD,MAAM,EAAE,mBAAmB,MAAM,OAAO;CAExC,QAAO,MADW,eAAe,OAAO,GAC7B,QAAQ,EAAE,aAAa,YAAY,QAAQ,KAAK,CAAC;AAC9D"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { r as writeJsonArray, t as readJsonArray } from "./json-store-WWsFzXub.js";
|
|
1
2
|
import { t as hybridSearch } from "./hybrid-search-BmHttLrR.js";
|
|
2
3
|
import path from "path";
|
|
3
|
-
import fs from "fs";
|
|
4
4
|
import { randomBytes } from "crypto";
|
|
5
5
|
//#region src/core/sop.ts
|
|
6
6
|
function globalPath(dataDir) {
|
|
@@ -10,17 +10,10 @@ function customerPath(dataDir, slug) {
|
|
|
10
10
|
return path.join(dataDir, "customers", slug, "sops.json");
|
|
11
11
|
}
|
|
12
12
|
function readFile(p) {
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
const data = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
16
|
-
return Array.isArray(data.sops) ? data.sops : [];
|
|
17
|
-
} catch {
|
|
18
|
-
return [];
|
|
19
|
-
}
|
|
13
|
+
return readJsonArray(p, "sops");
|
|
20
14
|
}
|
|
21
15
|
function writeFile(p, sops) {
|
|
22
|
-
|
|
23
|
-
fs.writeFileSync(p, JSON.stringify({ sops }, null, 2), "utf-8");
|
|
16
|
+
writeJsonArray(p, "sops", sops);
|
|
24
17
|
}
|
|
25
18
|
function addSop(dataDir, s) {
|
|
26
19
|
const sop = {
|
|
@@ -67,4 +60,4 @@ async function findSops(dataDir, query, slug) {
|
|
|
67
60
|
//#endregion
|
|
68
61
|
export { findSops as n, loadSops as r, addSop as t };
|
|
69
62
|
|
|
70
|
-
//# sourceMappingURL=sop-
|
|
63
|
+
//# sourceMappingURL=sop-BV7ICAFR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sop-BV7ICAFR.js","names":[],"sources":["../src/core/sop.ts"],"sourcesContent":["import { randomBytes } from \"crypto\";\nimport path from \"path\";\nimport { hybridSearch } from \"./hybrid-search.js\";\nimport { readJsonArray, writeJsonArray } from \"../fs/json-store.js\";\n\n/**\n * SOP module (domino D7 / F5): Standard Operating Procedures — procedural\n * instructions, global or per customer, found via hybrid search to guide task\n * execution (\"how we do X\"). Customer-specific SOPs take precedence over global.\n */\nexport interface Sop {\n id: string;\n scope: \"global\" | \"customer\";\n slug?: string;\n title: string;\n triggers: string[];\n tags?: string[];\n body: string;\n createdAt: string;\n}\n\nfunction globalPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sops.json\");\n}\nfunction customerPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"sops.json\");\n}\n\nfunction readFile(p: string): Sop[] {\n return readJsonArray<Sop>(p, \"sops\");\n}\nfunction writeFile(p: string, sops: Sop[]): void {\n writeJsonArray(p, \"sops\", sops);\n}\n\nexport function addSop(\n dataDir: string,\n s: {\n scope: \"global\" | \"customer\";\n slug?: string;\n title: string;\n triggers: string[];\n tags?: string[];\n body: string;\n }\n): Sop {\n const sop: Sop = {\n id: `sop_${randomBytes(5).toString(\"hex\")}`,\n scope: s.scope,\n ...(s.slug ? { slug: s.slug } : {}),\n title: s.title,\n triggers: s.triggers,\n ...(s.tags ? { tags: s.tags } : {}),\n body: s.body,\n createdAt: new Date().toISOString(),\n };\n const p = s.scope === \"global\" ? globalPath(dataDir) : customerPath(dataDir, s.slug ?? \"\");\n writeFile(p, [...readFile(p), sop]);\n return sop;\n}\n\nexport function loadSops(dataDir: string, slug?: string): Sop[] {\n const global = readFile(globalPath(dataDir));\n if (!slug) return global;\n return [...global, ...readFile(customerPath(dataDir, slug))];\n}\n\n/**\n * Find SOPs relevant to a task. Hybrid-search over title+triggers+body; among\n * matches, customer-specific SOPs are returned before global ones.\n */\nexport async function findSops(dataDir: string, query: string, slug?: string): Promise<Sop[]> {\n const sops = loadSops(dataDir, slug);\n const docs = sops.map((s) => ({\n id: s.id,\n text: `${s.title} ${s.triggers.join(\" \")} ${(s.tags ?? []).join(\" \")} ${s.body}`,\n }));\n const ranked = hybridSearch(query, docs);\n const byId = new Map(sops.map((s) => [s.id, s]));\n const matched = ranked.map((r, i) => ({ sop: byId.get(r.id)!, rank: i })).filter((x) => x.sop);\n // Customer-scoped SOPs first, then by relevance rank.\n matched.sort((a, b) => {\n const ac = a.sop.scope === \"customer\" ? 0 : 1;\n const bc = b.sop.scope === \"customer\" ? 0 : 1;\n return ac !== bc ? ac - bc : a.rank - b.rank;\n });\n return matched.map((x) => x.sop);\n}\n"],"mappings":";;;;;AAqBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,WAAW;AACnD;AACA,SAAS,aAAa,SAAiB,MAAsB;CAC3D,OAAO,KAAK,KAAK,SAAS,aAAa,MAAM,WAAW;AAC1D;AAEA,SAAS,SAAS,GAAkB;CAClC,OAAO,cAAmB,GAAG,MAAM;AACrC;AACA,SAAS,UAAU,GAAW,MAAmB;CAC/C,eAAe,GAAG,QAAQ,IAAI;AAChC;AAEA,SAAgB,OACd,SACA,GAQK;CACL,MAAM,MAAW;EACf,IAAI,OAAO,YAAY,CAAC,EAAE,SAAS,KAAK;EACxC,OAAO,EAAE;EACT,GAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;EACjC,OAAO,EAAE;EACT,UAAU,EAAE;EACZ,GAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;EACjC,MAAM,EAAE;EACR,4BAAW,IAAI,KAAK,GAAE,YAAY;CACpC;CACA,MAAM,IAAI,EAAE,UAAU,WAAW,WAAW,OAAO,IAAI,aAAa,SAAS,EAAE,QAAQ,EAAE;CACzF,UAAU,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC;CAClC,OAAO;AACT;AAEA,SAAgB,SAAS,SAAiB,MAAsB;CAC9D,MAAM,SAAS,SAAS,WAAW,OAAO,CAAC;CAC3C,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO,CAAC,GAAG,QAAQ,GAAG,SAAS,aAAa,SAAS,IAAI,CAAC,CAAC;AAC7D;;;;;AAMA,eAAsB,SAAS,SAAiB,OAAe,MAA+B;CAC5F,MAAM,OAAO,SAAS,SAAS,IAAI;CAKnC,MAAM,SAAS,aAAa,OAJf,KAAK,KAAK,OAAO;EAC5B,IAAI,EAAE;EACN,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,SAAS,KAAK,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,GAAG,KAAK,GAAG,EAAE,GAAG,EAAE;CAC5E,EACsC,CAAC;CACvC,MAAM,OAAO,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CAC/C,MAAM,UAAU,OAAO,KAAK,GAAG,OAAO;EAAE,KAAK,KAAK,IAAI,EAAE,EAAE;EAAI,MAAM;CAAE,EAAE,EAAE,QAAQ,MAAM,EAAE,GAAG;CAE7F,QAAQ,MAAM,GAAG,MAAM;EACrB,MAAM,KAAK,EAAE,IAAI,UAAU,aAAa,IAAI;EAC5C,MAAM,KAAK,EAAE,IAAI,UAAU,aAAa,IAAI;EAC5C,OAAO,OAAO,KAAK,KAAK,KAAK,EAAE,OAAO,EAAE;CAC1C,CAAC;CACD,OAAO,QAAQ,KAAK,MAAM,EAAE,GAAG;AACjC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as findSops, r as loadSops, t as addSop } from "./sop-
|
|
1
|
+
import { n as findSops, r as loadSops, t as addSop } from "./sop-BV7ICAFR.js";
|
|
2
2
|
export { addSop, findSops, loadSops };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
|
|
1
2
|
import path from "path";
|
|
2
3
|
import fs from "fs";
|
|
3
4
|
import { z } from "zod";
|
|
@@ -56,7 +57,7 @@ function getSurvey(dataDir, surveyId) {
|
|
|
56
57
|
function writeSurvey(dataDir, survey) {
|
|
57
58
|
const dir = surveysDir(dataDir);
|
|
58
59
|
fs.mkdirSync(dir, { recursive: true });
|
|
59
|
-
|
|
60
|
+
writeFileAtomic(path.join(dir, `${survey.id}.yaml`), yaml.dump(survey));
|
|
60
61
|
}
|
|
61
62
|
function listSurveys(dataDir) {
|
|
62
63
|
const dir = surveysDir(dataDir);
|
|
@@ -99,7 +100,7 @@ async function recordSurveyResponse(dataDir, token, score, comment) {
|
|
|
99
100
|
const dir = responsesDir(dataDir, pending.surveyId);
|
|
100
101
|
fs.mkdirSync(dir, { recursive: true });
|
|
101
102
|
const filename = `${pending.slug}_${pending.contactEmail.replace("@", "_at_")}_${Date.now()}.json`;
|
|
102
|
-
|
|
103
|
+
writeFileAtomic(path.join(dir, filename), JSON.stringify(response, null, 2));
|
|
103
104
|
fs.unlinkSync(path.join(pendingDir, file));
|
|
104
105
|
return response;
|
|
105
106
|
} catch {
|
|
@@ -139,9 +140,9 @@ async function savePendingSurvey(dataDir, surveyId, slug, contactEmail, token) {
|
|
|
139
140
|
contactEmail,
|
|
140
141
|
sentAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
141
142
|
};
|
|
142
|
-
|
|
143
|
+
writeFileAtomic(path.join(pendingDir, filename), JSON.stringify(pending, null, 2));
|
|
143
144
|
}
|
|
144
145
|
//#endregion
|
|
145
146
|
export { listSurveys as a, responsesDir as c, writeSurvey as d, getSurvey as i, savePendingSurvey as l, calcNpsScore as n, loadSurveyResponses as o, generateSurveyToken as r, recordSurveyResponse as s, buildSurveyEmail as t, surveysDir as u };
|
|
146
147
|
|
|
147
|
-
//# sourceMappingURL=survey-engine-
|
|
148
|
+
//# sourceMappingURL=survey-engine-DngXBv47.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"survey-engine-DngXBv47.js","names":[],"sources":["../src/schemas/survey.ts","../src/core/survey-engine.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const SurveyDefinitionSchema = z.object({\n id: z.string().min(1),\n type: z.enum([\"nps\", \"csat\", \"ces\"]).default(\"nps\"),\n question: z.string().min(1),\n scale: z\n .object({ min: z.number().default(0), max: z.number().default(10) })\n .default({ min: 0, max: 10 }),\n includeComment: z.boolean().default(true),\n commentPrompt: z.string().optional(),\n createdAt: z.string(),\n});\n\nexport const SurveyResponseSchema = z.object({\n surveyId: z.string(),\n slug: z.string(),\n contactEmail: z.string().email(),\n score: z.number().int(),\n comment: z.string().optional(),\n respondedAt: z.string(),\n token: z.string(),\n sentAt: z.string(),\n});\n\nexport type SurveyDefinition = z.infer<typeof SurveyDefinitionSchema>;\nexport type SurveyResponse = z.infer<typeof SurveyResponseSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { writeFileAtomic } from \"../fs/atomic-write.js\";\nimport { createHmac } from \"crypto\";\nimport yaml from \"js-yaml\";\nimport {\n SurveyDefinitionSchema,\n SurveyResponseSchema,\n type SurveyDefinition,\n type SurveyResponse,\n} from \"../schemas/survey.js\";\n\nconst SURVEY_SECRET = process.env[\"DXCRM_SURVEY_SECRET\"] ?? \"dxcrm-survey-default-secret\";\n\nexport function surveysDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"surveys\");\n}\n\nexport function responsesDir(dataDir: string, surveyId: string): string {\n return path.join(dataDir, \".agentic\", \"survey-responses\", surveyId);\n}\n\nexport function getSurvey(dataDir: string, surveyId: string): SurveyDefinition | null {\n const p = path.join(surveysDir(dataDir), `${surveyId}.yaml`);\n if (!fs.existsSync(p)) return null;\n try {\n const raw = yaml.load(fs.readFileSync(p, \"utf-8\") as string);\n const result = SurveyDefinitionSchema.safeParse(raw);\n return result.success ? result.data : null;\n } catch {\n return null;\n }\n}\n\nexport function writeSurvey(dataDir: string, survey: SurveyDefinition): void {\n const dir = surveysDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n writeFileAtomic(path.join(dir, `${survey.id}.yaml`), yaml.dump(survey));\n}\n\nexport function listSurveys(dataDir: string): SurveyDefinition[] {\n const dir = surveysDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".yaml\"))\n .flatMap((f) => {\n const s = getSurvey(dataDir, f.replace(/\\.yaml$/, \"\"));\n return s ? [s] : [];\n });\n}\n\nexport function generateSurveyToken(slug: string, contactEmail: string, surveyId: string): string {\n return createHmac(\"sha256\", SURVEY_SECRET)\n .update(`${slug}:${contactEmail}:${surveyId}`)\n .digest(\"hex\")\n .slice(0, 16);\n}\n\nexport function buildSurveyEmail(\n survey: SurveyDefinition,\n serverUrl: string,\n token: string\n): { subject: string; body: string } {\n const scores = Array.from(\n { length: survey.scale.max - survey.scale.min + 1 },\n (_, i) => i + survey.scale.min\n );\n const buttons = scores\n .map(\n (s) =>\n `<a href=\"${serverUrl}/survey/respond?token=${token}&score=${s}\" style=\"display:inline-block;margin:4px;padding:10px 16px;background:#1a1a2e;color:white;text-decoration:none;border-radius:4px;\">${s}</a>`\n )\n .join(\"\");\n\n const body = `<p>${survey.question}</p>\n<p>${buttons}</p>\n${survey.includeComment ? `<p>Or <a href=\"${serverUrl}/survey/respond?token=${token}&comment=true\">Click here to add a comment</a></p>` : \"\"}`;\n\n return {\n subject: survey.type === \"nps\" ? \"How likely are you to recommend us?\" : \"Rate your experience\",\n body,\n };\n}\n\nexport async function recordSurveyResponse(\n dataDir: string,\n token: string,\n score: number,\n comment?: string\n): Promise<SurveyResponse | null> {\n // Find pending response by token\n const pendingDir = path.join(dataDir, \".agentic\", \"survey-pending\");\n if (!fs.existsSync(pendingDir)) return null;\n\n const files = fs.readdirSync(pendingDir).filter((f) => f.endsWith(\".json\"));\n for (const file of files) {\n try {\n const pending = JSON.parse(\n fs.readFileSync(path.join(pendingDir, file), \"utf-8\") as string\n ) as {\n token: string;\n surveyId: string;\n slug: string;\n contactEmail: string;\n sentAt: string;\n };\n if (pending.token !== token) continue;\n\n const response: SurveyResponse = {\n surveyId: pending.surveyId,\n slug: pending.slug,\n contactEmail: pending.contactEmail,\n score,\n ...(comment ? { comment } : {}),\n respondedAt: new Date().toISOString(),\n token,\n sentAt: pending.sentAt,\n };\n\n const dir = responsesDir(dataDir, pending.surveyId);\n fs.mkdirSync(dir, { recursive: true });\n const filename = `${pending.slug}_${pending.contactEmail.replace(\"@\", \"_at_\")}_${Date.now()}.json`;\n writeFileAtomic(path.join(dir, filename), JSON.stringify(response, null, 2));\n\n // Delete pending entry\n fs.unlinkSync(path.join(pendingDir, file));\n return response;\n } catch {\n continue;\n }\n }\n return null;\n}\n\nexport function loadSurveyResponses(\n dataDir: string,\n surveyId: string,\n slug?: string\n): SurveyResponse[] {\n const dir = responsesDir(dataDir, surveyId);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .flatMap((f) => {\n try {\n const raw = JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\") as string) as unknown;\n const parsed = SurveyResponseSchema.safeParse(raw);\n if (!parsed.success) return [];\n if (slug && parsed.data.slug !== slug) return [];\n return [parsed.data];\n } catch {\n return [];\n }\n });\n}\n\nexport function calcNpsScore(responses: SurveyResponse[]): number {\n if (responses.length === 0) return 0;\n const promoters = responses.filter((r) => r.score >= 9).length;\n const detractors = responses.filter((r) => r.score <= 6).length;\n return Math.round(((promoters - detractors) / responses.length) * 100);\n}\n\nexport async function savePendingSurvey(\n dataDir: string,\n surveyId: string,\n slug: string,\n contactEmail: string,\n token: string\n): Promise<void> {\n const pendingDir = path.join(dataDir, \".agentic\", \"survey-pending\");\n fs.mkdirSync(pendingDir, { recursive: true });\n const filename = `${token}.json`;\n const pending = { token, surveyId, slug, contactEmail, sentAt: new Date().toISOString() };\n writeFileAtomic(path.join(pendingDir, filename), JSON.stringify(pending, null, 2));\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { r as updateSlugSyncState, t as getLastGmailSync } from "./sync-state-
|
|
1
|
+
import { r as updateSlugSyncState, t as getLastGmailSync } from "./sync-state-DMZgzpez.js";
|
|
2
2
|
export { getLastGmailSync, updateSlugSyncState };
|
|
@@ -1,27 +1,19 @@
|
|
|
1
|
+
import { i as writeJsonFile, n as readJsonFile } from "./json-store-WWsFzXub.js";
|
|
1
2
|
import path from "path";
|
|
2
|
-
import fs from "fs";
|
|
3
3
|
//#region src/fs/sync-state.ts
|
|
4
4
|
function getSyncStatePath(dataDir) {
|
|
5
5
|
return path.join(dataDir, ".agentic", "sync-state.json");
|
|
6
6
|
}
|
|
7
7
|
function readSyncState(dataDir) {
|
|
8
|
-
|
|
9
|
-
if (!fs.existsSync(filePath)) return {};
|
|
10
|
-
try {
|
|
11
|
-
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
12
|
-
} catch {
|
|
13
|
-
return {};
|
|
14
|
-
}
|
|
8
|
+
return readJsonFile(getSyncStatePath(dataDir), {});
|
|
15
9
|
}
|
|
16
10
|
function updateSlugSyncState(dataDir, slug, update) {
|
|
17
|
-
const filePath = getSyncStatePath(dataDir);
|
|
18
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
19
11
|
const state = readSyncState(dataDir);
|
|
20
12
|
state[slug] = {
|
|
21
13
|
...state[slug],
|
|
22
14
|
...update
|
|
23
15
|
};
|
|
24
|
-
|
|
16
|
+
writeJsonFile(getSyncStatePath(dataDir), state);
|
|
25
17
|
}
|
|
26
18
|
function getLastGmailSync(dataDir, slug) {
|
|
27
19
|
const ts = readSyncState(dataDir)[slug]?.lastGmailSync;
|
|
@@ -30,4 +22,4 @@ function getLastGmailSync(dataDir, slug) {
|
|
|
30
22
|
//#endregion
|
|
31
23
|
export { readSyncState as n, updateSlugSyncState as r, getLastGmailSync as t };
|
|
32
24
|
|
|
33
|
-
//# sourceMappingURL=sync-state-
|
|
25
|
+
//# sourceMappingURL=sync-state-DMZgzpez.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-state-DMZgzpez.js","names":[],"sources":["../src/fs/sync-state.ts"],"sourcesContent":["import path from \"path\";\nimport { readJsonFile, writeJsonFile } from \"./json-store.js\";\n\nexport interface SlugSyncState {\n lastGmailSync?: string;\n lastCalendarSync?: string;\n lastGmailPushHistoryId?: string;\n lastMicrosoftPushAt?: string;\n}\n\nexport interface SyncState {\n [slug: string]: SlugSyncState;\n}\n\nfunction getSyncStatePath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sync-state.json\");\n}\n\nexport function readSyncState(dataDir: string): SyncState {\n return readJsonFile<SyncState>(getSyncStatePath(dataDir), {});\n}\n\nexport function writeSyncState(dataDir: string, state: SyncState): void {\n writeJsonFile(getSyncStatePath(dataDir), state);\n}\n\nexport function updateSlugSyncState(\n dataDir: string,\n slug: string,\n update: Partial<SlugSyncState>\n): void {\n const state = readSyncState(dataDir);\n state[slug] = { ...state[slug], ...update };\n writeJsonFile(getSyncStatePath(dataDir), state);\n}\n\nexport function getLastGmailSync(dataDir: string, slug: string): Date | undefined {\n const ts = readSyncState(dataDir)[slug]?.lastGmailSync;\n return ts ? new Date(ts) : undefined;\n}\n\nexport function getLastCalendarSync(dataDir: string, slug: string): Date | undefined {\n const ts = readSyncState(dataDir)[slug]?.lastCalendarSync;\n return ts ? new Date(ts) : undefined;\n}\n"],"mappings":";;;AAcA,SAAS,iBAAiB,SAAyB;CACjD,OAAO,KAAK,KAAK,SAAS,YAAY,iBAAiB;AACzD;AAEA,SAAgB,cAAc,SAA4B;CACxD,OAAO,aAAwB,iBAAiB,OAAO,GAAG,CAAC,CAAC;AAC9D;AAMA,SAAgB,oBACd,SACA,MACA,QACM;CACN,MAAM,QAAQ,cAAc,OAAO;CACnC,MAAM,QAAQ;EAAE,GAAG,MAAM;EAAO,GAAG;CAAO;CAC1C,cAAc,iBAAiB,OAAO,GAAG,KAAK;AAChD;AAEA,SAAgB,iBAAiB,SAAiB,MAAgC;CAChF,MAAM,KAAK,cAAc,OAAO,EAAE,OAAO;CACzC,OAAO,KAAK,IAAI,KAAK,EAAE,IAAI,KAAA;AAC7B"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as upsertTicket, n as nextTicketId, r as readTickets, t as listAllTickets } from "./ticket-writer-
|
|
1
|
+
import { i as upsertTicket, n as nextTicketId, r as readTickets, t as listAllTickets } from "./ticket-writer-a9on36Wb.js";
|
|
2
2
|
export { listAllTickets, nextTicketId, readTickets, upsertTicket };
|