openship 0.4.4 → 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 (318) hide show
  1. package/README.md +2 -24
  2. package/dist/bare-2HYDXQEH.js +23 -0
  3. package/dist/{bowser-MFQA6OS4.js → bowser-O3AEA6AN.js} +5 -1
  4. package/dist/{chunk-KD7VO4LG.js → chunk-2ZLR7ULL.js} +255 -65
  5. package/dist/{chunk-YHXCHW7Q.js → chunk-3JQTL4R4.js} +236 -99
  6. package/dist/{chunk-E77LJWJK.js → chunk-4TJC4JZQ.js} +20 -9
  7. package/dist/{chunk-YL67EWYZ.js → chunk-4Z5QO7N6.js} +6 -2
  8. package/dist/{chunk-WJPCBU7Z.js → chunk-5FNIWBFI.js} +10 -6
  9. package/dist/{chunk-Y37KHU7L.js → chunk-5MZ624VX.js} +5 -0
  10. package/dist/{chunk-QKFRZNKN.js → chunk-6APIPIJY.js} +8 -4
  11. package/dist/{chunk-JZLNHGLW.js → chunk-6APULWSU.js} +153 -133
  12. package/dist/{chunk-F4DFVG6Q.js → chunk-6B5YCFFV.js} +8 -4
  13. package/dist/{chunk-XVSAUO2I.js → chunk-AVIB7CT7.js} +5 -1
  14. package/dist/{chunk-TEYJYSMT.js → chunk-C5OHTYEF.js} +12 -6
  15. package/dist/{chunk-7UOB5GGQ.js → chunk-DYPH4HCI.js} +5 -0
  16. package/dist/{chunk-XIFRMKYT.js → chunk-EI575GHN.js} +6 -2
  17. package/dist/{chunk-4CQWT2WL.js → chunk-FPRHYBY2.js} +5 -0
  18. package/dist/chunk-FQGJZ2HS.js +248 -0
  19. package/dist/{chunk-JR2OCMJA.js → chunk-FRNAR33O.js} +6 -2
  20. package/dist/{chunk-JUJBBF5U.js → chunk-FXTBHTWY.js} +362 -42
  21. package/dist/chunk-ILFETZOM.js +131 -0
  22. package/dist/{chunk-5ZCKQ2MI.js → chunk-IXCHURVK.js} +7 -3
  23. package/dist/{chunk-6ERIMTBC.js → chunk-JYGD5WSN.js} +5 -0
  24. package/dist/{chunk-4VNS5WPM.js → chunk-KI2EY3WB.js} +4 -0
  25. package/dist/{chunk-3U4SPZD5.js → chunk-L2VOGB2X.js} +7 -104
  26. package/dist/{chunk-NB2VEIWG.js → chunk-MZ2WEHHY.js} +7 -3
  27. package/dist/{chunk-BUBSSKO6.js → chunk-O6ZZF52V.js} +5 -1
  28. package/dist/{chunk-LBFH7THC.js → chunk-OWED2GRM.js} +5 -1
  29. package/dist/{chunk-4MG3BVHE.js → chunk-OXWDY22G.js} +6 -2
  30. package/dist/{chunk-4FABQGQP.js → chunk-Q2CIRAOK.js} +5 -0
  31. package/dist/{chunk-4LEL3H2Z.js → chunk-RPHRPFEH.js} +5 -0
  32. package/dist/{chunk-46YJFP4S.js → chunk-SQ77DZOI.js} +1728 -1484
  33. package/dist/{chunk-S6DSRLNE.js → chunk-TGIINPVM.js} +15 -2
  34. package/dist/{chunk-U2DPZSXA.js → chunk-TYVME3XD.js} +9 -1
  35. package/dist/{chunk-HFYDYK4D.js → chunk-V6AN6XLZ.js} +18 -6
  36. package/dist/{chunk-ZKJOFWBJ.js → chunk-VDMQJHPI.js} +38 -15
  37. package/dist/{chunk-E6W2JMMU.js → chunk-VFINNTUL.js} +22 -1
  38. package/dist/{chunk-OJE6YWPQ.js → chunk-VWPEKOH6.js} +6 -2
  39. package/dist/{chunk-5N7VUGL6.js → chunk-W73X43R7.js} +6 -2
  40. package/dist/cloud-7QYOGF7F.js +11 -0
  41. package/dist/cloud-ZSKOS4GY.js +30 -0
  42. package/dist/detect-SH4FHFBI.js +36 -0
  43. package/dist/{dist-L3MPOWCA.js → dist-IQJ6H6UZ.js} +6 -2
  44. package/dist/{dist-es-KE56MWYO.js → dist-es-5DY7K32L.js} +9 -5
  45. package/dist/dist-es-JSTDUS7Q.js +26 -0
  46. package/dist/{dist-es-GTAMF674.js → dist-es-OSNJBLXB.js} +8 -4
  47. package/dist/{dist-es-NRZIQY5B.js → dist-es-RYHIPOTN.js} +17 -13
  48. package/dist/{dist-es-XQCOTGC7.js → dist-es-VNKHQVIB.js} +7 -3
  49. package/dist/{dist-es-WZCR7DHQ.js → dist-es-WV4AMOII.js} +11 -7
  50. package/dist/{dist-es-ZW3HFQUM.js → dist-es-X3B5QYKB.js} +9 -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-7LGDL56D.js → event-streams-EHRAKL75.js} +7 -3
  56. package/dist/executor-YPT6RH6O.js +30 -0
  57. package/dist/index.d.ts +2 -1
  58. package/dist/index.js +258 -245
  59. package/dist/{loadSso-N273C5I7.js → loadSso-46FQRIZS.js} +13 -9
  60. package/dist/local-executor-V7U5VOZW.js +12 -0
  61. package/dist/nginx-2GTRUIDE.js +24 -0
  62. package/dist/noop-FMMFG2EZ.js +11 -0
  63. package/dist/{openresty-lua-HAO4EUDY.js → openresty-lua-4WLGDSER.js} +13 -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-DR462ERO.js → signin-ITOPKKUB.js} +13 -9
  305. package/dist/{sso-oidc-VBZLJUK5.js → sso-oidc-NON2PD7Y.js} +13 -9
  306. package/dist/{sts-EJLZ4Z77.js → sts-HHFFSAY2.js} +12 -8
  307. package/package.json +1 -1
  308. package/dist/bare-ITM45QBX.js +0 -18
  309. package/dist/cloud-JMPHO3CW.js +0 -7
  310. package/dist/cloud-W5G6YXAY.js +0 -24
  311. package/dist/dist-es-4C322O5D.js +0 -22
  312. package/dist/docker-3FVJMFC3.js +0 -12
  313. package/dist/docker-edge-executor-ZIWAZWPL.js +0 -9
  314. package/dist/edge-import-Y34YL6JK.js +0 -39
  315. package/dist/executor-J7BHW5HF.js +0 -26
  316. package/dist/nginx-YZWKYKQU.js +0 -16
  317. package/dist/noop-RRNCIUI5.js +0 -7
  318. package/dist/setup-5X3WEEHA.js +0 -14
