happy-stacks 0.2.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.
- package/README.md +84 -25
- package/bin/happys.mjs +116 -17
- package/docs/happy-development.md +2 -2
- package/docs/isolated-linux-vm.md +82 -0
- package/docs/mobile-ios.md +112 -54
- package/package.json +5 -1
- package/scripts/auth.mjs +59 -208
- package/scripts/build.mjs +58 -12
- package/scripts/cli-link.mjs +3 -3
- package/scripts/completion.mjs +5 -5
- package/scripts/daemon.mjs +168 -20
- package/scripts/dev.mjs +196 -70
- package/scripts/doctor.mjs +20 -36
- package/scripts/edison.mjs +105 -78
- package/scripts/happy.mjs +8 -19
- package/scripts/init.mjs +8 -14
- package/scripts/install.mjs +119 -23
- package/scripts/lint.mjs +31 -32
- package/scripts/menubar.mjs +6 -13
- package/scripts/migrate.mjs +11 -21
- package/scripts/mobile.mjs +93 -108
- package/scripts/mobile_dev_client.mjs +83 -0
- package/scripts/provision/linux-ubuntu-review-pr.sh +51 -0
- package/scripts/review.mjs +217 -0
- package/scripts/review_pr.mjs +368 -0
- package/scripts/run.mjs +95 -21
- package/scripts/self.mjs +11 -29
- package/scripts/server_flavor.mjs +4 -4
- package/scripts/service.mjs +19 -29
- package/scripts/setup.mjs +63 -160
- package/scripts/setup_pr.mjs +592 -52
- package/scripts/stack.mjs +608 -200
- package/scripts/stop.mjs +3 -3
- package/scripts/tailscale.mjs +44 -11
- package/scripts/test.mjs +52 -36
- package/scripts/tui.mjs +314 -74
- package/scripts/typecheck.mjs +31 -32
- package/scripts/ui_gateway.mjs +1 -1
- package/scripts/uninstall.mjs +6 -6
- package/scripts/utils/auth/daemon_gate.mjs +55 -0
- package/scripts/utils/auth/daemon_gate.test.mjs +37 -0
- package/scripts/utils/auth/dev_key.mjs +163 -0
- package/scripts/utils/{auth_files.mjs → auth/files.mjs} +2 -4
- package/scripts/utils/auth/guided_pr_auth.mjs +79 -0
- package/scripts/utils/auth/guided_stack_web_login.mjs +75 -0
- package/scripts/utils/auth/handy_master_secret.mjs +68 -0
- package/scripts/utils/auth/interactive_stack_auth.mjs +72 -0
- package/scripts/utils/{auth_login_ux.mjs → auth/login_ux.mjs} +32 -13
- package/scripts/utils/auth/sources.mjs +38 -0
- package/scripts/utils/auth/stack_guided_login.mjs +353 -0
- package/scripts/utils/cli/cli_registry.mjs +24 -0
- package/scripts/utils/cli/cwd_scope.mjs +82 -0
- package/scripts/utils/cli/cwd_scope.test.mjs +77 -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 +72 -0
- package/scripts/utils/cli/progress.mjs +126 -0
- package/scripts/utils/cli/smoke_help.mjs +2 -2
- package/scripts/utils/cli/verbosity.mjs +12 -0
- package/scripts/utils/cli/wizard.mjs +1 -1
- package/scripts/utils/crypto/tokens.mjs +14 -0
- package/scripts/utils/{dev_daemon.mjs → dev/daemon.mjs} +51 -7
- package/scripts/utils/dev/expo_dev.mjs +246 -0
- package/scripts/utils/dev/expo_dev.test.mjs +76 -0
- package/scripts/utils/{dev_server.mjs → dev/server.mjs} +22 -32
- package/scripts/utils/dev_auth_key.mjs +1 -1
- package/scripts/utils/{config.mjs → env/config.mjs} +3 -2
- package/scripts/utils/{dotenv.mjs → env/dotenv.mjs} +3 -0
- package/scripts/utils/{env.mjs → env/env.mjs} +5 -3
- package/scripts/utils/{env_file.mjs → env/env_file.mjs} +2 -1
- package/scripts/utils/{env_local.mjs → env/env_local.mjs} +1 -0
- package/scripts/utils/env/read.mjs +30 -0
- package/scripts/utils/env/values.mjs +13 -0
- package/scripts/utils/expo/command.mjs +52 -0
- package/scripts/utils/{expo.mjs → expo/expo.mjs} +23 -10
- package/scripts/utils/expo/metro_ports.mjs +114 -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/git.mjs +67 -0
- package/scripts/utils/git/refs.mjs +26 -0
- package/scripts/utils/{worktrees.mjs → git/worktrees.mjs} +27 -23
- package/scripts/utils/handy_master_secret.mjs +2 -2
- package/scripts/utils/mobile/config.mjs +31 -0
- package/scripts/utils/mobile/dev_client_links.mjs +60 -0
- package/scripts/utils/mobile/identifiers.mjs +47 -0
- package/scripts/utils/mobile/identifiers.test.mjs +42 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.mjs +128 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.test.mjs +98 -0
- package/scripts/utils/net/dns.mjs +10 -0
- package/scripts/utils/net/lan_ip.mjs +24 -0
- package/scripts/utils/{ports.mjs → net/ports.mjs} +12 -6
- package/scripts/utils/net/url.mjs +30 -0
- package/scripts/utils/net/url.test.mjs +20 -0
- package/scripts/utils/paths/localhost_host.mjs +56 -0
- package/scripts/utils/{paths.mjs → paths/paths.mjs} +52 -45
- package/scripts/utils/{runtime.mjs → paths/runtime.mjs} +3 -1
- package/scripts/utils/proc/commands.mjs +34 -0
- package/scripts/utils/{ownership.mjs → proc/ownership.mjs} +1 -1
- package/scripts/utils/proc/package_scripts.mjs +31 -0
- package/scripts/utils/proc/parallel.mjs +25 -0
- package/scripts/utils/proc/pids.mjs +11 -0
- package/scripts/utils/{pm.mjs → proc/pm.mjs} +128 -158
- package/scripts/utils/{proc.mjs → proc/proc.mjs} +77 -2
- package/scripts/utils/review/base_ref.mjs +74 -0
- package/scripts/utils/review/base_ref.test.mjs +54 -0
- package/scripts/utils/review/runners/coderabbit.mjs +19 -0
- package/scripts/utils/review/runners/codex.mjs +51 -0
- package/scripts/utils/review/targets.mjs +24 -0
- package/scripts/utils/review/targets.test.mjs +36 -0
- package/scripts/utils/sandbox/review_pr_sandbox.mjs +106 -0
- package/scripts/utils/{happy_server_infra.mjs → server/infra/happy_server_infra.mjs} +10 -49
- package/scripts/utils/server/mobile_api_url.mjs +61 -0
- package/scripts/utils/server/mobile_api_url.test.mjs +41 -0
- package/scripts/utils/server/port.mjs +68 -0
- package/scripts/utils/{server.mjs → server/server.mjs} +12 -0
- package/scripts/utils/server/urls.mjs +101 -0
- package/scripts/utils/server/validate.mjs +88 -0
- package/scripts/utils/service/autostart_darwin.mjs +182 -0
- package/scripts/utils/service/autostart_darwin.test.mjs +50 -0
- package/scripts/utils/stack/context.mjs +23 -0
- package/scripts/utils/stack/dirs.mjs +27 -0
- package/scripts/utils/stack/editor_workspace.mjs +152 -0
- package/scripts/utils/stack/names.mjs +12 -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 +45 -0
- package/scripts/utils/{stack_startup.mjs → stack/startup.mjs} +9 -2
- package/scripts/utils/{stack_stop.mjs → stack/stop.mjs} +24 -19
- package/scripts/utils/stack_context.mjs +3 -3
- package/scripts/utils/stack_runtime_state.mjs +1 -1
- package/scripts/utils/stacks.mjs +2 -2
- package/scripts/utils/{browser.mjs → ui/browser.mjs} +1 -1
- package/scripts/utils/ui/qr.mjs +17 -0
- package/scripts/utils/ui/text.mjs +16 -0
- package/scripts/utils/validate.mjs +1 -1
- package/scripts/where.mjs +6 -6
- package/scripts/worktrees.mjs +171 -113
- package/scripts/utils/auth_sources.mjs +0 -12
- package/scripts/utils/dev_expo_web.mjs +0 -112
- package/scripts/utils/localhost_host.mjs +0 -17
- package/scripts/utils/server_port.mjs +0 -9
- package/scripts/utils/server_urls.mjs +0 -54
- /package/scripts/utils/{sandbox.mjs → env/sandbox.mjs} +0 -0
- /package/scripts/utils/{fs.mjs → fs/fs.mjs} +0 -0
- /package/scripts/utils/{canonical_home.mjs → paths/canonical_home.mjs} +0 -0
- /package/scripts/utils/{watch.mjs → proc/watch.mjs} +0 -0
package/scripts/setup.mjs
CHANGED
|
@@ -1,111 +1,44 @@
|
|
|
1
|
-
import './utils/env.mjs';
|
|
1
|
+
import './utils/env/env.mjs';
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { parseArgs } from './utils/cli/args.mjs';
|
|
6
6
|
import { printResult, wantsHelp, wantsJson } from './utils/cli/cli.mjs';
|
|
7
|
-
import { getRootDir, resolveStackEnvPath } from './utils/paths.mjs';
|
|
7
|
+
import { getRootDir, resolveStackEnvPath } from './utils/paths/paths.mjs';
|
|
8
8
|
import { isTty, promptSelect, withRl } from './utils/cli/wizard.mjs';
|
|
9
|
-
import { getCanonicalHomeDir } from './utils/config.mjs';
|
|
10
|
-
import { ensureEnvLocalUpdated } from './utils/env_local.mjs';
|
|
11
|
-
import { run, runCapture } from './utils/proc.mjs';
|
|
12
|
-
import {
|
|
9
|
+
import { getCanonicalHomeDir } from './utils/env/config.mjs';
|
|
10
|
+
import { ensureEnvLocalUpdated } from './utils/env/env_local.mjs';
|
|
11
|
+
import { run, runCapture } from './utils/proc/proc.mjs';
|
|
12
|
+
import { waitForHappyHealthOk } from './utils/server/server.mjs';
|
|
13
13
|
import { tailscaleServeEnable, tailscaleServeHttpsUrlForInternalServerUrl } from './tailscale.mjs';
|
|
14
|
-
import { getRuntimeDir } from './utils/runtime.mjs';
|
|
14
|
+
import { getRuntimeDir } from './utils/paths/runtime.mjs';
|
|
15
15
|
import { readFile } from 'node:fs/promises';
|
|
16
16
|
import { homedir } from 'node:os';
|
|
17
|
-
import { parseDotenv } from './utils/dotenv.mjs';
|
|
18
17
|
import { installService } from './service.mjs';
|
|
19
|
-
import { getDevAuthKeyPath } from './utils/
|
|
20
|
-
import { isSandboxed, sandboxAllowsGlobalSideEffects } from './utils/sandbox.mjs';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return defaultValue;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function normalizeProfile(raw) {
|
|
34
|
-
const v = (raw ?? '').trim().toLowerCase();
|
|
35
|
-
if (!v) return '';
|
|
36
|
-
if (v === 'selfhost' || v === 'self-host' || v === 'self_host' || v === 'host') return 'selfhost';
|
|
37
|
-
if (v === 'dev' || v === 'developer' || v === 'develop') return 'dev';
|
|
38
|
-
return '';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function normalizeServer(raw) {
|
|
42
|
-
const v = (raw ?? '').trim().toLowerCase();
|
|
43
|
-
if (!v) return '';
|
|
44
|
-
if (v === 'light' || v === 'server-light' || v === 'happy-server-light') return 'happy-server-light';
|
|
45
|
-
if (v === 'server' || v === 'full' || v === 'happy-server') return 'happy-server';
|
|
46
|
-
return '';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function boolFromFlags({ flags, onFlag, offFlag, defaultValue }) {
|
|
50
|
-
if (flags.has(offFlag)) return false;
|
|
51
|
-
if (flags.has(onFlag)) return true;
|
|
52
|
-
return defaultValue;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function commandExists(cmd) {
|
|
56
|
-
try {
|
|
57
|
-
const out = (await runCapture('sh', ['-lc', `command -v ${cmd} >/dev/null 2>&1 && echo yes || echo no`])).trim();
|
|
58
|
-
return out === 'yes';
|
|
59
|
-
} catch {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function openUrl(url) {
|
|
65
|
-
const u = String(url ?? '').trim();
|
|
66
|
-
if (!u) return false;
|
|
67
|
-
if (process.platform === 'darwin') {
|
|
68
|
-
await run('open', [u]).catch(() => {});
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
if (process.platform === 'linux') {
|
|
72
|
-
if (await commandExists('xdg-open')) {
|
|
73
|
-
await run('xdg-open', [u]).catch(() => {});
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function waitForHealthOk(internalServerUrl, { timeoutMs = 60_000 } = {}) {
|
|
82
|
-
const deadline = Date.now() + timeoutMs;
|
|
83
|
-
while (Date.now() < deadline) {
|
|
84
|
-
// eslint-disable-next-line no-await-in-loop
|
|
85
|
-
const health = await fetchHappyHealth(internalServerUrl);
|
|
86
|
-
if (health.ok) {
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
// eslint-disable-next-line no-await-in-loop
|
|
90
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
91
|
-
}
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function parseEnvFileText(text) {
|
|
18
|
+
import { getDevAuthKeyPath } from './utils/auth/dev_key.mjs';
|
|
19
|
+
import { isSandboxed, sandboxAllowsGlobalSideEffects } from './utils/env/sandbox.mjs';
|
|
20
|
+
import { boolFromFlags, boolFromFlagsOrKv } from './utils/cli/flags.mjs';
|
|
21
|
+
import { normalizeProfile, normalizeServerComponent } from './utils/cli/normalize.mjs';
|
|
22
|
+
import { openUrlInBrowser } from './utils/ui/browser.mjs';
|
|
23
|
+
import { commandExists } from './utils/proc/commands.mjs';
|
|
24
|
+
import { readEnvValueFromFile } from './utils/env/read.mjs';
|
|
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 }) {
|
|
96
29
|
try {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
return (
|
|
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]) : '';
|
|
109
42
|
} catch {
|
|
110
43
|
return '';
|
|
111
44
|
}
|
|
@@ -116,33 +49,28 @@ async function resolveMainServerPort() {
|
|
|
116
49
|
// - explicit env var
|
|
117
50
|
// - main stack env file (preferred)
|
|
118
51
|
// - default
|
|
119
|
-
const
|
|
120
|
-
(process.env.
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
return Number.isFinite(n) && n > 0 ? n : 3005;
|
|
52
|
+
const hasEnvOverride =
|
|
53
|
+
(process.env.HAPPY_STACKS_SERVER_PORT ?? process.env.HAPPY_LOCAL_SERVER_PORT ?? '').toString().trim() !== '';
|
|
54
|
+
if (hasEnvOverride) {
|
|
55
|
+
return resolveServerPortFromEnv({ env: process.env, defaultPort: 3005 });
|
|
124
56
|
}
|
|
125
57
|
const envPath = resolveStackEnvPath('main').envPath;
|
|
126
|
-
|
|
127
|
-
(await readEnvValueFromFile(envPath, 'HAPPY_LOCAL_SERVER_PORT')) ||
|
|
128
|
-
(await readEnvValueFromFile(envPath, 'HAPPY_STACKS_SERVER_PORT')) ||
|
|
129
|
-
'';
|
|
130
|
-
if (v) {
|
|
131
|
-
const n = Number(v);
|
|
132
|
-
return Number.isFinite(n) && n > 0 ? n : 3005;
|
|
133
|
-
}
|
|
134
|
-
return 3005;
|
|
58
|
+
return await readServerPortFromEnvFile(envPath, { defaultPort: 3005 });
|
|
135
59
|
}
|
|
136
60
|
|
|
137
61
|
async function ensureSetupConfigPersisted({ rootDir, profile, serverComponent, tailscaleWanted, menubarMode }) {
|
|
62
|
+
const repoSourceForProfile =
|
|
63
|
+
profile === 'selfhost' ? (serverComponent === 'happy-server-light' ? 'forks' : 'upstream') : null;
|
|
138
64
|
const updates = [
|
|
139
65
|
{ key: 'HAPPY_STACKS_SERVER_COMPONENT', value: serverComponent },
|
|
140
66
|
{ key: 'HAPPY_LOCAL_SERVER_COMPONENT', value: serverComponent },
|
|
141
|
-
// Default for selfhost:
|
|
142
|
-
|
|
67
|
+
// Default for selfhost:
|
|
68
|
+
// - full server: upstream (slopus/*)
|
|
69
|
+
// - server-light: forks (sqlite server-light is not available upstream today)
|
|
70
|
+
...(repoSourceForProfile
|
|
143
71
|
? [
|
|
144
|
-
{ key: 'HAPPY_STACKS_REPO_SOURCE', value:
|
|
145
|
-
{ key: 'HAPPY_LOCAL_REPO_SOURCE', value:
|
|
72
|
+
{ key: 'HAPPY_STACKS_REPO_SOURCE', value: repoSourceForProfile },
|
|
73
|
+
{ key: 'HAPPY_LOCAL_REPO_SOURCE', value: repoSourceForProfile },
|
|
146
74
|
]
|
|
147
75
|
: []),
|
|
148
76
|
{ key: 'HAPPY_STACKS_MENUBAR_MODE', value: menubarMode },
|
|
@@ -393,8 +321,8 @@ async function cmdSetup({ rootDir, argv }) {
|
|
|
393
321
|
const supportsAutostart = platform === 'darwin' || platform === 'linux';
|
|
394
322
|
const supportsMenubar = platform === 'darwin';
|
|
395
323
|
|
|
396
|
-
const serverFromArg =
|
|
397
|
-
let serverComponent = serverFromArg ||
|
|
324
|
+
const serverFromArg = normalizeServerComponent(kv.get('--server'));
|
|
325
|
+
let serverComponent = serverFromArg || normalizeServerComponent(process.env.HAPPY_STACKS_SERVER_COMPONENT) || 'happy-server-light';
|
|
398
326
|
if (profile === 'selfhost' && interactive && !serverFromArg) {
|
|
399
327
|
serverComponent = await withRl(async (rl) => {
|
|
400
328
|
const picked = await promptSelect(rl, {
|
|
@@ -606,10 +534,11 @@ async function cmdSetup({ rootDir, argv }) {
|
|
|
606
534
|
}
|
|
607
535
|
} else {
|
|
608
536
|
// Selfhost setup: run non-interactively and keep it simple.
|
|
537
|
+
const repoFlag = serverComponent === 'happy-server-light' ? '--forks' : '--upstream';
|
|
609
538
|
await runNodeScript({
|
|
610
539
|
rootDir,
|
|
611
540
|
rel: 'scripts/install.mjs',
|
|
612
|
-
args: [`--server=${serverComponent}`,
|
|
541
|
+
args: [`--server=${serverComponent}`, repoFlag],
|
|
613
542
|
});
|
|
614
543
|
}
|
|
615
544
|
|
|
@@ -645,12 +574,12 @@ async function cmdSetup({ rootDir, argv }) {
|
|
|
645
574
|
// eslint-disable-next-line no-console
|
|
646
575
|
console.log(res.enableUrl);
|
|
647
576
|
// Best-effort open
|
|
648
|
-
await
|
|
577
|
+
await openUrlInBrowser(res.enableUrl).catch(() => {});
|
|
649
578
|
}
|
|
650
579
|
} catch (e) {
|
|
651
580
|
// eslint-disable-next-line no-console
|
|
652
581
|
console.log('[setup] tailscale not available. Install it from: https://tailscale.com/download');
|
|
653
|
-
await
|
|
582
|
+
await openUrlInBrowser('https://tailscale.com/download').catch(() => {});
|
|
654
583
|
}
|
|
655
584
|
}
|
|
656
585
|
|
|
@@ -664,7 +593,7 @@ async function cmdSetup({ rootDir, argv }) {
|
|
|
664
593
|
await spawnDetachedNodeScript({ rootDir, rel: 'scripts/run.mjs', args: [] });
|
|
665
594
|
}
|
|
666
595
|
|
|
667
|
-
const ready = await
|
|
596
|
+
const ready = await waitForHappyHealthOk(internalServerUrl, { timeoutMs: 90_000 });
|
|
668
597
|
if (!ready) {
|
|
669
598
|
// eslint-disable-next-line no-console
|
|
670
599
|
console.log(`[setup] started, but server did not become healthy yet: ${internalServerUrl}`);
|
|
@@ -688,46 +617,20 @@ async function cmdSetup({ rootDir, argv }) {
|
|
|
688
617
|
// eslint-disable-next-line no-console
|
|
689
618
|
console.log('[setup] auth: already configured (access.key exists)');
|
|
690
619
|
} else {
|
|
691
|
-
// Before starting an interactive login, offer the best available shortcut in this order:
|
|
692
|
-
// For selfhost profile:
|
|
693
|
-
// - prefer reusing legacy ~/.happy creds if present (maintainers often already have a local install)
|
|
694
|
-
// - otherwise, run the normal login flow
|
|
695
|
-
let reused = false;
|
|
696
620
|
if (interactive) {
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
const hasLegacy = allowLegacy && existsSync(legacyAccessKey);
|
|
700
|
-
|
|
701
|
-
if (hasLegacy) {
|
|
702
|
-
const options = [];
|
|
703
|
-
if (hasLegacy) {
|
|
704
|
-
options.push({ label: 'reuse legacy ~/.happy (symlink; stays up to date)', value: 'legacy-link' });
|
|
705
|
-
options.push({ label: 'reuse legacy ~/.happy (copy; more isolated)', value: 'legacy-copy' });
|
|
706
|
-
}
|
|
707
|
-
options.push({ label: 'do login flow instead', value: 'login' });
|
|
708
|
-
|
|
709
|
-
const choice = await withRl(async (rl) => {
|
|
710
|
-
return await promptSelect(rl, {
|
|
711
|
-
title:
|
|
712
|
-
'We found existing credentials on this machine. How should Happy Stacks main authenticate?',
|
|
713
|
-
options,
|
|
714
|
-
defaultIndex: 0,
|
|
715
|
-
});
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
if (choice === 'legacy-link') {
|
|
719
|
-
await runNodeScript({ rootDir, rel: 'scripts/auth.mjs', args: ['copy-from', 'legacy', '--allow-main', '--link'] });
|
|
720
|
-
reused = existsSync(accessKey);
|
|
721
|
-
} else if (choice === 'legacy-copy') {
|
|
722
|
-
await runNodeScript({ rootDir, rel: 'scripts/auth.mjs', args: ['copy-from', 'legacy', '--allow-main'] });
|
|
723
|
-
reused = existsSync(accessKey);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
if (!reused) {
|
|
729
|
-
await runNodeScript({ rootDir, rel: 'scripts/auth.mjs', args: ['login', `--context=${ctx}`] });
|
|
621
|
+
const webappUrl = await resolveMainWebappUrlForAuth({ rootDir, port });
|
|
622
|
+
await guidedStackWebSignupThenLogin({ webappUrl, stackName: 'main' });
|
|
730
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
|
+
});
|
|
731
634
|
|
|
732
635
|
if (!existsSync(accessKey)) {
|
|
733
636
|
// eslint-disable-next-line no-console
|
|
@@ -742,7 +645,7 @@ async function cmdSetup({ rootDir, argv }) {
|
|
|
742
645
|
console.log('[setup] tip: when you are ready, authenticate with: happys auth login');
|
|
743
646
|
}
|
|
744
647
|
|
|
745
|
-
await
|
|
648
|
+
await openUrlInBrowser(openTarget).catch(() => {});
|
|
746
649
|
// eslint-disable-next-line no-console
|
|
747
650
|
console.log(`[setup] open: ${openTarget}`);
|
|
748
651
|
}
|