laracrew 0.2.0 → 0.2.2
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/CHANGELOG.md +207 -173
- package/README.md +11 -5
- package/dist/index.js +54 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,173 +1,207 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project are documented here.
|
|
4
|
-
|
|
5
|
-
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
6
|
-
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). While the version is
|
|
7
|
-
below `1.0.0`, minor versions may contain breaking config changes; those are always listed
|
|
8
|
-
under **Changed** with a migration note.
|
|
9
|
-
|
|
10
|
-
## [Unreleased]
|
|
11
|
-
|
|
12
|
-
Planned, in order — see `.claude/PLAN.md`:
|
|
13
|
-
|
|
14
|
-
- `laracrew scan` to discover Laravel projects and scaffold a stack
|
|
15
|
-
- Live queue depth and Redis stream consumer lag in the process tree
|
|
16
|
-
- File watching with graceful `queue:restart`
|
|
17
|
-
- `laracrew run <task>` for one-shot cross-project sequences
|
|
18
|
-
- Background daemon: `up --detach`, `attach`, `status`, `logs -f`
|
|
19
|
-
|
|
20
|
-
## [0.2.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
###
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
**
|
|
104
|
-
|
|
105
|
-
-
|
|
106
|
-
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
- `
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
- `
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
-
|
|
165
|
-
|
|
166
|
-
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
6
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). While the version is
|
|
7
|
+
below `1.0.0`, minor versions may contain breaking config changes; those are always listed
|
|
8
|
+
under **Changed** with a migration note.
|
|
9
|
+
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
Planned, in order — see `.claude/PLAN.md`:
|
|
13
|
+
|
|
14
|
+
- `laracrew scan` to discover Laravel projects and scaffold a stack
|
|
15
|
+
- Live queue depth and Redis stream consumer lag in the process tree
|
|
16
|
+
- File watching with graceful `queue:restart`
|
|
17
|
+
- `laracrew run <task>` for one-shot cross-project sequences
|
|
18
|
+
- Background daemon: `up --detach`, `attach`, `status`, `logs -f`
|
|
19
|
+
|
|
20
|
+
## [0.2.2] — 2026-09-30
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **`R` restarts every running process** with one keypress, in dependency order, so a
|
|
25
|
+
dependency is back and ready before the services that need it are bounced. Only what is
|
|
26
|
+
`running` is touched: a service you stopped, one defined with `autostart: false`, one that
|
|
27
|
+
exited on its own and one that gave up all stay exactly as they are. Plain and JSON runs see
|
|
28
|
+
the usual stop and start events plus a notice naming the services.
|
|
29
|
+
|
|
30
|
+
### Notes
|
|
31
|
+
|
|
32
|
+
- Releases are now built and published by GitHub Actions. `test.yml` runs the suite and the
|
|
33
|
+
packed-tarball smoke test on Windows and Ubuntu for every push and pull request; `publish.yml`
|
|
34
|
+
is a manual release through npm trusted publishing, with a dry-run rehearsal as the default.
|
|
35
|
+
- Three tests left a log file handle open, which made the suite's teardown fail on Windows with
|
|
36
|
+
Node 20. Fixed; 248 tests.
|
|
37
|
+
|
|
38
|
+
## [0.2.1] — 2026-09-20
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- **A stack builder**, [`docs/stack-builder.html`](docs/stack-builder.html) — a single self-contained
|
|
43
|
+
page that writes a `stack.yaml` from projects and process commands as you fill them in. It
|
|
44
|
+
encodes the rules the config resolver enforces, so it catches the mistakes before laracrew does:
|
|
45
|
+
a service with no command, `stop.artisan` on a service with no project, a `needs:` pointing at
|
|
46
|
+
nothing, an auto-started service depending on one you have to start by hand, a dependency cycle.
|
|
47
|
+
It also shows the boot order the `needs:` graph produces, level by level.
|
|
48
|
+
|
|
49
|
+
Open it from disk or serve `docs/` with GitHub Pages. It is not part of the npm package — no
|
|
50
|
+
runtime dependencies, no build step, nothing to install.
|
|
51
|
+
|
|
52
|
+
## [0.2.0] — 2026-09-20
|
|
53
|
+
|
|
54
|
+
Graceful shutdown for any process, not only Laravel queue workers, and a `doctor` that only
|
|
55
|
+
applies its Laravel checks to Laravel.
|
|
56
|
+
|
|
57
|
+
laracrew has always been able to *run* anything — the supervisor takes a command and knows
|
|
58
|
+
nothing about the language behind it. But the two things that made it more than a process
|
|
59
|
+
runner, the graceful stop and the pre-flight checks, both assumed PHP. This release closes that
|
|
60
|
+
gap. Existing stack files keep working unchanged.
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- **`stop.exec`** — any command as the first step of the stop ladder. It runs in the service's
|
|
65
|
+
`cwd`, with the service's environment, and laracrew waits `graceMs` for the process to exit by
|
|
66
|
+
itself before escalating to the signal and the tree kill. A Celery worker, a BullMQ consumer or
|
|
67
|
+
a Compose project now shuts down as carefully as a queue worker:
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
stop: { exec: ["celery", "-A", "app", "control", "shutdown"], graceMs: 25000 }
|
|
71
|
+
stop: { exec: "npm run drain", graceMs: 10000 }
|
|
72
|
+
stop: { exec: ["docker", "compose", "stop"], graceMs: 30000 }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- **`doctor` checks external dependencies.** Every `external: true` service is probed through the
|
|
76
|
+
`tcp` or `http` gate it already declares, so a stack's Postgres, RabbitMQ or HTTP dependency is
|
|
77
|
+
verified before boot — whatever the stack is written in.
|
|
78
|
+
- `doctor` reads `REDIS_URL` when a project sets it instead of `REDIS_HOST` / `REDIS_PORT`.
|
|
79
|
+
|
|
80
|
+
### Changed
|
|
81
|
+
|
|
82
|
+
- **`stop.artisan` is now shorthand for `stop.exec`.** It resolves to `<project php> artisan
|
|
83
|
+
<command>`, run from the project root even when the service sets its own `cwd`. Behaviour for
|
|
84
|
+
existing Laravel stacks is unchanged; a service may declare one or the other, not both. A
|
|
85
|
+
service that declares its own graceful step now replaces an inherited one in either direction,
|
|
86
|
+
so a stack can default to `artisan: queue:restart` and still give one service its own `exec`.
|
|
87
|
+
- **`doctor` applies each check only where it means something.** It works out which projects run
|
|
88
|
+
PHP (from their commands and from `stop.artisan`) and which talk to Redis (from their `.env`
|
|
89
|
+
and their commands). A stack with no PHP in it gets no PHP findings; a stack with no Redis gets
|
|
90
|
+
no Redis findings. Laravel stacks see exactly what they saw before.
|
|
91
|
+
- A Redis address already covered by an `external: true` service's gate is no longer probed a
|
|
92
|
+
second time from the project's `.env`.
|
|
93
|
+
- `laracrew --help` and the generated `projects.yaml` no longer describe projects as Laravel-only.
|
|
94
|
+
`php:` is documented as what it is: the binary the `stop.artisan` shorthand runs.
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
|
|
98
|
+
- **`laracrew --version` reported `0.1.0` on every release.** The version was a hardcoded
|
|
99
|
+
constant that nobody bumped, so `0.1.1` and `0.1.2` both identified themselves as `0.1.0`. It is
|
|
100
|
+
now baked in from `package.json` at build time, with a test that fails if the two ever disagree.
|
|
101
|
+
- **`stop.artisan` on a service with no project was silently ignored** — the graceful step was
|
|
102
|
+
skipped and nothing said so. It is now a config error that names `stop.exec` as the way out.
|
|
103
|
+
- **`doctor` failed on a machine without PHP** for a stack that contains no PHP: `php --version`
|
|
104
|
+
ran for every declared project and a missing binary was a blocking `✖` with exit code 1.
|
|
105
|
+
- **False Redis collision between projects that never touch Redis.** Two projects with no `.env`
|
|
106
|
+
both resolved to the same synthesized default namespace and were reported as sharing it.
|
|
107
|
+
- The "no `artisan` file" and "`.env` is missing or empty" warnings no longer fire for projects
|
|
108
|
+
that are not PHP projects.
|
|
109
|
+
|
|
110
|
+
### Notes
|
|
111
|
+
|
|
112
|
+
- 244 tests. The graceful stop step had no coverage before this release; it now has unit tests
|
|
113
|
+
for both outcomes (the process exits by itself, and the ladder escalating when it does not),
|
|
114
|
+
plus tests for every new config error.
|
|
115
|
+
- The npm description and `laracrew --help` now say "your projects" rather than "your Laravel
|
|
116
|
+
projects". The README still leads with the Laravel story, which is what the tool was built for.
|
|
117
|
+
|
|
118
|
+
## [0.1.2] — 2026-09-20
|
|
119
|
+
|
|
120
|
+
### Added
|
|
121
|
+
|
|
122
|
+
- `repository`, `homepage` and `bugs` metadata, so the npm page links back to the source.
|
|
123
|
+
|
|
124
|
+
## [0.1.1] — 2026-09-20
|
|
125
|
+
|
|
126
|
+
First release published to npm. No code changes from `0.1.0`.
|
|
127
|
+
|
|
128
|
+
## [0.1.0] — 2026-09-19
|
|
129
|
+
|
|
130
|
+
First release. Boots and supervises the long-running processes of several Laravel projects at
|
|
131
|
+
once, from one command.
|
|
132
|
+
|
|
133
|
+
### Added
|
|
134
|
+
|
|
135
|
+
**Supervision**
|
|
136
|
+
|
|
137
|
+
- Dependency-ordered boot (`needs:`), with independent services started in parallel.
|
|
138
|
+
- Readiness gates before a dependent service starts: `tcp`, `http`, `logMatch`, `delayMs`.
|
|
139
|
+
- Restart policies (`never`, `on-failure`, `always`) with exponential backoff, a restart
|
|
140
|
+
ceiling, and an attempt counter that resets after a service has been stable.
|
|
141
|
+
- A Laravel-aware stop ladder: `php artisan queue:restart` (or `horizon:terminate`) first, wait
|
|
142
|
+
for the worker to finish the job it is holding, then terminate — and always as a process
|
|
143
|
+
tree, so `artisan serve`'s child PHP server and `npm run dev`'s Vite are never orphaned.
|
|
144
|
+
- Rollback on a failed boot: a readiness gate that never opens stops everything that had
|
|
145
|
+
already started, instead of leaving a half-booted stack behind.
|
|
146
|
+
- `external: true` for services laracrew health-checks but never starts, such as Redis.
|
|
147
|
+
- `autostart: false` for services defined but not launched, started later on demand.
|
|
148
|
+
|
|
149
|
+
**Interface**
|
|
150
|
+
|
|
151
|
+
- A full-screen process tree as the default view, which renders no log output at all; logs are
|
|
152
|
+
opened one process at a time. Built on plain ANSI — no TUI framework.
|
|
153
|
+
- Per-process log inspection, a merged log across all services, restart/stop/start, and help.
|
|
154
|
+
- `external: true` dependencies are listed above the tree under their own heading rather than
|
|
155
|
+
numbered among the managed processes, so a digit key always selects something startable.
|
|
156
|
+
- `--plain` prefixed interleaved logs, selected automatically when stdout is not a TTY.
|
|
157
|
+
- `--json` newline-delimited events for scripting.
|
|
158
|
+
- A broken pipe (`laracrew up --json | head`) shuts the stack down cleanly instead of crashing.
|
|
159
|
+
|
|
160
|
+
**Logs**
|
|
161
|
+
|
|
162
|
+
- Every line is written to `~/.laracrew/logs/<stack>/<service>.log`, on by default, rotated by
|
|
163
|
+
size. Plain text with a sortable local timestamp and no colour codes, so `grep` works.
|
|
164
|
+
- `laracrew logs [service]` reads them back after the fact, with `-n`, `-f`, `--since 10m`,
|
|
165
|
+
`--all` to merge every service in time order, and `--list`.
|
|
166
|
+
- Writes go through a held file descriptor rather than a stream, so a line reaches disk before
|
|
167
|
+
a crashing process can lose it, and rotation can close the handle deterministically — a
|
|
168
|
+
rename with an open handle fails outright on Windows.
|
|
169
|
+
|
|
170
|
+
**Configuration**
|
|
171
|
+
|
|
172
|
+
- `~/.laracrew/` home: `config.yaml`, `projects.yaml`, one folder per stack, tasks, fragments.
|
|
173
|
+
- YAML stacks validated against a schema, with errors that name the file, the field, and a
|
|
174
|
+
spelling suggestion.
|
|
175
|
+
- Interpolation: `${env:VAR}`, `${env:VAR:fallback}`, `${project.path}`, `${project.env:VAR}`,
|
|
176
|
+
`${stack.dir}`, `${port:N}`.
|
|
177
|
+
- Groups, profiles, `--only` / `--except` filtering, and `services/*.yaml` fragments.
|
|
178
|
+
|
|
179
|
+
**Commands**
|
|
180
|
+
|
|
181
|
+
- `laracrew init [--examples]`, `ls`, `up`, `doctor`, `link`, `unlink`.
|
|
182
|
+
- `laracrew link <stack>` installs a global command that boots one stack, so a project set is
|
|
183
|
+
one word to launch. Generated shims are marked, and laracrew refuses to overwrite files it
|
|
184
|
+
did not write or to shadow names like `npm` and `git`.
|
|
185
|
+
|
|
186
|
+
**Checks (`laracrew doctor`)**
|
|
187
|
+
|
|
188
|
+
- Two projects sharing a Redis database *and* a queue or stream name, computing the effective
|
|
189
|
+
prefix the way Laravel does (`REDIS_PREFIX`, else a slug of `APP_NAME`) so it does not report
|
|
190
|
+
a collision that isn't one.
|
|
191
|
+
- `QUEUE_CONNECTION=sync` in a project that runs a queue worker.
|
|
192
|
+
- Port clashes, missing project paths, an unreachable Redis, and a PHP binary that will not run.
|
|
193
|
+
|
|
194
|
+
### Notes
|
|
195
|
+
|
|
196
|
+
- Requires Node 20 or newer. Windows, macOS and Linux.
|
|
197
|
+
- Runtime dependencies: `commander`, `yaml`, `zod`.
|
|
198
|
+
- Accepted by the schema but not yet acted on: `watch`, `metrics` (read by `doctor` only), and
|
|
199
|
+
`hooks`. Stack files written today stay valid.
|
|
200
|
+
|
|
201
|
+
[Unreleased]: https://github.com/vidux/laracrew/compare/v0.2.2...HEAD
|
|
202
|
+
[0.2.2]: https://github.com/vidux/laracrew/compare/v0.2.1...v0.2.2
|
|
203
|
+
[0.2.1]: https://github.com/vidux/laracrew/compare/v0.2.0...v0.2.1
|
|
204
|
+
[0.2.0]: https://github.com/vidux/laracrew/compare/v0.1.2...v0.2.0
|
|
205
|
+
[0.1.2]: https://github.com/vidux/laracrew/compare/v0.1.1...v0.1.2
|
|
206
|
+
[0.1.1]: https://github.com/vidux/laracrew/releases/tag/v0.1.1
|
|
207
|
+
[0.1.0]: https://github.com/vidux/laracrew/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -155,6 +155,11 @@ laracrew doctor dual
|
|
|
155
155
|
laracrew up dual
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
**Prefer not to write YAML by hand?** Open [`docs/stack-builder.html`](docs/stack-builder.html) in
|
|
159
|
+
a browser. Add your projects and the commands that run under them, and it writes the `stack.yaml`
|
|
160
|
+
as you go — checking the same rules laracrew does, so a stack that looks right there boots. No
|
|
161
|
+
install, no build step; it is one file.
|
|
162
|
+
|
|
158
163
|
---
|
|
159
164
|
|
|
160
165
|
## One command per project set
|
|
@@ -305,6 +310,7 @@ the stack with a message naming both services rather than hanging.
|
|
|
305
310
|
| `esc` | Back to the tree |
|
|
306
311
|
| `a` | Merged log across every service - the firehose, on demand |
|
|
307
312
|
| `r` | Restart the selected process (graceful: `queue:restart` first) |
|
|
313
|
+
| `R` | Restart every running process, in dependency order. Stopped, idle and failed ones stay as they are |
|
|
308
314
|
| `s` | Stop it, or start it again if stopped |
|
|
309
315
|
| `f` / `g` / `G` | Follow-pause tailing; jump to top or bottom |
|
|
310
316
|
| `?` | Help |
|
|
@@ -575,13 +581,13 @@ project, `LARACREW_PROJECT=<key>`.
|
|
|
575
581
|
|
|
576
582
|
## Status
|
|
577
583
|
|
|
578
|
-
**v0.2.
|
|
584
|
+
**v0.2.2 — supervisor and full-screen view are built and tested.**
|
|
579
585
|
|
|
580
586
|
Working now: config pipeline, dependency-ordered boot with readiness gates, restart policies with
|
|
581
587
|
exponential backoff, the graceful stop ladder — `stop.exec` for any process, `stop.artisan` as the
|
|
582
|
-
Laravel shorthand — the full-screen process tree with per-process log inspection
|
|
583
|
-
disk with `laracrew logs` to read them back,
|
|
584
|
-
(`link` / `unlink`), `doctor`, `init`, `ls`.
|
|
588
|
+
Laravel shorthand — the full-screen process tree with per-process log inspection and a one-key
|
|
589
|
+
restart of everything running, logs persisted to disk with `laracrew logs` to read them back,
|
|
590
|
+
plain and JSON renderers, per-stack global commands (`link` / `unlink`), `doctor`, `init`, `ls`.
|
|
585
591
|
|
|
586
592
|
See [CHANGELOG.md](CHANGELOG.md) for what changed in each release.
|
|
587
593
|
|
|
@@ -615,7 +621,7 @@ The full plan lives in [.claude/PLAN.md](.claude/PLAN.md), with the design in
|
|
|
615
621
|
npm install
|
|
616
622
|
npm run dev -- up example # tsx, no build step
|
|
617
623
|
npm run build # tsup -> dist/index.js
|
|
618
|
-
npm test # vitest,
|
|
624
|
+
npm test # vitest, 248 tests
|
|
619
625
|
npm run typecheck
|
|
620
626
|
npm link # put `laracrew` on PATH while hacking on it
|
|
621
627
|
```
|
package/dist/index.js
CHANGED
|
@@ -1612,6 +1612,45 @@ var Supervisor = class {
|
|
|
1612
1612
|
const process2 = this.#requireProcess(name);
|
|
1613
1613
|
await process2.restart();
|
|
1614
1614
|
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Restarts every service that is `running` right now and nothing else: a stopped or idle
|
|
1617
|
+
* service stays where it is, a failed one is not resurrected, one still starting is left to
|
|
1618
|
+
* finish. Dependency order, parallel within a level, so a dependency is back and ready
|
|
1619
|
+
* before the services that need it are bounced. Returns the names restarted, in that order.
|
|
1620
|
+
*/
|
|
1621
|
+
async restartRunning() {
|
|
1622
|
+
const isTarget = (process2) => !process2.service.external && process2.state === "running";
|
|
1623
|
+
const wanted = new Set(this.processes.filter(isTarget).map((process2) => process2.service.name));
|
|
1624
|
+
if (wanted.size === 0) return [];
|
|
1625
|
+
this.#bus.emit({
|
|
1626
|
+
type: "notice",
|
|
1627
|
+
level: "info",
|
|
1628
|
+
message: `restarting ${wanted.size} running service${wanted.size === 1 ? "" : "s"}: ${[...wanted].join(", ")}`,
|
|
1629
|
+
at: Date.now()
|
|
1630
|
+
});
|
|
1631
|
+
const restarted = /* @__PURE__ */ new Set();
|
|
1632
|
+
for (const level of this.stack.levels) {
|
|
1633
|
+
await Promise.all(
|
|
1634
|
+
level.filter((name) => wanted.has(name)).map(async (name) => {
|
|
1635
|
+
const process2 = this.#processes.get(name);
|
|
1636
|
+
if (!isTarget(process2)) return;
|
|
1637
|
+
try {
|
|
1638
|
+
await process2.restart();
|
|
1639
|
+
restarted.add(name);
|
|
1640
|
+
} catch (error) {
|
|
1641
|
+
this.#bus.emit({
|
|
1642
|
+
type: "notice",
|
|
1643
|
+
level: "error",
|
|
1644
|
+
service: name,
|
|
1645
|
+
message: `restart failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1646
|
+
at: Date.now()
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
})
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
return this.stack.levels.flat().filter((name) => restarted.has(name));
|
|
1653
|
+
}
|
|
1615
1654
|
async stopService(name) {
|
|
1616
1655
|
await this.#requireProcess(name).stop();
|
|
1617
1656
|
}
|
|
@@ -1834,6 +1873,7 @@ var handleKey = (ui, key, rowCount) => {
|
|
|
1834
1873
|
case "t":
|
|
1835
1874
|
return { ui, command: { type: "toggle", index: ui.selected } };
|
|
1836
1875
|
case "r":
|
|
1876
|
+
if (key.shift) return { ui, command: { type: "restart-running" } };
|
|
1837
1877
|
return { ui, command: { type: "restart", index: ui.selected } };
|
|
1838
1878
|
case "a":
|
|
1839
1879
|
return none({ ...ui, view: ui.view === "merged" ? "overview" : "merged", scrollBack: 0, follow: true });
|
|
@@ -1997,6 +2037,7 @@ var KEYS = {
|
|
|
1997
2037
|
["enter", "inspect log"],
|
|
1998
2038
|
["0-9", "jump"],
|
|
1999
2039
|
["r", "restart"],
|
|
2040
|
+
["R", "restart all running"],
|
|
2000
2041
|
["s", "stop/start"],
|
|
2001
2042
|
["a", "all logs"],
|
|
2002
2043
|
["?", "help"],
|
|
@@ -2008,6 +2049,7 @@ var KEYS = {
|
|
|
2008
2049
|
["g/G", "top/bottom"],
|
|
2009
2050
|
["f", "follow"],
|
|
2010
2051
|
["r", "restart"],
|
|
2052
|
+
["R", "restart all running"],
|
|
2011
2053
|
["q", "quit"]
|
|
2012
2054
|
],
|
|
2013
2055
|
merged: [
|
|
@@ -2015,6 +2057,7 @@ var KEYS = {
|
|
|
2015
2057
|
["up/down", "scroll"],
|
|
2016
2058
|
["g/G", "top/bottom"],
|
|
2017
2059
|
["f", "follow"],
|
|
2060
|
+
["R", "restart all running"],
|
|
2018
2061
|
["q", "quit"]
|
|
2019
2062
|
],
|
|
2020
2063
|
help: [["any key", "back"]]
|
|
@@ -2141,6 +2184,7 @@ var help = ({ paint: paint2, glyph }) => [
|
|
|
2141
2184
|
["esc, s", "back to the stack overview"],
|
|
2142
2185
|
["a", "merged log across every service"],
|
|
2143
2186
|
["r", "restart the selected process (graceful)"],
|
|
2187
|
+
["R", "restart every running process, in dependency order; stopped and idle stay put"],
|
|
2144
2188
|
["t", "toggle the selected process: stop if running, start if stopped"],
|
|
2145
2189
|
["f", "follow / pause tailing in a log view"],
|
|
2146
2190
|
["g / G", "jump to the top / bottom of a log"],
|
|
@@ -2370,7 +2414,7 @@ var runTui = async (supervisor, bus, options = {}) => {
|
|
|
2370
2414
|
};
|
|
2371
2415
|
const perform = (command) => {
|
|
2372
2416
|
const stack = buildStackView(supervisor);
|
|
2373
|
-
const row = stack.rows[
|
|
2417
|
+
const row = stack.rows["index" in command ? command.index : ui.selected];
|
|
2374
2418
|
switch (command.type) {
|
|
2375
2419
|
case "quit":
|
|
2376
2420
|
void shutdown("user quit");
|
|
@@ -2384,6 +2428,14 @@ var runTui = async (supervisor, bus, options = {}) => {
|
|
|
2384
2428
|
setNote(`restarting ${row.name}\u2026`);
|
|
2385
2429
|
void supervisor.restart(row.name).then(() => setNote(`${row.name} restarted`)).catch((error) => setNote(`restart failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
2386
2430
|
break;
|
|
2431
|
+
case "restart-running":
|
|
2432
|
+
setNote("restarting every running process\u2026");
|
|
2433
|
+
void supervisor.restartRunning().then(
|
|
2434
|
+
(names) => setNote(
|
|
2435
|
+
names.length === 0 ? "nothing is running \u2014 nothing to restart" : `restarted ${names.length} process${names.length === 1 ? "" : "es"}: ${names.join(", ")}`
|
|
2436
|
+
)
|
|
2437
|
+
).catch((error) => setNote(`restart failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
2438
|
+
break;
|
|
2387
2439
|
case "toggle": {
|
|
2388
2440
|
if (!row) break;
|
|
2389
2441
|
if (row.external) {
|
|
@@ -3555,7 +3607,7 @@ ${paint("bold", "TASKS")}`);
|
|
|
3555
3607
|
};
|
|
3556
3608
|
|
|
3557
3609
|
// src/cli/program.ts
|
|
3558
|
-
var VERSION = true ? "0.2.
|
|
3610
|
+
var VERSION = true ? "0.2.2" : versionFromPackageJson();
|
|
3559
3611
|
var resolveStackName = (explicit, env = process.env) => {
|
|
3560
3612
|
if (explicit) return explicit;
|
|
3561
3613
|
const configured = loadGlobalConfig(env).defaultStack;
|