@happier-dev/stack 0.1.0-preview.74.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 +138 -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 +74 -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,717 @@
1
+ #!/bin/bash
2
+
3
+ shorten_text() {
4
+ local s="$1"
5
+ local max="${2:-44}"
6
+ if [[ ${#s} -le $max ]]; then
7
+ echo "$s"
8
+ return
9
+ fi
10
+ echo "${s:0:$((max - 3))}..."
11
+ }
12
+
13
+ shorten_path() {
14
+ local p="$1"
15
+ local max="${2:-44}"
16
+ local pretty="${p/#$HOME/~}"
17
+ shorten_text "$pretty" "$max"
18
+ }
19
+
20
+ swiftbar_sum_metrics_cpu_mem() {
21
+ # Usage: swiftbar_sum_metrics_cpu_mem <metrics...>
22
+ # Each metrics is "cpu|mem_mb|etime" (as produced by get_process_metrics), or empty/"-".
23
+ #
24
+ # Output: "cpu_total|mem_total" where cpu_total has 1 decimal.
25
+ # Notes:
26
+ # - CPU is summed as a float and can exceed 100 on multi-core machines.
27
+ # - Memory is summed as integer MB.
28
+ printf '%s\n' "$@" | awk -F'|' '
29
+ {
30
+ cpu=$1; mem=$2;
31
+ if (cpu ~ /^[0-9]+(\.[0-9]+)?$/) cpuSum += cpu;
32
+ if (mem ~ /^[0-9]+$/) memSum += mem;
33
+ }
34
+ END {
35
+ # Always print 1 decimal for CPU to keep it stable/readable.
36
+ printf "%.1f|%.0f\n", cpuSum + 0.0, memSum + 0.0;
37
+ }
38
+ '
39
+ }
40
+
41
+ swiftbar_worktree_spec_from_path() {
42
+ # Usage: swiftbar_worktree_spec_from_path <worktree_path> <repo_key>
43
+ #
44
+ # Returns:
45
+ # - "main" for the managed stable checkout at: */workspace/main
46
+ # - "dev" for the managed dev checkout at: */workspace/dev
47
+ # - "<category>/<...>" for managed worktrees at: */workspace/{pr,local,tmp}/...
48
+ # - "" otherwise (unmanaged path; menu should offer open-only actions)
49
+ local wt_path="$1"
50
+ local repo_key="$2"
51
+ [[ -n "$wt_path" && -n "$repo_key" ]] || { echo ""; return; }
52
+
53
+ local workspace
54
+ workspace="$(resolve_workspace_dir)"
55
+ local main_repo="${workspace}/main"
56
+ local dev_repo="${workspace}/dev"
57
+
58
+ if [[ "$wt_path" == "$main_repo" || "$wt_path" == "$main_repo/"* ]]; then
59
+ echo "main"
60
+ return
61
+ fi
62
+ if [[ "$wt_path" == "$dev_repo" || "$wt_path" == "$dev_repo/"* ]]; then
63
+ echo "dev"
64
+ return
65
+ fi
66
+
67
+ local cat
68
+ for cat in pr local tmp; do
69
+ local root="${workspace}/${cat}"
70
+ if [[ "$wt_path" == "$root/"* ]]; then
71
+ local rest="${wt_path#"$workspace/"}"
72
+ echo "$rest"
73
+ return
74
+ fi
75
+ done
76
+ echo ""
77
+ }
78
+
79
+ swiftbar_find_git_root_upwards() {
80
+ # Usage: swiftbar_find_git_root_upwards <path>
81
+ # Returns the nearest ancestor directory (including itself) that contains a .git dir/file.
82
+ #
83
+ # This is a fast filesystem-based check used during menu render, mainly to:
84
+ # - avoid repeating a monorepo root path for package dirs (expo-app/cli/server)
85
+ # - derive the "repo root" for "open repo root" actions
86
+ local start="$1"
87
+ [[ -n "$start" ]] || { echo ""; return; }
88
+ [[ -d "$start" ]] || { echo ""; return; }
89
+
90
+ local cur="$start"
91
+ local i=0
92
+ while [[ -n "$cur" && "$cur" != "/" && $i -lt 25 ]]; do
93
+ if [[ -d "$cur/.git" || -f "$cur/.git" ]]; then
94
+ echo "$cur"
95
+ return
96
+ fi
97
+ cur="$(dirname "$cur")"
98
+ i=$((i + 1))
99
+ done
100
+ if [[ "$cur" == "/" && ( -d "/.git" || -f "/.git" ) ]]; then
101
+ echo "/"
102
+ return
103
+ fi
104
+ echo ""
105
+ }
106
+
107
+ swiftbar_repo_key_from_path() {
108
+ # Usage: swiftbar_repo_key_from_path <path>
109
+ #
110
+ # Heuristic for hstack layouts:
111
+ # - Checkouts: */workspace/{main,dev}
112
+ # - Worktrees: */workspace/{pr,local,tmp}/...
113
+ #
114
+ # Returns "" when it can't be derived (unmanaged path).
115
+ local p="$1"
116
+ [[ -n "$p" ]] || { echo ""; return; }
117
+
118
+ local workspace
119
+ workspace="$(resolve_workspace_dir)"
120
+ if [[ "$p" == "${workspace}/main"* || "$p" == "${workspace}/dev"* || "$p" == "${workspace}/pr/"* || "$p" == "${workspace}/local/"* || "$p" == "${workspace}/tmp/"* ]]; then
121
+ # Kept as a stable identifier for renderers; effectively "the monorepo".
122
+ echo "happier"
123
+ return
124
+ fi
125
+
126
+ echo ""
127
+ }
128
+
129
+ swiftbar_is_sandboxed() {
130
+ [[ -n "${HAPPIER_STACK_SANDBOX_DIR:-}" ]]
131
+ }
132
+
133
+ swiftbar_profile_enabled() {
134
+ [[ "${HAPPIER_STACK_SWIFTBAR_PROFILE:-}" == "1" ]]
135
+ }
136
+
137
+ swiftbar_now_ms() {
138
+ # macOS `date` doesn't support %N, so use Time::HiRes when available.
139
+ if command -v perl >/dev/null 2>&1; then
140
+ perl -MTime::HiRes=time -e 'printf("%.0f\n", time()*1000)'
141
+ return
142
+ fi
143
+ if command -v python3 >/dev/null 2>&1; then
144
+ python3 -c 'import time; print(int(time.time()*1000))'
145
+ return
146
+ fi
147
+ # Fallback: seconds granularity.
148
+ echo $(( $(date +%s 2>/dev/null || echo 0) * 1000 ))
149
+ }
150
+
151
+ swiftbar_profile_log_file() {
152
+ # Keep logs in the home install by default (stable across repos/worktrees).
153
+ local home
154
+ home="$(resolve_home_dir)"
155
+ echo "${home}/cache/swiftbar/profile.log"
156
+ }
157
+
158
+ swiftbar_profile_log() {
159
+ # Usage: swiftbar_profile_log "event" "k=v" "k2=v2" ...
160
+ swiftbar_profile_enabled || return 0
161
+
162
+ local log_file
163
+ log_file="$(swiftbar_profile_log_file)"
164
+ mkdir -p "$(dirname "$log_file")" 2>/dev/null || true
165
+
166
+ local ts
167
+ ts="$(swiftbar_now_ms)"
168
+ {
169
+ printf '%s\t%s' "$ts" "$1"
170
+ shift || true
171
+ for kv in "$@"; do
172
+ printf '\t%s' "$kv"
173
+ done
174
+ printf '\n'
175
+ } >>"$log_file" 2>/dev/null || true
176
+ }
177
+
178
+ swiftbar_profile_time() {
179
+ # Usage: swiftbar_profile_time <label> -- <command...>
180
+ swiftbar_profile_enabled || { shift; [[ "${1:-}" == "--" ]] && shift; "$@"; return $?; }
181
+ local label="$1"
182
+ shift
183
+ [[ "${1:-}" == "--" ]] && shift
184
+
185
+ local t0 t1 rc
186
+ t0="$(swiftbar_now_ms)"
187
+ "$@"
188
+ rc=$?
189
+ t1="$(swiftbar_now_ms)"
190
+ swiftbar_profile_log "time" "label=${label}" "ms=$((t1 - t0))" "rc=${rc}"
191
+ return $rc
192
+ }
193
+
194
+ swiftbar_cache_hash12() {
195
+ # Usage: swiftbar_cache_hash12 "string"
196
+ local s="$1"
197
+ if command -v md5 >/dev/null 2>&1; then
198
+ md5 -q -s "$s" 2>/dev/null | head -c 12
199
+ return
200
+ fi
201
+ if command -v shasum >/dev/null 2>&1; then
202
+ printf '%s' "$s" | shasum -a 256 2>/dev/null | awk '{print substr($1,1,12)}'
203
+ return
204
+ fi
205
+ # Last resort (not cryptographic): length + a sanitized prefix.
206
+ printf '%s' "${#s}-$(echo "$s" | tr -cd '[:alnum:]' | head -c 10)"
207
+ }
208
+
209
+ swiftbar_hash() {
210
+ # Usage: swiftbar_hash "string"
211
+ local s="$1"
212
+ if command -v md5 >/dev/null 2>&1; then
213
+ md5 -q -s "$s" 2>/dev/null || true
214
+ return
215
+ fi
216
+ if command -v shasum >/dev/null 2>&1; then
217
+ printf '%s' "$s" | shasum -a 256 2>/dev/null | awk '{print $1}'
218
+ return
219
+ fi
220
+ printf '%s' "$(swiftbar_cache_hash12 "$s")"
221
+ }
222
+
223
+ swiftbar_run_cache_dir() {
224
+ # Per-process (per-refresh) cache. Avoid persisting across SwiftBar refreshes.
225
+ local base="${TMPDIR:-/tmp}"
226
+ local dir="${base%/}/hstack-swiftbar-cache-${UID:-0}-$$"
227
+ mkdir -p "$dir" 2>/dev/null || true
228
+ echo "$dir"
229
+ }
230
+
231
+ swiftbar_cache_file_for_key() {
232
+ local key="$1"
233
+ local dir
234
+ dir="$(swiftbar_run_cache_dir)"
235
+ echo "${dir}/$(swiftbar_cache_hash12 "$key").cache"
236
+ }
237
+
238
+ swiftbar_cache_get() {
239
+ # Usage: swiftbar_cache_get <key>
240
+ # Output: cached stdout (may be empty). Exit status: cached rc.
241
+ local key="$1"
242
+ local f
243
+ f="$(swiftbar_cache_file_for_key "$key")"
244
+ # Important: return a distinct code on cache-miss so callers can distinguish from cached rc=1.
245
+ [[ -f "$f" ]] || return 111
246
+ local rc_line rc
247
+ rc_line="$(head -n 1 "$f" 2>/dev/null || true)"
248
+ rc="${rc_line#rc:}"
249
+ tail -n +2 "$f" 2>/dev/null || true
250
+ [[ "$rc" =~ ^[0-9]+$ ]] || rc=0
251
+ return "$rc"
252
+ }
253
+
254
+ swiftbar_cache_set() {
255
+ # Usage: swiftbar_cache_set <key> <rc> <stdout>
256
+ local key="$1"
257
+ local rc="$2"
258
+ local out="${3:-}"
259
+ local f
260
+ f="$(swiftbar_cache_file_for_key "$key")"
261
+ {
262
+ printf 'rc:%s\n' "${rc:-0}"
263
+ printf '%s' "$out"
264
+ # Keep files line-friendly.
265
+ [[ "$out" == *$'\n' ]] || printf '\n'
266
+ } >"$f" 2>/dev/null || true
267
+ }
268
+
269
+ dotenv_get() {
270
+ # Usage: dotenv_get /path/to/env KEY
271
+ # Notes:
272
+ # - ignores blank lines and comments
273
+ # - does not expand variables
274
+ # - strips simple surrounding quotes
275
+ local file="$1"
276
+ local key="$2"
277
+ if [[ -z "$file" ]] || [[ -z "$key" ]] || [[ ! -f "$file" ]]; then
278
+ return
279
+ fi
280
+ awk -v k="$key" '
281
+ BEGIN { FS="=" }
282
+ /^[[:space:]]*$/ { next }
283
+ /^[[:space:]]*#/ { next }
284
+ {
285
+ kk=$1
286
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", kk)
287
+ if (kk != k) next
288
+
289
+ vv=$0
290
+ sub(/^[^=]*=/, "", vv)
291
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", vv)
292
+
293
+ if (vv ~ /^".*"$/) { sub(/^"/, "", vv); sub(/"$/, "", vv) }
294
+ if (vv ~ /^'\''.*'\''$/) { sub(/^'\''/, "", vv); sub(/'\''$/, "", vv) }
295
+
296
+ print vv
297
+ exit
298
+ }
299
+ ' "$file" 2>/dev/null
300
+ }
301
+
302
+ expand_home_path() {
303
+ local p="$1"
304
+ if [[ "$p" == "~/"* ]]; then
305
+ echo "$HOME/${p#~/}"
306
+ return
307
+ fi
308
+ echo "$p"
309
+ }
310
+
311
+ resolve_canonical_home_dir() {
312
+ local canonical="${HAPPIER_STACK_CANONICAL_HOME_DIR:-$HOME/.happier-stack}"
313
+ echo "$(expand_home_path "$canonical")"
314
+ }
315
+
316
+ resolve_home_dir() {
317
+ local from_env="${HAPPIER_STACK_HOME_DIR:-}"
318
+ if [[ -n "$from_env" ]]; then
319
+ echo "$(expand_home_path "$from_env")"
320
+ return
321
+ fi
322
+
323
+ local canonical
324
+ canonical="$(resolve_canonical_home_dir)"
325
+ local env_file="${canonical}/.env"
326
+ if [[ -f "$env_file" ]]; then
327
+ local p
328
+ p="$(dotenv_get "$env_file" "HAPPIER_STACK_HOME_DIR")"
329
+ if [[ -n "$p" ]]; then
330
+ echo "$(expand_home_path "$p")"
331
+ return
332
+ fi
333
+ fi
334
+
335
+ echo "$canonical"
336
+ }
337
+
338
+ resolve_hstack_root_dir() {
339
+ # Prefer the repo root if explicitly provided (dev); otherwise use the home install root.
340
+ local cli_root="${HAPPIER_STACK_CLI_ROOT_DIR:-}"
341
+ if [[ -n "$cli_root" ]] && [[ -f "$cli_root/extras/swiftbar/lib/utils.sh" ]]; then
342
+ echo "$cli_root"
343
+ return
344
+ fi
345
+ resolve_home_dir
346
+ }
347
+
348
+ resolve_stacks_storage_root() {
349
+ # Priority:
350
+ # 1) explicit env var
351
+ # 2) home env.local
352
+ # 3) home .env (canonical pointer file, written by `hstack init`)
353
+ # 4) default to ~/.happier/stacks
354
+ if [[ -n "${HAPPIER_STACK_STORAGE_DIR:-}" ]]; then
355
+ echo "$(expand_home_path "$HAPPIER_STACK_STORAGE_DIR")"
356
+ return
357
+ fi
358
+
359
+ local root
360
+ root="$(resolve_hstack_root_dir)"
361
+
362
+ local p
363
+ p="$(dotenv_get "$root/env.local" "HAPPIER_STACK_STORAGE_DIR")"
364
+ [[ -z "$p" ]] && p="$(dotenv_get "$root/.env" "HAPPIER_STACK_STORAGE_DIR")"
365
+ if [[ -n "$p" ]]; then
366
+ echo "$(expand_home_path "$p")"
367
+ return
368
+ fi
369
+
370
+ # In sandbox mode, avoid falling back to the user's real ~/.happier/stacks.
371
+ if swiftbar_is_sandboxed; then
372
+ echo "${HAPPIER_STACK_STORAGE_DIR:-${HAPPIER_STACK_SANDBOX_DIR%/}/storage}"
373
+ return
374
+ fi
375
+
376
+ echo "$HOME/.happier/stacks"
377
+ }
378
+
379
+ resolve_stack_env_file() {
380
+ local stack_name="${1:-main}"
381
+ local storage_root
382
+ storage_root="$(resolve_stacks_storage_root)"
383
+
384
+ local primary="${storage_root}/${stack_name}/env"
385
+ if [[ -f "$primary" ]]; then
386
+ echo "$primary"
387
+ return
388
+ fi
389
+
390
+ echo "$primary"
391
+ }
392
+
393
+ resolve_stack_base_dir() {
394
+ local stack_name="${1:-main}"
395
+ local env_file="${2:-}"
396
+ if [[ -z "$env_file" ]]; then
397
+ env_file="$(resolve_stack_env_file "$stack_name")"
398
+ fi
399
+ # If the env file exists, its parent directory is the stack base dir for all supported layouts.
400
+ if [[ -n "$env_file" ]] && [[ -f "$env_file" ]]; then
401
+ dirname "$env_file"
402
+ return
403
+ fi
404
+ local storage_root
405
+ storage_root="$(resolve_stacks_storage_root)"
406
+ echo "${storage_root}/${stack_name}"
407
+ }
408
+
409
+ resolve_stack_cli_home_dir() {
410
+ local stack_name="${1:-main}"
411
+ local env_file="${2:-}"
412
+ if [[ -z "$env_file" ]]; then
413
+ env_file="$(resolve_stack_env_file "$stack_name")"
414
+ fi
415
+ local cli_home=""
416
+ if [[ -n "$env_file" ]] && [[ -f "$env_file" ]]; then
417
+ cli_home="$(dotenv_get "$env_file" "HAPPIER_STACK_CLI_HOME_DIR")"
418
+ fi
419
+ if [[ -n "$cli_home" ]]; then
420
+ echo "$(expand_home_path "$cli_home")"
421
+ return
422
+ fi
423
+ local base_dir
424
+ base_dir="$(resolve_stack_base_dir "$stack_name" "$env_file")"
425
+ echo "${base_dir}/cli"
426
+ }
427
+
428
+ resolve_stack_label() {
429
+ local stack_name="${1:-main}"
430
+ local primary="com.happier.stack"
431
+ if [[ "$stack_name" != "main" ]]; then
432
+ primary="com.happier.stack.${stack_name}"
433
+ fi
434
+ if swiftbar_is_sandboxed; then
435
+ # Never inspect global LaunchAgents in sandbox mode.
436
+ echo "$primary"
437
+ return
438
+ fi
439
+ local primary_plist="$HOME/Library/LaunchAgents/${primary}.plist"
440
+ if [[ -f "$primary_plist" ]]; then
441
+ echo "$primary"
442
+ return
443
+ fi
444
+ echo "$primary"
445
+ }
446
+
447
+ resolve_hstack_bin() {
448
+ local root
449
+ root="$(resolve_hstack_root_dir)"
450
+
451
+ local wrapper="$root/extras/swiftbar/hstack.sh"
452
+ if [[ -x "$wrapper" ]]; then
453
+ echo "$wrapper"
454
+ return
455
+ fi
456
+
457
+ if ! swiftbar_is_sandboxed; then
458
+ local global
459
+ global="$(command -v hstack 2>/dev/null || true)"
460
+ if [[ -n "$global" ]]; then
461
+ echo "$global"
462
+ return
463
+ fi
464
+ fi
465
+
466
+ echo ""
467
+ }
468
+
469
+ resolve_node_bin() {
470
+ # Prefer explicit env vars first.
471
+ if [[ -n "${HAPPIER_STACK_NODE:-}" ]] && [[ -x "${HAPPIER_STACK_NODE:-}" ]]; then
472
+ echo "$HAPPIER_STACK_NODE"
473
+ return
474
+ fi
475
+
476
+ # Fall back to reading the canonical pointer env (written by `hstack init`).
477
+ local home
478
+ home="$(resolve_home_dir)"
479
+ local env_file="$home/.env"
480
+ if [[ -f "$env_file" ]]; then
481
+ local v
482
+ v="$(dotenv_get "$env_file" "HAPPIER_STACK_NODE")"
483
+ if [[ -n "$v" ]] && [[ -x "$v" ]]; then
484
+ echo "$v"
485
+ return
486
+ fi
487
+ fi
488
+
489
+ command -v node 2>/dev/null || true
490
+ }
491
+
492
+ resolve_workspace_dir() {
493
+ if [[ -n "${HAPPIER_STACK_WORKSPACE_DIR:-}" ]]; then
494
+ echo "$HAPPIER_STACK_WORKSPACE_DIR"
495
+ return
496
+ fi
497
+ local root
498
+ root="$(resolve_hstack_root_dir)"
499
+ local p
500
+ p="$(dotenv_get "$root/.env" "HAPPIER_STACK_WORKSPACE_DIR")"
501
+ [[ -z "$p" ]] && p="$(dotenv_get "$root/env.local" "HAPPIER_STACK_WORKSPACE_DIR")"
502
+ if [[ -n "$p" ]]; then
503
+ echo "$p"
504
+ return
505
+ fi
506
+ echo "$(resolve_home_dir)/workspace"
507
+ }
508
+
509
+ resolve_components_dir() {
510
+ echo "$(resolve_workspace_dir)/components"
511
+ }
512
+
513
+ resolve_main_env_file() {
514
+ local explicit="${HAPPIER_STACK_ENV_FILE:-}"
515
+ if [[ -n "$explicit" ]] && [[ -f "$explicit" ]]; then
516
+ echo "$explicit"
517
+ return
518
+ fi
519
+
520
+ local storage_root
521
+ storage_root="$(resolve_stacks_storage_root)"
522
+ local main="$storage_root/main/env"
523
+ if [[ -f "$main" ]]; then
524
+ echo "$main"
525
+ return
526
+ fi
527
+ echo ""
528
+ }
529
+
530
+ resolve_main_port() {
531
+ # Priority:
532
+ # 1) explicit env var
533
+ # 2) main stack env
534
+ # 3) home env.local
535
+ # 4) home .env
536
+ # 5) runtime state (ephemeral stacks)
537
+ # 6) fallback to 3005
538
+ if [[ -n "${HAPPIER_STACK_SERVER_PORT:-}" ]]; then
539
+ echo "$HAPPIER_STACK_SERVER_PORT"
540
+ return
541
+ fi
542
+
543
+ local p
544
+ local env_file
545
+ env_file="$(resolve_main_env_file)"
546
+ if [[ -n "$env_file" ]]; then
547
+ p="$(dotenv_get "$env_file" "HAPPIER_STACK_SERVER_PORT")"
548
+ fi
549
+ if [[ -n "$p" ]]; then
550
+ echo "$p"
551
+ return
552
+ fi
553
+
554
+ local root
555
+ root="$(resolve_hstack_root_dir)"
556
+ p="$(dotenv_get "$root/env.local" "HAPPIER_STACK_SERVER_PORT")"
557
+ if [[ -n "$p" ]]; then
558
+ echo "$p"
559
+ return
560
+ fi
561
+
562
+ p="$(dotenv_get "$root/.env" "HAPPIER_STACK_SERVER_PORT")"
563
+ if [[ -n "$p" ]]; then
564
+ echo "$p"
565
+ return
566
+ fi
567
+
568
+ # Runtime-only port overlay (ephemeral stacks): best-effort.
569
+ local base_dir state_file
570
+ base_dir="$(resolve_stack_base_dir main "$env_file")"
571
+ state_file="${base_dir}/stack.runtime.json"
572
+ p="$(resolve_runtime_server_port_from_state_file "$state_file")"
573
+ if [[ -n "$p" ]]; then
574
+ echo "$p"
575
+ return
576
+ fi
577
+
578
+ echo "3005"
579
+ }
580
+
581
+ resolve_runtime_server_port_from_state_file() {
582
+ # Reads stack.runtime.json and returns ports.server, but only if ownerPid is alive.
583
+ # Output: port number or empty.
584
+ local state_file="$1"
585
+ [[ -n "$state_file" && -f "$state_file" ]] || return 0
586
+
587
+ local owner="" port=""
588
+
589
+ # Fast-path: parse our own JSON shape without spawning node (best-effort).
590
+ if command -v grep >/dev/null 2>&1; then
591
+ owner="$(grep -oE '"ownerPid"[[:space:]]*:[[:space:]]*[0-9]+' "$state_file" 2>/dev/null | head -1 | grep -oE '[0-9]+' || true)"
592
+ port="$(grep -oE '"server"[[:space:]]*:[[:space:]]*[0-9]+' "$state_file" 2>/dev/null | head -1 | grep -oE '[0-9]+' || true)"
593
+ fi
594
+
595
+ if [[ -z "$owner" || -z "$port" ]]; then
596
+ local node_bin
597
+ node_bin="$(resolve_node_bin)"
598
+ if [[ -n "$node_bin" && -x "$node_bin" ]]; then
599
+ local out
600
+ out="$(
601
+ "$node_bin" -e '
602
+ const fs=require("fs");
603
+ try {
604
+ const s=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));
605
+ const owner=String(s?.ownerPid ?? "");
606
+ const port=String(s?.ports?.server ?? "");
607
+ process.stdout.write(owner + "\t" + port);
608
+ } catch { process.stdout.write("\t"); }
609
+ ' "$state_file" 2>/dev/null || true
610
+ )"
611
+ IFS=$'\t' read -r owner port <<<"$out"
612
+ elif command -v python3 >/dev/null 2>&1; then
613
+ local out
614
+ out="$(
615
+ python3 -c 'import json,sys;
616
+ try:
617
+ s=json.load(open(sys.argv[1],"r"))
618
+ owner=str(s.get("ownerPid",""))
619
+ port=str((s.get("ports") or {}).get("server",""))
620
+ print(owner+"\\t"+port,end="")
621
+ except Exception:
622
+ print("\\t",end="")' "$state_file" 2>/dev/null || true
623
+ )"
624
+ IFS=$'\t' read -r owner port <<<"$out"
625
+ fi
626
+ fi
627
+
628
+ [[ "$owner" =~ ^[0-9]+$ ]] || owner=""
629
+ [[ "$port" =~ ^[0-9]+$ ]] || port=""
630
+ if [[ -n "$owner" ]] && kill -0 "$owner" 2>/dev/null; then
631
+ echo "$port"
632
+ fi
633
+ }
634
+
635
+ resolve_stack_server_port() {
636
+ # Usage: resolve_stack_server_port <stack_name> <env_file>
637
+ # Priority:
638
+ # - pinned port in env file
639
+ # - runtime port in stack.runtime.json (only if ownerPid alive)
640
+ local stack_name="${1:-main}"
641
+ local env_file="${2:-}"
642
+
643
+ local p=""
644
+ if [[ -n "$env_file" && -f "$env_file" ]]; then
645
+ p="$(dotenv_get "$env_file" "HAPPIER_STACK_SERVER_PORT")"
646
+ fi
647
+ if [[ -n "$p" ]]; then
648
+ echo "$p"
649
+ return
650
+ fi
651
+
652
+ local base_dir state_file
653
+ base_dir="$(resolve_stack_base_dir "$stack_name" "$env_file")"
654
+ state_file="${base_dir}/stack.runtime.json"
655
+ p="$(resolve_runtime_server_port_from_state_file "$state_file")"
656
+ echo "$p"
657
+ }
658
+
659
+ resolve_main_server_component() {
660
+ if [[ -n "${HAPPIER_STACK_SERVER_COMPONENT:-}" ]]; then
661
+ echo "$HAPPIER_STACK_SERVER_COMPONENT"
662
+ return
663
+ fi
664
+
665
+ local c
666
+ local env_file
667
+ env_file="$(resolve_main_env_file)"
668
+ if [[ -n "$env_file" ]]; then
669
+ c="$(dotenv_get "$env_file" "HAPPIER_STACK_SERVER_COMPONENT")"
670
+ fi
671
+ if [[ -n "$c" ]]; then
672
+ echo "$c"
673
+ return
674
+ fi
675
+
676
+ local root
677
+ root="$(resolve_hstack_root_dir)"
678
+ c="$(dotenv_get "$root/env.local" "HAPPIER_STACK_SERVER_COMPONENT")"
679
+ if [[ -n "$c" ]]; then
680
+ echo "$c"
681
+ return
682
+ fi
683
+
684
+ c="$(dotenv_get "$root/.env" "HAPPIER_STACK_SERVER_COMPONENT")"
685
+ if [[ -n "$c" ]]; then
686
+ echo "$c"
687
+ return
688
+ fi
689
+
690
+ echo "happier-server-light"
691
+ }
692
+
693
+ resolve_menubar_mode() {
694
+ # selfhost | dev (default: dev)
695
+ local raw="${HAPPIER_STACK_MENUBAR_MODE:-}"
696
+
697
+ local env_file
698
+ env_file="$(resolve_main_env_file)"
699
+ if [[ -z "$raw" && -n "$env_file" ]]; then
700
+ raw="$(dotenv_get "$env_file" "HAPPIER_STACK_MENUBAR_MODE")"
701
+ fi
702
+
703
+ if [[ -z "$raw" ]]; then
704
+ local root
705
+ root="$(resolve_hstack_root_dir)"
706
+ raw="$(dotenv_get "$root/env.local" "HAPPIER_STACK_MENUBAR_MODE")"
707
+ fi
708
+ if [[ -z "$raw" ]]; then
709
+ raw="$(dotenv_get "$root/.env" "HAPPIER_STACK_MENUBAR_MODE")"
710
+ fi
711
+
712
+ raw="$(echo "${raw:-}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
713
+ case "$raw" in
714
+ selfhost|self-host|self_host|host) echo "selfhost" ;;
715
+ *) echo "dev" ;;
716
+ esac
717
+ }