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
package/docs/menubar.md
CHANGED
|
@@ -32,6 +32,26 @@ SwiftBar runs a script on an interval and renders its output as native macOS men
|
|
|
32
32
|
- Each component includes a **Worktrees** submenu listing all worktrees, with actions to switch/open
|
|
33
33
|
- Quick actions: `wt status/sync/update`, PR worktree prompt, open shells/editors (`wt shell/code/cursor`)
|
|
34
34
|
- Shows **origin** and **upstream** comparisons for the component repo’s main branch (based on your last `git fetch`)
|
|
35
|
+
- Uses a **Git cache** by default so SwiftBar refresh stays fast even with many stacks/worktrees
|
|
36
|
+
|
|
37
|
+
## Modes: selfhost vs dev
|
|
38
|
+
|
|
39
|
+
The menu supports two modes:
|
|
40
|
+
|
|
41
|
+
- **Selfhost mode** (`selfhost`): lightweight “control panel” for running Happy.
|
|
42
|
+
- Shows only the main stack essentials (Server/Daemon/Autostart/Tailscale) plus a small **Maintenance** section.
|
|
43
|
+
- Hides developer-oriented sections like stacks enumeration, components git/worktrees, and worktree tooling.
|
|
44
|
+
- **Dev mode** (`dev`): full happy-stacks control plane (stacks + components + worktrees).
|
|
45
|
+
|
|
46
|
+
### How to switch modes
|
|
47
|
+
|
|
48
|
+
- In the menu, use the **Mode** section at the top, or
|
|
49
|
+
- From a terminal:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
happys menubar mode selfhost
|
|
53
|
+
happys menubar mode dev
|
|
54
|
+
```
|
|
35
55
|
|
|
36
56
|
## Stacks (multiple instances)
|
|
37
57
|
|
|
@@ -125,6 +145,40 @@ The plugin defaults to a slower interval (recommended), and also sets:
|
|
|
125
145
|
|
|
126
146
|
You can also change the interval directly from the menu via **Refresh interval** (it renames the plugin file and restarts SwiftBar).
|
|
127
147
|
|
|
148
|
+
## Git cache (important for performance)
|
|
149
|
+
|
|
150
|
+
Git/worktree inspection is the most expensive part of the menu when you have many stacks.
|
|
151
|
+
By default, the plugin runs in **cached mode**:
|
|
152
|
+
|
|
153
|
+
- It renders git/worktree info from an on-disk cache under `~/.happy-stacks/cache/swiftbar/git`.
|
|
154
|
+
- Normal menu refreshes do **not** run git commands (so refresh stays snappy).
|
|
155
|
+
- The cache is refreshed explicitly (via menu actions), and can optionally refresh on TTL expiry.
|
|
156
|
+
|
|
157
|
+
Controls and settings:
|
|
158
|
+
|
|
159
|
+
- **Refresh now**: open **Components → Git cache** and run:
|
|
160
|
+
- “Refresh now (main components)”
|
|
161
|
+
- “Refresh now (all stacks/components)”
|
|
162
|
+
- or “Refresh now (this stack)” from a stack’s Components menu
|
|
163
|
+
- **TTL**: `HAPPY_STACKS_SWIFTBAR_GIT_TTL_SEC` (default `3600` seconds)
|
|
164
|
+
- **TTL**: `HAPPY_STACKS_SWIFTBAR_GIT_TTL_SEC` (default `21600` seconds = 6 hours)
|
|
165
|
+
- **Mode**: `HAPPY_STACKS_SWIFTBAR_GIT_MODE=cached|live` (default `cached`)
|
|
166
|
+
- (Optional) **Background auto-refresh**: `HAPPY_STACKS_SWIFTBAR_GIT_AUTO_REFRESH_SCOPE=main|all|off` (default `main`)
|
|
167
|
+
|
|
168
|
+
Notes:
|
|
169
|
+
|
|
170
|
+
- Cached git info can be stale; it’s meant for at-a-glance signal.
|
|
171
|
+
- Actions like worktree switching/build/dev are always live (they use `happys`); only *displayed git status* is cached.
|
|
172
|
+
|
|
173
|
+
## Maintenance (selfhost mode)
|
|
174
|
+
|
|
175
|
+
In **selfhost** mode, the menu includes a **Maintenance** section that can:
|
|
176
|
+
|
|
177
|
+
- show whether a `happy-stacks` update is available (from cached `~/.happy-stacks/cache/update.json`)
|
|
178
|
+
- run:
|
|
179
|
+
- `happys self check`
|
|
180
|
+
- `happys self update`
|
|
181
|
+
|
|
128
182
|
## Terminal preference for interactive actions
|
|
129
183
|
|
|
130
184
|
Many menu actions open a terminal (interactive wizards, long-running dev servers, etc).
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Paths, folders, and env precedence
|
|
2
|
+
|
|
3
|
+
This doc explains the **directories** that Happy Stacks uses (home/workspace/runtime/stacks), and the **environment file precedence** used by `happys`.
|
|
4
|
+
|
|
5
|
+
If you’re ever unsure what your machine is actually using, run:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
happys where
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quick glossary
|
|
14
|
+
|
|
15
|
+
- **CLI root dir**
|
|
16
|
+
- The directory containing the `happy-stacks` scripts (`scripts/*.mjs`) that your `happys` command is currently executing.
|
|
17
|
+
- This is *not* necessarily your current shell `cwd`.
|
|
18
|
+
- It can be:
|
|
19
|
+
- a cloned repo checkout (e.g. `/Users/<you>/.../happy-local`), or
|
|
20
|
+
- the installed runtime package under `~/.happy-stacks/runtime/node_modules/happy-stacks` (see “Runtime dir”).
|
|
21
|
+
|
|
22
|
+
- **Home dir** (`HAPPY_STACKS_HOME_DIR`)
|
|
23
|
+
- Default: `~/.happy-stacks`
|
|
24
|
+
- Stores **global user config** + caches, and may include a runtime install.
|
|
25
|
+
|
|
26
|
+
- **Runtime dir** (`HAPPY_STACKS_RUNTIME_DIR`)
|
|
27
|
+
- Default: `~/.happy-stacks/runtime`
|
|
28
|
+
- Used by `happys self update` to install/upgrade a pinned `happy-stacks` runtime package.
|
|
29
|
+
|
|
30
|
+
- **Workspace dir** (`HAPPY_STACKS_WORKSPACE_DIR`)
|
|
31
|
+
- Default: `~/.happy-stacks/workspace` (when it exists).
|
|
32
|
+
- This is the **storage workspace for component repos and worktrees** used by Happy Stacks.
|
|
33
|
+
- Important: this is **not your IDE workspace**; it’s where Happy Stacks keeps `components/` by default.
|
|
34
|
+
- Back-compat: before you run `happys init` (cloned repo usage), we fall back to using the CLI root dir as the workspace, so `components/` lives inside the repo checkout.
|
|
35
|
+
|
|
36
|
+
- **Components dir**
|
|
37
|
+
- Computed as: `<workspaceDir>/components`
|
|
38
|
+
- Contains `happy`, `happy-cli`, `happy-server-light`, `happy-server`, plus `.worktrees/`.
|
|
39
|
+
|
|
40
|
+
- **Stacks storage dir**
|
|
41
|
+
- Default: `~/.happy/stacks`
|
|
42
|
+
- Each stack lives under `~/.happy/stacks/<name>/...` and has its own env file:
|
|
43
|
+
- `~/.happy/stacks/<name>/env`
|
|
44
|
+
- Legacy stacks path is also supported:
|
|
45
|
+
- `~/.happy/local/stacks/<name>/env`
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## “Where am I actually running from?”
|
|
50
|
+
|
|
51
|
+
`happys` may **re-exec** to a different CLI root dir (for example, when you use an installed shim but want it to run a local checkout).
|
|
52
|
+
|
|
53
|
+
- Run `happys where` to see:
|
|
54
|
+
- **rootDir** (CLI root dir)
|
|
55
|
+
- **homeDir** (stacks home dir)
|
|
56
|
+
- **runtimeDir**
|
|
57
|
+
- **workspaceDir**
|
|
58
|
+
- resolved env file paths
|
|
59
|
+
|
|
60
|
+
Tip: `happys where --json` is easier to parse.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Env files + precedence (lowest → highest)
|
|
65
|
+
|
|
66
|
+
Happy Stacks loads env in `scripts/utils/env.mjs`.
|
|
67
|
+
|
|
68
|
+
### 0) “Canonical pointer” env (discovery)
|
|
69
|
+
|
|
70
|
+
If `HAPPY_STACKS_HOME_DIR` is *not* set, we first try to read the **canonical pointer** env file to discover the intended home dir (useful for LaunchAgents / SwiftBar / minimal shells).
|
|
71
|
+
|
|
72
|
+
- Default canonical pointer path: `~/.happy-stacks/.env`
|
|
73
|
+
- Override canonical pointer location:
|
|
74
|
+
- `HAPPY_STACKS_CANONICAL_HOME_DIR=/some/dir` (pointer becomes `<dir>/.env`)
|
|
75
|
+
|
|
76
|
+
### 1) Global defaults (home config) OR cloned-repo defaults
|
|
77
|
+
|
|
78
|
+
If home config exists, we load:
|
|
79
|
+
|
|
80
|
+
- `~/.happy-stacks/.env` (**defaults**)
|
|
81
|
+
- `~/.happy-stacks/env.local` (**overrides**, prefix-aware for `HAPPY_STACKS_*` / `HAPPY_LOCAL_*`)
|
|
82
|
+
|
|
83
|
+
If home config does *not* exist (cloned repo usage before `happys init`), we load:
|
|
84
|
+
|
|
85
|
+
- `<cliRootDir>/.env`
|
|
86
|
+
- `<cliRootDir>/env.local` (prefix-aware for `HAPPY_STACKS_*` / `HAPPY_LOCAL_*`)
|
|
87
|
+
|
|
88
|
+
### 2) Repo `.env` fallback (dev convenience)
|
|
89
|
+
|
|
90
|
+
Even when home config exists, we also load:
|
|
91
|
+
|
|
92
|
+
- `<cliRootDir>/.env` (non-overriding fallback)
|
|
93
|
+
|
|
94
|
+
This exists so repo-local dev settings (example: `HAPPY_CODEX_BIN`) can work without forcing everyone to duplicate them into `~/.happy-stacks/env.local`.
|
|
95
|
+
|
|
96
|
+
Notes:
|
|
97
|
+
- This is a **fallback only** (`override: false`): it won’t stomp on values already provided by the environment or home config.
|
|
98
|
+
- We intentionally do **not** auto-load `<cliRootDir>/env.local` in this “home config exists” path, because it’s higher-precedence and can unexpectedly fight stack config.
|
|
99
|
+
|
|
100
|
+
### 3) Stack env overlay (highest precedence)
|
|
101
|
+
|
|
102
|
+
Finally, we load the active stack env file (override = true):
|
|
103
|
+
|
|
104
|
+
- `HAPPY_STACKS_ENV_FILE` (or legacy `HAPPY_LOCAL_ENV_FILE`)
|
|
105
|
+
- if neither is set, we auto-select the env file for the current stack (defaults to `main`) if it exists
|
|
106
|
+
|
|
107
|
+
Stack env files are allowed to contain **non-prefixed keys** (like `DATABASE_URL`) because that’s required for per-stack isolation.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## What should go where? (rules of thumb)
|
|
112
|
+
|
|
113
|
+
- Put **global, machine-wide defaults** in `~/.happy-stacks/.env`.
|
|
114
|
+
- Put **your personal overrides** in `~/.happy-stacks/env.local`.
|
|
115
|
+
- Put **per-stack isolation config** in the stack env file `~/.happy/stacks/<name>/env` (this is what `happys stack edit` and `happys stack wt` mutate).
|
|
116
|
+
- Put **repo-local dev-only defaults** in `<cliRootDir>/.env` (works best when you’re actually running from that checkout as the CLI root dir).
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Sandbox / test installs (fully isolated)
|
|
121
|
+
|
|
122
|
+
If you want to test the full install + setup flows without touching your real installation, run with:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npx happy-stacks --sandbox-dir /tmp/happy-stacks-sandbox where
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
In sandbox mode, Happy Stacks redirects **home/workspace/runtime/storage** under the sandbox folder (so you can `rm -rf` it to reset).
|
|
129
|
+
|
|
130
|
+
Global OS side effects (PATH edits, SwiftBar plugin install, LaunchAgents/systemd services) are **disabled by default** in sandbox mode.
|
|
131
|
+
To explicitly allow them for testing, set:
|
|
132
|
+
|
|
133
|
+
- `HAPPY_STACKS_SANDBOX_ALLOW_GLOBAL=1`
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Related docs
|
|
138
|
+
|
|
139
|
+
- `docs/stacks.md` (stacks lifecycle + commands)
|
|
140
|
+
- `docs/worktrees-and-forks.md` (worktrees layout + upstream/fork workflows)
|
|
141
|
+
|
package/docs/stacks.md
CHANGED
|
@@ -41,6 +41,43 @@ Auto-pick a port:
|
|
|
41
41
|
happys stack new exp2
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
## Create a PR test stack (copy/paste friendly)
|
|
45
|
+
|
|
46
|
+
If you want maintainers to be able to try your PR quickly, you can give them a single command that:
|
|
47
|
+
|
|
48
|
+
- creates an isolated stack
|
|
49
|
+
- checks out PR(s) into worktrees
|
|
50
|
+
- pins those worktrees to the stack
|
|
51
|
+
- optionally seeds auth
|
|
52
|
+
- optionally starts the stack in dev mode
|
|
53
|
+
|
|
54
|
+
Example (most common):
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
happys stack pr pr123 \
|
|
58
|
+
--happy=https://github.com/slopus/happy/pull/123 \
|
|
59
|
+
--happy-cli=https://github.com/slopus/happy-cli/pull/456 \
|
|
60
|
+
--seed-auth --copy-auth-from=dev-auth --link-auth \
|
|
61
|
+
--dev
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Notes:
|
|
65
|
+
|
|
66
|
+
- `--remote` (default `upstream`) controls which Git remote is used to fetch `refs/pull/<n>/head`.
|
|
67
|
+
- `--seed-auth` uses `happys stack auth <stack> copy-from <source>` under the hood, which also best-effort seeds DB Account rows (avoids FK errors like Prisma `P2003`).
|
|
68
|
+
- You can use your existing non-stacks Happy install as an auth seed source with:
|
|
69
|
+
- `--copy-auth-from=legacy` (reads from `~/.happy/{cli,server-light}` best-effort)
|
|
70
|
+
- `--link-auth` symlinks auth files instead of copying them (keeps credentials in sync, but reduces isolation).
|
|
71
|
+
- For full-server stacks (`happy-server`), seeding may need Docker infra:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
happys stack pr pr789 \
|
|
75
|
+
--server=happy-server \
|
|
76
|
+
--happy-server=https://github.com/slopus/happy-server/pull/789 \
|
|
77
|
+
--seed-auth --copy-auth-from=dev-auth --with-infra \
|
|
78
|
+
--dev
|
|
79
|
+
```
|
|
80
|
+
|
|
44
81
|
Interactive wizard (TTY only):
|
|
45
82
|
|
|
46
83
|
```bash
|
|
@@ -193,6 +230,8 @@ On startup, `happy-stacks` loads env in this order:
|
|
|
193
230
|
|
|
194
231
|
`happys stack ...` sets `HAPPY_STACKS_ENV_FILE=~/.happy/stacks/<name>/env` (and also sets legacy `HAPPY_LOCAL_ENV_FILE`) and clears any already-exported `HAPPY_STACKS_*` / `HAPPY_LOCAL_*` variables so the stack env stays authoritative.
|
|
195
232
|
|
|
233
|
+
For a full explanation of the different folders/paths (`home` vs `workspace` vs `runtime` vs stack storage) and the exact env precedence rules, see: `[docs/paths-and-env.md](docs/paths-and-env.md)`.
|
|
234
|
+
|
|
196
235
|
Cloned-repo fallback (before you run `happys init`):
|
|
197
236
|
|
|
198
237
|
1. `<repo>/.env` (defaults)
|
|
@@ -15,8 +15,11 @@ _server_url="${2:-}" # ignored (kept for backwards compatibility)
|
|
|
15
15
|
_webapp_url="${3:-}" # ignored (kept for backwards compatibility)
|
|
16
16
|
_cli_home_dir="${4:-}" # ignored (kept for backwards compatibility)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
|
+
DEFAULT_HOME_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
20
|
+
|
|
21
|
+
HAPPY_LOCAL_DIR="${HAPPY_LOCAL_DIR:-${HAPPY_STACKS_HOME_DIR:-$DEFAULT_HOME_DIR}}"
|
|
22
|
+
HAPPY_STACKS_HOME_DIR="${HAPPY_STACKS_HOME_DIR:-$HAPPY_LOCAL_DIR}"
|
|
20
23
|
|
|
21
24
|
HAPPYS_TERM="$HAPPY_LOCAL_DIR/extras/swiftbar/happys-term.sh"
|
|
22
25
|
if [[ ! -x "$HAPPYS_TERM" ]]; then
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Refresh SwiftBar Git/worktree cache.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# ./git-cache-refresh.sh all
|
|
8
|
+
# ./git-cache-refresh.sh main
|
|
9
|
+
# ./git-cache-refresh.sh stack <name>
|
|
10
|
+
# ./git-cache-refresh.sh component <context:main|stack> <stackName> <component>
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
DEFAULT_HOME_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
14
|
+
|
|
15
|
+
HAPPY_LOCAL_DIR="${HAPPY_LOCAL_DIR:-${HAPPY_STACKS_HOME_DIR:-$DEFAULT_HOME_DIR}}"
|
|
16
|
+
HAPPY_STACKS_HOME_DIR="${HAPPY_STACKS_HOME_DIR:-$HAPPY_LOCAL_DIR}"
|
|
17
|
+
|
|
18
|
+
LIB_DIR="$HAPPY_LOCAL_DIR/extras/swiftbar/lib"
|
|
19
|
+
if [[ ! -f "$LIB_DIR/utils.sh" ]]; then
|
|
20
|
+
echo "missing SwiftBar libs at: $LIB_DIR" >&2
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# shellcheck source=/dev/null
|
|
25
|
+
source "$LIB_DIR/utils.sh"
|
|
26
|
+
HAPPY_LOCAL_DIR="$(resolve_happy_local_dir)"
|
|
27
|
+
LIB_DIR="$HAPPY_LOCAL_DIR/extras/swiftbar/lib"
|
|
28
|
+
# shellcheck source=/dev/null
|
|
29
|
+
source "$LIB_DIR/git.sh"
|
|
30
|
+
|
|
31
|
+
components=(happy happy-cli happy-server-light happy-server)
|
|
32
|
+
|
|
33
|
+
refresh_one() {
|
|
34
|
+
local context="$1"
|
|
35
|
+
local stack="$2"
|
|
36
|
+
local component="$3"
|
|
37
|
+
local env_file="$4"
|
|
38
|
+
|
|
39
|
+
local active_dir=""
|
|
40
|
+
if [[ -n "$env_file" && -f "$env_file" ]]; then
|
|
41
|
+
active_dir="$(resolve_component_dir_from_env_file "$env_file" "$component")"
|
|
42
|
+
else
|
|
43
|
+
active_dir="$(resolve_component_dir_from_env "$component")"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
git_cache_refresh_one "$context" "$stack" "$component" "$active_dir" >/dev/null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
refresh_stack() {
|
|
50
|
+
local stack="$1"
|
|
51
|
+
local env_file=""
|
|
52
|
+
if [[ "$stack" == "main" ]]; then
|
|
53
|
+
env_file="$(resolve_main_env_file)"
|
|
54
|
+
[[ -z "$env_file" ]] && env_file="$(resolve_stack_env_file main)"
|
|
55
|
+
else
|
|
56
|
+
env_file="$(resolve_stack_env_file "$stack")"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
for c in "${components[@]}"; do
|
|
60
|
+
refresh_one "stack" "$stack" "$c" "$env_file"
|
|
61
|
+
done
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
cmd="${1:-}"
|
|
65
|
+
case "$cmd" in
|
|
66
|
+
all)
|
|
67
|
+
refresh_stack "main"
|
|
68
|
+
STACKS_DIR="$(resolve_stacks_storage_root)"
|
|
69
|
+
LEGACY_STACKS_DIR="$HOME/.happy/local/stacks"
|
|
70
|
+
if swiftbar_is_sandboxed; then
|
|
71
|
+
LEGACY_STACKS_DIR=""
|
|
72
|
+
fi
|
|
73
|
+
STACK_NAMES="$(
|
|
74
|
+
{
|
|
75
|
+
ls -1 "$STACKS_DIR" 2>/dev/null || true
|
|
76
|
+
[[ -n "$LEGACY_STACKS_DIR" ]] && ls -1 "$LEGACY_STACKS_DIR" 2>/dev/null || true
|
|
77
|
+
} | sort -u
|
|
78
|
+
)"
|
|
79
|
+
while IFS= read -r s; do
|
|
80
|
+
[[ -n "$s" ]] || continue
|
|
81
|
+
[[ "$s" == "main" ]] && continue
|
|
82
|
+
refresh_stack "$s"
|
|
83
|
+
done <<<"$STACK_NAMES"
|
|
84
|
+
git_cache_touch_last_refresh "all"
|
|
85
|
+
echo "ok: git cache refreshed (all)"
|
|
86
|
+
;;
|
|
87
|
+
main)
|
|
88
|
+
# Main stack only (fast).
|
|
89
|
+
local_env="$(resolve_main_env_file)"
|
|
90
|
+
for c in "${components[@]}"; do
|
|
91
|
+
refresh_one "main" "main" "$c" "$local_env"
|
|
92
|
+
done
|
|
93
|
+
git_cache_touch_last_refresh "main"
|
|
94
|
+
echo "ok: git cache refreshed (main)"
|
|
95
|
+
;;
|
|
96
|
+
stack)
|
|
97
|
+
stack="${2:-}"
|
|
98
|
+
if [[ -z "$stack" ]]; then
|
|
99
|
+
echo "usage: $0 stack <name>" >&2
|
|
100
|
+
exit 2
|
|
101
|
+
fi
|
|
102
|
+
refresh_stack "$stack"
|
|
103
|
+
git_cache_touch_last_refresh "stack:${stack}"
|
|
104
|
+
echo "ok: git cache refreshed (stack $stack)"
|
|
105
|
+
;;
|
|
106
|
+
component)
|
|
107
|
+
context="${2:-}"
|
|
108
|
+
stack="${3:-}"
|
|
109
|
+
component="${4:-}"
|
|
110
|
+
if [[ -z "$context" || -z "$stack" || -z "$component" ]]; then
|
|
111
|
+
echo "usage: $0 component <main|stack> <stackName> <component>" >&2
|
|
112
|
+
exit 2
|
|
113
|
+
fi
|
|
114
|
+
env_file=""
|
|
115
|
+
if [[ "$stack" == "main" ]]; then
|
|
116
|
+
env_file="$(resolve_main_env_file)"
|
|
117
|
+
[[ -z "$env_file" ]] && env_file="$(resolve_stack_env_file main)"
|
|
118
|
+
else
|
|
119
|
+
env_file="$(resolve_stack_env_file "$stack")"
|
|
120
|
+
fi
|
|
121
|
+
refresh_one "$context" "$stack" "$component" "$env_file"
|
|
122
|
+
git_cache_touch_last_refresh "component:${context}:${stack}:${component}"
|
|
123
|
+
echo "ok: git cache refreshed ($context/$stack/$component)"
|
|
124
|
+
;;
|
|
125
|
+
*)
|
|
126
|
+
echo "usage: $0 all|main|stack <name>|component <context> <stackName> <component>" >&2
|
|
127
|
+
exit 2
|
|
128
|
+
;;
|
|
129
|
+
esac
|
|
130
|
+
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
|
|
20
20
|
# SwiftBar runs with a minimal environment, so users often won't have
|
|
21
21
|
# HAPPY_STACKS_HOME_DIR / HAPPY_STACKS_WORKSPACE_DIR exported.
|
|
22
|
-
# Treat
|
|
23
|
-
|
|
22
|
+
# Treat <canonicalHomeDir>/.env as the canonical pointer file (written by `happys init`).
|
|
23
|
+
# Default: ~/.happy-stacks/.env
|
|
24
|
+
CANONICAL_HOME_DIR="${HAPPY_STACKS_CANONICAL_HOME_DIR:-${HAPPY_LOCAL_CANONICAL_HOME_DIR:-$HOME/.happy-stacks}}"
|
|
25
|
+
CANONICAL_ENV_FILE="$CANONICAL_HOME_DIR/.env"
|
|
24
26
|
|
|
25
27
|
_dotenv_get_quick() {
|
|
26
28
|
# Usage: _dotenv_get_quick /path/to/env KEY
|
|
@@ -54,7 +56,7 @@ if [[ -f "$CANONICAL_ENV_FILE" ]]; then
|
|
|
54
56
|
fi
|
|
55
57
|
_home_from_canonical="$(_expand_home_quick "${_home_from_canonical:-}")"
|
|
56
58
|
|
|
57
|
-
HAPPY_STACKS_HOME_DIR="${HAPPY_STACKS_HOME_DIR:-${_home_from_canonical:-$
|
|
59
|
+
HAPPY_STACKS_HOME_DIR="${HAPPY_STACKS_HOME_DIR:-${_home_from_canonical:-$CANONICAL_HOME_DIR}}"
|
|
58
60
|
HAPPY_LOCAL_DIR="${HAPPY_LOCAL_DIR:-$HAPPY_STACKS_HOME_DIR}"
|
|
59
61
|
HAPPY_LOCAL_PORT="${HAPPY_LOCAL_PORT:-3005}"
|
|
60
62
|
|
|
@@ -104,7 +106,12 @@ PNPM_BIN="$(resolve_pnpm_bin)"
|
|
|
104
106
|
MAIN_PORT="$(resolve_main_port)"
|
|
105
107
|
MAIN_SERVER_COMPONENT="$(resolve_main_server_component)"
|
|
106
108
|
TAILSCALE_URL="$(get_tailscale_url)"
|
|
109
|
+
if swiftbar_is_sandboxed; then
|
|
110
|
+
# Never probe Tailscale (global machine state) when sandboxing.
|
|
111
|
+
TAILSCALE_URL=""
|
|
112
|
+
fi
|
|
107
113
|
MAIN_ENV_FILE="$(resolve_main_env_file)"
|
|
114
|
+
MENUBAR_MODE="$(resolve_menubar_mode)"
|
|
108
115
|
|
|
109
116
|
ensure_launchctl_cache
|
|
110
117
|
|
|
@@ -142,6 +149,21 @@ echo "---"
|
|
|
142
149
|
echo "Happy Stacks | size=14 font=SF Pro Display"
|
|
143
150
|
echo "---"
|
|
144
151
|
|
|
152
|
+
# Mode (selfhost vs dev)
|
|
153
|
+
if [[ "$MENUBAR_MODE" == "selfhost" ]]; then
|
|
154
|
+
echo "Mode: Selfhost | sfimage=house"
|
|
155
|
+
else
|
|
156
|
+
echo "Mode: Dev | sfimage=hammer"
|
|
157
|
+
fi
|
|
158
|
+
if [[ -n "$PNPM_BIN" ]]; then
|
|
159
|
+
if [[ "$MENUBAR_MODE" == "selfhost" ]]; then
|
|
160
|
+
echo "--Switch to Dev mode | bash=$PNPM_BIN param1=menubar param2=mode param3=dev dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
161
|
+
else
|
|
162
|
+
echo "--Switch to Selfhost mode | bash=$PNPM_BIN param1=menubar param2=mode param3=selfhost dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
163
|
+
fi
|
|
164
|
+
fi
|
|
165
|
+
echo "---"
|
|
166
|
+
|
|
145
167
|
# Main stack (inline)
|
|
146
168
|
echo "Main stack"
|
|
147
169
|
echo "---"
|
|
@@ -153,90 +175,118 @@ render_component_autostart "" "main" "$MAIN_LABEL" "$MAIN_LAUNCHAGENT_STATUS" "$
|
|
|
153
175
|
render_component_tailscale "" "main" "$TAILSCALE_URL"
|
|
154
176
|
|
|
155
177
|
echo "---"
|
|
156
|
-
|
|
157
|
-
echo "
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
178
|
+
if [[ "$MENUBAR_MODE" == "selfhost" ]]; then
|
|
179
|
+
echo "Maintenance | sfimage=wrench.and.screwdriver"
|
|
180
|
+
if [[ -n "$PNPM_BIN" ]]; then
|
|
181
|
+
UPDATE_JSON="${HAPPY_LOCAL_DIR}/cache/update.json"
|
|
182
|
+
update_available=""
|
|
183
|
+
latest=""
|
|
184
|
+
current=""
|
|
185
|
+
if [[ -f "$UPDATE_JSON" ]]; then
|
|
186
|
+
update_available="$(grep -oE '\"updateAvailable\"[[:space:]]*:[[:space:]]*(true|false)' "$UPDATE_JSON" 2>/dev/null | head -1 | grep -oE '(true|false)' || true)"
|
|
187
|
+
latest="$(grep -oE '\"latest\"[[:space:]]*:[[:space:]]*\"[^\"]+\"' "$UPDATE_JSON" 2>/dev/null | head -1 | sed -E 's/.*\"latest\"[[:space:]]*:[[:space:]]*\"([^\"]+)\".*/\\1/' || true)"
|
|
188
|
+
current="$(grep -oE '\"current\"[[:space:]]*:[[:space:]]*\"[^\"]+\"' "$UPDATE_JSON" 2>/dev/null | head -1 | sed -E 's/.*\"current\"[[:space:]]*:[[:space:]]*\"([^\"]+)\".*/\\1/' || true)"
|
|
189
|
+
fi
|
|
190
|
+
if [[ "$update_available" == "true" && -n "$latest" ]]; then
|
|
191
|
+
echo "--Update available: ${current:-current} → ${latest} | color=$YELLOW"
|
|
192
|
+
else
|
|
193
|
+
echo "--Updates: up to date | color=$GRAY"
|
|
194
|
+
fi
|
|
195
|
+
echo "--Check for updates | bash=$PNPM_BIN param1=self param2=check dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
196
|
+
echo "--Update happy-stacks runtime | bash=$PNPM_BIN param1=self param2=update dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
197
|
+
echo "--Doctor | bash=$PNPM_BIN param1=doctor dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
198
|
+
else
|
|
199
|
+
echo "--⚠️ happys not found (run: npx happy-stacks setup)"
|
|
200
|
+
fi
|
|
201
|
+
else
|
|
202
|
+
echo "Stacks | sfimage=server.rack"
|
|
203
|
+
STACKS_PREFIX="--"
|
|
204
|
+
|
|
205
|
+
if [[ -n "$PNPM_BIN" ]]; then
|
|
206
|
+
HAPPYS_TERM="$HAPPY_LOCAL_DIR/extras/swiftbar/happys-term.sh"
|
|
207
|
+
echo "${STACKS_PREFIX}New stack (interactive) | bash=$HAPPYS_TERM param1=stack param2=new param3=--interactive dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
208
|
+
echo "${STACKS_PREFIX}List stacks | bash=$HAPPYS_TERM param1=stack param2=list dir=$HAPPY_LOCAL_DIR terminal=false"
|
|
209
|
+
print_sep "$STACKS_PREFIX"
|
|
210
|
+
fi
|
|
165
211
|
|
|
166
|
-
STACKS_DIR="$(resolve_stacks_storage_root)"
|
|
167
|
-
LEGACY_STACKS_DIR="$HOME/.happy/local/stacks"
|
|
168
|
-
if
|
|
169
|
-
|
|
170
|
-
{
|
|
171
|
-
ls -1 "$STACKS_DIR" 2>/dev/null || true
|
|
172
|
-
ls -1 "$LEGACY_STACKS_DIR" 2>/dev/null || true
|
|
173
|
-
} | sort -u
|
|
174
|
-
)"
|
|
175
|
-
if [[ -z "$STACK_NAMES" ]]; then
|
|
176
|
-
echo "No stacks found | color=$GRAY"
|
|
212
|
+
STACKS_DIR="$(resolve_stacks_storage_root)"
|
|
213
|
+
LEGACY_STACKS_DIR="$HOME/.happy/local/stacks"
|
|
214
|
+
if swiftbar_is_sandboxed; then
|
|
215
|
+
LEGACY_STACKS_DIR=""
|
|
177
216
|
fi
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
217
|
+
if [[ -d "$STACKS_DIR" ]] || [[ -n "$LEGACY_STACKS_DIR" && -d "$LEGACY_STACKS_DIR" ]]; then
|
|
218
|
+
STACK_NAMES="$(
|
|
219
|
+
{
|
|
220
|
+
ls -1 "$STACKS_DIR" 2>/dev/null || true
|
|
221
|
+
[[ -n "$LEGACY_STACKS_DIR" ]] && ls -1 "$LEGACY_STACKS_DIR" 2>/dev/null || true
|
|
222
|
+
} | sort -u
|
|
223
|
+
)"
|
|
224
|
+
if [[ -z "$STACK_NAMES" ]]; then
|
|
225
|
+
echo "${STACKS_PREFIX}No stacks found | color=$GRAY"
|
|
226
|
+
fi
|
|
227
|
+
for s in $STACK_NAMES; do
|
|
228
|
+
env_file="$(resolve_stack_env_file "$s")"
|
|
229
|
+
[[ -f "$env_file" ]] || continue
|
|
230
|
+
|
|
231
|
+
# Ports may be ephemeral (runtime-only). Do not skip stacks if the env file does not pin a port.
|
|
232
|
+
port="$(resolve_stack_server_port "$s" "$env_file")"
|
|
233
|
+
|
|
234
|
+
server_component="$(dotenv_get "$env_file" "HAPPY_STACKS_SERVER_COMPONENT")"
|
|
235
|
+
[[ -z "$server_component" ]] && server_component="$(dotenv_get "$env_file" "HAPPY_LOCAL_SERVER_COMPONENT")"
|
|
236
|
+
[[ -n "$server_component" ]] || server_component="happy-server-light"
|
|
237
|
+
|
|
238
|
+
base_dir="$(resolve_stack_base_dir "$s" "$env_file")"
|
|
239
|
+
cli_home_dir="$(resolve_stack_cli_home_dir "$s" "$env_file")"
|
|
240
|
+
label="$(resolve_stack_label "$s")"
|
|
241
|
+
|
|
242
|
+
COLLECT="$(collect_stack_status "$port" "$cli_home_dir" "$label" "$base_dir")"
|
|
243
|
+
IFS=$'\t' read -r LEVEL SERVER_STATUS SERVER_PID SERVER_METRICS DAEMON_STATUS DAEMON_PID DAEMON_METRICS DAEMON_UPTIME LAST_HEARTBEAT LAUNCHAGENT_STATUS AUTOSTART_PID AUTOSTART_METRICS <<<"$COLLECT"
|
|
244
|
+
for v in SERVER_PID SERVER_METRICS DAEMON_PID DAEMON_METRICS DAEMON_UPTIME LAST_HEARTBEAT AUTOSTART_PID AUTOSTART_METRICS; do
|
|
245
|
+
if [[ "${!v}" == "-" ]]; then
|
|
246
|
+
printf -v "$v" '%s' ""
|
|
247
|
+
fi
|
|
248
|
+
done
|
|
249
|
+
|
|
250
|
+
render_stack_overview_item "Stack: $s" "$LEVEL" "$STACKS_PREFIX"
|
|
251
|
+
export STACK_LEVEL="$LEVEL"
|
|
252
|
+
render_stack_info "${STACKS_PREFIX}--" "$s" "$port" "$server_component" "$base_dir" "$cli_home_dir" "$label" "$env_file" ""
|
|
253
|
+
render_component_server "${STACKS_PREFIX}--" "$s" "$port" "$server_component" "$SERVER_STATUS" "$SERVER_PID" "$SERVER_METRICS" "" "$label"
|
|
254
|
+
render_component_daemon "${STACKS_PREFIX}--" "$DAEMON_STATUS" "$DAEMON_PID" "$DAEMON_METRICS" "$DAEMON_UPTIME" "$LAST_HEARTBEAT" "$cli_home_dir/daemon.state.json" "$s"
|
|
255
|
+
render_component_autostart "${STACKS_PREFIX}--" "$s" "$label" "$LAUNCHAGENT_STATUS" "$AUTOSTART_PID" "$AUTOSTART_METRICS" "$base_dir/logs"
|
|
256
|
+
render_component_tailscale "${STACKS_PREFIX}--" "$s" ""
|
|
257
|
+
render_components_menu "${STACKS_PREFIX}--" "stack" "$s" "$env_file"
|
|
200
258
|
done
|
|
259
|
+
else
|
|
260
|
+
echo "${STACKS_PREFIX}No stacks dir found at: $(shorten_path "$STACKS_DIR" 52) | color=$GRAY"
|
|
261
|
+
fi
|
|
201
262
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
render_stack_info "--" "$s" "$port" "$server_component" "$base_dir" "$cli_home_dir" "$label" "$env_file" ""
|
|
205
|
-
render_component_server "--" "$s" "$port" "$server_component" "$SERVER_STATUS" "$SERVER_PID" "$SERVER_METRICS" "" "$label"
|
|
206
|
-
render_component_daemon "--" "$DAEMON_STATUS" "$DAEMON_PID" "$DAEMON_METRICS" "$DAEMON_UPTIME" "$LAST_HEARTBEAT" "$cli_home_dir/daemon.state.json" "$s"
|
|
207
|
-
render_component_autostart "--" "$s" "$label" "$LAUNCHAGENT_STATUS" "$AUTOSTART_PID" "$AUTOSTART_METRICS" "$base_dir/logs"
|
|
208
|
-
render_component_tailscale "--" "$s" ""
|
|
209
|
-
render_components_menu "--" "stack" "$s" "$env_file"
|
|
210
|
-
done
|
|
211
|
-
else
|
|
212
|
-
echo "No stacks dir found at: $(shorten_path "$STACKS_DIR" 52) | color=$GRAY"
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
echo "---"
|
|
216
|
-
render_components_menu "" "main" "main" "$MAIN_ENV_FILE"
|
|
263
|
+
echo "---"
|
|
264
|
+
render_components_menu "" "main" "main" "$MAIN_ENV_FILE"
|
|
217
265
|
|
|
218
|
-
echo "Worktrees | sfimage=arrow.triangle.branch"
|
|
219
|
-
if [[ -z "$PNPM_BIN" ]]; then
|
|
220
|
-
|
|
221
|
-
else
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
fi
|
|
266
|
+
echo "Worktrees | sfimage=arrow.triangle.branch"
|
|
267
|
+
if [[ -z "$PNPM_BIN" ]]; then
|
|
268
|
+
echo "--⚠️ happys not found (run: npx happy-stacks setup)"
|
|
269
|
+
else
|
|
270
|
+
HAPPYS_TERM="$HAPPY_LOCAL_DIR/extras/swiftbar/happys-term.sh"
|
|
271
|
+
echo "--Use (interactive) | bash=$HAPPYS_TERM param1=wt param2=use param3=--interactive dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
272
|
+
echo "--New (interactive) | bash=$HAPPYS_TERM param1=wt param2=new param3=--interactive dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
273
|
+
echo "--PR worktree (prompt) | bash=$HAPPY_LOCAL_DIR/extras/swiftbar/wt-pr.sh dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
274
|
+
echo "--Sync mirrors (all) | bash=$PNPM_BIN param1=wt param2=sync-all dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
275
|
+
echo "--Update all (dry-run) | bash=$HAPPYS_TERM param1=wt param2=update-all param3=--dry-run dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
276
|
+
echo "--Update all (apply) | bash=$PNPM_BIN param1=wt param2=update-all dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
277
|
+
fi
|
|
230
278
|
|
|
231
|
-
echo "---"
|
|
232
|
-
echo "Setup / Tools"
|
|
233
|
-
if [[ -z "$PNPM_BIN" ]]; then
|
|
234
|
-
|
|
235
|
-
else
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
279
|
+
echo "---"
|
|
280
|
+
echo "Setup / Tools"
|
|
281
|
+
if [[ -z "$PNPM_BIN" ]]; then
|
|
282
|
+
echo "--⚠️ happys not found (run: npx happy-stacks setup)"
|
|
283
|
+
else
|
|
284
|
+
HAPPYS_TERM="$HAPPY_LOCAL_DIR/extras/swiftbar/happys-term.sh"
|
|
285
|
+
echo "--Setup (guided) | bash=$HAPPYS_TERM param1=setup dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
286
|
+
echo "--Bootstrap (clone/install) | bash=$HAPPYS_TERM param1=bootstrap dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
287
|
+
echo "--CLI link (install happy wrapper) | bash=$HAPPYS_TERM param1=cli:link dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
288
|
+
echo "--Mobile dev helper | bash=$HAPPYS_TERM param1=mobile dir=$HAPPY_LOCAL_DIR terminal=false"
|
|
289
|
+
fi
|
|
240
290
|
fi
|
|
241
291
|
|
|
242
292
|
echo "---"
|