@happier-dev/stack 0.1.0-preview.5.1

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 (439) hide show
  1. package/README.md +501 -0
  2. package/bin/hstack.mjs +348 -0
  3. package/docs/codex-mcp-resume.md +129 -0
  4. package/docs/edison.md +74 -0
  5. package/docs/forking-and-branding.md +189 -0
  6. package/docs/happy-development.md +22 -0
  7. package/docs/isolated-linux-vm.md +243 -0
  8. package/docs/menubar.md +244 -0
  9. package/docs/mobile-ios.md +322 -0
  10. package/docs/monorepo-migration.md +20 -0
  11. package/docs/paths-and-env.md +154 -0
  12. package/docs/remote-access.md +43 -0
  13. package/docs/server-flavors.md +147 -0
  14. package/docs/stacks.md +330 -0
  15. package/docs/tauri.md +60 -0
  16. package/docs/worktrees-and-forks.md +133 -0
  17. package/extras/swiftbar/auth-login.sh +29 -0
  18. package/extras/swiftbar/git-cache-refresh.sh +122 -0
  19. package/extras/swiftbar/hstack-term.sh +133 -0
  20. package/extras/swiftbar/hstack.5s.sh +296 -0
  21. package/extras/swiftbar/hstack.sh +35 -0
  22. package/extras/swiftbar/icons/happy-green.png +0 -0
  23. package/extras/swiftbar/icons/happy-orange.png +0 -0
  24. package/extras/swiftbar/icons/happy-red.png +0 -0
  25. package/extras/swiftbar/icons/logo-white.png +0 -0
  26. package/extras/swiftbar/install.sh +265 -0
  27. package/extras/swiftbar/lib/git.sh +629 -0
  28. package/extras/swiftbar/lib/icons.sh +92 -0
  29. package/extras/swiftbar/lib/render.sh +999 -0
  30. package/extras/swiftbar/lib/system.sh +244 -0
  31. package/extras/swiftbar/lib/utils.sh +717 -0
  32. package/extras/swiftbar/set-interval.sh +65 -0
  33. package/extras/swiftbar/set-server-flavor.sh +61 -0
  34. package/extras/swiftbar/wt-pr.sh +140 -0
  35. package/node_modules/@happier-dev/cli-common/README.md +6 -0
  36. package/node_modules/@happier-dev/cli-common/dist/index.d.ts +4 -0
  37. package/node_modules/@happier-dev/cli-common/dist/index.d.ts.map +1 -0
  38. package/node_modules/@happier-dev/cli-common/dist/index.js +4 -0
  39. package/node_modules/@happier-dev/cli-common/dist/index.js.map +1 -0
  40. package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts +18 -0
  41. package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts.map +1 -0
  42. package/node_modules/@happier-dev/cli-common/dist/links/index.js +25 -0
  43. package/node_modules/@happier-dev/cli-common/dist/links/index.js.map +1 -0
  44. package/node_modules/@happier-dev/cli-common/dist/links.d.ts +2 -0
  45. package/node_modules/@happier-dev/cli-common/dist/links.d.ts.map +1 -0
  46. package/node_modules/@happier-dev/cli-common/dist/links.js +2 -0
  47. package/node_modules/@happier-dev/cli-common/dist/links.js.map +1 -0
  48. package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts +67 -0
  49. package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts.map +1 -0
  50. package/node_modules/@happier-dev/cli-common/dist/update/index.js +259 -0
  51. package/node_modules/@happier-dev/cli-common/dist/update/index.js.map +1 -0
  52. package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts +17 -0
  53. package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts.map +1 -0
  54. package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js +80 -0
  55. package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js.map +1 -0
  56. package/node_modules/@happier-dev/cli-common/package.json +26 -0
  57. package/package.json +77 -0
  58. package/scripts/auth.mjs +1829 -0
  59. package/scripts/auth_copy_from_pglite_lock_in_use.integration.test.mjs +90 -0
  60. package/scripts/auth_copy_from_runCapture.integration.test.mjs +447 -0
  61. package/scripts/auth_help_cmd.test.mjs +28 -0
  62. package/scripts/auth_login_flow_in_tty.test.mjs +100 -0
  63. package/scripts/auth_login_force_default.test.mjs +66 -0
  64. package/scripts/auth_login_guided_server_no_expo.test.mjs +126 -0
  65. package/scripts/auth_login_method_override.test.mjs +67 -0
  66. package/scripts/auth_login_print_includes_configure_links.test.mjs +99 -0
  67. package/scripts/auth_status_server_validation.integration.test.mjs +140 -0
  68. package/scripts/build.mjs +266 -0
  69. package/scripts/bundleWorkspaceDeps.mjs +38 -0
  70. package/scripts/bundleWorkspaceDeps.test.mjs +77 -0
  71. package/scripts/ci.mjs +135 -0
  72. package/scripts/ci.test.mjs +50 -0
  73. package/scripts/cli-link.mjs +57 -0
  74. package/scripts/completion.mjs +395 -0
  75. package/scripts/contrib.mjs +333 -0
  76. package/scripts/daemon.mjs +1160 -0
  77. package/scripts/daemon.status_scope.test.mjs +51 -0
  78. package/scripts/daemon_cmd.mjs +26 -0
  79. package/scripts/daemon_dist_guard.test.mjs +171 -0
  80. package/scripts/daemon_invalid_auth_reseed_stack_name.integration.test.mjs +608 -0
  81. package/scripts/daemon_server_scoped_state.test.mjs +49 -0
  82. package/scripts/daemon_start_verification.integration.test.mjs +296 -0
  83. package/scripts/dev.mjs +545 -0
  84. package/scripts/doctor.mjs +340 -0
  85. package/scripts/doctor_cmd.test.mjs +22 -0
  86. package/scripts/doctor_ui_index_missing.test.mjs +37 -0
  87. package/scripts/eas.mjs +367 -0
  88. package/scripts/eas_platform_parsing.test.mjs +63 -0
  89. package/scripts/edison.mjs +1848 -0
  90. package/scripts/env.mjs +149 -0
  91. package/scripts/env_cmd.test.mjs +118 -0
  92. package/scripts/exit_cleanup_kills_detached_children_on_crash.integration.test.mjs +80 -0
  93. package/scripts/happier.mjs +82 -0
  94. package/scripts/import.mjs +1327 -0
  95. package/scripts/init.mjs +464 -0
  96. package/scripts/install.mjs +550 -0
  97. package/scripts/lint.mjs +177 -0
  98. package/scripts/menubar.mjs +202 -0
  99. package/scripts/migrate.mjs +318 -0
  100. package/scripts/mobile.mjs +353 -0
  101. package/scripts/mobile_dev_client.mjs +87 -0
  102. package/scripts/monorepo.mjs +2234 -0
  103. package/scripts/monorepo_port.apply.integration.test.mjs +680 -0
  104. package/scripts/monorepo_port.conflicts.integration.test.mjs +454 -0
  105. package/scripts/monorepo_port.validation.integration.test.mjs +486 -0
  106. package/scripts/orchestrated_stack_auth_flow.test.mjs +134 -0
  107. package/scripts/orchestrated_stack_auth_flow_resolve_port.test.mjs +98 -0
  108. package/scripts/orchestrated_stack_auth_flow_webapp_url.test.mjs +119 -0
  109. package/scripts/pack.mjs +257 -0
  110. package/scripts/pack.test.mjs +68 -0
  111. package/scripts/pglite_lock.integration.test.mjs +152 -0
  112. package/scripts/provision/linux-ubuntu-e2e.sh +132 -0
  113. package/scripts/provision/linux-ubuntu-review-pr.sh +66 -0
  114. package/scripts/provision/macos-lima-happy-vm.sh +192 -0
  115. package/scripts/provision/macos-lima-hstack-e2e.sh +100 -0
  116. package/scripts/release.mjs +53 -0
  117. package/scripts/release_binary_smoke.integration.test.mjs +159 -0
  118. package/scripts/review.mjs +1752 -0
  119. package/scripts/review_pr.mjs +435 -0
  120. package/scripts/run.mjs +561 -0
  121. package/scripts/run_script_with_stack_env.restart_port_reuse.test.mjs +30 -0
  122. package/scripts/self.mjs +465 -0
  123. package/scripts/self_host.mjs +9 -0
  124. package/scripts/self_host_binary_smoke.integration.test.mjs +94 -0
  125. package/scripts/self_host_runtime.mjs +883 -0
  126. package/scripts/self_host_runtime.test.mjs +82 -0
  127. package/scripts/self_host_systemd.real.integration.test.mjs +367 -0
  128. package/scripts/server_flavor.mjs +148 -0
  129. package/scripts/service.mjs +868 -0
  130. package/scripts/service_mode_help.test.mjs +27 -0
  131. package/scripts/setup.mjs +1324 -0
  132. package/scripts/setup_non_interactive_flag.test.mjs +60 -0
  133. package/scripts/setup_pr.mjs +605 -0
  134. package/scripts/setup_pr_orchestrated_auth_flow_util_import.test.mjs +117 -0
  135. package/scripts/stack/command_arguments.mjs +91 -0
  136. package/scripts/stack/copy_auth_from_stack.mjs +111 -0
  137. package/scripts/stack/delegated_script_commands.mjs +92 -0
  138. package/scripts/stack/help_text.mjs +110 -0
  139. package/scripts/stack/port_reservation.mjs +74 -0
  140. package/scripts/stack/repo_checkout_resolution.mjs +31 -0
  141. package/scripts/stack/run_script_with_stack_env.mjs +634 -0
  142. package/scripts/stack/stack_daemon_command.mjs +219 -0
  143. package/scripts/stack/stack_delegated_help.mjs +81 -0
  144. package/scripts/stack/stack_environment.mjs +151 -0
  145. package/scripts/stack/stack_environment.sanitization.test.mjs +75 -0
  146. package/scripts/stack/stack_happier_passthrough_command.mjs +63 -0
  147. package/scripts/stack/stack_info_snapshot.mjs +167 -0
  148. package/scripts/stack/stack_mobile_install_command.mjs +61 -0
  149. package/scripts/stack/stack_resume_command.mjs +76 -0
  150. package/scripts/stack/stack_stop_command.mjs +34 -0
  151. package/scripts/stack/stack_workspace_command.mjs +83 -0
  152. package/scripts/stack/transient_repo_overrides.mjs +29 -0
  153. package/scripts/stack.mjs +2388 -0
  154. package/scripts/stack_archive_cmd.integration.test.mjs +31 -0
  155. package/scripts/stack_audit_fix_light_env.test.mjs +129 -0
  156. package/scripts/stack_background_pinned_stack_json.test.mjs +81 -0
  157. package/scripts/stack_copy_auth_server_scoped.test.mjs +243 -0
  158. package/scripts/stack_daemon_cmd.integration.test.mjs +484 -0
  159. package/scripts/stack_eas_help.test.mjs +72 -0
  160. package/scripts/stack_editor_workspace_monorepo_root.test.mjs +102 -0
  161. package/scripts/stack_env_cmd.test.mjs +107 -0
  162. package/scripts/stack_guided_login_bundle_error_parse.test.mjs +20 -0
  163. package/scripts/stack_guided_login_inner_invocation.test.mjs +46 -0
  164. package/scripts/stack_happy_cmd.integration.test.mjs +263 -0
  165. package/scripts/stack_info_snapshot_running_status.test.mjs +186 -0
  166. package/scripts/stack_interactive_monorepo_group.test.mjs +128 -0
  167. package/scripts/stack_monorepo_defaults.test.mjs +31 -0
  168. package/scripts/stack_monorepo_repo_dev_token.test.mjs +32 -0
  169. package/scripts/stack_monorepo_server_light_from_happy_spec.test.mjs +37 -0
  170. package/scripts/stack_new_name_normalize_cmd.test.mjs +38 -0
  171. package/scripts/stack_pr_name_normalize_cmd.test.mjs +84 -0
  172. package/scripts/stack_resume_cmd.integration.test.mjs +134 -0
  173. package/scripts/stack_server_flavors_defaults.test.mjs +64 -0
  174. package/scripts/stack_shorthand_cmd.integration.test.mjs +74 -0
  175. package/scripts/stack_stop_sweeps_legacy_infra_without_kind.integration.test.mjs +44 -0
  176. package/scripts/stack_stop_sweeps_when_runtime_missing.integration.test.mjs +42 -0
  177. package/scripts/stack_stop_sweeps_when_runtime_stale.integration.test.mjs +50 -0
  178. package/scripts/stack_wt_list.test.mjs +117 -0
  179. package/scripts/start_ui_required_default.test.mjs +63 -0
  180. package/scripts/stop.mjs +190 -0
  181. package/scripts/stopStackWithEnv_no_autosweep_when_runtime_missing.integration.test.mjs +95 -0
  182. package/scripts/swiftbar_git_monorepo_cmd.test.mjs +75 -0
  183. package/scripts/swiftbar_render_monorepo_wt_actions.integration.test.mjs +116 -0
  184. package/scripts/swiftbar_utils_cmd.test.mjs +92 -0
  185. package/scripts/swiftbar_wt_pr_backcompat.test.mjs +162 -0
  186. package/scripts/systemd_unit_info.test.mjs +24 -0
  187. package/scripts/tailscale.mjs +490 -0
  188. package/scripts/test_ci.mjs +36 -0
  189. package/scripts/test_cmd.mjs +274 -0
  190. package/scripts/test_cmd.test.mjs +133 -0
  191. package/scripts/test_integration.mjs +33 -0
  192. package/scripts/testkit/auth_testkit.mjs +121 -0
  193. package/scripts/testkit/doctor_testkit.mjs +68 -0
  194. package/scripts/testkit/monorepo_port_testkit.mjs +157 -0
  195. package/scripts/testkit/stack_archive_command_testkit.mjs +55 -0
  196. package/scripts/testkit/stack_new_monorepo_testkit.mjs +83 -0
  197. package/scripts/testkit/stack_script_command_testkit.mjs +27 -0
  198. package/scripts/testkit/stack_stop_sweeps_testkit.mjs +172 -0
  199. package/scripts/testkit/worktrees_monorepo_testkit.mjs +53 -0
  200. package/scripts/tools.mjs +70 -0
  201. package/scripts/tui.mjs +914 -0
  202. package/scripts/tui_stopStackForTuiExit_no_autosweep.integration.test.mjs +95 -0
  203. package/scripts/typecheck.mjs +178 -0
  204. package/scripts/ui_gateway.mjs +247 -0
  205. package/scripts/uninstall.mjs +179 -0
  206. package/scripts/utils/auth/credentials_paths.mjs +181 -0
  207. package/scripts/utils/auth/credentials_paths.test.mjs +187 -0
  208. package/scripts/utils/auth/daemon_gate.mjs +66 -0
  209. package/scripts/utils/auth/daemon_gate.test.mjs +116 -0
  210. package/scripts/utils/auth/decode_jwt_payload_unsafe.mjs +16 -0
  211. package/scripts/utils/auth/dev_key.mjs +163 -0
  212. package/scripts/utils/auth/files.mjs +56 -0
  213. package/scripts/utils/auth/guided_pr_auth.mjs +86 -0
  214. package/scripts/utils/auth/guided_stack_web_login.mjs +56 -0
  215. package/scripts/utils/auth/handy_master_secret.mjs +42 -0
  216. package/scripts/utils/auth/interactive_stack_auth.mjs +70 -0
  217. package/scripts/utils/auth/login_ux.mjs +105 -0
  218. package/scripts/utils/auth/orchestrated_stack_auth_flow.mjs +291 -0
  219. package/scripts/utils/auth/sources.mjs +28 -0
  220. package/scripts/utils/auth/stable_scope_id.mjs +91 -0
  221. package/scripts/utils/auth/stable_scope_id.test.mjs +51 -0
  222. package/scripts/utils/auth/stack_guided_login.mjs +438 -0
  223. package/scripts/utils/cli/arg_values.mjs +23 -0
  224. package/scripts/utils/cli/arg_values.test.mjs +43 -0
  225. package/scripts/utils/cli/args.mjs +17 -0
  226. package/scripts/utils/cli/cli.mjs +24 -0
  227. package/scripts/utils/cli/cli_registry.mjs +440 -0
  228. package/scripts/utils/cli/cwd_scope.mjs +158 -0
  229. package/scripts/utils/cli/cwd_scope.test.mjs +154 -0
  230. package/scripts/utils/cli/flags.mjs +17 -0
  231. package/scripts/utils/cli/log_forwarder.mjs +157 -0
  232. package/scripts/utils/cli/normalize.mjs +16 -0
  233. package/scripts/utils/cli/prereqs.mjs +103 -0
  234. package/scripts/utils/cli/prereqs.test.mjs +33 -0
  235. package/scripts/utils/cli/progress.mjs +141 -0
  236. package/scripts/utils/cli/smoke_help.mjs +44 -0
  237. package/scripts/utils/cli/verbosity.mjs +11 -0
  238. package/scripts/utils/cli/wizard.mjs +139 -0
  239. package/scripts/utils/cli/wizard_promptSelect.test.mjs +44 -0
  240. package/scripts/utils/cli/wizard_prompt_worktree_source_lazy.test.mjs +132 -0
  241. package/scripts/utils/cli/wizard_worktree_slug.test.mjs +33 -0
  242. package/scripts/utils/crypto/tokens.mjs +14 -0
  243. package/scripts/utils/dev/daemon.mjs +232 -0
  244. package/scripts/utils/dev/daemon_watch_resilience.test.mjs +224 -0
  245. package/scripts/utils/dev/expo_dev.buildEnv.test.mjs +35 -0
  246. package/scripts/utils/dev/expo_dev.mjs +478 -0
  247. package/scripts/utils/dev/expo_dev.test.mjs +89 -0
  248. package/scripts/utils/dev/expo_dev_restart_port_reservation.test.mjs +120 -0
  249. package/scripts/utils/dev/expo_dev_verbose_logs.test.mjs +60 -0
  250. package/scripts/utils/dev/server.mjs +180 -0
  251. package/scripts/utils/dev_auth_key.mjs +7 -0
  252. package/scripts/utils/edison/git_roots.mjs +30 -0
  253. package/scripts/utils/edison/git_roots.test.mjs +49 -0
  254. package/scripts/utils/env/config.mjs +52 -0
  255. package/scripts/utils/env/dotenv.mjs +32 -0
  256. package/scripts/utils/env/dotenv.test.mjs +32 -0
  257. package/scripts/utils/env/env.mjs +130 -0
  258. package/scripts/utils/env/env_file.mjs +98 -0
  259. package/scripts/utils/env/env_file.test.mjs +49 -0
  260. package/scripts/utils/env/env_local.mjs +25 -0
  261. package/scripts/utils/env/load_env_file.mjs +34 -0
  262. package/scripts/utils/env/read.mjs +30 -0
  263. package/scripts/utils/env/sandbox.mjs +13 -0
  264. package/scripts/utils/env/scrub_env.mjs +69 -0
  265. package/scripts/utils/env/scrub_env.test.mjs +102 -0
  266. package/scripts/utils/env/values.mjs +13 -0
  267. package/scripts/utils/expo/command.mjs +65 -0
  268. package/scripts/utils/expo/expo.mjs +139 -0
  269. package/scripts/utils/expo/expo_state_running.test.mjs +48 -0
  270. package/scripts/utils/expo/metro_ports.mjs +101 -0
  271. package/scripts/utils/expo/metro_ports.test.mjs +35 -0
  272. package/scripts/utils/fs/atomic_dir_swap.mjs +55 -0
  273. package/scripts/utils/fs/atomic_dir_swap.test.mjs +54 -0
  274. package/scripts/utils/fs/file_has_content.mjs +10 -0
  275. package/scripts/utils/fs/fs.mjs +11 -0
  276. package/scripts/utils/fs/json.mjs +25 -0
  277. package/scripts/utils/fs/ops.mjs +29 -0
  278. package/scripts/utils/fs/package_json.mjs +8 -0
  279. package/scripts/utils/fs/tail.mjs +12 -0
  280. package/scripts/utils/git/dev_checkout.mjs +127 -0
  281. package/scripts/utils/git/dev_checkout.test.mjs +115 -0
  282. package/scripts/utils/git/git.mjs +67 -0
  283. package/scripts/utils/git/parse_name_status_z.mjs +21 -0
  284. package/scripts/utils/git/refs.mjs +26 -0
  285. package/scripts/utils/git/worktrees.mjs +323 -0
  286. package/scripts/utils/git/worktrees_monorepo.test.mjs +60 -0
  287. package/scripts/utils/git/worktrees_pathstyle.test.mjs +53 -0
  288. package/scripts/utils/llm/assist.mjs +260 -0
  289. package/scripts/utils/llm/codex_exec.mjs +61 -0
  290. package/scripts/utils/llm/codex_exec.test.mjs +46 -0
  291. package/scripts/utils/llm/hstack_runner.mjs +59 -0
  292. package/scripts/utils/llm/tools.mjs +56 -0
  293. package/scripts/utils/llm/tools.test.mjs +67 -0
  294. package/scripts/utils/menubar/swiftbar.mjs +121 -0
  295. package/scripts/utils/menubar/swiftbar.test.mjs +85 -0
  296. package/scripts/utils/mobile/config.mjs +35 -0
  297. package/scripts/utils/mobile/dev_client_links.mjs +59 -0
  298. package/scripts/utils/mobile/identifiers.mjs +46 -0
  299. package/scripts/utils/mobile/identifiers.test.mjs +41 -0
  300. package/scripts/utils/mobile/ios_xcodeproj_patch.mjs +128 -0
  301. package/scripts/utils/mobile/ios_xcodeproj_patch.test.mjs +131 -0
  302. package/scripts/utils/net/bind_mode.mjs +39 -0
  303. package/scripts/utils/net/dns.mjs +10 -0
  304. package/scripts/utils/net/lan_ip.mjs +24 -0
  305. package/scripts/utils/net/ports.mjs +110 -0
  306. package/scripts/utils/net/tcp_forward.mjs +162 -0
  307. package/scripts/utils/net/url.mjs +30 -0
  308. package/scripts/utils/net/url.test.mjs +29 -0
  309. package/scripts/utils/paths/canonical_home.mjs +15 -0
  310. package/scripts/utils/paths/canonical_home.test.mjs +28 -0
  311. package/scripts/utils/paths/localhost_host.mjs +112 -0
  312. package/scripts/utils/paths/localhost_host.test.mjs +58 -0
  313. package/scripts/utils/paths/paths.mjs +302 -0
  314. package/scripts/utils/paths/paths_env_win32.test.mjs +36 -0
  315. package/scripts/utils/paths/paths_monorepo.test.mjs +58 -0
  316. package/scripts/utils/paths/paths_server_flavors.test.mjs +50 -0
  317. package/scripts/utils/paths/runtime.mjs +41 -0
  318. package/scripts/utils/pglite_lock.mjs +107 -0
  319. package/scripts/utils/proc/commands.mjs +33 -0
  320. package/scripts/utils/proc/exit_cleanup.mjs +57 -0
  321. package/scripts/utils/proc/happy_monorepo_deps.mjs +37 -0
  322. package/scripts/utils/proc/happy_monorepo_deps.test.mjs +89 -0
  323. package/scripts/utils/proc/ownership.mjs +217 -0
  324. package/scripts/utils/proc/ownership_killProcessGroupOwnedByStack.test.mjs +216 -0
  325. package/scripts/utils/proc/ownership_listPidsWithEnvNeedles.test.mjs +88 -0
  326. package/scripts/utils/proc/package_scripts.mjs +38 -0
  327. package/scripts/utils/proc/package_scripts.test.mjs +58 -0
  328. package/scripts/utils/proc/parallel.mjs +25 -0
  329. package/scripts/utils/proc/pids.mjs +11 -0
  330. package/scripts/utils/proc/pm.mjs +478 -0
  331. package/scripts/utils/proc/pm_spawn.integration.test.mjs +131 -0
  332. package/scripts/utils/proc/pm_stack_cache_env.test.mjs +313 -0
  333. package/scripts/utils/proc/proc.mjs +331 -0
  334. package/scripts/utils/proc/proc.test.mjs +85 -0
  335. package/scripts/utils/proc/terminate.mjs +69 -0
  336. package/scripts/utils/proc/terminate.test.mjs +54 -0
  337. package/scripts/utils/proc/watch.mjs +63 -0
  338. package/scripts/utils/review/augment_runner_integration.test.mjs +105 -0
  339. package/scripts/utils/review/base_ref.mjs +82 -0
  340. package/scripts/utils/review/base_ref.test.mjs +89 -0
  341. package/scripts/utils/review/chunks.mjs +55 -0
  342. package/scripts/utils/review/chunks.test.mjs +107 -0
  343. package/scripts/utils/review/detached_worktree.mjs +61 -0
  344. package/scripts/utils/review/detached_worktree.test.mjs +61 -0
  345. package/scripts/utils/review/findings.mjs +278 -0
  346. package/scripts/utils/review/findings.test.mjs +203 -0
  347. package/scripts/utils/review/head_slice.mjs +132 -0
  348. package/scripts/utils/review/head_slice.test.mjs +117 -0
  349. package/scripts/utils/review/instructions/deep.md +20 -0
  350. package/scripts/utils/review/prompts.mjs +279 -0
  351. package/scripts/utils/review/prompts.test.mjs +77 -0
  352. package/scripts/utils/review/run_reviewers_safe.mjs +12 -0
  353. package/scripts/utils/review/run_reviewers_safe.test.mjs +45 -0
  354. package/scripts/utils/review/runners/augment.mjs +91 -0
  355. package/scripts/utils/review/runners/augment.test.mjs +64 -0
  356. package/scripts/utils/review/runners/claude.mjs +92 -0
  357. package/scripts/utils/review/runners/claude.test.mjs +47 -0
  358. package/scripts/utils/review/runners/coderabbit.mjs +105 -0
  359. package/scripts/utils/review/runners/coderabbit.test.mjs +32 -0
  360. package/scripts/utils/review/runners/codex.mjs +129 -0
  361. package/scripts/utils/review/runners/codex.test.mjs +115 -0
  362. package/scripts/utils/review/slice_mode.mjs +20 -0
  363. package/scripts/utils/review/slice_mode.test.mjs +69 -0
  364. package/scripts/utils/review/sliced_runner.mjs +39 -0
  365. package/scripts/utils/review/sliced_runner.test.mjs +57 -0
  366. package/scripts/utils/review/slices.mjs +140 -0
  367. package/scripts/utils/review/slices.test.mjs +41 -0
  368. package/scripts/utils/review/targets.mjs +23 -0
  369. package/scripts/utils/review/targets.test.mjs +31 -0
  370. package/scripts/utils/review/tool_home_seed.mjs +106 -0
  371. package/scripts/utils/review/tool_home_seed.test.mjs +124 -0
  372. package/scripts/utils/review/uncommitted_ops.mjs +77 -0
  373. package/scripts/utils/review/uncommitted_ops.test.mjs +117 -0
  374. package/scripts/utils/sandbox/review_pr_sandbox.mjs +105 -0
  375. package/scripts/utils/server/apply_server_light_env_defaults.mjs +14 -0
  376. package/scripts/utils/server/flavor_scripts.mjs +138 -0
  377. package/scripts/utils/server/flavor_scripts.test.mjs +115 -0
  378. package/scripts/utils/server/infra/happy_server_infra.mjs +444 -0
  379. package/scripts/utils/server/mobile_api_url.mjs +60 -0
  380. package/scripts/utils/server/mobile_api_url.test.mjs +58 -0
  381. package/scripts/utils/server/port.mjs +55 -0
  382. package/scripts/utils/server/prisma_import.mjs +36 -0
  383. package/scripts/utils/server/prisma_import.test.mjs +78 -0
  384. package/scripts/utils/server/server.mjs +109 -0
  385. package/scripts/utils/server/ui_build_check.mjs +37 -0
  386. package/scripts/utils/server/ui_build_check.test.mjs +70 -0
  387. package/scripts/utils/server/ui_env.mjs +13 -0
  388. package/scripts/utils/server/ui_env.test.mjs +57 -0
  389. package/scripts/utils/server/urls.mjs +100 -0
  390. package/scripts/utils/server/validate.mjs +60 -0
  391. package/scripts/utils/server/validate.test.mjs +76 -0
  392. package/scripts/utils/service/autostart_darwin.mjs +198 -0
  393. package/scripts/utils/service/autostart_darwin.test.mjs +49 -0
  394. package/scripts/utils/service/autostart_darwin_keepalive.test.mjs +19 -0
  395. package/scripts/utils/stack/cli_identities.mjs +29 -0
  396. package/scripts/utils/stack/context.mjs +19 -0
  397. package/scripts/utils/stack/dirs.mjs +26 -0
  398. package/scripts/utils/stack/editor_workspace.mjs +126 -0
  399. package/scripts/utils/stack/interactive_stack_config.mjs +266 -0
  400. package/scripts/utils/stack/interactive_stack_config.port_validation.test.mjs +93 -0
  401. package/scripts/utils/stack/interactive_stack_config.remote_validation.test.mjs +122 -0
  402. package/scripts/utils/stack/interactive_stack_config.stack_name_validation.test.mjs +76 -0
  403. package/scripts/utils/stack/interactive_stack_config_testkit.mjs +18 -0
  404. package/scripts/utils/stack/names.mjs +27 -0
  405. package/scripts/utils/stack/names.test.mjs +26 -0
  406. package/scripts/utils/stack/pr_stack_name.mjs +16 -0
  407. package/scripts/utils/stack/runtime_state.mjs +88 -0
  408. package/scripts/utils/stack/stacks.mjs +40 -0
  409. package/scripts/utils/stack/startup.mjs +370 -0
  410. package/scripts/utils/stack/startup_server_light_dirs.test.mjs +119 -0
  411. package/scripts/utils/stack/startup_server_light_generate.test.mjs +20 -0
  412. package/scripts/utils/stack/startup_server_light_legacy.test.mjs +79 -0
  413. package/scripts/utils/stack/startup_server_light_testkit.mjs +106 -0
  414. package/scripts/utils/stack/stop.mjs +284 -0
  415. package/scripts/utils/stack_context.mjs +1 -0
  416. package/scripts/utils/stack_runtime_state.mjs +1 -0
  417. package/scripts/utils/stacks.mjs +1 -0
  418. package/scripts/utils/tailscale/ip.mjs +116 -0
  419. package/scripts/utils/tauri/stack_overrides.mjs +22 -0
  420. package/scripts/utils/test/collect_test_files.mjs +29 -0
  421. package/scripts/utils/time/get_today_ymd.mjs +7 -0
  422. package/scripts/utils/tui/cleanup.mjs +38 -0
  423. package/scripts/utils/ui/ansi.mjs +47 -0
  424. package/scripts/utils/ui/browser.mjs +31 -0
  425. package/scripts/utils/ui/browser.test.mjs +56 -0
  426. package/scripts/utils/ui/clipboard.mjs +38 -0
  427. package/scripts/utils/ui/layout.mjs +44 -0
  428. package/scripts/utils/ui/qr.mjs +17 -0
  429. package/scripts/utils/ui/terminal_launcher.mjs +129 -0
  430. package/scripts/utils/ui/text.mjs +16 -0
  431. package/scripts/utils/update/auto_update_notice.mjs +93 -0
  432. package/scripts/utils/validate.mjs +5 -0
  433. package/scripts/where.mjs +138 -0
  434. package/scripts/worktrees.mjs +2174 -0
  435. package/scripts/worktrees_archive_cmd.integration.test.mjs +228 -0
  436. package/scripts/worktrees_cursor_monorepo_root.test.mjs +23 -0
  437. package/scripts/worktrees_list_specs_no_recurse.test.mjs +32 -0
  438. package/scripts/worktrees_monorepo_testkit.test.mjs +29 -0
  439. package/scripts/worktrees_monorepo_use_group.test.mjs +41 -0