package/dist/index.js CHANGED
@@ -1,4 +1,7 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env sh
2
+ ":" //# ; exec "$(command -v bun || command -v node)" "$0" "$@"
3
+ import { createRequire as __ospCreateRequire } from "node:module";
4
+ const require = __ospCreateRequire(import.meta.url);
2
5
  import {
3
6
  IS_ALT_HOME,
4
7
  OS_DIR,
@@ -6,6 +9,9 @@ import {
6
9
  composeInternalToken,
7
10
  composeIsViableDefault,
8
11
  composePs,
12
+ composeRestart,
13
+ composeRunning,
14
+ composeStart,
9
15
  composeUninstall,
10
16
  composeUp,
11
17
  composeUpdate,
@@ -19,47 +25,54 @@ import {
19
25
  remoteSha,
20
26
  shortSha,
21
27
  sourceBuildDir
22
- } from "./chunk-YHXCHW7Q.js";
23
- import "./chunk-QKFRZNKN.js";
24
- import "./chunk-XIFRMKYT.js";
25
- import "./chunk-5ZCKQ2MI.js";
26
- import "./chunk-LBFH7THC.js";
27
- import "./chunk-Y37KHU7L.js";
28
- import "./chunk-4FABQGQP.js";
29
- import "./chunk-S6DSRLNE.js";
30
- import "./chunk-TEYJYSMT.js";
31
- import "./chunk-BUBSSKO6.js";
32
- import "./chunk-JR2OCMJA.js";
33
- import "./chunk-OJE6YWPQ.js";
34
- import "./chunk-5N7VUGL6.js";
35
- import "./chunk-6ERIMTBC.js";
36
- import "./chunk-KD7VO4LG.js";
37
- import "./chunk-ZKJOFWBJ.js";
38
- import "./chunk-WJPCBU7Z.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";
39
42
  import {
40
43
  beginEdgeTakeover,
44
+ collectProxyCerts,
41
45
  completeEdgeTakeover,
42
46
  detectInstalledProxy,
47
+ edgeProxy,
48
+ edgeProxyFor,
43
49
  foreignProxyOnEdge,
44
50
  importSites,
45
- ourEdgeContainerRunning,
46
51
  recoverInterruptedTakeover,
47
52
  rollbackEdgeTakeover,
48
53
  scanImportableSites
49
- } from "./chunk-46YJFP4S.js";
50
- import "./chunk-HFYDYK4D.js";
51
- import "./chunk-NB2VEIWG.js";
52
- import "./chunk-3U4SPZD5.js";
53
- import "./chunk-YL67EWYZ.js";
54
- import "./chunk-E77LJWJK.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";
55
66
  import {
56
67
  LocalExecutor
57
- } from "./chunk-U2DPZSXA.js";
58
- import "./chunk-4MG3BVHE.js";
59
- import "./chunk-4LEL3H2Z.js";
60
- import "./chunk-JUJBBF5U.js";
61
- import "./chunk-4CQWT2WL.js";
62
- import "./chunk-E6W2JMMU.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";
63
76
  import {
64
77
  CLOUD_DASHBOARD_URL,
65
78
  LOCAL_API_URL,
@@ -68,9 +81,8 @@ import {
68
81
  isValidEmail,
69
82
  parseOpenshipConfigJson,
70
83
  resolveCliUpdatePlan
71
- } from "./chunk-JZLNHGLW.js";
72
- import "./chunk-XVSAUO2I.js";
73
- import "./chunk-4VNS5WPM.js";
84
+ } from "./chunk-6APULWSU.js";
85
+ import "./chunk-KI2EY3WB.js";
74
86
 
75
87
  // src/index.ts
76
88
  import { Command as Command28 } from "commander";
@@ -472,7 +484,7 @@ import chalk6 from "chalk";
472
484
  import ora from "ora";
473
485
  import { spawn } from "child_process";
474
486
  import { randomBytes as randomBytes2 } from "crypto";
475
- 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";
476
488
  import { dirname as dirname2, join as join8 } from "path";
477
489
  import { fileURLToPath } from "url";
478
490
 
@@ -998,20 +1010,10 @@ async function resolvePorts(prefs) {
998
1010
 
999
1011
  // src/lib/edge-preflight.ts
1000
1012
  import { spawnSync as spawnSync3 } from "child_process";
1001
- 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";
1002
1014
  import { join as join6 } from "path";
1003
1015
  import chalk5 from "chalk";
1004
1016
  import { isCancel, log, note, select } from "@clack/prompts";
1005
- function isSafePath(p) {
1006
- return /^\/[A-Za-z0-9._/-]+$/.test(p);
1007
- }
1008
- function readCertFile(p) {
1009
- try {
1010
- return readFileSync4(p, "utf8");
1011
- } catch {
1012
- return null;
1013
- }
1014
- }
1015
1017
  async function planAndApplyHostEdge(opts, overrides = {}) {
1016
1018
  const deps = { ...defaultDeps(), ...overrides };
1017
1019
  if (deps.platform !== "linux") return { proceed: true };
@@ -1029,7 +1031,7 @@ async function planAndApplyHostEdge(opts, overrides = {}) {
1029
1031
  const { sites, warnings } = await deps.importSites(executor, status);
1030
1032
  const action2 = await resolveAction(opts.edge, { status, owner, sites, warnings }, deps);
1031
1033
  if (action2 === "cancel") return { proceed: false };
1032
- const certPems = action2 === "migrate" ? collectCertPems(sites, deps.readCert) : void 0;
1034
+ const certPems = action2 === "migrate" ? await deps.collectCerts(executor, sites, { status }) : void 0;
1033
1035
  await deps.beginEdgeTakeover(
1034
1036
  executor,
1035
1037
  status,
@@ -1106,7 +1108,12 @@ async function offerStoppedProxyImport(executor, deps) {
1106
1108
  if (!await deps.confirmStoppedImport({ proxy, count: sites.length })) {
1107
1109
  return { proceed: true };
1108
1110
  }
1109
- 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
+ };
1110
1117
  }
1111
1118
  async function completeHostEdge() {
1112
1119
  if (process.platform !== "linux") return;
@@ -1130,17 +1137,57 @@ async function resolveAction(flag, ctx, deps) {
1130
1137
  importable: ctx.sites.length
1131
1138
  });
1132
1139
  }
1133
- function collectCertPems(sites, readCert) {
1134
- const out = {};
1135
- for (const s of sites) {
1136
- if (!s.ssl || !s.tls) continue;
1137
- const { certPath, keyPath } = s.tls;
1138
- if (!isSafePath(certPath) || !isSafePath(keyPath)) continue;
1139
- const certPem = readCert(certPath);
1140
- const keyPem = readCert(keyPath);
1141
- 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}`));
1142
1160
  }
1143
- 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;
1144
1191
  }
1145
1192
  function defaultDeps() {
1146
1193
  return {
@@ -1170,61 +1217,20 @@ function defaultDeps() {
1170
1217
  });
1171
1218
  return !isCancel(take) && take === "import";
1172
1219
  },
1173
- readCert: readCertFile,
1174
- render: ({ owner, sites, warnings }) => {
1175
- if (sites.length > 0) {
1176
- const lines = sites.map((st) => {
1177
- const host = (st.serverNames ?? []).join(", ") || "(no server_name)";
1178
- const dest = st.target.kind === "static" ? `static: ${st.target.root}` : st.target.url;
1179
- return `${chalk5.bold(host)} \u2192 ${chalk5.dim(dest)}${st.ssl ? chalk5.green(" [TLS]") : ""}`;
1180
- });
1181
- note(lines.join("\n"), `Detected ${sites.length} site${sites.length === 1 ? "" : "s"} on ${owner}`);
1182
- }
1183
- if (warnings.length > 0) {
1184
- log.warn(`${warnings.length} config item${warnings.length === 1 ? "" : "s"} won't migrate automatically:`);
1185
- for (const w of warnings.slice(0, 8)) log.message(chalk5.dim(`\u2022 ${w}`));
1186
- }
1187
- },
1188
- confirm: async ({ owner, known, importable }) => {
1189
- const choice = await select({
1190
- 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.`,
1191
- options: [
1192
- ...importable > 0 ? [
1193
- {
1194
- value: "migrate",
1195
- label: `Migrate ${importable} site${importable === 1 ? "" : "s"} & take over`,
1196
- hint: "import the existing sites into Openship's edge, then take 80/443"
1197
- }
1198
- ] : [],
1199
- {
1200
- value: "takeover",
1201
- label: "Stop it & take over 80/443",
1202
- hint: known ? "the existing sites stop being served" : "may interrupt a running service"
1203
- },
1204
- { value: "cancel", label: "Cancel \u2014 leave it running" }
1205
- ],
1206
- // Default to MIGRATE whenever there are sites we can import: it's the only
1207
- // option that both gets Openship onto 80/443 and keeps the operator's
1208
- // existing sites served, so it's the choice they almost always want and
1209
- // the one that loses nothing on a reflexive Enter.
1210
- // With nothing importable there's no safe default: a known proxy falls
1211
- // back to cancel (blind Enter would stop sites that are being served),
1212
- // and an unidentified port holder to takeover.
1213
- initialValue: importable > 0 ? "migrate" : known ? "cancel" : "takeover"
1214
- });
1215
- return isCancel(choice) ? "cancel" : choice;
1216
- },
1220
+ collectCerts: collectCertsFromProxy,
1221
+ render: renderEdgeConflict,
1222
+ confirm: confirmEdgeAction,
1217
1223
  warn: (message) => console.log(chalk5.yellow(` ${message}`))
1218
1224
  };
1219
1225
  }
1220
1226
 
1221
1227
  // src/lib/loopback-api.ts
1222
- 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";
1223
1229
  import { randomBytes } from "crypto";
1224
1230
  import { join as join7 } from "path";
1225
1231
  function ensureInternalToken() {
1226
1232
  const path2 = join7(OS_DIR, "internal-token");
1227
- if (existsSync6(path2)) return readFileSync5(path2, "utf8").trim();
1233
+ if (existsSync6(path2)) return readFileSync4(path2, "utf8").trim();
1228
1234
  mkdirSync7(OS_DIR, { recursive: true, mode: 448 });
1229
1235
  const token = randomBytes(32).toString("hex");
1230
1236
  writeFileSync6(path2, token, { mode: 384 });
@@ -1484,26 +1490,31 @@ async function headlessProvision(opts) {
1484
1490
  warnings.push(`Custom domain provisioning returned: ${res.data?.error || "failed"}`);
1485
1491
  }
1486
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
+ });
1487
1502
  let slug = d.slug;
1488
- let res = await internalPost(
1489
- port,
1490
- "/api/system/self-register",
1491
- { domainType: "free", slug, publicHost: d.publicHost, dashPort },
1492
- token
1493
- );
1503
+ let res = await internalPost(port, "/api/system/self-register", freeBody(slug), token);
1494
1504
  for (let attempt = 0; !res.ok && isSlugTaken(res.data) && opts.onSlugTaken && attempt < 5; attempt++) {
1495
1505
  const next = await opts.onSlugTaken(slug);
1496
1506
  if (!next) break;
1497
1507
  slug = next;
1498
- res = await internalPost(
1499
- port,
1500
- "/api/system/self-register",
1501
- { domainType: "free", slug, publicHost: d.publicHost, dashPort },
1502
- token
1503
- );
1508
+ res = await internalPost(port, "/api/system/self-register", freeBody(slug), token);
1504
1509
  }
1505
1510
  domainRegistered = res.ok;
1506
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
+ }
1507
1518
  if (!res.ok) {
1508
1519
  warnings.push(
1509
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.`
@@ -1524,6 +1535,12 @@ function normalizeUrl(raw) {
1524
1535
  return null;
1525
1536
  }
1526
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
+ }
1527
1544
  function normalizePublicUrl(raw) {
1528
1545
  const url = normalizeUrl(raw);
1529
1546
  if (!url) {
@@ -1539,7 +1556,7 @@ var DIST_DIR = dirname2(fileURLToPath(import.meta.url));
1539
1556
  var SERVER_DIR = join8(DIST_DIR, "server");
1540
1557
  function ensureAuthSecret() {
1541
1558
  const path2 = join8(OS_DIR, "auth-secret");
1542
- if (existsSync7(path2)) return readFileSync6(path2, "utf8").trim();
1559
+ if (existsSync7(path2)) return readFileSync5(path2, "utf8").trim();
1543
1560
  mkdirSync8(OS_DIR, { recursive: true, mode: 448 });
1544
1561
  const secret = randomBytes2(32).toString("hex");
1545
1562
  writeFileSync7(path2, secret, { mode: 384 });
@@ -1681,7 +1698,7 @@ async function runCompose(opts) {
1681
1698
  );
1682
1699
  process.exit(1);
1683
1700
  }
1684
- const publicUrl = opts.publicUrl ? normalizePublicUrl(opts.publicUrl) : void 0;
1701
+ const publicUrl = effectivePublicUrl(opts.publicUrl);
1685
1702
  const fromSource = sourceBuildDir();
1686
1703
  const spinner5 = ora(
1687
1704
  fromSource ? `Building Openship from ${fromSource} and starting the stack\u2026` : "Starting Openship via Docker Compose\u2026"
@@ -1691,8 +1708,10 @@ async function runCompose(opts) {
1691
1708
  dashboardPort: opts.dashboardPort,
1692
1709
  publicUrl,
1693
1710
  trustProxy: opts.trustProxy,
1694
- // commander maps `--no-host-control` to hostControl === false.
1695
- 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
1696
1715
  });
1697
1716
  if (!res.ok) {
1698
1717
  spinner5.fail("docker compose failed to start the stack");
@@ -1740,7 +1759,7 @@ async function runFromSource(opts) {
1740
1759
  await runForeground(opts, src);
1741
1760
  }
1742
1761
  async function startService(opts, runOpts = {}) {
1743
- const publicUrl = opts.publicUrl ? normalizePublicUrl(opts.publicUrl) : void 0;
1762
+ const publicUrl = effectivePublicUrl(opts.publicUrl);
1744
1763
  if (opts.dryRun) {
1745
1764
  const p = preview({
1746
1765
  port: opts.port,
@@ -1839,7 +1858,7 @@ async function runForeground(opts, source) {
1839
1858
  });
1840
1859
  const port = String(resolved.api);
1841
1860
  const dashPort = String(resolved.dashboard);
1842
- const publicUrl = opts.publicUrl ? normalizePublicUrl(opts.publicUrl) : void 0;
1861
+ const publicUrl = effectivePublicUrl(opts.publicUrl);
1843
1862
  const managedEdge = Boolean(opts.managedEdge && publicUrl);
1844
1863
  const dataDir = opts.dataDir || join8(OS_DIR, "data");
1845
1864
  mkdirSync8(dataDir, { recursive: true });
@@ -1861,9 +1880,12 @@ async function runForeground(opts, source) {
1861
1880
  if (!source) {
1862
1881
  env.OPENSHIP_MIGRATIONS_DIR = join8(SERVER_DIR, "migrations");
1863
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;
1864
1885
  }
1865
1886
  env.OPENSHIP_REQUIRE_AUTH = "true";
1866
1887
  env.INTERNAL_TOKEN = ensureInternalToken();
1888
+ if (!env.OPENSHIP_AUTH_MODE) env.OPENSHIP_AUTH_MODE = "local";
1867
1889
  env.OPENSHIP_API_HOST = "127.0.0.1";
1868
1890
  env.OPENSHIP_DASHBOARD_PORT = dashPort;
1869
1891
  env.OPENSHIP_INSTANCE_LOG = instanceLogPath;
@@ -1951,7 +1973,7 @@ async function runForeground(opts, source) {
1951
1973
  const uiSpinner = ora("Preparing the dashboard\u2026").start();
1952
1974
  try {
1953
1975
  const bundle = await ensureDashboard({
1954
- tag: source ? "local" : opts.uiVersion || `v${"0.4.4"}`,
1976
+ tag: source ? "local" : opts.uiVersion || `v${"0.4.6"}`,
1955
1977
  onProgress: (received, total) => {
1956
1978
  if (total) {
1957
1979
  uiSpinner.text = `Downloading dashboard\u2026 ${Math.round(received / total * 100)}%`;
@@ -2220,7 +2242,7 @@ var initCommand = new Command7("init").description("Link the current directory t
2220
2242
 
2221
2243
  // src/commands/config.ts
2222
2244
  import { Command as Command8 } from "commander";
2223
- 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";
2224
2246
  import { join as join10 } from "path";
2225
2247
  var SCHEMA_URL = "https://openship.io/openship.schema.json";
2226
2248
  var CONFIG_FILE = "openship.json";
@@ -2241,7 +2263,7 @@ function detectHints(dir) {
2241
2263
  const pkgPath = join10(dir, "package.json");
2242
2264
  if (existsSync10(pkgPath)) {
2243
2265
  try {
2244
- const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
2266
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
2245
2267
  const runner = hints.packageManager ?? "npm";
2246
2268
  const exec = runner === "npm" ? "npm run" : runner;
2247
2269
  if (pkg.scripts?.build) hints.buildCommand = `${exec} build`;
@@ -2277,7 +2299,7 @@ var validateCmd = new Command8("validate").description("Validate an openship.jso
2277
2299
  else err(`Not found: ${path2}`);
2278
2300
  process.exit(1);
2279
2301
  }
2280
- const { config, errors, warnings } = parseOpenshipConfigJson(readFileSync7(path2, "utf8"));
2302
+ const { config, errors, warnings } = parseOpenshipConfigJson(readFileSync6(path2, "utf8"));
2281
2303
  const valid = errors.length === 0 && config !== null;
2282
2304
  if (isJsonMode()) {
2283
2305
  printJson({ valid, errors, warnings });
@@ -2350,11 +2372,11 @@ var contextCommand = new Command9("context").alias("ctx").description("Manage co
2350
2372
  // src/commands/status.ts
2351
2373
  import { Command as Command10 } from "commander";
2352
2374
  import chalk9 from "chalk";
2353
- import { readFileSync as readFileSync8 } from "fs";
2375
+ import { readFileSync as readFileSync7 } from "fs";
2354
2376
  import { join as join11 } from "path";
2355
2377
  function readPorts() {
2356
2378
  try {
2357
- return JSON.parse(readFileSync8(join11(OS_DIR, "ports.json"), "utf8"));
2379
+ return JSON.parse(readFileSync7(join11(OS_DIR, "ports.json"), "utf8"));
2358
2380
  } catch {
2359
2381
  return {};
2360
2382
  }
@@ -2418,7 +2440,7 @@ import { intro, outro, note as note2, select as select3, log as log3, spinner as
2418
2440
 
2419
2441
  // src/lib/repair.ts
2420
2442
  import { spawnSync as spawnSync4 } from "child_process";
2421
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
2443
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
2422
2444
  import { join as join13 } from "path";
2423
2445
  import chalk10 from "chalk";
2424
2446
  import { spinner, log as log2, select as select2, isCancel as isCancel3, cancel } from "@clack/prompts";
@@ -2532,7 +2554,7 @@ function ensure(value) {
2532
2554
  function storedPorts() {
2533
2555
  try {
2534
2556
  const p = join13(OS_DIR, "ports.json");
2535
- return existsSync12(p) ? JSON.parse(readFileSync9(p, "utf8")) : {};
2557
+ return existsSync12(p) ? JSON.parse(readFileSync8(p, "utf8")) : {};
2536
2558
  } catch {
2537
2559
  return {};
2538
2560
  }
@@ -2584,7 +2606,7 @@ function lastServiceError() {
2584
2606
  const p = join13(LOG_DIR2, name);
2585
2607
  if (!existsSync12(p)) continue;
2586
2608
  try {
2587
- const lines = readFileSync9(p, "utf8").trim().split("\n");
2609
+ const lines = readFileSync8(p, "utf8").trim().split("\n");
2588
2610
  const hit = [...lines].reverse().find((l) => /error|locked|aborted|malformed|corrupt|throw|cannot|EADDRINUSE/i.test(l));
2589
2611
  if (hit) return hit.trim().slice(0, 240);
2590
2612
  } catch {
@@ -3011,7 +3033,7 @@ import { execFileSync as execFileSync4 } from "child_process";
3011
3033
  import ora2 from "ora";
3012
3034
 
3013
3035
  // src/lib/project-link.ts
3014
- import { readFileSync as readFileSync10, existsSync as existsSync14 } from "fs";
3036
+ import { readFileSync as readFileSync9, existsSync as existsSync14 } from "fs";
3015
3037
  import { join as join14, dirname as dirname3, parse } from "path";
3016
3038
  var LINK_REL = join14(".openship", "project.json");
3017
3039
  function findProjectLinkPath(from = process.cwd()) {
@@ -3028,7 +3050,7 @@ function readProjectLink(from) {
3028
3050
  const path2 = findProjectLinkPath(from);
3029
3051
  if (!path2) return null;
3030
3052
  try {
3031
- return JSON.parse(readFileSync10(path2, "utf8"));
3053
+ return JSON.parse(readFileSync9(path2, "utf8"));
3032
3054
  } catch {
3033
3055
  return null;
3034
3056
  }
@@ -3036,7 +3058,7 @@ function readProjectLink(from) {
3036
3058
 
3037
3059
  // src/lib/folder-deploy.ts
3038
3060
  import { execFileSync as execFileSync3 } from "child_process";
3039
- 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";
3040
3062
  import { tmpdir } from "os";
3041
3063
  import { join as join15, basename } from "path";
3042
3064
  function detectPackageManager(dir) {
@@ -3086,7 +3108,7 @@ async function deployFolder(opts) {
3086
3108
  );
3087
3109
  step("Uploading source");
3088
3110
  try {
3089
- const body = readFileSync11(tarball);
3111
+ const body = readFileSync10(tarball);
3090
3112
  const up = session.upload;
3091
3113
  const method = up.method || "POST";
3092
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 });
@@ -4798,7 +4820,7 @@ var domainCommand = new Command17("domain").description("Manage custom domains,
4798
4820
  import { Command as Command18 } from "commander";
4799
4821
  import chalk15 from "chalk";
4800
4822
  import ora4 from "ora";
4801
- var INSTALLABLE = ["docker", "git", "openresty", "certbot", "rsync"];
4823
+ var INSTALLABLE = ["docker", "git", "edge", "rsync"];
4802
4824
  function guard(fn) {
4803
4825
  return async (...args) => {
4804
4826
  if (!getToken()) {
@@ -5092,7 +5114,7 @@ var serverCommand = server;
5092
5114
  // src/commands/system.ts
5093
5115
  import { Command as Command19 } from "commander";
5094
5116
  import ora5 from "ora";
5095
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
5117
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "fs";
5096
5118
  import { createInterface as createInterface6 } from "readline/promises";
5097
5119
  import { stdin as input5, stdout as output5 } from "process";
5098
5120
  async function guarded(fn) {
@@ -5418,7 +5440,7 @@ dataTransferCommand.command("import").description("Import an instance export fil
5418
5440
  }
5419
5441
  let file;
5420
5442
  try {
5421
- file = JSON.parse(readFileSync12(opts.file, "utf8"));
5443
+ file = JSON.parse(readFileSync11(opts.file, "utf8"));
5422
5444
  } catch {
5423
5445
  err(`
5424
5446
  Could not read or parse ${opts.file}.
@@ -5769,7 +5791,7 @@ var mailCommand = new Command20("mail").description("Self-hosted mail server (iR
5769
5791
  // src/commands/backup.ts
5770
5792
  import { Command as Command21 } from "commander";
5771
5793
  import ora7 from "ora";
5772
- import { readFileSync as readFileSync13 } from "fs";
5794
+ import { readFileSync as readFileSync12 } from "fs";
5773
5795
  async function guard3(fn) {
5774
5796
  try {
5775
5797
  await fn();
@@ -6062,7 +6084,7 @@ destinationCmd.command("create").description("Create a backup destination").requ
6062
6084
  let sftpPrivateKey = opts.sftpPrivateKey;
6063
6085
  if (opts.sftpPrivateKeyFile) {
6064
6086
  try {
6065
- sftpPrivateKey = readFileSync13(opts.sftpPrivateKeyFile, "utf8");
6087
+ sftpPrivateKey = readFileSync12(opts.sftpPrivateKeyFile, "utf8");
6066
6088
  } catch {
6067
6089
  throw new Error(`Cannot read key file: ${opts.sftpPrivateKeyFile}`);
6068
6090
  }
@@ -6254,11 +6276,11 @@ var apiCommand = new Command23("api").description("Make an authenticated request
6254
6276
  import { Command as Command24 } from "commander";
6255
6277
  import chalk19 from "chalk";
6256
6278
  import { intro as intro2, outro as outro2, password as passwordPrompt, isCancel as isCancel4, cancel as cancel3, log as log4 } from "@clack/prompts";
6257
- import { readFileSync as readFileSync14 } from "fs";
6279
+ import { readFileSync as readFileSync13 } from "fs";
6258
6280
  import { join as join16 } from "path";
6259
6281
  function resolvedApiPort() {
6260
6282
  try {
6261
- return JSON.parse(readFileSync14(join16(OS_DIR, "ports.json"), "utf8")).api;
6283
+ return JSON.parse(readFileSync13(join16(OS_DIR, "ports.json"), "utf8")).api;
6262
6284
  } catch {
6263
6285
  return void 0;
6264
6286
  }
@@ -6312,7 +6334,7 @@ var resetAdminCommand = new Command24("reset-admin-password").description("Reset
6312
6334
 
6313
6335
  // src/commands/install.ts
6314
6336
  import { Command as Command25 } from "commander";
6315
- 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";
6316
6338
  import { spawn as spawn2, spawnSync as spawnSync6 } from "child_process";
6317
6339
  import { homedir as homedir3 } from "os";
6318
6340
  import { join as join17 } from "path";
@@ -6449,7 +6471,7 @@ var installCommand = new Command25("install").description("Download and install
6449
6471
  try {
6450
6472
  let sidecarBody;
6451
6473
  if (existsSync16(sidecarPath) && !downloaded) {
6452
- sidecarBody = readFileSync15(sidecarPath, "utf8");
6474
+ sidecarBody = readFileSync14(sidecarPath, "utf8");
6453
6475
  } else {
6454
6476
  sidecarBody = await fetchSidecar(sidecarUrl);
6455
6477
  }
@@ -6560,7 +6582,7 @@ function detectPackageManager2(override) {
6560
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) => {
6561
6583
  const source = readSourceInstall();
6562
6584
  if (source) return runSourceUpdate(source, opts);
6563
- const current = "0.4.4";
6585
+ const current = "0.4.6";
6564
6586
  let latest;
6565
6587
  try {
6566
6588
  latest = (await resolveLatestTag()).replace(/^v/, "");
@@ -6595,13 +6617,13 @@ var updateCommand = new Command26("update").description("Update the Openship CLI
6595
6617
  return;
6596
6618
  }
6597
6619
  if (readInstallMethod() === "compose") {
6598
- const pulled = composeUpdate(latest);
6620
+ const applied = composeUpdate(latest);
6599
6621
  if (isJsonMode()) {
6600
- printJson({ updated: true, from: current, to: latest, via: pm, method: "compose", pulled });
6601
- } else if (pulled) {
6602
- 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.`);
6603
6625
  } else {
6604
- 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.`);
6605
6627
  process.exitCode = 1;
6606
6628
  }
6607
6629
  return;
@@ -6618,7 +6640,7 @@ var updateCommand = new Command26("update").description("Update the Openship CLI
6618
6640
 
6619
6641
  // src/commands/cache.ts
6620
6642
  import { Command as Command27 } from "commander";
6621
- 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";
6622
6644
  import { join as join18 } from "path";
6623
6645
  function listAssets() {
6624
6646
  if (!existsSync17(RELEASES_DIR)) return [];
@@ -6667,7 +6689,7 @@ var verifyCmd2 = new Command27("verify").description("Re-hash cached assets and
6667
6689
  results.push({ tag: a.tag, asset: a.name, result: "no-sidecar" });
6668
6690
  continue;
6669
6691
  }
6670
- const expected = parseSha256(readFileSync16(`${a.path}.sha256`, "utf8"));
6692
+ const expected = parseSha256(readFileSync15(`${a.path}.sha256`, "utf8"));
6671
6693
  const actual = await hashFile(a.path);
6672
6694
  const okMatch = expected !== null && expected === actual;
6673
6695
  if (!okMatch) bad += 1;
@@ -6703,7 +6725,7 @@ var cacheCommand = new Command27("cache").description("Manage the local download
6703
6725
  import chalk20 from "chalk";
6704
6726
  import open2 from "open";
6705
6727
  import { createHash as createHash2, randomBytes as randomBytes3 } from "crypto";
6706
- 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";
6707
6729
  import { join as join19 } from "path";
6708
6730
  import {
6709
6731
  intro as intro3,
@@ -6891,6 +6913,21 @@ ${pad("Login")}${opts.adminEmail} ${chalk20.dim("(email + password you set)")}
6891
6913
  );
6892
6914
  outro3(opts.byo ? chalk20.dim("Point your reverse proxy at the dashboard port above.") : chalk20.green("Happy shipping."));
6893
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
+ }
6894
6931
  async function runWizard() {
6895
6932
  const resuming = isSetupInProgress();
6896
6933
  intro3(`${chalk20.bgCyan(chalk20.black(" Openship "))}${chalk20.dim(" setup")}`);
@@ -7112,7 +7149,7 @@ async function runWizard() {
7112
7149
  }
7113
7150
  }
7114
7151
  }
7115
- const uiTag = `v${"0.4.4"}`;
7152
+ const uiTag = `v${"0.4.6"}`;
7116
7153
  if (method === "bare") {
7117
7154
  const dl = spinner4();
7118
7155
  dl.start("Pulling the Openship dist from GitHub");
@@ -7180,7 +7217,7 @@ async function runWizard() {
7180
7217
  const up = composeUp({
7181
7218
  publicUrl,
7182
7219
  trustProxy: behindProxy,
7183
- version: "0.4.4",
7220
+ version: "0.4.6",
7184
7221
  noHostControl: !allowHostControl
7185
7222
  });
7186
7223
  if (!up.ok) {
@@ -7317,93 +7354,49 @@ async function runWizard() {
7317
7354
  await internalPost(port, "/api/system/self-register", { domainType: "byo" });
7318
7355
  } else {
7319
7356
  cloudEmail = cloud.email;
7320
- let regSlug = domainPlan.slug;
7321
- while (true) {
7322
- const s2 = spinner4();
7323
- s2.start(`Registering ${chalk20.bold(`${regSlug}.opsh.io`)} with Openship Cloud`);
7324
- const res = await internalPost(port, "/api/system/self-register", {
7325
- domainType: "free",
7326
- slug: regSlug,
7327
- publicHost: domainPlan.publicHost,
7328
- dashPort: Number(started.dashPort)
7329
- });
7330
- if (res.ok && res.data?.url) {
7331
- liveUrl = res.data.url;
7332
- s2.stop(`Free domain live: ${res.data.url}`);
7333
- break;
7334
- }
7335
- s2.stop(`Couldn't register ${regSlug}.opsh.io: ${res.data?.error || "failed"}`, 1);
7336
- const next = ensure2(
7337
- await select4({
7338
- message: "Try a different subdomain?",
7339
- options: [
7340
- { value: "retry", label: "Pick another subdomain" },
7341
- { value: "skip", label: "Skip for now", hint: "log in on this server; add a domain later in Settings \u2192 Cloud" }
7342
- ]
7343
- })
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."
7344
7361
  );
7345
- if (next === "skip") break;
7346
- regSlug = ensure2(
7347
- await text({
7348
- message: "Choose your subdomain",
7349
- placeholder: "my-openship",
7350
- initialValue: regSlug,
7351
- validate: (v) => v && SLUG_RE2.test(v.trim().toLowerCase()) ? void 0 : "Lowercase letters, digits, hyphens"
7352
- })
7353
- ).trim().toLowerCase();
7354
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);
7355
7394
  }
7356
7395
  } else if (domainPlan.type === "custom") {
7357
- let edgeTakeover = false;
7358
- let edgeMigrate = false;
7359
- let proceedCustom = true;
7360
- const pf = await internalPost(port, "/api/system/self-edge/preflight", {});
7361
- const status = pf.ok ? pf.data?.status : void 0;
7362
- const importable = pf.ok && Array.isArray(pf.data?.sites) ? pf.data.sites.length : 0;
7363
- if (status && !status.canProceedClean && status.occupants?.length) {
7364
- const owner = status.occupants.map((o) => o.command ?? `port ${o.port}`).join(", ");
7365
- const known = status.classification === "known";
7366
- const sites = pf.ok && Array.isArray(pf.data?.sites) ? pf.data.sites : [];
7367
- if (sites.length > 0) {
7368
- const lines = sites.map((st) => {
7369
- const host = (st.serverNames ?? []).join(", ") || "(no server_name)";
7370
- const dest = st.target?.kind === "static" ? `static: ${st.target?.root ?? ""}` : st.target?.url ?? "";
7371
- return `${chalk20.bold(host)} \u2192 ${chalk20.dim(dest)}${st.ssl ? chalk20.green(" [TLS]") : ""}`;
7372
- });
7373
- note3(lines.join("\n"), `Detected ${sites.length} site${sites.length === 1 ? "" : "s"} on ${owner}`);
7374
- }
7375
- const warns = pf.ok && Array.isArray(pf.data?.warnings) ? pf.data.warnings : [];
7376
- if (warns.length > 0) {
7377
- log5.warn(`${warns.length} config item${warns.length === 1 ? "" : "s"} won't migrate automatically:`);
7378
- for (const w of warns.slice(0, 8)) log5.message(chalk20.dim(`\u2022 ${w}`));
7379
- }
7380
- const choice = ensure2(
7381
- await select4({
7382
- 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.`,
7383
- options: [
7384
- ...importable > 0 ? [{
7385
- value: "migrate",
7386
- label: `Migrate ${importable} site${importable === 1 ? "" : "s"} & take over`,
7387
- hint: "import the existing sites into Openship, then take 80/443"
7388
- }] : [],
7389
- {
7390
- value: "override",
7391
- label: "Stop it & take over 80/443",
7392
- hint: known ? "the existing sites stop being served" : "may interrupt a running service"
7393
- },
7394
- { value: "cancel", label: "Cancel \u2014 leave it running" }
7395
- ],
7396
- // Same default as the `up` preflight (edge-preflight.ts): migrate when
7397
- // there are importable sites — it takes 80/443 without dropping any of
7398
- // them. Otherwise a known proxy defaults to cancel (Enter must not stop
7399
- // sites that are being served) and an unidentified holder to takeover.
7400
- initialValue: importable > 0 ? "migrate" : known ? "cancel" : "override"
7401
- })
7402
- );
7403
- if (choice === "cancel") proceedCustom = false;
7404
- else if (choice === "migrate") edgeMigrate = true;
7405
- else edgeTakeover = true;
7406
- }
7396
+ const edge = await promptLocalEdgeTakeover(port);
7397
+ const proceedCustom = edge.proceed;
7398
+ const edgeTakeover = edge.edgeTakeover;
7399
+ const edgeMigrate = edge.edgeMigrate;
7407
7400
  if (!proceedCustom) {
7408
7401
  log5.warn(
7409
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."
@@ -7458,13 +7451,16 @@ async function runWizard() {
7458
7451
  function storedPorts2() {
7459
7452
  const p = join19(OS_DIR, "ports.json");
7460
7453
  try {
7461
- return existsSync18(p) ? JSON.parse(readFileSync17(p, "utf8")) : {};
7454
+ return existsSync18(p) ? JSON.parse(readFileSync16(p, "utf8")) : {};
7462
7455
  } catch {
7463
7456
  return {};
7464
7457
  }
7465
7458
  }
7466
7459
  async function runControl() {
7467
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;
7468
7464
  const ports = storedPorts2();
7469
7465
  const apiPort2 = String(ports.api ?? 4e3);
7470
7466
  const dashUrl = `http://localhost:${ports.dashboard ?? 3001}`;
@@ -7473,10 +7469,10 @@ async function runControl() {
7473
7469
  intro3(`${chalk20.bgCyan(chalk20.black(" Openship "))}${chalk20.dim(" control")}`);
7474
7470
  note3(
7475
7471
  `${chalk20.dim("URL".padEnd(11))}${chalk20.bold(primaryUrl)}
7476
- ${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")}
7477
7473
  ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7478
7474
  ` + (ports.api ? `${chalk20.dim("API".padEnd(11))}http://localhost:${ports.api}
7479
- ` : "") + `${chalk20.dim("Manager".padEnd(11))}${svc.kind === "unsupported" ? "none" : svc.kind}`,
7475
+ ` : "") + `${chalk20.dim("Manager".padEnd(11))}${managerLabel}`,
7480
7476
  "Openship is already set up"
7481
7477
  );
7482
7478
  const corrupted = looksCorrupted();
@@ -7489,7 +7485,7 @@ ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7489
7485
  options: [
7490
7486
  ...corrupted ? [{ value: "repair", label: "Repair database", hint: "backup \u2192 heal \u2192 verify" }] : [],
7491
7487
  { value: "open", label: "Open the dashboard" },
7492
- 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" },
7493
7489
  { value: "stop", label: "Stop the service", hint: "won't restart on boot" },
7494
7490
  ...corrupted ? [] : [{ value: "repair", label: "Repair database", hint: "backup \u2192 heal a corrupt DB" }],
7495
7491
  { value: "reset", label: "Reset admin password", hint: "sets a local email + password login" },
@@ -7509,15 +7505,31 @@ ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7509
7505
  });
7510
7506
  outro3(chalk20.dim(`Opening ${primaryUrl}`));
7511
7507
  return;
7512
- 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
+ }
7513
7514
  await startService({});
7514
7515
  return;
7516
+ }
7515
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
+ }
7516
7523
  const r = restart();
7517
7524
  outro3(r.restarted ? chalk20.green("Restarted.") : chalk20.yellow(r.detail));
7518
7525
  return;
7519
7526
  }
7520
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
+ }
7521
7533
  const r = stop();
7522
7534
  outro3(chalk20.green(`Stopped. ${chalk20.dim(r.detail)}`));
7523
7535
  return;
@@ -7542,10 +7554,11 @@ ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7542
7554
 
7543
7555
  // src/index.ts
7544
7556
  var program = new Command28();
7545
- program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.4.4").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) => {
7546
7558
  if (thisCommand.opts().json) setJsonMode(true);
7547
7559
  }).action(async () => {
7548
- if (serviceStatus().installed && !isSetupInProgress()) await runControl();
7560
+ const installed = serviceStatus().installed || readInstallMethod() === "compose";
7561
+ if (installed && !isSetupInProgress()) await runControl();
7549
7562
  else await runWizard();
7550
7563
  });
7551
7564
  program.addCommand(upCommand);