openship 0.4.5 → 0.4.6

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 (314) hide show
  1. package/README.md +2 -24
  2. package/dist/bare-2HYDXQEH.js +23 -0
  3. package/dist/{bowser-L6PVH37V.js → bowser-O3AEA6AN.js} +3 -1
  4. package/dist/{chunk-R74UB4X4.js → chunk-2ZLR7ULL.js} +253 -65
  5. package/dist/{chunk-E3ZPCNPU.js → chunk-3JQTL4R4.js} +234 -99
  6. package/dist/{chunk-YFLQGDN2.js → chunk-4TJC4JZQ.js} +18 -9
  7. package/dist/{chunk-JXQCFTDV.js → chunk-4Z5QO7N6.js} +4 -2
  8. package/dist/{chunk-7EYFCKZX.js → chunk-5FNIWBFI.js} +8 -6
  9. package/dist/{chunk-JJVSOE4I.js → chunk-5MZ624VX.js} +2 -0
  10. package/dist/{chunk-TDFK6ISF.js → chunk-6APIPIJY.js} +6 -4
  11. package/dist/{chunk-3LCRNQ2C.js → chunk-6APULWSU.js} +151 -133
  12. package/dist/{chunk-FTBXJ2VA.js → chunk-6B5YCFFV.js} +6 -4
  13. package/dist/{chunk-MDFYN2QH.js → chunk-AVIB7CT7.js} +3 -1
  14. package/dist/{chunk-VJTXRCTT.js → chunk-C5OHTYEF.js} +10 -6
  15. package/dist/{chunk-W7KGHYLV.js → chunk-DYPH4HCI.js} +2 -0
  16. package/dist/{chunk-5WI7ZXOC.js → chunk-EI575GHN.js} +4 -2
  17. package/dist/{chunk-Z6EKX6CX.js → chunk-FPRHYBY2.js} +2 -0
  18. package/dist/chunk-FQGJZ2HS.js +248 -0
  19. package/dist/{chunk-FZJX6XP7.js → chunk-FRNAR33O.js} +4 -2
  20. package/dist/{chunk-XRWV5ZNR.js → chunk-FXTBHTWY.js} +360 -42
  21. package/dist/chunk-ILFETZOM.js +131 -0
  22. package/dist/{chunk-PARRPUXN.js → chunk-IXCHURVK.js} +5 -3
  23. package/dist/{chunk-YJAS5RQX.js → chunk-JYGD5WSN.js} +2 -0
  24. package/dist/{chunk-JWBXYO24.js → chunk-KI2EY3WB.js} +2 -0
  25. package/dist/{chunk-YVXF6GGR.js → chunk-L2VOGB2X.js} +5 -104
  26. package/dist/{chunk-K5OH5U7N.js → chunk-MZ2WEHHY.js} +5 -3
  27. package/dist/{chunk-SJBR527P.js → chunk-O6ZZF52V.js} +3 -1
  28. package/dist/{chunk-VLPJLXXB.js → chunk-OWED2GRM.js} +3 -1
  29. package/dist/{chunk-C4FOQJ63.js → chunk-OXWDY22G.js} +4 -2
  30. package/dist/{chunk-NPVJYR7M.js → chunk-Q2CIRAOK.js} +2 -0
  31. package/dist/{chunk-JYAGTMSJ.js → chunk-RPHRPFEH.js} +2 -0
  32. package/dist/{chunk-UMYQALBH.js → chunk-SQ77DZOI.js} +1726 -1484
  33. package/dist/{chunk-HYL4QYZM.js → chunk-TGIINPVM.js} +13 -2
  34. package/dist/{chunk-SYCZE6FQ.js → chunk-TYVME3XD.js} +7 -1
  35. package/dist/{chunk-HDV4NSA3.js → chunk-V6AN6XLZ.js} +16 -6
  36. package/dist/{chunk-3YCMJRDZ.js → chunk-VDMQJHPI.js} +36 -15
  37. package/dist/{chunk-RZPDJC2J.js → chunk-VFINNTUL.js} +20 -1
  38. package/dist/{chunk-65KKUJBR.js → chunk-VWPEKOH6.js} +4 -2
  39. package/dist/{chunk-GHXGCANU.js → chunk-W73X43R7.js} +4 -2
  40. package/dist/{cloud-7IGG45CF.js → cloud-7QYOGF7F.js} +4 -2
  41. package/dist/cloud-ZSKOS4GY.js +30 -0
  42. package/dist/detect-SH4FHFBI.js +36 -0
  43. package/dist/{dist-NQB6QGX3.js → dist-IQJ6H6UZ.js} +4 -2
  44. package/dist/{dist-es-DCR6UJMP.js → dist-es-5DY7K32L.js} +7 -5
  45. package/dist/{dist-es-TYYZ5KEF.js → dist-es-JSTDUS7Q.js} +7 -5
  46. package/dist/{dist-es-7EZIM4JU.js → dist-es-OSNJBLXB.js} +6 -4
  47. package/dist/{dist-es-6RSPR56Q.js → dist-es-RYHIPOTN.js} +15 -13
  48. package/dist/{dist-es-33Z3Y6VE.js → dist-es-VNKHQVIB.js} +5 -3
  49. package/dist/{dist-es-D6QQVWRZ.js → dist-es-WV4AMOII.js} +9 -7
  50. package/dist/{dist-es-GMEQINIE.js → dist-es-X3B5QYKB.js} +7 -5
  51. package/dist/docker-U7AF5PTA.js +16 -0
  52. package/dist/docker-edge-executor-VHW3JPJS.js +13 -0
  53. package/dist/edge-import-V5CBA47C.js +45 -0
  54. package/dist/ensure-container-edge-CMYROUSI.js +33 -0
  55. package/dist/{event-streams-G7F6Q7QY.js → event-streams-EHRAKL75.js} +5 -3
  56. package/dist/{executor-O5NR7LMP.js → executor-YPT6RH6O.js} +9 -7
  57. package/dist/index.d.ts +2 -1
  58. package/dist/index.js +256 -245
  59. package/dist/{loadSso-LH753OSJ.js → loadSso-46FQRIZS.js} +11 -9
  60. package/dist/local-executor-V7U5VOZW.js +12 -0
  61. package/dist/nginx-2GTRUIDE.js +24 -0
  62. package/dist/{noop-6KC6HRDB.js → noop-FMMFG2EZ.js} +4 -2
  63. package/dist/{openresty-lua-KFCOJAG4.js → openresty-lua-4WLGDSER.js} +11 -3
  64. package/dist/server/engine/ChangeLog +2289 -0
  65. package/dist/server/engine/Documentations +5 -0
  66. package/dist/server/engine/LICENSE +674 -0
  67. package/dist/server/engine/README.md +34 -0
  68. package/dist/server/engine/conf/amavisd +150 -0
  69. package/dist/server/engine/conf/clamav +71 -0
  70. package/dist/server/engine/conf/core +632 -0
  71. package/dist/server/engine/conf/dovecot +151 -0
  72. package/dist/server/engine/conf/fail2ban +72 -0
  73. package/dist/server/engine/conf/global +698 -0
  74. package/dist/server/engine/conf/iredapd +52 -0
  75. package/dist/server/engine/conf/logwatch +28 -0
  76. package/dist/server/engine/conf/postfix +97 -0
  77. package/dist/server/engine/conf/postgresql +93 -0
  78. package/dist/server/engine/conf/spamassassin +39 -0
  79. package/dist/server/engine/dialog/config_via_dialog.sh +258 -0
  80. package/dist/server/engine/dialog/optional_components.sh +122 -0
  81. package/dist/server/engine/dialog/pgsql_config.sh +52 -0
  82. package/dist/server/engine/dialog/virtual_domain_config.sh +94 -0
  83. package/dist/server/engine/functions/amavisd.sh +267 -0
  84. package/dist/server/engine/functions/backend.sh +85 -0
  85. package/dist/server/engine/functions/clamav.sh +100 -0
  86. package/dist/server/engine/functions/cleanup.sh +393 -0
  87. package/dist/server/engine/functions/dovecot.sh +583 -0
  88. package/dist/server/engine/functions/fail2ban.sh +209 -0
  89. package/dist/server/engine/functions/iredapd.sh +356 -0
  90. package/dist/server/engine/functions/optional_components.sh +24 -0
  91. package/dist/server/engine/functions/packages.sh +522 -0
  92. package/dist/server/engine/functions/packages_freebsd.sh +179 -0
  93. package/dist/server/engine/functions/postfix.sh +457 -0
  94. package/dist/server/engine/functions/postgresql.sh +239 -0
  95. package/dist/server/engine/functions/spamassassin.sh +49 -0
  96. package/dist/server/engine/functions/system_accounts.sh +128 -0
  97. package/dist/server/engine/iRedMail.sh +144 -0
  98. package/dist/server/engine/pkgs/get_all.sh +338 -0
  99. package/dist/server/engine/pkgs/pkgs.freebsd.sha256 +3 -0
  100. package/dist/server/engine/pkgs/pkgs.openbsd.sha256 +6 -0
  101. package/dist/server/engine/pkgs/pkgs.sha256 +1 -0
  102. package/dist/server/engine/samples/amavisd/amavisd.conf +677 -0
  103. package/dist/server/engine/samples/amavisd/amavisd.mysql +278 -0
  104. package/dist/server/engine/samples/amavisd/amavisd.pgsql +253 -0
  105. package/dist/server/engine/samples/amavisd/amavisd.schema +942 -0
  106. package/dist/server/engine/samples/amavisd/default_spam_policy.sql +46 -0
  107. package/dist/server/engine/samples/dovecot/dovecot-2.4-mariadb.conf +758 -0
  108. package/dist/server/engine/samples/dovecot/dovecot-2.4-openldap.conf +767 -0
  109. package/dist/server/engine/samples/dovecot/dovecot-2.4-pgsql.conf +762 -0
  110. package/dist/server/engine/samples/dovecot/dovecot-last-login.conf +50 -0
  111. package/dist/server/engine/samples/dovecot/dovecot-ldap.conf +21 -0
  112. package/dist/server/engine/samples/dovecot/dovecot-share-folder.conf +22 -0
  113. package/dist/server/engine/samples/dovecot/dovecot-sql.conf +28 -0
  114. package/dist/server/engine/samples/dovecot/dovecot-used-quota.conf +13 -0
  115. package/dist/server/engine/samples/dovecot/dovecot.conf +484 -0
  116. package/dist/server/engine/samples/dovecot/dovecot.sieve +7 -0
  117. package/dist/server/engine/samples/dovecot/dovecot2-quota-warning.sh +38 -0
  118. package/dist/server/engine/samples/dovecot/sql/imap_share_folder.mysql +16 -0
  119. package/dist/server/engine/samples/dovecot/sql/last_login.mysql +12 -0
  120. package/dist/server/engine/samples/dovecot/sql/used_quota.mysql +26 -0
  121. package/dist/server/engine/samples/dovecot/systemd/override.conf +3 -0
  122. package/dist/server/engine/samples/fail2ban/action.d/banned_db.conf +9 -0
  123. package/dist/server/engine/samples/fail2ban/bin/fail2ban_banned_db +268 -0
  124. package/dist/server/engine/samples/fail2ban/fail2ban.local +17 -0
  125. package/dist/server/engine/samples/fail2ban/filter.d/dovecot.iredmail.conf +5 -0
  126. package/dist/server/engine/samples/fail2ban/filter.d/postfix-pregreet.iredmail.conf +16 -0
  127. package/dist/server/engine/samples/fail2ban/filter.d/postfix.iredmail.conf +18 -0
  128. package/dist/server/engine/samples/fail2ban/filter.d/roundcube.iredmail.conf +9 -0
  129. package/dist/server/engine/samples/fail2ban/jail.d/dovecot.local +8 -0
  130. package/dist/server/engine/samples/fail2ban/jail.d/nginx-http-auth.local +8 -0
  131. package/dist/server/engine/samples/fail2ban/jail.d/postfix-pregreet.local +9 -0
  132. package/dist/server/engine/samples/fail2ban/jail.d/postfix.local +8 -0
  133. package/dist/server/engine/samples/fail2ban/jail.d/roundcube.local +9 -0
  134. package/dist/server/engine/samples/fail2ban/jail.d/sogo.local +8 -0
  135. package/dist/server/engine/samples/fail2ban/jail.d/sshd.local +8 -0
  136. package/dist/server/engine/samples/fail2ban/jail.local +6 -0
  137. package/dist/server/engine/samples/fail2ban/openbsd/rc +27 -0
  138. package/dist/server/engine/samples/fail2ban/sql/fail2ban.mysql +49 -0
  139. package/dist/server/engine/samples/fail2ban/sql/fail2ban.pgsql +48 -0
  140. package/dist/server/engine/samples/firewall/firewalld/services/ssh.xml +6 -0
  141. package/dist/server/engine/samples/firewall/firewalld/zones/iredmail.xml +15 -0
  142. package/dist/server/engine/samples/firewall/iptables/ip6tables.init.debian +362 -0
  143. package/dist/server/engine/samples/firewall/iptables/ip6tables.rules +53 -0
  144. package/dist/server/engine/samples/firewall/iptables/iptables.init.debian +363 -0
  145. package/dist/server/engine/samples/firewall/iptables/iptables.rules +54 -0
  146. package/dist/server/engine/samples/firewall/nftables.conf +58 -0
  147. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/dovecot.conf +3 -0
  148. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/iredapd.conf +1 -0
  149. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/mlmmjadmin.conf +1 -0
  150. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/php-fpm.conf +2 -0
  151. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/slapd.conf +1 -0
  152. package/dist/server/engine/samples/freebsd/syslog.d/dovecot.conf +2 -0
  153. package/dist/server/engine/samples/freebsd/syslog.d/fail2ban.conf +2 -0
  154. package/dist/server/engine/samples/freebsd/syslog.d/iredapd.conf +2 -0
  155. package/dist/server/engine/samples/freebsd/syslog.d/mlmmjadmin.conf +2 -0
  156. package/dist/server/engine/samples/freebsd/syslog.d/php-fpm.conf +2 -0
  157. package/dist/server/engine/samples/freebsd/syslog.d/slapd.conf +2 -0
  158. package/dist/server/engine/samples/iredmail/iredmail.mysql +491 -0
  159. package/dist/server/engine/samples/iredmail/iredmail.pgsql +465 -0
  160. package/dist/server/engine/samples/iredmail/iredmail.schema +456 -0
  161. package/dist/server/engine/samples/iredmail/used_quota_triggers_dovecot_2.3.pgsql +40 -0
  162. package/dist/server/engine/samples/iredmail/used_quota_triggers_dovecot_2.4.pgsql +18 -0
  163. package/dist/server/engine/samples/logrotate/dovecot +13 -0
  164. package/dist/server/engine/samples/logrotate/iredapd +13 -0
  165. package/dist/server/engine/samples/logrotate/mlmmjadmin +13 -0
  166. package/dist/server/engine/samples/logrotate/openldap +19 -0
  167. package/dist/server/engine/samples/logrotate/php-fpm +13 -0
  168. package/dist/server/engine/samples/openbsd/ldapd.conf +58 -0
  169. package/dist/server/engine/samples/openbsd/pf.conf +28 -0
  170. package/dist/server/engine/samples/postfix/command_filter.pcre +3 -0
  171. package/dist/server/engine/samples/postfix/freebsd/mailer.conf +9 -0
  172. package/dist/server/engine/samples/postfix/helo_access.pcre +180 -0
  173. package/dist/server/engine/samples/postfix/ldap/catchall_maps.cf +11 -0
  174. package/dist/server/engine/samples/postfix/ldap/recipient_bcc_maps_domain.cf +11 -0
  175. package/dist/server/engine/samples/postfix/ldap/recipient_bcc_maps_user.cf +11 -0
  176. package/dist/server/engine/samples/postfix/ldap/relay_domains.cf +11 -0
  177. package/dist/server/engine/samples/postfix/ldap/sender_bcc_maps_domain.cf +11 -0
  178. package/dist/server/engine/samples/postfix/ldap/sender_bcc_maps_user.cf +11 -0
  179. package/dist/server/engine/samples/postfix/ldap/sender_dependent_relayhost_maps_domain.cf +11 -0
  180. package/dist/server/engine/samples/postfix/ldap/sender_dependent_relayhost_maps_user.cf +11 -0
  181. package/dist/server/engine/samples/postfix/ldap/sender_login_maps.cf +11 -0
  182. package/dist/server/engine/samples/postfix/ldap/transport_maps_domain.cf +11 -0
  183. package/dist/server/engine/samples/postfix/ldap/transport_maps_user.cf +11 -0
  184. package/dist/server/engine/samples/postfix/ldap/virtual_alias_maps.cf +11 -0
  185. package/dist/server/engine/samples/postfix/ldap/virtual_group_maps.cf +11 -0
  186. package/dist/server/engine/samples/postfix/ldap/virtual_group_members_maps.cf +11 -0
  187. package/dist/server/engine/samples/postfix/ldap/virtual_mailbox_domains.cf +11 -0
  188. package/dist/server/engine/samples/postfix/ldap/virtual_mailbox_maps.cf +12 -0
  189. package/dist/server/engine/samples/postfix/main.cf +282 -0
  190. package/dist/server/engine/samples/postfix/main.cf.amavisd +7 -0
  191. package/dist/server/engine/samples/postfix/main.cf.dovecot +8 -0
  192. package/dist/server/engine/samples/postfix/main.cf.ldap +39 -0
  193. package/dist/server/engine/samples/postfix/main.cf.mlmmj +5 -0
  194. package/dist/server/engine/samples/postfix/main.cf.mysql +39 -0
  195. package/dist/server/engine/samples/postfix/main.cf.pgsql +39 -0
  196. package/dist/server/engine/samples/postfix/main.cf.postscreen +30 -0
  197. package/dist/server/engine/samples/postfix/master.cf +87 -0
  198. package/dist/server/engine/samples/postfix/mysql/catchall_maps.cf +5 -0
  199. package/dist/server/engine/samples/postfix/mysql/domain_alias_catchall_maps.cf +5 -0
  200. package/dist/server/engine/samples/postfix/mysql/domain_alias_maps.cf +5 -0
  201. package/dist/server/engine/samples/postfix/mysql/recipient_bcc_maps_domain.cf +5 -0
  202. package/dist/server/engine/samples/postfix/mysql/recipient_bcc_maps_user.cf +5 -0
  203. package/dist/server/engine/samples/postfix/mysql/relay_domains.cf +5 -0
  204. package/dist/server/engine/samples/postfix/mysql/sender_bcc_maps_domain.cf +5 -0
  205. package/dist/server/engine/samples/postfix/mysql/sender_bcc_maps_user.cf +5 -0
  206. package/dist/server/engine/samples/postfix/mysql/sender_dependent_relayhost_maps.cf +6 -0
  207. package/dist/server/engine/samples/postfix/mysql/sender_login_maps.cf +5 -0
  208. package/dist/server/engine/samples/postfix/mysql/transport_maps_domain.cf +5 -0
  209. package/dist/server/engine/samples/postfix/mysql/transport_maps_maillist.cf +5 -0
  210. package/dist/server/engine/samples/postfix/mysql/transport_maps_user.cf +5 -0
  211. package/dist/server/engine/samples/postfix/mysql/virtual_alias_maps.cf +5 -0
  212. package/dist/server/engine/samples/postfix/mysql/virtual_mailbox_domains.cf +5 -0
  213. package/dist/server/engine/samples/postfix/mysql/virtual_mailbox_maps.cf +5 -0
  214. package/dist/server/engine/samples/postfix/pgsql/catchall_maps.cf +5 -0
  215. package/dist/server/engine/samples/postfix/pgsql/domain_alias_catchall_maps.cf +5 -0
  216. package/dist/server/engine/samples/postfix/pgsql/domain_alias_maps.cf +5 -0
  217. package/dist/server/engine/samples/postfix/pgsql/recipient_bcc_maps_domain.cf +5 -0
  218. package/dist/server/engine/samples/postfix/pgsql/recipient_bcc_maps_user.cf +5 -0
  219. package/dist/server/engine/samples/postfix/pgsql/relay_domains.cf +5 -0
  220. package/dist/server/engine/samples/postfix/pgsql/sender_bcc_maps_domain.cf +5 -0
  221. package/dist/server/engine/samples/postfix/pgsql/sender_bcc_maps_user.cf +5 -0
  222. package/dist/server/engine/samples/postfix/pgsql/sender_dependent_relayhost_maps.cf +6 -0
  223. package/dist/server/engine/samples/postfix/pgsql/sender_login_maps.cf +5 -0
  224. package/dist/server/engine/samples/postfix/pgsql/transport_maps_domain.cf +5 -0
  225. package/dist/server/engine/samples/postfix/pgsql/transport_maps_maillist.cf +5 -0
  226. package/dist/server/engine/samples/postfix/pgsql/transport_maps_user.cf +5 -0
  227. package/dist/server/engine/samples/postfix/pgsql/virtual_alias_maps.cf +5 -0
  228. package/dist/server/engine/samples/postfix/pgsql/virtual_mailbox_domains.cf +5 -0
  229. package/dist/server/engine/samples/postfix/pgsql/virtual_mailbox_maps.cf +5 -0
  230. package/dist/server/engine/samples/postfix/postscreen_access.cidr +6 -0
  231. package/dist/server/engine/samples/postgresql/sql/add_first_domain_and_user.sql +34 -0
  232. package/dist/server/engine/samples/postgresql/sql/grant_permissions.sql +14 -0
  233. package/dist/server/engine/samples/postgresql/sql/init_vmail_db.sql +15 -0
  234. package/dist/server/engine/samples/rsyslog.d/1-iredmail-dovecot.conf +23 -0
  235. package/dist/server/engine/samples/rsyslog.d/1-iredmail-fail2ban.conf +2 -0
  236. package/dist/server/engine/samples/rsyslog.d/1-iredmail-iredapd.conf +12 -0
  237. package/dist/server/engine/samples/rsyslog.d/1-iredmail-mlmmjadmin.conf +12 -0
  238. package/dist/server/engine/samples/rsyslog.d/1-iredmail-openldap.conf +2 -0
  239. package/dist/server/engine/samples/rsyslog.d/1-iredmail-phpfpm.conf +3 -0
  240. package/dist/server/engine/samples/spamassassin/local.cf +186 -0
  241. package/dist/server/engine/samples/spamassassin/razor.conf +1 -0
  242. package/dist/server/engine/samples/systemd/clamd.service.d/override.conf +2 -0
  243. package/dist/server/engine/samples/systemd/mariadb.service.d/override.conf +3 -0
  244. package/dist/server/engine/samples/systemd/slapd.service.d/override.conf +6 -0
  245. package/dist/server/engine/samples/yum/CentOS-Linux-AppStream.repo +6 -0
  246. package/dist/server/engine/samples/yum/CentOS-Linux-PowerTools.repo +6 -0
  247. package/dist/server/engine/samples/yum/CentOS-Stream-AppStream.repo +7 -0
  248. package/dist/server/engine/samples/yum/CentOS-Stream-PowerTools.repo +7 -0
  249. package/dist/server/engine/samples/yum/symas-openldap.repo +6 -0
  250. package/dist/server/engine/tools/add_enabled_service.py +60 -0
  251. package/dist/server/engine/tools/backup_mysql.sh +238 -0
  252. package/dist/server/engine/tools/backup_openldap.sh +230 -0
  253. package/dist/server/engine/tools/backup_pgsql.sh +217 -0
  254. package/dist/server/engine/tools/backup_sogo.sh +156 -0
  255. package/dist/server/engine/tools/create_mail_user_OpenLDAP.py +335 -0
  256. package/dist/server/engine/tools/create_mail_user_OpenLDAP.sh +287 -0
  257. package/dist/server/engine/tools/create_mail_user_SQL.sh +127 -0
  258. package/dist/server/engine/tools/enable_postscreen.sh +144 -0
  259. package/dist/server/engine/tools/fail2ban_unban_ip.sh +39 -0
  260. package/dist/server/engine/tools/find_sasl_login_ip.sh +20 -0
  261. package/dist/server/engine/tools/find_top_sasl_usernames.sh +28 -0
  262. package/dist/server/engine/tools/generate_ssl_keys.sh +85 -0
  263. package/dist/server/engine/tools/kill_high_cpu_sogo_process.sh +32 -0
  264. package/dist/server/engine/tools/ldap_assign_user_to_groups.py +151 -0
  265. package/dist/server/engine/tools/ldap_move_members_to_another_group.py +129 -0
  266. package/dist/server/engine/tools/migrate_sql_alias_table.py +177 -0
  267. package/dist/server/engine/update/0.9.8/amavisd.mysql +29 -0
  268. package/dist/server/engine/update/0.9.8/amavisd.pgsql +39 -0
  269. package/dist/server/engine/update/0.9.8/iredmail.mysql +53 -0
  270. package/dist/server/engine/update/0.9.8/iredmail.pgsql +61 -0
  271. package/dist/server/engine/update/0.9.9/iredmail.mysql +2 -0
  272. package/dist/server/engine/update/0.9.9/iredmail.pgsql +2 -0
  273. package/dist/server/engine/update/1.0/iredmail.mysql +2 -0
  274. package/dist/server/engine/update/1.0/iredmail.pgsql +2 -0
  275. package/dist/server/engine/update/1.2/last_login.mysql +16 -0
  276. package/dist/server/engine/update/1.4.0/iredmail.mysql +22 -0
  277. package/dist/server/engine/update/1.4.0/iredmail.pgsql +20 -0
  278. package/dist/server/engine/update/1.4.1/iredmail.mysql +3 -0
  279. package/dist/server/engine/update/1.4.1/iredmail.pgsql +3 -0
  280. package/dist/server/engine/update/1.4.1/sogo.mysql +11 -0
  281. package/dist/server/engine/update/1.4.1/sogo.pgsql +30 -0
  282. package/dist/server/engine/update/1.4.1/update-ldap.py +66 -0
  283. package/dist/server/engine/update/1.4.2/iredmail.mysql +9 -0
  284. package/dist/server/engine/update/1.4.2/iredmail.pgsql +9 -0
  285. package/dist/server/engine/update/1.4.2/sogo.pgsql +30 -0
  286. package/dist/server/engine/update/1.6.0/sogo_view.pgsql +27 -0
  287. package/dist/server/engine/update/1.6.3/iredmail.mysql +1 -0
  288. package/dist/server/engine/update/1.7.0/fail2ban.mysql +11 -0
  289. package/dist/server/engine/update/1.7.0/fail2ban.pgsql +12 -0
  290. package/dist/server/engine/update/1.7.1/amavisd.mysql +1 -0
  291. package/dist/server/engine/update/1.7.2/vmail.mysql +30 -0
  292. package/dist/server/engine/update/1.7.3/deleted_mailboxes.mysql +43 -0
  293. package/dist/server/engine/update/1.7.3/iredadmin.mysql +26 -0
  294. package/dist/server/engine/update/1.7.3/vmail.mysql +79 -0
  295. package/dist/server/engine/update/1.7.3/vmail.pgsql +9 -0
  296. package/dist/server/engine/update/README.md +1 -0
  297. package/dist/server/engine/update/ldap/README.md +40 -0
  298. package/dist/server/engine/update/ldap/update-ldap-dovecot-2.3.py +69 -0
  299. package/dist/server/index.js +55574 -53675
  300. package/dist/server/migrations/0071_gh_device_token.sql +3 -0
  301. package/dist/server/migrations/0072_gh_device_token_method.sql +1 -0
  302. package/dist/server/migrations/meta/_journal.json +14 -0
  303. package/dist/setup-HQIZLUOJ.js +20 -0
  304. package/dist/{signin-PVC7MBZJ.js → signin-ITOPKKUB.js} +11 -9
  305. package/dist/{sso-oidc-ZXY77BSL.js → sso-oidc-NON2PD7Y.js} +11 -9
  306. package/dist/{sts-LX5L4UU3.js → sts-HHFFSAY2.js} +10 -8
  307. package/package.json +1 -1
  308. package/dist/bare-CNSFDNSZ.js +0 -20
  309. package/dist/cloud-655YYCEW.js +0 -26
  310. package/dist/docker-UWQPDEGW.js +0 -14
  311. package/dist/docker-edge-executor-ZLAYE5FE.js +0 -11
  312. package/dist/edge-import-AFID2EN3.js +0 -41
  313. package/dist/nginx-M36GDYL7.js +0 -18
  314. package/dist/setup-ZXXB5S22.js +0 -16
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env sh
2
+ ":" //# ; exec "$(command -v bun || command -v node)" "$0" "$@"
2
3
  import { createRequire as __ospCreateRequire } from "node:module";
