@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/daemon/worker.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { i as writeJsonFile } from "../json-store-WWsFzXub.js";
|
|
2
|
+
import { n as logger } from "../logger-Dyl4VcLO.js";
|
|
1
3
|
import path from "path";
|
|
2
4
|
import fs from "fs";
|
|
3
5
|
import { CronJob } from "cron";
|
|
@@ -12,7 +14,7 @@ async function syncWithBackoff(fn, maxRetries = 3) {
|
|
|
12
14
|
const msg = err.message;
|
|
13
15
|
if (msg.includes("429") || msg.includes("rateLimitExceeded")) {
|
|
14
16
|
const delay = Math.pow(2, attempt) * 2e3;
|
|
15
|
-
|
|
17
|
+
logger.warn("daemon", "rate limit, retrying", { delayMs: delay });
|
|
16
18
|
await new Promise((r) => setTimeout(r, delay));
|
|
17
19
|
} else throw err;
|
|
18
20
|
}
|
|
@@ -37,7 +39,7 @@ async function syncAllCustomers() {
|
|
|
37
39
|
const credPath = path.join(DATA_DIR, ".agentic", "gmail-credentials.json");
|
|
38
40
|
if (fs.existsSync(tokenPath) && fs.existsSync(credPath)) {
|
|
39
41
|
const { getGmailAuth } = await import("../gmail-auth-OComS92L.js");
|
|
40
|
-
const { syncGmail } = await import("../gmail-sync-
|
|
42
|
+
const { syncGmail } = await import("../gmail-sync-DueE6tl5.js");
|
|
41
43
|
const auth = await getGmailAuth(credPath, tokenPath);
|
|
42
44
|
await syncWithBackoff(async () => {
|
|
43
45
|
const result = await syncGmail({
|
|
@@ -47,14 +49,20 @@ async function syncAllCustomers() {
|
|
|
47
49
|
query: sources.gmail.query,
|
|
48
50
|
since: /* @__PURE__ */ new Date(Date.now() - 1800 * 1e3)
|
|
49
51
|
});
|
|
50
|
-
if (result.synced > 0)
|
|
51
|
-
|
|
52
|
+
if (result.synced > 0) logger.info("daemon", "synced emails", {
|
|
53
|
+
slug,
|
|
54
|
+
synced: result.synced
|
|
55
|
+
});
|
|
56
|
+
const { updateSlugSyncState } = await import("../sync-state-BaA8LbTI.js");
|
|
52
57
|
updateSlugSyncState(DATA_DIR, slug, { lastGmailSync: (/* @__PURE__ */ new Date()).toISOString() });
|
|
53
58
|
});
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
} catch (err) {
|
|
57
|
-
|
|
62
|
+
logger.error("daemon", "error syncing customer", {
|
|
63
|
+
slug,
|
|
64
|
+
error: err.message
|
|
65
|
+
});
|
|
58
66
|
}
|
|
59
67
|
}
|
|
60
68
|
}
|
|
@@ -64,17 +72,17 @@ async function startWatcher() {
|
|
|
64
72
|
try {
|
|
65
73
|
const sources = JSON.parse(fs.readFileSync(agenticSourcesPath, "utf-8"));
|
|
66
74
|
if (sources.transcripts?.enabled && sources.transcripts.paths?.length) {
|
|
67
|
-
const { watchTranscripts, processTranscriptFileAutoMatch } = await import("../transcript-watcher-
|
|
75
|
+
const { watchTranscripts, processTranscriptFileAutoMatch } = await import("../transcript-watcher-0mh2ZhmH.js");
|
|
68
76
|
watchTranscripts({
|
|
69
77
|
paths: sources.transcripts.paths,
|
|
70
78
|
extensions: sources.transcripts.extensions ?? [".txt", ".vtt"],
|
|
71
79
|
dataDir: DATA_DIR,
|
|
72
80
|
onFile: (filePath) => processTranscriptFileAutoMatch(filePath, DATA_DIR)
|
|
73
81
|
});
|
|
74
|
-
|
|
82
|
+
logger.info("daemon", "watching transcripts (LLM auto-match)");
|
|
75
83
|
}
|
|
76
84
|
} catch (err) {
|
|
77
|
-
|
|
85
|
+
logger.error("daemon", "watcher error", { error: err.message });
|
|
78
86
|
}
|
|
79
87
|
}
|
|
80
88
|
async function checkAgentWakeTriggers() {
|
|
@@ -84,13 +92,13 @@ async function checkAgentWakeTriggers() {
|
|
|
84
92
|
for (const file of files) try {
|
|
85
93
|
const config = JSON.parse(fs.readFileSync(path.join(agentsDir, file), "utf-8"));
|
|
86
94
|
if (!config.wakeOn.includes("email")) continue;
|
|
87
|
-
const { getLastGmailSync } = await import("../sync-state-
|
|
95
|
+
const { getLastGmailSync } = await import("../sync-state-BaA8LbTI.js");
|
|
88
96
|
const lastSync = getLastGmailSync(DATA_DIR, config.slug);
|
|
89
97
|
const lastWake = config.lastWake ? new Date(config.lastWake) : null;
|
|
90
98
|
if (!lastSync) continue;
|
|
91
99
|
if (lastWake && lastSync <= lastWake) continue;
|
|
92
|
-
|
|
93
|
-
const { buildContext } = await import("../context-builder-
|
|
100
|
+
logger.info("daemon", "wake trigger", { slug: config.slug });
|
|
101
|
+
const { buildContext } = await import("../context-builder-hmOPvgso.js");
|
|
94
102
|
const context = await buildContext(DATA_DIR, config.slug).catch(() => null);
|
|
95
103
|
if (!context) continue;
|
|
96
104
|
if (config.channel === "telegram" && process.env["TELEGRAM_BOT_TOKEN"] && (config.telegramChatId ?? process.env["TELEGRAM_CHAT_ID"])) {
|
|
@@ -119,88 +127,106 @@ async function checkAgentWakeTriggers() {
|
|
|
119
127
|
req.write(body);
|
|
120
128
|
req.end();
|
|
121
129
|
});
|
|
122
|
-
|
|
130
|
+
logger.info("daemon", "telegram sent", { slug: config.slug });
|
|
123
131
|
} catch (err) {
|
|
124
|
-
|
|
132
|
+
logger.error("daemon", "telegram failed", { error: err.message });
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
135
|
config.lastWake = (/* @__PURE__ */ new Date()).toISOString();
|
|
128
|
-
|
|
136
|
+
writeJsonFile(path.join(agentsDir, file), config);
|
|
129
137
|
} catch (err) {
|
|
130
|
-
|
|
138
|
+
logger.error("daemon", "agent check error", {
|
|
139
|
+
file,
|
|
140
|
+
error: err.message
|
|
141
|
+
});
|
|
131
142
|
}
|
|
132
143
|
}
|
|
133
144
|
new CronJob(`*/${Math.max(1, parseInt(process.env["DXCRM_DAEMON_INTERVAL"] ?? "30", 10) || 30)} * * * *`, async () => {
|
|
134
145
|
await syncAllCustomers();
|
|
135
146
|
await checkAgentWakeTriggers().catch((err) => {
|
|
136
|
-
|
|
147
|
+
logger.error("daemon", "wake trigger check failed", { error: err.message });
|
|
137
148
|
});
|
|
138
149
|
}, null, true, void 0, null, false, void 0, false, true);
|
|
139
150
|
new CronJob("*/60 * * * *", async () => {
|
|
140
151
|
try {
|
|
141
|
-
const { runScheduledBackupIfDue } = await import("../backup-
|
|
152
|
+
const { runScheduledBackupIfDue } = await import("../backup-LFnC09oV.js");
|
|
142
153
|
await runScheduledBackupIfDue(DATA_DIR);
|
|
143
154
|
} catch (err) {
|
|
144
|
-
|
|
155
|
+
logger.error("daemon", "backup check error", { error: err.message });
|
|
145
156
|
}
|
|
146
157
|
}, null, true, void 0, null, false, void 0, false, true);
|
|
147
158
|
new CronJob("0 6 * * *", async () => {
|
|
148
159
|
try {
|
|
149
|
-
const { renewExpiringSubscriptions } = await import("../push-manager-
|
|
150
|
-
const { buildGmailRenewFn } = await import("../gmail-webhook-handler-
|
|
160
|
+
const { renewExpiringSubscriptions } = await import("../push-manager-BXM-IHfP.js");
|
|
161
|
+
const { buildGmailRenewFn } = await import("../gmail-webhook-handler-B26COilD.js");
|
|
151
162
|
const tokenPath = path.join(DATA_DIR, ".agentic", "gmail-token.json");
|
|
152
163
|
const credPath = path.join(DATA_DIR, ".agentic", "gmail-credentials.json");
|
|
153
|
-
const { readSubscriptions } = await import("../push-manager-
|
|
164
|
+
const { readSubscriptions } = await import("../push-manager-BXM-IHfP.js");
|
|
154
165
|
if ((await readSubscriptions(DATA_DIR)).filter((s) => s.provider === "gmail" && s.status === "active").length === 0) return;
|
|
155
166
|
if (!fs.existsSync(tokenPath) || !fs.existsSync(credPath)) return;
|
|
156
167
|
const { getGmailAuth } = await import("../gmail-auth-OComS92L.js");
|
|
157
168
|
const result = await renewExpiringSubscriptions(DATA_DIR, buildGmailRenewFn((await getGmailAuth(credPath, tokenPath)).credentials?.access_token ?? "", ""), 24);
|
|
158
|
-
if (result.renewed.length > 0)
|
|
159
|
-
if (result.errors.length > 0)
|
|
169
|
+
if (result.renewed.length > 0) logger.info("push", "renewed subscriptions", { count: result.renewed.length });
|
|
170
|
+
if (result.errors.length > 0) logger.warn("push", "renewal errors", { errors: result.errors });
|
|
160
171
|
} catch (err) {
|
|
161
|
-
|
|
172
|
+
logger.error("push", "renewal failed", { error: err.message });
|
|
162
173
|
}
|
|
163
174
|
}, null, true, void 0, null, false, void 0, false, true);
|
|
164
175
|
new CronJob("0 7 * * *", async () => {
|
|
165
176
|
try {
|
|
166
|
-
const { runDailyProactiveChecks } = await import("../proactive-worker-
|
|
177
|
+
const { runDailyProactiveChecks } = await import("../proactive-worker-1zkm6aJD.js");
|
|
167
178
|
const result = await runDailyProactiveChecks(DATA_DIR);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
179
|
+
logger.info("proactive", "daily check", {
|
|
180
|
+
customersChecked: result.customersChecked,
|
|
181
|
+
tasksEnqueued: result.tasksEnqueued
|
|
182
|
+
});
|
|
183
|
+
if (result.errors.length > 0) logger.warn("proactive", "errors during daily check", { errors: result.errors });
|
|
184
|
+
const { drainProactiveQueue } = await import("../notification-dispatcher-inpKyuBz.js");
|
|
171
185
|
const drain = await drainProactiveQueue(DATA_DIR);
|
|
172
|
-
|
|
173
|
-
|
|
186
|
+
logger.info("proactive", "dispatched tasks", {
|
|
187
|
+
sent: drain.sent,
|
|
188
|
+
failed: drain.failed
|
|
189
|
+
});
|
|
190
|
+
const { syncGoalProgressFromPipeline } = await import("../goal-engine-CfDAJTFt.js");
|
|
174
191
|
const goalSync = await syncGoalProgressFromPipeline(DATA_DIR);
|
|
175
|
-
if (goalSync.updated.length > 0)
|
|
192
|
+
if (goalSync.updated.length > 0) logger.info("goals", "progress synced", { updated: goalSync.updated });
|
|
176
193
|
} catch (err) {
|
|
177
|
-
|
|
194
|
+
logger.error("proactive", "daily check failed", { error: err.message });
|
|
178
195
|
}
|
|
179
196
|
}, null, true, void 0, null, false, void 0, false, true);
|
|
180
197
|
new CronJob("0 8 * * *", async () => {
|
|
181
198
|
try {
|
|
182
|
-
const { checkSlaBreaches } = await import("../sla-engine-
|
|
199
|
+
const { checkSlaBreaches } = await import("../sla-engine-CP2KiKDS.js");
|
|
183
200
|
const breaches = await checkSlaBreaches(DATA_DIR, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
184
201
|
if (breaches.length > 0) {
|
|
185
|
-
|
|
186
|
-
for (const { slug, ticket } of breaches)
|
|
202
|
+
logger.warn("tickets", "SLA breaches found", { count: breaches.length });
|
|
203
|
+
for (const { slug, ticket } of breaches) logger.warn("tickets", "SLA breach", {
|
|
204
|
+
slug,
|
|
205
|
+
ticketId: ticket.id,
|
|
206
|
+
title: ticket.title,
|
|
207
|
+
due: ticket.slaDue
|
|
208
|
+
});
|
|
187
209
|
}
|
|
188
210
|
} catch (err) {
|
|
189
|
-
|
|
211
|
+
logger.error("tickets", "SLA check failed", { error: err.message });
|
|
190
212
|
}
|
|
191
213
|
}, null, true, void 0, null, false, void 0, false, true);
|
|
192
214
|
new CronJob("0 */6 * * *", async () => {
|
|
193
215
|
try {
|
|
194
|
-
const { runSequenceCycle } = await import("../sequence-engine-
|
|
216
|
+
const { runSequenceCycle } = await import("../sequence-engine-C6nnewHX.js");
|
|
195
217
|
const result = await runSequenceCycle(DATA_DIR, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
196
|
-
|
|
218
|
+
logger.info("sequences", "cycle complete", {
|
|
219
|
+
sent: result.sent,
|
|
220
|
+
completed: result.completed,
|
|
221
|
+
errors: result.errors.length
|
|
222
|
+
});
|
|
197
223
|
} catch (err) {
|
|
198
|
-
|
|
224
|
+
logger.error("sequences", "cycle failed", { error: err.message });
|
|
199
225
|
}
|
|
200
226
|
}, null, true, void 0, null, false, void 0, false, true);
|
|
201
227
|
await startWatcher();
|
|
202
228
|
if (process.send) process.send("ready");
|
|
203
|
-
|
|
229
|
+
logger.info("daemon", "daemon started");
|
|
204
230
|
//#endregion
|
|
205
231
|
export {};
|
|
206
232
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","names":[],"sources":["../../src/daemon/worker.ts"],"sourcesContent":["// src/daemon/worker.ts\n// Standalone detached process — started by `dxcrm daemon start`\n// Handles background Gmail sync + transcript watching via cron\nimport { CronJob } from \"cron\";\nimport fs from \"fs\";\nimport path from \"path\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nconst MAX_CUSTOMERS_PER_CYCLE = 50;\n\nasync function syncWithBackoff(fn: () => Promise<void>, maxRetries = 3): Promise<void> {\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n await fn();\n return;\n } catch (err) {\n const msg = (err as Error).message;\n if (msg.includes(\"429\") || msg.includes(\"rateLimitExceeded\")) {\n const delay = Math.pow(2, attempt) * 2000; // 2s, 4s, 8s\n process.stderr.write(`[daemon] Rate limit, retrying in ${delay}ms\\n`);\n await new Promise((r) => setTimeout(r, delay));\n } else {\n throw err;\n }\n }\n }\n}\n\nasync function syncAllCustomers(): Promise<void> {\n const customersDir = path.join(DATA_DIR, \"customers\");\n if (!fs.existsSync(customersDir)) return;\n\n const slugs = fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n const slugsToSync = slugs.slice(0, MAX_CUSTOMERS_PER_CYCLE);\n\n for (const slug of slugsToSync) {\n const sourcesPath = path.join(customersDir, slug, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) continue;\n\n try {\n const sources = JSON.parse(fs.readFileSync(sourcesPath, \"utf-8\")) as {\n gmail?: { query?: string; enabled?: boolean };\n };\n\n if (sources.gmail?.enabled && sources.gmail.query) {\n // Gmail sync requires auth — skip if token not configured\n const tokenPath = path.join(DATA_DIR, \".agentic\", \"gmail-token.json\");\n const credPath = path.join(DATA_DIR, \".agentic\", \"gmail-credentials.json\");\n if (fs.existsSync(tokenPath) && fs.existsSync(credPath)) {\n const { getGmailAuth } = await import(\"../sync/gmail-auth.js\");\n const { syncGmail } = await import(\"../sync/gmail-sync.js\");\n const auth = await getGmailAuth(credPath, tokenPath);\n await syncWithBackoff(async () => {\n const result = await syncGmail({\n slug,\n dataDir: DATA_DIR,\n auth,\n query: sources.gmail!.query!,\n since: new Date(Date.now() - 30 * 60 * 1000), // last 30 min\n });\n if (result.synced > 0) {\n process.stderr.write(`[daemon] ${slug}: synced ${result.synced} emails\\n`);\n }\n // Update sync state after each successful customer sync\n const { updateSlugSyncState } = await import(\"../fs/sync-state.js\");\n updateSlugSyncState(DATA_DIR, slug, { lastGmailSync: new Date().toISOString() });\n });\n }\n }\n } catch (err) {\n process.stderr.write(`[daemon] Error syncing ${slug}: ${(err as Error).message}\\n`);\n }\n }\n}\n\n// Start transcript watcher\nasync function startWatcher(): Promise<void> {\n const agenticSourcesPath = path.join(DATA_DIR, \".agentic\", \"sources.json\");\n if (!fs.existsSync(agenticSourcesPath)) return;\n\n try {\n const sources = JSON.parse(fs.readFileSync(agenticSourcesPath, \"utf-8\")) as {\n transcripts?: { paths?: string[]; extensions?: string[]; enabled?: boolean };\n };\n\n if (sources.transcripts?.enabled && sources.transcripts.paths?.length) {\n const { watchTranscripts, processTranscriptFileAutoMatch } =\n await import(\"../sync/transcript-watcher.js\");\n watchTranscripts({\n paths: sources.transcripts.paths,\n extensions: sources.transcripts.extensions ?? [\".txt\", \".vtt\"],\n dataDir: DATA_DIR,\n onFile: (filePath) => processTranscriptFileAutoMatch(filePath, DATA_DIR),\n });\n process.stderr.write(`[daemon] Watching transcripts (LLM auto-match)\\n`);\n }\n } catch (err) {\n process.stderr.write(`[daemon] Watcher error: ${(err as Error).message}\\n`);\n }\n}\n\nasync function checkAgentWakeTriggers(): Promise<void> {\n const agentsDir = path.join(DATA_DIR, \".agentic\", \"agents\");\n if (!fs.existsSync(agentsDir)) return;\n\n const files = fs.readdirSync(agentsDir).filter((f) => f.endsWith(\".agent.json\"));\n\n for (const file of files) {\n try {\n const config = JSON.parse(fs.readFileSync(path.join(agentsDir, file), \"utf-8\") as string) as {\n slug: string;\n channel: string;\n wakeOn: string[];\n lastWake: string | null;\n telegramChatId?: string;\n };\n\n if (!config.wakeOn.includes(\"email\")) continue;\n\n const { getLastGmailSync } = await import(\"../fs/sync-state.js\");\n const lastSync = getLastGmailSync(DATA_DIR, config.slug);\n const lastWake = config.lastWake ? new Date(config.lastWake) : null;\n\n if (!lastSync) continue;\n if (lastWake && lastSync <= lastWake) continue;\n\n // New email since last wake — build context and send notification\n process.stderr.write(`[daemon] Wake trigger: ${config.slug}\\n`);\n\n const { buildContext } = await import(\"../core/context-builder.js\");\n const context = await buildContext(DATA_DIR, config.slug).catch(() => null);\n if (!context) continue;\n\n if (\n config.channel === \"telegram\" &&\n process.env[\"TELEGRAM_BOT_TOKEN\"] &&\n (config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"])\n ) {\n const chatId = config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"]!;\n const token = process.env[\"TELEGRAM_BOT_TOKEN\"];\n const message = `📬 New activity: *${config.slug}*\\n\\n${context.slice(0, 800)}`;\n\n try {\n const { default: https } = await import(\"https\");\n const body = JSON.stringify({ chat_id: chatId, text: message, parse_mode: \"Markdown\" });\n await new Promise<void>((resolve, reject) => {\n const req = https.request(\n `https://api.telegram.org/bot${token}/sendMessage`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n },\n },\n (res) => {\n res.resume();\n resolve();\n }\n );\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n process.stderr.write(`[daemon] Telegram sent for ${config.slug}\\n`);\n } catch (err) {\n process.stderr.write(`[daemon] Telegram failed: ${(err as Error).message}\\n`);\n }\n }\n\n // Update lastWake\n config.lastWake = new Date().toISOString();\n fs.writeFileSync(path.join(agentsDir, file), JSON.stringify(config, null, 2), \"utf-8\");\n } catch (err) {\n process.stderr.write(`[daemon] Agent check error ${file}: ${(err as Error).message}\\n`);\n }\n }\n}\n\n// Gmail sync — interval configurable via DXCRM_DAEMON_INTERVAL (minutes, default 30)\nconst daemonIntervalMin = Math.max(\n 1,\n parseInt(process.env[\"DXCRM_DAEMON_INTERVAL\"] ?? \"30\", 10) || 30\n);\nnew CronJob(\n `*/${daemonIntervalMin} * * * *`,\n async () => {\n await syncAllCustomers();\n await checkAgentWakeTriggers().catch((err: unknown) => {\n process.stderr.write(`[daemon] Wake trigger check failed: ${(err as Error).message}\\n`);\n });\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Scheduled backup check — hourly, runs backup if >1 day since last\nnew CronJob(\n \"*/60 * * * *\",\n async () => {\n try {\n const { runScheduledBackupIfDue } = await import(\"../commands/backup.js\");\n await runScheduledBackupIfDue(DATA_DIR);\n } catch (err) {\n process.stderr.write(`[daemon] Backup check error: ${(err as Error).message}\\n`);\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Daily push subscription renewal at 06:00\nnew CronJob(\n \"0 6 * * *\",\n async () => {\n try {\n const { renewExpiringSubscriptions } = await import(\"../sync/push-manager.js\");\n const { buildGmailRenewFn } = await import(\"../sync/gmail-webhook-handler.js\");\n const tokenPath = path.join(DATA_DIR, \".agentic\", \"gmail-token.json\");\n const credPath = path.join(DATA_DIR, \".agentic\", \"gmail-credentials.json\");\n const { readSubscriptions } = await import(\"../sync/push-manager.js\");\n const subs = await readSubscriptions(DATA_DIR);\n const gmailSubs = subs.filter((s) => s.provider === \"gmail\" && s.status === \"active\");\n if (gmailSubs.length === 0) return;\n if (!fs.existsSync(tokenPath) || !fs.existsSync(credPath)) return;\n const { getGmailAuth } = await import(\"../sync/gmail-auth.js\");\n const auth = await getGmailAuth(credPath, tokenPath);\n const token = (auth.credentials?.access_token as string | undefined) ?? \"\";\n const result = await renewExpiringSubscriptions(DATA_DIR, buildGmailRenewFn(token, \"\"), 24);\n if (result.renewed.length > 0) {\n process.stderr.write(`[push] Renewed ${result.renewed.length} subscription(s)\\n`);\n }\n if (result.errors.length > 0) {\n process.stderr.write(`[push] Renewal errors: ${result.errors.join(\", \")}\\n`);\n }\n } catch (err) {\n process.stderr.write(`[push] Renewal failed: ${(err as Error).message}\\n`);\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Daily proactive checks at 07:00 — relationship decay, deal risk, daily briefing\nnew CronJob(\n \"0 7 * * *\",\n async () => {\n try {\n const { runDailyProactiveChecks } = await import(\"../daemon/proactive-worker.js\");\n const result = await runDailyProactiveChecks(DATA_DIR);\n process.stderr.write(\n `[proactive] Daily check: ${result.customersChecked} customers, ${result.tasksEnqueued} tasks enqueued\\n`\n );\n if (result.errors.length > 0) {\n process.stderr.write(`[proactive] Errors: ${result.errors.join(\", \")}\\n`);\n }\n const { drainProactiveQueue } = await import(\"../core/notification-dispatcher.js\");\n const drain = await drainProactiveQueue(DATA_DIR);\n process.stderr.write(\n `[proactive] Dispatched ${drain.sent} task(s), ${drain.failed} failed\\n`\n );\n const { syncGoalProgressFromPipeline } = await import(\"../core/goal-engine.js\");\n const goalSync = await syncGoalProgressFromPipeline(DATA_DIR);\n if (goalSync.updated.length > 0) {\n process.stderr.write(`[goals] Progress synced: ${goalSync.updated.join(\", \")}\\n`);\n }\n } catch (err) {\n process.stderr.write(`[proactive] Daily check failed: ${(err as Error).message}\\n`);\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// SLA breach check — daily at 08:00\nnew CronJob(\n \"0 8 * * *\",\n async () => {\n try {\n const { checkSlaBreaches } = await import(\"../core/sla-engine.js\");\n const today = new Date().toISOString().slice(0, 10);\n const breaches = await checkSlaBreaches(DATA_DIR, today);\n if (breaches.length > 0) {\n process.stderr.write(`[tickets] ${breaches.length} SLA breach(es) found\\n`);\n for (const { slug, ticket } of breaches) {\n process.stderr.write(\n `[tickets] SLA breach: ${slug}/${ticket.id} \"${ticket.title}\" due ${ticket.slaDue}\\n`\n );\n }\n }\n } catch (err) {\n process.stderr.write(`[tickets] SLA check failed: ${(err as Error).message}\\n`);\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Email sequence cycle — every 6 hours\nnew CronJob(\n \"0 */6 * * *\",\n async () => {\n try {\n const { runSequenceCycle } = await import(\"../core/sequence-engine.js\");\n const today = new Date().toISOString().slice(0, 10);\n const result = await runSequenceCycle(DATA_DIR, today);\n process.stderr.write(\n `[sequences] ${result.sent} sent, ${result.completed} completed, ${result.errors.length} errors\\n`\n );\n } catch (err) {\n process.stderr.write(`[sequences] cycle failed: ${(err as Error).message}\\n`);\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\nawait startWatcher();\n\n// Signal ready\nif (process.send) process.send(\"ready\");\nprocess.stderr.write(\"[daemon] DatasynxOpenCRM daemon started\\n\");\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"worker.js","names":[],"sources":["../../src/daemon/worker.ts"],"sourcesContent":["// src/daemon/worker.ts\n// Standalone detached process — started by `dxcrm daemon start`\n// Handles background Gmail sync + transcript watching via cron\nimport { CronJob } from \"cron\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { logger } from \"../core/logger.js\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nconst MAX_CUSTOMERS_PER_CYCLE = 50;\n\nasync function syncWithBackoff(fn: () => Promise<void>, maxRetries = 3): Promise<void> {\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n await fn();\n return;\n } catch (err) {\n const msg = (err as Error).message;\n if (msg.includes(\"429\") || msg.includes(\"rateLimitExceeded\")) {\n const delay = Math.pow(2, attempt) * 2000; // 2s, 4s, 8s\n logger.warn(\"daemon\", \"rate limit, retrying\", { delayMs: delay });\n await new Promise((r) => setTimeout(r, delay));\n } else {\n throw err;\n }\n }\n }\n}\n\nasync function syncAllCustomers(): Promise<void> {\n const customersDir = path.join(DATA_DIR, \"customers\");\n if (!fs.existsSync(customersDir)) return;\n\n const slugs = fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n const slugsToSync = slugs.slice(0, MAX_CUSTOMERS_PER_CYCLE);\n\n for (const slug of slugsToSync) {\n const sourcesPath = path.join(customersDir, slug, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) continue;\n\n try {\n const sources = JSON.parse(fs.readFileSync(sourcesPath, \"utf-8\")) as {\n gmail?: { query?: string; enabled?: boolean };\n };\n\n if (sources.gmail?.enabled && sources.gmail.query) {\n // Gmail sync requires auth — skip if token not configured\n const tokenPath = path.join(DATA_DIR, \".agentic\", \"gmail-token.json\");\n const credPath = path.join(DATA_DIR, \".agentic\", \"gmail-credentials.json\");\n if (fs.existsSync(tokenPath) && fs.existsSync(credPath)) {\n const { getGmailAuth } = await import(\"../sync/gmail-auth.js\");\n const { syncGmail } = await import(\"../sync/gmail-sync.js\");\n const auth = await getGmailAuth(credPath, tokenPath);\n await syncWithBackoff(async () => {\n const result = await syncGmail({\n slug,\n dataDir: DATA_DIR,\n auth,\n query: sources.gmail!.query!,\n since: new Date(Date.now() - 30 * 60 * 1000), // last 30 min\n });\n if (result.synced > 0) {\n logger.info(\"daemon\", \"synced emails\", { slug, synced: result.synced });\n }\n // Update sync state after each successful customer sync\n const { updateSlugSyncState } = await import(\"../fs/sync-state.js\");\n updateSlugSyncState(DATA_DIR, slug, { lastGmailSync: new Date().toISOString() });\n });\n }\n }\n } catch (err) {\n logger.error(\"daemon\", \"error syncing customer\", { slug, error: (err as Error).message });\n }\n }\n}\n\n// Start transcript watcher\nasync function startWatcher(): Promise<void> {\n const agenticSourcesPath = path.join(DATA_DIR, \".agentic\", \"sources.json\");\n if (!fs.existsSync(agenticSourcesPath)) return;\n\n try {\n const sources = JSON.parse(fs.readFileSync(agenticSourcesPath, \"utf-8\")) as {\n transcripts?: { paths?: string[]; extensions?: string[]; enabled?: boolean };\n };\n\n if (sources.transcripts?.enabled && sources.transcripts.paths?.length) {\n const { watchTranscripts, processTranscriptFileAutoMatch } =\n await import(\"../sync/transcript-watcher.js\");\n watchTranscripts({\n paths: sources.transcripts.paths,\n extensions: sources.transcripts.extensions ?? [\".txt\", \".vtt\"],\n dataDir: DATA_DIR,\n onFile: (filePath) => processTranscriptFileAutoMatch(filePath, DATA_DIR),\n });\n logger.info(\"daemon\", \"watching transcripts (LLM auto-match)\");\n }\n } catch (err) {\n logger.error(\"daemon\", \"watcher error\", { error: (err as Error).message });\n }\n}\n\nasync function checkAgentWakeTriggers(): Promise<void> {\n const agentsDir = path.join(DATA_DIR, \".agentic\", \"agents\");\n if (!fs.existsSync(agentsDir)) return;\n\n const files = fs.readdirSync(agentsDir).filter((f) => f.endsWith(\".agent.json\"));\n\n for (const file of files) {\n try {\n const config = JSON.parse(fs.readFileSync(path.join(agentsDir, file), \"utf-8\") as string) as {\n slug: string;\n channel: string;\n wakeOn: string[];\n lastWake: string | null;\n telegramChatId?: string;\n };\n\n if (!config.wakeOn.includes(\"email\")) continue;\n\n const { getLastGmailSync } = await import(\"../fs/sync-state.js\");\n const lastSync = getLastGmailSync(DATA_DIR, config.slug);\n const lastWake = config.lastWake ? new Date(config.lastWake) : null;\n\n if (!lastSync) continue;\n if (lastWake && lastSync <= lastWake) continue;\n\n // New email since last wake — build context and send notification\n logger.info(\"daemon\", \"wake trigger\", { slug: config.slug });\n\n const { buildContext } = await import(\"../core/context-builder.js\");\n const context = await buildContext(DATA_DIR, config.slug).catch(() => null);\n if (!context) continue;\n\n if (\n config.channel === \"telegram\" &&\n process.env[\"TELEGRAM_BOT_TOKEN\"] &&\n (config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"])\n ) {\n const chatId = config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"]!;\n const token = process.env[\"TELEGRAM_BOT_TOKEN\"];\n const message = `📬 New activity: *${config.slug}*\\n\\n${context.slice(0, 800)}`;\n\n try {\n const { default: https } = await import(\"https\");\n const body = JSON.stringify({ chat_id: chatId, text: message, parse_mode: \"Markdown\" });\n await new Promise<void>((resolve, reject) => {\n const req = https.request(\n `https://api.telegram.org/bot${token}/sendMessage`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n },\n },\n (res) => {\n res.resume();\n resolve();\n }\n );\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n logger.info(\"daemon\", \"telegram sent\", { slug: config.slug });\n } catch (err) {\n logger.error(\"daemon\", \"telegram failed\", { error: (err as Error).message });\n }\n }\n\n // Update lastWake\n config.lastWake = new Date().toISOString();\n writeJsonFile(path.join(agentsDir, file), config);\n } catch (err) {\n logger.error(\"daemon\", \"agent check error\", { file, error: (err as Error).message });\n }\n }\n}\n\n// Gmail sync — interval configurable via DXCRM_DAEMON_INTERVAL (minutes, default 30)\nconst daemonIntervalMin = Math.max(\n 1,\n parseInt(process.env[\"DXCRM_DAEMON_INTERVAL\"] ?? \"30\", 10) || 30\n);\nnew CronJob(\n `*/${daemonIntervalMin} * * * *`,\n async () => {\n await syncAllCustomers();\n await checkAgentWakeTriggers().catch((err: unknown) => {\n logger.error(\"daemon\", \"wake trigger check failed\", { error: (err as Error).message });\n });\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Scheduled backup check — hourly, runs backup if >1 day since last\nnew CronJob(\n \"*/60 * * * *\",\n async () => {\n try {\n const { runScheduledBackupIfDue } = await import(\"../commands/backup.js\");\n await runScheduledBackupIfDue(DATA_DIR);\n } catch (err) {\n logger.error(\"daemon\", \"backup check error\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Daily push subscription renewal at 06:00\nnew CronJob(\n \"0 6 * * *\",\n async () => {\n try {\n const { renewExpiringSubscriptions } = await import(\"../sync/push-manager.js\");\n const { buildGmailRenewFn } = await import(\"../sync/gmail-webhook-handler.js\");\n const tokenPath = path.join(DATA_DIR, \".agentic\", \"gmail-token.json\");\n const credPath = path.join(DATA_DIR, \".agentic\", \"gmail-credentials.json\");\n const { readSubscriptions } = await import(\"../sync/push-manager.js\");\n const subs = await readSubscriptions(DATA_DIR);\n const gmailSubs = subs.filter((s) => s.provider === \"gmail\" && s.status === \"active\");\n if (gmailSubs.length === 0) return;\n if (!fs.existsSync(tokenPath) || !fs.existsSync(credPath)) return;\n const { getGmailAuth } = await import(\"../sync/gmail-auth.js\");\n const auth = await getGmailAuth(credPath, tokenPath);\n const token = (auth.credentials?.access_token as string | undefined) ?? \"\";\n const result = await renewExpiringSubscriptions(DATA_DIR, buildGmailRenewFn(token, \"\"), 24);\n if (result.renewed.length > 0) {\n logger.info(\"push\", \"renewed subscriptions\", { count: result.renewed.length });\n }\n if (result.errors.length > 0) {\n logger.warn(\"push\", \"renewal errors\", { errors: result.errors });\n }\n } catch (err) {\n logger.error(\"push\", \"renewal failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Daily proactive checks at 07:00 — relationship decay, deal risk, daily briefing\nnew CronJob(\n \"0 7 * * *\",\n async () => {\n try {\n const { runDailyProactiveChecks } = await import(\"../daemon/proactive-worker.js\");\n const result = await runDailyProactiveChecks(DATA_DIR);\n logger.info(\"proactive\", \"daily check\", {\n customersChecked: result.customersChecked,\n tasksEnqueued: result.tasksEnqueued,\n });\n if (result.errors.length > 0) {\n logger.warn(\"proactive\", \"errors during daily check\", { errors: result.errors });\n }\n const { drainProactiveQueue } = await import(\"../core/notification-dispatcher.js\");\n const drain = await drainProactiveQueue(DATA_DIR);\n logger.info(\"proactive\", \"dispatched tasks\", { sent: drain.sent, failed: drain.failed });\n const { syncGoalProgressFromPipeline } = await import(\"../core/goal-engine.js\");\n const goalSync = await syncGoalProgressFromPipeline(DATA_DIR);\n if (goalSync.updated.length > 0) {\n logger.info(\"goals\", \"progress synced\", { updated: goalSync.updated });\n }\n } catch (err) {\n logger.error(\"proactive\", \"daily check failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// SLA breach check — daily at 08:00\nnew CronJob(\n \"0 8 * * *\",\n async () => {\n try {\n const { checkSlaBreaches } = await import(\"../core/sla-engine.js\");\n const today = new Date().toISOString().slice(0, 10);\n const breaches = await checkSlaBreaches(DATA_DIR, today);\n if (breaches.length > 0) {\n logger.warn(\"tickets\", \"SLA breaches found\", { count: breaches.length });\n for (const { slug, ticket } of breaches) {\n logger.warn(\"tickets\", \"SLA breach\", {\n slug,\n ticketId: ticket.id,\n title: ticket.title,\n due: ticket.slaDue,\n });\n }\n }\n } catch (err) {\n logger.error(\"tickets\", \"SLA check failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\n// Email sequence cycle — every 6 hours\nnew CronJob(\n \"0 */6 * * *\",\n async () => {\n try {\n const { runSequenceCycle } = await import(\"../core/sequence-engine.js\");\n const today = new Date().toISOString().slice(0, 10);\n const result = await runSequenceCycle(DATA_DIR, today);\n logger.info(\"sequences\", \"cycle complete\", {\n sent: result.sent,\n completed: result.completed,\n errors: result.errors.length,\n });\n } catch (err) {\n logger.error(\"sequences\", \"cycle failed\", { error: (err as Error).message });\n }\n },\n null,\n true,\n undefined,\n null,\n false,\n undefined,\n false, // unrefTimeout — keep event loop alive\n true // waitForCompletion\n);\n\nawait startWatcher();\n\n// Signal ready\nif (process.send) process.send(\"ready\");\nlogger.info(\"daemon\", \"daemon started\");\n"],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { a as readMainFacts, i as listCustomerSlugs } from "./customer-dir-CkMMXhb0.js";
|
|
2
|
+
import { a as summarizeLogs } from "./logger-Dyl4VcLO.js";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
//#region src/core/doctor.ts
|
|
6
|
+
/** Recursively collect files whose name matches the atomic-write temp pattern. */
|
|
7
|
+
function findOrphanedTempFiles(dir, depth = 0) {
|
|
8
|
+
if (depth > 3 || !fs.existsSync(dir)) return [];
|
|
9
|
+
const out = [];
|
|
10
|
+
let entries;
|
|
11
|
+
try {
|
|
12
|
+
entries = fs.readdirSync(dir);
|
|
13
|
+
} catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const full = path.join(dir, entry);
|
|
18
|
+
let isDir = false;
|
|
19
|
+
try {
|
|
20
|
+
isDir = fs.statSync(full).isDirectory();
|
|
21
|
+
} catch {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (isDir) out.push(...findOrphanedTempFiles(full, depth + 1));
|
|
25
|
+
else if (/\.\d+\.[0-9a-f]+\.tmp$/.test(entry)) out.push(full);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/** Delete orphaned atomic-write temp files; returns the paths removed. */
|
|
30
|
+
function cleanupTempFiles(dataDir) {
|
|
31
|
+
const temps = [...findOrphanedTempFiles(path.join(dataDir, ".agentic")), ...findOrphanedTempFiles(path.join(dataDir, "customers"))];
|
|
32
|
+
const removed = [];
|
|
33
|
+
for (const f of temps) try {
|
|
34
|
+
fs.rmSync(f, { force: true });
|
|
35
|
+
removed.push(f);
|
|
36
|
+
} catch {}
|
|
37
|
+
return removed;
|
|
38
|
+
}
|
|
39
|
+
async function runDiagnostics(dataDir) {
|
|
40
|
+
const checks = [];
|
|
41
|
+
const agenticDir = path.join(dataDir, ".agentic");
|
|
42
|
+
const customersDir = path.join(dataDir, "customers");
|
|
43
|
+
if (!fs.existsSync(agenticDir) && !fs.existsSync(customersDir)) checks.push({
|
|
44
|
+
name: "data directory",
|
|
45
|
+
status: "fail",
|
|
46
|
+
detail: `Neither .agentic/ nor customers/ found under ${dataDir} — run 'dxcrm init'`
|
|
47
|
+
});
|
|
48
|
+
else checks.push({
|
|
49
|
+
name: "data directory",
|
|
50
|
+
status: "ok",
|
|
51
|
+
detail: dataDir
|
|
52
|
+
});
|
|
53
|
+
const slugs = listCustomerSlugs(dataDir);
|
|
54
|
+
const invalid = [];
|
|
55
|
+
for (const slug of slugs) try {
|
|
56
|
+
await readMainFacts(dataDir, slug);
|
|
57
|
+
} catch {
|
|
58
|
+
invalid.push(slug);
|
|
59
|
+
}
|
|
60
|
+
checks.push({
|
|
61
|
+
name: "customer data",
|
|
62
|
+
status: invalid.length > 0 ? "fail" : "ok",
|
|
63
|
+
detail: invalid.length > 0 ? `${invalid.length} of ${slugs.length} invalid: ${invalid.slice(0, 5).join(", ")}` : `${slugs.length} customer(s) valid`
|
|
64
|
+
});
|
|
65
|
+
const temps = [...findOrphanedTempFiles(agenticDir), ...findOrphanedTempFiles(customersDir)];
|
|
66
|
+
checks.push({
|
|
67
|
+
name: "temp files",
|
|
68
|
+
status: temps.length > 0 ? "warn" : "ok",
|
|
69
|
+
detail: temps.length > 0 ? `${temps.length} orphaned temp file(s) from interrupted writes — safe to delete` : "no orphaned temp files"
|
|
70
|
+
});
|
|
71
|
+
const summary = summarizeLogs(dataDir);
|
|
72
|
+
const errorCount = summary.byLevel.error;
|
|
73
|
+
checks.push({
|
|
74
|
+
name: "logs",
|
|
75
|
+
status: errorCount > 0 ? "warn" : "ok",
|
|
76
|
+
detail: errorCount > 0 ? `${errorCount} error entr${errorCount === 1 ? "y" : "ies"} in the log (dxcrm logs --level error)` : `${summary.total} log entr${summary.total === 1 ? "y" : "ies"}, no errors`
|
|
77
|
+
});
|
|
78
|
+
const backupLogPath = path.join(agenticDir, "backup-log.json");
|
|
79
|
+
if (fs.existsSync(backupLogPath)) try {
|
|
80
|
+
const entries = JSON.parse(fs.readFileSync(backupLogPath, "utf-8"));
|
|
81
|
+
const last = entries[entries.length - 1]?.createdAt;
|
|
82
|
+
const ageDays = last ? Math.floor((Date.now() - new Date(last).getTime()) / 864e5) : Infinity;
|
|
83
|
+
checks.push({
|
|
84
|
+
name: "backups",
|
|
85
|
+
status: ageDays > 7 ? "warn" : "ok",
|
|
86
|
+
detail: last ? `last backup ${ageDays}d ago` : "no backups recorded"
|
|
87
|
+
});
|
|
88
|
+
} catch {
|
|
89
|
+
checks.push({
|
|
90
|
+
name: "backups",
|
|
91
|
+
status: "warn",
|
|
92
|
+
detail: "backup log unreadable"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
ok: !checks.some((c) => c.status === "fail"),
|
|
97
|
+
checks
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { cleanupTempFiles, runDiagnostics };
|
|
102
|
+
|
|
103
|
+
//# sourceMappingURL=doctor-C14-vnJ1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-C14-vnJ1.js","names":[],"sources":["../src/core/doctor.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport { listCustomerSlugs, readMainFacts } from \"../fs/customer-dir.js\";\nimport { summarizeLogs } from \"./logger.js\";\n\n/**\n * Self-diagnostic (`dxcrm doctor`). Ties together the integrity, observability\n * and validation work into a single operator-facing health check: is the data\n * directory sound, is customer data valid, are there orphaned atomic-write temp\n * files (a crash signature), recent log errors, or a stale backup?\n */\nexport type CheckStatus = \"ok\" | \"warn\" | \"fail\";\n\nexport interface DiagnosticCheck {\n name: string;\n status: CheckStatus;\n detail: string;\n}\n\nexport interface DiagnosticReport {\n ok: boolean; // false if any check failed\n checks: DiagnosticCheck[];\n}\n\n/** Recursively collect files whose name matches the atomic-write temp pattern. */\nfunction findOrphanedTempFiles(dir: string, depth = 0): string[] {\n if (depth > 3 || !fs.existsSync(dir)) return [];\n const out: string[] = [];\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n return [];\n }\n for (const entry of entries) {\n const full = path.join(dir, entry);\n let isDir = false;\n try {\n isDir = fs.statSync(full).isDirectory();\n } catch {\n continue;\n }\n if (isDir) {\n out.push(...findOrphanedTempFiles(full, depth + 1));\n } else if (/\\.\\d+\\.[0-9a-f]+\\.tmp$/.test(entry)) {\n out.push(full);\n }\n }\n return out;\n}\n\n/** Delete orphaned atomic-write temp files; returns the paths removed. */\nexport function cleanupTempFiles(dataDir: string): string[] {\n const temps = [\n ...findOrphanedTempFiles(path.join(dataDir, \".agentic\")),\n ...findOrphanedTempFiles(path.join(dataDir, \"customers\")),\n ];\n const removed: string[] = [];\n for (const f of temps) {\n try {\n fs.rmSync(f, { force: true });\n removed.push(f);\n } catch {\n /* leave it; reported but not removable */\n }\n }\n return removed;\n}\n\nexport async function runDiagnostics(dataDir: string): Promise<DiagnosticReport> {\n const checks: DiagnosticCheck[] = [];\n\n // 1. Data directory structure\n const agenticDir = path.join(dataDir, \".agentic\");\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(agenticDir) && !fs.existsSync(customersDir)) {\n checks.push({\n name: \"data directory\",\n status: \"fail\",\n detail: `Neither .agentic/ nor customers/ found under ${dataDir} — run 'dxcrm init'`,\n });\n } else {\n checks.push({\n name: \"data directory\",\n status: \"ok\",\n detail: dataDir,\n });\n }\n\n // 2. Customer data validity\n const slugs = listCustomerSlugs(dataDir);\n const invalid: string[] = [];\n for (const slug of slugs) {\n try {\n await readMainFacts(dataDir, slug);\n } catch {\n invalid.push(slug);\n }\n }\n checks.push({\n name: \"customer data\",\n status: invalid.length > 0 ? \"fail\" : \"ok\",\n detail:\n invalid.length > 0\n ? `${invalid.length} of ${slugs.length} invalid: ${invalid.slice(0, 5).join(\", \")}`\n : `${slugs.length} customer(s) valid`,\n });\n\n // 3. Orphaned atomic-write temp files (crash signature)\n const temps = [...findOrphanedTempFiles(agenticDir), ...findOrphanedTempFiles(customersDir)];\n checks.push({\n name: \"temp files\",\n status: temps.length > 0 ? \"warn\" : \"ok\",\n detail:\n temps.length > 0\n ? `${temps.length} orphaned temp file(s) from interrupted writes — safe to delete`\n : \"no orphaned temp files\",\n });\n\n // 4. Recent log errors\n const summary = summarizeLogs(dataDir);\n const errorCount = summary.byLevel.error;\n checks.push({\n name: \"logs\",\n status: errorCount > 0 ? \"warn\" : \"ok\",\n detail:\n errorCount > 0\n ? `${errorCount} error entr${errorCount === 1 ? \"y\" : \"ies\"} in the log (dxcrm logs --level error)`\n : `${summary.total} log entr${summary.total === 1 ? \"y\" : \"ies\"}, no errors`,\n });\n\n // 5. Backup freshness\n const backupLogPath = path.join(agenticDir, \"backup-log.json\");\n if (fs.existsSync(backupLogPath)) {\n try {\n const entries = JSON.parse(fs.readFileSync(backupLogPath, \"utf-8\") as string) as Array<{\n createdAt?: string;\n }>;\n const last = entries[entries.length - 1]?.createdAt;\n const ageDays = last\n ? Math.floor((Date.now() - new Date(last).getTime()) / 86_400_000)\n : Infinity;\n checks.push({\n name: \"backups\",\n status: ageDays > 7 ? \"warn\" : \"ok\",\n detail: last ? `last backup ${ageDays}d ago` : \"no backups recorded\",\n });\n } catch {\n checks.push({ name: \"backups\", status: \"warn\", detail: \"backup log unreadable\" });\n }\n }\n\n return { ok: !checks.some((c) => c.status === \"fail\"), checks };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { t as getSecret } from "./vault-
|
|
1
|
+
import { a as readMainFacts, o as writeMainFacts } from "./customer-dir-CkMMXhb0.js";
|
|
2
|
+
import { t as getSecret } from "./vault-CfwZdNzC.js";
|
|
3
3
|
//#region src/core/enrichment.ts
|
|
4
4
|
const ENRICHABLE = [
|
|
5
5
|
"domain",
|
|
@@ -100,4 +100,4 @@ async function enrichCustomer(dataDir, slug, opts = {}) {
|
|
|
100
100
|
//#endregion
|
|
101
101
|
export { enrichCustomer };
|
|
102
102
|
|
|
103
|
-
//# sourceMappingURL=enrichment-
|
|
103
|
+
//# sourceMappingURL=enrichment-CDFdWmvD.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrichment-
|
|
1
|
+
{"version":3,"file":"enrichment-CDFdWmvD.js","names":["vaultGetSecret"],"sources":["../src/core/enrichment.ts"],"sourcesContent":["import { readMainFacts, writeMainFacts } from \"../fs/customer-dir.js\";\nimport { getSecret as vaultGetSecret } from \"./vault.js\";\n\n/**\n * Enrichment layer (domino D15 / C6): a pluggable, vault-backed way to fill in\n * missing customer facts (domain, industry, contact info). The package ships an\n * offline built-in (derive domain from an email) and a provider interface so\n * external data providers can be added as plugins. Provider API keys come from\n * the D12 vault via the context — never from the markdown. Enrichment only fills\n * gaps; it never overwrites human-entered facts.\n */\nexport interface EnrichmentData {\n domain?: string;\n email?: string;\n phone?: string;\n industry?: string;\n}\n\nexport interface EnrichmentContext {\n /** Look up a provider credential from the local vault (or env). */\n getSecret(name: string): string | undefined;\n}\n\nexport interface EnrichmentInput extends EnrichmentData {\n name: string;\n}\n\nexport interface EnrichmentProvider {\n name: string;\n enrich(input: EnrichmentInput, ctx: EnrichmentContext): Promise<EnrichmentData> | EnrichmentData;\n}\n\nconst ENRICHABLE: Array<keyof EnrichmentData> = [\"domain\", \"email\", \"phone\", \"industry\"];\n\nfunction isEmpty(v: unknown): boolean {\n return v === undefined || v === null || (typeof v === \"string\" && v.trim() === \"\");\n}\n\n/** Merge additions into base, only filling fields that are currently empty. */\nexport function mergeEnrichment(base: EnrichmentData, additions: EnrichmentData): EnrichmentData {\n const out: EnrichmentData = { ...base };\n for (const key of ENRICHABLE) {\n const incoming = additions[key];\n if (isEmpty(out[key]) && incoming !== undefined && incoming.trim() !== \"\") out[key] = incoming;\n }\n return out;\n}\n\n/** Built-in, offline provider: derive a company domain from a contact email. */\nexport const domainFromEmailProvider: EnrichmentProvider = {\n name: \"domain-from-email\",\n enrich(input) {\n const email = input.email ?? \"\";\n const at = email.indexOf(\"@\");\n if (at > 0 && at < email.length - 1) {\n const domain = email\n .slice(at + 1)\n .trim()\n .toLowerCase();\n if (domain) return { domain };\n }\n return {};\n },\n};\n\nexport const DEFAULT_PROVIDERS: EnrichmentProvider[] = [domainFromEmailProvider];\n\n/** Run providers in order, merging their output into the input (gaps only). */\nexport async function runEnrichment(\n input: EnrichmentInput,\n providers: EnrichmentProvider[],\n ctx: EnrichmentContext\n): Promise<EnrichmentData> {\n let data: EnrichmentData = {\n ...(input.domain !== undefined ? { domain: input.domain } : {}),\n ...(input.email !== undefined ? { email: input.email } : {}),\n ...(input.phone !== undefined ? { phone: input.phone } : {}),\n ...(input.industry !== undefined ? { industry: input.industry } : {}),\n };\n for (const provider of providers) {\n const additions = await provider.enrich({ ...data, name: input.name }, ctx);\n data = mergeEnrichment(data, additions);\n }\n return data;\n}\n\nexport interface EnrichResult {\n /** Fields newly added by enrichment (i.e. previously empty). */\n applied: EnrichmentData;\n /** The full merged enrichment data. */\n merged: EnrichmentData;\n written: boolean;\n}\n\nfunction vaultContext(dataDir: string): EnrichmentContext {\n const key = process.env[\"DXCRM_VAULT_KEY\"];\n if (!key) return { getSecret: (n) => process.env[n] };\n return {\n getSecret(name: string) {\n try {\n return vaultGetSecret(dataDir, key, name) ?? process.env[name];\n } catch {\n return process.env[name];\n }\n },\n };\n}\n\n/**\n * Enrich one customer: read main_facts, run providers, and (optionally) write\n * the newly-filled fields back. Existing human-entered facts are preserved.\n */\nexport async function enrichCustomer(\n dataDir: string,\n slug: string,\n opts: { providers?: EnrichmentProvider[]; write?: boolean; ctx?: EnrichmentContext } = {}\n): Promise<EnrichResult> {\n const facts = await readMainFacts(dataDir, slug);\n const providers = opts.providers ?? DEFAULT_PROVIDERS;\n const ctx = opts.ctx ?? vaultContext(dataDir);\n\n const before: EnrichmentData = {\n ...(facts.domain !== undefined ? { domain: facts.domain } : {}),\n ...(facts.email !== undefined ? { email: facts.email } : {}),\n ...(facts.phone !== undefined ? { phone: facts.phone } : {}),\n ...(facts.industry !== undefined ? { industry: facts.industry } : {}),\n };\n const merged = await runEnrichment({ name: facts.name, ...before }, providers, ctx);\n\n const applied: EnrichmentData = {};\n for (const key of ENRICHABLE) {\n const value = merged[key];\n if (isEmpty(before[key]) && value !== undefined && value.trim() !== \"\") applied[key] = value;\n }\n\n let written = false;\n if (opts.write && Object.keys(applied).length > 0) {\n await writeMainFacts(dataDir, slug, {\n ...facts,\n ...applied,\n updated: new Date().toISOString().slice(0, 10),\n });\n written = true;\n }\n\n return { applied, merged, written };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
|
|
1
2
|
import { t as withFileQueue } from "./write-queue-IbsAjUnh.js";
|
|
2
|
-
import path from "path";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
//#region src/core/file-lock.ts
|
|
5
5
|
async function withJsonFile(filePath, updater) {
|
|
@@ -11,12 +11,11 @@ async function withJsonFile(filePath, updater) {
|
|
|
11
11
|
current = null;
|
|
12
12
|
}
|
|
13
13
|
const next = await updater(current);
|
|
14
|
-
|
|
15
|
-
fs.writeFileSync(filePath, JSON.stringify(next, null, 2), "utf-8");
|
|
14
|
+
writeFileAtomic(filePath, JSON.stringify(next, null, 2));
|
|
16
15
|
return next;
|
|
17
16
|
});
|
|
18
17
|
}
|
|
19
18
|
//#endregion
|
|
20
19
|
export { withJsonFile as t };
|
|
21
20
|
|
|
22
|
-
//# sourceMappingURL=file-lock-
|
|
21
|
+
//# sourceMappingURL=file-lock-CcHotQkZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock-CcHotQkZ.js","names":[],"sources":["../src/core/file-lock.ts"],"sourcesContent":["import fs from \"fs\";\nimport { withFileQueue } from \"../fs/write-queue.js\";\nimport { writeFileAtomic } from \"../fs/atomic-write.js\";\n\nexport async function withJsonFile<T>(\n filePath: string,\n updater: (current: T | null) => T | Promise<T>\n): Promise<T> {\n return withFileQueue(filePath, async () => {\n // Read current state\n let current: T | null = null;\n if (fs.existsSync(filePath)) {\n try {\n current = JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as T;\n } catch {\n current = null;\n }\n }\n\n // Apply updater — may throw, in which case we do NOT write\n const next = await updater(current);\n\n // Serialized by the queue lock AND crash-safe via temp-file + rename.\n writeFileAtomic(filePath, JSON.stringify(next, null, 2));\n\n return next;\n });\n}\n"],"mappings":";;;;AAIA,eAAsB,aACpB,UACA,SACY;CACZ,OAAO,cAAc,UAAU,YAAY;EAEzC,IAAI,UAAoB;EACxB,IAAI,GAAG,WAAW,QAAQ,GACxB,IAAI;GACF,UAAU,KAAK,MAAM,GAAG,aAAa,UAAU,OAAO,CAAW;EACnE,QAAQ;GACN,UAAU;EACZ;EAIF,MAAM,OAAO,MAAM,QAAQ,OAAO;EAGlC,gBAAgB,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;EAEvD,OAAO;CACT,CAAC;AACH"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { w as writeJsonFile } from "./session-store-DWxJ5Pof.js";
|
|
2
|
+
import { i as readInteractions, t as appendInteraction } from "./interactions-writer-BZzUIgJd.js";
|
|
3
|
+
import { t as logger } from "./logger-UaF5p9d1.js";
|
|
4
|
+
import { r as summarizeEmail } from "./llm-BnSUBisu.js";
|
|
3
5
|
import path from "path";
|
|
4
6
|
import fs from "fs";
|
|
5
7
|
import { z } from "zod";
|
|
@@ -33,11 +35,10 @@ function readAgentConfig(dataDir, slug) {
|
|
|
33
35
|
function writeLastWake(dataDir, slug, config) {
|
|
34
36
|
const p = agentConfigPath(dataDir, slug);
|
|
35
37
|
try {
|
|
36
|
-
|
|
38
|
+
writeJsonFile(p, {
|
|
37
39
|
...config,
|
|
38
40
|
lastWake: (/* @__PURE__ */ new Date()).toISOString()
|
|
39
|
-
};
|
|
40
|
-
fs.writeFileSync(p, JSON.stringify(updated, null, 2), "utf-8");
|
|
41
|
+
});
|
|
41
42
|
} catch {}
|
|
42
43
|
}
|
|
43
44
|
function sendTelegramMessage(token, chatId, text) {
|
|
@@ -154,7 +155,10 @@ async function syncGmail(opts) {
|
|
|
154
155
|
]
|
|
155
156
|
}))).data;
|
|
156
157
|
} catch (err) {
|
|
157
|
-
|
|
158
|
+
logger.warn("gmail-sync", "skipping message after retries", {
|
|
159
|
+
messageId: msg.id,
|
|
160
|
+
error: err.message
|
|
161
|
+
});
|
|
158
162
|
skipped++;
|
|
159
163
|
continue;
|
|
160
164
|
}
|
|
@@ -164,7 +168,7 @@ async function syncGmail(opts) {
|
|
|
164
168
|
const dateStr = headers.find((h) => h.name === "Date")?.value;
|
|
165
169
|
const date = dateStr ? new Date(dateStr).toISOString().slice(0, 10) : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
166
170
|
const snippet = msgData.snippet ?? "";
|
|
167
|
-
const { summarizeEmail } = await import("./llm-
|
|
171
|
+
const { summarizeEmail } = await import("./llm-BnSUBisu.js").then((n) => n.n);
|
|
168
172
|
const emailSummary = await summarizeEmail(subject, snippet, from);
|
|
169
173
|
await appendInteraction(opts.dataDir, opts.slug, {
|
|
170
174
|
date,
|
|
@@ -183,7 +187,7 @@ async function syncGmail(opts) {
|
|
|
183
187
|
date,
|
|
184
188
|
type: "Email"
|
|
185
189
|
}).catch((err) => {
|
|
186
|
-
|
|
190
|
+
logger.error("gmail-sync", "LanceDB index failed", { error: err.message });
|
|
187
191
|
});
|
|
188
192
|
if (agentConfigExists(opts.dataDir, opts.slug)) notifyAgentWake(opts.dataDir, opts.slug, {
|
|
189
193
|
trigger: "email",
|
|
@@ -211,4 +215,4 @@ function sleep(ms) {
|
|
|
211
215
|
//#endregion
|
|
212
216
|
export { syncGmail };
|
|
213
217
|
|
|
214
|
-
//# sourceMappingURL=gmail-sync-
|
|
218
|
+
//# sourceMappingURL=gmail-sync-C-NmibzS.js.map
|