happy-stacks 0.1.2 → 0.2.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 +121 -83
- package/bin/happys.mjs +70 -10
- package/docs/edison.md +381 -0
- package/docs/happy-development.md +733 -0
- package/docs/menubar.md +54 -0
- package/docs/paths-and-env.md +141 -0
- package/docs/stacks.md +39 -0
- package/extras/swiftbar/auth-login.sh +5 -2
- package/extras/swiftbar/git-cache-refresh.sh +130 -0
- package/extras/swiftbar/happy-stacks.5s.sh +131 -81
- package/extras/swiftbar/happys-term.sh +15 -38
- package/extras/swiftbar/happys.sh +15 -32
- package/extras/swiftbar/install.sh +99 -13
- package/extras/swiftbar/lib/git.sh +309 -1
- package/extras/swiftbar/lib/icons.sh +2 -2
- package/extras/swiftbar/lib/render.sh +209 -80
- package/extras/swiftbar/lib/system.sh +27 -4
- package/extras/swiftbar/lib/utils.sh +311 -28
- package/extras/swiftbar/pnpm.sh +2 -1
- package/extras/swiftbar/set-interval.sh +10 -5
- package/extras/swiftbar/set-server-flavor.sh +11 -2
- package/extras/swiftbar/wt-pr.sh +9 -2
- package/package.json +2 -1
- package/scripts/auth.mjs +560 -112
- package/scripts/build.mjs +24 -4
- package/scripts/cli-link.mjs +3 -3
- package/scripts/completion.mjs +15 -8
- package/scripts/daemon.mjs +130 -20
- package/scripts/dev.mjs +201 -133
- package/scripts/doctor.mjs +26 -21
- package/scripts/edison.mjs +1828 -0
- package/scripts/happy.mjs +3 -7
- package/scripts/init.mjs +43 -20
- package/scripts/install.mjs +14 -8
- package/scripts/lint.mjs +145 -0
- package/scripts/menubar.mjs +81 -8
- package/scripts/migrate.mjs +25 -15
- package/scripts/mobile.mjs +13 -7
- package/scripts/run.mjs +114 -27
- package/scripts/self.mjs +3 -7
- package/scripts/server_flavor.mjs +3 -3
- package/scripts/service.mjs +15 -2
- package/scripts/setup.mjs +790 -0
- package/scripts/setup_pr.mjs +182 -0
- package/scripts/stack.mjs +1792 -254
- package/scripts/stop.mjs +6 -3
- package/scripts/tailscale.mjs +17 -2
- package/scripts/test.mjs +144 -0
- package/scripts/tui.mjs +556 -0
- package/scripts/typecheck.mjs +2 -2
- package/scripts/ui_gateway.mjs +2 -2
- package/scripts/uninstall.mjs +18 -10
- package/scripts/utils/auth_files.mjs +58 -0
- package/scripts/utils/auth_login_ux.mjs +76 -0
- package/scripts/utils/auth_sources.mjs +12 -0
- package/scripts/utils/browser.mjs +22 -0
- package/scripts/utils/canonical_home.mjs +20 -0
- package/scripts/utils/{cli_registry.mjs → cli/cli_registry.mjs} +48 -0
- package/scripts/utils/{wizard.mjs → cli/wizard.mjs} +1 -1
- package/scripts/utils/config.mjs +6 -2
- package/scripts/utils/dev_auth_key.mjs +169 -0
- package/scripts/utils/dev_daemon.mjs +104 -0
- package/scripts/utils/dev_expo_web.mjs +112 -0
- package/scripts/utils/dev_server.mjs +183 -0
- package/scripts/utils/env.mjs +60 -11
- package/scripts/utils/env_file.mjs +36 -0
- package/scripts/utils/expo.mjs +4 -2
- package/scripts/utils/handy_master_secret.mjs +94 -0
- package/scripts/utils/happy_server_infra.mjs +100 -46
- package/scripts/utils/localhost_host.mjs +17 -0
- package/scripts/utils/ownership.mjs +135 -0
- package/scripts/utils/paths.mjs +5 -2
- package/scripts/utils/pm.mjs +121 -20
- package/scripts/utils/proc.mjs +29 -2
- package/scripts/utils/runtime.mjs +1 -3
- package/scripts/utils/sandbox.mjs +14 -0
- package/scripts/utils/server.mjs +24 -0
- package/scripts/utils/server_port.mjs +9 -0
- package/scripts/utils/server_urls.mjs +54 -0
- package/scripts/utils/stack_context.mjs +23 -0
- package/scripts/utils/stack_runtime_state.mjs +104 -0
- package/scripts/utils/stack_startup.mjs +208 -0
- package/scripts/utils/stack_stop.mjs +79 -30
- package/scripts/utils/stacks.mjs +38 -0
- package/scripts/utils/watch.mjs +63 -0
- package/scripts/utils/worktrees.mjs +57 -1
- package/scripts/where.mjs +14 -7
- package/scripts/worktrees.mjs +82 -8
- /package/scripts/utils/{args.mjs → cli/args.mjs} +0 -0
- /package/scripts/utils/{cli.mjs → cli/cli.mjs} +0 -0
- /package/scripts/utils/{smoke_help.mjs → cli/smoke_help.mjs} +0 -0
|
@@ -17,6 +17,146 @@ shorten_path() {
|
|
|
17
17
|
shorten_text "$pretty" "$max"
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
swiftbar_is_sandboxed() {
|
|
21
|
+
[[ -n "${HAPPY_STACKS_SANDBOX_DIR:-}" ]]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
swiftbar_profile_enabled() {
|
|
25
|
+
[[ "${HAPPY_STACKS_SWIFTBAR_PROFILE:-}" == "1" || "${HAPPY_LOCAL_SWIFTBAR_PROFILE:-}" == "1" ]]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
swiftbar_now_ms() {
|
|
29
|
+
# macOS `date` doesn't support %N, so use Time::HiRes when available.
|
|
30
|
+
if command -v perl >/dev/null 2>&1; then
|
|
31
|
+
perl -MTime::HiRes=time -e 'printf("%.0f\n", time()*1000)'
|
|
32
|
+
return
|
|
33
|
+
fi
|
|
34
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
35
|
+
python3 -c 'import time; print(int(time.time()*1000))'
|
|
36
|
+
return
|
|
37
|
+
fi
|
|
38
|
+
# Fallback: seconds granularity.
|
|
39
|
+
echo $(( $(date +%s 2>/dev/null || echo 0) * 1000 ))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
swiftbar_profile_log_file() {
|
|
43
|
+
# Keep logs in the home install by default (stable across repos/worktrees).
|
|
44
|
+
local canonical="${HAPPY_STACKS_CANONICAL_HOME_DIR:-${HAPPY_LOCAL_CANONICAL_HOME_DIR:-$HOME/.happy-stacks}}"
|
|
45
|
+
local home="${HAPPY_STACKS_HOME_DIR:-${HAPPY_LOCAL_DIR:-$canonical}}"
|
|
46
|
+
echo "${home}/cache/swiftbar/profile.log"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
swiftbar_profile_log() {
|
|
50
|
+
# Usage: swiftbar_profile_log "event" "k=v" "k2=v2" ...
|
|
51
|
+
swiftbar_profile_enabled || return 0
|
|
52
|
+
|
|
53
|
+
local log_file
|
|
54
|
+
log_file="$(swiftbar_profile_log_file)"
|
|
55
|
+
mkdir -p "$(dirname "$log_file")" 2>/dev/null || true
|
|
56
|
+
|
|
57
|
+
local ts
|
|
58
|
+
ts="$(swiftbar_now_ms)"
|
|
59
|
+
{
|
|
60
|
+
printf '%s\t%s' "$ts" "$1"
|
|
61
|
+
shift || true
|
|
62
|
+
for kv in "$@"; do
|
|
63
|
+
printf '\t%s' "$kv"
|
|
64
|
+
done
|
|
65
|
+
printf '\n'
|
|
66
|
+
} >>"$log_file" 2>/dev/null || true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
swiftbar_profile_time() {
|
|
70
|
+
# Usage: swiftbar_profile_time <label> -- <command...>
|
|
71
|
+
swiftbar_profile_enabled || { shift; [[ "${1:-}" == "--" ]] && shift; "$@"; return $?; }
|
|
72
|
+
local label="$1"
|
|
73
|
+
shift
|
|
74
|
+
[[ "${1:-}" == "--" ]] && shift
|
|
75
|
+
|
|
76
|
+
local t0 t1 rc
|
|
77
|
+
t0="$(swiftbar_now_ms)"
|
|
78
|
+
"$@"
|
|
79
|
+
rc=$?
|
|
80
|
+
t1="$(swiftbar_now_ms)"
|
|
81
|
+
swiftbar_profile_log "time" "label=${label}" "ms=$((t1 - t0))" "rc=${rc}"
|
|
82
|
+
return $rc
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
swiftbar_cache_hash12() {
|
|
86
|
+
# Usage: swiftbar_cache_hash12 "string"
|
|
87
|
+
local s="$1"
|
|
88
|
+
if command -v md5 >/dev/null 2>&1; then
|
|
89
|
+
md5 -q -s "$s" 2>/dev/null | head -c 12
|
|
90
|
+
return
|
|
91
|
+
fi
|
|
92
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
93
|
+
printf '%s' "$s" | shasum -a 256 2>/dev/null | awk '{print substr($1,1,12)}'
|
|
94
|
+
return
|
|
95
|
+
fi
|
|
96
|
+
# Last resort (not cryptographic): length + a sanitized prefix.
|
|
97
|
+
printf '%s' "${#s}-$(echo "$s" | tr -cd '[:alnum:]' | head -c 10)"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
swiftbar_hash() {
|
|
101
|
+
# Usage: swiftbar_hash "string"
|
|
102
|
+
local s="$1"
|
|
103
|
+
if command -v md5 >/dev/null 2>&1; then
|
|
104
|
+
md5 -q -s "$s" 2>/dev/null || true
|
|
105
|
+
return
|
|
106
|
+
fi
|
|
107
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
108
|
+
printf '%s' "$s" | shasum -a 256 2>/dev/null | awk '{print $1}'
|
|
109
|
+
return
|
|
110
|
+
fi
|
|
111
|
+
printf '%s' "$(swiftbar_cache_hash12 "$s")"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
swiftbar_run_cache_dir() {
|
|
115
|
+
# Per-process (per-refresh) cache. Avoid persisting across SwiftBar refreshes.
|
|
116
|
+
local base="${TMPDIR:-/tmp}"
|
|
117
|
+
local dir="${base%/}/happy-stacks-swiftbar-cache-${UID:-0}-$$"
|
|
118
|
+
mkdir -p "$dir" 2>/dev/null || true
|
|
119
|
+
echo "$dir"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
swiftbar_cache_file_for_key() {
|
|
123
|
+
local key="$1"
|
|
124
|
+
local dir
|
|
125
|
+
dir="$(swiftbar_run_cache_dir)"
|
|
126
|
+
echo "${dir}/$(swiftbar_cache_hash12 "$key").cache"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
swiftbar_cache_get() {
|
|
130
|
+
# Usage: swiftbar_cache_get <key>
|
|
131
|
+
# Output: cached stdout (may be empty). Exit status: cached rc.
|
|
132
|
+
local key="$1"
|
|
133
|
+
local f
|
|
134
|
+
f="$(swiftbar_cache_file_for_key "$key")"
|
|
135
|
+
# Important: return a distinct code on cache-miss so callers can distinguish from cached rc=1.
|
|
136
|
+
[[ -f "$f" ]] || return 111
|
|
137
|
+
local rc_line rc
|
|
138
|
+
rc_line="$(head -n 1 "$f" 2>/dev/null || true)"
|
|
139
|
+
rc="${rc_line#rc:}"
|
|
140
|
+
tail -n +2 "$f" 2>/dev/null || true
|
|
141
|
+
[[ "$rc" =~ ^[0-9]+$ ]] || rc=0
|
|
142
|
+
return "$rc"
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
swiftbar_cache_set() {
|
|
146
|
+
# Usage: swiftbar_cache_set <key> <rc> <stdout>
|
|
147
|
+
local key="$1"
|
|
148
|
+
local rc="$2"
|
|
149
|
+
local out="${3:-}"
|
|
150
|
+
local f
|
|
151
|
+
f="$(swiftbar_cache_file_for_key "$key")"
|
|
152
|
+
{
|
|
153
|
+
printf 'rc:%s\n' "${rc:-0}"
|
|
154
|
+
printf '%s' "$out"
|
|
155
|
+
# Keep files line-friendly.
|
|
156
|
+
[[ "$out" == *$'\n' ]] || printf '\n'
|
|
157
|
+
} >"$f" 2>/dev/null || true
|
|
158
|
+
}
|
|
159
|
+
|
|
20
160
|
dotenv_get() {
|
|
21
161
|
# Usage: dotenv_get /path/to/env KEY
|
|
22
162
|
# Notes:
|
|
@@ -60,7 +200,8 @@ expand_home_path() {
|
|
|
60
200
|
}
|
|
61
201
|
|
|
62
202
|
resolve_happy_local_dir() {
|
|
63
|
-
local
|
|
203
|
+
local canonical="${HAPPY_STACKS_CANONICAL_HOME_DIR:-${HAPPY_LOCAL_CANONICAL_HOME_DIR:-$HOME/.happy-stacks}}"
|
|
204
|
+
local home="${HAPPY_STACKS_HOME_DIR:-${HAPPY_LOCAL_DIR:-$canonical}}"
|
|
64
205
|
|
|
65
206
|
# If user provided a valid directory, keep it.
|
|
66
207
|
if [[ -n "${HAPPY_LOCAL_DIR:-}" ]] && [[ -f "$HAPPY_LOCAL_DIR/extras/swiftbar/lib/utils.sh" ]]; then
|
|
@@ -103,6 +244,12 @@ resolve_stacks_storage_root() {
|
|
|
103
244
|
return
|
|
104
245
|
fi
|
|
105
246
|
|
|
247
|
+
# In sandbox mode, avoid falling back to the user's real ~/.happy/stacks.
|
|
248
|
+
if swiftbar_is_sandboxed; then
|
|
249
|
+
echo "${HAPPY_STACKS_STORAGE_DIR:-${HAPPY_STACKS_SANDBOX_DIR%/}/storage}"
|
|
250
|
+
return
|
|
251
|
+
fi
|
|
252
|
+
|
|
106
253
|
echo "$HOME/.happy/stacks"
|
|
107
254
|
}
|
|
108
255
|
|
|
@@ -117,18 +264,22 @@ resolve_stack_env_file() {
|
|
|
117
264
|
return
|
|
118
265
|
fi
|
|
119
266
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
267
|
+
if ! swiftbar_is_sandboxed; then
|
|
268
|
+
local legacy="$HOME/.happy/local/stacks/${stack_name}/env"
|
|
269
|
+
if [[ -f "$legacy" ]]; then
|
|
270
|
+
echo "$legacy"
|
|
271
|
+
return
|
|
272
|
+
fi
|
|
124
273
|
fi
|
|
125
274
|
|
|
126
275
|
# Very old single-stack location (best-effort).
|
|
127
|
-
if
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
276
|
+
if ! swiftbar_is_sandboxed; then
|
|
277
|
+
if [[ "$stack_name" == "main" ]]; then
|
|
278
|
+
local legacy_single="$HOME/.happy/local/env"
|
|
279
|
+
if [[ -f "$legacy_single" ]]; then
|
|
280
|
+
echo "$legacy_single"
|
|
281
|
+
return
|
|
282
|
+
fi
|
|
132
283
|
fi
|
|
133
284
|
fi
|
|
134
285
|
|
|
@@ -179,6 +330,11 @@ resolve_stack_label() {
|
|
|
179
330
|
primary="com.happy.stacks.${stack_name}"
|
|
180
331
|
legacy="com.happy.local.${stack_name}"
|
|
181
332
|
fi
|
|
333
|
+
if swiftbar_is_sandboxed; then
|
|
334
|
+
# Never inspect global LaunchAgents in sandbox mode.
|
|
335
|
+
echo "$primary"
|
|
336
|
+
return
|
|
337
|
+
fi
|
|
182
338
|
local primary_plist="$HOME/Library/LaunchAgents/${primary}.plist"
|
|
183
339
|
local legacy_plist="$HOME/Library/LaunchAgents/${legacy}.plist"
|
|
184
340
|
if [[ -f "$primary_plist" ]]; then
|
|
@@ -208,10 +364,12 @@ resolve_pnpm_bin() {
|
|
|
208
364
|
fi
|
|
209
365
|
|
|
210
366
|
local global_happys
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
367
|
+
if ! swiftbar_is_sandboxed; then
|
|
368
|
+
global_happys="$(command -v happys 2>/dev/null || true)"
|
|
369
|
+
if [[ -n "$global_happys" ]]; then
|
|
370
|
+
echo "$global_happys"
|
|
371
|
+
return
|
|
372
|
+
fi
|
|
215
373
|
fi
|
|
216
374
|
|
|
217
375
|
echo ""
|
|
@@ -228,8 +386,9 @@ resolve_node_bin() {
|
|
|
228
386
|
return
|
|
229
387
|
fi
|
|
230
388
|
|
|
231
|
-
# Fall back to reading
|
|
232
|
-
local
|
|
389
|
+
# Fall back to reading the canonical pointer env (written by `happys init`).
|
|
390
|
+
local canonical="${HAPPY_STACKS_CANONICAL_HOME_DIR:-${HAPPY_LOCAL_CANONICAL_HOME_DIR:-$HOME/.happy-stacks}}"
|
|
391
|
+
local home="${HAPPY_STACKS_HOME_DIR:-${HAPPY_LOCAL_DIR:-$canonical}}"
|
|
233
392
|
local env_file="$home/.env"
|
|
234
393
|
if [[ -f "$env_file" ]]; then
|
|
235
394
|
local v
|
|
@@ -281,17 +440,19 @@ resolve_main_env_file() {
|
|
|
281
440
|
echo "$main"
|
|
282
441
|
return
|
|
283
442
|
fi
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
443
|
+
if ! swiftbar_is_sandboxed; then
|
|
444
|
+
# Legacy stacks location (pre-migration).
|
|
445
|
+
local legacy="$HOME/.happy/local/stacks/main/env"
|
|
446
|
+
if [[ -f "$legacy" ]]; then
|
|
447
|
+
echo "$legacy"
|
|
448
|
+
return
|
|
449
|
+
fi
|
|
450
|
+
# Very old single-stack location (best-effort).
|
|
451
|
+
local legacy_single="$HOME/.happy/local/env"
|
|
452
|
+
if [[ -f "$legacy_single" ]]; then
|
|
453
|
+
echo "$legacy_single"
|
|
454
|
+
return
|
|
455
|
+
fi
|
|
295
456
|
fi
|
|
296
457
|
echo ""
|
|
297
458
|
}
|
|
@@ -302,7 +463,8 @@ resolve_main_port() {
|
|
|
302
463
|
# 2) main stack env
|
|
303
464
|
# 3) home env.local
|
|
304
465
|
# 4) home .env
|
|
305
|
-
#
|
|
466
|
+
# 5) runtime state (ephemeral stacks)
|
|
467
|
+
# 6) fallback to HAPPY_LOCAL_PORT / 3005
|
|
306
468
|
if [[ -n "${HAPPY_LOCAL_SERVER_PORT:-}" ]]; then
|
|
307
469
|
echo "$HAPPY_LOCAL_SERVER_PORT"
|
|
308
470
|
return
|
|
@@ -338,9 +500,98 @@ resolve_main_port() {
|
|
|
338
500
|
return
|
|
339
501
|
fi
|
|
340
502
|
|
|
503
|
+
# Runtime-only port overlay (ephemeral stacks): best-effort.
|
|
504
|
+
local base_dir state_file
|
|
505
|
+
base_dir="$(resolve_stack_base_dir main "$env_file")"
|
|
506
|
+
state_file="${base_dir}/stack.runtime.json"
|
|
507
|
+
p="$(resolve_runtime_server_port_from_state_file "$state_file")"
|
|
508
|
+
if [[ -n "$p" ]]; then
|
|
509
|
+
echo "$p"
|
|
510
|
+
return
|
|
511
|
+
fi
|
|
512
|
+
|
|
341
513
|
echo "${HAPPY_LOCAL_PORT:-3005}"
|
|
342
514
|
}
|
|
343
515
|
|
|
516
|
+
resolve_runtime_server_port_from_state_file() {
|
|
517
|
+
# Reads stack.runtime.json and returns ports.server, but only if ownerPid is alive.
|
|
518
|
+
# Output: port number or empty.
|
|
519
|
+
local state_file="$1"
|
|
520
|
+
[[ -n "$state_file" && -f "$state_file" ]] || return 0
|
|
521
|
+
|
|
522
|
+
local owner="" port=""
|
|
523
|
+
|
|
524
|
+
# Fast-path: parse our own JSON shape without spawning node (best-effort).
|
|
525
|
+
if command -v grep >/dev/null 2>&1; then
|
|
526
|
+
owner="$(grep -oE '"ownerPid"[[:space:]]*:[[:space:]]*[0-9]+' "$state_file" 2>/dev/null | head -1 | grep -oE '[0-9]+' || true)"
|
|
527
|
+
port="$(grep -oE '"server"[[:space:]]*:[[:space:]]*[0-9]+' "$state_file" 2>/dev/null | head -1 | grep -oE '[0-9]+' || true)"
|
|
528
|
+
fi
|
|
529
|
+
|
|
530
|
+
if [[ -z "$owner" || -z "$port" ]]; then
|
|
531
|
+
local node_bin
|
|
532
|
+
node_bin="$(resolve_node_bin)"
|
|
533
|
+
if [[ -n "$node_bin" && -x "$node_bin" ]]; then
|
|
534
|
+
local out
|
|
535
|
+
out="$(
|
|
536
|
+
"$node_bin" -e '
|
|
537
|
+
const fs=require("fs");
|
|
538
|
+
try {
|
|
539
|
+
const s=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));
|
|
540
|
+
const owner=String(s?.ownerPid ?? "");
|
|
541
|
+
const port=String(s?.ports?.server ?? "");
|
|
542
|
+
process.stdout.write(owner + "\t" + port);
|
|
543
|
+
} catch { process.stdout.write("\t"); }
|
|
544
|
+
' "$state_file" 2>/dev/null || true
|
|
545
|
+
)"
|
|
546
|
+
IFS=$'\t' read -r owner port <<<"$out"
|
|
547
|
+
elif command -v python3 >/dev/null 2>&1; then
|
|
548
|
+
local out
|
|
549
|
+
out="$(
|
|
550
|
+
python3 -c 'import json,sys;
|
|
551
|
+
try:
|
|
552
|
+
s=json.load(open(sys.argv[1],"r"))
|
|
553
|
+
owner=str(s.get("ownerPid",""))
|
|
554
|
+
port=str((s.get("ports") or {}).get("server",""))
|
|
555
|
+
print(owner+"\\t"+port,end="")
|
|
556
|
+
except Exception:
|
|
557
|
+
print("\\t",end="")' "$state_file" 2>/dev/null || true
|
|
558
|
+
)"
|
|
559
|
+
IFS=$'\t' read -r owner port <<<"$out"
|
|
560
|
+
fi
|
|
561
|
+
fi
|
|
562
|
+
|
|
563
|
+
[[ "$owner" =~ ^[0-9]+$ ]] || owner=""
|
|
564
|
+
[[ "$port" =~ ^[0-9]+$ ]] || port=""
|
|
565
|
+
if [[ -n "$owner" ]] && kill -0 "$owner" 2>/dev/null; then
|
|
566
|
+
echo "$port"
|
|
567
|
+
fi
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
resolve_stack_server_port() {
|
|
571
|
+
# Usage: resolve_stack_server_port <stack_name> <env_file>
|
|
572
|
+
# Priority:
|
|
573
|
+
# - pinned port in env file
|
|
574
|
+
# - runtime port in stack.runtime.json (only if ownerPid alive)
|
|
575
|
+
local stack_name="${1:-main}"
|
|
576
|
+
local env_file="${2:-}"
|
|
577
|
+
|
|
578
|
+
local p=""
|
|
579
|
+
if [[ -n "$env_file" && -f "$env_file" ]]; then
|
|
580
|
+
p="$(dotenv_get "$env_file" "HAPPY_STACKS_SERVER_PORT")"
|
|
581
|
+
[[ -z "$p" ]] && p="$(dotenv_get "$env_file" "HAPPY_LOCAL_SERVER_PORT")"
|
|
582
|
+
fi
|
|
583
|
+
if [[ -n "$p" ]]; then
|
|
584
|
+
echo "$p"
|
|
585
|
+
return
|
|
586
|
+
fi
|
|
587
|
+
|
|
588
|
+
local base_dir state_file
|
|
589
|
+
base_dir="$(resolve_stack_base_dir "$stack_name" "$env_file")"
|
|
590
|
+
state_file="${base_dir}/stack.runtime.json"
|
|
591
|
+
p="$(resolve_runtime_server_port_from_state_file "$state_file")"
|
|
592
|
+
echo "$p"
|
|
593
|
+
}
|
|
594
|
+
|
|
344
595
|
resolve_main_server_component() {
|
|
345
596
|
if [[ -n "${HAPPY_LOCAL_SERVER_COMPONENT:-}" ]]; then
|
|
346
597
|
echo "$HAPPY_LOCAL_SERVER_COMPONENT"
|
|
@@ -379,3 +630,35 @@ resolve_main_server_component() {
|
|
|
379
630
|
|
|
380
631
|
echo "happy-server-light"
|
|
381
632
|
}
|
|
633
|
+
|
|
634
|
+
resolve_menubar_mode() {
|
|
635
|
+
# selfhost | dev (default: dev)
|
|
636
|
+
local raw=""
|
|
637
|
+
if [[ -n "${HAPPY_LOCAL_MENUBAR_MODE:-}" ]]; then
|
|
638
|
+
raw="$HAPPY_LOCAL_MENUBAR_MODE"
|
|
639
|
+
elif [[ -n "${HAPPY_STACKS_MENUBAR_MODE:-}" ]]; then
|
|
640
|
+
raw="$HAPPY_STACKS_MENUBAR_MODE"
|
|
641
|
+
fi
|
|
642
|
+
|
|
643
|
+
local env_file
|
|
644
|
+
env_file="$(resolve_main_env_file)"
|
|
645
|
+
if [[ -z "$raw" && -n "$env_file" ]]; then
|
|
646
|
+
raw="$(dotenv_get "$env_file" "HAPPY_LOCAL_MENUBAR_MODE")"
|
|
647
|
+
[[ -z "$raw" ]] && raw="$(dotenv_get "$env_file" "HAPPY_STACKS_MENUBAR_MODE")"
|
|
648
|
+
fi
|
|
649
|
+
|
|
650
|
+
if [[ -z "$raw" ]]; then
|
|
651
|
+
raw="$(dotenv_get "$HAPPY_LOCAL_DIR/env.local" "HAPPY_LOCAL_MENUBAR_MODE")"
|
|
652
|
+
[[ -z "$raw" ]] && raw="$(dotenv_get "$HAPPY_LOCAL_DIR/env.local" "HAPPY_STACKS_MENUBAR_MODE")"
|
|
653
|
+
fi
|
|
654
|
+
if [[ -z "$raw" ]]; then
|
|
655
|
+
raw="$(dotenv_get "$HAPPY_LOCAL_DIR/.env" "HAPPY_LOCAL_MENUBAR_MODE")"
|
|
656
|
+
[[ -z "$raw" ]] && raw="$(dotenv_get "$HAPPY_LOCAL_DIR/.env" "HAPPY_STACKS_MENUBAR_MODE")"
|
|
657
|
+
fi
|
|
658
|
+
|
|
659
|
+
raw="$(echo "${raw:-}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
|
|
660
|
+
case "$raw" in
|
|
661
|
+
selfhost|self-host|self_host|host) echo "selfhost" ;;
|
|
662
|
+
*) echo "dev" ;;
|
|
663
|
+
esac
|
|
664
|
+
}
|
package/extras/swiftbar/pnpm.sh
CHANGED
|
@@ -4,7 +4,8 @@ set -euo pipefail
|
|
|
4
4
|
# Back-compat wrapper for SwiftBar menu actions.
|
|
5
5
|
# Historically this executed `pnpm`; now it delegates to `happys.sh`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
CANONICAL_HOME_DIR="${HAPPY_STACKS_CANONICAL_HOME_DIR:-${HAPPY_LOCAL_CANONICAL_HOME_DIR:-$HOME/.happy-stacks}}"
|
|
8
|
+
HAPPY_STACKS_HOME_DIR="${HAPPY_STACKS_HOME_DIR:-$CANONICAL_HOME_DIR}"
|
|
8
9
|
HAPPY_LOCAL_DIR="${HAPPY_LOCAL_DIR:-$HAPPY_STACKS_HOME_DIR}"
|
|
9
10
|
|
|
10
11
|
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/happys.sh" "$@"
|
|
@@ -26,13 +26,18 @@ if [[ -z "$PLUGIN_DIR" ]]; then
|
|
|
26
26
|
fi
|
|
27
27
|
mkdir -p "$PLUGIN_DIR"
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
PLUGIN_BASENAME="${HAPPY_STACKS_SWIFTBAR_PLUGIN_BASENAME:-${HAPPY_LOCAL_SWIFTBAR_PLUGIN_BASENAME:-happy-stacks}}"
|
|
30
|
+
TARGET="$PLUGIN_DIR/${PLUGIN_BASENAME}.${INTERVAL}.sh"
|
|
31
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
32
|
+
DEFAULT_HOME_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
33
|
+
|
|
34
|
+
CANONICAL_HOME_DIR="${HAPPY_STACKS_CANONICAL_HOME_DIR:-${HAPPY_LOCAL_CANONICAL_HOME_DIR:-$DEFAULT_HOME_DIR}}"
|
|
35
|
+
HAPPY_LOCAL_DIR="${HAPPY_LOCAL_DIR:-${HAPPY_STACKS_HOME_DIR:-$CANONICAL_HOME_DIR}}"
|
|
36
|
+
HAPPY_STACKS_HOME_DIR="${HAPPY_STACKS_HOME_DIR:-$HAPPY_LOCAL_DIR}"
|
|
32
37
|
SOURCE="${HAPPY_LOCAL_DIR}/extras/swiftbar/happy-stacks.5s.sh"
|
|
33
38
|
|
|
34
39
|
# If a happy-stacks plugin already exists, rename it into place; otherwise copy from repo source.
|
|
35
|
-
EXISTING="$(ls "$PLUGIN_DIR"/
|
|
40
|
+
EXISTING="$(ls "$PLUGIN_DIR"/"${PLUGIN_BASENAME}".*.sh 2>/dev/null | head -1 || true)"
|
|
36
41
|
if [[ -n "$EXISTING" ]]; then
|
|
37
42
|
if [[ "$EXISTING" != "$TARGET" ]]; then
|
|
38
43
|
rm -f "$TARGET"
|
|
@@ -47,7 +52,7 @@ else
|
|
|
47
52
|
fi
|
|
48
53
|
|
|
49
54
|
# Remove any other intervals to avoid duplicates in SwiftBar.
|
|
50
|
-
for f in "$PLUGIN_DIR"/
|
|
55
|
+
for f in "$PLUGIN_DIR"/"${PLUGIN_BASENAME}".*.sh; do
|
|
51
56
|
[[ "$f" == "$TARGET" ]] && continue
|
|
52
57
|
rm -f "$f" || true
|
|
53
58
|
done
|
|
@@ -24,8 +24,11 @@ if [[ "$FLAVOR" != "happy-server" && "$FLAVOR" != "happy-server-light" ]]; then
|
|
|
24
24
|
exit 2
|
|
25
25
|
fi
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
28
|
+
DEFAULT_HOME_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
29
|
+
|
|
30
|
+
HAPPY_LOCAL_DIR="${HAPPY_LOCAL_DIR:-${HAPPY_STACKS_HOME_DIR:-$DEFAULT_HOME_DIR}}"
|
|
31
|
+
HAPPY_STACKS_HOME_DIR="${HAPPY_STACKS_HOME_DIR:-$HAPPY_LOCAL_DIR}"
|
|
29
32
|
|
|
30
33
|
HAPPYS_BIN="$HAPPY_LOCAL_DIR/extras/swiftbar/happys.sh"
|
|
31
34
|
if [[ ! -x "$HAPPYS_BIN" ]]; then
|
|
@@ -34,6 +37,9 @@ if [[ ! -x "$HAPPYS_BIN" ]]; then
|
|
|
34
37
|
fi
|
|
35
38
|
|
|
36
39
|
restart_main_service_best_effort() {
|
|
40
|
+
if [[ -n "${HAPPY_STACKS_SANDBOX_DIR:-}" ]]; then
|
|
41
|
+
return 0
|
|
42
|
+
fi
|
|
37
43
|
"$HAPPYS_BIN" service:restart >/dev/null 2>&1 || true
|
|
38
44
|
# If the installed LaunchAgent is still legacy/baked, reinstall so it persists only env-file pointer.
|
|
39
45
|
"$HAPPYS_BIN" service:install >/dev/null 2>&1 || true
|
|
@@ -41,6 +47,9 @@ restart_main_service_best_effort() {
|
|
|
41
47
|
|
|
42
48
|
restart_stack_service_best_effort() {
|
|
43
49
|
local name="$1"
|
|
50
|
+
if [[ -n "${HAPPY_STACKS_SANDBOX_DIR:-}" ]]; then
|
|
51
|
+
return 0
|
|
52
|
+
fi
|
|
44
53
|
"$HAPPYS_BIN" stack service:restart "$name" >/dev/null 2>&1 || true
|
|
45
54
|
"$HAPPYS_BIN" stack service:install "$name" >/dev/null 2>&1 || true
|
|
46
55
|
}
|
package/extras/swiftbar/wt-pr.sh
CHANGED
|
@@ -17,11 +17,18 @@ set -euo pipefail
|
|
|
17
17
|
COMPONENT="${1:-}"
|
|
18
18
|
STACK_NAME="${2:-}"
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
DEFAULT_HOME_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
22
|
+
|
|
23
|
+
HAPPY_LOCAL_DIR="${HAPPY_LOCAL_DIR:-${HAPPY_STACKS_HOME_DIR:-$DEFAULT_HOME_DIR}}"
|
|
24
|
+
HAPPY_STACKS_HOME_DIR="${HAPPY_STACKS_HOME_DIR:-$HAPPY_LOCAL_DIR}"
|
|
22
25
|
|
|
23
26
|
HAPPYS="$HAPPY_LOCAL_DIR/extras/swiftbar/happys.sh"
|
|
24
27
|
if [[ ! -x "$HAPPYS" ]]; then
|
|
28
|
+
if [[ -n "${HAPPY_STACKS_SANDBOX_DIR:-}" ]]; then
|
|
29
|
+
echo "missing happys wrapper in sandbox: $HAPPYS" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
25
32
|
HAPPYS="$(command -v happys 2>/dev/null || true)"
|
|
26
33
|
fi
|
|
27
34
|
if [[ -z "$HAPPYS" ]]; then
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "happy-stacks",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"packageManager": "pnpm@10.18.3",
|
|
6
6
|
"bin": {
|
|
7
7
|
"happys": "./bin/happys.mjs",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"scripts/"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
+
"setup": "node ./scripts/setup.mjs",
|
|
17
18
|
"init": "node ./scripts/init.mjs",
|
|
18
19
|
"uninstall": "node ./scripts/uninstall.mjs",
|
|
19
20
|
"where": "node ./scripts/where.mjs",
|