happy-stacks 0.3.0 → 0.4.0

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 (94) hide show
  1. package/README.md +29 -7
  2. package/bin/happys.mjs +114 -15
  3. package/docs/happy-development.md +2 -2
  4. package/docs/isolated-linux-vm.md +82 -0
  5. package/docs/mobile-ios.md +112 -54
  6. package/package.json +5 -1
  7. package/scripts/auth.mjs +11 -7
  8. package/scripts/build.mjs +54 -7
  9. package/scripts/daemon.mjs +166 -10
  10. package/scripts/dev.mjs +181 -46
  11. package/scripts/edison.mjs +4 -2
  12. package/scripts/init.mjs +3 -1
  13. package/scripts/install.mjs +112 -16
  14. package/scripts/lint.mjs +24 -4
  15. package/scripts/mobile.mjs +88 -104
  16. package/scripts/mobile_dev_client.mjs +83 -0
  17. package/scripts/provision/linux-ubuntu-review-pr.sh +51 -0
  18. package/scripts/review.mjs +217 -0
  19. package/scripts/review_pr.mjs +368 -0
  20. package/scripts/run.mjs +83 -9
  21. package/scripts/service.mjs +2 -2
  22. package/scripts/setup.mjs +42 -43
  23. package/scripts/setup_pr.mjs +591 -34
  24. package/scripts/stack.mjs +503 -45
  25. package/scripts/tailscale.mjs +37 -1
  26. package/scripts/test.mjs +45 -8
  27. package/scripts/tui.mjs +309 -39
  28. package/scripts/typecheck.mjs +24 -4
  29. package/scripts/utils/auth/daemon_gate.mjs +55 -0
  30. package/scripts/utils/auth/daemon_gate.test.mjs +37 -0
  31. package/scripts/utils/auth/guided_pr_auth.mjs +79 -0
  32. package/scripts/utils/auth/guided_stack_web_login.mjs +75 -0
  33. package/scripts/utils/auth/interactive_stack_auth.mjs +72 -0
  34. package/scripts/utils/auth/login_ux.mjs +32 -13
  35. package/scripts/utils/auth/sources.mjs +26 -0
  36. package/scripts/utils/auth/stack_guided_login.mjs +353 -0
  37. package/scripts/utils/cli/cli_registry.mjs +24 -0
  38. package/scripts/utils/cli/cwd_scope.mjs +82 -0
  39. package/scripts/utils/cli/cwd_scope.test.mjs +77 -0
  40. package/scripts/utils/cli/log_forwarder.mjs +157 -0
  41. package/scripts/utils/cli/prereqs.mjs +72 -0
  42. package/scripts/utils/cli/progress.mjs +126 -0
  43. package/scripts/utils/cli/verbosity.mjs +12 -0
  44. package/scripts/utils/dev/daemon.mjs +47 -3
  45. package/scripts/utils/dev/expo_dev.mjs +246 -0
  46. package/scripts/utils/dev/expo_dev.test.mjs +76 -0
  47. package/scripts/utils/dev/server.mjs +15 -25
  48. package/scripts/utils/dev_auth_key.mjs +169 -0
  49. package/scripts/utils/expo/command.mjs +52 -0
  50. package/scripts/utils/expo/expo.mjs +20 -1
  51. package/scripts/utils/expo/metro_ports.mjs +114 -0
  52. package/scripts/utils/git/git.mjs +67 -0
  53. package/scripts/utils/git/worktrees.mjs +24 -20
  54. package/scripts/utils/handy_master_secret.mjs +94 -0
  55. package/scripts/utils/mobile/config.mjs +31 -0
  56. package/scripts/utils/mobile/dev_client_links.mjs +60 -0
  57. package/scripts/utils/mobile/identifiers.mjs +47 -0
  58. package/scripts/utils/mobile/identifiers.test.mjs +42 -0
  59. package/scripts/utils/mobile/ios_xcodeproj_patch.mjs +128 -0
  60. package/scripts/utils/mobile/ios_xcodeproj_patch.test.mjs +98 -0
  61. package/scripts/utils/net/lan_ip.mjs +24 -0
  62. package/scripts/utils/net/ports.mjs +9 -1
  63. package/scripts/utils/net/url.mjs +30 -0
  64. package/scripts/utils/net/url.test.mjs +20 -0
  65. package/scripts/utils/paths/localhost_host.mjs +50 -3
  66. package/scripts/utils/paths/paths.mjs +42 -38
  67. package/scripts/utils/proc/parallel.mjs +25 -0
  68. package/scripts/utils/proc/pm.mjs +69 -12
  69. package/scripts/utils/proc/proc.mjs +76 -2
  70. package/scripts/utils/review/base_ref.mjs +74 -0
  71. package/scripts/utils/review/base_ref.test.mjs +54 -0
  72. package/scripts/utils/review/runners/coderabbit.mjs +19 -0
  73. package/scripts/utils/review/runners/codex.mjs +51 -0
  74. package/scripts/utils/review/targets.mjs +24 -0
  75. package/scripts/utils/review/targets.test.mjs +36 -0
  76. package/scripts/utils/sandbox/review_pr_sandbox.mjs +106 -0
  77. package/scripts/utils/server/mobile_api_url.mjs +61 -0
  78. package/scripts/utils/server/mobile_api_url.test.mjs +41 -0
  79. package/scripts/utils/server/urls.mjs +14 -4
  80. package/scripts/utils/service/autostart_darwin.mjs +42 -2
  81. package/scripts/utils/service/autostart_darwin.test.mjs +50 -0
  82. package/scripts/utils/stack/context.mjs +2 -2
  83. package/scripts/utils/stack/editor_workspace.mjs +2 -2
  84. package/scripts/utils/stack/pr_stack_name.mjs +16 -0
  85. package/scripts/utils/stack/runtime_state.mjs +2 -1
  86. package/scripts/utils/stack/startup.mjs +7 -0
  87. package/scripts/utils/stack/stop.mjs +15 -4
  88. package/scripts/utils/stack_context.mjs +23 -0
  89. package/scripts/utils/stack_runtime_state.mjs +104 -0
  90. package/scripts/utils/stacks.mjs +38 -0
  91. package/scripts/utils/ui/qr.mjs +17 -0
  92. package/scripts/utils/validate.mjs +88 -0
  93. package/scripts/worktrees.mjs +141 -55
  94. package/scripts/utils/dev/expo_web.mjs +0 -112
