happy-stacks 0.1.0 → 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 +130 -74
- package/bin/happys.mjs +140 -9
- 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/server-flavors.md +61 -2
- package/docs/stacks.md +55 -4
- package/extras/swiftbar/auth-login.sh +10 -7
- package/extras/swiftbar/git-cache-refresh.sh +130 -0
- package/extras/swiftbar/happy-stacks.5s.sh +175 -83
- package/extras/swiftbar/happys-term.sh +128 -0
- package/extras/swiftbar/happys.sh +35 -0
- 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 +279 -132
- package/extras/swiftbar/lib/system.sh +64 -10
- package/extras/swiftbar/lib/utils.sh +469 -10
- package/extras/swiftbar/pnpm-term.sh +2 -122
- package/extras/swiftbar/pnpm.sh +4 -14
- package/extras/swiftbar/set-interval.sh +10 -5
- package/extras/swiftbar/set-server-flavor.sh +19 -10
- package/extras/swiftbar/wt-pr.sh +10 -3
- package/package.json +2 -1
- package/scripts/auth.mjs +833 -14
- package/scripts/build.mjs +24 -4
- package/scripts/cli-link.mjs +3 -3
- package/scripts/completion.mjs +15 -8
- package/scripts/daemon.mjs +200 -23
- package/scripts/dev.mjs +230 -57
- package/scripts/doctor.mjs +26 -21
- package/scripts/edison.mjs +1828 -0
- package/scripts/happy.mjs +3 -7
- package/scripts/init.mjs +275 -46
- package/scripts/install.mjs +14 -8
- package/scripts/lint.mjs +145 -0
- package/scripts/menubar.mjs +81 -8
- package/scripts/migrate.mjs +302 -0
- package/scripts/mobile.mjs +59 -21
- package/scripts/run.mjs +222 -43
- package/scripts/self.mjs +3 -7
- package/scripts/server_flavor.mjs +3 -3
- package/scripts/service.mjs +190 -38
- package/scripts/setup.mjs +790 -0
- package/scripts/setup_pr.mjs +182 -0
- package/scripts/stack.mjs +2273 -92
- package/scripts/stop.mjs +160 -0
- package/scripts/tailscale.mjs +164 -23
- package/scripts/test.mjs +144 -0
- package/scripts/tui.mjs +556 -0
- package/scripts/typecheck.mjs +145 -0
- package/scripts/ui_gateway.mjs +248 -0
- package/scripts/uninstall.mjs +21 -13
- 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} +71 -0
- package/scripts/utils/{wizard.mjs → cli/wizard.mjs} +1 -1
- package/scripts/utils/config.mjs +13 -1
- 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 +94 -23
- package/scripts/utils/env_file.mjs +36 -0
- package/scripts/utils/expo.mjs +96 -0
- package/scripts/utils/handy_master_secret.mjs +94 -0
- package/scripts/utils/happy_server_infra.mjs +484 -0
- 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 +132 -22
- package/scripts/utils/ports.mjs +51 -13
- package/scripts/utils/proc.mjs +75 -7
- package/scripts/utils/runtime.mjs +1 -3
- package/scripts/utils/sandbox.mjs +14 -0
- package/scripts/utils/server.mjs +61 -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 +255 -0
- package/scripts/utils/stacks.mjs +38 -0
- package/scripts/utils/validate.mjs +42 -1
- package/scripts/utils/watch.mjs +63 -0
- package/scripts/utils/worktrees.mjs +57 -1
- package/scripts/where.mjs +14 -7
- package/scripts/worktrees.mjs +135 -15
- /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
|
@@ -14,9 +14,7 @@ get_process_metrics() {
|
|
|
14
14
|
return
|
|
15
15
|
fi
|
|
16
16
|
local cpu rss etime
|
|
17
|
-
|
|
18
|
-
rss="$(echo "$line" | awk '{print $2}')" # KB
|
|
19
|
-
etime="$(echo "$line" | awk '{print $3}')"
|
|
17
|
+
IFS=' ' read -r cpu rss etime <<<"$line"
|
|
20
18
|
local mem_mb
|
|
21
19
|
mem_mb="$(awk -v rss="$rss" 'BEGIN { printf "%.0f", (rss/1024.0) }')"
|
|
22
20
|
echo "$cpu|$mem_mb|$etime"
|
|
@@ -43,7 +41,11 @@ ensure_launchctl_cache() {
|
|
|
43
41
|
return
|
|
44
42
|
fi
|
|
45
43
|
if command -v launchctl >/dev/null 2>&1; then
|
|
44
|
+
local t0 t1
|
|
45
|
+
t0="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
46
46
|
LAUNCHCTL_LIST_CACHE="$(launchctl list 2>/dev/null || true)"
|
|
47
|
+
t1="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
48
|
+
swiftbar_profile_log "time" "label=launchctl_list" "ms=$((t1 - t0))"
|
|
47
49
|
fi
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -56,7 +58,8 @@ check_launchagent_status() {
|
|
|
56
58
|
fi
|
|
57
59
|
|
|
58
60
|
ensure_launchctl_cache
|
|
59
|
-
|
|
61
|
+
# Match the label column exactly (avoid substring false positives).
|
|
62
|
+
if echo "$LAUNCHCTL_LIST_CACHE" | awk -v lbl="$label" '$3==lbl{found=1} END{exit found?0:1}'; then
|
|
60
63
|
echo "loaded"
|
|
61
64
|
return
|
|
62
65
|
fi
|
|
@@ -88,7 +91,11 @@ check_server_health() {
|
|
|
88
91
|
fi
|
|
89
92
|
local response
|
|
90
93
|
# Tight timeouts to keep menus snappy even with many stacks.
|
|
94
|
+
local t0 t1
|
|
95
|
+
t0="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
91
96
|
response="$(curl -s --connect-timeout 0.2 --max-time 0.6 "http://127.0.0.1:${port}/health" 2>/dev/null || true)"
|
|
97
|
+
t1="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
98
|
+
swiftbar_profile_log "time" "label=curl_health" "port=${port}" "ms=$((t1 - t0))" "bytes=${#response}"
|
|
92
99
|
if [[ "$response" == *"ok"* ]] || [[ "$response" == *"Welcome"* ]]; then
|
|
93
100
|
echo "running"
|
|
94
101
|
return
|
|
@@ -99,6 +106,8 @@ check_server_health() {
|
|
|
99
106
|
check_daemon_status() {
|
|
100
107
|
local cli_home_dir="$1"
|
|
101
108
|
local state_file="$cli_home_dir/daemon.state.json"
|
|
109
|
+
local t0 t1
|
|
110
|
+
t0="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
102
111
|
if [[ -z "$cli_home_dir" ]] || [[ ! -f "$state_file" ]]; then
|
|
103
112
|
# If the daemon is starting but hasn't written daemon.state.json yet, we can still detect it
|
|
104
113
|
# via the lock file PID.
|
|
@@ -112,7 +121,7 @@ check_daemon_status() {
|
|
|
112
121
|
local latest_log
|
|
113
122
|
latest_log="$(ls -1t "$cli_home_dir"/logs/*-daemon.log 2>/dev/null | head -1 || true)"
|
|
114
123
|
if [[ -n "$latest_log" ]]; then
|
|
115
|
-
if tail -n 120 "$latest_log" 2>/dev/null |
|
|
124
|
+
if tail -n 120 "$latest_log" 2>/dev/null | grep -Eq "No credentials found|starting authentication flow|Waiting for credentials"; then
|
|
116
125
|
echo "auth_required:$lock_pid"
|
|
117
126
|
return
|
|
118
127
|
fi
|
|
@@ -129,9 +138,16 @@ check_daemon_status() {
|
|
|
129
138
|
return
|
|
130
139
|
fi
|
|
131
140
|
|
|
141
|
+
local node_bin
|
|
142
|
+
node_bin="$(resolve_node_bin)"
|
|
143
|
+
if [[ -z "$node_bin" ]] || [[ ! -x "$node_bin" ]]; then
|
|
144
|
+
echo "unknown"
|
|
145
|
+
return
|
|
146
|
+
fi
|
|
147
|
+
|
|
132
148
|
local pid httpPort
|
|
133
|
-
pid="$(
|
|
134
|
-
httpPort="$(
|
|
149
|
+
pid="$("$node_bin" -e 'const fs=require("fs"); const s=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); process.stdout.write(String(s.pid ?? ""));' "$state_file" 2>/dev/null || true)"
|
|
150
|
+
httpPort="$("$node_bin" -e 'const fs=require("fs"); const s=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); process.stdout.write(String(s.httpPort ?? ""));' "$state_file" 2>/dev/null || true)"
|
|
135
151
|
|
|
136
152
|
if [[ -z "$pid" ]] || ! [[ "$pid" =~ ^[0-9]+$ ]]; then
|
|
137
153
|
echo "unknown"
|
|
@@ -146,13 +162,19 @@ check_daemon_status() {
|
|
|
146
162
|
# Best-effort: confirm the control server is responding (tight timeouts).
|
|
147
163
|
if [[ -n "$httpPort" ]] && [[ "$httpPort" =~ ^[0-9]+$ ]]; then
|
|
148
164
|
if curl -s --connect-timeout 0.2 --max-time 0.5 -X POST -H 'content-type: application/json' -d '{}' "http://127.0.0.1:${httpPort}/list" >/dev/null 2>&1; then
|
|
165
|
+
t1="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
166
|
+
swiftbar_profile_log "time" "label=daemon_status" "ms=$((t1 - t0))" "httpProbe=ok"
|
|
149
167
|
echo "running:$pid"
|
|
150
168
|
return
|
|
151
169
|
fi
|
|
170
|
+
t1="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
171
|
+
swiftbar_profile_log "time" "label=daemon_status" "ms=$((t1 - t0))" "httpProbe=fail"
|
|
152
172
|
echo "running-no-http:$pid"
|
|
153
173
|
return
|
|
154
174
|
fi
|
|
155
175
|
|
|
176
|
+
t1="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
177
|
+
swiftbar_profile_log "time" "label=daemon_status" "ms=$((t1 - t0))" "httpProbe=skip"
|
|
156
178
|
echo "running:$pid"
|
|
157
179
|
}
|
|
158
180
|
|
|
@@ -162,7 +184,12 @@ get_daemon_uptime() {
|
|
|
162
184
|
if [[ -z "$cli_home_dir" ]] || [[ ! -f "$state_file" ]]; then
|
|
163
185
|
return
|
|
164
186
|
fi
|
|
165
|
-
|
|
187
|
+
local node_bin
|
|
188
|
+
node_bin="$(resolve_node_bin)"
|
|
189
|
+
if [[ -z "$node_bin" ]] || [[ ! -x "$node_bin" ]]; then
|
|
190
|
+
return
|
|
191
|
+
fi
|
|
192
|
+
"$node_bin" -e 'const fs=require("fs"); const s=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); if (s.startTime) process.stdout.write(String(s.startTime));' "$state_file" 2>/dev/null || true
|
|
166
193
|
}
|
|
167
194
|
|
|
168
195
|
get_last_heartbeat() {
|
|
@@ -171,15 +198,43 @@ get_last_heartbeat() {
|
|
|
171
198
|
if [[ -z "$cli_home_dir" ]] || [[ ! -f "$state_file" ]]; then
|
|
172
199
|
return
|
|
173
200
|
fi
|
|
174
|
-
|
|
201
|
+
local node_bin
|
|
202
|
+
node_bin="$(resolve_node_bin)"
|
|
203
|
+
if [[ -z "$node_bin" ]] || [[ ! -x "$node_bin" ]]; then
|
|
204
|
+
return
|
|
205
|
+
fi
|
|
206
|
+
"$node_bin" -e 'const fs=require("fs"); const s=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); if (s.lastHeartbeat) process.stdout.write(String(s.lastHeartbeat));' "$state_file" 2>/dev/null || true
|
|
175
207
|
}
|
|
176
208
|
|
|
177
209
|
get_tailscale_url() {
|
|
178
210
|
# Try multiple methods to get the Tailscale URL (best-effort).
|
|
179
211
|
local url=""
|
|
180
212
|
|
|
213
|
+
# Preferred: use happys (respects our own timeouts/env handling).
|
|
214
|
+
local happys_sh="$HAPPY_LOCAL_DIR/extras/swiftbar/happys.sh"
|
|
215
|
+
if [[ -x "$happys_sh" ]]; then
|
|
216
|
+
# Keep SwiftBar responsive: use a tight timeout for this periodic probe.
|
|
217
|
+
local t0 t1
|
|
218
|
+
t0="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
219
|
+
url="$("$happys_sh" tailscale:url --timeout-ms=2500 2>/dev/null | head -1 | tr -d '[:space:]' || true)"
|
|
220
|
+
t1="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
221
|
+
swiftbar_profile_log "time" "label=tailscale_url_happys" "ms=$((t1 - t0))" "ok=$([[ "$url" == https://* ]] && echo 1 || echo 0)"
|
|
222
|
+
if [[ "$url" == https://* ]]; then
|
|
223
|
+
echo "$url"
|
|
224
|
+
return
|
|
225
|
+
fi
|
|
226
|
+
url=""
|
|
227
|
+
fi
|
|
228
|
+
|
|
181
229
|
if command -v tailscale &>/dev/null; then
|
|
230
|
+
local t0 t1
|
|
231
|
+
t0="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
182
232
|
url="$(tailscale serve status 2>/dev/null | grep -oE 'https://[^ ]+' | head -1 || true)"
|
|
233
|
+
t1="$(swiftbar_now_ms 2>/dev/null || echo 0)"
|
|
234
|
+
swiftbar_profile_log "time" "label=tailscale_url_cli" "ms=$((t1 - t0))" "ok=$([[ -n "$url" ]] && echo 1 || echo 0)"
|
|
235
|
+
fi
|
|
236
|
+
if [[ -z "$url" ]] && [[ -x "/Applications/Tailscale.app/Contents/MacOS/tailscale" ]]; then
|
|
237
|
+
url="$(/Applications/Tailscale.app/Contents/MacOS/tailscale serve status 2>/dev/null | grep -oE 'https://[^ ]+' | head -1 || true)"
|
|
183
238
|
fi
|
|
184
239
|
if [[ -z "$url" ]] && [[ -x "/Applications/Tailscale.app/Contents/MacOS/Tailscale" ]]; then
|
|
185
240
|
url="$(/Applications/Tailscale.app/Contents/MacOS/Tailscale serve status 2>/dev/null | grep -oE 'https://[^ ]+' | head -1 || true)"
|
|
@@ -187,4 +242,3 @@ get_tailscale_url() {
|
|
|
187
242
|
|
|
188
243
|
echo "$url"
|
|
189
244
|
}
|
|
190
|
-
|