3
4
  const require = __ospCreateRequire(import.meta.url);
4
5
  import {
@@ -8,6 +9,9 @@ import {
8
9
  composeInternalToken,
9
10
  composeIsViableDefault,
10
11
  composePs,
12
+ composeRestart,
13
+ composeRunning,
14
+ composeStart,
11
15
  composeUninstall,
12
16
  composeUp,
13
17
  composeUpdate,
@@ -21,47 +25,54 @@ import {
21
25
  remoteSha,
22
26
  shortSha,
23
27
  sourceBuildDir
24
- } from "./chunk-E3ZPCNPU.js";
25
- import "./chunk-TDFK6ISF.js";
26
- import "./chunk-5WI7ZXOC.js";
27
- import "./chunk-PARRPUXN.js";
28
- import "./chunk-VLPJLXXB.js";
29
- import "./chunk-JJVSOE4I.js";
30
- import "./chunk-NPVJYR7M.js";
31
- import "./chunk-HYL4QYZM.js";
32
- import "./chunk-VJTXRCTT.js";
33
- import "./chunk-SJBR527P.js";
34
- import "./chunk-FZJX6XP7.js";
35
- import "./chunk-65KKUJBR.js";
36
- import "./chunk-GHXGCANU.js";
37
- import "./chunk-YJAS5RQX.js";
38
- import "./chunk-R74UB4X4.js";
39
- import "./chunk-3YCMJRDZ.js";
40
- import "./chunk-7EYFCKZX.js";
28
+ } from "./chunk-3JQTL4R4.js";
29
+ import "./chunk-FRNAR33O.js";
30
+ import "./chunk-EI575GHN.js";
31
+ import "./chunk-6APIPIJY.js";
32
+ import "./chunk-IXCHURVK.js";
33
+ import "./chunk-OWED2GRM.js";
34
+ import "./chunk-VWPEKOH6.js";
35
+ import "./chunk-W73X43R7.js";
36
+ import "./chunk-TGIINPVM.js";
37
+ import "./chunk-5FNIWBFI.js";
38
+ import "./chunk-AVIB7CT7.js";
39
+ import "./chunk-5MZ624VX.js";
40
+ import "./chunk-Q2CIRAOK.js";
41
+ import "./chunk-C5OHTYEF.js";
41
42
  import {
42
43
  beginEdgeTakeover,
44
+ collectProxyCerts,
43
45
  completeEdgeTakeover,
44
46
  detectInstalledProxy,
47
+ edgeProxy,
48
+ edgeProxyFor,
45
49
  foreignProxyOnEdge,
46
50
  importSites,
47
- ourEdgeContainerRunning,
48
51
  recoverInterruptedTakeover,
49
52
  rollbackEdgeTakeover,
50
53
  scanImportableSites
51
- } from "./chunk-UMYQALBH.js";
52
- import "./chunk-HDV4NSA3.js";
53
- import "./chunk-K5OH5U7N.js";
54
- import "./chunk-YVXF6GGR.js";
55
- import "./chunk-JXQCFTDV.js";
56
- import "./chunk-YFLQGDN2.js";
54
+ } from "./chunk-SQ77DZOI.js";
55
+ import "./chunk-O6ZZF52V.js";
56
+ import "./chunk-JYGD5WSN.js";
57
+ import "./chunk-2ZLR7ULL.js";
58
+ import "./chunk-V6AN6XLZ.js";
59
+ import "./chunk-VDMQJHPI.js";
60
+ import "./chunk-MZ2WEHHY.js";
61
+ import "./chunk-L2VOGB2X.js";
62
+ import "./chunk-4Z5QO7N6.js";
63
+ import "./chunk-4TJC4JZQ.js";
64
+ import "./chunk-OXWDY22G.js";
65
+ import "./chunk-RPHRPFEH.js";
57
66
  import {
58
67
  LocalExecutor
59
- } from "./chunk-SYCZE6FQ.js";
60
- import "./chunk-C4FOQJ63.js";
61
- import "./chunk-JYAGTMSJ.js";
62
- import "./chunk-XRWV5ZNR.js";
63
- import "./chunk-Z6EKX6CX.js";
64
- import "./chunk-RZPDJC2J.js";
68
+ } from "./chunk-TYVME3XD.js";
69
+ import "./chunk-FXTBHTWY.js";
70
+ import "./chunk-FPRHYBY2.js";
71
+ import {
72
+ ourEdgeContainerRunning
73
+ } from "./chunk-FQGJZ2HS.js";
74
+ import "./chunk-ILFETZOM.js";
75
+ import "./chunk-VFINNTUL.js";
65
76
  import {
66
77
  CLOUD_DASHBOARD_URL,
67
78
  LOCAL_API_URL,
@@ -70,9 +81,8 @@ import {
70
81
  isValidEmail,
71
82
  parseOpenshipConfigJson,
72
83
  resolveCliUpdatePlan
73
- } from "./chunk-3LCRNQ2C.js";
74
- import "./chunk-MDFYN2QH.js";
75
- import "./chunk-JWBXYO24.js";
84
+ } from "./chunk-6APULWSU.js";
85
+ import "./chunk-KI2EY3WB.js";
76
86
 
