@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
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["ensureCustomerDir","writeMainFacts","Command","success","bold","error","filterAuditLog","readAuditLog","Command","matter","warning","listCustomerSlugs","error","info","success","Command"],"sources":["../src/commands/create.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/version.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport slugify from \"slug\";\nimport { ensureCustomerDir, writeMainFacts } from \"../fs/customer-dir.js\";\nimport { success, error, bold } from \"../ui/colors.js\";\n\nexport async function createCustomer(opts: {\n name: string;\n domain?: string;\n email?: string;\n dataDir?: string;\n}): Promise<{ id: string; dir: string }> {\n const id = slugify(opts.name, { lower: true });\n const dataDir = opts.dataDir ?? process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n await ensureCustomerDir(dataDir, id);\n const dir = path.join(dataDir, \"customers\", id);\n\n // Write main_facts.md\n const today = new Date().toISOString().slice(0, 10);\n await writeMainFacts(dataDir, id, {\n name: opts.name,\n domain: opts.domain,\n email: opts.email,\n relationship_stage: \"prospect\",\n tags: [],\n currency: \"EUR\",\n created: today,\n updated: today,\n });\n\n // Create interactions.md\n const interactionsPath = path.join(dir, \"interactions.md\");\n if (!fs.existsSync(interactionsPath)) {\n fs.writeFileSync(interactionsPath, `# Interactions — ${opts.name}\\n\\n`);\n }\n\n // Create pipeline.md\n const pipelinePath = path.join(dir, \"pipeline.md\");\n if (!fs.existsSync(pipelinePath)) {\n fs.writeFileSync(\n pipelinePath,\n `# Pipeline — ${opts.name}\\n\\n| Deal | Stage | Value | Currency | Probability | Close Date | Updated | Notes |\\n|---|---|---|---|---|---|---|---|\\n`\n );\n }\n\n // Create sources.json\n const sourcesPath = path.join(dir, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) {\n const gmailQuery = opts.domain\n ? `from:${opts.domain} OR to:${opts.domain}`\n : opts.email\n ? `from:${opts.email} OR to:${opts.email}`\n : \"\";\n const sources = {\n gmail: {\n type: \"gmail\",\n query: gmailQuery,\n enabled: true,\n },\n version: 1,\n created: new Date().toISOString(),\n };\n fs.writeFileSync(sourcesPath, JSON.stringify(sources, null, 2));\n }\n\n return { id, dir };\n}\n\nexport const createCommand = new Command(\"create\")\n .argument(\"<name>\", \"Customer name\")\n .option(\"--domain <domain>\", \"Primary domain (for Gmail sync)\")\n .option(\"--email <email>\", \"Primary contact email\")\n .action(async (name: string, opts: { domain?: string; email?: string }) => {\n try {\n const { id, dir } = await createCustomer({ name, ...opts });\n console.log(success(`✓ Created customer: ${bold(id)}`));\n console.log(` Dir: ${dir}`);\n console.log(` Files: main_facts.md, interactions.md, pipeline.md, sources.json`);\n } catch (err) {\n console.error(error(`✗ ${(err as Error).message}`));\n process.exit(1);\n }\n });\n","import { Command } from \"commander\";\nimport { readAuditLog, filterAuditLog } from \"../fs/audit-log.js\";\n\nconst SEP = \"─\".repeat(70);\n\nexport async function runAudit(\n opts: {\n slug?: string;\n actor?: string;\n limit?: number;\n tail?: boolean;\n },\n dataDir?: string\n): Promise<void> {\n const dir = dataDir ?? process.cwd();\n const limit = opts.limit ?? 20;\n\n const allEntries = readAuditLog(dir);\n const entries = filterAuditLog(allEntries, {\n ...(opts.slug !== undefined ? { slug: opts.slug } : {}),\n ...(opts.actor !== undefined ? { actor: opts.actor } : {}),\n limit,\n });\n\n console.log(SEP);\n console.log(\" DatasynxOpenCRM — Audit Trail\");\n\n if (opts.slug) console.log(` Customer: ${opts.slug}`);\n if (opts.actor) console.log(` Actor: ${opts.actor}`);\n\n console.log(SEP);\n\n if (entries.length === 0) {\n console.log(\" No audit entries found.\");\n console.log(SEP);\n return;\n }\n\n for (const entry of entries) {\n console.log(\n ` ${entry.timestamp} ${entry.actor.padEnd(12)} ${entry.tool.padEnd(20)} ${entry.slug.padEnd(20)} ${entry.summary}`\n );\n }\n\n console.log(SEP);\n console.log(` ${entries.length} entr${entries.length === 1 ? \"y\" : \"ies\"} shown`);\n console.log(SEP);\n}\n\nexport const auditCommand = new Command(\"audit\")\n .description(\"Show CRM audit trail — who changed what and when\")\n .option(\"--slug <slug>\", \"Filter by customer slug\")\n .option(\"--actor <actor>\", \"Filter by actor\")\n .option(\"--limit <n>\", \"Number of entries to show (default: 20)\", parseInt)\n .option(\"--tail\", \"Show all new entries (simplified: shows current entries)\")\n .action((opts: { slug?: string; actor?: string; limit?: number; tail?: boolean }) =>\n runAudit(opts, process.env[\"DXCRM_DATA_DIR\"])\n );\n","import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { MainFactsSchema } from \"../schemas/main-facts.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport matter from \"gray-matter\";\nimport { success, error, warning, info } from \"../ui/colors.js\";\n\nconst RECOVERABLE_DEFAULTS: Record<string, unknown> = {\n tags: [],\n currency: \"EUR\",\n};\n\nexport function applyFix(\n factsPath: string,\n content: string,\n data: Record<string, unknown>\n): { fixed: string[]; content: string } | null {\n const fixed: string[] = [];\n const patched = { ...data };\n\n for (const [field, defaultValue] of Object.entries(RECOVERABLE_DEFAULTS)) {\n if (patched[field] === undefined || patched[field] === null) {\n patched[field] = defaultValue;\n fixed.push(`${field} → ${JSON.stringify(defaultValue)}`);\n }\n }\n\n if (patched[\"updated\"] === undefined && patched[\"created\"]) {\n patched[\"updated\"] = patched[\"created\"];\n fixed.push(`updated → ${String(patched[\"created\"])}`);\n }\n\n if (fixed.length === 0) return null;\n\n const parsed = matter(content);\n const newContent = matter.stringify(parsed.content, patched);\n fs.writeFileSync(factsPath, newContent);\n return { fixed, content: newContent };\n}\n\nexport async function runValidate(opts: { fix?: boolean }, dataDir: string): Promise<void> {\n const customersDir = path.join(dataDir, \"customers\");\n\n if (!fs.existsSync(customersDir)) {\n console.log(warning(\"⚠ No customers directory found.\"));\n return;\n }\n\n const slugs = listCustomerSlugs(dataDir);\n\n let errorCount = 0;\n let fixedCount = 0;\n\n for (const slug of slugs) {\n const factsPath = path.join(customersDir, slug, \"main_facts.md\");\n const interactionsPath = path.join(customersDir, slug, \"interactions.md\");\n\n if (!fs.existsSync(factsPath)) {\n console.log(error(`✗ ${slug}: missing main_facts.md`));\n errorCount++;\n continue;\n }\n\n try {\n let content = fs.readFileSync(factsPath, \"utf-8\") as string;\n const { data } = matter(content);\n\n if (opts.fix) {\n const result = applyFix(factsPath, content, data as Record<string, unknown>);\n if (result) {\n content = result.content;\n fixedCount++;\n console.log(info(`⚙ ${slug}: fixed ${result.fixed.join(\", \")}`));\n }\n }\n\n const { data: refetchedData } = matter(content);\n MainFactsSchema.parse(refetchedData);\n\n if (!fs.existsSync(interactionsPath)) {\n console.log(warning(`⚠ ${slug}: missing interactions.md`));\n } else {\n console.log(success(`✓ ${slug}`));\n }\n } catch (err) {\n console.log(error(`✗ ${slug}: ${(err as Error).message}`));\n errorCount++;\n }\n }\n\n if (opts.fix && fixedCount > 0) {\n console.log(info(`\\n⚙ Fixed ${fixedCount} customer(s).`));\n }\n\n if (errorCount > 0) {\n console.error(error(`\\n${errorCount} error(s) found.`));\n process.exit(1);\n } else {\n console.log(success(\"\\n✓ All customers valid.\"));\n }\n}\n\nexport const validateCommand = new Command(\"validate\")\n .option(\"--fix\", \"Auto-fix recoverable issues\")\n .action(async (opts: { fix?: boolean }) => {\n await runValidate(opts, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd());\n });\n","export const VERSION = \"0.1.0\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["ensureCustomerDir","writeMainFacts","Command","success","bold","error","filterAuditLog","readAuditLog","Command","matter","warning","listCustomerSlugs","error","info","success","Command"],"sources":["../src/commands/create.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/version.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport slugify from \"slug\";\nimport { ensureCustomerDir, writeMainFacts } from \"../fs/customer-dir.js\";\nimport { writeFileAtomic } from \"../fs/atomic-write.js\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\nimport { success, error, bold } from \"../ui/colors.js\";\n\nexport async function createCustomer(opts: {\n name: string;\n domain?: string;\n email?: string;\n dataDir?: string;\n}): Promise<{ id: string; dir: string }> {\n const id = slugify(opts.name, { lower: true });\n const dataDir = opts.dataDir ?? process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n await ensureCustomerDir(dataDir, id);\n const dir = path.join(dataDir, \"customers\", id);\n\n // Write main_facts.md\n const today = new Date().toISOString().slice(0, 10);\n await writeMainFacts(dataDir, id, {\n name: opts.name,\n domain: opts.domain,\n email: opts.email,\n relationship_stage: \"prospect\",\n tags: [],\n currency: \"EUR\",\n created: today,\n updated: today,\n });\n\n // Create interactions.md\n const interactionsPath = path.join(dir, \"interactions.md\");\n if (!fs.existsSync(interactionsPath)) {\n writeFileAtomic(interactionsPath, `# Interactions — ${opts.name}\\n\\n`);\n }\n\n // Create pipeline.md\n const pipelinePath = path.join(dir, \"pipeline.md\");\n if (!fs.existsSync(pipelinePath)) {\n writeFileAtomic(\n pipelinePath,\n `# Pipeline — ${opts.name}\\n\\n| Deal | Stage | Value | Currency | Probability | Close Date | Updated | Notes |\\n|---|---|---|---|---|---|---|---|\\n`\n );\n }\n\n // Create sources.json\n const sourcesPath = path.join(dir, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) {\n const gmailQuery = opts.domain\n ? `from:${opts.domain} OR to:${opts.domain}`\n : opts.email\n ? `from:${opts.email} OR to:${opts.email}`\n : \"\";\n const sources = {\n gmail: {\n type: \"gmail\",\n query: gmailQuery,\n enabled: true,\n },\n version: 1,\n created: new Date().toISOString(),\n };\n writeJsonFile(sourcesPath, sources);\n }\n\n return { id, dir };\n}\n\nexport const createCommand = new Command(\"create\")\n .description(\"Create a new customer\")\n .argument(\"<name>\", \"Customer name\")\n .option(\"--domain <domain>\", \"Primary domain (for Gmail sync)\")\n .option(\"--email <email>\", \"Primary contact email\")\n .action(async (name: string, opts: { domain?: string; email?: string }) => {\n try {\n const { id, dir } = await createCustomer({ name, ...opts });\n console.log(success(`✓ Created customer: ${bold(id)}`));\n console.log(` Dir: ${dir}`);\n console.log(` Files: main_facts.md, interactions.md, pipeline.md, sources.json`);\n } catch (err) {\n console.error(error(`✗ ${(err as Error).message}`));\n process.exit(1);\n }\n });\n","import { Command } from \"commander\";\nimport { readAuditLog, filterAuditLog } from \"../fs/audit-log.js\";\n\nconst SEP = \"─\".repeat(70);\n\nexport async function runAudit(\n opts: {\n slug?: string;\n actor?: string;\n limit?: number;\n tail?: boolean;\n },\n dataDir?: string\n): Promise<void> {\n const dir = dataDir ?? process.cwd();\n const limit = opts.limit ?? 20;\n\n const allEntries = readAuditLog(dir);\n const entries = filterAuditLog(allEntries, {\n ...(opts.slug !== undefined ? { slug: opts.slug } : {}),\n ...(opts.actor !== undefined ? { actor: opts.actor } : {}),\n limit,\n });\n\n console.log(SEP);\n console.log(\" DatasynxOpenCRM — Audit Trail\");\n\n if (opts.slug) console.log(` Customer: ${opts.slug}`);\n if (opts.actor) console.log(` Actor: ${opts.actor}`);\n\n console.log(SEP);\n\n if (entries.length === 0) {\n console.log(\" No audit entries found.\");\n console.log(SEP);\n return;\n }\n\n for (const entry of entries) {\n console.log(\n ` ${entry.timestamp} ${entry.actor.padEnd(12)} ${entry.tool.padEnd(20)} ${entry.slug.padEnd(20)} ${entry.summary}`\n );\n }\n\n console.log(SEP);\n console.log(` ${entries.length} entr${entries.length === 1 ? \"y\" : \"ies\"} shown`);\n console.log(SEP);\n}\n\nexport const auditCommand = new Command(\"audit\")\n .description(\"Show CRM audit trail — who changed what and when\")\n .option(\"--slug <slug>\", \"Filter by customer slug\")\n .option(\"--actor <actor>\", \"Filter by actor\")\n .option(\"--limit <n>\", \"Number of entries to show (default: 20)\", parseInt)\n .option(\"--tail\", \"Show all new entries (simplified: shows current entries)\")\n .action((opts: { slug?: string; actor?: string; limit?: number; tail?: boolean }) =>\n runAudit(opts, process.env[\"DXCRM_DATA_DIR\"])\n );\n","import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { MainFactsSchema } from \"../schemas/main-facts.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport matter from \"gray-matter\";\nimport { success, error, warning, info } from \"../ui/colors.js\";\n\nconst RECOVERABLE_DEFAULTS: Record<string, unknown> = {\n tags: [],\n currency: \"EUR\",\n};\n\nexport function applyFix(\n factsPath: string,\n content: string,\n data: Record<string, unknown>\n): { fixed: string[]; content: string } | null {\n const fixed: string[] = [];\n const patched = { ...data };\n\n for (const [field, defaultValue] of Object.entries(RECOVERABLE_DEFAULTS)) {\n if (patched[field] === undefined || patched[field] === null) {\n patched[field] = defaultValue;\n fixed.push(`${field} → ${JSON.stringify(defaultValue)}`);\n }\n }\n\n if (patched[\"updated\"] === undefined && patched[\"created\"]) {\n patched[\"updated\"] = patched[\"created\"];\n fixed.push(`updated → ${String(patched[\"created\"])}`);\n }\n\n if (fixed.length === 0) return null;\n\n const parsed = matter(content);\n const newContent = matter.stringify(parsed.content, patched);\n fs.writeFileSync(factsPath, newContent);\n return { fixed, content: newContent };\n}\n\nexport async function runValidate(opts: { fix?: boolean }, dataDir: string): Promise<void> {\n const customersDir = path.join(dataDir, \"customers\");\n\n if (!fs.existsSync(customersDir)) {\n console.log(warning(\"⚠ No customers directory found.\"));\n return;\n }\n\n const slugs = listCustomerSlugs(dataDir);\n\n let errorCount = 0;\n let fixedCount = 0;\n\n for (const slug of slugs) {\n const factsPath = path.join(customersDir, slug, \"main_facts.md\");\n const interactionsPath = path.join(customersDir, slug, \"interactions.md\");\n\n if (!fs.existsSync(factsPath)) {\n console.log(error(`✗ ${slug}: missing main_facts.md`));\n errorCount++;\n continue;\n }\n\n try {\n let content = fs.readFileSync(factsPath, \"utf-8\") as string;\n let parsed = matter(content);\n\n if (opts.fix) {\n const result = applyFix(factsPath, content, parsed.data as Record<string, unknown>);\n if (result) {\n content = result.content;\n parsed = matter(content); // only re-parse when a fix actually rewrote content\n fixedCount++;\n console.log(info(`⚙ ${slug}: fixed ${result.fixed.join(\", \")}`));\n }\n }\n\n MainFactsSchema.parse(parsed.data);\n\n if (!fs.existsSync(interactionsPath)) {\n console.log(warning(`⚠ ${slug}: missing interactions.md`));\n } else {\n console.log(success(`✓ ${slug}`));\n }\n } catch (err) {\n console.log(error(`✗ ${slug}: ${(err as Error).message}`));\n errorCount++;\n }\n }\n\n if (opts.fix && fixedCount > 0) {\n console.log(info(`\\n⚙ Fixed ${fixedCount} customer(s).`));\n }\n\n if (errorCount > 0) {\n console.error(error(`\\n${errorCount} error(s) found.`));\n process.exit(1);\n } else {\n console.log(success(\"\\n✓ All customers valid.\"));\n }\n}\n\nexport const validateCommand = new Command(\"validate\")\n .description(\"Validate all customer data against schemas\")\n .option(\"--fix\", \"Auto-fix recoverable issues\")\n .action(async (opts: { fix?: boolean }) => {\n await runValidate(opts, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd());\n });\n","export const VERSION = \"0.1.0\";\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -138,8 +138,8 @@ declare const InteractionEntrySchema: z.ZodObject<{
|
|
|
138
138
|
sourceRef: z.ZodString;
|
|
139
139
|
synced: z.ZodString;
|
|
140
140
|
}, "strip", z.ZodTypeAny, {
|
|
141
|
-
date: string;
|
|
142
141
|
type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other";
|
|
142
|
+
date: string;
|
|
143
143
|
with: string;
|
|
144
144
|
summary: string;
|
|
145
145
|
nextSteps: string[];
|
|
@@ -148,8 +148,8 @@ declare const InteractionEntrySchema: z.ZodObject<{
|
|
|
148
148
|
direction?: "inbound" | "outbound" | undefined;
|
|
149
149
|
subject?: string | undefined;
|
|
150
150
|
}, {
|
|
151
|
-
date: string;
|
|
152
151
|
type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other";
|
|
152
|
+
date: string;
|
|
153
153
|
with: string;
|
|
154
154
|
summary: string;
|
|
155
155
|
sourceRef: string;
|
|
@@ -515,7 +515,11 @@ interface RbacConfig {
|
|
|
515
515
|
declare function getRbacConfig(dataDir: string): RbacConfig;
|
|
516
516
|
declare function getRole(dataDir: string, actor: string): Role;
|
|
517
517
|
declare function canSeeCustomer(dataDir: string, actor: string, slug: string): boolean;
|
|
518
|
-
/**
|
|
518
|
+
/**
|
|
519
|
+
* Build a once-loaded predicate for which customers `actor` may see. Equivalent
|
|
520
|
+
* to calling canSeeCustomer per slug, but reads/parses rbac.json a single time
|
|
521
|
+
* (and uses O(1) Set membership) — for hot loops like list_customers.
|
|
522
|
+
*/
|
|
519
523
|
//#endregion
|
|
520
524
|
//#region src/core/session-store.d.ts
|
|
521
525
|
interface Session {
|
|
@@ -535,4 +539,4 @@ declare const VERSION = "0.1.0";
|
|
|
535
539
|
|
|
536
540
|
//#endregion
|
|
537
541
|
export { type GlobalSources, type InteractionEntry, type KbArticle, type MainFacts, type PipelineDeal, type QuoteLineItem, type Quote as QuoteRecord, type SurveyDefinition, type SurveyResponse, type TicketPriority, type Ticket as TicketRecord, type TicketStatus, VERSION, canSeeCustomer, clearSession, createCustomer, customerExists, filterAuditLog, getRbacConfig, getRole, getSession, readAuditLog, readMainFacts, runAudit, runBackup, runValidate, setSession };
|
|
538
|
-
//# sourceMappingURL=index-
|
|
542
|
+
//# sourceMappingURL=index-pY7tYXwH.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-pY7tYXwH.d.cts","names":[],"sources":["../src/schemas/sources.ts","../src/schemas/main-facts.ts","../src/schemas/interaction.ts","../src/schemas/pipeline.ts","../src/schemas/ticket.ts","../src/schemas/quote.ts","../src/schemas/kb-article.ts","../src/schemas/survey.ts","../src/commands/create.ts","../src/commands/backup.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/fs/customer-dir.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts","../src/version.ts"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -139,8 +139,8 @@ declare const InteractionEntrySchema: z.ZodObject<{
|
|
|
139
139
|
sourceRef: z.ZodString;
|
|
140
140
|
synced: z.ZodString;
|
|
141
141
|
}, "strip", z.ZodTypeAny, {
|
|
142
|
-
type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other";
|
|
143
142
|
date: string;
|
|
143
|
+
type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other";
|
|
144
144
|
with: string;
|
|
145
145
|
summary: string;
|
|
146
146
|
nextSteps: string[];
|
|
@@ -149,8 +149,8 @@ declare const InteractionEntrySchema: z.ZodObject<{
|
|
|
149
149
|
direction?: "inbound" | "outbound" | undefined;
|
|
150
150
|
subject?: string | undefined;
|
|
151
151
|
}, {
|
|
152
|
-
type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other";
|
|
153
152
|
date: string;
|
|
153
|
+
type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other";
|
|
154
154
|
with: string;
|
|
155
155
|
summary: string;
|
|
156
156
|
sourceRef: string;
|
|
@@ -516,7 +516,11 @@ interface RbacConfig {
|
|
|
516
516
|
declare function getRbacConfig(dataDir: string): RbacConfig;
|
|
517
517
|
declare function getRole(dataDir: string, actor: string): Role;
|
|
518
518
|
declare function canSeeCustomer(dataDir: string, actor: string, slug: string): boolean;
|
|
519
|
-
/**
|
|
519
|
+
/**
|
|
520
|
+
* Build a once-loaded predicate for which customers `actor` may see. Equivalent
|
|
521
|
+
* to calling canSeeCustomer per slug, but reads/parses rbac.json a single time
|
|
522
|
+
* (and uses O(1) Set membership) — for hot loops like list_customers.
|
|
523
|
+
*/
|
|
520
524
|
//#endregion
|
|
521
525
|
//#region src/core/session-store.d.ts
|
|
522
526
|
interface Session {
|
|
@@ -536,4 +540,4 @@ declare const VERSION = "0.1.0";
|
|
|
536
540
|
|
|
537
541
|
//#endregion
|
|
538
542
|
export { type GlobalSources, type InteractionEntry, type KbArticle, type MainFacts, type PipelineDeal, type QuoteLineItem, type Quote as QuoteRecord, type SurveyDefinition, type SurveyResponse, type TicketPriority, type Ticket as TicketRecord, type TicketStatus, VERSION, canSeeCustomer, clearSession, createCustomer, customerExists, filterAuditLog, getRbacConfig, getRole, getSession, readAuditLog, readMainFacts, runAudit, runBackup, runValidate, setSession };
|
|
539
|
-
//# sourceMappingURL=index-
|
|
543
|
+
//# sourceMappingURL=index-B0IMMrp_.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-B0IMMrp_.d.ts","names":[],"sources":["../src/schemas/sources.ts","../src/schemas/main-facts.ts","../src/schemas/interaction.ts","../src/schemas/pipeline.ts","../src/schemas/ticket.ts","../src/schemas/quote.ts","../src/schemas/kb-article.ts","../src/schemas/survey.ts","../src/commands/create.ts","../src/commands/backup.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/fs/customer-dir.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts","../src/version.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as writeMainFacts, D as ensureCustomerDir, E as customerExists, N as MainFactsSchema, O as listCustomerSlugs, _ as error, b as warning, c as getRole, d as readAuditLog, g as bold, h as runBackup, i as canSeeCustomer, k as readMainFacts, l as filterAuditLog, n as getSession, r as setSession, s as getRbacConfig, t as clearSession, v as info, w as writeJsonFile, y as success } from "./session-store-DWxJ5Pof.js";
|
|
2
|
+
import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
|
|
2
3
|
import { Command } from "commander";
|
|
3
4
|
import path from "path";
|
|
4
5
|
import fs from "fs";
|
|
@@ -22,28 +23,25 @@ async function createCustomer(opts) {
|
|
|
22
23
|
updated: today
|
|
23
24
|
});
|
|
24
25
|
const interactionsPath = path.join(dir, "interactions.md");
|
|
25
|
-
if (!fs.existsSync(interactionsPath))
|
|
26
|
+
if (!fs.existsSync(interactionsPath)) writeFileAtomic(interactionsPath, `# Interactions — ${opts.name}\n\n`);
|
|
26
27
|
const pipelinePath = path.join(dir, "pipeline.md");
|
|
27
|
-
if (!fs.existsSync(pipelinePath))
|
|
28
|
+
if (!fs.existsSync(pipelinePath)) writeFileAtomic(pipelinePath, `# Pipeline — ${opts.name}\n\n| Deal | Stage | Value | Currency | Probability | Close Date | Updated | Notes |\n|---|---|---|---|---|---|---|---|\n`);
|
|
28
29
|
const sourcesPath = path.join(dir, "sources.json");
|
|
29
|
-
if (!fs.existsSync(sourcesPath)) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
};
|
|
39
|
-
fs.writeFileSync(sourcesPath, JSON.stringify(sources, null, 2));
|
|
40
|
-
}
|
|
30
|
+
if (!fs.existsSync(sourcesPath)) writeJsonFile(sourcesPath, {
|
|
31
|
+
gmail: {
|
|
32
|
+
type: "gmail",
|
|
33
|
+
query: opts.domain ? `from:${opts.domain} OR to:${opts.domain}` : opts.email ? `from:${opts.email} OR to:${opts.email}` : "",
|
|
34
|
+
enabled: true
|
|
35
|
+
},
|
|
36
|
+
version: 1,
|
|
37
|
+
created: (/* @__PURE__ */ new Date()).toISOString()
|
|
38
|
+
});
|
|
41
39
|
return {
|
|
42
40
|
id,
|
|
43
41
|
dir
|
|
44
42
|
};
|
|
45
43
|
}
|
|
46
|
-
new Command("create").argument("<name>", "Customer name").option("--domain <domain>", "Primary domain (for Gmail sync)").option("--email <email>", "Primary contact email").action(async (name, opts) => {
|
|
44
|
+
new Command("create").description("Create a new customer").argument("<name>", "Customer name").option("--domain <domain>", "Primary domain (for Gmail sync)").option("--email <email>", "Primary contact email").action(async (name, opts) => {
|
|
47
45
|
try {
|
|
48
46
|
const { id, dir } = await createCustomer({
|
|
49
47
|
name,
|
|
@@ -129,17 +127,17 @@ async function runValidate(opts, dataDir) {
|
|
|
129
127
|
}
|
|
130
128
|
try {
|
|
131
129
|
let content = fs.readFileSync(factsPath, "utf-8");
|
|
132
|
-
|
|
130
|
+
let parsed = matter(content);
|
|
133
131
|
if (opts.fix) {
|
|
134
|
-
const result = applyFix(factsPath, content, data);
|
|
132
|
+
const result = applyFix(factsPath, content, parsed.data);
|
|
135
133
|
if (result) {
|
|
136
134
|
content = result.content;
|
|
135
|
+
parsed = matter(content);
|
|
137
136
|
fixedCount++;
|
|
138
137
|
console.log(info(`⚙ ${slug}: fixed ${result.fixed.join(", ")}`));
|
|
139
138
|
}
|
|
140
139
|
}
|
|
141
|
-
|
|
142
|
-
MainFactsSchema.parse(refetchedData);
|
|
140
|
+
MainFactsSchema.parse(parsed.data);
|
|
143
141
|
if (!fs.existsSync(interactionsPath)) console.log(warning(`⚠ ${slug}: missing interactions.md`));
|
|
144
142
|
else console.log(success(`✓ ${slug}`));
|
|
145
143
|
} catch (err) {
|
|
@@ -153,7 +151,7 @@ async function runValidate(opts, dataDir) {
|
|
|
153
151
|
process.exit(1);
|
|
154
152
|
} else console.log(success("\n✓ All customers valid."));
|
|
155
153
|
}
|
|
156
|
-
new Command("validate").option("--fix", "Auto-fix recoverable issues").action(async (opts) => {
|
|
154
|
+
new Command("validate").description("Validate all customer data against schemas").option("--fix", "Auto-fix recoverable issues").action(async (opts) => {
|
|
157
155
|
await runValidate(opts, process.env["DXCRM_DATA_DIR"] ?? process.cwd());
|
|
158
156
|
});
|
|
159
157
|
//#endregion
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/commands/create.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/version.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport slugify from \"slug\";\nimport { ensureCustomerDir, writeMainFacts } from \"../fs/customer-dir.js\";\nimport { success, error, bold } from \"../ui/colors.js\";\n\nexport async function createCustomer(opts: {\n name: string;\n domain?: string;\n email?: string;\n dataDir?: string;\n}): Promise<{ id: string; dir: string }> {\n const id = slugify(opts.name, { lower: true });\n const dataDir = opts.dataDir ?? process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n await ensureCustomerDir(dataDir, id);\n const dir = path.join(dataDir, \"customers\", id);\n\n // Write main_facts.md\n const today = new Date().toISOString().slice(0, 10);\n await writeMainFacts(dataDir, id, {\n name: opts.name,\n domain: opts.domain,\n email: opts.email,\n relationship_stage: \"prospect\",\n tags: [],\n currency: \"EUR\",\n created: today,\n updated: today,\n });\n\n // Create interactions.md\n const interactionsPath = path.join(dir, \"interactions.md\");\n if (!fs.existsSync(interactionsPath)) {\n fs.writeFileSync(interactionsPath, `# Interactions — ${opts.name}\\n\\n`);\n }\n\n // Create pipeline.md\n const pipelinePath = path.join(dir, \"pipeline.md\");\n if (!fs.existsSync(pipelinePath)) {\n fs.writeFileSync(\n pipelinePath,\n `# Pipeline — ${opts.name}\\n\\n| Deal | Stage | Value | Currency | Probability | Close Date | Updated | Notes |\\n|---|---|---|---|---|---|---|---|\\n`\n );\n }\n\n // Create sources.json\n const sourcesPath = path.join(dir, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) {\n const gmailQuery = opts.domain\n ? `from:${opts.domain} OR to:${opts.domain}`\n : opts.email\n ? `from:${opts.email} OR to:${opts.email}`\n : \"\";\n const sources = {\n gmail: {\n type: \"gmail\",\n query: gmailQuery,\n enabled: true,\n },\n version: 1,\n created: new Date().toISOString(),\n };\n fs.writeFileSync(sourcesPath, JSON.stringify(sources, null, 2));\n }\n\n return { id, dir };\n}\n\nexport const createCommand = new Command(\"create\")\n .argument(\"<name>\", \"Customer name\")\n .option(\"--domain <domain>\", \"Primary domain (for Gmail sync)\")\n .option(\"--email <email>\", \"Primary contact email\")\n .action(async (name: string, opts: { domain?: string; email?: string }) => {\n try {\n const { id, dir } = await createCustomer({ name, ...opts });\n console.log(success(`✓ Created customer: ${bold(id)}`));\n console.log(` Dir: ${dir}`);\n console.log(` Files: main_facts.md, interactions.md, pipeline.md, sources.json`);\n } catch (err) {\n console.error(error(`✗ ${(err as Error).message}`));\n process.exit(1);\n }\n });\n","import { Command } from \"commander\";\nimport { readAuditLog, filterAuditLog } from \"../fs/audit-log.js\";\n\nconst SEP = \"─\".repeat(70);\n\nexport async function runAudit(\n opts: {\n slug?: string;\n actor?: string;\n limit?: number;\n tail?: boolean;\n },\n dataDir?: string\n): Promise<void> {\n const dir = dataDir ?? process.cwd();\n const limit = opts.limit ?? 20;\n\n const allEntries = readAuditLog(dir);\n const entries = filterAuditLog(allEntries, {\n ...(opts.slug !== undefined ? { slug: opts.slug } : {}),\n ...(opts.actor !== undefined ? { actor: opts.actor } : {}),\n limit,\n });\n\n console.log(SEP);\n console.log(\" DatasynxOpenCRM — Audit Trail\");\n\n if (opts.slug) console.log(` Customer: ${opts.slug}`);\n if (opts.actor) console.log(` Actor: ${opts.actor}`);\n\n console.log(SEP);\n\n if (entries.length === 0) {\n console.log(\" No audit entries found.\");\n console.log(SEP);\n return;\n }\n\n for (const entry of entries) {\n console.log(\n ` ${entry.timestamp} ${entry.actor.padEnd(12)} ${entry.tool.padEnd(20)} ${entry.slug.padEnd(20)} ${entry.summary}`\n );\n }\n\n console.log(SEP);\n console.log(` ${entries.length} entr${entries.length === 1 ? \"y\" : \"ies\"} shown`);\n console.log(SEP);\n}\n\nexport const auditCommand = new Command(\"audit\")\n .description(\"Show CRM audit trail — who changed what and when\")\n .option(\"--slug <slug>\", \"Filter by customer slug\")\n .option(\"--actor <actor>\", \"Filter by actor\")\n .option(\"--limit <n>\", \"Number of entries to show (default: 20)\", parseInt)\n .option(\"--tail\", \"Show all new entries (simplified: shows current entries)\")\n .action((opts: { slug?: string; actor?: string; limit?: number; tail?: boolean }) =>\n runAudit(opts, process.env[\"DXCRM_DATA_DIR\"])\n );\n","import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { MainFactsSchema } from \"../schemas/main-facts.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport matter from \"gray-matter\";\nimport { success, error, warning, info } from \"../ui/colors.js\";\n\nconst RECOVERABLE_DEFAULTS: Record<string, unknown> = {\n tags: [],\n currency: \"EUR\",\n};\n\nexport function applyFix(\n factsPath: string,\n content: string,\n data: Record<string, unknown>\n): { fixed: string[]; content: string } | null {\n const fixed: string[] = [];\n const patched = { ...data };\n\n for (const [field, defaultValue] of Object.entries(RECOVERABLE_DEFAULTS)) {\n if (patched[field] === undefined || patched[field] === null) {\n patched[field] = defaultValue;\n fixed.push(`${field} → ${JSON.stringify(defaultValue)}`);\n }\n }\n\n if (patched[\"updated\"] === undefined && patched[\"created\"]) {\n patched[\"updated\"] = patched[\"created\"];\n fixed.push(`updated → ${String(patched[\"created\"])}`);\n }\n\n if (fixed.length === 0) return null;\n\n const parsed = matter(content);\n const newContent = matter.stringify(parsed.content, patched);\n fs.writeFileSync(factsPath, newContent);\n return { fixed, content: newContent };\n}\n\nexport async function runValidate(opts: { fix?: boolean }, dataDir: string): Promise<void> {\n const customersDir = path.join(dataDir, \"customers\");\n\n if (!fs.existsSync(customersDir)) {\n console.log(warning(\"⚠ No customers directory found.\"));\n return;\n }\n\n const slugs = listCustomerSlugs(dataDir);\n\n let errorCount = 0;\n let fixedCount = 0;\n\n for (const slug of slugs) {\n const factsPath = path.join(customersDir, slug, \"main_facts.md\");\n const interactionsPath = path.join(customersDir, slug, \"interactions.md\");\n\n if (!fs.existsSync(factsPath)) {\n console.log(error(`✗ ${slug}: missing main_facts.md`));\n errorCount++;\n continue;\n }\n\n try {\n let content = fs.readFileSync(factsPath, \"utf-8\") as string;\n const { data } = matter(content);\n\n if (opts.fix) {\n const result = applyFix(factsPath, content, data as Record<string, unknown>);\n if (result) {\n content = result.content;\n fixedCount++;\n console.log(info(`⚙ ${slug}: fixed ${result.fixed.join(\", \")}`));\n }\n }\n\n const { data: refetchedData } = matter(content);\n MainFactsSchema.parse(refetchedData);\n\n if (!fs.existsSync(interactionsPath)) {\n console.log(warning(`⚠ ${slug}: missing interactions.md`));\n } else {\n console.log(success(`✓ ${slug}`));\n }\n } catch (err) {\n console.log(error(`✗ ${slug}: ${(err as Error).message}`));\n errorCount++;\n }\n }\n\n if (opts.fix && fixedCount > 0) {\n console.log(info(`\\n⚙ Fixed ${fixedCount} customer(s).`));\n }\n\n if (errorCount > 0) {\n console.error(error(`\\n${errorCount} error(s) found.`));\n process.exit(1);\n } else {\n console.log(success(\"\\n✓ All customers valid.\"));\n }\n}\n\nexport const validateCommand = new Command(\"validate\")\n .option(\"--fix\", \"Auto-fix recoverable issues\")\n .action(async (opts: { fix?: boolean }) => {\n await runValidate(opts, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd());\n });\n","export const VERSION = \"0.1.0\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/commands/create.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/version.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport slugify from \"slug\";\nimport { ensureCustomerDir, writeMainFacts } from \"../fs/customer-dir.js\";\nimport { writeFileAtomic } from \"../fs/atomic-write.js\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\nimport { success, error, bold } from \"../ui/colors.js\";\n\nexport async function createCustomer(opts: {\n name: string;\n domain?: string;\n email?: string;\n dataDir?: string;\n}): Promise<{ id: string; dir: string }> {\n const id = slugify(opts.name, { lower: true });\n const dataDir = opts.dataDir ?? process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n await ensureCustomerDir(dataDir, id);\n const dir = path.join(dataDir, \"customers\", id);\n\n // Write main_facts.md\n const today = new Date().toISOString().slice(0, 10);\n await writeMainFacts(dataDir, id, {\n name: opts.name,\n domain: opts.domain,\n email: opts.email,\n relationship_stage: \"prospect\",\n tags: [],\n currency: \"EUR\",\n created: today,\n updated: today,\n });\n\n // Create interactions.md\n const interactionsPath = path.join(dir, \"interactions.md\");\n if (!fs.existsSync(interactionsPath)) {\n writeFileAtomic(interactionsPath, `# Interactions — ${opts.name}\\n\\n`);\n }\n\n // Create pipeline.md\n const pipelinePath = path.join(dir, \"pipeline.md\");\n if (!fs.existsSync(pipelinePath)) {\n writeFileAtomic(\n pipelinePath,\n `# Pipeline — ${opts.name}\\n\\n| Deal | Stage | Value | Currency | Probability | Close Date | Updated | Notes |\\n|---|---|---|---|---|---|---|---|\\n`\n );\n }\n\n // Create sources.json\n const sourcesPath = path.join(dir, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) {\n const gmailQuery = opts.domain\n ? `from:${opts.domain} OR to:${opts.domain}`\n : opts.email\n ? `from:${opts.email} OR to:${opts.email}`\n : \"\";\n const sources = {\n gmail: {\n type: \"gmail\",\n query: gmailQuery,\n enabled: true,\n },\n version: 1,\n created: new Date().toISOString(),\n };\n writeJsonFile(sourcesPath, sources);\n }\n\n return { id, dir };\n}\n\nexport const createCommand = new Command(\"create\")\n .description(\"Create a new customer\")\n .argument(\"<name>\", \"Customer name\")\n .option(\"--domain <domain>\", \"Primary domain (for Gmail sync)\")\n .option(\"--email <email>\", \"Primary contact email\")\n .action(async (name: string, opts: { domain?: string; email?: string }) => {\n try {\n const { id, dir } = await createCustomer({ name, ...opts });\n console.log(success(`✓ Created customer: ${bold(id)}`));\n console.log(` Dir: ${dir}`);\n console.log(` Files: main_facts.md, interactions.md, pipeline.md, sources.json`);\n } catch (err) {\n console.error(error(`✗ ${(err as Error).message}`));\n process.exit(1);\n }\n });\n","import { Command } from \"commander\";\nimport { readAuditLog, filterAuditLog } from \"../fs/audit-log.js\";\n\nconst SEP = \"─\".repeat(70);\n\nexport async function runAudit(\n opts: {\n slug?: string;\n actor?: string;\n limit?: number;\n tail?: boolean;\n },\n dataDir?: string\n): Promise<void> {\n const dir = dataDir ?? process.cwd();\n const limit = opts.limit ?? 20;\n\n const allEntries = readAuditLog(dir);\n const entries = filterAuditLog(allEntries, {\n ...(opts.slug !== undefined ? { slug: opts.slug } : {}),\n ...(opts.actor !== undefined ? { actor: opts.actor } : {}),\n limit,\n });\n\n console.log(SEP);\n console.log(\" DatasynxOpenCRM — Audit Trail\");\n\n if (opts.slug) console.log(` Customer: ${opts.slug}`);\n if (opts.actor) console.log(` Actor: ${opts.actor}`);\n\n console.log(SEP);\n\n if (entries.length === 0) {\n console.log(\" No audit entries found.\");\n console.log(SEP);\n return;\n }\n\n for (const entry of entries) {\n console.log(\n ` ${entry.timestamp} ${entry.actor.padEnd(12)} ${entry.tool.padEnd(20)} ${entry.slug.padEnd(20)} ${entry.summary}`\n );\n }\n\n console.log(SEP);\n console.log(` ${entries.length} entr${entries.length === 1 ? \"y\" : \"ies\"} shown`);\n console.log(SEP);\n}\n\nexport const auditCommand = new Command(\"audit\")\n .description(\"Show CRM audit trail — who changed what and when\")\n .option(\"--slug <slug>\", \"Filter by customer slug\")\n .option(\"--actor <actor>\", \"Filter by actor\")\n .option(\"--limit <n>\", \"Number of entries to show (default: 20)\", parseInt)\n .option(\"--tail\", \"Show all new entries (simplified: shows current entries)\")\n .action((opts: { slug?: string; actor?: string; limit?: number; tail?: boolean }) =>\n runAudit(opts, process.env[\"DXCRM_DATA_DIR\"])\n );\n","import { Command } from \"commander\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { MainFactsSchema } from \"../schemas/main-facts.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport matter from \"gray-matter\";\nimport { success, error, warning, info } from \"../ui/colors.js\";\n\nconst RECOVERABLE_DEFAULTS: Record<string, unknown> = {\n tags: [],\n currency: \"EUR\",\n};\n\nexport function applyFix(\n factsPath: string,\n content: string,\n data: Record<string, unknown>\n): { fixed: string[]; content: string } | null {\n const fixed: string[] = [];\n const patched = { ...data };\n\n for (const [field, defaultValue] of Object.entries(RECOVERABLE_DEFAULTS)) {\n if (patched[field] === undefined || patched[field] === null) {\n patched[field] = defaultValue;\n fixed.push(`${field} → ${JSON.stringify(defaultValue)}`);\n }\n }\n\n if (patched[\"updated\"] === undefined && patched[\"created\"]) {\n patched[\"updated\"] = patched[\"created\"];\n fixed.push(`updated → ${String(patched[\"created\"])}`);\n }\n\n if (fixed.length === 0) return null;\n\n const parsed = matter(content);\n const newContent = matter.stringify(parsed.content, patched);\n fs.writeFileSync(factsPath, newContent);\n return { fixed, content: newContent };\n}\n\nexport async function runValidate(opts: { fix?: boolean }, dataDir: string): Promise<void> {\n const customersDir = path.join(dataDir, \"customers\");\n\n if (!fs.existsSync(customersDir)) {\n console.log(warning(\"⚠ No customers directory found.\"));\n return;\n }\n\n const slugs = listCustomerSlugs(dataDir);\n\n let errorCount = 0;\n let fixedCount = 0;\n\n for (const slug of slugs) {\n const factsPath = path.join(customersDir, slug, \"main_facts.md\");\n const interactionsPath = path.join(customersDir, slug, \"interactions.md\");\n\n if (!fs.existsSync(factsPath)) {\n console.log(error(`✗ ${slug}: missing main_facts.md`));\n errorCount++;\n continue;\n }\n\n try {\n let content = fs.readFileSync(factsPath, \"utf-8\") as string;\n let parsed = matter(content);\n\n if (opts.fix) {\n const result = applyFix(factsPath, content, parsed.data as Record<string, unknown>);\n if (result) {\n content = result.content;\n parsed = matter(content); // only re-parse when a fix actually rewrote content\n fixedCount++;\n console.log(info(`⚙ ${slug}: fixed ${result.fixed.join(\", \")}`));\n }\n }\n\n MainFactsSchema.parse(parsed.data);\n\n if (!fs.existsSync(interactionsPath)) {\n console.log(warning(`⚠ ${slug}: missing interactions.md`));\n } else {\n console.log(success(`✓ ${slug}`));\n }\n } catch (err) {\n console.log(error(`✗ ${slug}: ${(err as Error).message}`));\n errorCount++;\n }\n }\n\n if (opts.fix && fixedCount > 0) {\n console.log(info(`\\n⚙ Fixed ${fixedCount} customer(s).`));\n }\n\n if (errorCount > 0) {\n console.error(error(`\\n${errorCount} error(s) found.`));\n process.exit(1);\n } else {\n console.log(success(\"\\n✓ All customers valid.\"));\n }\n}\n\nexport const validateCommand = new Command(\"validate\")\n .description(\"Validate all customer data against schemas\")\n .option(\"--fix\", \"Auto-fix recoverable issues\")\n .action(async (opts: { fix?: boolean }) => {\n await runValidate(opts, process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd());\n });\n","export const VERSION = \"0.1.0\";\n"],"mappings":"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"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { T as assertSafeSlug } from "./session-store-DWxJ5Pof.js";
|
|
3
|
+
import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
|
|
2
4
|
import { t as withFileQueue } from "./write-queue-IbsAjUnh.js";
|
|
3
5
|
import path from "path";
|
|
4
6
|
import fs from "fs";
|
|
@@ -29,6 +31,7 @@ async function readInteractions(dataDir, slug) {
|
|
|
29
31
|
return fs.readFileSync(filePath, "utf-8");
|
|
30
32
|
}
|
|
31
33
|
async function appendInteraction(dataDir, slug, entry) {
|
|
34
|
+
assertSafeSlug(slug);
|
|
32
35
|
const filePath = path.join(dataDir, "customers", slug, "interactions.md");
|
|
33
36
|
return withFileQueue(filePath, async () => {
|
|
34
37
|
const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8") : "";
|
|
@@ -43,10 +46,10 @@ async function appendInteraction(dataDir, slug, entry) {
|
|
|
43
46
|
newContent = header + formatted + "\n" + body;
|
|
44
47
|
} else newContent = existing + "\n" + formatted;
|
|
45
48
|
}
|
|
46
|
-
|
|
49
|
+
writeFileAtomic(filePath, newContent);
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
52
|
//#endregion
|
|
50
53
|
export { readInteractions as i, formatInteractionEntry as n, interactions_writer_exports as r, appendInteraction as t };
|
|
51
54
|
|
|
52
|
-
//# sourceMappingURL=interactions-writer-
|
|
55
|
+
//# sourceMappingURL=interactions-writer-BZzUIgJd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactions-writer-BZzUIgJd.js","names":[],"sources":["../src/fs/interactions-writer.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport type { InteractionEntry } from \"../schemas/interaction.js\";\nimport { withFileQueue } from \"./write-queue.js\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport { assertSafeSlug } from \"./customer-dir.js\";\n\nconst INTERACTION_SEPARATOR = \"---\";\n\nexport function formatInteractionEntry(entry: InteractionEntry): string {\n const header = `## ${entry.date} · ${entry.type}${entry.direction ? ` · ${entry.direction}` : \"\"}`;\n const withLabel = entry.type === \"Email\" ? \"Subject\" : \"With\";\n const nextStepsBlock =\n entry.nextSteps.length > 0 ? entry.nextSteps.map((s) => `- [ ] ${s}`).join(\"\\n\") : \"- [ ] —\";\n\n return `${header}\n**${withLabel}:** ${entry.with}\n**Summary:** ${entry.summary}\n**Next Steps:**\n${nextStepsBlock}\n**Source:** ${entry.sourceRef}\n**Synced:** ${entry.synced}\n${INTERACTION_SEPARATOR}\n`;\n}\n\nexport async function readInteractions(dataDir: string, slug: string): Promise<string> {\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n if (!fs.existsSync(filePath)) {\n return \"\";\n }\n return fs.readFileSync(filePath, \"utf-8\");\n}\n\n/**\n * Per-run source-ref deduplication index for bulk imports. Loads each slug's\n * interactions file once (not once per row) and tracks freshly-appended refs\n * in memory, so a 5k-row import stays linear instead of re-reading a growing\n * file on every row (the previous O(rows²) behavior).\n */\nexport class InteractionDedup {\n private readonly cache = new Map<string, string>();\n constructor(private readonly dataDir: string) {}\n\n /** True if `sourceRef` already exists for `slug` (on disk or appended this run). */\n async seen(slug: string, sourceRef: string): Promise<boolean> {\n let content = this.cache.get(slug);\n if (content === undefined) {\n content = await readInteractions(this.dataDir, slug).catch(() => \"\");\n this.cache.set(slug, content);\n }\n return content.includes(sourceRef);\n }\n\n /** Record that `sourceRef` was just appended, so later rows dedupe against it. */\n markAppended(slug: string, sourceRef: string): void {\n this.cache.set(slug, (this.cache.get(slug) ?? \"\") + sourceRef);\n }\n}\n\nexport async function appendInteraction(\n dataDir: string,\n slug: string,\n entry: InteractionEntry\n): Promise<void> {\n assertSafeSlug(slug);\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n return withFileQueue(filePath, async () => {\n const existing = fs.existsSync(filePath) ? (fs.readFileSync(filePath, \"utf-8\") as string) : \"\";\n\n const formatted = formatInteractionEntry(entry);\n\n let newContent: string;\n if (existing === \"\") {\n newContent = formatted;\n } else {\n const headerEnd = existing.indexOf(\"\\n\\n\");\n if (headerEnd > -1) {\n const header = existing.slice(0, headerEnd + 2);\n const body = existing.slice(headerEnd + 2);\n newContent = header + formatted + \"\\n\" + body;\n } else {\n newContent = existing + \"\\n\" + formatted;\n }\n }\n\n writeFileAtomic(filePath, newContent);\n });\n}\n"],"mappings":";;;;;;;;;;;;AAOA,MAAM,wBAAwB;AAE9B,SAAgB,uBAAuB,OAAiC;CACtE,MAAM,SAAS,MAAM,MAAM,KAAK,KAAK,MAAM,OAAO,MAAM,YAAY,MAAM,MAAM,cAAc;CAC9F,MAAM,YAAY,MAAM,SAAS,UAAU,YAAY;CACvD,MAAM,iBACJ,MAAM,UAAU,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM,SAAS,GAAG,EAAE,KAAK,IAAI,IAAI;CAErF,OAAO,GAAG,OAAO;IACf,UAAU,MAAM,MAAM,KAAK;eAChB,MAAM,QAAQ;;EAE3B,eAAe;cACH,MAAM,UAAU;cAChB,MAAM,OAAO;EACzB,sBAAsB;;AAExB;AAEA,eAAsB,iBAAiB,SAAiB,MAA+B;CACrF,MAAM,WAAW,KAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,IAAI,CAAC,GAAG,WAAW,QAAQ,GACzB,OAAO;CAET,OAAO,GAAG,aAAa,UAAU,OAAO;AAC1C;AA4BA,eAAsB,kBACpB,SACA,MACA,OACe;CACf,eAAe,IAAI;CACnB,MAAM,WAAW,KAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,OAAO,cAAc,UAAU,YAAY;EACzC,MAAM,WAAW,GAAG,WAAW,QAAQ,IAAK,GAAG,aAAa,UAAU,OAAO,IAAe;EAE5F,MAAM,YAAY,uBAAuB,KAAK;EAE9C,IAAI;EACJ,IAAI,aAAa,IACf,aAAa;OACR;GACL,MAAM,YAAY,SAAS,QAAQ,MAAM;GACzC,IAAI,YAAY,IAAI;IAClB,MAAM,SAAS,SAAS,MAAM,GAAG,YAAY,CAAC;IAC9C,MAAM,OAAO,SAAS,MAAM,YAAY,CAAC;IACzC,aAAa,SAAS,YAAY,OAAO;GAC3C,OACE,aAAa,WAAW,OAAO;EAEnC;EAEA,gBAAgB,UAAU,UAAU;CACtC,CAAC;AACH"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { t as assertSafeSlug } from "./customer-dir-CkMMXhb0.js";
|
|
2
|
+
import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
|
|
1
3
|
import { t as withFileQueue } from "./write-queue-IbsAjUnh.js";
|
|
2
4
|
import path from "path";
|
|
3
5
|
import fs from "fs";
|
|
@@ -22,7 +24,34 @@ async function readInteractions(dataDir, slug) {
|
|
|
22
24
|
if (!fs.existsSync(filePath)) return "";
|
|
23
25
|
return fs.readFileSync(filePath, "utf-8");
|
|
24
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Per-run source-ref deduplication index for bulk imports. Loads each slug's
|
|
29
|
+
* interactions file once (not once per row) and tracks freshly-appended refs
|
|
30
|
+
* in memory, so a 5k-row import stays linear instead of re-reading a growing
|
|
31
|
+
* file on every row (the previous O(rows²) behavior).
|
|
32
|
+
*/
|
|
33
|
+
var InteractionDedup = class {
|
|
34
|
+
dataDir;
|
|
35
|
+
cache = /* @__PURE__ */ new Map();
|
|
36
|
+
constructor(dataDir) {
|
|
37
|
+
this.dataDir = dataDir;
|
|
38
|
+
}
|
|
39
|
+
/** True if `sourceRef` already exists for `slug` (on disk or appended this run). */
|
|
40
|
+
async seen(slug, sourceRef) {
|
|
41
|
+
let content = this.cache.get(slug);
|
|
42
|
+
if (content === void 0) {
|
|
43
|
+
content = await readInteractions(this.dataDir, slug).catch(() => "");
|
|
44
|
+
this.cache.set(slug, content);
|
|
45
|
+
}
|
|
46
|
+
return content.includes(sourceRef);
|
|
47
|
+
}
|
|
48
|
+
/** Record that `sourceRef` was just appended, so later rows dedupe against it. */
|
|
49
|
+
markAppended(slug, sourceRef) {
|
|
50
|
+
this.cache.set(slug, (this.cache.get(slug) ?? "") + sourceRef);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
25
53
|
async function appendInteraction(dataDir, slug, entry) {
|
|
54
|
+
assertSafeSlug(slug);
|
|
26
55
|
const filePath = path.join(dataDir, "customers", slug, "interactions.md");
|
|
27
56
|
return withFileQueue(filePath, async () => {
|
|
28
57
|
const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8") : "";
|
|
@@ -37,10 +66,10 @@ async function appendInteraction(dataDir, slug, entry) {
|
|
|
37
66
|
newContent = header + formatted + "\n" + body;
|
|
38
67
|
} else newContent = existing + "\n" + formatted;
|
|
39
68
|
}
|
|
40
|
-
|
|
69
|
+
writeFileAtomic(filePath, newContent);
|
|
41
70
|
});
|
|
42
71
|
}
|
|
43
72
|
//#endregion
|
|
44
|
-
export {
|
|
73
|
+
export { readInteractions as i, appendInteraction as n, formatInteractionEntry as r, InteractionDedup as t };
|
|
45
74
|
|
|
46
|
-
//# sourceMappingURL=interactions-writer-
|
|
75
|
+
//# sourceMappingURL=interactions-writer-DbSyI2rt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactions-writer-DbSyI2rt.js","names":[],"sources":["../src/fs/interactions-writer.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport type { InteractionEntry } from \"../schemas/interaction.js\";\nimport { withFileQueue } from \"./write-queue.js\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport { assertSafeSlug } from \"./customer-dir.js\";\n\nconst INTERACTION_SEPARATOR = \"---\";\n\nexport function formatInteractionEntry(entry: InteractionEntry): string {\n const header = `## ${entry.date} · ${entry.type}${entry.direction ? ` · ${entry.direction}` : \"\"}`;\n const withLabel = entry.type === \"Email\" ? \"Subject\" : \"With\";\n const nextStepsBlock =\n entry.nextSteps.length > 0 ? entry.nextSteps.map((s) => `- [ ] ${s}`).join(\"\\n\") : \"- [ ] —\";\n\n return `${header}\n**${withLabel}:** ${entry.with}\n**Summary:** ${entry.summary}\n**Next Steps:**\n${nextStepsBlock}\n**Source:** ${entry.sourceRef}\n**Synced:** ${entry.synced}\n${INTERACTION_SEPARATOR}\n`;\n}\n\nexport async function readInteractions(dataDir: string, slug: string): Promise<string> {\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n if (!fs.existsSync(filePath)) {\n return \"\";\n }\n return fs.readFileSync(filePath, \"utf-8\");\n}\n\n/**\n * Per-run source-ref deduplication index for bulk imports. Loads each slug's\n * interactions file once (not once per row) and tracks freshly-appended refs\n * in memory, so a 5k-row import stays linear instead of re-reading a growing\n * file on every row (the previous O(rows²) behavior).\n */\nexport class InteractionDedup {\n private readonly cache = new Map<string, string>();\n constructor(private readonly dataDir: string) {}\n\n /** True if `sourceRef` already exists for `slug` (on disk or appended this run). */\n async seen(slug: string, sourceRef: string): Promise<boolean> {\n let content = this.cache.get(slug);\n if (content === undefined) {\n content = await readInteractions(this.dataDir, slug).catch(() => \"\");\n this.cache.set(slug, content);\n }\n return content.includes(sourceRef);\n }\n\n /** Record that `sourceRef` was just appended, so later rows dedupe against it. */\n markAppended(slug: string, sourceRef: string): void {\n this.cache.set(slug, (this.cache.get(slug) ?? \"\") + sourceRef);\n }\n}\n\nexport async function appendInteraction(\n dataDir: string,\n slug: string,\n entry: InteractionEntry\n): Promise<void> {\n assertSafeSlug(slug);\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n return withFileQueue(filePath, async () => {\n const existing = fs.existsSync(filePath) ? (fs.readFileSync(filePath, \"utf-8\") as string) : \"\";\n\n const formatted = formatInteractionEntry(entry);\n\n let newContent: string;\n if (existing === \"\") {\n newContent = formatted;\n } else {\n const headerEnd = existing.indexOf(\"\\n\\n\");\n if (headerEnd > -1) {\n const header = existing.slice(0, headerEnd + 2);\n const body = existing.slice(headerEnd + 2);\n newContent = header + formatted + \"\\n\" + body;\n } else {\n newContent = existing + \"\\n\" + formatted;\n }\n }\n\n writeFileAtomic(filePath, newContent);\n });\n}\n"],"mappings":";;;;;;AAOA,MAAM,wBAAwB;AAE9B,SAAgB,uBAAuB,OAAiC;CACtE,MAAM,SAAS,MAAM,MAAM,KAAK,KAAK,MAAM,OAAO,MAAM,YAAY,MAAM,MAAM,cAAc;CAC9F,MAAM,YAAY,MAAM,SAAS,UAAU,YAAY;CACvD,MAAM,iBACJ,MAAM,UAAU,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM,SAAS,GAAG,EAAE,KAAK,IAAI,IAAI;CAErF,OAAO,GAAG,OAAO;IACf,UAAU,MAAM,MAAM,KAAK;eAChB,MAAM,QAAQ;;EAE3B,eAAe;cACH,MAAM,UAAU;cAChB,MAAM,OAAO;EACzB,sBAAsB;;AAExB;AAEA,eAAsB,iBAAiB,SAAiB,MAA+B;CACrF,MAAM,WAAW,KAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,IAAI,CAAC,GAAG,WAAW,QAAQ,GACzB,OAAO;CAET,OAAO,GAAG,aAAa,UAAU,OAAO;AAC1C;;;;;;;AAQA,IAAa,mBAAb,MAA8B;CAEC;CAD7B,wBAAyB,IAAI,IAAoB;CACjD,YAAY,SAAkC;EAAjB,KAAA,UAAA;CAAkB;;CAG/C,MAAM,KAAK,MAAc,WAAqC;EAC5D,IAAI,UAAU,KAAK,MAAM,IAAI,IAAI;EACjC,IAAI,YAAY,KAAA,GAAW;GACzB,UAAU,MAAM,iBAAiB,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE;GACnE,KAAK,MAAM,IAAI,MAAM,OAAO;EAC9B;EACA,OAAO,QAAQ,SAAS,SAAS;CACnC;;CAGA,aAAa,MAAc,WAAyB;EAClD,KAAK,MAAM,IAAI,OAAO,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM,SAAS;CAC/D;AACF;AAEA,eAAsB,kBACpB,SACA,MACA,OACe;CACf,eAAe,IAAI;CACnB,MAAM,WAAW,KAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,OAAO,cAAc,UAAU,YAAY;EACzC,MAAM,WAAW,GAAG,WAAW,QAAQ,IAAK,GAAG,aAAa,UAAU,OAAO,IAAe;EAE5F,MAAM,YAAY,uBAAuB,KAAK;EAE9C,IAAI;EACJ,IAAI,aAAa,IACf,aAAa;OACR;GACL,MAAM,YAAY,SAAS,QAAQ,MAAM;GACzC,IAAI,YAAY,IAAI;IAClB,MAAM,SAAS,SAAS,MAAM,GAAG,YAAY,CAAC;IAC9C,MAAM,OAAO,SAAS,MAAM,YAAY,CAAC;IACzC,aAAa,SAAS,YAAY,OAAO;GAC3C,OACE,aAAa,WAAW,OAAO;EAEnC;EAEA,gBAAgB,UAAU,UAAU;CACtC,CAAC;AACH"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
+
const require_session_store = require("./session-store-yfwnj0OC.cjs");
|
|
3
|
+
const require_atomic_write = require("./atomic-write-BYmF-ThH.cjs");
|
|
2
4
|
const require_write_queue = require("./write-queue-BDolUxfs.cjs");
|
|
3
5
|
let path = require("path");
|
|
4
6
|
path = require_chunk.__toESM(path, 1);
|
|
@@ -31,6 +33,7 @@ async function readInteractions(dataDir, slug) {
|
|
|
31
33
|
return fs.default.readFileSync(filePath, "utf-8");
|
|
32
34
|
}
|
|
33
35
|
async function appendInteraction(dataDir, slug, entry) {
|
|
36
|
+
require_session_store.assertSafeSlug(slug);
|
|
34
37
|
const filePath = path.default.join(dataDir, "customers", slug, "interactions.md");
|
|
35
38
|
return require_write_queue.withFileQueue(filePath, async () => {
|
|
36
39
|
const existing = fs.default.existsSync(filePath) ? fs.default.readFileSync(filePath, "utf-8") : "";
|
|
@@ -45,7 +48,7 @@ async function appendInteraction(dataDir, slug, entry) {
|
|
|
45
48
|
newContent = header + formatted + "\n" + body;
|
|
46
49
|
} else newContent = existing + "\n" + formatted;
|
|
47
50
|
}
|
|
48
|
-
|
|
51
|
+
require_atomic_write.writeFileAtomic(filePath, newContent);
|
|
49
52
|
});
|
|
50
53
|
}
|
|
51
54
|
//#endregion
|
|
@@ -74,4 +77,4 @@ Object.defineProperty(exports, "readInteractions", {
|
|
|
74
77
|
}
|
|
75
78
|
});
|
|
76
79
|
|
|
77
|
-
//# sourceMappingURL=interactions-writer-
|
|
80
|
+
//# sourceMappingURL=interactions-writer-a2yzBd7T.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactions-writer-a2yzBd7T.cjs","names":["withFileQueue"],"sources":["../src/fs/interactions-writer.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport type { InteractionEntry } from \"../schemas/interaction.js\";\nimport { withFileQueue } from \"./write-queue.js\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport { assertSafeSlug } from \"./customer-dir.js\";\n\nconst INTERACTION_SEPARATOR = \"---\";\n\nexport function formatInteractionEntry(entry: InteractionEntry): string {\n const header = `## ${entry.date} · ${entry.type}${entry.direction ? ` · ${entry.direction}` : \"\"}`;\n const withLabel = entry.type === \"Email\" ? \"Subject\" : \"With\";\n const nextStepsBlock =\n entry.nextSteps.length > 0 ? entry.nextSteps.map((s) => `- [ ] ${s}`).join(\"\\n\") : \"- [ ] —\";\n\n return `${header}\n**${withLabel}:** ${entry.with}\n**Summary:** ${entry.summary}\n**Next Steps:**\n${nextStepsBlock}\n**Source:** ${entry.sourceRef}\n**Synced:** ${entry.synced}\n${INTERACTION_SEPARATOR}\n`;\n}\n\nexport async function readInteractions(dataDir: string, slug: string): Promise<string> {\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n if (!fs.existsSync(filePath)) {\n return \"\";\n }\n return fs.readFileSync(filePath, \"utf-8\");\n}\n\n/**\n * Per-run source-ref deduplication index for bulk imports. Loads each slug's\n * interactions file once (not once per row) and tracks freshly-appended refs\n * in memory, so a 5k-row import stays linear instead of re-reading a growing\n * file on every row (the previous O(rows²) behavior).\n */\nexport class InteractionDedup {\n private readonly cache = new Map<string, string>();\n constructor(private readonly dataDir: string) {}\n\n /** True if `sourceRef` already exists for `slug` (on disk or appended this run). */\n async seen(slug: string, sourceRef: string): Promise<boolean> {\n let content = this.cache.get(slug);\n if (content === undefined) {\n content = await readInteractions(this.dataDir, slug).catch(() => \"\");\n this.cache.set(slug, content);\n }\n return content.includes(sourceRef);\n }\n\n /** Record that `sourceRef` was just appended, so later rows dedupe against it. */\n markAppended(slug: string, sourceRef: string): void {\n this.cache.set(slug, (this.cache.get(slug) ?? \"\") + sourceRef);\n }\n}\n\nexport async function appendInteraction(\n dataDir: string,\n slug: string,\n entry: InteractionEntry\n): Promise<void> {\n assertSafeSlug(slug);\n const filePath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n return withFileQueue(filePath, async () => {\n const existing = fs.existsSync(filePath) ? (fs.readFileSync(filePath, \"utf-8\") as string) : \"\";\n\n const formatted = formatInteractionEntry(entry);\n\n let newContent: string;\n if (existing === \"\") {\n newContent = formatted;\n } else {\n const headerEnd = existing.indexOf(\"\\n\\n\");\n if (headerEnd > -1) {\n const header = existing.slice(0, headerEnd + 2);\n const body = existing.slice(headerEnd + 2);\n newContent = header + formatted + \"\\n\" + body;\n } else {\n newContent = existing + \"\\n\" + formatted;\n }\n }\n\n writeFileAtomic(filePath, newContent);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAOA,MAAM,wBAAwB;AAE9B,SAAgB,uBAAuB,OAAiC;CACtE,MAAM,SAAS,MAAM,MAAM,KAAK,KAAK,MAAM,OAAO,MAAM,YAAY,MAAM,MAAM,cAAc;CAC9F,MAAM,YAAY,MAAM,SAAS,UAAU,YAAY;CACvD,MAAM,iBACJ,MAAM,UAAU,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM,SAAS,GAAG,EAAE,KAAK,IAAI,IAAI;CAErF,OAAO,GAAG,OAAO;IACf,UAAU,MAAM,MAAM,KAAK;eAChB,MAAM,QAAQ;;EAE3B,eAAe;cACH,MAAM,UAAU;cAChB,MAAM,OAAO;EACzB,sBAAsB;;AAExB;AAEA,eAAsB,iBAAiB,SAAiB,MAA+B;CACrF,MAAM,WAAW,KAAA,QAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,IAAI,CAAC,GAAA,QAAG,WAAW,QAAQ,GACzB,OAAO;CAET,OAAO,GAAA,QAAG,aAAa,UAAU,OAAO;AAC1C;AA4BA,eAAsB,kBACpB,SACA,MACA,OACe;CACf,sBAAA,eAAe,IAAI;CACnB,MAAM,WAAW,KAAA,QAAK,KAAK,SAAS,aAAa,MAAM,iBAAiB;CACxE,OAAOA,oBAAAA,cAAc,UAAU,YAAY;EACzC,MAAM,WAAW,GAAA,QAAG,WAAW,QAAQ,IAAK,GAAA,QAAG,aAAa,UAAU,OAAO,IAAe;EAE5F,MAAM,YAAY,uBAAuB,KAAK;EAE9C,IAAI;EACJ,IAAI,aAAa,IACf,aAAa;OACR;GACL,MAAM,YAAY,SAAS,QAAQ,MAAM;GACzC,IAAI,YAAY,IAAI;IAClB,MAAM,SAAS,SAAS,MAAM,GAAG,YAAY,CAAC;IAC9C,MAAM,OAAO,SAAS,MAAM,YAAY,CAAC;IACzC,aAAa,SAAS,YAAY,OAAO;GAC3C,OACE,aAAa,WAAW,OAAO;EAEnC;EAEA,qBAAA,gBAAgB,UAAU,UAAU;CACtC,CAAC;AACH"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
//#region src/fs/json-store.ts
|
|
4
|
+
/**
|
|
5
|
+
* Small shared JSON persistence helpers. Many modules independently reimplemented
|
|
6
|
+
* the same "read a JSON file, fall back to a default on missing/parse-error" and
|
|
7
|
+
* "write a `{ key: items }` array store" logic — this centralizes both so the
|
|
8
|
+
* behavior (and the silent-fallback semantics) is defined in exactly one place.
|
|
9
|
+
*
|
|
10
|
+
* Writes go through writeFileAtomic, so a crash mid-write can never leave a
|
|
11
|
+
* half-written (corrupt) JSON file — readers always see either the old or the
|
|
12
|
+
* new complete document. This matters for the config, audit, and state files
|
|
13
|
+
* the whole product depends on.
|
|
14
|
+
*/
|
|
15
|
+
/** Read and parse a JSON file, returning `fallback` if it is missing or invalid. */
|
|
16
|
+
function readJsonFile(filePath, fallback) {
|
|
17
|
+
if (!fs.existsSync(filePath)) return fallback;
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
20
|
+
} catch {
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Atomically write `value` as pretty-printed JSON, creating parent dirs as needed. */
|
|
25
|
+
function writeJsonFile(filePath, value) {
|
|
26
|
+
writeFileAtomic(filePath, JSON.stringify(value, null, 2));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Read an array stored under `key` in a `{ [key]: T[] }` JSON document. Returns
|
|
30
|
+
* an empty array if the file is missing, unparsable, or the key is not an array.
|
|
31
|
+
*/
|
|
32
|
+
function readJsonArray(filePath, key) {
|
|
33
|
+
const arr = readJsonFile(filePath, {})[key];
|
|
34
|
+
return Array.isArray(arr) ? arr : [];
|
|
35
|
+
}
|
|
36
|
+
/** Write an array under `key` as a `{ [key]: items }` JSON document. */
|
|
37
|
+
function writeJsonArray(filePath, key, items) {
|
|
38
|
+
writeJsonFile(filePath, { [key]: items });
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { writeJsonFile as i, readJsonFile as n, writeJsonArray as r, readJsonArray as t };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=json-store-WWsFzXub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-store-WWsFzXub.js","names":[],"sources":["../src/fs/json-store.ts"],"sourcesContent":["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"],"mappings":";;;;;;;;;;;;;;;AAgBA,SAAgB,aAAgB,UAAkB,UAAgB;CAChE,IAAI,CAAC,GAAG,WAAW,QAAQ,GAAG,OAAO;CACrC,IAAI;EACF,OAAO,KAAK,MAAM,GAAG,aAAa,UAAU,OAAO,CAAW;CAChE,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAgB,cAAc,UAAkB,OAAsB;CACpE,gBAAgB,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC1D;;;;;AAMA,SAAgB,cAAiB,UAAkB,KAAkB;CAEnE,MAAM,MADO,aAAsC,UAAU,CAAC,CAC/C,EAAE;CACjB,OAAO,MAAM,QAAQ,GAAG,IAAK,MAAc,CAAC;AAC9C;;AAGA,SAAgB,eAAkB,UAAkB,KAAa,OAAkB;CACjF,cAAc,UAAU,GAAG,MAAM,MAAM,CAAC;AAC1C"}
|