package/scripts/setup.mjs CHANGED
@@ -23,6 +23,26 @@ import { openUrlInBrowser } from './utils/ui/browser.mjs';
23
23
  import { commandExists } from './utils/proc/commands.mjs';
24
24
  import { readEnvValueFromFile } from './utils/env/read.mjs';
25
25
  import { readServerPortFromEnvFile, resolveServerPortFromEnv } from './utils/server/port.mjs';
26
+ import { guidedStackWebSignupThenLogin } from './utils/auth/guided_stack_web_login.mjs';
27
+
28
+ async function resolveMainWebappUrlForAuth({ rootDir, port }) {
29
+ try {
30
+ const raw = await runCapture(process.execPath, [join(rootDir, 'scripts', 'auth.mjs'), 'login', '--print', '--json'], {
31
+ cwd: rootDir,
32
+ env: {
33
+ ...process.env,
34
+ HAPPY_STACKS_SERVER_PORT: String(port),
35
+ HAPPY_LOCAL_SERVER_PORT: String(port),
36
+ },
37
+ });
38
+ const parsed = JSON.parse(String(raw ?? '').trim());
39
+ const cmd = typeof parsed?.cmd === 'string' ? parsed.cmd : '';
40
+ const m = cmd.match(/HAPPY_WEBAPP_URL="([^"]+)"/);
41
+ return m?.[1] ? String(m[1]) : '';
42
+ } catch {
43
+ return '';
44
+ }
45
+ }
26
46
 