77
87
  // src/index.ts
78
88
  import { Command as Command28 } from "commander";
@@ -474,7 +484,7 @@ import chalk6 from "chalk";
474
484
  import ora from "ora";
475
485
  import { spawn } from "child_process";
476
486
  import { randomBytes as randomBytes2 } from "crypto";
477
- import { createWriteStream as createWriteStream2, existsSync as existsSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
487
+ import { createWriteStream as createWriteStream2, existsSync as existsSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync7 } from "fs";
478
488
  import { dirname as dirname2, join as join8 } from "path";
479
489
  import { fileURLToPath } from "url";
480
490
 
@@ -1000,20 +1010,10 @@ async function resolvePorts(prefs) {
1000
1010
 
1001
1011
  // src/lib/edge-preflight.ts
1002
1012
  import { spawnSync as spawnSync3 } from "child_process";
1003
- import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
1013
+ import { existsSync as existsSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "fs";
1004
1014
  import { join as join6 } from "path";
1005
1015
  import chalk5 from "chalk";
1006
1016
  import { isCancel, log, note, select } from "@clack/prompts";
1007
- function isSafePath(p) {
1008
- return /^\/[A-Za-z0-9._/-]+$/.test(p);
1009
- }
1010
- function readCertFile(p) {
1011
- try {
1012
- return readFileSync4(p, "utf8");
1013
- } catch {
1014
- return null;
1015
- }
1016
- }
1017
1017
  async function planAndApplyHostEdge(opts, overrides = {}) {
1018
1018
  const deps = { ...defaultDeps(), ...overrides };
1019
1019
  if (deps.platform !== "linux") return { proceed: true };
@@ -1031,7 +1031,7 @@ async function planAndApplyHostEdge(opts, overrides = {}) {
1031
1031
  const { sites, warnings } = await deps.importSites(executor, status);
1032
1032
  const action2 = await resolveAction(opts.edge, { status, owner, sites, warnings }, deps);
1033
1033
  if (action2 === "cancel") return { proceed: false };
1034
- const certPems = action2 === "migrate" ? collectCertPems(sites, deps.readCert) : void 0;
1034
+ const certPems = action2 === "migrate" ? await deps.collectCerts(executor, sites, { status }) : void 0;
1035
1035
  await deps.beginEdgeTakeover(
1036
1036
  executor,
1037
1037
  status,
@@ -1108,7 +1108,12 @@ async function offerStoppedProxyImport(executor, deps) {
1108
1108
  if (!await deps.confirmStoppedImport({ proxy, count: sites.length })) {
1109
1109
  return { proceed: true };
1110
1110
  }
1111
- return { proceed: true, action: "migrate", sites, certPems: collectCertPems(sites, readCertFile) };
1111
+ return {
1112
+ proceed: true,
1113
+ action: "migrate",
1114
+ sites,
1115
+ certPems: await deps.collectCerts(executor, sites, { proxy })
1116
+ };
1112
1117
  }
1113
1118
  async function completeHostEdge() {
1114
1119
  if (process.platform !== "linux") return;
@@ -1132,17 +1137,57 @@ async function resolveAction(flag, ctx, deps) {
1132
1137
  importable: ctx.sites.length
1133
1138
  });
1134
1139
  }
1135
- function collectCertPems(sites, readCert) {
1136
- const out = {};
1137
- for (const s of sites) {
1138
- if (!s.ssl || !s.tls) continue;
1139
- const { certPath, keyPath } = s.tls;
1140
- if (!isSafePath(certPath) || !isSafePath(keyPath)) continue;
1141
- const certPem = readCert(certPath);
1142
- const keyPem = readCert(keyPath);
1143
- if (certPem && keyPem) out[certPath] = { certPem, keyPem };
1140
+ async function collectCertsFromProxy(executor, sites, source) {
1141
+ const api = source.proxy ? edgeProxyFor(executor, source.proxy) : await edgeProxy(executor, source.status ? { status: source.status } : {});
1142
+ if (!api) return {};
1143
+ const { certPems, warnings } = await collectProxyCerts(api, sites);
1144
+ for (const w of warnings) console.log(chalk5.yellow(` ${w}`));
1145
+ return certPems;
1146
+ }
1147
+ function renderEdgeConflict(info3) {
1148
+ const { owner, sites, warnings } = info3;
1149
+ if (sites.length > 0) {
1150
+ const lines = sites.map((st) => {
1151
+ const host = (st.serverNames ?? []).join(", ") || "(no server_name)";
1152
+ const dest = st.target.kind === "static" ? `static: ${st.target.root}` : st.target.url;
1153
+ return `${chalk5.bold(host)} \u2192 ${chalk5.dim(dest)}${st.ssl ? chalk5.green(" [TLS]") : ""}`;
1154
+ });
1155
+ note(lines.join("\n"), `Detected ${sites.length} site${sites.length === 1 ? "" : "s"} on ${owner}`);
1156
+ }
1157
+ if (warnings.length > 0) {
1158
+ log.warn(`${warnings.length} config item${warnings.length === 1 ? "" : "s"} won't migrate automatically:`);
1159
+ for (const w of warnings.slice(0, 8)) log.message(chalk5.dim(`\u2022 ${w}`));
1144
1160
  }
1145
- return out;
1161
+ }
1162
+ async function confirmEdgeAction(info3) {
1163
+ const { owner, known, importable } = info3;
1164
+ const choice = await select({
1165
+ message: known ? `An existing reverse proxy (${owner}) is serving ports 80/443.` : `Ports 80/443 are in use by ${owner}, which we couldn't identify.`,
1166
+ options: [
1167
+ ...importable > 0 ? [
1168
+ {
1169
+ value: "migrate",
1170
+ label: `Migrate ${importable} site${importable === 1 ? "" : "s"} & take over`,
1171
+ hint: "import the existing sites into Openship's edge, then take 80/443"
1172
+ }
1173
+ ] : [],
1174
+ {
1175
+ value: "takeover",
1176
+ label: "Stop it & take over 80/443",
1177
+ hint: known ? "the existing sites stop being served" : "may interrupt a running service"
1178
+ },
1179
+ { value: "cancel", label: "Cancel \u2014 leave it running" }
1180
+ ],
1181
+ // Default to MIGRATE whenever there are sites we can import: it's the only
1182
+ // option that both gets Openship onto 80/443 and keeps the operator's
1183
+ // existing sites served, so it's the choice they almost always want and
1184
+ // the one that loses nothing on a reflexive Enter.
1185
+ // With nothing importable there's no safe default: a known proxy falls
1186
+ // back to cancel (blind Enter would stop sites that are being served),
1187
+ // and an unidentified port holder to takeover.
1188
+ initialValue: importable > 0 ? "migrate" : known ? "cancel" : "takeover"
1189
+ });
1190
+ return isCancel(choice) ? "cancel" : choice;
1146
1191
  }
1147
1192
  function defaultDeps() {
1148
1193
  return {
@@ -1172,61 +1217,20 @@ function defaultDeps() {
1172
1217
  });
1173
1218
  return !isCancel(take) && take === "import";
1174
1219
  },
1175
- readCert: readCertFile,
1176
- render: ({ owner, sites, warnings }) => {
1177
- if (sites.length > 0) {
1178
- const lines = sites.map((st) => {
1179
- const host = (st.serverNames ?? []).join(", ") || "(no server_name)";
1180
- const dest = st.target.kind === "static" ? `static: ${st.target.root}` : st.target.url;
1181
- return `${chalk5.bold(host)} \u2192 ${chalk5.dim(dest)}${st.ssl ? chalk5.green(" [TLS]") : ""}`;
1182
- });
1183
- note(lines.join("\n"), `Detected ${sites.length} site${sites.length === 1 ? "" : "s"} on ${owner}`);
1184
- }
1185
- if (warnings.length > 0) {
1186
- log.warn(`${warnings.length} config item${warnings.length === 1 ? "" : "s"} won't migrate automatically:`);
1187
- for (const w of warnings.slice(0, 8)) log.message(chalk5.dim(`\u2022 ${w}`));
1188
- }
1189
- },
1190
- confirm: async ({ owner, known, importable }) => {
1191
- const choice = await select({
1192
- message: known ? `An existing reverse proxy (${owner}) is serving ports 80/443.` : `Ports 80/443 are in use by ${owner}, which we couldn't identify.`,
1193
- options: [
1194
- ...importable > 0 ? [
1195
- {
1196
- value: "migrate",
1197
- label: `Migrate ${importable} site${importable === 1 ? "" : "s"} & take over`,
1198
- hint: "import the existing sites into Openship's edge, then take 80/443"
1199
- }
1200
- ] : [],
1201
- {
1202
- value: "takeover",
1203
- label: "Stop it & take over 80/443",
1204
- hint: known ? "the existing sites stop being served" : "may interrupt a running service"
1205
- },
1206
- { value: "cancel", label: "Cancel \u2014 leave it running" }
1207
- ],
1208
- // Default to MIGRATE whenever there are sites we can import: it's the only
1209
- // option that both gets Openship onto 80/443 and keeps the operator's
1210
- // existing sites served, so it's the choice they almost always want and
1211
- // the one that loses nothing on a reflexive Enter.
1212
- // With nothing importable there's no safe default: a known proxy falls
1213
- // back to cancel (blind Enter would stop sites that are being served),
1214
- // and an unidentified port holder to takeover.
1215
- initialValue: importable > 0 ? "migrate" : known ? "cancel" : "takeover"
1216
- });
1217
- return isCancel(choice) ? "cancel" : choice;
1218
- },
1220
+ collectCerts: collectCertsFromProxy,
1221
+ render: renderEdgeConflict,
1222
+ confirm: confirmEdgeAction,
1219
1223
  warn: (message) => console.log(chalk5.yellow(` ${message}`))
1220
1224
  };
1221
1225
  }
1222
1226
 
1223
1227
  // src/lib/loopback-api.ts
1224
- import { existsSync as existsSync6, readFileSync as readFileSync5, mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "fs";
1228
+ import { existsSync as existsSync6, readFileSync as readFileSync4, mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "fs";
1225
1229
  import { randomBytes } from "crypto";
1226
1230
  import { join as join7 } from "path";
1227
1231
  function ensureInternalToken() {
1228
1232
  const path2 = join7(OS_DIR, "internal-token");
1229
- if (existsSync6(path2)) return readFileSync5(path2, "utf8").trim();
1233
+ if (existsSync6(path2)) return readFileSync4(path2, "utf8").trim();
1230
1234
  mkdirSync7(OS_DIR, { recursive: true, mode: 448 });
1231
1235
  const token = randomBytes(32).toString("hex");
1232
1236
  writeFileSync6(path2, token, { mode: 384 });
@@ -1486,26 +1490,31 @@ async function headlessProvision(opts) {
1486
1490
  warnings.push(`Custom domain provisioning returned: ${res.data?.error || "failed"}`);
1487
1491
  }
1488
1492
  } else {
1493
+ const freeBody = (s) => ({
1494
+ domainType: "free",
1495
+ slug: s,
1496
+ publicHost: d.publicHost,
1497
+ dashPort,
1498
+ localEdge: d.localEdge,
1499
+ edgeTakeover: d.edgeTakeover,
1500
+ edgeMigrate: d.edgeMigrate
1501
+ });
1489
1502
  let slug = d.slug;
1490
- let res = await internalPost(
1491
- port,
1492
- "/api/system/self-register",
1493
- { domainType: "free", slug, publicHost: d.publicHost, dashPort },
1494
- token
1495
- );
1503
+ let res = await internalPost(port, "/api/system/self-register", freeBody(slug), token);
1496
1504
  for (let attempt = 0; !res.ok && isSlugTaken(res.data) && opts.onSlugTaken && attempt < 5; attempt++) {
1497
1505
  const next = await opts.onSlugTaken(slug);
1498
1506
  if (!next) break;
1499
1507
  slug = next;
1500
- res = await internalPost(
1501
- port,
1502
- "/api/system/self-register",
1503
- { domainType: "free", slug, publicHost: d.publicHost, dashPort },
1504
- token
1505
- );
1508
+ res = await internalPost(port, "/api/system/self-register", freeBody(slug), token);
1506
1509
  }
1507
1510
  domainRegistered = res.ok;
1508
1511
  liveUrl = res.data?.url;
1512
+ if (res.ok && res.data?.sessionId) {
1513
+ const edge = await drainProvisionStream(port, String(res.data.sessionId), token, (m) => log6(` ${m}`));
1514
+ if (!edge.completed && edge.detail) {
1515
+ warnings.push(`Local edge not fully ready: ${edge.detail} \u2014 it retries on next boot.`);
1516
+ }
1517
+ }
1509
1518
  if (!res.ok) {
1510
1519
  warnings.push(
1511
1520
  isSlugTaken(res.data) ? `Free domain not created: "${slug}.opsh.io" is already taken. Re-run and choose a different subdomain, or add one later in Settings \u2192 Domains.` : `Free .opsh.io domain not registered: ${res.data?.error || "failed"}. A free domain needs the box connected to Openship Cloud \u2014 connect it in Settings \u2192 Cloud, or use --domain-kind byo/custom.`
@@ -1526,6 +1535,12 @@ function normalizeUrl(raw) {
1526
1535
  return null;
1527
1536
  }
1528
1537
  }
1538
+ function effectivePublicUrl(flag) {
1539
+ if (flag) return normalizePublicUrl(flag);
1540
+ const saved = readInstanceUrl();
1541
+ if (!saved || /^https?:\/\/(localhost|127\.|\[?::1\]?)([:/]|$)/i.test(saved)) return void 0;
1542
+ return normalizePublicUrl(saved);
1543
+ }
1529
1544
  function normalizePublicUrl(raw) {
1530
1545
  const url = normalizeUrl(raw);
1531
1546
  if (!url) {
@@ -1541,7 +1556,7 @@ var DIST_DIR = dirname2(fileURLToPath(import.meta.url));
1541
1556
  var SERVER_DIR = join8(DIST_DIR, "server");
1542
1557
  function ensureAuthSecret() {
1543
1558
  const path2 = join8(OS_DIR, "auth-secret");
1544
- if (existsSync7(path2)) return readFileSync6(path2, "utf8").trim();
1559
+ if (existsSync7(path2)) return readFileSync5(path2, "utf8").trim();
1545
1560
  mkdirSync8(OS_DIR, { recursive: true, mode: 448 });
1546
1561
  const secret = randomBytes2(32).toString("hex");
1547
1562
  writeFileSync7(path2, secret, { mode: 384 });
@@ -1683,7 +1698,7 @@ async function runCompose(opts) {
1683
1698
  );
1684
1699
  process.exit(1);
1685
1700
  }
1686
- const publicUrl = opts.publicUrl ? normalizePublicUrl(opts.publicUrl) : void 0;
1701
+ const publicUrl = effectivePublicUrl(opts.publicUrl);
1687
1702
  const fromSource = sourceBuildDir();
1688
1703
  const spinner5 = ora(
1689
1704
  fromSource ? `Building Openship from ${fromSource} and starting the stack\u2026` : "Starting Openship via Docker Compose\u2026"
@@ -1693,8 +1708,10 @@ async function runCompose(opts) {
1693
1708
  dashboardPort: opts.dashboardPort,
1694
1709
  publicUrl,
1695
1710
  trustProxy: opts.trustProxy,
1696
- // commander maps `--no-host-control` to hostControl === false.
1697
- noHostControl: opts.hostControl === false
1711
+ // commander maps `--no-host-control` to hostControl === false. `undefined` when
1712
+ // the flag is absent, so a plain re-run keeps the install's original choice
1713
+ // instead of silently re-granting host control (see resolveEnvConfig).
1714
+ noHostControl: opts.hostControl === false ? true : void 0
1698
1715
  });
1699
1716
  if (!res.ok) {
1700
1717
  spinner5.fail("docker compose failed to start the stack");
@@ -1742,7 +1759,7 @@ async function runFromSource(opts) {
1742
1759
  await runForeground(opts, src);
1743
1760
  }
1744
1761
  async function startService(opts, runOpts = {}) {
1745
- const publicUrl = opts.publicUrl ? normalizePublicUrl(opts.publicUrl) : void 0;
1762
+ const publicUrl = effectivePublicUrl(opts.publicUrl);
1746
1763
  if (opts.dryRun) {
1747
1764
  const p = preview({
1748
1765
  port: opts.port,
@@ -1841,7 +1858,7 @@ async function runForeground(opts, source) {
1841
1858
  });
1842
1859
  const port = String(resolved.api);
1843
1860
  const dashPort = String(resolved.dashboard);
1844
- const publicUrl = opts.publicUrl ? normalizePublicUrl(opts.publicUrl) : void 0;
1861
+ const publicUrl = effectivePublicUrl(opts.publicUrl);
1845
1862
  const managedEdge = Boolean(opts.managedEdge && publicUrl);
1846
1863
  const dataDir = opts.dataDir || join8(OS_DIR, "data");
1847
1864
  mkdirSync8(dataDir, { recursive: true });
@@ -1863,9 +1880,12 @@ async function runForeground(opts, source) {
1863
1880
  if (!source) {
1864
1881
  env.OPENSHIP_MIGRATIONS_DIR = join8(SERVER_DIR, "migrations");
1865
1882
  env.OPENSHIP_PGLITE_ASSETS_DIR = join8(SERVER_DIR, "pglite");
1883
+ const engineDir = join8(SERVER_DIR, "engine");
1884
+ if (existsSync7(engineDir)) env.MAIL_SERVER_ENGINE_DIR = engineDir;
1866
1885
  }
1867
1886
  env.OPENSHIP_REQUIRE_AUTH = "true";
1868
1887
  env.INTERNAL_TOKEN = ensureInternalToken();
1888
+ if (!env.OPENSHIP_AUTH_MODE) env.OPENSHIP_AUTH_MODE = "local";
1869
1889
  env.OPENSHIP_API_HOST = "127.0.0.1";
1870
1890
  env.OPENSHIP_DASHBOARD_PORT = dashPort;
1871
1891
  env.OPENSHIP_INSTANCE_LOG = instanceLogPath;
@@ -1953,7 +1973,7 @@ async function runForeground(opts, source) {
1953
1973
  const uiSpinner = ora("Preparing the dashboard\u2026").start();
1954
1974
  try {
1955
1975
  const bundle = await ensureDashboard({
1956
- tag: source ? "local" : opts.uiVersion || `v${"0.4.5"}`,
1976
+ tag: source ? "local" : opts.uiVersion || `v${"0.4.6"}`,
1957
1977
  onProgress: (received, total) => {
1958
1978
  if (total) {
1959
1979
  uiSpinner.text = `Downloading dashboard\u2026 ${Math.round(received / total * 100)}%`;
@@ -2222,7 +2242,7 @@ var initCommand = new Command7("init").description("Link the current directory t
2222
2242
 
2223
2243
  // src/commands/config.ts
2224
2244
  import { Command as Command8 } from "commander";
2225
- import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync9 } from "fs";
2245
+ import { existsSync as existsSync10, readFileSync as readFileSync6, writeFileSync as writeFileSync9 } from "fs";
2226
2246
  import { join as join10 } from "path";
2227
2247
  var SCHEMA_URL = "https://openship.io/openship.schema.json";
2228
2248
  var CONFIG_FILE = "openship.json";
@@ -2243,7 +2263,7 @@ function detectHints(dir) {
2243
2263
  const pkgPath = join10(dir, "package.json");
2244
2264
  if (existsSync10(pkgPath)) {
2245
2265
  try {
2246
- const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
2266
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
2247
2267
  const runner = hints.packageManager ?? "npm";
2248
2268
  const exec = runner === "npm" ? "npm run" : runner;
2249
2269
  if (pkg.scripts?.build) hints.buildCommand = `${exec} build`;
@@ -2279,7 +2299,7 @@ var validateCmd = new Command8("validate").description("Validate an openship.jso
2279
2299
  else err(`Not found: ${path2}`);
2280
2300
  process.exit(1);
2281
2301
  }
2282
- const { config, errors, warnings } = parseOpenshipConfigJson(readFileSync7(path2, "utf8"));
2302
+ const { config, errors, warnings } = parseOpenshipConfigJson(readFileSync6(path2, "utf8"));
2283
2303
  const valid = errors.length === 0 && config !== null;
2284
2304
  if (isJsonMode()) {
2285
2305
  printJson({ valid, errors, warnings });
@@ -2352,11 +2372,11 @@ var contextCommand = new Command9("context").alias("ctx").description("Manage co
2352
2372
  // src/commands/status.ts
2353
2373
  import { Command as Command10 } from "commander";
2354
2374
  import chalk9 from "chalk";
2355
- import { readFileSync as readFileSync8 } from "fs";
2375
+ import { readFileSync as readFileSync7 } from "fs";
2356
2376
  import { join as join11 } from "path";
2357
2377
  function readPorts() {
2358
2378
  try {
2359
- return JSON.parse(readFileSync8(join11(OS_DIR, "ports.json"), "utf8"));
2379
+ return JSON.parse(readFileSync7(join11(OS_DIR, "ports.json"), "utf8"));
2360
2380
  } catch {
2361
2381
  return {};
2362
2382
  }
@@ -2420,7 +2440,7 @@ import { intro, outro, note as note2, select as select3, log as log3, spinner as
2420
2440
 
2421
2441
  // src/lib/repair.ts
2422
2442
  import { spawnSync as spawnSync4 } from "child_process";
2423
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
2443
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
2424
2444
  import { join as join13 } from "path";
2425
2445
  import chalk10 from "chalk";
2426
2446
  import { spinner, log as log2, select as select2, isCancel as isCancel3, cancel } from "@clack/prompts";
@@ -2534,7 +2554,7 @@ function ensure(value) {
2534
2554
  function storedPorts() {
2535
2555
  try {
2536
2556
  const p = join13(OS_DIR, "ports.json");
2537
- return existsSync12(p) ? JSON.parse(readFileSync9(p, "utf8")) : {};
2557
+ return existsSync12(p) ? JSON.parse(readFileSync8(p, "utf8")) : {};
2538
2558
  } catch {
2539
2559
  return {};
2540
2560
  }
@@ -2586,7 +2606,7 @@ function lastServiceError() {
2586
2606
  const p = join13(LOG_DIR2, name);
2587
2607
  if (!existsSync12(p)) continue;
2588
2608
  try {
2589
- const lines = readFileSync9(p, "utf8").trim().split("\n");
2609
+ const lines = readFileSync8(p, "utf8").trim().split("\n");
2590
2610
  const hit = [...lines].reverse().find((l) => /error|locked|aborted|malformed|corrupt|throw|cannot|EADDRINUSE/i.test(l));
2591
2611
  if (hit) return hit.trim().slice(0, 240);
2592
2612
  } catch {
@@ -3013,7 +3033,7 @@ import { execFileSync as execFileSync4 } from "child_process";
3013
3033
  import ora2 from "ora";
3014
3034
 
3015
3035
  // src/lib/project-link.ts
3016
- import { readFileSync as readFileSync10, existsSync as existsSync14 } from "fs";
3036
+ import { readFileSync as readFileSync9, existsSync as existsSync14 } from "fs";
3017
3037
  import { join as join14, dirname as dirname3, parse } from "path";
3018
3038
  var LINK_REL = join14(".openship", "project.json");
3019
3039
  function findProjectLinkPath(from = process.cwd()) {
@@ -3030,7 +3050,7 @@ function readProjectLink(from) {
3030
3050
  const path2 = findProjectLinkPath(from);
3031
3051
  if (!path2) return null;
3032
3052
  try {
3033
- return JSON.parse(readFileSync10(path2, "utf8"));
3053
+ return JSON.parse(readFileSync9(path2, "utf8"));
3034
3054
  } catch {
3035
3055
  return null;
3036
3056
  }
@@ -3038,7 +3058,7 @@ function readProjectLink(from) {
3038
3058
 
3039
3059
  // src/lib/folder-deploy.ts
3040
3060
  import { execFileSync as execFileSync3 } from "child_process";
3041
- import { readFileSync as readFileSync11, existsSync as existsSync15, rmSync as rmSync4 } from "fs";
3061
+ import { readFileSync as readFileSync10, existsSync as existsSync15, rmSync as rmSync4 } from "fs";
3042
3062
  import { tmpdir } from "os";
3043
3063
  import { join as join15, basename } from "path";
3044
3064
  function detectPackageManager(dir) {
@@ -3088,7 +3108,7 @@ async function deployFolder(opts) {
3088
3108
  );
3089
3109
  step("Uploading source");
3090
3110
  try {
3091
- const body = readFileSync11(tarball);
3111
+ const body = readFileSync10(tarball);
3092
3112
  const up = session.upload;
3093
3113
  const method = up.method || "POST";
3094
3114
  const res = /^https?:\/\//i.test(up.url) ? await fetch(up.url, { method, headers: up.headers, body }) : await apiRaw(`/${up.url.replace(/^\/+/, "")}`, { method, headers: up.headers, body });
@@ -4800,7 +4820,7 @@ var domainCommand = new Command17("domain").description("Manage custom domains,
4800
4820
  import { Command as Command18 } from "commander";
4801
4821
  import chalk15 from "chalk";
4802
4822
  import ora4 from "ora";
4803
- var INSTALLABLE = ["docker", "git", "openresty", "certbot", "rsync"];
4823
+ var INSTALLABLE = ["docker", "git", "edge", "rsync"];
4804
4824
  function guard(fn) {
4805
4825
  return async (...args) => {
4806
4826
  if (!getToken()) {
@@ -5094,7 +5114,7 @@ var serverCommand = server;
5094
5114
  // src/commands/system.ts
5095
5115
  import { Command as Command19 } from "commander";
5096
5116
  import ora5 from "ora";
5097
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
5117
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "fs";
5098
5118
  import { createInterface as createInterface6 } from "readline/promises";
5099
5119
  import { stdin as input5, stdout as output5 } from "process";
5100
5120
  async function guarded(fn) {
@@ -5420,7 +5440,7 @@ dataTransferCommand.command("import").description("Import an instance export fil
5420
5440
  }
5421
5441
  let file;
5422
5442
  try {
5423
- file = JSON.parse(readFileSync12(opts.file, "utf8"));
5443
+ file = JSON.parse(readFileSync11(opts.file, "utf8"));
5424
5444
  } catch {
5425
5445
  err(`
5426
5446
  Could not read or parse ${opts.file}.
@@ -5771,7 +5791,7 @@ var mailCommand = new Command20("mail").description("Self-hosted mail server (iR
5771
5791
  // src/commands/backup.ts
5772
5792
  import { Command as Command21 } from "commander";
5773
5793
  import ora7 from "ora";
5774
- import { readFileSync as readFileSync13 } from "fs";
5794
+ import { readFileSync as readFileSync12 } from "fs";
5775
5795
  async function guard3(fn) {
5776
5796
  try {
5777
5797
  await fn();
@@ -6064,7 +6084,7 @@ destinationCmd.command("create").description("Create a backup destination").requ
6064
6084
  let sftpPrivateKey = opts.sftpPrivateKey;
6065
6085
  if (opts.sftpPrivateKeyFile) {
6066
6086
  try {
6067
- sftpPrivateKey = readFileSync13(opts.sftpPrivateKeyFile, "utf8");
6087
+ sftpPrivateKey = readFileSync12(opts.sftpPrivateKeyFile, "utf8");
6068
6088
  } catch {
6069
6089
  throw new Error(`Cannot read key file: ${opts.sftpPrivateKeyFile}`);
6070
6090
  }
@@ -6256,11 +6276,11 @@ var apiCommand = new Command23("api").description("Make an authenticated request
6256
6276
  import { Command as Command24 } from "commander";
6257
6277
  import chalk19 from "chalk";
6258
6278
  import { intro as intro2, outro as outro2, password as passwordPrompt, isCancel as isCancel4, cancel as cancel3, log as log4 } from "@clack/prompts";
6259
- import { readFileSync as readFileSync14 } from "fs";
6279
+ import { readFileSync as readFileSync13 } from "fs";
6260
6280
  import { join as join16 } from "path";
6261
6281
  function resolvedApiPort() {
6262
6282
  try {
6263
- return JSON.parse(readFileSync14(join16(OS_DIR, "ports.json"), "utf8")).api;
6283
+ return JSON.parse(readFileSync13(join16(OS_DIR, "ports.json"), "utf8")).api;
6264
6284
  } catch {
6265
6285
  return void 0;
6266
6286
  }
@@ -6314,7 +6334,7 @@ var resetAdminCommand = new Command24("reset-admin-password").description("Reset
6314
6334
 
6315
6335
  // src/commands/install.ts
6316
6336
  import { Command as Command25 } from "commander";
6317
- import { chmodSync as chmodSync2, existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "fs";
6337
+ import { chmodSync as chmodSync2, existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
6318
6338
  import { spawn as spawn2, spawnSync as spawnSync6 } from "child_process";
6319
6339
  import { homedir as homedir3 } from "os";
6320
6340
  import { join as join17 } from "path";
@@ -6451,7 +6471,7 @@ var installCommand = new Command25("install").description("Download and install
6451
6471
  try {
6452
6472
  let sidecarBody;
6453
6473
  if (existsSync16(sidecarPath) && !downloaded) {
6454
- sidecarBody = readFileSync15(sidecarPath, "utf8");
6474
+ sidecarBody = readFileSync14(sidecarPath, "utf8");
6455
6475
  } else {
6456
6476
  sidecarBody = await fetchSidecar(sidecarUrl);
6457
6477
  }
@@ -6562,7 +6582,7 @@ function detectPackageManager2(override) {
6562
6582
  var updateCommand = new Command26("update").description("Update the Openship CLI + bundled server to the latest release").option("--check", "Only report the current + latest version; don't install").option("--via <manager>", "Package manager to update with: bun | npm").option("--rebuild", "From-source installs: rebuild even if already at the remote tip").action(async (opts) => {
6563
6583
  const source = readSourceInstall();
6564
6584
  if (source) return runSourceUpdate(source, opts);
6565
- const current = "0.4.5";
6585
+ const current = "0.4.6";
6566
6586
  let latest;
6567
6587
  try {
6568
6588
  latest = (await resolveLatestTag()).replace(/^v/, "");
@@ -6597,13 +6617,13 @@ var updateCommand = new Command26("update").description("Update the Openship CLI
6597
6617
  return;
6598
6618
  }
6599
6619
  if (readInstallMethod() === "compose") {
6600
- const pulled = composeUpdate(latest);
6620
+ const applied = composeUpdate(latest);
6601
6621
  if (isJsonMode()) {
6602
- printJson({ updated: true, from: current, to: latest, via: pm, method: "compose", pulled });
6603
- } else if (pulled) {
6604
- ok(`Updated to v${latest} and pulled the new images \u2014 the compose stack is on the new version.`);
6622
+ printJson({ updated: true, from: current, to: latest, via: pm, method: "compose", applied });
6623
+ } else if (applied) {
6624
+ ok(`Updated to v${latest} \u2014 compose stack regenerated, images pulled, services recreated.`);
6605
6625
  } else {
6606
- err(`Updated the CLI to v${latest}, but \`docker compose pull\` failed. Run \`openship up\` to retry.`);
6626
+ err(`Updated the CLI to v${latest}, but bringing the compose stack up failed. Re-run \`openship update\`, or \`openship up\` to retry.`);
6607
6627
  process.exitCode = 1;
6608
6628
  }
6609
6629
  return;
@@ -6620,7 +6640,7 @@ var updateCommand = new Command26("update").description("Update the Openship CLI
6620
6640
 
6621
6641
  // src/commands/cache.ts
6622
6642
  import { Command as Command27 } from "commander";
6623
- import { existsSync as existsSync17, readdirSync as readdirSync2, readFileSync as readFileSync16, rmSync as rmSync5, statSync as statSync2 } from "fs";
6643
+ import { existsSync as existsSync17, readdirSync as readdirSync2, readFileSync as readFileSync15, rmSync as rmSync5, statSync as statSync2 } from "fs";
6624
6644
  import { join as join18 } from "path";
6625
6645
  function listAssets() {
6626
6646
  if (!existsSync17(RELEASES_DIR)) return [];
@@ -6669,7 +6689,7 @@ var verifyCmd2 = new Command27("verify").description("Re-hash cached assets and
6669
6689
  results.push({ tag: a.tag, asset: a.name, result: "no-sidecar" });
6670
6690
  continue;
6671
6691
  }
6672
- const expected = parseSha256(readFileSync16(`${a.path}.sha256`, "utf8"));
6692
+ const expected = parseSha256(readFileSync15(`${a.path}.sha256`, "utf8"));
6673
6693
  const actual = await hashFile(a.path);
6674
6694
  const okMatch = expected !== null && expected === actual;
6675
6695
  if (!okMatch) bad += 1;
@@ -6705,7 +6725,7 @@ var cacheCommand = new Command27("cache").description("Manage the local download
6705
6725
  import chalk20 from "chalk";
6706
6726
  import open2 from "open";
6707
6727
  import { createHash as createHash2, randomBytes as randomBytes3 } from "crypto";
6708
- import { existsSync as existsSync18, mkdirSync as mkdirSync12, readFileSync as readFileSync17, rmSync as rmSync6, writeFileSync as writeFileSync12 } from "fs";
6728
+ import { existsSync as existsSync18, mkdirSync as mkdirSync12, readFileSync as readFileSync16, rmSync as rmSync6, writeFileSync as writeFileSync12 } from "fs";
6709
6729
  import { join as join19 } from "path";
6710
6730
  import {
6711
6731
  intro as intro3,
@@ -6893,6 +6913,21 @@ ${pad("Login")}${opts.adminEmail} ${chalk20.dim("(email + password you set)")}
6893
6913
  );
6894
6914
  outro3(opts.byo ? chalk20.dim("Point your reverse proxy at the dashboard port above.") : chalk20.green("Happy shipping."));
6895
6915
  }
6916
+ async function promptLocalEdgeTakeover(port) {
6917
+ const pf = await internalPost(port, "/api/system/self-edge/preflight", {});
6918
+ const status = pf.ok ? pf.data?.status : void 0;
6919
+ if (!status || status.canProceedClean || !status.occupants?.length) {
6920
+ return { proceed: true, edgeMigrate: false, edgeTakeover: false };
6921
+ }
6922
+ const owner = status.occupants.map((o) => o.command ?? `port ${o.port}`).join(", ");
6923
+ const known = status.classification === "known";
6924
+ const sites = pf.ok && Array.isArray(pf.data?.sites) ? pf.data.sites : [];
6925
+ const warnings = pf.ok && Array.isArray(pf.data?.warnings) ? pf.data.warnings : [];
6926
+ renderEdgeConflict({ owner, sites, warnings });
6927
+ const action2 = await confirmEdgeAction({ owner, known, importable: sites.length });
6928
+ if (action2 === "cancel") return { proceed: false, edgeMigrate: false, edgeTakeover: false };
6929
+ return { proceed: true, edgeMigrate: action2 === "migrate", edgeTakeover: action2 === "takeover" };
6930
+ }
6896
6931
  async function runWizard() {
6897
6932
  const resuming = isSetupInProgress();
6898
6933
  intro3(`${chalk20.bgCyan(chalk20.black(" Openship "))}${chalk20.dim(" setup")}`);
@@ -7114,7 +7149,7 @@ async function runWizard() {
7114
7149
  }
7115
7150
  }
7116
7151
  }
7117
- const uiTag = `v${"0.4.5"}`;
7152
+ const uiTag = `v${"0.4.6"}`;
7118
7153
  if (method === "bare") {
7119
7154
  const dl = spinner4();
7120
7155
  dl.start("Pulling the Openship dist from GitHub");
@@ -7182,7 +7217,7 @@ async function runWizard() {
7182
7217
  const up = composeUp({
7183
7218
  publicUrl,
7184
7219
  trustProxy: behindProxy,
7185
- version: "0.4.5",
7220
+ version: "0.4.6",
7186
7221
  noHostControl: !allowHostControl
7187
7222
  });
7188
7223
  if (!up.ok) {
@@ -7319,93 +7354,49 @@ async function runWizard() {
7319
7354
  await internalPost(port, "/api/system/self-register", { domainType: "byo" });
7320
7355
  } else {
7321
7356
  cloudEmail = cloud.email;
7322
- let regSlug = domainPlan.slug;
7323
- while (true) {
7324
- const s2 = spinner4();
7325
- s2.start(`Registering ${chalk20.bold(`${regSlug}.opsh.io`)} with Openship Cloud`);
7326
- const res = await internalPost(port, "/api/system/self-register", {
7327
- domainType: "free",
7328
- slug: regSlug,
7329
- publicHost: domainPlan.publicHost,
7330
- dashPort: Number(started.dashPort)
7331
- });
7332
- if (res.ok && res.data?.url) {
7333
- liveUrl = res.data.url;
7334
- s2.stop(`Free domain live: ${res.data.url}`);
7335
- break;
7336
- }
7337
- s2.stop(`Couldn't register ${regSlug}.opsh.io: ${res.data?.error || "failed"}`, 1);
7338
- const next = ensure2(
7339
- await select4({
7340
- message: "Try a different subdomain?",
7341
- options: [
7342
- { value: "retry", label: "Pick another subdomain" },
7343
- { value: "skip", label: "Skip for now", hint: "log in on this server; add a domain later in Settings \u2192 Cloud" }
7344
- ]
7345
- })
7357
+ const edge = await promptLocalEdgeTakeover(port);
7358
+ if (!edge.proceed) {
7359
+ log5.warn(
7360
+ "Left the existing proxy on 80/443 running \u2014 a free domain forwards to :80 here, so it won't serve until Openship owns that port. Re-run setup and choose migrate / take over."
7346
7361
  );
7347
- if (next === "skip") break;
7348
- regSlug = ensure2(
7349
- await text({
7350
- message: "Choose your subdomain",
7351
- placeholder: "my-openship",
7352
- initialValue: regSlug,
7353
- validate: (v) => v && SLUG_RE2.test(v.trim().toLowerCase()) ? void 0 : "Lowercase letters, digits, hyphens"
7354
- })
7355
- ).trim().toLowerCase();
7356
7362
  }
7363
+ const result = await headlessProvision({
7364
+ port,
7365
+ dashPort: started.dashPort,
7366
+ token: provisionToken,
7367
+ method: "bare",
7368
+ inputs: {
7369
+ admin,
7370
+ domain: {
7371
+ kind: "free",
7372
+ slug: domainPlan.slug,
7373
+ publicHost: domainPlan.publicHost,
7374
+ // Bare needs a local :80 edge for Cloud to forward to; skip only when
7375
+ // the operator declined the takeover (their proxy keeps 80/443).
7376
+ localEdge: edge.proceed,
7377
+ edgeTakeover: edge.edgeTakeover,
7378
+ edgeMigrate: edge.edgeMigrate
7379
+ }
7380
+ },
7381
+ onLog: (msg) => log5.message(chalk20.dim(msg)),
7382
+ onSlugTaken: async (taken) => {
7383
+ log5.warn(`"${taken}.opsh.io" is already taken.`);
7384
+ const next = await text({
7385
+ message: "Choose a different subdomain (or leave empty to skip the free domain)",
7386
+ placeholder: "my-openship",
7387
+ validate: (v) => !v || SLUG_RE2.test(v.trim().toLowerCase()) ? void 0 : "Lowercase letters, digits, hyphens"
7388
+ });
7389
+ return isCancel5(next) || !next.trim() ? null : next.trim().toLowerCase();
7390
+ }
7391
+ });
7392
+ if (result.liveUrl) liveUrl = result.liveUrl;
7393
+ for (const w of result.warnings) log5.warn(w);
7357
7394
  }
7358
7395
  } else if (domainPlan.type === "custom") {
7359
- let edgeTakeover = false;
7360
- let edgeMigrate = false;
7361
- let proceedCustom = true;
7362
- const pf = await internalPost(port, "/api/system/self-edge/preflight", {});
7363
- const status = pf.ok ? pf.data?.status : void 0;
7364
- const importable = pf.ok && Array.isArray(pf.data?.sites) ? pf.data.sites.length : 0;
7365
- if (status && !status.canProceedClean && status.occupants?.length) {
7366
- const owner = status.occupants.map((o) => o.command ?? `port ${o.port}`).join(", ");
7367
- const known = status.classification === "known";
7368
- const sites = pf.ok && Array.isArray(pf.data?.sites) ? pf.data.sites : [];
7369
- if (sites.length > 0) {
7370
- const lines = sites.map((st) => {
7371
- const host = (st.serverNames ?? []).join(", ") || "(no server_name)";
7372
- const dest = st.target?.kind === "static" ? `static: ${st.target?.root ?? ""}` : st.target?.url ?? "";
7373
- return `${chalk20.bold(host)} \u2192 ${chalk20.dim(dest)}${st.ssl ? chalk20.green(" [TLS]") : ""}`;
7374
- });
7375
- note3(lines.join("\n"), `Detected ${sites.length} site${sites.length === 1 ? "" : "s"} on ${owner}`);
7376
- }
7377
- const warns = pf.ok && Array.isArray(pf.data?.warnings) ? pf.data.warnings : [];
7378
- if (warns.length > 0) {
7379
- log5.warn(`${warns.length} config item${warns.length === 1 ? "" : "s"} won't migrate automatically:`);
7380
- for (const w of warns.slice(0, 8)) log5.message(chalk20.dim(`\u2022 ${w}`));
7381
- }
7382
- const choice = ensure2(
7383
- await select4({
7384
- message: known ? `An existing reverse proxy (${owner}) is serving ports 80/443.` : `Ports 80/443 are in use by ${owner}, which we couldn't identify.`,
7385
- options: [
7386
- ...importable > 0 ? [{
7387
- value: "migrate",
7388
- label: `Migrate ${importable} site${importable === 1 ? "" : "s"} & take over`,
7389
- hint: "import the existing sites into Openship, then take 80/443"
7390
- }] : [],
7391
- {
7392
- value: "override",
7393
- label: "Stop it & take over 80/443",
7394
- hint: known ? "the existing sites stop being served" : "may interrupt a running service"
7395
- },
7396
- { value: "cancel", label: "Cancel \u2014 leave it running" }
7397
- ],
7398
- // Same default as the `up` preflight (edge-preflight.ts): migrate when
7399
- // there are importable sites — it takes 80/443 without dropping any of
7400
- // them. Otherwise a known proxy defaults to cancel (Enter must not stop
7401
- // sites that are being served) and an unidentified holder to takeover.
7402
- initialValue: importable > 0 ? "migrate" : known ? "cancel" : "override"
7403
- })
7404
- );
7405
- if (choice === "cancel") proceedCustom = false;
7406
- else if (choice === "migrate") edgeMigrate = true;
7407
- else edgeTakeover = true;
7408
- }
7396
+ const edge = await promptLocalEdgeTakeover(port);
7397
+ const proceedCustom = edge.proceed;
7398
+ const edgeTakeover = edge.edgeTakeover;
7399
+ const edgeMigrate = edge.edgeMigrate;
7409
7400
  if (!proceedCustom) {
7410
7401
  log5.warn(
7411
7402
  "Left the existing proxy on 80/443 running. Registering Openship without managed HTTPS \u2014 front it with your proxy, or re-run setup to take over."
@@ -7460,13 +7451,16 @@ async function runWizard() {
7460
7451
  function storedPorts2() {
7461
7452
  const p = join19(OS_DIR, "ports.json");
7462
7453
  try {
7463
- return existsSync18(p) ? JSON.parse(readFileSync17(p, "utf8")) : {};
7454
+ return existsSync18(p) ? JSON.parse(readFileSync16(p, "utf8")) : {};
7464
7455
  } catch {
7465
7456
  return {};
7466
7457
  }
7467
7458
  }
7468
7459
  async function runControl() {
7469
7460
  const svc = serviceStatus();
7461
+ const isCompose = readInstallMethod() === "compose";
7462
+ const running = isCompose ? composeRunning() : svc.running;
7463
+ const managerLabel = isCompose ? "docker compose" : svc.kind === "unsupported" ? "none" : svc.kind;
7470
7464
  const ports = storedPorts2();
7471
7465
  const apiPort2 = String(ports.api ?? 4e3);
7472
7466
  const dashUrl = `http://localhost:${ports.dashboard ?? 3001}`;
@@ -7475,10 +7469,10 @@ async function runControl() {
7475
7469
  intro3(`${chalk20.bgCyan(chalk20.black(" Openship "))}${chalk20.dim(" control")}`);
7476
7470
  note3(
7477
7471
  `${chalk20.dim("URL".padEnd(11))}${chalk20.bold(primaryUrl)}
7478
- ${chalk20.dim("Service".padEnd(11))}${svc.running ? chalk20.green("running") : chalk20.yellow("stopped")}
7472
+ ${chalk20.dim("Service".padEnd(11))}${running ? chalk20.green("running") : chalk20.yellow("stopped")}
7479
7473
  ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7480
7474
  ` + (ports.api ? `${chalk20.dim("API".padEnd(11))}http://localhost:${ports.api}
7481
- ` : "") + `${chalk20.dim("Manager".padEnd(11))}${svc.kind === "unsupported" ? "none" : svc.kind}`,
7475
+ ` : "") + `${chalk20.dim("Manager".padEnd(11))}${managerLabel}`,
7482
7476
  "Openship is already set up"
7483
7477
  );
7484
7478
  const corrupted = looksCorrupted();
@@ -7491,7 +7485,7 @@ ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7491
7485
  options: [
7492
7486
  ...corrupted ? [{ value: "repair", label: "Repair database", hint: "backup \u2192 heal \u2192 verify" }] : [],
7493
7487
  { value: "open", label: "Open the dashboard" },
7494
- svc.running ? { value: "restart", label: "Restart the service" } : { value: "start", label: "Start the service" },
7488
+ running ? { value: "restart", label: "Restart the service" } : { value: "start", label: "Start the service" },
7495
7489
  { value: "stop", label: "Stop the service", hint: "won't restart on boot" },
7496
7490
  ...corrupted ? [] : [{ value: "repair", label: "Repair database", hint: "backup \u2192 heal a corrupt DB" }],
7497
7491
  { value: "reset", label: "Reset admin password", hint: "sets a local email + password login" },
@@ -7511,15 +7505,31 @@ ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7511
7505
  });
7512
7506
  outro3(chalk20.dim(`Opening ${primaryUrl}`));
7513
7507
  return;
7514
- case "start":
7508
+ case "start": {
7509
+ if (isCompose) {
7510
+ const ok3 = composeStart();
7511
+ outro3(ok3 ? chalk20.green("Started.") : chalk20.yellow("Couldn't start the stack \u2014 run `openship up` to see the error."));
7512
+ return;
7513
+ }
7515
7514
  await startService({});
7516
7515
  return;
7516
+ }
7517
7517
  case "restart": {
7518
+ if (isCompose) {
7519
+ const ok3 = composeRestart();
7520
+ outro3(ok3 ? chalk20.green("Restarted.") : chalk20.yellow("Couldn't restart the stack."));
7521
+ return;
7522
+ }
7518
7523
  const r = restart();
7519
7524
  outro3(r.restarted ? chalk20.green("Restarted.") : chalk20.yellow(r.detail));
7520
7525
  return;
7521
7526
  }
7522
7527
  case "stop": {
7528
+ if (isCompose) {
7529
+ const ok3 = composeDown();
7530
+ outro3(ok3 ? chalk20.green("Stopped. Won't restart on boot.") : chalk20.yellow("Couldn't stop the stack."));
7531
+ return;
7532
+ }
7523
7533
  const r = stop();
7524
7534
  outro3(chalk20.green(`Stopped. ${chalk20.dim(r.detail)}`));
7525
7535
  return;
@@ -7544,10 +7554,11 @@ ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7544
7554
 
7545
7555
  // src/index.ts
7546
7556
  var program = new Command28();
7547
- program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.4.5").option("--json", "Machine-readable JSON output (stdout data only)").hook("preAction", (thisCommand) => {
7557
+ program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.4.6").option("--json", "Machine-readable JSON output (stdout data only)").hook("preAction", (thisCommand) => {
7548
7558
  if (thisCommand.opts().json) setJsonMode(true);
7549
7559
  }).action(async () => {
7550
- if (serviceStatus().installed && !isSetupInProgress()) await runControl();
7560
+ const installed = serviceStatus().installed || readInstallMethod() === "compose";
7561
+ if (installed && !isSetupInProgress()) await runControl();
7551
7562
  else await runWizard();
7552
7563
  });
7553
7564
  program.addCommand(upCommand);