@askexenow/exe-os 0.9.154 → 0.9.156

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.
Files changed (270) hide show
  1. package/deploy/compose/.env.example +25 -39
  2. package/deploy/compose/backup.sh +37 -0
  3. package/deploy/compose/branding.json +20 -0
  4. package/deploy/compose/cloudflared/config.yml.example +29 -0
  5. package/deploy/compose/docker-compose.yml +108 -0
  6. package/deploy/compose/gateway.json +11 -1
  7. package/deploy/compose/generate-env.ts +18 -4
  8. package/deploy/compose/init-db.sql +55 -0
  9. package/deploy/compose/setup.sh +166 -0
  10. package/deploy/compose/status.sh +48 -0
  11. package/dist/{active-agent-IVY5D7DJ.js → active-agent-GHJBTJRF.js} +4 -4
  12. package/dist/{active-agent-JN7A2D2E.js → active-agent-RY74REEW.js} +4 -4
  13. package/dist/{agentic-ontology-NU5FACOX.js → agentic-ontology-FP67RCR6.js} +1 -1
  14. package/dist/{backfill-metadata-XBWBPBN2.js → backfill-metadata-W7FUQRD7.js} +5 -5
  15. package/dist/{background-jobs-IUB22CRF.js → background-jobs-XYVQT4HN.js} +2 -2
  16. package/dist/{behaviors-4USTCRU7.js → behaviors-KA64SSPT.js} +4 -4
  17. package/dist/bin/age-ontology-load.js +2 -2
  18. package/dist/bin/agentic-ontology-backfill.js +7 -7
  19. package/dist/bin/agentic-reflection-backfill.js +8 -8
  20. package/dist/bin/agentic-semantic-label.js +7 -7
  21. package/dist/bin/backfill-conversations.js +7 -7
  22. package/dist/bin/backfill-responses.js +7 -7
  23. package/dist/bin/backfill-vectors.js +9 -9
  24. package/dist/bin/bulk-sync-postgres.js +8 -8
  25. package/dist/bin/cc-doctor.js +3 -3
  26. package/dist/bin/cleanup-stale-review-tasks.js +9 -9
  27. package/dist/bin/cli.js +18 -18
  28. package/dist/bin/exe-agent-config.js +4 -4
  29. package/dist/bin/exe-agent.js +5 -5
  30. package/dist/bin/exe-assign.js +9 -9
  31. package/dist/bin/exe-boot.js +16 -16
  32. package/dist/bin/exe-call.js +5 -5
  33. package/dist/bin/exe-cloud.js +7 -7
  34. package/dist/bin/exe-dispatch.js +9 -9
  35. package/dist/bin/exe-doctor.js +1 -1
  36. package/dist/bin/exe-export-behaviors.js +8 -8
  37. package/dist/bin/exe-forget.js +7 -7
  38. package/dist/bin/exe-gateway.js +8 -8
  39. package/dist/bin/exe-healthcheck.js +3 -3
  40. package/dist/bin/exe-heartbeat.js +9 -9
  41. package/dist/bin/exe-kill.js +12 -12
  42. package/dist/bin/exe-launch-agent.js +12 -12
  43. package/dist/bin/exe-new-employee.js +8 -8
  44. package/dist/bin/exe-pending-messages.js +10 -10
  45. package/dist/bin/exe-pending-notifications.js +9 -9
  46. package/dist/bin/exe-pending-reviews.js +9 -9
  47. package/dist/bin/exe-rename.js +5 -5
  48. package/dist/bin/exe-review.js +11 -11
  49. package/dist/bin/exe-search.js +6 -6
  50. package/dist/bin/exe-session-cleanup.js +13 -13
  51. package/dist/bin/exe-settings.js +7 -7
  52. package/dist/bin/exe-start-codex.js +12 -12
  53. package/dist/bin/exe-start-opencode.js +9 -9
  54. package/dist/bin/exe-status.js +10 -10
  55. package/dist/bin/exe-support.js +3 -3
  56. package/dist/bin/exe-team.js +4 -4
  57. package/dist/bin/git-sweep.js +9 -9
  58. package/dist/bin/graph-backfill.js +6 -6
  59. package/dist/bin/graph-export.js +6 -6
  60. package/dist/bin/install.js +7 -7
  61. package/dist/bin/intercom-check.js +4 -4
  62. package/dist/bin/postgres-agentic-reflection-backfill.js +4 -4
  63. package/dist/bin/postgres-agentic-semantic-backfill.js +3 -3
  64. package/dist/bin/scan-tasks.js +9 -9
  65. package/dist/bin/setup.js +2 -2
  66. package/dist/bin/shard-migrate.js +6 -6
  67. package/dist/bin/stack-update.js +2 -2
  68. package/dist/{branding-EHDA3CCK.js → branding-YUEILP4B.js} +1 -1
  69. package/dist/{capacity-monitor-VJLRBE7S.js → capacity-monitor-WAZTZAR5.js} +10 -10
  70. package/dist/{catchup-brief-T2RPTOTZ.js → catchup-brief-PKETW4ND.js} +11 -11
  71. package/dist/{chunk-OSPZOCPU.js → chunk-23UPL4NU.js} +3 -3
  72. package/dist/{chunk-TME75K53.js → chunk-2X5G7F5C.js} +3 -3
  73. package/dist/{chunk-23H5ZURC.js → chunk-3AN3RTUT.js} +4 -4
  74. package/dist/{chunk-XYFPCAA2.js → chunk-452JBQHH.js} +1 -1
  75. package/dist/{chunk-TOWAZ5IV.js → chunk-4GXRETYL.js} +16 -1
  76. package/dist/{chunk-AL7JZARP.js → chunk-4ZQDR3MI.js} +1 -1
  77. package/dist/{chunk-DW6P7UVY.js → chunk-66QQPSWI.js} +2 -2
  78. package/dist/{chunk-LHMOPUZE.js → chunk-6F46227N.js} +6 -6
  79. package/dist/{chunk-LVEOCWPL.js → chunk-77QEF6FR.js} +2 -2
  80. package/dist/{chunk-VRKPBY6D.js → chunk-7G44AU2D.js} +1 -1
  81. package/dist/{chunk-3RJBXMWJ.js → chunk-7VIH6LOQ.js} +2 -2
  82. package/dist/{chunk-5KUJDZ3J.js → chunk-7XRGVVG6.js} +3 -3
  83. package/dist/{chunk-NL4YO6I2.js → chunk-A6LSJC2O.js} +1 -1
  84. package/dist/{chunk-DMUNYW65.js → chunk-AK7S4GRC.js} +3 -3
  85. package/dist/{chunk-6M65LFNM.js → chunk-BRHV6CJT.js} +2 -2
  86. package/dist/{chunk-KSOPUPQX.js → chunk-CGLSVN6N.js} +9 -9
  87. package/dist/{chunk-Q7TTJD4A.js → chunk-CK5ZKW25.js} +1 -1
  88. package/dist/{chunk-K4KAPZC7.js → chunk-CMFLJNP6.js} +1 -1
  89. package/dist/{chunk-6YUACQQA.js → chunk-CTOQYEKJ.js} +1 -1
  90. package/dist/{chunk-UREIHGOQ.js → chunk-CXGOW4EJ.js} +3 -3
  91. package/dist/{chunk-6HFZ2KUC.js → chunk-DH65L4XI.js} +1 -1
  92. package/dist/{chunk-W6SKR3HZ.js → chunk-E2O7YYDS.js} +2 -2
  93. package/dist/{chunk-2G2KOWBI.js → chunk-EVB53OQD.js} +11 -11
  94. package/dist/{chunk-H5STRY47.js → chunk-F7ITFLVR.js} +1 -1
  95. package/dist/{chunk-SFZUC72J.js → chunk-FOG7AEEO.js} +2 -2
  96. package/dist/{chunk-T2B7637C.js → chunk-G2RRAFCH.js} +1 -1
  97. package/dist/{chunk-EYQIEK5M.js → chunk-GUCKE3GK.js} +1 -1
  98. package/dist/{chunk-KIUU4PNX.js → chunk-H2CFBUGF.js} +1 -1
  99. package/dist/{chunk-VB23RNNI.js → chunk-HBPFK6AO.js} +1 -1
  100. package/dist/{chunk-3I44JXWH.js → chunk-HDQJMTBG.js} +2 -2
  101. package/dist/{chunk-66RYFM6Z.js → chunk-HF6TVN2J.js} +3 -3
  102. package/dist/{chunk-3CZBCOYI.js → chunk-HH6ZCDZH.js} +2 -2
  103. package/dist/{chunk-Y3Z6QHUR.js → chunk-INWPOOB6.js} +1 -1
  104. package/dist/{chunk-DTT4TRFR.js → chunk-IRVLV6C5.js} +1 -1
  105. package/dist/{chunk-EHXOWGQG.js → chunk-IXJ4SLKR.js} +2 -2
  106. package/dist/{chunk-K23KJITV.js → chunk-J73N5EJ6.js} +2 -2
  107. package/dist/{chunk-OHYMA6C3.js → chunk-JFEFVLLD.js} +7 -7
  108. package/dist/{chunk-IWSXQKSB.js → chunk-JTHPKHKC.js} +1 -1
  109. package/dist/{chunk-KUPUTWQX.js → chunk-JYYWHPQC.js} +7 -7
  110. package/dist/{chunk-SE2DYYVB.js → chunk-KL5VDEBT.js} +1 -1
  111. package/dist/{chunk-ULFBLCIP.js → chunk-KPB5SN3Q.js} +1 -1
  112. package/dist/{chunk-L3JRSHHU.js → chunk-KZXC3G3P.js} +2 -2
  113. package/dist/{chunk-F2WGMIFZ.js → chunk-LVWXGZER.js} +1 -1
  114. package/dist/{chunk-HYRYMZRT.js → chunk-LWABYGNF.js} +1 -1
  115. package/dist/{chunk-G7IQNOSY.js → chunk-MBXAWOYB.js} +1 -1
  116. package/dist/{chunk-OC7Q4LOK.js → chunk-OCFYT3LE.js} +40 -9
  117. package/dist/{chunk-GCNWCYJI.js → chunk-ONKIWA3R.js} +2 -2
  118. package/dist/{chunk-2GUTGEFX.js → chunk-PELAHAJR.js} +1 -1
  119. package/dist/{chunk-VZDPV32D.js → chunk-PY7QM2C7.js} +3 -3
  120. package/dist/{chunk-Z6UYJ7HZ.js → chunk-Q46X2YPY.js} +4 -4
  121. package/dist/{chunk-PE6NCL7A.js → chunk-Q55XPOXU.js} +1 -1
  122. package/dist/{chunk-DWLDYEGO.js → chunk-RTNRPBQP.js} +10 -10
  123. package/dist/{chunk-EOO32RJZ.js → chunk-RVSGXGWG.js} +1 -1
  124. package/dist/{chunk-UIAYIZNR.js → chunk-RZE4KMRO.js} +2 -2
  125. package/dist/{chunk-V4MKR32F.js → chunk-S6DDN7IS.js} +2 -2
  126. package/dist/{chunk-TH2OFEQH.js → chunk-SEENMPG7.js} +2 -2
  127. package/dist/{chunk-GCB4MHTG.js → chunk-SNN634YS.js} +1 -1
  128. package/dist/{chunk-LWJ4AMMY.js → chunk-T5YT64SZ.js} +1 -1
  129. package/dist/{chunk-NNT2ZNMC.js → chunk-TJEETRHD.js} +1 -1
  130. package/dist/{chunk-45APPAB2.js → chunk-TM3WWKNB.js} +5 -5
  131. package/dist/{chunk-3S2HQUP6.js → chunk-U56WZR5E.js} +6 -6
  132. package/dist/{chunk-UJ2KUF4C.js → chunk-U5GTF2JP.js} +1 -1
  133. package/dist/{chunk-MLL5ICNL.js → chunk-UWUF5NWA.js} +7 -7
  134. package/dist/{chunk-XEN5RMGU.js → chunk-V5Y323LO.js} +1 -1
  135. package/dist/{chunk-XMRDT4PB.js → chunk-WZTQUBIE.js} +1 -1
  136. package/dist/{chunk-ICKEGWWP.js → chunk-X5O3FKRU.js} +1 -1
  137. package/dist/{chunk-4MN44ORL.js → chunk-XIGJOVTD.js} +1 -1
  138. package/dist/{chunk-2BTXFKAV.js → chunk-XIZR2YLR.js} +70 -71
  139. package/dist/{chunk-ABVCJBON.js → chunk-ZIJR3H2O.js} +2 -2
  140. package/dist/{chunk-3OGN523Z.js → chunk-ZPM2RMK2.js} +3 -3
  141. package/dist/{code-context-index-I5A7I4JQ.js → code-context-index-XZ22G57G.js} +4 -4
  142. package/dist/{conversation-wiki-populator-RSJ44BRO.js → conversation-wiki-populator-HVROKOEU.js} +1 -1
  143. package/dist/{crdt-sync-B7IUR3BR.js → crdt-sync-5JERNXAI.js} +1 -1
  144. package/dist/{crm-bridge-4QZRMOF7.js → crm-bridge-BVTB6LZK.js} +1 -1
  145. package/dist/{crm-webhook-YU7PDS7U.js → crm-webhook-DGL6XEOQ.js} +2 -2
  146. package/dist/{cto-delegation-gate-JDDOHVQL.js → cto-delegation-gate-UFSXT2CA.js} +8 -8
  147. package/dist/{daemon-auth-F47P6HTC.js → daemon-auth-H4EJ7KAQ.js} +2 -2
  148. package/dist/{daemon-orchestration-5OYKXYSR.js → daemon-orchestration-IP2LHJJK.js} +11 -11
  149. package/dist/{db-backup-W56YOLMC.js → db-backup-7UOCAPHO.js} +2 -2
  150. package/dist/{exe-drift-DXRU4SXJ.js → exe-drift-FNK7OXMY.js} +4 -4
  151. package/dist/{exe-export-X3AWJONG.js → exe-export-LLKDWX7F.js} +6 -6
  152. package/dist/{exe-import-GC5HO3TD.js → exe-import-XVAR7ZVC.js} +6 -6
  153. package/dist/{exe-key-R3ER4MYY.js → exe-key-E3YOJ2ME.js} +3 -3
  154. package/dist/{exe-org-OOO7KJVZ.js → exe-org-FEX66BTG.js} +2 -2
  155. package/dist/{fast-db-init-5DTYG5FZ.js → fast-db-init-SMQC6S3C.js} +1 -1
  156. package/dist/gateway/index.js +9 -9
  157. package/dist/{git-staleness-SVUTMS2F.js → git-staleness-HE6QCRSC.js} +3 -3
  158. package/dist/{git-task-sweep-EQHX4LXT.js → git-task-sweep-NWHG3FWG.js} +9 -9
  159. package/dist/{global-procedures-P7VOE476.js → global-procedures-KNA62DIE.js} +4 -4
  160. package/dist/{graph-auto-extract-UI374EYL.js → graph-auto-extract-4EZVBWQN.js} +3 -3
  161. package/dist/{hook-integrity-SA7S2SNI.js → hook-integrity-P3Z3HKMR.js} +1 -1
  162. package/dist/hooks/bug-report-worker.js +10 -10
  163. package/dist/hooks/codex-stop-task-finalizer.js +10 -10
  164. package/dist/hooks/commit-complete.js +11 -11
  165. package/dist/hooks/error-recall.js +7 -7
  166. package/dist/hooks/exe-heartbeat-hook.js +4 -4
  167. package/dist/hooks/ingest-worker.js +5 -5
  168. package/dist/hooks/ingest.js +12 -12
  169. package/dist/hooks/instructions-loaded.js +5 -5
  170. package/dist/hooks/notification.js +5 -5
  171. package/dist/hooks/post-compact.js +10 -10
  172. package/dist/hooks/post-tool-combined.js +5 -5
  173. package/dist/hooks/pre-compact.js +15 -15
  174. package/dist/hooks/pre-tool-use.js +14 -14
  175. package/dist/hooks/prompt-submit.js +59 -20
  176. package/dist/hooks/session-end.js +19 -19
  177. package/dist/hooks/session-start.js +9 -9
  178. package/dist/hooks/stop.js +17 -17
  179. package/dist/hooks/subagent-stop.js +10 -10
  180. package/dist/hooks/summary-worker.js +18 -18
  181. package/dist/index.js +17 -17
  182. package/dist/{installer-L72MRJPI.js → installer-4GPRFPIU.js} +6 -6
  183. package/dist/{installer-SLJ3YCHB.js → installer-MMXK6EHQ.js} +6 -6
  184. package/dist/{installer-LX2TU7RS.js → installer-TAFFG7WH.js} +6 -6
  185. package/dist/{key-backup-status-ORNMJEEA.js → key-backup-status-KQUGSNVJ.js} +1 -1
  186. package/dist/lib/agent-config.js +2 -2
  187. package/dist/lib/cloud-sync.js +6 -6
  188. package/dist/lib/config.js +1 -1
  189. package/dist/lib/consolidation.js +6 -6
  190. package/dist/lib/database.js +3 -3
  191. package/dist/lib/db-daemon-client.js +3 -3
  192. package/dist/lib/db.js +3 -3
  193. package/dist/lib/device-registry.js +1 -1
  194. package/dist/lib/embedder.js +4 -4
  195. package/dist/lib/employee-templates.js +5 -5
  196. package/dist/lib/employees.js +3 -3
  197. package/dist/lib/exe-daemon-client.js +3 -3
  198. package/dist/lib/exe-daemon.js +39 -39
  199. package/dist/lib/hybrid-search.js +6 -6
  200. package/dist/lib/identity.js +3 -3
  201. package/dist/lib/license.js +2 -2
  202. package/dist/lib/messaging.js +9 -9
  203. package/dist/lib/reminders.js +4 -4
  204. package/dist/lib/schedules.js +6 -6
  205. package/dist/lib/skill-learning.js +5 -5
  206. package/dist/lib/store.js +5 -5
  207. package/dist/lib/task-router.js +4 -4
  208. package/dist/lib/tasks.js +9 -9
  209. package/dist/lib/tmux-routing.js +8 -8
  210. package/dist/lib/token-spend.js +4 -4
  211. package/dist/{license-gate-J5YN264E.js → license-gate-OAPGTQXF.js} +3 -3
  212. package/dist/mcp/register-tools.js +57 -57
  213. package/dist/mcp/server.js +58 -58
  214. package/dist/mcp/tools/complete-reminder.js +5 -5
  215. package/dist/mcp/tools/create-reminder.js +5 -5
  216. package/dist/mcp/tools/create-task.js +11 -11
  217. package/dist/mcp/tools/deactivate-behavior.js +6 -6
  218. package/dist/mcp/tools/list-reminders.js +5 -5
  219. package/dist/mcp/tools/list-tasks.js +11 -11
  220. package/dist/mcp/tools/send-message.js +11 -11
  221. package/dist/mcp/tools/update-task.js +10 -10
  222. package/dist/{mcp-http-config-T76QB243.js → mcp-http-config-FPHVLE3N.js} +4 -4
  223. package/dist/{memory-cards-NTRYLVOU.js → memory-cards-VYZSCHZR.js} +3 -3
  224. package/dist/{memory-poisoning-defense-OJ2DEGBA.js → memory-poisoning-defense-NGJ63VZE.js} +3 -3
  225. package/dist/{memory-queue-NYIGZNCG.js → memory-queue-2SHTE6GU.js} +2 -2
  226. package/dist/{memory-queue-client-YJOLYALV.js → memory-queue-client-M7NHD4GY.js} +5 -5
  227. package/dist/{memory-reflection-4YOUTIMO.js → memory-reflection-XXN375ZD.js} +3 -3
  228. package/dist/{notifications-SF2XN5JE.js → notifications-Z6Q2SEHP.js} +8 -8
  229. package/dist/{orchestration-phase-FXKZYK6R.js → orchestration-phase-D3PNCSUY.js} +2 -2
  230. package/dist/{orchestrator-2W3VNAKG.js → orchestrator-RHJGFMAS.js} +10 -10
  231. package/dist/{plan-limits-LEM76SOQ.js → plan-limits-GCKK3L6F.js} +5 -5
  232. package/dist/{projection-worker-RQHV4VIR.js → projection-worker-GHWA6NEA.js} +2 -2
  233. package/dist/{push-notifications-NSYLKFXR.js → push-notifications-VJZFCUFZ.js} +2 -2
  234. package/dist/{reranker-OMADCS3D.js → reranker-JJRY3V3V.js} +2 -2
  235. package/dist/{review-polling-HD26LRW2.js → review-polling-XUIKH7HF.js} +9 -9
  236. package/dist/runtime/index.js +11 -11
  237. package/dist/{session-events-NYMENC3B.js → session-events-KURSY7LK.js} +9 -9
  238. package/dist/{session-kill-telemetry-LJU2MMZC.js → session-kill-telemetry-PD4UM5FM.js} +4 -4
  239. package/dist/{session-scope-QIDIKKFB.js → session-scope-5MOCMTAM.js} +8 -8
  240. package/dist/{setup-wizard-ANL7CVWX.js → setup-wizard-6SW2ND2B.js} +2 -2
  241. package/dist/{shard-manager-SP4YL5JQ.js → shard-manager-6JBG2K5X.js} +2 -2
  242. package/dist/{skill-refinement-3W3CIIG3.js → skill-refinement-JBZRPZKW.js} +3 -3
  243. package/dist/{task-enforcement-YXEOTTUG.js → task-enforcement-2WCWSZQ3.js} +8 -8
  244. package/dist/{task-scope-XXPTORGS.js → task-scope-HW6BE2PI.js} +8 -8
  245. package/dist/{tasks-crud-3UCNTXKQ.js → tasks-crud-WACRTPQK.js} +8 -8
  246. package/dist/{tasks-review-IPOWGI4L.js → tasks-review-LIWD4PGN.js} +8 -8
  247. package/dist/{token-budget-NP22DW2M.js → token-budget-5QD4ZSDX.js} +3 -3
  248. package/dist/{tool-capability-index-OBZXORBP.js → tool-capability-index-LBDYUK6L.js} +1 -1
  249. package/dist/{tool-telemetry-5JF2OAHG.js → tool-telemetry-2VGSVV3J.js} +1 -1
  250. package/dist/tui/App.js +21 -21
  251. package/dist/{tui-data-Z7FCER3K.js → tui-data-UKFAWSNP.js} +8 -8
  252. package/dist/{worker-gate-PJYHXNUB.js → worker-gate-NJRW5QYT.js} +2 -2
  253. package/dist/{workflow-engine-GA7JS7X2.js → workflow-engine-ICAQJSYT.js} +2 -2
  254. package/package.json +1 -1
  255. package/release-notes.json +187 -187
  256. package/stack.release.json +2 -2
  257. /package/dist/{chunk-ZYETFCDX.js → chunk-E5PVJXIP.js} +0 -0
  258. /package/dist/{chunk-J2OPV5FE.js → chunk-FMBRO3D7.js} +0 -0
  259. /package/dist/{chunk-NQND3GFO.js → chunk-J2KP4ZDX.js} +0 -0
  260. /package/dist/{chunk-PBBIAKN5.js → chunk-LKKKOWTH.js} +0 -0
  261. /package/dist/{chunk-LYMBXTEO.js → chunk-LZLXG7TK.js} +0 -0
  262. /package/dist/{chunk-7E6ZBXO7.js → chunk-PYDL42BL.js} +0 -0
  263. /package/dist/{chunk-7U664OM4.js → chunk-RES22KSZ.js} +0 -0
  264. /package/dist/{core-memory-KL5BOUXU.js → core-memory-QRQ62AZY.js} +0 -0
  265. /package/dist/{entity-boost-LIBVNNXO.js → entity-boost-MDWFYORT.js} +0 -0
  266. /package/dist/{message-queue-client-NTLKYGVO.js → message-queue-client-BJZEA6RV.js} +0 -0
  267. /package/dist/{oauth-server-TOWAZR4K.js → oauth-server-UM2TQJLJ.js} +0 -0
  268. /package/dist/{webhook-pipe-PJOFVBD3.js → webhook-pipe-VSLEA26V.js} +0 -0
  269. /package/dist/{wiki-acl-ZRCU7XYC.js → wiki-acl-RZCXSF5O.js} +0 -0
  270. /package/dist/{wiki-client-MMVRPTPK.js → wiki-client-NZR7RW4I.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  # exe-os VPS stack — example environment variables
2
- # Copy to .env before deployment and replace every CHANGEME_* value.
3
- # Values under # SET_MANUALLY must be provided by the operator.
2
+ # Run `exe-os stack-init` or `node deploy/compose/generate-env.js` to auto-generate
3
+ # all secrets. Or copy to .env and replace CHANGEME_* values manually.
4
4
 
5
5
  # --- Data Layer ---
6
6
  POSTGRES_USER=exe
@@ -13,67 +13,53 @@ CLICKHOUSE_PASSWORD=CHANGEME_CLICKHOUSE_PASSWORD
13
13
 
14
14
  REDIS_PASSWORD=CHANGEME_REDIS_PASSWORD
15
15
 
16
+ # --- GoTrue (shared auth) ---
17
+ GOTRUE_JWT_SECRET=CHANGEME_GOTRUE_JWT_SECRET
18
+ GOTRUE_SITE_URL=https://crm.askexe.com
19
+ GOTRUE_EXTERNAL_URL=https://auth.askexe.com
20
+ GOTRUE_DISABLE_SIGNUP=false
21
+ GOTRUE_MAILER_AUTOCONFIRM=true
22
+
16
23
  # --- CRM ---
17
- CRM_IMAGE_TAG=ghcr.io/askexe/exe-crm:v0.9.2
18
- CRM_SERVER_URL=https://CHANGEME_DOMAIN
24
+ CRM_IMAGE_TAG=ghcr.io/askexe/exe-crm:v0.9.3
25
+ CRM_SERVER_URL=https://crm.askexe.com
19
26
  CRM_APP_SECRET=CHANGEME_CRM_APP_SECRET
27
+ EXE_CRM_ADMIN_TOKEN=CHANGEME_EXE_CRM_ADMIN_TOKEN
20
28
  CRM_HOST_PORT=3000
21
29
 
22
30
  # --- Wiki ---
23
- WIKI_IMAGE_TAG=ghcr.io/askexe/exe-wiki:v0.9.2
31
+ WIKI_IMAGE_TAG=ghcr.io/askexe/exe-wiki:v0.9.4
24
32
  WIKI_DB_SCHEMA=wiki
25
33
  WIKI_VECTOR_DB=postgres
26
34
  WIKI_AUTH_TOKEN=CHANGEME_WIKI_AUTH_TOKEN
35
+ EXE_WIKI_ADMIN_TOKEN=CHANGEME_EXE_WIKI_ADMIN_TOKEN
27
36
  WIKI_JWT_SECRET=CHANGEME_WIKI_JWT_SECRET
28
37
  WIKI_SIG_KEY=CHANGEME_WIKI_SIG_KEY
29
38
  WIKI_SIG_SALT=CHANGEME_WIKI_SIG_SALT
30
39
  WIKI_HOST_PORT=3001
31
40
 
32
- # --- exe-os ---
33
- EXE_OS_IMAGE_TAG=ghcr.io/askexe/exe-os:v0.9.2
41
+ # --- exe-os (daemon) ---
42
+ EXE_OS_IMAGE_TAG=ghcr.io/askexe/exe-os:v0.9.154
34
43
  EXED_MCP_TOKEN=CHANGEME_EXED_MCP_TOKEN
35
44
  EXED_DEVICE_ID=vps-default
36
- # VPS-only: enables cloud/local SQLite -> exe-db Postgres projection.
37
- # Keep false on laptops/dev boxes.
38
45
  EXE_CLOUD_SYNC_TO_POSTGRES=true
46
+ EXE_LICENSE_KEY=CHANGEME_EXE_LICENSE_KEY
39
47
 
40
48
  # --- Gateway ---
41
- GATEWAY_IMAGE_TAG=ghcr.io/askexe/exe-gateway:v0.9.2
49
+ GATEWAY_IMAGE_TAG=ghcr.io/askexe/exe-gateway:v0.9.3
42
50
  EXE_GATEWAY_AUTH_TOKEN=CHANGEME_EXE_GATEWAY_AUTH_TOKEN
43
51
  EXE_GATEWAY_WS_RELAY_AUTH_TOKEN=CHANGEME_EXE_GATEWAY_WS_RELAY_AUTH_TOKEN
44
52
  EXE_GATEWAY_WHATSAPP_VERIFY_TOKEN=CHANGEME_EXE_GATEWAY_WHATSAPP_VERIFY_TOKEN
45
- # SET_MANUALLY
46
- WHATSAPP_ACCESS_TOKEN=CHANGEME_WHATSAPP_ACCESS_TOKEN
47
53
  API_ROUTER_URL=https://gateway.askexe.com
48
- API_ROUTER_KEY=CHANGEME_API_ROUTER_KEY
49
- # BYOK: to use your own API keys instead of the Exe API Router,
50
- # set BYOK_ENABLED=true and provide ANTHROPIC_API_KEY below.
51
- # BYOK_ENABLED=false
52
- # ANTHROPIC_API_KEY=CHANGEME_ANTHROPIC_API_KEY
53
54
  GATEWAY_HTTP_HOST_PORT=3100
54
55
  GATEWAY_WS_HOST_PORT=3101
55
56
 
56
- # --- Monitoring agent (standard for managed customer VPSs) ---
57
- MONITOR_AGENT_IMAGE_TAG=ghcr.io/askexe/exe-monitor-agent:v0.9.2
57
+ # --- Monitoring ---
58
+ MONITOR_HUB_IMAGE_TAG=ghcr.io/askexe/exe-monitor-hub:v0.9.4
59
+ MONITOR_AGENT_IMAGE_TAG=ghcr.io/askexe/exe-monitor-agent:v0.9.4
60
+ EXE_MONITOR_ADMIN_TOKEN=CHANGEME_EXE_MONITOR_ADMIN_TOKEN
58
61
  MONITOR_HUB_URL=https://monitor.askexe.com
59
- # Values copied from monitor.askexe.com when adding a new system.
60
- MONITOR_AGENT_TOKEN=CHANGEME_MONITOR_AGENT_TOKEN_FROM_MONITOR_HUB
61
- MONITOR_AGENT_KEY=CHANGEME_MONITOR_AGENT_PUBLIC_KEY_FROM_MONITOR_HUB
62
+ MONITOR_AGENT_TOKEN=CHANGEME_MONITOR_AGENT_TOKEN
63
+ MONITOR_AGENT_KEY=CHANGEME_MONITOR_AGENT_KEY
62
64
  MONITOR_AGENT_LISTEN=:45876
63
-
64
- # --- AskExe central monitoring hub auth (AskExe-owned infra only) ---
65
- # Customer VPSs normally run only MONITOR_AGENT_* above. These hub values are
66
- # for monitor.askexe.com on exe-db-jkt.
67
- MONITOR_HUB_PUBLIC_URL=https://monitor.askexe.com
68
- MONITOR_HUB_SOURCE_DIR=/opt/exe-monitor
69
- MONITOR_HUB_HOST_PORT=8090
70
- MONITOR_HUB_DATA_DIR=/opt/exe-monitor-data
71
- MONITOR_TRUSTED_AUTH_HEADER=X-AskExe-User-Email
72
- # Keep false during bootstrap; flip true after the GoTrue auth proxy is live.
73
- MONITOR_DISABLE_PASSWORD_AUTH=false
74
- MONITOR_USER_CREATION=true
75
- MONITOR_SHARE_ALL_SYSTEMS=false
76
-
77
- # --- License ---
78
- # injected by deploy_client
79
- EXE_LICENSE_KEY=CHANGEME_EXE_LICENSE_KEY
65
+ MONITOR_HUB_PORT=8090
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+ # Automated backup for exe-os stack — runs daily via cron or systemd timer.
3
+ # Backs up: postgres (all databases), gateway auth state, wiki storage.
4
+ set -euo pipefail
5
+
6
+ BACKUP_DIR="${BACKUP_DIR:-/opt/exe-backups}"
7
+ RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}"
8
+ DATE=$(date +%Y%m%d-%H%M%S)
9
+ COMPOSE_DIR="$(cd "$(dirname "$0")" && pwd)"
10
+
11
+ mkdir -p "$BACKUP_DIR"
12
+
13
+ echo "[backup] Starting exe-os stack backup ($DATE)"
14
+
15
+ # 1. Postgres dump (all databases)
16
+ echo "[backup] Dumping postgres..."
17
+ docker exec exe-db pg_dumpall -U exe > "$BACKUP_DIR/postgres-$DATE.sql" 2>/dev/null
18
+ gzip "$BACKUP_DIR/postgres-$DATE.sql"
19
+ echo "[backup] Postgres: $(du -h "$BACKUP_DIR/postgres-$DATE.sql.gz" | cut -f1)"
20
+
21
+ # 2. Gateway auth state (Baileys creds)
22
+ echo "[backup] Backing up gateway auth state..."
23
+ docker cp exe-gateway:/data/. "$BACKUP_DIR/gateway-$DATE/" 2>/dev/null || echo "[backup] Gateway backup skipped (not running)"
24
+ tar -czf "$BACKUP_DIR/gateway-$DATE.tar.gz" -C "$BACKUP_DIR" "gateway-$DATE" 2>/dev/null && rm -rf "$BACKUP_DIR/gateway-$DATE"
25
+
26
+ # 3. Wiki storage (uploaded docs)
27
+ echo "[backup] Backing up wiki storage..."
28
+ docker cp exe-wiki:/app/server/storage/. "$BACKUP_DIR/wiki-$DATE/" 2>/dev/null || echo "[backup] Wiki backup skipped"
29
+ tar -czf "$BACKUP_DIR/wiki-$DATE.tar.gz" -C "$BACKUP_DIR" "wiki-$DATE" 2>/dev/null && rm -rf "$BACKUP_DIR/wiki-$DATE"
30
+
31
+ # 4. Retention — delete backups older than N days
32
+ echo "[backup] Cleaning backups older than $RETENTION_DAYS days..."
33
+ find "$BACKUP_DIR" -name "*.gz" -mtime "+$RETENTION_DAYS" -delete 2>/dev/null
34
+ find "$BACKUP_DIR" -name "*.sql" -mtime "+$RETENTION_DAYS" -delete 2>/dev/null
35
+
36
+ echo "[backup] Done. Backups at $BACKUP_DIR:"
37
+ ls -lh "$BACKUP_DIR"/*.gz 2>/dev/null | tail -5
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "Exe OS",
3
+ "logo": null,
4
+ "colors": {
5
+ "primary": "#F5D76E",
6
+ "background": "#0F0E1A",
7
+ "surface": "rgba(245, 215, 110, 0.08)",
8
+ "text": "#f8f5ea",
9
+ "muted": "rgba(248, 245, 234, 0.72)"
10
+ },
11
+ "fonts": {
12
+ "heading": "Epilogue",
13
+ "body": "Manrope",
14
+ "mono": "Space Grotesk"
15
+ },
16
+ "support": {
17
+ "email": "support@askexe.com",
18
+ "url": "https://askexe.com"
19
+ }
20
+ }
@@ -0,0 +1,29 @@
1
+ # Cloudflare Tunnel config — routes *.askexe.com to local services.
2
+ # Copy to config.yml and replace TUNNEL_ID with your tunnel UUID.
3
+ # Create tunnel: cloudflared tunnel create <name>
4
+ # Then copy credentials JSON to this directory.
5
+
6
+ tunnel: CHANGEME_TUNNEL_ID
7
+ credentials-file: /etc/cloudflared/CHANGEME_TUNNEL_ID.json
8
+
9
+ ingress:
10
+ # CRM
11
+ - hostname: crm.CHANGEME_DOMAIN
12
+ service: http://exe-crm:3000
13
+ # Wiki
14
+ - hostname: wiki.CHANGEME_DOMAIN
15
+ service: http://exe-wiki:3001
16
+ # Gateway (WhatsApp, webhooks, pairing)
17
+ - hostname: gateway.CHANGEME_DOMAIN
18
+ service: http://exe-gateway:3100
19
+ # Monitor
20
+ - hostname: monitor.CHANGEME_DOMAIN
21
+ service: http://exe-monitor-hub:8090
22
+ # Auth (GoTrue)
23
+ - hostname: auth.CHANGEME_DOMAIN
24
+ service: http://gotrue:9999
25
+ # exe-os daemon (MCP)
26
+ - hostname: api.CHANGEME_DOMAIN
27
+ service: http://exe-os:8765
28
+ # Catch-all
29
+ - service: http_status:404
@@ -36,6 +36,7 @@ services:
36
36
  PGDATA: /var/lib/postgresql/data/pgdata
37
37
  volumes:
38
38
  - postgres_data:/var/lib/postgresql/data
39
+ - ./init-db.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
39
40
  networks:
40
41
  backend:
41
42
  ipv4_address: 10.42.0.10
@@ -104,6 +105,71 @@ services:
104
105
  driver: json-file
105
106
  options: { max-size: "10m", max-file: "3" }
106
107
 
108
+ gotrue:
109
+ image: supabase/gotrue:v2.172.1@sha256:b0dd7aa28dd1f9a8bd31f136c8b912661f701d9b863171e5843cb08f18f49de4
110
+ container_name: gotrue
111
+ restart: unless-stopped
112
+ depends_on:
113
+ exe-db:
114
+ condition: service_healthy
115
+ env_file:
116
+ - path: .env
117
+ required: false
118
+ environment:
119
+ GOTRUE_DB_DRIVER: postgres
120
+ GOTRUE_DB_DATABASE_URL: postgres://${POSTGRES_USER:-exe}:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@exe-db:5432/${POSTGRES_DB:-exedb}?sslmode=disable&search_path=auth
121
+ GOTRUE_SITE_URL: ${GOTRUE_SITE_URL:-https://crm.askexe.com}
122
+ GOTRUE_URI_ALLOW_LIST: ${GOTRUE_URI_ALLOW_LIST:-}
123
+ GOTRUE_JWT_SECRET: ${GOTRUE_JWT_SECRET:?GOTRUE_JWT_SECRET is required}
124
+ GOTRUE_JWT_EXP: ${GOTRUE_JWT_EXP:-3600}
125
+ GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
126
+ API_EXTERNAL_URL: ${GOTRUE_EXTERNAL_URL:-https://auth.askexe.com}
127
+ GOTRUE_DISABLE_SIGNUP: ${GOTRUE_DISABLE_SIGNUP:-false}
128
+ GOTRUE_MAILER_AUTOCONFIRM: ${GOTRUE_MAILER_AUTOCONFIRM:-true}
129
+ GOTRUE_SMTP_HOST: ${SMTP_HOST:-}
130
+ GOTRUE_SMTP_PORT: ${SMTP_PORT:-587}
131
+ GOTRUE_SMTP_USER: ${SMTP_USER:-}
132
+ GOTRUE_SMTP_PASS: ${SMTP_PASS:-}
133
+ GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_FROM:-noreply@askexe.com}
134
+ ports:
135
+ - "127.0.0.1:${GOTRUE_HOST_PORT:-9999}:9999"
136
+ networks:
137
+ backend:
138
+ ipv4_address: 10.42.0.13
139
+ healthcheck:
140
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:9999/health"]
141
+ interval: 15s
142
+ timeout: 5s
143
+ start_period: 10s
144
+ retries: 5
145
+ logging:
146
+ driver: json-file
147
+ options: { max-size: "10m", max-file: "3" }
148
+
149
+ exe-monitor-hub:
150
+ image: ${MONITOR_HUB_IMAGE_TAG:-ghcr.io/askexe/exe-monitor-hub:v0.9.4}
151
+ container_name: exe-monitor-hub
152
+ restart: unless-stopped
153
+ environment:
154
+ EXE_MONITOR_ADMIN_TOKEN: ${EXE_MONITOR_ADMIN_TOKEN:-}
155
+ GOTRUE_URL: http://gotrue:9999
156
+ ports:
157
+ - "127.0.0.1:${MONITOR_HUB_PORT:-8090}:8090"
158
+ volumes:
159
+ - monitor_hub_data:/app/pb_data
160
+ networks:
161
+ backend:
162
+ ipv4_address: 10.42.0.14
163
+ healthcheck:
164
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8090/api/health"]
165
+ interval: 30s
166
+ timeout: 5s
167
+ start_period: 15s
168
+ retries: 5
169
+ logging:
170
+ driver: json-file
171
+ options: { max-size: "10m", max-file: "3" }
172
+
107
173
  # ------------------------------------------------------------------
108
174
  # Apps
109
175
  # ------------------------------------------------------------------
@@ -112,9 +178,14 @@ services:
112
178
  image: ${CRM_IMAGE_TAG:-ghcr.io/askexe/exe-crm:v0.9.2}
113
179
  container_name: exe-crm
114
180
  restart: unless-stopped
181
+ # Auto-migrate on boot: run database init before starting the app.
182
+ # Twenty CRM won't create tables automatically — this ensures they exist.
183
+ command: ["sh", "-c", "yarn database:init:prod 2>/dev/null || true && node dist/src/main"]
115
184
  depends_on:
116
185
  exe-db:
117
186
  condition: service_healthy
187
+ gotrue:
188
+ condition: service_healthy
118
189
  clickhouse:
119
190
  condition: service_healthy
120
191
  redis:
@@ -128,6 +199,8 @@ services:
128
199
  EXE_LICENSE_KEY: ${EXE_LICENSE_KEY:?EXE_LICENSE_KEY is required — purchase at https://askexe.com}
129
200
  SERVER_URL: ${CRM_SERVER_URL:-https://crm.askexe.com}
130
201
  APP_SECRET: ${CRM_APP_SECRET:?CRM_APP_SECRET is required}
202
+ EXE_CRM_ADMIN_TOKEN: ${EXE_CRM_ADMIN_TOKEN:-}
203
+ GOTRUE_URL: http://gotrue:9999
131
204
  PG_DATABASE_URL: postgres://${POSTGRES_USER:-exe}:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@exe-db:5432/${POSTGRES_DB:-exedb}
132
205
  REDIS_URL: redis://:${REDIS_PASSWORD:?REDIS_PASSWORD is required}@redis:6379
133
206
  CLICKHOUSE_URL: http://${CLICKHOUSE_USER:-exe}:${CLICKHOUSE_PASSWORD:?CLICKHOUSE_PASSWORD is required}@clickhouse:8123/${CLICKHOUSE_DB:-default}
@@ -198,9 +271,13 @@ services:
198
271
  image: ${WIKI_IMAGE_TAG:-ghcr.io/askexe/exe-wiki:v0.9.2}
199
272
  container_name: exe-wiki
200
273
  restart: unless-stopped
274
+ # Wiki uses Prisma — runs migrate on boot via built-in entrypoint.
275
+ # If tables don't exist, Prisma creates them automatically.
201
276
  depends_on:
202
277
  exe-db:
203
278
  condition: service_healthy
279
+ gotrue:
280
+ condition: service_healthy
204
281
  env_file:
205
282
  - path: .env
206
283
  required: false
@@ -211,6 +288,8 @@ services:
211
288
  STORAGE_DIR: /app/server/storage
212
289
  DATABASE_URL: postgres://${POSTGRES_USER:-exe}:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@exe-db:5432/${POSTGRES_DB:-exedb}?schema=${WIKI_DB_SCHEMA:-wiki}
213
290
  AUTH_TOKEN: ${WIKI_AUTH_TOKEN:?WIKI_AUTH_TOKEN is required}
291
+ EXE_WIKI_ADMIN_TOKEN: ${EXE_WIKI_ADMIN_TOKEN:-}
292
+ GOTRUE_URL: http://gotrue:9999
214
293
  JWT_SECRET: ${WIKI_JWT_SECRET:?WIKI_JWT_SECRET is required}
215
294
  SIG_KEY: ${WIKI_SIG_KEY:?WIKI_SIG_KEY is required}
216
295
  SIG_SALT: ${WIKI_SIG_SALT:?WIKI_SIG_SALT is required}
@@ -290,6 +369,7 @@ services:
290
369
  EXE_GATEWAY_PORT: "3100"
291
370
  EXE_GATEWAY_HOST: "127.0.0.1"
292
371
  EXE_GATEWAY_AUTH_TOKEN: ${EXE_GATEWAY_AUTH_TOKEN:?EXE_GATEWAY_AUTH_TOKEN is required}
372
+ GOTRUE_URL: http://gotrue:9999
293
373
  EXE_GATEWAY_WHATSAPP_VERIFY_TOKEN: ${EXE_GATEWAY_WHATSAPP_VERIFY_TOKEN:?EXE_GATEWAY_WHATSAPP_VERIFY_TOKEN is required}
294
374
  EXE_GATEWAY_WS_RELAY_ENABLED: "true"
295
375
  EXE_GATEWAY_WS_RELAY_HOST: "127.0.0.1"
@@ -351,6 +431,32 @@ services:
351
431
  driver: json-file
352
432
  options: { max-size: "10m", max-file: "3" }
353
433
 
434
+ # ------------------------------------------------------------------
435
+ # Infrastructure — Cloudflare Tunnel (replaces nginx + SSL certs)
436
+ # ------------------------------------------------------------------
437
+
438
+ cloudflared:
439
+ image: cloudflare/cloudflared:latest
440
+ container_name: cloudflared
441
+ restart: unless-stopped
442
+ command: tunnel --config /etc/cloudflared/config.yml run
443
+ volumes:
444
+ - ${CLOUDFLARED_CONFIG_DIR:-./cloudflared}:/etc/cloudflared:ro
445
+ networks:
446
+ backend:
447
+ ipv4_address: 10.42.0.31
448
+ frontend:
449
+ ipv4_address: 10.43.0.31
450
+ healthcheck:
451
+ test: ["CMD", "cloudflared", "tunnel", "info", "--output", "json"]
452
+ interval: 30s
453
+ timeout: 5s
454
+ start_period: 15s
455
+ retries: 5
456
+ logging:
457
+ driver: json-file
458
+ options: { max-size: "10m", max-file: "3" }
459
+
354
460
  # ------------------------------------------------------------------
355
461
  # Volumes
356
462
  # ------------------------------------------------------------------
@@ -373,6 +479,8 @@ volumes:
373
479
  driver: local
374
480
  monitor_agent_data:
375
481
  driver: local
482
+ monitor_hub_data:
483
+ driver: local
376
484
 
377
485
  # ------------------------------------------------------------------
378
486
  # Networks
@@ -1 +1,11 @@
1
- {}
1
+ {
2
+ "port": 3100,
3
+ "host": "0.0.0.0",
4
+ "readOnly": false,
5
+ "adapters": {
6
+ "whatsapp": {
7
+ "enabled": false,
8
+ "accounts": []
9
+ }
10
+ }
11
+ }
@@ -5,11 +5,11 @@ const REGISTRY = "ghcr.io/askexe";
5
5
  // own package semver internally, but customer deployments use the tested stack
6
6
  // image alias so one stack manifest can pin the whole release. exe-os/exed is
7
7
  // the exception because the runtime package version is the orchestrator release.
8
- const STACK_IMAGE_TAG = "v0.9.2";
9
- const EXED_RELEASE_TAG = "v0.9.2";
8
+ const STACK_IMAGE_TAG = "v0.9.4";
9
+ const EXED_RELEASE_TAG = "v0.9.154";
10
10
 
11
11
  const POSTGRES_USER = "exe";
12
- const POSTGRES_DB = "default";
12
+ const POSTGRES_DB = "exedb";
13
13
  const CLICKHOUSE_DB = "default";
14
14
  const CLICKHOUSE_USER = "exe";
15
15
  export const CRM_IMAGE_TAG = `${REGISTRY}/exe-crm:${STACK_IMAGE_TAG}`;
@@ -21,6 +21,7 @@ const WIKI_HOST_PORT = "3001";
21
21
  export const EXE_OS_IMAGE_TAG = `${REGISTRY}/exe-os:${EXED_RELEASE_TAG}`;
22
22
  export const GATEWAY_IMAGE_TAG = `${REGISTRY}/exe-gateway:${STACK_IMAGE_TAG}`;
23
23
  export const MONITOR_AGENT_IMAGE_TAG = `${REGISTRY}/exe-monitor-agent:${STACK_IMAGE_TAG}`;
24
+ export const MONITOR_HUB_IMAGE_TAG = `${REGISTRY}/exe-monitor-hub:${STACK_IMAGE_TAG}`;
24
25
  const GATEWAY_HTTP_HOST_PORT = "3100";
25
26
  const GATEWAY_WS_HOST_PORT = "3101";
26
27
  const RANDOM_SECRET_16 = 16;
@@ -56,10 +57,18 @@ export function generateEnv(options: GenerateEnvOptions): string {
56
57
  "",
57
58
  `REDIS_PASSWORD=${randomSecret(RANDOM_SECRET_32)}`,
58
59
  "",
60
+ "# --- GoTrue (shared auth) ---",
61
+ `GOTRUE_JWT_SECRET=${randomSecret(RANDOM_SECRET_48)}`,
62
+ `GOTRUE_SITE_URL=https://crm.${normalizedDomain}`,
63
+ `GOTRUE_EXTERNAL_URL=https://auth.${normalizedDomain}`,
64
+ "GOTRUE_DISABLE_SIGNUP=false",
65
+ "GOTRUE_MAILER_AUTOCONFIRM=true",
66
+ "",
59
67
  "# --- CRM ---",
60
68
  `CRM_IMAGE_TAG=${CRM_IMAGE_TAG}`,
61
69
  `CRM_SERVER_URL=https://${normalizedDomain}`,
62
70
  `CRM_APP_SECRET=${randomSecret(RANDOM_SECRET_48)}`,
71
+ `EXE_CRM_ADMIN_TOKEN=${randomSecret(RANDOM_SECRET_48)}`,
63
72
  `CRM_HOST_PORT=${CRM_HOST_PORT}`,
64
73
  "",
65
74
  "# --- Wiki ---",
@@ -67,6 +76,7 @@ export function generateEnv(options: GenerateEnvOptions): string {
67
76
  `WIKI_DB_SCHEMA=${WIKI_DB_SCHEMA}`,
68
77
  `WIKI_VECTOR_DB=${WIKI_VECTOR_DB}`,
69
78
  `WIKI_AUTH_TOKEN=${randomSecret(RANDOM_SECRET_48)}`,
79
+ `EXE_WIKI_ADMIN_TOKEN=${randomSecret(RANDOM_SECRET_48)}`,
70
80
  `WIKI_JWT_SECRET=${randomSecret(RANDOM_SECRET_48)}`,
71
81
  `WIKI_SIG_KEY=${randomSecret(RANDOM_SECRET_48)}`,
72
82
  `WIKI_SIG_SALT=${randomSecret(RANDOM_SECRET_16)}`,
@@ -95,12 +105,15 @@ export function generateEnv(options: GenerateEnvOptions): string {
95
105
  `GATEWAY_HTTP_HOST_PORT=${GATEWAY_HTTP_HOST_PORT}`,
96
106
  `GATEWAY_WS_HOST_PORT=${GATEWAY_WS_HOST_PORT}`,
97
107
  "",
98
- "# --- Monitoring agent (standard for managed customer VPSs) ---",
108
+ "# --- Monitoring ---",
109
+ `MONITOR_HUB_IMAGE_TAG=${MONITOR_HUB_IMAGE_TAG}`,
99
110
  `MONITOR_AGENT_IMAGE_TAG=${MONITOR_AGENT_IMAGE_TAG}`,
111
+ `EXE_MONITOR_ADMIN_TOKEN=${randomSecret(RANDOM_SECRET_48)}`,
100
112
  `MONITOR_HUB_URL=${DEFAULT_MONITOR_HUB_URL}`,
101
113
  "MONITOR_AGENT_TOKEN=CHANGEME_MONITOR_AGENT_TOKEN_FROM_MONITOR_HUB",
102
114
  "MONITOR_AGENT_KEY=CHANGEME_MONITOR_AGENT_PUBLIC_KEY_FROM_MONITOR_HUB",
103
115
  `MONITOR_AGENT_LISTEN=${DEFAULT_MONITOR_AGENT_LISTEN}`,
116
+ "MONITOR_HUB_PORT=8090",
104
117
  "",
105
118
  "# --- License ---",
106
119
  LICENSE_KEY_COMMENT,
@@ -129,6 +142,7 @@ export function generateExampleEnv(): string {
129
142
  `CRM_IMAGE_TAG=${CRM_IMAGE_TAG}`,
130
143
  "CRM_SERVER_URL=https://CHANGEME_DOMAIN",
131
144
  "CRM_APP_SECRET=CHANGEME_CRM_APP_SECRET",
145
+ "EXE_CRM_ADMIN_TOKEN=CHANGEME_EXE_CRM_ADMIN_TOKEN",
132
146
  `CRM_HOST_PORT=${CRM_HOST_PORT}`,
133
147
  "",
134
148
  "# --- Wiki ---",
@@ -0,0 +1,55 @@
1
+ -- exe-os database initialization — runs on first boot.
2
+ -- Creates the exe superuser, schemas, and extensions.
3
+ -- Idempotent: safe to run multiple times.
4
+
5
+ -- Ensure exe user exists with superuser
6
+ DO $$ BEGIN
7
+ IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'exe') THEN
8
+ CREATE ROLE exe WITH LOGIN SUPERUSER PASSWORD current_setting('app.postgres_password', true);
9
+ ELSE
10
+ ALTER ROLE exe SUPERUSER;
11
+ END IF;
12
+ END $$;
13
+
14
+ -- Create extensions
15
+ CREATE EXTENSION IF NOT EXISTS vector;
16
+ CREATE EXTENSION IF NOT EXISTS pgcrypto;
17
+
18
+ -- Create all schemas
19
+ CREATE SCHEMA IF NOT EXISTS raw;
20
+ CREATE SCHEMA IF NOT EXISTS graph;
21
+ CREATE SCHEMA IF NOT EXISTS gateway;
22
+ CREATE SCHEMA IF NOT EXISTS wiki;
23
+ CREATE SCHEMA IF NOT EXISTS crm;
24
+ CREATE SCHEMA IF NOT EXISTS auth;
25
+
26
+ -- Grant exe full access to all schemas
27
+ DO $$ DECLARE s text; BEGIN
28
+ FOR s IN SELECT schema_name FROM information_schema.schemata
29
+ WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
30
+ LOOP
31
+ EXECUTE 'GRANT ALL ON SCHEMA ' || quote_ident(s) || ' TO exe';
32
+ EXECUTE 'GRANT ALL ON ALL TABLES IN SCHEMA ' || quote_ident(s) || ' TO exe';
33
+ EXECUTE 'GRANT ALL ON ALL SEQUENCES IN SCHEMA ' || quote_ident(s) || ' TO exe';
34
+ EXECUTE 'ALTER DEFAULT PRIVILEGES IN SCHEMA ' || quote_ident(s) || ' GRANT ALL ON TABLES TO exe';
35
+ EXECUTE 'ALTER DEFAULT PRIVILEGES IN SCHEMA ' || quote_ident(s) || ' GRANT ALL ON SEQUENCES TO exe';
36
+ END LOOP;
37
+ END $$;
38
+
39
+ -- Create raw.raw_events landing pad
40
+ CREATE TABLE IF NOT EXISTS raw.raw_events (
41
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
42
+ source TEXT NOT NULL,
43
+ source_id TEXT,
44
+ event_type TEXT NOT NULL,
45
+ payload JSONB NOT NULL,
46
+ metadata JSONB,
47
+ timestamp TIMESTAMPTZ DEFAULT now(),
48
+ created_at TIMESTAMPTZ DEFAULT now(),
49
+ processed_at TIMESTAMPTZ,
50
+ error TEXT,
51
+ projections JSONB DEFAULT '{}'::jsonb
52
+ );
53
+
54
+ CREATE INDEX IF NOT EXISTS idx_raw_events_unprocessed ON raw.raw_events (created_at) WHERE processed_at IS NULL;
55
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_raw_events_dedup ON raw.raw_events (source, source_id, event_type);
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env bash
2
+ # exe-os stack first-time setup — run once on a fresh VPS.
3
+ # Usage: ./setup.sh --client <name> --domain <domain> [--license <key>]
4
+ set -euo pipefail
5
+
6
+ RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
7
+ info() { echo -e "${GREEN}[setup]${NC} $1"; }
8
+ warn() { echo -e "${YELLOW}[setup]${NC} $1"; }
9
+ err() { echo -e "${RED}[setup]${NC} $1" >&2; }
10
+
11
+ # Parse args
12
+ CLIENT="" DOMAIN="" LICENSE=""
13
+ while [[ $# -gt 0 ]]; do
14
+ case $1 in
15
+ --client) CLIENT="$2"; shift 2;;
16
+ --domain) DOMAIN="$2"; shift 2;;
17
+ --license) LICENSE="$2"; shift 2;;
18
+ *) err "Unknown arg: $1"; exit 1;;
19
+ esac
20
+ done
21
+ [[ -z "$CLIENT" || -z "$DOMAIN" ]] && { err "Usage: ./setup.sh --client <name> --domain <domain>"; exit 1; }
22
+
23
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
24
+ cd "$SCRIPT_DIR"
25
+
26
+ # Step 1: Docker + GHCR auth
27
+ info "Step 1: Docker registry authentication"
28
+ if ! docker info >/dev/null 2>&1; then
29
+ err "Docker is not running. Install Docker first: https://docs.docker.com/engine/install/"
30
+ exit 1
31
+ fi
32
+
33
+ if [[ -f .ghcr-token ]]; then
34
+ info "Logging into GHCR with stored token..."
35
+ cat .ghcr-token | docker login ghcr.io -u exe-os-pull --password-stdin 2>/dev/null || true
36
+ elif [[ -n "${GHCR_TOKEN:-}" ]]; then
37
+ info "Logging into GHCR with GHCR_TOKEN env var..."
38
+ echo "$GHCR_TOKEN" | docker login ghcr.io -u exe-os-pull --password-stdin 2>/dev/null || true
39
+ else
40
+ warn "No GHCR token found. Set GHCR_TOKEN env var or create .ghcr-token file."
41
+ warn "Images must be pullable — public or pre-authed."
42
+ fi
43
+
44
+ # Step 2: Generate .env
45
+ info "Step 2: Generating .env file"
46
+ if [[ -f .env ]]; then
47
+ warn ".env already exists — skipping generation. Delete .env to regenerate."
48
+ else
49
+ if command -v node >/dev/null 2>&1; then
50
+ node -e "
51
+ const { generateEnv } = require('../../dist/deploy/compose/generate-env.js');
52
+ console.log(generateEnv({ clientName: '$CLIENT', domain: '$DOMAIN', licenseKey: '${LICENSE:-}' || undefined }));
53
+ " > .env 2>/dev/null || {
54
+ # Fallback: generate inline
55
+ info "Generating secrets inline..."
56
+ gen() { openssl rand -hex "$1"; }
57
+ cat > .env << ENVEOF
58
+ POSTGRES_USER=exe
59
+ POSTGRES_PASSWORD=$(gen 32)
60
+ POSTGRES_DB=exedb
61
+ CLICKHOUSE_DB=default
62
+ CLICKHOUSE_USER=exe
63
+ CLICKHOUSE_PASSWORD=$(gen 32)
64
+ REDIS_PASSWORD=$(gen 32)
65
+ GOTRUE_JWT_SECRET=$(gen 48)
66
+ GOTRUE_SITE_URL=https://crm.${DOMAIN}
67
+ GOTRUE_EXTERNAL_URL=https://auth.${DOMAIN}
68
+ GOTRUE_DISABLE_SIGNUP=false
69
+ GOTRUE_MAILER_AUTOCONFIRM=true
70
+ CRM_IMAGE_TAG=ghcr.io/askexe/exe-crm:v0.9.3
71
+ CRM_SERVER_URL=https://crm.${DOMAIN}
72
+ CRM_APP_SECRET=$(gen 48)
73
+ EXE_CRM_ADMIN_TOKEN=$(gen 48)
74
+ CRM_HOST_PORT=3000
75
+ WIKI_IMAGE_TAG=ghcr.io/askexe/exe-wiki:v0.9.4
76
+ WIKI_DB_SCHEMA=wiki
77
+ WIKI_VECTOR_DB=postgres
78
+ WIKI_AUTH_TOKEN=$(gen 48)
79
+ EXE_WIKI_ADMIN_TOKEN=$(gen 48)
80
+ WIKI_JWT_SECRET=$(gen 48)
81
+ WIKI_SIG_KEY=$(gen 48)
82
+ WIKI_SIG_SALT=$(gen 16)
83
+ WIKI_HOST_PORT=3001
84
+ EXE_OS_IMAGE_TAG=ghcr.io/askexe/exe-os:v0.9.155
85
+ EXED_MCP_TOKEN=$(gen 48)
86
+ EXED_DEVICE_ID=vps-${CLIENT}
87
+ EXE_CLOUD_SYNC_TO_POSTGRES=true
88
+ EXE_LICENSE_KEY=${LICENSE:-CHANGEME_EXE_LICENSE_KEY}
89
+ GATEWAY_IMAGE_TAG=ghcr.io/askexe/exe-gateway:v0.9.3
90
+ EXE_GATEWAY_AUTH_TOKEN=$(gen 48)
91
+ EXE_GATEWAY_WS_RELAY_AUTH_TOKEN=$(gen 48)
92
+ EXE_GATEWAY_WHATSAPP_VERIFY_TOKEN=$(gen 32)
93
+ API_ROUTER_URL=https://gateway.${DOMAIN}
94
+ GATEWAY_HTTP_HOST_PORT=3100
95
+ GATEWAY_WS_HOST_PORT=3101
96
+ MONITOR_HUB_IMAGE_TAG=ghcr.io/askexe/exe-monitor-hub:v0.9.4
97
+ MONITOR_AGENT_IMAGE_TAG=ghcr.io/askexe/exe-monitor-agent:v0.9.4
98
+ EXE_MONITOR_ADMIN_TOKEN=$(gen 48)
99
+ MONITOR_HUB_URL=https://monitor.${DOMAIN}
100
+ MONITOR_AGENT_TOKEN=CHANGEME_MONITOR_AGENT_TOKEN
101
+ MONITOR_AGENT_KEY=CHANGEME_MONITOR_AGENT_KEY
102
+ MONITOR_AGENT_LISTEN=:45876
103
+ MONITOR_HUB_PORT=8090
104
+ ENVEOF
105
+ }
106
+ fi
107
+ info ".env generated with auto-generated secrets"
108
+ fi
109
+
110
+ # Step 3: Cloudflared tunnel config
111
+ info "Step 3: Cloudflare Tunnel setup"
112
+ if [[ ! -f cloudflared/config.yml ]]; then
113
+ if [[ -f cloudflared/config.yml.example ]]; then
114
+ warn "Copy cloudflared/config.yml.example → cloudflared/config.yml"
115
+ warn "Then set TUNNEL_ID and DOMAIN in the config."
116
+ warn "Create tunnel: cloudflared tunnel create exe-${CLIENT}"
117
+ fi
118
+ else
119
+ info "Cloudflared config exists."
120
+ fi
121
+
122
+ # Step 4: Pull images
123
+ info "Step 4: Pulling Docker images..."
124
+ docker compose pull 2>&1 || { err "Image pull failed — check GHCR authentication"; exit 1; }
125
+
126
+ # Step 5: Start stack
127
+ info "Step 5: Starting stack..."
128
+ docker compose up -d 2>&1
129
+
130
+ # Step 6: Wait for health
131
+ info "Step 6: Waiting for services to be healthy..."
132
+ sleep 10
133
+ HEALTHY=0
134
+ for i in $(seq 1 30); do
135
+ COUNT=$(docker ps --filter "health=healthy" --format '{{.Names}}' | wc -l | tr -d ' ')
136
+ TOTAL=$(docker ps --format '{{.Names}}' | wc -l | tr -d ' ')
137
+ echo -ne "\r $COUNT/$TOTAL healthy (attempt $i/30)..."
138
+ if [[ "$COUNT" -ge "$TOTAL" ]]; then HEALTHY=1; break; fi
139
+ sleep 5
140
+ done
141
+ echo ""
142
+ [[ "$HEALTHY" -eq 1 ]] && info "All services healthy!" || warn "Some services not healthy yet — check docker ps"
143
+
144
+ # Step 7: Verify
145
+ info "Step 7: Verification"
146
+ docker ps --format 'table {{.Names}}\t{{.Status}}'
147
+ echo ""
148
+ info "Stack deployed! Next steps:"
149
+ echo " 1. Configure Cloudflare tunnel (if not done)"
150
+ echo " 2. Open https://crm.${DOMAIN} to create admin account"
151
+ echo " 3. Open https://wiki.${DOMAIN} to set up wiki"
152
+ echo " 4. Configure WhatsApp: edit gateway.json, restart gateway, pair at https://gateway.${DOMAIN}/pair/<name>"
153
+ echo " 5. Verify: curl https://crm.${DOMAIN}/healthz && curl https://wiki.${DOMAIN}/api/ping"
154
+
155
+ # Step 8: Firewall — ensure outbound HTTPS is open
156
+ info "Step 8: Checking network connectivity..."
157
+ if curl -s --max-time 5 https://ghcr.io >/dev/null 2>&1; then
158
+ info "Outbound HTTPS working (GHCR reachable)"
159
+ else
160
+ warn "Cannot reach ghcr.io — check firewall/network. Docker pulls will fail."
161
+ fi
162
+ if curl -s --max-time 5 https://api.cloudflare.com >/dev/null 2>&1; then
163
+ info "Cloudflare reachable"
164
+ else
165
+ warn "Cannot reach Cloudflare — tunnel won't work."
166
+ fi