@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.
- package/README.md +501 -0
- package/bin/hstack.mjs +348 -0
- package/docs/codex-mcp-resume.md +129 -0
- package/docs/edison.md +74 -0
- package/docs/forking-and-branding.md +189 -0
- package/docs/happy-development.md +22 -0
- package/docs/isolated-linux-vm.md +243 -0
- package/docs/menubar.md +244 -0
- package/docs/mobile-ios.md +322 -0
- package/docs/monorepo-migration.md +20 -0
- package/docs/paths-and-env.md +154 -0
- package/docs/remote-access.md +43 -0
- package/docs/server-flavors.md +147 -0
- package/docs/stacks.md +330 -0
- package/docs/tauri.md +60 -0
- package/docs/worktrees-and-forks.md +133 -0
- package/extras/swiftbar/auth-login.sh +29 -0
- package/extras/swiftbar/git-cache-refresh.sh +122 -0
- package/extras/swiftbar/hstack-term.sh +133 -0
- package/extras/swiftbar/hstack.5s.sh +296 -0
- package/extras/swiftbar/hstack.sh +35 -0
- package/extras/swiftbar/icons/happy-green.png +0 -0
- package/extras/swiftbar/icons/happy-orange.png +0 -0
- package/extras/swiftbar/icons/happy-red.png +0 -0
- package/extras/swiftbar/icons/logo-white.png +0 -0
- package/extras/swiftbar/install.sh +265 -0
- package/extras/swiftbar/lib/git.sh +629 -0
- package/extras/swiftbar/lib/icons.sh +92 -0
- package/extras/swiftbar/lib/render.sh +999 -0
- package/extras/swiftbar/lib/system.sh +244 -0
- package/extras/swiftbar/lib/utils.sh +717 -0
- package/extras/swiftbar/set-interval.sh +65 -0
- package/extras/swiftbar/set-server-flavor.sh +61 -0
- package/extras/swiftbar/wt-pr.sh +140 -0
- package/node_modules/@happier-dev/cli-common/README.md +6 -0
- package/node_modules/@happier-dev/cli-common/dist/index.d.ts +4 -0
- package/node_modules/@happier-dev/cli-common/dist/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/index.js +4 -0
- package/node_modules/@happier-dev/cli-common/dist/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts +18 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.js +25 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links.d.ts +2 -0
- package/node_modules/@happier-dev/cli-common/dist/links.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links.js +2 -0
- package/node_modules/@happier-dev/cli-common/dist/links.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts +67 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.js +259 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts +17 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js +80 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/package.json +26 -0
- package/package.json +77 -0
- package/scripts/auth.mjs +1829 -0
- package/scripts/auth_copy_from_pglite_lock_in_use.integration.test.mjs +90 -0
- package/scripts/auth_copy_from_runCapture.integration.test.mjs +447 -0
- package/scripts/auth_help_cmd.test.mjs +28 -0
- package/scripts/auth_login_flow_in_tty.test.mjs +100 -0
- package/scripts/auth_login_force_default.test.mjs +66 -0
- package/scripts/auth_login_guided_server_no_expo.test.mjs +126 -0
- package/scripts/auth_login_method_override.test.mjs +67 -0
- package/scripts/auth_login_print_includes_configure_links.test.mjs +99 -0
- package/scripts/auth_status_server_validation.integration.test.mjs +140 -0
- package/scripts/build.mjs +266 -0
- package/scripts/bundleWorkspaceDeps.mjs +38 -0
- package/scripts/bundleWorkspaceDeps.test.mjs +77 -0
- package/scripts/ci.mjs +135 -0
- package/scripts/ci.test.mjs +50 -0
- package/scripts/cli-link.mjs +57 -0
- package/scripts/completion.mjs +395 -0
- package/scripts/contrib.mjs +333 -0
- package/scripts/daemon.mjs +1160 -0
- package/scripts/daemon.status_scope.test.mjs +51 -0
- package/scripts/daemon_cmd.mjs +26 -0
- package/scripts/daemon_dist_guard.test.mjs +171 -0
- package/scripts/daemon_invalid_auth_reseed_stack_name.integration.test.mjs +608 -0
- package/scripts/daemon_server_scoped_state.test.mjs +49 -0
- package/scripts/daemon_start_verification.integration.test.mjs +296 -0
- package/scripts/dev.mjs +545 -0
- package/scripts/doctor.mjs +340 -0
- package/scripts/doctor_cmd.test.mjs +22 -0
- package/scripts/doctor_ui_index_missing.test.mjs +37 -0
- package/scripts/eas.mjs +367 -0
- package/scripts/eas_platform_parsing.test.mjs +63 -0
- package/scripts/edison.mjs +1848 -0
- package/scripts/env.mjs +149 -0
- package/scripts/env_cmd.test.mjs +118 -0
- package/scripts/exit_cleanup_kills_detached_children_on_crash.integration.test.mjs +80 -0
- package/scripts/happier.mjs +82 -0
- package/scripts/import.mjs +1327 -0
- package/scripts/init.mjs +464 -0
- package/scripts/install.mjs +550 -0
- package/scripts/lint.mjs +177 -0
- package/scripts/menubar.mjs +202 -0
- package/scripts/migrate.mjs +318 -0
- package/scripts/mobile.mjs +353 -0
- package/scripts/mobile_dev_client.mjs +87 -0
- package/scripts/monorepo.mjs +2234 -0
- package/scripts/monorepo_port.apply.integration.test.mjs +680 -0
- package/scripts/monorepo_port.conflicts.integration.test.mjs +454 -0
- package/scripts/monorepo_port.validation.integration.test.mjs +486 -0
- package/scripts/orchestrated_stack_auth_flow.test.mjs +134 -0
- package/scripts/orchestrated_stack_auth_flow_resolve_port.test.mjs +98 -0
- package/scripts/orchestrated_stack_auth_flow_webapp_url.test.mjs +119 -0
- package/scripts/pack.mjs +257 -0
- package/scripts/pack.test.mjs +68 -0
- package/scripts/pglite_lock.integration.test.mjs +152 -0
- package/scripts/provision/linux-ubuntu-e2e.sh +132 -0
- package/scripts/provision/linux-ubuntu-review-pr.sh +66 -0
- package/scripts/provision/macos-lima-happy-vm.sh +192 -0
- package/scripts/provision/macos-lima-hstack-e2e.sh +100 -0
- package/scripts/release.mjs +53 -0
- package/scripts/release_binary_smoke.integration.test.mjs +138 -0
- package/scripts/review.mjs +1752 -0
- package/scripts/review_pr.mjs +435 -0
- package/scripts/run.mjs +561 -0
- package/scripts/run_script_with_stack_env.restart_port_reuse.test.mjs +30 -0
- package/scripts/self.mjs +465 -0
- package/scripts/self_host.mjs +9 -0
- package/scripts/self_host_binary_smoke.integration.test.mjs +74 -0
- package/scripts/self_host_runtime.mjs +883 -0
- package/scripts/self_host_runtime.test.mjs +82 -0
- package/scripts/self_host_systemd.real.integration.test.mjs +367 -0
- package/scripts/server_flavor.mjs +148 -0
- package/scripts/service.mjs +868 -0
- package/scripts/service_mode_help.test.mjs +27 -0
- package/scripts/setup.mjs +1324 -0
- package/scripts/setup_non_interactive_flag.test.mjs +60 -0
- package/scripts/setup_pr.mjs +605 -0
- package/scripts/setup_pr_orchestrated_auth_flow_util_import.test.mjs +117 -0
- package/scripts/stack/command_arguments.mjs +91 -0
- package/scripts/stack/copy_auth_from_stack.mjs +111 -0
- package/scripts/stack/delegated_script_commands.mjs +92 -0
- package/scripts/stack/help_text.mjs +110 -0
- package/scripts/stack/port_reservation.mjs +74 -0
- package/scripts/stack/repo_checkout_resolution.mjs +31 -0
- package/scripts/stack/run_script_with_stack_env.mjs +634 -0
- package/scripts/stack/stack_daemon_command.mjs +219 -0
- package/scripts/stack/stack_delegated_help.mjs +81 -0
- package/scripts/stack/stack_environment.mjs +151 -0
- package/scripts/stack/stack_environment.sanitization.test.mjs +75 -0
- package/scripts/stack/stack_happier_passthrough_command.mjs +63 -0
- package/scripts/stack/stack_info_snapshot.mjs +167 -0
- package/scripts/stack/stack_mobile_install_command.mjs +61 -0
- package/scripts/stack/stack_resume_command.mjs +76 -0
- package/scripts/stack/stack_stop_command.mjs +34 -0
- package/scripts/stack/stack_workspace_command.mjs +83 -0
- package/scripts/stack/transient_repo_overrides.mjs +29 -0
- package/scripts/stack.mjs +2388 -0
- package/scripts/stack_archive_cmd.integration.test.mjs +31 -0
- package/scripts/stack_audit_fix_light_env.test.mjs +129 -0
- package/scripts/stack_background_pinned_stack_json.test.mjs +81 -0
- package/scripts/stack_copy_auth_server_scoped.test.mjs +243 -0
- package/scripts/stack_daemon_cmd.integration.test.mjs +484 -0
- package/scripts/stack_eas_help.test.mjs +72 -0
- package/scripts/stack_editor_workspace_monorepo_root.test.mjs +102 -0
- package/scripts/stack_env_cmd.test.mjs +107 -0
- package/scripts/stack_guided_login_bundle_error_parse.test.mjs +20 -0
- package/scripts/stack_guided_login_inner_invocation.test.mjs +46 -0
- package/scripts/stack_happy_cmd.integration.test.mjs +263 -0
- package/scripts/stack_info_snapshot_running_status.test.mjs +186 -0
- package/scripts/stack_interactive_monorepo_group.test.mjs +128 -0
- package/scripts/stack_monorepo_defaults.test.mjs +31 -0
- package/scripts/stack_monorepo_repo_dev_token.test.mjs +32 -0
- package/scripts/stack_monorepo_server_light_from_happy_spec.test.mjs +37 -0
- package/scripts/stack_new_name_normalize_cmd.test.mjs +38 -0
- package/scripts/stack_pr_name_normalize_cmd.test.mjs +84 -0
- package/scripts/stack_resume_cmd.integration.test.mjs +134 -0
- package/scripts/stack_server_flavors_defaults.test.mjs +64 -0
- package/scripts/stack_shorthand_cmd.integration.test.mjs +74 -0
- package/scripts/stack_stop_sweeps_legacy_infra_without_kind.integration.test.mjs +44 -0
- package/scripts/stack_stop_sweeps_when_runtime_missing.integration.test.mjs +42 -0
- package/scripts/stack_stop_sweeps_when_runtime_stale.integration.test.mjs +50 -0
- package/scripts/stack_wt_list.test.mjs +117 -0
- package/scripts/start_ui_required_default.test.mjs +63 -0
- package/scripts/stop.mjs +190 -0
- package/scripts/stopStackWithEnv_no_autosweep_when_runtime_missing.integration.test.mjs +95 -0
- package/scripts/swiftbar_git_monorepo_cmd.test.mjs +75 -0
- package/scripts/swiftbar_render_monorepo_wt_actions.integration.test.mjs +116 -0
- package/scripts/swiftbar_utils_cmd.test.mjs +92 -0
- package/scripts/swiftbar_wt_pr_backcompat.test.mjs +162 -0
- package/scripts/systemd_unit_info.test.mjs +24 -0
- package/scripts/tailscale.mjs +490 -0
- package/scripts/test_ci.mjs +36 -0
- package/scripts/test_cmd.mjs +274 -0
- package/scripts/test_cmd.test.mjs +133 -0
- package/scripts/test_integration.mjs +33 -0
- package/scripts/testkit/auth_testkit.mjs +121 -0
- package/scripts/testkit/doctor_testkit.mjs +68 -0
- package/scripts/testkit/monorepo_port_testkit.mjs +157 -0
- package/scripts/testkit/stack_archive_command_testkit.mjs +55 -0
- package/scripts/testkit/stack_new_monorepo_testkit.mjs +83 -0
- package/scripts/testkit/stack_script_command_testkit.mjs +27 -0
- package/scripts/testkit/stack_stop_sweeps_testkit.mjs +172 -0
- package/scripts/testkit/worktrees_monorepo_testkit.mjs +53 -0
- package/scripts/tools.mjs +70 -0
- package/scripts/tui.mjs +914 -0
- package/scripts/tui_stopStackForTuiExit_no_autosweep.integration.test.mjs +95 -0
- package/scripts/typecheck.mjs +178 -0
- package/scripts/ui_gateway.mjs +247 -0
- package/scripts/uninstall.mjs +179 -0
- package/scripts/utils/auth/credentials_paths.mjs +181 -0
- package/scripts/utils/auth/credentials_paths.test.mjs +187 -0
- package/scripts/utils/auth/daemon_gate.mjs +66 -0
- package/scripts/utils/auth/daemon_gate.test.mjs +116 -0
- package/scripts/utils/auth/decode_jwt_payload_unsafe.mjs +16 -0
- package/scripts/utils/auth/dev_key.mjs +163 -0
- package/scripts/utils/auth/files.mjs +56 -0
- package/scripts/utils/auth/guided_pr_auth.mjs +86 -0
- package/scripts/utils/auth/guided_stack_web_login.mjs +56 -0
- package/scripts/utils/auth/handy_master_secret.mjs +42 -0
- package/scripts/utils/auth/interactive_stack_auth.mjs +70 -0
- package/scripts/utils/auth/login_ux.mjs +105 -0
- package/scripts/utils/auth/orchestrated_stack_auth_flow.mjs +291 -0
- package/scripts/utils/auth/sources.mjs +28 -0
- package/scripts/utils/auth/stable_scope_id.mjs +91 -0
- package/scripts/utils/auth/stable_scope_id.test.mjs +51 -0
- package/scripts/utils/auth/stack_guided_login.mjs +438 -0
- package/scripts/utils/cli/arg_values.mjs +23 -0
- package/scripts/utils/cli/arg_values.test.mjs +43 -0
- package/scripts/utils/cli/args.mjs +17 -0
- package/scripts/utils/cli/cli.mjs +24 -0
- package/scripts/utils/cli/cli_registry.mjs +440 -0
- package/scripts/utils/cli/cwd_scope.mjs +158 -0
- package/scripts/utils/cli/cwd_scope.test.mjs +154 -0
- package/scripts/utils/cli/flags.mjs +17 -0
- package/scripts/utils/cli/log_forwarder.mjs +157 -0
- package/scripts/utils/cli/normalize.mjs +16 -0
- package/scripts/utils/cli/prereqs.mjs +103 -0
- package/scripts/utils/cli/prereqs.test.mjs +33 -0
- package/scripts/utils/cli/progress.mjs +141 -0
- package/scripts/utils/cli/smoke_help.mjs +44 -0
- package/scripts/utils/cli/verbosity.mjs +11 -0
- package/scripts/utils/cli/wizard.mjs +139 -0
- package/scripts/utils/cli/wizard_promptSelect.test.mjs +44 -0
- package/scripts/utils/cli/wizard_prompt_worktree_source_lazy.test.mjs +132 -0
- package/scripts/utils/cli/wizard_worktree_slug.test.mjs +33 -0
- package/scripts/utils/crypto/tokens.mjs +14 -0
- package/scripts/utils/dev/daemon.mjs +232 -0
- package/scripts/utils/dev/daemon_watch_resilience.test.mjs +224 -0
- package/scripts/utils/dev/expo_dev.buildEnv.test.mjs +35 -0
- package/scripts/utils/dev/expo_dev.mjs +478 -0
- package/scripts/utils/dev/expo_dev.test.mjs +89 -0
- package/scripts/utils/dev/expo_dev_restart_port_reservation.test.mjs +120 -0
- package/scripts/utils/dev/expo_dev_verbose_logs.test.mjs +60 -0
- package/scripts/utils/dev/server.mjs +180 -0
- package/scripts/utils/dev_auth_key.mjs +7 -0
- package/scripts/utils/edison/git_roots.mjs +30 -0
- package/scripts/utils/edison/git_roots.test.mjs +49 -0
- package/scripts/utils/env/config.mjs +52 -0
- package/scripts/utils/env/dotenv.mjs +32 -0
- package/scripts/utils/env/dotenv.test.mjs +32 -0
- package/scripts/utils/env/env.mjs +130 -0
- package/scripts/utils/env/env_file.mjs +98 -0
- package/scripts/utils/env/env_file.test.mjs +49 -0
- package/scripts/utils/env/env_local.mjs +25 -0
- package/scripts/utils/env/load_env_file.mjs +34 -0
- package/scripts/utils/env/read.mjs +30 -0
- package/scripts/utils/env/sandbox.mjs +13 -0
- package/scripts/utils/env/scrub_env.mjs +69 -0
- package/scripts/utils/env/scrub_env.test.mjs +102 -0
- package/scripts/utils/env/values.mjs +13 -0
- package/scripts/utils/expo/command.mjs +65 -0
- package/scripts/utils/expo/expo.mjs +139 -0
- package/scripts/utils/expo/expo_state_running.test.mjs +48 -0
- package/scripts/utils/expo/metro_ports.mjs +101 -0
- package/scripts/utils/expo/metro_ports.test.mjs +35 -0
- package/scripts/utils/fs/atomic_dir_swap.mjs +55 -0
- package/scripts/utils/fs/atomic_dir_swap.test.mjs +54 -0
- package/scripts/utils/fs/file_has_content.mjs +10 -0
- package/scripts/utils/fs/fs.mjs +11 -0
- package/scripts/utils/fs/json.mjs +25 -0
- package/scripts/utils/fs/ops.mjs +29 -0
- package/scripts/utils/fs/package_json.mjs +8 -0
- package/scripts/utils/fs/tail.mjs +12 -0
- package/scripts/utils/git/dev_checkout.mjs +127 -0
- package/scripts/utils/git/dev_checkout.test.mjs +115 -0
- package/scripts/utils/git/git.mjs +67 -0
- package/scripts/utils/git/parse_name_status_z.mjs +21 -0
- package/scripts/utils/git/refs.mjs +26 -0
- package/scripts/utils/git/worktrees.mjs +323 -0
- package/scripts/utils/git/worktrees_monorepo.test.mjs +60 -0
- package/scripts/utils/git/worktrees_pathstyle.test.mjs +53 -0
- package/scripts/utils/llm/assist.mjs +260 -0
- package/scripts/utils/llm/codex_exec.mjs +61 -0
- package/scripts/utils/llm/codex_exec.test.mjs +46 -0
- package/scripts/utils/llm/hstack_runner.mjs +59 -0
- package/scripts/utils/llm/tools.mjs +56 -0
- package/scripts/utils/llm/tools.test.mjs +67 -0
- package/scripts/utils/menubar/swiftbar.mjs +121 -0
- package/scripts/utils/menubar/swiftbar.test.mjs +85 -0
- package/scripts/utils/mobile/config.mjs +35 -0
- package/scripts/utils/mobile/dev_client_links.mjs +59 -0
- package/scripts/utils/mobile/identifiers.mjs +46 -0
- package/scripts/utils/mobile/identifiers.test.mjs +41 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.mjs +128 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.test.mjs +131 -0
- package/scripts/utils/net/bind_mode.mjs +39 -0
- package/scripts/utils/net/dns.mjs +10 -0
- package/scripts/utils/net/lan_ip.mjs +24 -0
- package/scripts/utils/net/ports.mjs +110 -0
- package/scripts/utils/net/tcp_forward.mjs +162 -0
- package/scripts/utils/net/url.mjs +30 -0
- package/scripts/utils/net/url.test.mjs +29 -0
- package/scripts/utils/paths/canonical_home.mjs +15 -0
- package/scripts/utils/paths/canonical_home.test.mjs +28 -0
- package/scripts/utils/paths/localhost_host.mjs +112 -0
- package/scripts/utils/paths/localhost_host.test.mjs +58 -0
- package/scripts/utils/paths/paths.mjs +302 -0
- package/scripts/utils/paths/paths_env_win32.test.mjs +36 -0
- package/scripts/utils/paths/paths_monorepo.test.mjs +58 -0
- package/scripts/utils/paths/paths_server_flavors.test.mjs +50 -0
- package/scripts/utils/paths/runtime.mjs +41 -0
- package/scripts/utils/pglite_lock.mjs +107 -0
- package/scripts/utils/proc/commands.mjs +33 -0
- package/scripts/utils/proc/exit_cleanup.mjs +57 -0
- package/scripts/utils/proc/happy_monorepo_deps.mjs +37 -0
- package/scripts/utils/proc/happy_monorepo_deps.test.mjs +89 -0
- package/scripts/utils/proc/ownership.mjs +217 -0
- package/scripts/utils/proc/ownership_killProcessGroupOwnedByStack.test.mjs +216 -0
- package/scripts/utils/proc/ownership_listPidsWithEnvNeedles.test.mjs +88 -0
- package/scripts/utils/proc/package_scripts.mjs +38 -0
- package/scripts/utils/proc/package_scripts.test.mjs +58 -0
- package/scripts/utils/proc/parallel.mjs +25 -0
- package/scripts/utils/proc/pids.mjs +11 -0
- package/scripts/utils/proc/pm.mjs +478 -0
- package/scripts/utils/proc/pm_spawn.integration.test.mjs +131 -0
- package/scripts/utils/proc/pm_stack_cache_env.test.mjs +313 -0
- package/scripts/utils/proc/proc.mjs +331 -0
- package/scripts/utils/proc/proc.test.mjs +85 -0
- package/scripts/utils/proc/terminate.mjs +69 -0
- package/scripts/utils/proc/terminate.test.mjs +54 -0
- package/scripts/utils/proc/watch.mjs +63 -0
- package/scripts/utils/review/augment_runner_integration.test.mjs +105 -0
- package/scripts/utils/review/base_ref.mjs +82 -0
- package/scripts/utils/review/base_ref.test.mjs +89 -0
- package/scripts/utils/review/chunks.mjs +55 -0
- package/scripts/utils/review/chunks.test.mjs +107 -0
- package/scripts/utils/review/detached_worktree.mjs +61 -0
- package/scripts/utils/review/detached_worktree.test.mjs +61 -0
- package/scripts/utils/review/findings.mjs +278 -0
- package/scripts/utils/review/findings.test.mjs +203 -0
- package/scripts/utils/review/head_slice.mjs +132 -0
- package/scripts/utils/review/head_slice.test.mjs +117 -0
- package/scripts/utils/review/instructions/deep.md +20 -0
- package/scripts/utils/review/prompts.mjs +279 -0
- package/scripts/utils/review/prompts.test.mjs +77 -0
- package/scripts/utils/review/run_reviewers_safe.mjs +12 -0
- package/scripts/utils/review/run_reviewers_safe.test.mjs +45 -0
- package/scripts/utils/review/runners/augment.mjs +91 -0
- package/scripts/utils/review/runners/augment.test.mjs +64 -0
- package/scripts/utils/review/runners/claude.mjs +92 -0
- package/scripts/utils/review/runners/claude.test.mjs +47 -0
- package/scripts/utils/review/runners/coderabbit.mjs +105 -0
- package/scripts/utils/review/runners/coderabbit.test.mjs +32 -0
- package/scripts/utils/review/runners/codex.mjs +129 -0
- package/scripts/utils/review/runners/codex.test.mjs +115 -0
- package/scripts/utils/review/slice_mode.mjs +20 -0
- package/scripts/utils/review/slice_mode.test.mjs +69 -0
- package/scripts/utils/review/sliced_runner.mjs +39 -0
- package/scripts/utils/review/sliced_runner.test.mjs +57 -0
- package/scripts/utils/review/slices.mjs +140 -0
- package/scripts/utils/review/slices.test.mjs +41 -0
- package/scripts/utils/review/targets.mjs +23 -0
- package/scripts/utils/review/targets.test.mjs +31 -0
- package/scripts/utils/review/tool_home_seed.mjs +106 -0
- package/scripts/utils/review/tool_home_seed.test.mjs +124 -0
- package/scripts/utils/review/uncommitted_ops.mjs +77 -0
- package/scripts/utils/review/uncommitted_ops.test.mjs +117 -0
- package/scripts/utils/sandbox/review_pr_sandbox.mjs +105 -0
- package/scripts/utils/server/apply_server_light_env_defaults.mjs +14 -0
- package/scripts/utils/server/flavor_scripts.mjs +138 -0
- package/scripts/utils/server/flavor_scripts.test.mjs +115 -0
- package/scripts/utils/server/infra/happy_server_infra.mjs +444 -0
- package/scripts/utils/server/mobile_api_url.mjs +60 -0
- package/scripts/utils/server/mobile_api_url.test.mjs +58 -0
- package/scripts/utils/server/port.mjs +55 -0
- package/scripts/utils/server/prisma_import.mjs +36 -0
- package/scripts/utils/server/prisma_import.test.mjs +78 -0
- package/scripts/utils/server/server.mjs +109 -0
- package/scripts/utils/server/ui_build_check.mjs +37 -0
- package/scripts/utils/server/ui_build_check.test.mjs +70 -0
- package/scripts/utils/server/ui_env.mjs +13 -0
- package/scripts/utils/server/ui_env.test.mjs +57 -0
- package/scripts/utils/server/urls.mjs +100 -0
- package/scripts/utils/server/validate.mjs +60 -0
- package/scripts/utils/server/validate.test.mjs +76 -0
- package/scripts/utils/service/autostart_darwin.mjs +198 -0
- package/scripts/utils/service/autostart_darwin.test.mjs +49 -0
- package/scripts/utils/service/autostart_darwin_keepalive.test.mjs +19 -0
- package/scripts/utils/stack/cli_identities.mjs +29 -0
- package/scripts/utils/stack/context.mjs +19 -0
- package/scripts/utils/stack/dirs.mjs +26 -0
- package/scripts/utils/stack/editor_workspace.mjs +126 -0
- package/scripts/utils/stack/interactive_stack_config.mjs +266 -0
- package/scripts/utils/stack/interactive_stack_config.port_validation.test.mjs +93 -0
- package/scripts/utils/stack/interactive_stack_config.remote_validation.test.mjs +122 -0
- package/scripts/utils/stack/interactive_stack_config.stack_name_validation.test.mjs +76 -0
- package/scripts/utils/stack/interactive_stack_config_testkit.mjs +18 -0
- package/scripts/utils/stack/names.mjs +27 -0
- package/scripts/utils/stack/names.test.mjs +26 -0
- package/scripts/utils/stack/pr_stack_name.mjs +16 -0
- package/scripts/utils/stack/runtime_state.mjs +88 -0
- package/scripts/utils/stack/stacks.mjs +40 -0
- package/scripts/utils/stack/startup.mjs +370 -0
- package/scripts/utils/stack/startup_server_light_dirs.test.mjs +119 -0
- package/scripts/utils/stack/startup_server_light_generate.test.mjs +20 -0
- package/scripts/utils/stack/startup_server_light_legacy.test.mjs +79 -0
- package/scripts/utils/stack/startup_server_light_testkit.mjs +106 -0
- package/scripts/utils/stack/stop.mjs +284 -0
- package/scripts/utils/stack_context.mjs +1 -0
- package/scripts/utils/stack_runtime_state.mjs +1 -0
- package/scripts/utils/stacks.mjs +1 -0
- package/scripts/utils/tailscale/ip.mjs +116 -0
- package/scripts/utils/tauri/stack_overrides.mjs +22 -0
- package/scripts/utils/test/collect_test_files.mjs +29 -0
- package/scripts/utils/time/get_today_ymd.mjs +7 -0
- package/scripts/utils/tui/cleanup.mjs +38 -0
- package/scripts/utils/ui/ansi.mjs +47 -0
- package/scripts/utils/ui/browser.mjs +31 -0
- package/scripts/utils/ui/browser.test.mjs +56 -0
- package/scripts/utils/ui/clipboard.mjs +38 -0
- package/scripts/utils/ui/layout.mjs +44 -0
- package/scripts/utils/ui/qr.mjs +17 -0
- package/scripts/utils/ui/terminal_launcher.mjs +129 -0
- package/scripts/utils/ui/text.mjs +16 -0
- package/scripts/utils/update/auto_update_notice.mjs +93 -0
- package/scripts/utils/validate.mjs +5 -0
- package/scripts/where.mjs +138 -0
- package/scripts/worktrees.mjs +2174 -0
- package/scripts/worktrees_archive_cmd.integration.test.mjs +228 -0
- package/scripts/worktrees_cursor_monorepo_root.test.mjs +23 -0
- package/scripts/worktrees_list_specs_no_recurse.test.mjs +32 -0
- package/scripts/worktrees_monorepo_testkit.test.mjs +29 -0
- package/scripts/worktrees_monorepo_use_group.test.mjs +41 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import './utils/env/env.mjs';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { join, resolve } from 'node:path';
|
|
7
|
+
import { getRootDir } from './utils/paths/paths.mjs';
|
|
8
|
+
import { printResult, wantsHelp, wantsJson } from './utils/cli/cli.mjs';
|
|
9
|
+
import { parseArgs } from './utils/cli/args.mjs';
|
|
10
|
+
import { getVerbosityLevel } from './utils/cli/verbosity.mjs';
|
|
11
|
+
import { createStepPrinter } from './utils/cli/progress.mjs';
|
|
12
|
+
import { prompt, promptSelect, withRl } from './utils/cli/wizard.mjs';
|
|
13
|
+
import { assertCliPrereqs } from './utils/cli/prereqs.mjs';
|
|
14
|
+
import { randomToken } from './utils/crypto/tokens.mjs';
|
|
15
|
+
import { inferPrStackBaseName } from './utils/stack/pr_stack_name.mjs';
|
|
16
|
+
import { sanitizeStackName } from './utils/stack/names.mjs';
|
|
17
|
+
import { listReviewPrSandboxes, reviewPrSandboxPrefixPath, writeReviewPrSandboxMeta } from './utils/sandbox/review_pr_sandbox.mjs';
|
|
18
|
+
import { bold, cyan, dim } from './utils/ui/ansi.mjs';
|
|
19
|
+
|
|
20
|
+
function usage() {
|
|
21
|
+
return [
|
|
22
|
+
'[review-pr] usage:',
|
|
23
|
+
' hstack tools review-pr --repo=<pr-url|number> [--name=<stack>] [--dev|--start] [--mobile|--no-mobile] [--forks|--upstream] [--seed-auth|--no-seed-auth] [--copy-auth-from=<stack>] [--link-auth|--copy-auth] [--update] [--force] [--keep-sandbox] [--json] [-- <stack dev/start args...>]',
|
|
24
|
+
'',
|
|
25
|
+
'VM port forwarding (optional):',
|
|
26
|
+
'- `--vm-ports`: convenience preset for port-forwarded VMs (stack ports ~13xxx, Expo ports ~18xxx)',
|
|
27
|
+
'- `--stack-port-start=<n>`: sets HAPPIER_STACK_STACK_PORT_START inside the sandbox',
|
|
28
|
+
'- `--expo-dev-port-strategy=stable|ephemeral`: sets HAPPIER_STACK_EXPO_DEV_PORT_STRATEGY inside the sandbox',
|
|
29
|
+
'- `--expo-dev-port-base=<n>` / `--expo-dev-port-range=<n>`: stable Expo port hashing params',
|
|
30
|
+
'- `--expo-dev-port=<n>`: force the Expo dev (Metro) port inside the sandbox',
|
|
31
|
+
'',
|
|
32
|
+
'What it does:',
|
|
33
|
+
'- creates a temporary sandbox dir',
|
|
34
|
+
'- runs `hstack tools setup-pr ...` inside that sandbox (fully isolated state)',
|
|
35
|
+
'- on exit (including Ctrl+C): stops sandbox processes and deletes the sandbox dir',
|
|
36
|
+
'',
|
|
37
|
+
].join('\n');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function waitForExit(child) {
|
|
41
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
42
|
+
child.on('error', rejectPromise);
|
|
43
|
+
child.on('close', (code, signal) => resolvePromise({ code: code ?? 1, signal: signal ?? null }));
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function tryStopSandbox({ rootDir, sandboxDir }) {
|
|
48
|
+
const bin = join(rootDir, 'bin', 'hstack.mjs');
|
|
49
|
+
const child = spawn(process.execPath, [bin, '--sandbox-dir', sandboxDir, 'stop', '--yes', '--aggressive', '--sweep-owned', '--no-service'], {
|
|
50
|
+
cwd: rootDir,
|
|
51
|
+
env: process.env,
|
|
52
|
+
stdio: 'ignore',
|
|
53
|
+
});
|
|
54
|
+
await waitForExit(child);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function argvHasFlag(argv, names) {
|
|
58
|
+
for (const n of names) {
|
|
59
|
+
if (argv.includes(n)) return true;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function kvValue(argv, names) {
|
|
65
|
+
for (const a of argv) {
|
|
66
|
+
for (const n of names) {
|
|
67
|
+
if (a === n) {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
if (a.startsWith(`${n}=`)) {
|
|
71
|
+
return a.slice(`${n}=`.length);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function stripArgv(argv, names) {
|
|
79
|
+
const out = [];
|
|
80
|
+
for (const a of argv) {
|
|
81
|
+
let keep = true;
|
|
82
|
+
for (const n of names) {
|
|
83
|
+
if (a === n || a.startsWith(`${n}=`)) {
|
|
84
|
+
keep = false;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (keep) out.push(a);
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function resolveSandboxPortEnvOverrides(argv) {
|
|
94
|
+
const overrides = {};
|
|
95
|
+
|
|
96
|
+
// Convenience preset for VM review flows (pairs with Lima port-forward ranges in docs).
|
|
97
|
+
if (argvHasFlag(argv, ['--vm-ports'])) {
|
|
98
|
+
overrides.HAPPIER_STACK_STACK_PORT_START = '13005';
|
|
99
|
+
|
|
100
|
+
// Keep Expo dev ports stable per stack so forwarded ports remain predictable.
|
|
101
|
+
overrides.HAPPIER_STACK_EXPO_DEV_PORT_STRATEGY = 'stable';
|
|
102
|
+
overrides.HAPPIER_STACK_EXPO_DEV_PORT_BASE = '18081';
|
|
103
|
+
overrides.HAPPIER_STACK_EXPO_DEV_PORT_RANGE = '1000';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const stackPortStart = (kvValue(argv, ['--stack-port-start']) ?? '').trim();
|
|
107
|
+
if (stackPortStart) {
|
|
108
|
+
overrides.HAPPIER_STACK_STACK_PORT_START = stackPortStart;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const expoStrategy = (kvValue(argv, ['--expo-dev-port-strategy']) ?? '').trim().toLowerCase();
|
|
112
|
+
if (expoStrategy === 'stable' || expoStrategy === 'ephemeral') {
|
|
113
|
+
overrides.HAPPIER_STACK_EXPO_DEV_PORT_STRATEGY = expoStrategy;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const expoBase = (kvValue(argv, ['--expo-dev-port-base']) ?? '').trim();
|
|
117
|
+
if (expoBase) {
|
|
118
|
+
overrides.HAPPIER_STACK_EXPO_DEV_PORT_BASE = expoBase;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const expoRange = (kvValue(argv, ['--expo-dev-port-range']) ?? '').trim();
|
|
122
|
+
if (expoRange) {
|
|
123
|
+
overrides.HAPPIER_STACK_EXPO_DEV_PORT_RANGE = expoRange;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const expoForced = (kvValue(argv, ['--expo-dev-port']) ?? '').trim();
|
|
127
|
+
if (expoForced) {
|
|
128
|
+
overrides.HAPPIER_STACK_EXPO_DEV_PORT = expoForced;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return Object.keys(overrides).length ? overrides : null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function main() {
|
|
135
|
+
const rootDir = getRootDir(import.meta.url);
|
|
136
|
+
const argv = process.argv.slice(2);
|
|
137
|
+
const { flags } = parseArgs(argv);
|
|
138
|
+
const json = wantsJson(argv, { flags });
|
|
139
|
+
const verbosity = getVerbosityLevel(process.env);
|
|
140
|
+
const steps = createStepPrinter({ enabled: Boolean(process.stdout.isTTY && !json && verbosity === 0) });
|
|
141
|
+
|
|
142
|
+
if (wantsHelp(argv, { flags })) {
|
|
143
|
+
printResult({ json, data: { usage: usage() }, text: usage() });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
await assertCliPrereqs({ git: true, yarn: true });
|
|
148
|
+
|
|
149
|
+
// Determine a stable base stack name from PR inputs (used for sandbox discovery),
|
|
150
|
+
// and a per-run unique stack name by default (prevents browser storage collisions across deleted sandboxes).
|
|
151
|
+
const prRepo = (kvValue(argv, ['--repo', '--pr']) ?? '').trim();
|
|
152
|
+
const legacyHappy = (kvValue(argv, ['--happy']) ?? '').trim();
|
|
153
|
+
if (legacyHappy) {
|
|
154
|
+
throw new Error('[review-pr] use --repo=<pr-url|number> (the old --happy flag has been removed)');
|
|
155
|
+
}
|
|
156
|
+
if (!prRepo) {
|
|
157
|
+
throw new Error('[review-pr] missing PR input. Provide --repo=<pr-url|number>.');
|
|
158
|
+
}
|
|
159
|
+
for (const legacy of ['--happy-cli', '--happy-server', '--happy-server-light']) {
|
|
160
|
+
const v = (kvValue(argv, [legacy]) ?? '').trim();
|
|
161
|
+
if (v) {
|
|
162
|
+
throw new Error(`[review-pr] legacy split-repo flag is not supported anymore: ${legacy}\nFix: use --repo=<pr-url|number>`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const explicitName = (kvValue(argv, ['--name']) ?? '').trim();
|
|
166
|
+
|
|
167
|
+
const baseStackName = explicitName
|
|
168
|
+
? sanitizeStackName(explicitName, { fallback: 'pr', maxLen: 64 })
|
|
169
|
+
: inferPrStackBaseName({ happy: prRepo, happyCli: '', server: '', serverLight: '', fallback: 'pr' });
|
|
170
|
+
|
|
171
|
+
const shouldAutoSuffix = !explicitName;
|
|
172
|
+
const uniqueSuffix = randomToken(4); // short, URL-safe-ish
|
|
173
|
+
const newStackName = shouldAutoSuffix
|
|
174
|
+
? sanitizeStackName(`${baseStackName}-${uniqueSuffix}`, { fallback: baseStackName, maxLen: 64 })
|
|
175
|
+
: baseStackName;
|
|
176
|
+
|
|
177
|
+
// Look for leftover sandboxes for the same PR base name (typically due to --keep-sandbox / failures).
|
|
178
|
+
const canPrompt = Boolean(process.stdout.isTTY && process.stdin.isTTY && !json);
|
|
179
|
+
const existingSandboxes = canPrompt ? await listReviewPrSandboxes({ baseStackName }) : [];
|
|
180
|
+
|
|
181
|
+
if (process.stdout.isTTY && !json) {
|
|
182
|
+
const intro = [
|
|
183
|
+
'',
|
|
184
|
+
'',
|
|
185
|
+
bold(`✨ ${cyan('hstack')} review-pr ✨`),
|
|
186
|
+
'',
|
|
187
|
+
'It will help you review a PR for Happier in a completely isolated environment.',
|
|
188
|
+
dim('Uses the light server flavor by default (no Redis, no Postgres, no Docker).'),
|
|
189
|
+
dim('Desktop browser + optional mobile review (Expo dev-client).'),
|
|
190
|
+
'',
|
|
191
|
+
bold('What will happen:'),
|
|
192
|
+
`- ${cyan('sandbox')}: temporary isolated Happier install`,
|
|
193
|
+
`- ${cyan('repos')}: clone/install (inside the sandbox only)`,
|
|
194
|
+
`- ${cyan('start')}: start the Happier stack in sandbox (server, daemon, web, mobile)`,
|
|
195
|
+
`- ${cyan('login')}: guide you through Happier login for this sandbox`,
|
|
196
|
+
`- ${cyan('browser')}: open the Happier web app`,
|
|
197
|
+
`- ${cyan('mobile')}: start Expo dev-client (optional)`,
|
|
198
|
+
`- ${cyan('cleanup')}: stop processes + delete sandbox on exit`,
|
|
199
|
+
'',
|
|
200
|
+
dim('Everything is deleted automatically when you exit.'),
|
|
201
|
+
dim('Your main Happier installation remains untouched.'),
|
|
202
|
+
'',
|
|
203
|
+
dim('Tips:'),
|
|
204
|
+
dim('- Add `-v` / `-vv` / `-vvv` to show the full logs'),
|
|
205
|
+
dim('- Add `--keep-sandbox` to keep the sandbox directory between runs'),
|
|
206
|
+
'',
|
|
207
|
+
existingSandboxes.length
|
|
208
|
+
? bold('Choose how to proceed') + dim(' (or Ctrl+C to cancel).')
|
|
209
|
+
: bold('Press Enter to proceed') + dim(' (or Ctrl+C to cancel).'),
|
|
210
|
+
].join('\n');
|
|
211
|
+
// eslint-disable-next-line no-console
|
|
212
|
+
console.log(intro);
|
|
213
|
+
if (!existingSandboxes.length) {
|
|
214
|
+
await withRl(async (rl) => {
|
|
215
|
+
await prompt(rl, '', { defaultValue: '' });
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let sandboxDir = '';
|
|
221
|
+
let createdNewSandbox = false;
|
|
222
|
+
let reusedSandboxMeta = null;
|
|
223
|
+
|
|
224
|
+
if (existingSandboxes.length) {
|
|
225
|
+
const picked = await withRl(async (rl) => {
|
|
226
|
+
const options = [
|
|
227
|
+
{ label: 'Create a new sandbox (recommended)', value: 'new' },
|
|
228
|
+
...existingSandboxes.map((s) => {
|
|
229
|
+
const stackLabel = s.stackName ? `stack=${s.stackName}` : 'stack=?';
|
|
230
|
+
return { label: `Reuse existing sandbox (${stackLabel}) — ${s.dir}`, value: s.dir };
|
|
231
|
+
}),
|
|
232
|
+
];
|
|
233
|
+
return await promptSelect(rl, {
|
|
234
|
+
title: 'Review-pr sandbox:',
|
|
235
|
+
options,
|
|
236
|
+
defaultIndex: 0,
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
if (picked === 'new') {
|
|
240
|
+
steps.start('create temporary sandbox');
|
|
241
|
+
const prefix = reviewPrSandboxPrefixPath(baseStackName);
|
|
242
|
+
sandboxDir = resolve(await mkdtemp(prefix));
|
|
243
|
+
createdNewSandbox = true;
|
|
244
|
+
steps.stop('✓', 'create temporary sandbox');
|
|
245
|
+
} else {
|
|
246
|
+
sandboxDir = resolve(String(picked));
|
|
247
|
+
reusedSandboxMeta = existingSandboxes.find((s) => resolve(s.dir) === sandboxDir) ?? null;
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
steps.start('create temporary sandbox');
|
|
251
|
+
const prefix = reviewPrSandboxPrefixPath(baseStackName);
|
|
252
|
+
sandboxDir = resolve(await mkdtemp(prefix));
|
|
253
|
+
createdNewSandbox = true;
|
|
254
|
+
steps.stop('✓', 'create temporary sandbox');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// If we're reusing a sandbox, prefer the stack name recorded in its meta file (keeps hostname stable),
|
|
258
|
+
// but only when the user did not explicitly pass --name.
|
|
259
|
+
const effectiveStackName =
|
|
260
|
+
!explicitName && reusedSandboxMeta?.stackName
|
|
261
|
+
? sanitizeStackName(reusedSandboxMeta.stackName, { fallback: baseStackName, maxLen: 64 })
|
|
262
|
+
: newStackName;
|
|
263
|
+
|
|
264
|
+
// Safety marker to ensure we only delete what we created.
|
|
265
|
+
const markerPath = join(sandboxDir, '.happier-stack-sandbox-marker');
|
|
266
|
+
// Always ensure the marker exists for safety; write meta only for new sandboxes.
|
|
267
|
+
try {
|
|
268
|
+
if (!existsSync(markerPath)) {
|
|
269
|
+
await writeFile(markerPath, 'review-pr\n', 'utf-8');
|
|
270
|
+
}
|
|
271
|
+
} catch {
|
|
272
|
+
// ignore; deletion guard will fail closed later if marker is missing
|
|
273
|
+
}
|
|
274
|
+
if (createdNewSandbox && existsSync(markerPath)) {
|
|
275
|
+
try {
|
|
276
|
+
await writeReviewPrSandboxMeta({ sandboxDir, baseStackName, stackName: effectiveStackName, argv });
|
|
277
|
+
} catch {
|
|
278
|
+
// ignore
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const bin = join(rootDir, 'bin', 'hstack.mjs');
|
|
283
|
+
|
|
284
|
+
let child = null;
|
|
285
|
+
let gotSignal = null;
|
|
286
|
+
let childExitCode = null;
|
|
287
|
+
|
|
288
|
+
const forwardSignal = (sig) => {
|
|
289
|
+
const first = gotSignal == null;
|
|
290
|
+
gotSignal = gotSignal ?? sig;
|
|
291
|
+
if (first && process.stdout.isTTY && !json) {
|
|
292
|
+
// eslint-disable-next-line no-console
|
|
293
|
+
console.log('\n[review-pr] received Ctrl+C — cleaning up sandbox, please wait...');
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
child?.kill(sig);
|
|
297
|
+
} catch {
|
|
298
|
+
// ignore
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const onSigInt = () => forwardSignal('SIGINT');
|
|
303
|
+
const onSigTerm = () => forwardSignal('SIGTERM');
|
|
304
|
+
process.on('SIGINT', onSigInt);
|
|
305
|
+
process.on('SIGTERM', onSigTerm);
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
const wantsStart = flags.has('--start') || flags.has('--prod');
|
|
309
|
+
const hasMobileFlag = argv.includes('--mobile') || argv.includes('--with-mobile') || argv.includes('--no-mobile');
|
|
310
|
+
const argvWithDefaults =
|
|
311
|
+
process.stdout.isTTY && !json && !wantsStart && !hasMobileFlag ? [...argv, '--mobile'] : argv;
|
|
312
|
+
|
|
313
|
+
// If the caller did not explicitly name the stack, make it unique per run.
|
|
314
|
+
// This prevents browser storage collisions when sandboxes are deleted between runs.
|
|
315
|
+
const hasNameFlag = argvWithDefaults.some((a) => a === '--name' || a.startsWith('--name='));
|
|
316
|
+
const argvFinal = hasNameFlag ? argvWithDefaults : [...argvWithDefaults, `--name=${effectiveStackName}`];
|
|
317
|
+
|
|
318
|
+
// Sandbox-only port overrides (useful for VM testing where host port-forwarding expects specific ranges).
|
|
319
|
+
const portEnv = resolveSandboxPortEnvOverrides(argvFinal);
|
|
320
|
+
const argvForSetupPr = stripArgv(argvFinal, [
|
|
321
|
+
'--vm-ports',
|
|
322
|
+
'--stack-port-start',
|
|
323
|
+
'--expo-dev-port-strategy',
|
|
324
|
+
'--expo-dev-port-base',
|
|
325
|
+
'--expo-dev-port-range',
|
|
326
|
+
'--expo-dev-port',
|
|
327
|
+
]);
|
|
328
|
+
|
|
329
|
+
child = spawn(process.execPath, [bin, '--sandbox-dir', sandboxDir, 'setup-pr', ...argvForSetupPr], {
|
|
330
|
+
cwd: rootDir,
|
|
331
|
+
env: portEnv ? { ...process.env, ...portEnv } : process.env,
|
|
332
|
+
stdio: 'inherit',
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const { code } = await waitForExit(child);
|
|
336
|
+
childExitCode = code;
|
|
337
|
+
process.exitCode = code;
|
|
338
|
+
} finally {
|
|
339
|
+
process.off('SIGINT', onSigInt);
|
|
340
|
+
process.off('SIGTERM', onSigTerm);
|
|
341
|
+
|
|
342
|
+
steps.start('stop sandbox processes (best-effort)');
|
|
343
|
+
try {
|
|
344
|
+
// Best-effort stop before deleting the sandbox.
|
|
345
|
+
await tryStopSandbox({ rootDir, sandboxDir });
|
|
346
|
+
steps.stop('✓', 'stop sandbox processes (best-effort)');
|
|
347
|
+
} catch {
|
|
348
|
+
steps.stop('x', 'stop sandbox processes (best-effort)');
|
|
349
|
+
// eslint-disable-next-line no-console
|
|
350
|
+
console.warn(`[review-pr] warning: failed to stop all sandbox processes. Attempting cleanup anyway.`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// On failure, offer to keep the sandbox for inspection (TTY only).
|
|
354
|
+
// - `--keep-sandbox` always wins (no prompt)
|
|
355
|
+
// - on signals, don't prompt (just follow the normal cleanup rules)
|
|
356
|
+
const keepSandbox = flags.has('--keep-sandbox');
|
|
357
|
+
const failed = !json && (childExitCode ?? 0) !== 0;
|
|
358
|
+
const canPromptKeep =
|
|
359
|
+
failed &&
|
|
360
|
+
!keepSandbox &&
|
|
361
|
+
!gotSignal &&
|
|
362
|
+
Boolean(process.stdout.isTTY && process.stdin.isTTY) &&
|
|
363
|
+
!json;
|
|
364
|
+
|
|
365
|
+
let keepOnFail = false;
|
|
366
|
+
if (failed && !keepSandbox && !gotSignal) {
|
|
367
|
+
if (canPromptKeep) {
|
|
368
|
+
// Default: keep in verbose mode, delete otherwise.
|
|
369
|
+
const defaultKeep = getVerbosityLevel(process.env) > 0;
|
|
370
|
+
keepOnFail = await withRl(async (rl) => {
|
|
371
|
+
return await promptSelect(rl, {
|
|
372
|
+
title: 'Review-pr failed. Keep the sandbox for inspection?',
|
|
373
|
+
options: [
|
|
374
|
+
{ label: 'yes (keep sandbox directory)', value: true },
|
|
375
|
+
{ label: 'no (delete sandbox directory)', value: false },
|
|
376
|
+
],
|
|
377
|
+
defaultIndex: defaultKeep ? 0 : 1,
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
} else {
|
|
381
|
+
// Non-interactive: keep old behavior (verbose keeps, otherwise delete).
|
|
382
|
+
keepOnFail = getVerbosityLevel(process.env) > 0;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const shouldDeleteSandbox = !keepSandbox && !(failed && keepOnFail);
|
|
387
|
+
|
|
388
|
+
steps.start('delete sandbox directory');
|
|
389
|
+
// Only delete if marker exists (paranoia guard).
|
|
390
|
+
// Note: if marker is missing, we intentionally leave the sandbox dir on disk.
|
|
391
|
+
try {
|
|
392
|
+
if (!existsSync(markerPath)) {
|
|
393
|
+
throw new Error('missing marker');
|
|
394
|
+
}
|
|
395
|
+
if (!shouldDeleteSandbox) {
|
|
396
|
+
steps.stop('!', 'delete sandbox directory');
|
|
397
|
+
// eslint-disable-next-line no-console
|
|
398
|
+
console.warn(`[review-pr] sandbox preserved at: ${sandboxDir}`);
|
|
399
|
+
if (!json && (childExitCode ?? 0) !== 0) {
|
|
400
|
+
// eslint-disable-next-line no-console
|
|
401
|
+
console.warn(`[review-pr] tip: inspect stack wiring with:`);
|
|
402
|
+
// eslint-disable-next-line no-console
|
|
403
|
+
console.warn(` npx --yes -p @happier-dev/stack hstack --sandbox-dir "${sandboxDir}" stack info ${effectiveStackName}`);
|
|
404
|
+
}
|
|
405
|
+
} else {
|
|
406
|
+
await rm(markerPath, { force: false });
|
|
407
|
+
await rm(sandboxDir, { recursive: true, force: true });
|
|
408
|
+
steps.stop('✓', 'delete sandbox directory');
|
|
409
|
+
}
|
|
410
|
+
} catch {
|
|
411
|
+
steps.stop('x', 'delete sandbox directory');
|
|
412
|
+
// eslint-disable-next-line no-console
|
|
413
|
+
console.warn(`[review-pr] warning: failed to delete sandbox directory: ${sandboxDir}`);
|
|
414
|
+
// eslint-disable-next-line no-console
|
|
415
|
+
console.warn(`[review-pr] you can remove it manually after stopping any remaining processes.`);
|
|
416
|
+
// Preserve conventional exit codes on signals.
|
|
417
|
+
if (gotSignal) {
|
|
418
|
+
const code = gotSignal === 'SIGINT' ? 130 : gotSignal === 'SIGTERM' ? 143 : 1;
|
|
419
|
+
process.exitCode = process.exitCode ?? code;
|
|
420
|
+
}
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
// Preserve conventional exit codes on signals.
|
|
424
|
+
if (gotSignal) {
|
|
425
|
+
const code = gotSignal === 'SIGINT' ? 130 : gotSignal === 'SIGTERM' ? 143 : 1;
|
|
426
|
+
process.exitCode = process.exitCode ?? code;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
main().catch((err) => {
|
|
432
|
+
console.error('[review-pr] failed:', err);
|
|
433
|
+
process.exit(1);
|
|
434
|
+
});
|
|
435
|
+
|