happy-stacks 0.0.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 +314 -0
- package/bin/happys.mjs +168 -0
- package/docs/menubar.md +186 -0
- package/docs/mobile-ios.md +134 -0
- package/docs/remote-access.md +43 -0
- package/docs/server-flavors.md +79 -0
- package/docs/stacks.md +218 -0
- package/docs/tauri.md +62 -0
- package/docs/worktrees-and-forks.md +395 -0
- package/extras/swiftbar/auth-login.sh +31 -0
- package/extras/swiftbar/happy-stacks.5s.sh +218 -0
- package/extras/swiftbar/icons/happy-green.png +0 -0
- package/extras/swiftbar/icons/happy-orange.png +0 -0
- package/extras/swiftbar/icons/happy-red.png +0 -0
- package/extras/swiftbar/icons/logo-white.png +0 -0
- package/extras/swiftbar/install.sh +191 -0
- package/extras/swiftbar/lib/git.sh +330 -0
- package/extras/swiftbar/lib/icons.sh +105 -0
- package/extras/swiftbar/lib/render.sh +774 -0
- package/extras/swiftbar/lib/system.sh +190 -0
- package/extras/swiftbar/lib/utils.sh +205 -0
- package/extras/swiftbar/pnpm-term.sh +125 -0
- package/extras/swiftbar/pnpm.sh +21 -0
- package/extras/swiftbar/set-interval.sh +62 -0
- package/extras/swiftbar/set-server-flavor.sh +57 -0
- package/extras/swiftbar/wt-pr.sh +95 -0
- package/package.json +58 -0
- package/scripts/auth.mjs +272 -0
- package/scripts/build.mjs +204 -0
- package/scripts/cli-link.mjs +58 -0
- package/scripts/completion.mjs +364 -0
- package/scripts/daemon.mjs +349 -0
- package/scripts/dev.mjs +181 -0
- package/scripts/doctor.mjs +342 -0
- package/scripts/happy.mjs +79 -0
- package/scripts/init.mjs +232 -0
- package/scripts/install.mjs +379 -0
- package/scripts/menubar.mjs +107 -0
- package/scripts/mobile.mjs +305 -0
- package/scripts/run.mjs +236 -0
- package/scripts/self.mjs +298 -0
- package/scripts/server_flavor.mjs +125 -0
- package/scripts/service.mjs +526 -0
- package/scripts/stack.mjs +815 -0
- package/scripts/tailscale.mjs +278 -0
- package/scripts/uninstall.mjs +190 -0
- package/scripts/utils/args.mjs +17 -0
- package/scripts/utils/cli.mjs +24 -0
- package/scripts/utils/cli_registry.mjs +262 -0
- package/scripts/utils/config.mjs +40 -0
- package/scripts/utils/dotenv.mjs +30 -0
- package/scripts/utils/env.mjs +138 -0
- package/scripts/utils/env_file.mjs +59 -0
- package/scripts/utils/env_local.mjs +25 -0
- package/scripts/utils/fs.mjs +11 -0
- package/scripts/utils/paths.mjs +184 -0
- package/scripts/utils/pm.mjs +294 -0
- package/scripts/utils/ports.mjs +66 -0
- package/scripts/utils/proc.mjs +66 -0
- package/scripts/utils/runtime.mjs +30 -0
- package/scripts/utils/server.mjs +41 -0
- package/scripts/utils/smoke_help.mjs +45 -0
- package/scripts/utils/validate.mjs +47 -0
- package/scripts/utils/wizard.mjs +69 -0
- package/scripts/utils/worktrees.mjs +78 -0
- package/scripts/where.mjs +105 -0
- package/scripts/worktrees.mjs +1721 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# Worktrees + forks (happy-stacks)
|
|
2
|
+
|
|
3
|
+
This repo is designed to run the Happy stack locally, while still making it easy to:
|
|
4
|
+
|
|
5
|
+
- keep using **your fork** day-to-day (your fork’s `main` stays your “distribution” branch)
|
|
6
|
+
- create **clean upstream PR branches** quickly (without carrying fork-only patches)
|
|
7
|
+
|
|
8
|
+
## Key idea: keep “active components” stable, put worktrees elsewhere
|
|
9
|
+
|
|
10
|
+
`happy-stacks` runs components from these default paths (in your workspace):
|
|
11
|
+
|
|
12
|
+
- `components/happy`
|
|
13
|
+
- `components/happy-cli`
|
|
14
|
+
- `components/happy-server-light`
|
|
15
|
+
- (optional) `components/happy-server`
|
|
16
|
+
|
|
17
|
+
All worktrees live under a hidden folder:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
components/.worktrees/<component>/<owner>/<branch...>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
|
|
25
|
+
- `components/.worktrees/happy/slopus/pr/session-rename-upstream`
|
|
26
|
+
- `components/.worktrees/happy-cli/slopus/ci/typecheck-gha-upstream`
|
|
27
|
+
- `components/.worktrees/happy/leeroybrun/local/my-fork-only-patch`
|
|
28
|
+
|
|
29
|
+
## Branch naming convention
|
|
30
|
+
|
|
31
|
+
Branches created/managed by `happy-stacks` worktree tooling are named:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
<owner>/<branch...>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Where:
|
|
38
|
+
|
|
39
|
+
- `<owner>` is derived from the repo remote you’re basing on
|
|
40
|
+
- **origin** → usually your fork owner (e.g. `leeroybrun`)
|
|
41
|
+
- **upstream** → upstream owner (e.g. `slopus`)
|
|
42
|
+
- `<branch...>` is whatever you choose (`pr/...`, `feat/...`, `local/...`, etc.)
|
|
43
|
+
|
|
44
|
+
## Choosing which checkout happy-stacks runs
|
|
45
|
+
|
|
46
|
+
`happy-stacks` supports per-component directory overrides via the stack env file (main: `~/.happy/stacks/main/env`, or a specific stack: `~/.happy/stacks/<name>/env`):
|
|
47
|
+
|
|
48
|
+
- `HAPPY_STACKS_COMPONENT_DIR_HAPPY` (legacy: `HAPPY_LOCAL_COMPONENT_DIR_HAPPY`)
|
|
49
|
+
- `HAPPY_STACKS_COMPONENT_DIR_HAPPY_CLI` (legacy: `HAPPY_LOCAL_COMPONENT_DIR_HAPPY_CLI`)
|
|
50
|
+
- `HAPPY_STACKS_COMPONENT_DIR_HAPPY_SERVER_LIGHT` (legacy: `HAPPY_LOCAL_COMPONENT_DIR_HAPPY_SERVER_LIGHT`)
|
|
51
|
+
- `HAPPY_STACKS_COMPONENT_DIR_HAPPY_SERVER` (legacy: `HAPPY_LOCAL_COMPONENT_DIR_HAPPY_SERVER`)
|
|
52
|
+
|
|
53
|
+
The easiest way to set these is with:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
happys wt use happy slopus/pr/session-rename-upstream
|
|
57
|
+
happys wt use happy-cli slopus/pr/resume-session-from-ui-upstream
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Now `happys dev`, `happys start`, and `happys build` will use those active checkouts.
|
|
61
|
+
|
|
62
|
+
## Switching server flavor (server-light vs full server)
|
|
63
|
+
|
|
64
|
+
You can persistently switch which server implementation is used by setting `HAPPY_STACKS_SERVER_COMPONENT` (legacy: `HAPPY_LOCAL_SERVER_COMPONENT`) in the stack env file (main: `~/.happy/stacks/main/env`).
|
|
65
|
+
|
|
66
|
+
Use the convenience CLI (recommended):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
happys srv status
|
|
70
|
+
happys srv use happy-server-light
|
|
71
|
+
happys srv use happy-server
|
|
72
|
+
happys srv use --interactive
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Note: in a cloned repo, the legacy equivalent is `pnpm srv -- ...`.
|
|
76
|
+
|
|
77
|
+
Reset back to default:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
happys wt use happy default
|
|
81
|
+
happys wt use happy-cli default
|
|
82
|
+
happys wt use happy-server-light default
|
|
83
|
+
happys wt use happy-server default
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Note:
|
|
87
|
+
- `happys srv use ...` picks **which** server component is run.
|
|
88
|
+
- `happys wt use happy-server-light ...` / `happys wt use happy-server ...` pick **which checkout** is used for each server component.
|
|
89
|
+
- `happys start/dev/doctor` will error if these are accidentally mismatched (e.g. server-light selected but its component dir points inside a `happy-server` checkout).
|
|
90
|
+
|
|
91
|
+
## Creating worktrees
|
|
92
|
+
|
|
93
|
+
Create a new worktree branch based on **upstream** (for upstream PRs):
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
happys wt new happy pr/my-feature --from=upstream --use
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Testing a GitHub PR locally (`wt pr`)
|
|
100
|
+
|
|
101
|
+
If you have a GitHub PR URL (or just the PR number), you can create a worktree at the PR head ref:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
happys wt pr happy https://github.com/slopus/happy/pull/123 --use
|
|
105
|
+
|
|
106
|
+
# same, but specify the remote explicitly
|
|
107
|
+
happys wt pr happy 123 --remote=upstream --use
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Notes:
|
|
111
|
+
- This uses GitHub’s standard `refs/pull/<N>/head` ref on the chosen remote (default: `upstream`).
|
|
112
|
+
- To refresh the worktree when new commits are pushed to the PR, re-run with `--update`:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
happys wt pr happy 123 --update
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- If you have uncommitted changes in the PR worktree, you can use `--stash` / `--stash-keep`:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
happys wt pr happy 123 --update --stash
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- If the PR was force-pushed and the update is not a fast-forward, `--update` will abort and tell you to re-run with `--update --force`.
|
|
125
|
+
- Use `--slug=<name>` to create a nicer local branch name (example: `slopus/pr/123-fix-thing`).
|
|
126
|
+
|
|
127
|
+
### Testing a PR inside a stack (recommended)
|
|
128
|
+
|
|
129
|
+
Create a dedicated stack, then apply the PR into that stack env:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
happys stack new pr123
|
|
133
|
+
happys stack wt pr123 -- pr happy https://github.com/slopus/happy/pull/123 --use
|
|
134
|
+
happys stack dev pr123
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Create a new worktree branch based on **your fork** (for fork-only patches):
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
happys wt new happy local/my-patch --from=origin --use
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Remote + base behavior (automatic)
|
|
144
|
+
|
|
145
|
+
If you do **not** pass `--remote`, `happys wt new` defaults to using the Git remote named `upstream`.
|
|
146
|
+
|
|
147
|
+
It will also keep a local **mirror branch** named after the remote owner **and that remote’s default branch**:
|
|
148
|
+
|
|
149
|
+
- if `upstream` points at `slopus/*` and its default branch is `main`, it will create/update `slopus/main` tracking `upstream/main`
|
|
150
|
+
- if `origin` (or `fork`) points at `leeroybrun/*` and its default branch is `happy-server-light`, it will create/update `leeroybrun/happy-server-light` tracking `fork/happy-server-light`
|
|
151
|
+
|
|
152
|
+
New PR worktrees created without `--base` will default to using that mirror branch (example: `slopus/main`) as the base.
|
|
153
|
+
|
|
154
|
+
### Syncing a remote to a local mirror branch
|
|
155
|
+
|
|
156
|
+
`happys wt sync <component>` keeps a local mirror branch up to date inside that component repo:
|
|
157
|
+
|
|
158
|
+
- It fetches the remote’s **default branch** (for that remote + component)
|
|
159
|
+
- Then it updates a local branch named `<owner>/<default-branch>` to track it
|
|
160
|
+
|
|
161
|
+
Examples:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# Sync upstream (usually slopus/main)
|
|
165
|
+
happys wt sync happy --remote=upstream
|
|
166
|
+
|
|
167
|
+
# Sync your fork remote (origin/fork). For happy-server-light this is typically leeroybrun/happy-server-light.
|
|
168
|
+
happys wt sync happy-server-light --remote=origin
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
After syncing, you can explicitly base a new worktree on the mirror branch if you want:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
happys wt new happy pr/my-feature --remote=upstream --base=slopus/main --use
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Interactive mode
|
|
178
|
+
|
|
179
|
+
If you prefer prompts:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
happys wt new --interactive
|
|
183
|
+
happys wt use --interactive
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### JSON mode
|
|
187
|
+
|
|
188
|
+
For programmatic usage:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
happys wt list happy --json
|
|
192
|
+
happys wt sync happy --json
|
|
193
|
+
happys wt new happy pr/my-feature --use --json
|
|
194
|
+
happys wt status happy --json
|
|
195
|
+
happys wt update happy default --dry-run --json
|
|
196
|
+
happys wt push happy default --dry-run --json
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Migrating old worktree layout (one-time)
|
|
200
|
+
|
|
201
|
+
## Workflow helpers (sync / update / push)
|
|
202
|
+
|
|
203
|
+
These are convenience commands to keep PR branches updated and to automate the “check conflicts first” loop.
|
|
204
|
+
|
|
205
|
+
### `wt status`
|
|
206
|
+
|
|
207
|
+
Shows branch / upstream / ahead/behind / clean state:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
happys wt status happy
|
|
211
|
+
happys wt status happy --json
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Worktree selector semantics (`default` / `main` / `active`)
|
|
215
|
+
|
|
216
|
+
Many `happys wt` commands accept an optional “selector” argument to choose *which checkout* you mean.
|
|
217
|
+
|
|
218
|
+
- **(omitted)** or **`active`**: the currently active checkout for that component (env override if set; otherwise `components/<component>`)
|
|
219
|
+
- **`default`** or **`main`**: the default embedded checkout at `components/<component>`
|
|
220
|
+
- **`<owner>/<branch...>`**: resolves to `components/.worktrees/<component>/<owner>/<branch...>`
|
|
221
|
+
- **`/absolute/path`**: explicit checkout path
|
|
222
|
+
|
|
223
|
+
Important: `default` / `main` refers to the **checkout location**, not the Git branch name.
|
|
224
|
+
|
|
225
|
+
### `wt update`
|
|
226
|
+
|
|
227
|
+
Update a worktree branch from its upstream base.
|
|
228
|
+
|
|
229
|
+
- **Default base**: the per-repo mirror branch (example: `slopus/main`, or `leeroybrun/happy-server-light` when syncing from your fork remote)
|
|
230
|
+
- **Default mode**: `rebase` (recommended for clean PR branches)
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Check if update would conflict (no changes applied)
|
|
234
|
+
happys wt update happy default --dry-run
|
|
235
|
+
|
|
236
|
+
# Apply rebase if clean; if conflicts, abort and print conflicting files
|
|
237
|
+
happys wt update happy default
|
|
238
|
+
|
|
239
|
+
# If you have uncommitted changes, auto-stash, update, then pop the stash back (only if the update was clean)
|
|
240
|
+
happys wt update happy default --stash
|
|
241
|
+
|
|
242
|
+
# Same, but keep the stash (do not pop) so you can apply it later
|
|
243
|
+
happys wt update happy default --stash-keep
|
|
244
|
+
|
|
245
|
+
# Keep conflict state in place for manual resolution
|
|
246
|
+
happys wt update happy default --force
|
|
247
|
+
|
|
248
|
+
# Use merge instead of rebase (optional)
|
|
249
|
+
happys wt update happy default --merge
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Open a “real” shell in a worktree (`wt shell`)
|
|
253
|
+
|
|
254
|
+
This starts a new interactive shell **with cwd set to the worktree**, which is the closest thing to a “real cd” a CLI can do:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
happys wt shell happy slopus/pr/123
|
|
258
|
+
|
|
259
|
+
# choose a shell explicitly
|
|
260
|
+
happys wt shell happy slopus/pr/123 --shell=/bin/zsh
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
You can also ask it to open a new terminal window/tab (best-effort):
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
happys wt shell happy slopus/pr/123 --new-window
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
On macOS, auto-detection tries: Ghostty → iTerm → Terminal. You can override via:
|
|
270
|
+
|
|
271
|
+
- `HAPPY_STACKS_WT_TERMINAL=ghostty|iterm|terminal|current` (legacy: `HAPPY_LOCAL_WT_TERMINAL`)
|
|
272
|
+
- `HAPPY_STACKS_WT_SHELL=/path/to/shell` (legacy: `HAPPY_LOCAL_WT_SHELL`)
|
|
273
|
+
|
|
274
|
+
Works with stacks too:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
happys stack wt pr123 -- shell happy active
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Open in editors (`wt code` / `wt cursor`)
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
happys wt code happy slopus/pr/123
|
|
284
|
+
happys wt cursor happy slopus/pr/123
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Notes:
|
|
288
|
+
- `wt code` requires VS Code’s `code` CLI on PATH.
|
|
289
|
+
- `wt cursor` uses the `cursor` CLI if available; on macOS it falls back to `open -a Cursor`.
|
|
290
|
+
|
|
291
|
+
### `wt push`
|
|
292
|
+
|
|
293
|
+
Push current HEAD branch to a remote:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
happys wt push happy default
|
|
297
|
+
happys wt push happy default --remote=origin
|
|
298
|
+
happys wt push happy default --dry-run
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Create worktrees from an existing worktree/branch
|
|
302
|
+
|
|
303
|
+
If you want to base a new worktree off another worktree’s current branch/HEAD:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
happys wt new happy pr/next-step --base-worktree=slopus/pr/my-existing-thing
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Run git inside a worktree (`wt git`)
|
|
310
|
+
|
|
311
|
+
This is a convenience wrapper that runs `git` in a selected checkout:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
happys wt git happy main -- status
|
|
315
|
+
happys wt git happy active -- log -n 5 --oneline
|
|
316
|
+
happys wt git happy slopus/pr/session-rename-upstream -- diff
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
For programmatic usage:
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
happys wt git happy main -- status --porcelain -b --json
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Sync/update everything
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
happys wt sync-all
|
|
329
|
+
happys wt sync-all --json
|
|
330
|
+
|
|
331
|
+
# Dry-run updates across all worktrees for a component (or all components if omitted)
|
|
332
|
+
happys wt update-all happy --dry-run
|
|
333
|
+
happys wt update-all --dry-run --json
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
If you previously had worktrees under `components/happy-worktrees/*`, run:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
happys wt migrate
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
This will:
|
|
343
|
+
|
|
344
|
+
- move worktrees into `components/.worktrees/...`
|
|
345
|
+
- rename checked-out branches to the `<owner>/...` convention
|
|
346
|
+
|
|
347
|
+
## Server selection: happy-server-light vs happy-server
|
|
348
|
+
|
|
349
|
+
By default, `happy-stacks` uses `happy-server-light`.
|
|
350
|
+
|
|
351
|
+
To run the full upstream server instead:
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
happys bootstrap --server=happy-server
|
|
355
|
+
happys dev --server=happy-server
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Notes:
|
|
359
|
+
|
|
360
|
+
- `happys start` (production-like) serves the built UI via `happy-server-light`; UI serving is **disabled** automatically when using `happy-server`.
|
|
361
|
+
- `happys dev` works with either server (it runs the UI separately via Expo).
|
|
362
|
+
|
|
363
|
+
### Selecting server via env (including LaunchAgent service)
|
|
364
|
+
|
|
365
|
+
You can set a default server implementation via:
|
|
366
|
+
|
|
367
|
+
- `HAPPY_STACKS_SERVER_COMPONENT=happy-server-light` (default) (legacy: `HAPPY_LOCAL_SERVER_COMPONENT`)
|
|
368
|
+
- `HAPPY_STACKS_SERVER_COMPONENT=happy-server` (legacy: `HAPPY_LOCAL_SERVER_COMPONENT`)
|
|
369
|
+
|
|
370
|
+
If you use the macOS LaunchAgent (`happys service install`), the service persists only a pointer to the env file path; the server flavor is read from that env file on every start.
|
|
371
|
+
|
|
372
|
+
## Env precedence (important)
|
|
373
|
+
|
|
374
|
+
When `happy-stacks` starts, it loads env in this order:
|
|
375
|
+
|
|
376
|
+
1) `~/.happy-stacks/.env` (defaults)
|
|
377
|
+
2) `~/.happy-stacks/env.local` (optional global overrides; most config is written to the stack env)
|
|
378
|
+
3) `HAPPY_STACKS_ENV_FILE` (stack env / explicit overlay; highest precedence for `HAPPY_STACKS_*` / `HAPPY_LOCAL_*`)
|
|
379
|
+
|
|
380
|
+
Notes:
|
|
381
|
+
|
|
382
|
+
- `HAPPY_STACKS_ENV_FILE` (legacy: `HAPPY_LOCAL_ENV_FILE`) is the mechanism used by `happys stack ...` to apply stack-specific settings.
|
|
383
|
+
- For stack runs, the stack wrapper clears any already-exported `HAPPY_STACKS_*` / legacy `HAPPY_LOCAL_*` variables so the stack env file stays authoritative.
|
|
384
|
+
- By default (after `happys init`), commands that “persist config” write to the main stack env file: `~/.happy/stacks/main/env`.
|
|
385
|
+
|
|
386
|
+
## Repo URLs
|
|
387
|
+
|
|
388
|
+
You can override clone sources in your main stack env file (`~/.happy/stacks/main/env`) or any explicit `HAPPY_STACKS_ENV_FILE`:
|
|
389
|
+
|
|
390
|
+
- `HAPPY_STACKS_REPO_SOURCE=forks|upstream` (legacy: `HAPPY_LOCAL_REPO_SOURCE`)
|
|
391
|
+
- `HAPPY_STACKS_UI_REPO_URL` (legacy: `HAPPY_LOCAL_UI_REPO_URL`)
|
|
392
|
+
- `HAPPY_STACKS_CLI_REPO_URL` (legacy: `HAPPY_LOCAL_CLI_REPO_URL`)
|
|
393
|
+
- `HAPPY_STACKS_SERVER_REPO_URL` (legacy: `HAPPY_LOCAL_SERVER_REPO_URL`) (server-light, backwards compatible)
|
|
394
|
+
- `HAPPY_STACKS_SERVER_LIGHT_REPO_URL` (legacy: `HAPPY_LOCAL_SERVER_LIGHT_REPO_URL`)
|
|
395
|
+
- `HAPPY_STACKS_SERVER_FULL_REPO_URL` (legacy: `HAPPY_LOCAL_SERVER_FULL_REPO_URL`)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Run auth login (interactive) in the user's preferred terminal.
|
|
5
|
+
#
|
|
6
|
+
# Usage (backwards compatible with older callers):
|
|
7
|
+
# ./auth-login.sh main <serverUrl> <webappUrl>
|
|
8
|
+
# ./auth-login.sh <stackName> <serverUrl> <webappUrl> <cliHomeDir>
|
|
9
|
+
#
|
|
10
|
+
# New behavior:
|
|
11
|
+
# - Delegate to `happys auth login` / `happys stack auth <name> login` so URL + cliHome resolution stays centralized.
|
|
12
|
+
|
|
13
|
+
stack="${1:-main}"
|
|
14
|
+
_server_url="${2:-}" # ignored (kept for backwards compatibility)
|
|
15
|
+
_webapp_url="${3:-}" # ignored (kept for backwards compatibility)
|
|
16
|
+
_cli_home_dir="${4:-}" # ignored (kept for backwards compatibility)
|
|
17
|
+
|
|
18
|
+
HAPPY_STACKS_HOME_DIR="${HAPPY_STACKS_HOME_DIR:-$HOME/.happy-stacks}"
|
|
19
|
+
HAPPY_LOCAL_DIR="${HAPPY_LOCAL_DIR:-$HAPPY_STACKS_HOME_DIR}"
|
|
20
|
+
|
|
21
|
+
PNPM_TERM="$HAPPY_LOCAL_DIR/extras/swiftbar/pnpm-term.sh"
|
|
22
|
+
if [[ ! -x "$PNPM_TERM" ]]; then
|
|
23
|
+
echo "missing terminal happys wrapper: $PNPM_TERM" >&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
if [[ "$stack" == "main" ]]; then
|
|
28
|
+
exec "$PNPM_TERM" auth login
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
exec "$PNPM_TERM" stack auth "$stack" login
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# <xbar.title>Happy Stacks</xbar.title>
|
|
4
|
+
# <xbar.version>1.0.0</xbar.version>
|
|
5
|
+
# <xbar.author>Happy Stacks</xbar.author>
|
|
6
|
+
# <xbar.author.github>leeroybrun</xbar.author.github>
|
|
7
|
+
# <xbar.desc>Monitor and control your Happy stacks from the menu bar</xbar.desc>
|
|
8
|
+
# <xbar.dependencies>node</xbar.dependencies>
|
|
9
|
+
# <swiftbar.hideAbout>true</swiftbar.hideAbout>
|
|
10
|
+
# <swiftbar.hideRunInTerminal>true</swiftbar.hideRunInTerminal>
|
|
11
|
+
# <swiftbar.hideLastUpdated>false</swiftbar.hideLastUpdated>
|
|
12
|
+
# <swiftbar.hideDisablePlugin>true</swiftbar.hideDisablePlugin>
|
|
13
|
+
# <swiftbar.hideSwiftBar>true</swiftbar.hideSwiftBar>
|
|
14
|
+
# <swiftbar.refreshOnOpen>false</swiftbar.refreshOnOpen>
|
|
15
|
+
|
|
16
|
+
# ============================================================================
|
|
17
|
+
# Configuration
|
|
18
|
+
# ============================================================================
|
|
19
|
+
|
|
20
|
+
HAPPY_STACKS_HOME_DIR="${HAPPY_STACKS_HOME_DIR:-$HOME/.happy-stacks}"
|
|
21
|
+
HAPPY_LOCAL_DIR="${HAPPY_LOCAL_DIR:-$HAPPY_STACKS_HOME_DIR}"
|
|
22
|
+
HAPPY_LOCAL_PORT="${HAPPY_LOCAL_PORT:-3005}"
|
|
23
|
+
|
|
24
|
+
# Map common preferences from HAPPY_STACKS_* -> HAPPY_LOCAL_* (SwiftBar scripts are shell-based).
|
|
25
|
+
if [[ -n "${HAPPY_STACKS_WT_TERMINAL:-}" && -z "${HAPPY_LOCAL_WT_TERMINAL:-}" ]]; then HAPPY_LOCAL_WT_TERMINAL="$HAPPY_STACKS_WT_TERMINAL"; fi
|
|
26
|
+
if [[ -n "${HAPPY_STACKS_WT_SHELL:-}" && -z "${HAPPY_LOCAL_WT_SHELL:-}" ]]; then HAPPY_LOCAL_WT_SHELL="$HAPPY_STACKS_WT_SHELL"; fi
|
|
27
|
+
if [[ -n "${HAPPY_STACKS_SWIFTBAR_ICON_PATH:-}" && -z "${HAPPY_LOCAL_SWIFTBAR_ICON_PATH:-}" ]]; then HAPPY_LOCAL_SWIFTBAR_ICON_PATH="$HAPPY_STACKS_SWIFTBAR_ICON_PATH"; fi
|
|
28
|
+
|
|
29
|
+
# Storage root migrated from ~/.happy/local -> ~/.happy/stacks/main.
|
|
30
|
+
if [[ -z "${HAPPY_HOME_DIR:-}" ]]; then
|
|
31
|
+
if [[ -d "$HOME/.happy/stacks/main" ]] || [[ ! -d "$HOME/.happy/local" ]]; then
|
|
32
|
+
HAPPY_HOME_DIR="$HOME/.happy/stacks/main"
|
|
33
|
+
else
|
|
34
|
+
HAPPY_HOME_DIR="$HOME/.happy/local"
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
CLI_HOME_DIR="$HAPPY_HOME_DIR/cli"
|
|
38
|
+
LOGS_DIR="$HAPPY_HOME_DIR/logs"
|
|
39
|
+
|
|
40
|
+
# Colors
|
|
41
|
+
GREEN="#34C759"
|
|
42
|
+
RED="#FF3B30"
|
|
43
|
+
YELLOW="#FFCC00"
|
|
44
|
+
GRAY="#8E8E93"
|
|
45
|
+
BLUE="#007AFF"
|
|
46
|
+
|
|
47
|
+
# ============================================================================
|
|
48
|
+
# Load libs
|
|
49
|
+
# ============================================================================
|
|
50
|
+
|
|
51
|
+
LIB_DIR="$HAPPY_LOCAL_DIR/extras/swiftbar/lib"
|
|
52
|
+
if [[ ! -f "$LIB_DIR/utils.sh" ]]; then
|
|
53
|
+
echo "Happy Stacks"
|
|
54
|
+
echo "---"
|
|
55
|
+
echo "SwiftBar libs missing at: $LIB_DIR"
|
|
56
|
+
echo "↪ run: happys menubar install"
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# shellcheck source=/dev/null
|
|
61
|
+
source "$LIB_DIR/utils.sh"
|
|
62
|
+
HAPPY_LOCAL_DIR="$(resolve_happy_local_dir)"
|
|
63
|
+
LIB_DIR="$HAPPY_LOCAL_DIR/extras/swiftbar/lib"
|
|
64
|
+
# shellcheck source=/dev/null
|
|
65
|
+
source "$LIB_DIR/icons.sh"
|
|
66
|
+
# shellcheck source=/dev/null
|
|
67
|
+
source "$LIB_DIR/system.sh"
|
|
68
|
+
# shellcheck source=/dev/null
|
|
69
|
+
source "$LIB_DIR/git.sh"
|
|
70
|
+
# shellcheck source=/dev/null
|
|
71
|
+
source "$LIB_DIR/render.sh"
|
|
72
|
+
|
|
73
|
+
# ============================================================================
|
|
74
|
+
# Menu
|
|
75
|
+
# ============================================================================
|
|
76
|
+
|
|
77
|
+
PNPM_BIN="$(resolve_pnpm_bin)"
|
|
78
|
+
MAIN_PORT="$(resolve_main_port)"
|
|
79
|
+
MAIN_SERVER_COMPONENT="$(resolve_main_server_component)"
|
|
80
|
+
TAILSCALE_URL="$(get_tailscale_url)"
|
|
81
|
+
MAIN_ENV_FILE="$(resolve_main_env_file)"
|
|
82
|
+
|
|
83
|
+
ensure_launchctl_cache
|
|
84
|
+
|
|
85
|
+
MAIN_COLLECT="$(collect_stack_status "$MAIN_PORT" "$CLI_HOME_DIR" "com.happy.stacks" "$HAPPY_HOME_DIR")"
|
|
86
|
+
IFS=$'\t' read -r MAIN_LEVEL MAIN_SERVER_STATUS MAIN_SERVER_PID MAIN_SERVER_METRICS MAIN_DAEMON_STATUS MAIN_DAEMON_PID MAIN_DAEMON_METRICS MAIN_DAEMON_UPTIME MAIN_LAST_HEARTBEAT MAIN_LAUNCHAGENT_STATUS MAIN_AUTOSTART_PID MAIN_AUTOSTART_METRICS <<<"$MAIN_COLLECT"
|
|
87
|
+
for v in MAIN_SERVER_PID MAIN_SERVER_METRICS MAIN_DAEMON_PID MAIN_DAEMON_METRICS MAIN_DAEMON_UPTIME MAIN_LAST_HEARTBEAT MAIN_AUTOSTART_PID MAIN_AUTOSTART_METRICS; do
|
|
88
|
+
if [[ "${!v}" == "-" ]]; then
|
|
89
|
+
printf -v "$v" '%s' ""
|
|
90
|
+
fi
|
|
91
|
+
done
|
|
92
|
+
|
|
93
|
+
# Menu bar icon
|
|
94
|
+
MENU_STATUS_ICON_B64="$(status_icon_b64 "$MAIN_LEVEL" 18)"
|
|
95
|
+
if [[ -n "$MENU_STATUS_ICON_B64" ]]; then
|
|
96
|
+
echo " | image=$MENU_STATUS_ICON_B64"
|
|
97
|
+
else
|
|
98
|
+
STATUS_COLOR="$(color_for_level "$MAIN_LEVEL")"
|
|
99
|
+
ICON_B64="$(get_menu_icon_b64)"
|
|
100
|
+
if [[ -n "$ICON_B64" ]]; then
|
|
101
|
+
echo "● | templateImage=$ICON_B64 color=$STATUS_COLOR"
|
|
102
|
+
else
|
|
103
|
+
echo "Happy"
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
echo "---"
|
|
108
|
+
echo "Happy Stacks | size=14 font=SF Pro Display"
|
|
109
|
+
echo "---"
|
|
110
|
+
|
|
111
|
+
# Main stack (inline)
|
|
112
|
+
echo "Main stack"
|
|
113
|
+
echo "---"
|
|
114
|
+
export MAIN_LEVEL="$MAIN_LEVEL"
|
|
115
|
+
render_stack_info "" "main" "$MAIN_PORT" "$MAIN_SERVER_COMPONENT" "$HAPPY_HOME_DIR" "$CLI_HOME_DIR" "com.happy.stacks" "$MAIN_ENV_FILE" "$TAILSCALE_URL"
|
|
116
|
+
render_component_server "" "main" "$MAIN_PORT" "$MAIN_SERVER_COMPONENT" "$MAIN_SERVER_STATUS" "$MAIN_SERVER_PID" "$MAIN_SERVER_METRICS" "$TAILSCALE_URL" "com.happy.stacks"
|
|
117
|
+
render_component_daemon "" "$MAIN_DAEMON_STATUS" "$MAIN_DAEMON_PID" "$MAIN_DAEMON_METRICS" "$MAIN_DAEMON_UPTIME" "$MAIN_LAST_HEARTBEAT" "$CLI_HOME_DIR/daemon.state.json" "main"
|
|
118
|
+
render_component_autostart "" "main" "com.happy.stacks" "$MAIN_LAUNCHAGENT_STATUS" "$MAIN_AUTOSTART_PID" "$MAIN_AUTOSTART_METRICS" "$LOGS_DIR"
|
|
119
|
+
render_component_tailscale "" "main" "$TAILSCALE_URL"
|
|
120
|
+
|
|
121
|
+
echo "---"
|
|
122
|
+
echo "Stacks"
|
|
123
|
+
echo "---"
|
|
124
|
+
|
|
125
|
+
if [[ -n "$PNPM_BIN" ]]; then
|
|
126
|
+
PNPM_TERM="$HAPPY_LOCAL_DIR/extras/swiftbar/pnpm-term.sh"
|
|
127
|
+
echo "New stack (interactive) | bash=$PNPM_TERM param1=stack param2=new param3=--interactive dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
128
|
+
echo "List stacks | bash=$PNPM_TERM param1=stack param2=list dir=$HAPPY_LOCAL_DIR terminal=false"
|
|
129
|
+
echo "---"
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
STACKS_DIR="$HOME/.happy/stacks"
|
|
133
|
+
if [[ -d "$STACKS_DIR" ]]; then
|
|
134
|
+
STACK_NAMES="$(ls -1 "$STACKS_DIR" 2>/dev/null || true)"
|
|
135
|
+
if [[ -z "$STACK_NAMES" ]]; then
|
|
136
|
+
echo "No stacks found | color=$GRAY"
|
|
137
|
+
fi
|
|
138
|
+
for s in $STACK_NAMES; do
|
|
139
|
+
env_file="$STACKS_DIR/$s/env"
|
|
140
|
+
[[ -f "$env_file" ]] || continue
|
|
141
|
+
|
|
142
|
+
port="$(dotenv_get "$env_file" "HAPPY_LOCAL_SERVER_PORT")"
|
|
143
|
+
[[ -n "$port" ]] || continue
|
|
144
|
+
|
|
145
|
+
server_component="$(dotenv_get "$env_file" "HAPPY_LOCAL_SERVER_COMPONENT")"
|
|
146
|
+
[[ -n "$server_component" ]] || server_component="happy-server-light"
|
|
147
|
+
|
|
148
|
+
cli_home_dir="$(dotenv_get "$env_file" "HAPPY_LOCAL_CLI_HOME_DIR")"
|
|
149
|
+
[[ -n "$cli_home_dir" ]] || cli_home_dir="$STACKS_DIR/$s/cli"
|
|
150
|
+
|
|
151
|
+
base_dir="$STACKS_DIR/$s"
|
|
152
|
+
label="com.happy.stacks.$s"
|
|
153
|
+
|
|
154
|
+
COLLECT="$(collect_stack_status "$port" "$cli_home_dir" "$label" "$base_dir")"
|
|
155
|
+
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"
|
|
156
|
+
for v in SERVER_PID SERVER_METRICS DAEMON_PID DAEMON_METRICS DAEMON_UPTIME LAST_HEARTBEAT AUTOSTART_PID AUTOSTART_METRICS; do
|
|
157
|
+
if [[ "${!v}" == "-" ]]; then
|
|
158
|
+
printf -v "$v" '%s' ""
|
|
159
|
+
fi
|
|
160
|
+
done
|
|
161
|
+
|
|
162
|
+
render_stack_overview_item "Stack: $s" "$LEVEL" ""
|
|
163
|
+
export STACK_LEVEL="$LEVEL"
|
|
164
|
+
render_stack_info "--" "$s" "$port" "$server_component" "$base_dir" "$cli_home_dir" "$label" "$env_file" ""
|
|
165
|
+
render_component_server "--" "$s" "$port" "$server_component" "$SERVER_STATUS" "$SERVER_PID" "$SERVER_METRICS" "" "$label"
|
|
166
|
+
render_component_daemon "--" "$DAEMON_STATUS" "$DAEMON_PID" "$DAEMON_METRICS" "$DAEMON_UPTIME" "$LAST_HEARTBEAT" "$cli_home_dir/daemon.state.json" "$s"
|
|
167
|
+
render_component_autostart "--" "$s" "$label" "$LAUNCHAGENT_STATUS" "$AUTOSTART_PID" "$AUTOSTART_METRICS" "$base_dir/logs"
|
|
168
|
+
render_component_tailscale "--" "$s" ""
|
|
169
|
+
render_components_menu "--" "stack" "$s" "$env_file"
|
|
170
|
+
done
|
|
171
|
+
else
|
|
172
|
+
echo "No stacks dir found at ~/.happy/stacks | color=$GRAY"
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
echo "---"
|
|
176
|
+
render_components_menu "" "main" "main" ""
|
|
177
|
+
|
|
178
|
+
echo "Worktrees | sfimage=arrow.triangle.branch"
|
|
179
|
+
if [[ -z "$PNPM_BIN" ]]; then
|
|
180
|
+
echo "--⚠️ happys not found (run: npx happy-stacks init, or install happy-stacks globally)"
|
|
181
|
+
else
|
|
182
|
+
PNPM_TERM="$HAPPY_LOCAL_DIR/extras/swiftbar/pnpm-term.sh"
|
|
183
|
+
echo "--Use (interactive) | bash=$PNPM_TERM param1=wt param2=use param3=--interactive dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
184
|
+
echo "--New (interactive) | bash=$PNPM_TERM param1=wt param2=new param3=--interactive dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
185
|
+
echo "--PR worktree (prompt) | bash=$HAPPY_LOCAL_DIR/extras/swiftbar/wt-pr.sh dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
186
|
+
echo "--Sync mirrors (all) | bash=$PNPM_BIN param1=wt param2=sync-all dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
187
|
+
echo "--Update all (dry-run) | bash=$PNPM_TERM param1=wt param2=update-all param3=--dry-run dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
188
|
+
echo "--Update all (apply) | bash=$PNPM_BIN param1=wt param2=update-all dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
echo "---"
|
|
192
|
+
echo "Setup / Tools"
|
|
193
|
+
if [[ -z "$PNPM_BIN" ]]; then
|
|
194
|
+
echo "--⚠️ happys not found (run: npx happy-stacks init, or install happy-stacks globally)"
|
|
195
|
+
else
|
|
196
|
+
PNPM_TERM="$HAPPY_LOCAL_DIR/extras/swiftbar/pnpm-term.sh"
|
|
197
|
+
echo "--Bootstrap (clone/install) | bash=$PNPM_TERM param1=bootstrap dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
198
|
+
echo "--CLI link (install happy wrapper) | bash=$PNPM_TERM param1=cli:link dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
199
|
+
echo "--Mobile dev helper | bash=$PNPM_TERM param1=mobile dir=$HAPPY_LOCAL_DIR terminal=false"
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
echo "---"
|
|
203
|
+
echo "Refresh | sfimage=arrow.clockwise refresh=true"
|
|
204
|
+
echo "---"
|
|
205
|
+
echo "Refresh interval | sfimage=timer"
|
|
206
|
+
SET_INTERVAL="$HAPPY_LOCAL_DIR/extras/swiftbar/set-interval.sh"
|
|
207
|
+
echo "--10s | bash=$SET_INTERVAL param1=10s dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
208
|
+
echo "--30s | bash=$SET_INTERVAL param1=30s dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
209
|
+
echo "--1m | bash=$SET_INTERVAL param1=1m dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
210
|
+
echo "--5m (recommended) | bash=$SET_INTERVAL param1=5m dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
211
|
+
echo "--10m | bash=$SET_INTERVAL param1=10m dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
212
|
+
echo "--15m | bash=$SET_INTERVAL param1=15m dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
213
|
+
echo "--30m | bash=$SET_INTERVAL param1=30m dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
214
|
+
echo "--1h | bash=$SET_INTERVAL param1=1h dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
215
|
+
echo "--2h | bash=$SET_INTERVAL param1=2h dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
216
|
+
echo "--6h | bash=$SET_INTERVAL param1=6h dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
217
|
+
echo "--12h | bash=$SET_INTERVAL param1=12h dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"
|
|
218
|
+
echo "--1d | bash=$SET_INTERVAL param1=1d dir=$HAPPY_LOCAL_DIR terminal=false refresh=true"exit 0
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|