@@ -0,0 +1,353 @@
1
+ import './utils/env/env.mjs';
2
+ import { existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { parseArgs } from './utils/cli/args.mjs';
5
+ import { run, runCapture, spawnProc } from './utils/proc/proc.mjs';
6
+ import { getComponentDir, getDefaultAutostartPaths, getRootDir } from './utils/paths/paths.mjs';
7
+ import { ensureDepsInstalled, pmExecBin, pmSpawnBin, requireDir } from './utils/proc/pm.mjs';
8
+ import { printResult, wantsHelp, wantsJson } from './utils/cli/cli.mjs';
9
+ import { ensureExpoIsolationEnv, getExpoStatePaths } from './utils/expo/expo.mjs';
10
+ import { resolveServerPortFromEnv, resolveServerUrls } from './utils/server/urls.mjs';
11
+ import { resolveMobileExpoConfig } from './utils/mobile/config.mjs';
12
+ import { resolveStackContext } from './utils/stack/context.mjs';
13
+ import { expoExec } from './utils/expo/command.mjs';
14
+ import { ensureDevExpoServer } from './utils/dev/expo_dev.mjs';
15
+ import { resolveMobileReachableServerUrl } from './utils/server/mobile_api_url.mjs';
16
+ import { patchIosXcodeProjectsForSigningAndIdentity, resolveIosAppXcodeProjects } from './utils/mobile/ios_xcodeproj_patch.mjs';
17
+
18
+ /**
19
+ * Mobile dev helper for the Happier UI Expo app (typically `apps/ui`).
20
+ *
21
+ * Goals:
22
+ * - Avoid editing upstream config files in-place.
23
+ * - Ensure the QR/deeplink opens the *dev build* even if the App Store app is installed.
24
+ *
25
+ * Usage:
26
+ * hstack mobile
27
+ * hstack mobile --host=lan
28
+ * hstack mobile --scheme=dev.happier.app.dev
29
+ * hstack mobile --no-metro
30
+ * hstack mobile --run-ios --device="Your iPhone"
31
+ */
32
+
33
+ async function main() {
34
+ const argv = process.argv.slice(2);
35
+ const { flags, kv } = parseArgs(argv);
36
+ const json = wantsJson(argv, { flags });
37
+ const restart = flags.has('--restart');
38
+
39
+ if (wantsHelp(argv, { flags })) {
40
+ printResult({
41
+ json,
42
+ data: {
43
+ flags: [
44
+ '--host=lan|localhost|tunnel',
45
+ '--port=8081',
46
+ '--scheme=<url-scheme>',
47
+ '--ios-bundle-id=<bundle-id>',
48
+ '--ios-app-name=<name>',
49
+ '--app-env=development|production',
50
+ '--prebuild [--platform=ios|all] [--clean]',
51
+ '--run-ios [--device=<id-or-name>] [--configuration=Debug|Release]',
52
+ '--metro / --no-metro',
53
+ '--restart',
54
+ '--no-signing-fix',
55
+ ],
56
+ json: true,
57
+ },
58
+ text: [
59
+ '[mobile] usage:',
60
+ ' hstack mobile [--host=lan|localhost|tunnel] [--port=8081] [--scheme=...] [--json]',
61
+ ' hstack mobile --restart # force-restart Metro for this stack/worktree',
62
+ ' hstack mobile --run-ios [--device=...] [--configuration=Debug|Release]',
63
+ ' hstack mobile --prebuild [--platform=ios|all] [--clean]',
64
+ ' hstack mobile --no-metro # just build/install (if --run-ios) without starting Metro',
65
+ '',
66
+ 'Notes:',
67
+ '- This script is designed to avoid editing upstream UI config in-place.',
68
+ '- If you explicitly set HAPPIER_STACK_SERVER_URL, it bakes that URL into the app via EXPO_PUBLIC_HAPPY_SERVER_URL.',
69
+ ].join('\n'),
70
+ });
71
+ return;
72
+ }
73
+
74
+ const rootDir = getRootDir(import.meta.url);
75
+ const uiRepoDir = getComponentDir(rootDir, 'happier-ui');
76
+ await requireDir('happier-ui', uiRepoDir);
77
+ await ensureDepsInstalled(uiRepoDir, 'happier-ui');
78
+
79
+ // Happy monorepo layouts (historical):
80
+ // - legacy: <happyDir>/expo-app (split-repo era)
81
+ // - current: <happyDir>/apps/ui (monorepo packages/)
82
+ //
83
+ // `hstack mobile` should operate on the Expo project root, not the monorepo root.
84
+ const packagesAppDir = join(uiRepoDir, 'apps', 'ui');
85
+ const legacyExpoAppDir = join(uiRepoDir, 'expo-app');
86
+ const uiDir = existsSync(join(packagesAppDir, 'app.config.js'))
87
+ ? packagesAppDir
88
+ : existsSync(join(legacyExpoAppDir, 'package.json'))
89
+ ? legacyExpoAppDir
90
+ : uiRepoDir;
91
+
92
+ async function readXcdeviceList() {
93
+ if (process.platform !== 'darwin') {
94
+ return [];
95
+ }
96
+ const raw = await runCapture('xcrun', ['xcdevice', 'list'], { cwd: uiRepoDir, env: process.env });
97
+ const start = raw.indexOf('[');
98
+ const jsonText = start >= 0 ? raw.slice(start) : raw;
99
+ const parsed = JSON.parse(jsonText);
100
+ return Array.isArray(parsed) ? parsed : [];
101
+ }
102
+
103
+ // Default to the existing dev bundle identifier, which is also registered as a URL scheme
104
+ // (Info.plist includes `dev.happier.app.dev`), so iOS will open the dev build instead of the App Store app.
105
+ const appEnv = process.env.APP_ENV ?? kv.get('--app-env') ?? 'development';
106
+ const host = kv.get('--host') ?? process.env.HAPPIER_STACK_MOBILE_HOST ?? 'lan';
107
+ const portRaw = kv.get('--port') ?? process.env.HAPPIER_STACK_MOBILE_PORT ?? '8081';
108
+ // Default behavior:
109
+ // - `hstack mobile` starts Metro and keeps running.
110
+ // - `hstack mobile --run-ios` / `hstack mobile:ios` just builds/installs and exits (unless --metro is provided).
111
+ const shouldStartMetro =
112
+ flags.has('--metro') ||
113
+ (!flags.has('--no-metro') && !flags.has('--run-ios') && !flags.has('--prebuild'));
114
+
115
+ const env = {
116
+ ...process.env,
117
+ APP_ENV: appEnv,
118
+ };
119
+
120
+ const cfgBase = resolveMobileExpoConfig({ env });
121
+ const iosAppName = (kv.get('--ios-app-name') ?? cfgBase.iosAppName ?? '').toString();
122
+ const iosBundleId = (kv.get('--ios-bundle-id') ?? cfgBase.iosBundleId ?? '').toString();
123
+ const scheme = (kv.get('--scheme') ?? cfgBase.scheme ?? iosBundleId).toString();
124
+
125
+ const autostart = getDefaultAutostartPaths();
126
+ const stackCtx = resolveStackContext({ env, autostart });
127
+ const { stackMode, runtimeStatePath, stackName, envPath } = stackCtx;
128
+
129
+ // Ensure the built iOS app registers the same scheme we use for dev-client QR links.
130
+ // (Happy app reads EXPO_APP_SCHEME in app.config.js; default remains unchanged when unset.)
131
+ env.EXPO_APP_SCHEME = scheme;
132
+ // Ensure the app display name + bundle id are consistent with what we install.
133
+ // (app.config.js keeps upstream defaults unless these are explicitly set.)
134
+ if (iosAppName && iosAppName.trim()) {
135
+ env.EXPO_APP_NAME = iosAppName.trim();
136
+ }
137
+ if (iosBundleId && iosBundleId.trim()) {
138
+ env.EXPO_APP_BUNDLE_ID = iosBundleId.trim();
139
+ }
140
+
141
+ // Always isolate Expo home + TMPDIR to avoid cross-worktree cache pollution (and to keep sandbox runs contained).
142
+ const expoPaths = getExpoStatePaths({
143
+ baseDir: autostart.baseDir,
144
+ kind: 'expo-dev',
145
+ projectDir: uiDir,
146
+ stateFileName: 'expo.state.json',
147
+ });
148
+ await ensureExpoIsolationEnv({
149
+ env,
150
+ stateDir: expoPaths.stateDir,
151
+ expoHomeDir: expoPaths.expoHomeDir,
152
+ tmpDir: expoPaths.tmpDir,
153
+ });
154
+
155
+ // Allow happy-stacks to define the default server URL baked into the app bundle.
156
+ // This is read by the app via `process.env.EXPO_PUBLIC_HAPPY_SERVER_URL`.
157
+ const serverPort = resolveServerPortFromEnv({ env, defaultPort: 3005 });
158
+ const allowEnableTailscale =
159
+ !stackMode || stackName === 'main' || (env.HAPPIER_STACK_TAILSCALE_SERVE ?? '0').toString().trim() === '1';
160
+ const resolvedUrls = await resolveServerUrls({ env, serverPort, allowEnable: allowEnableTailscale });
161
+ if (resolvedUrls.publicServerUrl && !env.EXPO_PUBLIC_HAPPY_SERVER_URL) {
162
+ env.EXPO_PUBLIC_HAPPY_SERVER_URL = resolvedUrls.publicServerUrl;
163
+ }
164
+ if (env.EXPO_PUBLIC_HAPPY_SERVER_URL) {
165
+ env.EXPO_PUBLIC_HAPPY_SERVER_URL = resolveMobileReachableServerUrl({
166
+ env,
167
+ serverUrl: env.EXPO_PUBLIC_HAPPY_SERVER_URL,
168
+ serverPort,
169
+ });
170
+ }
171
+
172
+ if (json) {
173
+ printResult({
174
+ json,
175
+ data: {
176
+ ok: true,
177
+ uiDir,
178
+ appEnv,
179
+ iosAppName,
180
+ iosBundleId,
181
+ scheme,
182
+ host,
183
+ port: portRaw,
184
+ shouldPrebuild: flags.has('--prebuild'),
185
+ shouldRunIos: flags.has('--run-ios'),
186
+ shouldStartMetro,
187
+ expoPublicHappyServerUrl: env.EXPO_PUBLIC_HAPPY_SERVER_URL ?? '',
188
+ },
189
+ });
190
+ return;
191
+ }
192
+
193
+ const shouldPrebuild = flags.has('--prebuild');
194
+ if (shouldPrebuild) {
195
+ const platform = kv.get('--platform') ?? 'ios';
196
+ const shouldClean = flags.has('--clean');
197
+ // Prebuild can fail during `pod install` if deployment target mismatches.
198
+ // We skip installs, patch deployment target + RN build mode, then run `pod install` ourselves.
199
+ const prebuildArgs = ['prebuild', '--no-install', '--platform', platform];
200
+ if (shouldClean) {
201
+ prebuildArgs.push('--clean');
202
+ }
203
+ // Run Expo from the monorepo deps (runnerDir=happyDir), but target the Expo project (projectDir=uiDir).
204
+ await expoExec({ dir: happyDir, projectDir: uiDir, args: prebuildArgs, env, ensureDepsLabel: 'happy' });
205
+
206
+ // Always patch iOS props if iOS was generated.
207
+ if (platform === 'ios' || platform === 'all') {
208
+ const fs = await import('node:fs/promises');
209
+ const podPropsPath = `${uiDir}/ios/Podfile.properties.json`;
210
+ try {
211
+ const raw = await fs.readFile(podPropsPath, 'utf-8');
212
+ const json = JSON.parse(raw);
213
+ json['ios.deploymentTarget'] = '16.0';
214
+ json['ios.buildReactNativeFromSource'] = 'true';
215
+ await fs.writeFile(podPropsPath, JSON.stringify(json, null, 2) + '\n', 'utf-8');
216
+ } catch {
217
+ // ignore if path missing (platform != ios)
218
+ }
219
+
220
+ const iosProjects = await resolveIosAppXcodeProjects({ uiDir });
221
+ for (const project of iosProjects) {
222
+ try {
223
+ const raw = await fs.readFile(project.pbxprojPath, 'utf-8');
224
+ const next = raw.replaceAll('IPHONEOS_DEPLOYMENT_TARGET = 15.1;', 'IPHONEOS_DEPLOYMENT_TARGET = 16.0;');
225
+ if (next !== raw) {
226
+ await fs.writeFile(project.pbxprojPath, next, 'utf-8');
227
+ }
228
+ } catch {
229
+ // ignore missing/invalid pbxproj; Expo will surface actionable errors if needed
230
+ }
231
+ }
232
+
233
+ // Ensure CocoaPods doesn't crash due to locale issues.
234
+ env.LANG = env.LANG ?? 'en_US.UTF-8';
235
+ env.LC_ALL = env.LC_ALL ?? 'en_US.UTF-8';
236
+
237
+ // Help Node module resolution during `pod install` in Yarn workspace layouts:
238
+ // some deps are hoisted to repo root while `react-native` is not, so Node invocations
239
+ // from hoisted packages can fail to resolve `react-native/package.json`.
240
+ const appNodeModulesDir = join(uiDir, 'node_modules');
241
+ if (existsSync(appNodeModulesDir)) {
242
+ const delim = process.platform === 'win32' ? ';' : ':';
243
+ env.NODE_PATH = env.NODE_PATH ? `${appNodeModulesDir}${delim}${env.NODE_PATH}` : appNodeModulesDir;
244
+ }
245
+
246
+ // CocoaPods repo state can be stale on first runs; `--repo-update` fixes missing specs.
247
+ const podCmd = shouldClean ? 'cd ios && pod install --repo-update' : 'cd ios && pod install';
248
+ await run('sh', ['-lc', podCmd], { cwd: uiDir, env });
249
+ }
250
+ }
251
+
252
+ if (flags.has('--run-ios')) {
253
+ let device = kv.get('--device') ?? '';
254
+ let resolvedDevice = null;
255
+ if (process.platform === 'darwin') {
256
+ try {
257
+ const list = await readXcdeviceList();
258
+ resolvedDevice = device
259
+ ? list.find((d) => d && (d.identifier === device || d.name === device)) ?? null
260
+ : null;
261
+ } catch {
262
+ resolvedDevice = null;
263
+ }
264
+ }
265
+
266
+ if (!device && process.platform === 'darwin') {
267
+ // Auto-pick a connected physical iPhone/iPad if available.
268
+ // This avoids needing to know the exact "Your iPhone" string.
269
+ try {
270
+ const list = await readXcdeviceList();
271
+ const firstConnectedIosDevice = Array.isArray(list)
272
+ ? list.find(
273
+ (d) =>
274
+ d &&
275
+ d.platform === 'com.apple.platform.iphoneos' &&
276
+ d.interface === 'usb' &&
277
+ (d.available === true || d.available === 'YES') &&
278
+ typeof d.identifier === 'string' &&
279
+ d.identifier.length > 0
280
+ )
281
+ : null;
282
+ if (firstConnectedIosDevice?.identifier) {
283
+ device = firstConnectedIosDevice.identifier;
284
+ resolvedDevice = firstConnectedIosDevice;
285
+ // eslint-disable-next-line no-console
286
+ console.log(`[mobile] using connected device: ${firstConnectedIosDevice.name} (${device})`);
287
+ }
288
+ } catch {
289
+ // ignore and let Expo choose
290
+ }
291
+ }
292
+
293
+ const isPhysicalIosDevice =
294
+ resolvedDevice?.platform === 'com.apple.platform.iphoneos' && resolvedDevice?.simulator === false;
295
+
296
+ const shouldPatchXcodeProject = isPhysicalIosDevice || !!iosAppName;
297
+ if (shouldPatchXcodeProject && !flags.has('--no-signing-fix')) {
298
+ // Expo CLI only passes `-allowProvisioningUpdates` when it *needs* to configure signing.
299
+ // If the pbxproj already has a DEVELOPMENT_TEAM set but no local provisioning profile exists yet,
300
+ // xcodebuild fails with:
301
+ // "Automatic signing is disabled ... pass -allowProvisioningUpdates"
302
+ //
303
+ // We force Expo CLI to go through its signing configuration path by clearing any pre-existing
304
+ // team/profile identifiers, so it will re-set the team and include the provisioning flags.
305
+ await patchIosXcodeProjectsForSigningAndIdentity({ uiDir, iosBundleId, iosAppName });
306
+ }
307
+
308
+ const configuration = kv.get('--configuration') ?? 'Debug';
309
+ const args = ['run:ios', '--no-bundler', '--no-build-cache', '--configuration', configuration];
310
+ if (device) {
311
+ args.push('-d', device);
312
+ }
313
+ // Ensure CocoaPods doesn't crash due to locale issues.
314
+ env.LANG = env.LANG ?? 'en_US.UTF-8';
315
+ env.LC_ALL = env.LC_ALL ?? 'en_US.UTF-8';
316
+ await expoExec({ dir: happyDir, projectDir: uiDir, args, env, ensureDepsLabel: 'happy' });
317
+ }
318
+
319
+ if (!shouldStartMetro) {
320
+ return;
321
+ }
322
+
323
+ // Unify Expo: one Expo dev server per stack/worktree. If dev mode already started Expo, we reuse it.
324
+ // If Expo is already running without dev-client enabled, we fail closed (no second Expo).
325
+ env.HAPPIER_STACK_EXPO_HOST = host;
326
+ env.HAPPIER_STACK_MOBILE_HOST = host;
327
+ env.HAPPIER_STACK_MOBILE_SCHEME = scheme;
328
+ env.HAPPIER_STACK_EXPO_DEV_PORT = String(portRaw);
329
+
330
+ const children = [];
331
+ await ensureDevExpoServer({
332
+ startUi: false,
333
+ startMobile: true,
334
+ uiDir: happyDir,
335
+ expoProjectDir: uiDir,
336
+ autostart,
337
+ baseEnv: env,
338
+ apiServerUrl: env.EXPO_PUBLIC_HAPPY_SERVER_URL ?? '',
339
+ restart,
340
+ stackMode,
341
+ runtimeStatePath,
342
+ stackName,
343
+ envPath,
344
+ children,
345
+ });
346
+
347
+ await new Promise(() => {});
348
+ }
349
+
350
+ main().catch((err) => {
351
+ console.error('[mobile] failed:', err);
352
+ process.exit(1);
353
+ });
@@ -0,0 +1,87 @@
1
+ import './utils/env/env.mjs';
2
+ import { parseArgs } from './utils/cli/args.mjs';
3
+ import { printResult, wantsHelp, wantsJson } from './utils/cli/cli.mjs';
4
+ import { run } from './utils/proc/proc.mjs';
5
+ import { getRootDir } from './utils/paths/paths.mjs';
6
+ import { join } from 'node:path';
7
+ import { banner, cmd, sectionTitle } from './utils/ui/layout.mjs';
8
+ import { cyan, dim, yellow } from './utils/ui/ansi.mjs';
9
+
10
+ import { defaultDevClientIdentity } from './utils/mobile/identifiers.mjs';
11
+
12
+ async function main() {
13
+ const argv = process.argv.slice(2);
14
+ const { flags, kv } = parseArgs(argv);
15
+ const json = wantsJson(argv, { flags });
16
+
17
+ if (wantsHelp(argv, { flags }) || flags.has('--help') || argv.length === 0) {
18
+ printResult({
19
+ json,
20
+ data: {
21
+ flags: ['--device=<id-or-name>', '--clean', '--configuration=Debug|Release', '--json'],
22
+ },
23
+ text: [
24
+ banner('mobile-dev-client', { subtitle: 'Install the shared iOS dev-client app (one-time).' }),
25
+ '',
26
+ sectionTitle('usage:'),
27
+ ` ${cyan('hstack mobile-dev-client')} --install [--device=...] [--clean] [--configuration=Debug|Release] [--json]`,
28
+ '',
29
+ sectionTitle('notes:'),
30
+ `- Installs a dedicated ${cyan('hstack Dev')} Expo dev-client app on your iPhone.`,
31
+ `- This app is intended to be ${cyan('reused across stacks')} (no per-stack installs).`,
32
+ `- Requires ${yellow('Xcode')} + ${yellow('CocoaPods')} (macOS).`,
33
+ ].join('\n'),
34
+ });
35
+ return;
36
+ }
37
+
38
+ if (!flags.has('--install')) {
39
+ printResult({
40
+ json,
41
+ data: { ok: false, error: 'missing_install_flag' },
42
+ text: `${yellow('!')} missing ${cyan('--install')}. Run: ${cmd('hstack mobile-dev-client --help')}`,
43
+ });
44
+ process.exit(1);
45
+ }
46
+
47
+ const rootDir = getRootDir(import.meta.url);
48
+ const mobileScript = join(rootDir, 'scripts', 'mobile.mjs');
49
+
50
+ const device = kv.get('--device') ?? '';
51
+ const clean = flags.has('--clean');
52
+ const configuration = kv.get('--configuration') ?? 'Debug';
53
+
54
+ const id = defaultDevClientIdentity({ user: process.env.USER ?? process.env.USERNAME ?? 'user' });
55
+
56
+ const args = [
57
+ mobileScript,
58
+ '--app-env=development',
59
+ `--ios-app-name=${id.iosAppName}`,
60
+ `--ios-bundle-id=${id.iosBundleId}`,
61
+ `--scheme=${id.scheme}`,
62
+ '--prebuild',
63
+ ...(clean ? ['--clean'] : []),
64
+ '--run-ios',
65
+ `--configuration=${configuration}`,
66
+ '--no-metro',
67
+ ...(device ? [`--device=${device}`] : []),
68
+ ];
69
+
70
+ const env = {
71
+ ...process.env,
72
+ // Ensure Expo app config uses the dev-client scheme.
73
+ EXPO_APP_SCHEME: id.scheme,
74
+ // Ensure per-stack storage isolation is available during dev-client usage.
75
+ EXPO_PUBLIC_HAPPY_STORAGE_SCOPE: process.env.EXPO_PUBLIC_HAPPY_STORAGE_SCOPE ?? '',
76
+ };
77
+
78
+ const out = await run(process.execPath, args, { cwd: rootDir, env });
79
+ if (json) {
80
+ printResult({ json, data: { ok: true, installed: true, identity: id, out } });
81
+ }
82
+ }
83
+
84
+ main().catch((err) => {
85
+ console.error('[mobile-dev-client] failed:', err);
86
+ process.exit(1);
87
+ });