27
47
  async function resolveMainServerPort() {
28
48
  // Priority:
@@ -39,14 +59,18 @@ async function resolveMainServerPort() {
39
59
  }
40
60
 
41
61
  async function ensureSetupConfigPersisted({ rootDir, profile, serverComponent, tailscaleWanted, menubarMode }) {
62
+ const repoSourceForProfile =
63
+ profile === 'selfhost' ? (serverComponent === 'happy-server-light' ? 'forks' : 'upstream') : null;
42
64
  const updates = [
43
65
  { key: 'HAPPY_STACKS_SERVER_COMPONENT', value: serverComponent },
44
66
  { key: 'HAPPY_LOCAL_SERVER_COMPONENT', value: serverComponent },
45
- // Default for selfhost: upstream.
46
- ...(profile === 'selfhost'
67
+ // Default for selfhost:
68
+ // - full server: upstream (slopus/*)
69
+ // - server-light: forks (sqlite server-light is not available upstream today)
70
+ ...(repoSourceForProfile
47
71
  ? [
48
- { key: 'HAPPY_STACKS_REPO_SOURCE', value: 'upstream' },
49
- { key: 'HAPPY_LOCAL_REPO_SOURCE', value: 'upstream' },
72
+ { key: 'HAPPY_STACKS_REPO_SOURCE', value: repoSourceForProfile },
73
+ { key: 'HAPPY_LOCAL_REPO_SOURCE', value: repoSourceForProfile },
50
74
  ]
51
75
  : []),
52
76
  { key: 'HAPPY_STACKS_MENUBAR_MODE', value: menubarMode },
@@ -510,10 +534,11 @@ async function cmdSetup({ rootDir, argv }) {
510
534
  }
511
535
  } else {
512
536
  // Selfhost setup: run non-interactively and keep it simple.
537
+ const repoFlag = serverComponent === 'happy-server-light' ? '--forks' : '--upstream';
513
538
  await runNodeScript({
514
539
  rootDir,
515
540
  rel: 'scripts/install.mjs',
516
- args: [`--server=${serverComponent}`, '--upstream'],
541
+ args: [`--server=${serverComponent}`, repoFlag],
517
542
  });
518
543
  }
519
544
 
@@ -592,46 +617,20 @@ async function cmdSetup({ rootDir, argv }) {
592
617
  // eslint-disable-next-line no-console
593
618
  console.log('[setup] auth: already configured (access.key exists)');
594
619
  } else {
595
- // Before starting an interactive login, offer the best available shortcut in this order:
596
- // For selfhost profile:
597
- // - prefer reusing legacy ~/.happy creds if present (maintainers often already have a local install)
598
- // - otherwise, run the normal login flow
599
- let reused = false;
600
620
  if (interactive) {
601
- const legacyAccessKey = join(homedir(), '.happy', 'cli', 'access.key');
602
- const allowLegacy = !isSandboxed() || sandboxAllowsGlobalSideEffects();
603
- const hasLegacy = allowLegacy && existsSync(legacyAccessKey);
604
-
605
- if (hasLegacy) {
606
- const options = [];
607
- if (hasLegacy) {
608
- options.push({ label: 'reuse legacy ~/.happy (symlink; stays up to date)', value: 'legacy-link' });
609
- options.push({ label: 'reuse legacy ~/.happy (copy; more isolated)', value: 'legacy-copy' });
610
- }
611
- options.push({ label: 'do login flow instead', value: 'login' });
612
-
613
- const choice = await withRl(async (rl) => {
614
- return await promptSelect(rl, {
615
- title:
616
- 'We found existing credentials on this machine. How should Happy Stacks main authenticate?',
617
- options,
618
- defaultIndex: 0,
619
- });
620
- });
621
-
622
- if (choice === 'legacy-link') {
623
- await runNodeScript({ rootDir, rel: 'scripts/auth.mjs', args: ['copy-from', 'legacy', '--allow-main', '--link'] });
624
- reused = existsSync(accessKey);
625
- } else if (choice === 'legacy-copy') {
626
- await runNodeScript({ rootDir, rel: 'scripts/auth.mjs', args: ['copy-from', 'legacy', '--allow-main'] });
627
- reused = existsSync(accessKey);
628
- }
629
- }
630
- }
631
-
632
- if (!reused) {
633
- await runNodeScript({ rootDir, rel: 'scripts/auth.mjs', args: ['login', `--context=${ctx}`] });
621
+ const webappUrl = await resolveMainWebappUrlForAuth({ rootDir, port });
622
+ await guidedStackWebSignupThenLogin({ webappUrl, stackName: 'main' });
634
623
  }
624
+ await runNodeScript({
625
+ rootDir,
626
+ rel: 'scripts/auth.mjs',
627
+ args: ['login', `--context=${ctx}`, '--quiet'],
628
+ env: {
629
+ ...process.env,
630
+ HAPPY_STACKS_SERVER_PORT: String(port),
631
+ HAPPY_LOCAL_SERVER_PORT: String(port),
632
+ },
633
+ });
635
634
 
636
635
  if (!existsSync(accessKey)) {
637
636
  // eslint-disable-next-line no-console