dev-cockpit 0.1.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/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/bin/dev-cockpit.mjs +20 -0
- package/dist/buildCli.d.ts +17 -0
- package/dist/buildCli.d.ts.map +1 -0
- package/dist/buildCli.js +107 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2 -0
- package/dist/cockpit/Cockpit.d.ts +33 -0
- package/dist/cockpit/Cockpit.d.ts.map +1 -0
- package/dist/cockpit/Cockpit.js +73 -0
- package/dist/cockpit/Footer.d.ts +22 -0
- package/dist/cockpit/Footer.d.ts.map +1 -0
- package/dist/cockpit/Footer.js +33 -0
- package/dist/cockpit/TabBar.d.ts +3 -0
- package/dist/cockpit/TabBar.d.ts.map +1 -0
- package/dist/cockpit/TabBar.js +12 -0
- package/dist/cockpit/help/content.d.ts +12 -0
- package/dist/cockpit/help/content.d.ts.map +1 -0
- package/dist/cockpit/help/content.js +22 -0
- package/dist/cockpit/help/loader.d.ts +65 -0
- package/dist/cockpit/help/loader.d.ts.map +1 -0
- package/dist/cockpit/help/loader.js +118 -0
- package/dist/cockpit/help/renderer.d.ts +16 -0
- package/dist/cockpit/help/renderer.d.ts.map +1 -0
- package/dist/cockpit/help/renderer.js +35 -0
- package/dist/cockpit/help/types.d.ts +12 -0
- package/dist/cockpit/help/types.d.ts.map +1 -0
- package/dist/cockpit/help/types.js +1 -0
- package/dist/cockpit/hooks/useCockpitStore.d.ts +3 -0
- package/dist/cockpit/hooks/useCockpitStore.d.ts.map +1 -0
- package/dist/cockpit/hooks/useCockpitStore.js +5 -0
- package/dist/cockpit/hooks/useGlobalKeys.d.ts +56 -0
- package/dist/cockpit/hooks/useGlobalKeys.d.ts.map +1 -0
- package/dist/cockpit/hooks/useGlobalKeys.js +173 -0
- package/dist/cockpit/panes/FilterModal.d.ts +3 -0
- package/dist/cockpit/panes/FilterModal.d.ts.map +1 -0
- package/dist/cockpit/panes/FilterModal.js +22 -0
- package/dist/cockpit/panes/Health.d.ts +13 -0
- package/dist/cockpit/panes/Health.d.ts.map +1 -0
- package/dist/cockpit/panes/Health.js +30 -0
- package/dist/cockpit/panes/Help.d.ts +14 -0
- package/dist/cockpit/panes/Help.d.ts.map +1 -0
- package/dist/cockpit/panes/Help.js +81 -0
- package/dist/cockpit/panes/Output.d.ts +14 -0
- package/dist/cockpit/panes/Output.d.ts.map +1 -0
- package/dist/cockpit/panes/Output.js +108 -0
- package/dist/cockpit/panes/Repos.d.ts +3 -0
- package/dist/cockpit/panes/Repos.d.ts.map +1 -0
- package/dist/cockpit/panes/Repos.js +48 -0
- package/dist/cockpit/panes/SearchModal.d.ts +3 -0
- package/dist/cockpit/panes/SearchModal.d.ts.map +1 -0
- package/dist/cockpit/panes/SearchModal.js +31 -0
- package/dist/cockpit/state/store.d.ts +93 -0
- package/dist/cockpit/state/store.d.ts.map +1 -0
- package/dist/cockpit/state/store.js +111 -0
- package/dist/cockpit/tab-state.d.ts +4 -0
- package/dist/cockpit/tab-state.d.ts.map +1 -0
- package/dist/cockpit/tab-state.js +7 -0
- package/dist/commands/dev.d.ts +20 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +158 -0
- package/dist/commands/doctor.d.ts +20 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +66 -0
- package/dist/commands/init-config-wizard.d.ts +84 -0
- package/dist/commands/init-config-wizard.d.ts.map +1 -0
- package/dist/commands/init-config-wizard.js +818 -0
- package/dist/commands/init-config.d.ts +35 -0
- package/dist/commands/init-config.d.ts.map +1 -0
- package/dist/commands/init-config.js +131 -0
- package/dist/commands/mount.d.ts +48 -0
- package/dist/commands/mount.d.ts.map +1 -0
- package/dist/commands/mount.js +150 -0
- package/dist/core/config.d.ts +391 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +152 -0
- package/dist/core/logger.d.ts +6 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +38 -0
- package/dist/core/notifier.d.ts +23 -0
- package/dist/core/notifier.d.ts.map +1 -0
- package/dist/core/notifier.js +100 -0
- package/dist/core/paths.d.ts +15 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +18 -0
- package/dist/core/subprocess.d.ts +20 -0
- package/dist/core/subprocess.d.ts.map +1 -0
- package/dist/core/subprocess.js +82 -0
- package/dist/core/types.d.ts +125 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +1 -0
- package/dist/docker/highlights.d.ts +48 -0
- package/dist/docker/highlights.d.ts.map +1 -0
- package/dist/docker/highlights.js +79 -0
- package/dist/docker/logs.d.ts +84 -0
- package/dist/docker/logs.d.ts.map +1 -0
- package/dist/docker/logs.js +172 -0
- package/dist/docker/restart.d.ts +26 -0
- package/dist/docker/restart.d.ts.map +1 -0
- package/dist/docker/restart.js +45 -0
- package/dist/docker/stack-trace.d.ts +25 -0
- package/dist/docker/stack-trace.d.ts.map +1 -0
- package/dist/docker/stack-trace.js +44 -0
- package/dist/health/builtin.d.ts +8 -0
- package/dist/health/builtin.d.ts.map +1 -0
- package/dist/health/builtin.js +144 -0
- package/dist/health/context.d.ts +3 -0
- package/dist/health/context.d.ts.map +1 -0
- package/dist/health/context.js +31 -0
- package/dist/health/notify-resolver.d.ts +18 -0
- package/dist/health/notify-resolver.d.ts.map +1 -0
- package/dist/health/notify-resolver.js +28 -0
- package/dist/health/registry.d.ts +20 -0
- package/dist/health/registry.d.ts.map +1 -0
- package/dist/health/registry.js +64 -0
- package/dist/health/remediations.d.ts +6 -0
- package/dist/health/remediations.d.ts.map +1 -0
- package/dist/health/remediations.js +41 -0
- package/dist/health/runner.d.ts +4 -0
- package/dist/health/runner.d.ts.map +1 -0
- package/dist/health/runner.js +22 -0
- package/dist/health/scheduler.d.ts +41 -0
- package/dist/health/scheduler.d.ts.map +1 -0
- package/dist/health/scheduler.js +107 -0
- package/dist/health/types.d.ts +73 -0
- package/dist/health/types.d.ts.map +1 -0
- package/dist/health/types.js +1 -0
- package/dist/health/useHealth.d.ts +40 -0
- package/dist/health/useHealth.d.ts.map +1 -0
- package/dist/health/useHealth.js +122 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/ink.d.ts +3 -0
- package/dist/ink.d.ts.map +1 -0
- package/dist/ink.js +1 -0
- package/dist/lint/reactive.d.ts +38 -0
- package/dist/lint/reactive.d.ts.map +1 -0
- package/dist/lint/reactive.js +131 -0
- package/dist/react.d.ts +3 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +1 -0
- package/dist/runCockpit.d.ts +34 -0
- package/dist/runCockpit.d.ts.map +1 -0
- package/dist/runCockpit.js +75 -0
- package/dist/watchers/manager.d.ts +63 -0
- package/dist/watchers/manager.d.ts.map +1 -0
- package/dist/watchers/manager.js +239 -0
- package/dist/watchers/path-mapper.d.ts +23 -0
- package/dist/watchers/path-mapper.d.ts.map +1 -0
- package/dist/watchers/path-mapper.js +29 -0
- package/dist/watchers/types.d.ts +22 -0
- package/dist/watchers/types.d.ts.map +1 -0
- package/dist/watchers/types.js +9 -0
- package/docs/commands.md +71 -0
- package/docs/config-reference.md +20 -0
- package/docs/getting-started.md +39 -0
- package/docs/health.md +120 -0
- package/docs/index.md +13 -0
- package/docs/init-config.md +46 -0
- package/docs/mount.md +55 -0
- package/docs/notifications.md +39 -0
- package/docs/panes.md +45 -0
- package/docs/watchers.md +27 -0
- package/examples/cockpit.yaml +116 -0
- package/package.json +91 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watcher subsystem types.
|
|
3
|
+
*
|
|
4
|
+
* The watcher manager works on a *normalized* repo config — a per-repo
|
|
5
|
+
* declaration that says where the repo lives, which paths trigger work,
|
|
6
|
+
* and what commands to run. Profiles produce these from their own config
|
|
7
|
+
* shapes before constructing a WatcherManager.
|
|
8
|
+
*/
|
|
9
|
+
export interface NormalizedRepoConfig {
|
|
10
|
+
name: string;
|
|
11
|
+
/** Glob patterns (chokidar shape) — paths that should trigger watcher / lint. */
|
|
12
|
+
discover: string[];
|
|
13
|
+
/** Shell command spawned as the persistent watcher (or undefined for none). */
|
|
14
|
+
watch: string | undefined;
|
|
15
|
+
/** Per-extension lint commands. Undefined = no lint for that family. */
|
|
16
|
+
lint: {
|
|
17
|
+
js: string | undefined;
|
|
18
|
+
css: string | undefined;
|
|
19
|
+
php: string | undefined;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/watchers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,wEAAwE;IACxE,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;QACvB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;KACzB,CAAC;CACH"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watcher subsystem types.
|
|
3
|
+
*
|
|
4
|
+
* The watcher manager works on a *normalized* repo config — a per-repo
|
|
5
|
+
* declaration that says where the repo lives, which paths trigger work,
|
|
6
|
+
* and what commands to run. Profiles produce these from their own config
|
|
7
|
+
* shapes before constructing a WatcherManager.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
package/docs/commands.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Commands
|
|
2
|
+
|
|
3
|
+
`dev-cockpit` ships four core commands. A profile contributes its own commands via `setupCli(program)` — these register **first**, so a profile can take over a core command name (the matching core command is then skipped).
|
|
4
|
+
|
|
5
|
+
| Command | What it does |
|
|
6
|
+
|---------------|-------------------------------------------------------------------------------|
|
|
7
|
+
| `dev` | Boots the three-pane TUI (Repos / Output / Health / Help). |
|
|
8
|
+
| `doctor` | Runs every registered health check once and prints a status table. |
|
|
9
|
+
| `init-config` | Writes a starter `cockpit.yaml` in the current directory. |
|
|
10
|
+
| `mount` | Generates a Docker compose overlay for declared bind mounts. |
|
|
11
|
+
|
|
12
|
+
## `dev`
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
dev-cockpit dev [-c <path>]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Loads `cockpit.yaml`, wires the active profile + config-declared health checks into the cockpit shell, and starts an optional Docker log tailer if the config declares services to tail.
|
|
19
|
+
|
|
20
|
+
## `doctor`
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
dev-cockpit doctor [-c <path>]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Builds the health registry from `config.health[]` plus `profile.healthChecks`, runs the `startup` trigger set once, and prints `✓ / ⚠ / ✗` per check. Exits non-zero on any `error` severity.
|
|
27
|
+
|
|
28
|
+
## `init-config`
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
dev-cockpit init-config [--with-docker] [-f|--force]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Writes a `cockpit.yaml` template at the current directory. `--with-docker` uncomments the docker block. The template is otherwise heavily commented — uncomment the sections you need.
|
|
35
|
+
|
|
36
|
+
## `mount`
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
dev-cockpit mount [-c <path>] [-s|--service <name>]
|
|
40
|
+
dev-cockpit mount status [-c <path>]
|
|
41
|
+
dev-cockpit mount clear [-c <path>]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Merges `config.mounts[]` with `profile.mountCandidatesProvider?.()`, then writes:
|
|
45
|
+
|
|
46
|
+
- `<workspace>/docker-compose.dev-cockpit.yml` — the overlay
|
|
47
|
+
- `<state-dir>/mount.manifest.json` — what was applied + when
|
|
48
|
+
|
|
49
|
+
See [mount.md](./mount.md) for the full mount story.
|
|
50
|
+
|
|
51
|
+
## Profile setupCli
|
|
52
|
+
|
|
53
|
+
A profile bundle implements the `Profile` type's `setupCli(program)` to register additional commands or override core ones:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import type { Profile } from 'dev-cockpit';
|
|
57
|
+
|
|
58
|
+
export const myProfile: Profile = {
|
|
59
|
+
appName: 'my-app',
|
|
60
|
+
setupCli(program) {
|
|
61
|
+
program
|
|
62
|
+
.command('release')
|
|
63
|
+
.description('cut a release')
|
|
64
|
+
.action(async () => {
|
|
65
|
+
/* … */
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The profile is invoked **before** the core commands register. Any name the profile claims (`dev`, `doctor`, etc.) silently skips its core counterpart — no overrides API, no array mutation, just registration order.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Config reference
|
|
2
|
+
|
|
3
|
+
A `cockpit.yaml` lives at the root of your project. The cockpit reads it on startup and seeds the store from it.
|
|
4
|
+
|
|
5
|
+
## Top-level keys
|
|
6
|
+
|
|
7
|
+
- `version` (number, required) — schema version. Must match the running build's `CONFIG_VERSION`.
|
|
8
|
+
- `appName` (string, required) — used as the state-dir name (`~/.local/state/<appName>/...`) and the OS-notification title prefix.
|
|
9
|
+
- `watchers` (array) — see watchers.md.
|
|
10
|
+
- `repos` (array) — items to show in the Repos pane.
|
|
11
|
+
- `docker` (object) — `composeFile` + `services[]`. Omit the whole block on plain-Node projects.
|
|
12
|
+
- `highlights` (array) — patterns applied to all log streams.
|
|
13
|
+
- `health` (array) — see health.md.
|
|
14
|
+
- `help` (object) — extra docs sources + `defaultPage`.
|
|
15
|
+
- `notifications` (object) — see notifications.md.
|
|
16
|
+
- `profile` (object) — namespace for profile-specific extension keys (e.g. `profile.myapp = { ... }`). Each profile validates its own block via its `configSchemaExt`.
|
|
17
|
+
|
|
18
|
+
## Example
|
|
19
|
+
|
|
20
|
+
A fully-commented example lives at `examples/cockpit.yaml` in the dev-cockpit package. Copy it as a starting point and trim what doesn't apply.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
Run `dev-cockpit dev` from a directory that contains a `cockpit.yaml`. The TUI boots into the **Repos** tab and starts any watchers + health checks declared in your config.
|
|
4
|
+
|
|
5
|
+
## At a glance
|
|
6
|
+
|
|
7
|
+
- **Cycle tabs** — `←` / `→` (or `Tab` / `Shift-Tab`)
|
|
8
|
+
- **Quit** — `q`
|
|
9
|
+
- **Toggle notifications for this session** — `n`
|
|
10
|
+
|
|
11
|
+
Every tab fills the full terminal. Modals (filter, search) render in place of the active pane.
|
|
12
|
+
|
|
13
|
+
## What the panes do
|
|
14
|
+
|
|
15
|
+
- **Repos** — list of repos and (optionally) Docker services with watcher / lint status; per-row actions live here.
|
|
16
|
+
- **Output** — streaming logs from watchers and Docker services, with filter and search modals.
|
|
17
|
+
- **Health** — workspace health checks; each check has a one-keystroke remediation.
|
|
18
|
+
- **Help** — this guide. `j` / `k` to flip pages, `↑` / `↓` to scroll.
|
|
19
|
+
|
|
20
|
+
## Try it on a fixture
|
|
21
|
+
|
|
22
|
+
The repo ships two minimal sample projects under `tests/fixtures/`:
|
|
23
|
+
|
|
24
|
+
- `tests/fixtures/plain-node/` — no docker dependency. Watcher (`tsc --watch`), `http-ok` health against a tiny http server, and a `file-exists` health on `package-lock.json`.
|
|
25
|
+
- `tests/fixtures/dockerized/` — a `docker compose` stack (alpine heartbeat + redis), `container-running` and `port-open` health checks, log tailing.
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
cd tests/fixtures/plain-node
|
|
29
|
+
npm install
|
|
30
|
+
node src/server.mjs & # so the http-ok check goes green
|
|
31
|
+
dev-cockpit dev
|
|
32
|
+
|
|
33
|
+
# Or for the docker variant:
|
|
34
|
+
cd tests/fixtures/dockerized
|
|
35
|
+
docker compose up -d
|
|
36
|
+
dev-cockpit dev
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
These fixtures double as the project's smoke tests (`tests/fixtures/smoke.test.ts`) — running `npm test` parses each `cockpit.yaml` and asserts the shell renders.
|
package/docs/health.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Health
|
|
2
|
+
|
|
3
|
+
A health check is a small predicate that decides whether something in your workspace is `ok`, `warn`, or `error`. Each check pairs with a one-keystroke remediation that the user can press from the Health pane to fix it.
|
|
4
|
+
|
|
5
|
+
The cockpit re-runs health checks on three triggers:
|
|
6
|
+
|
|
7
|
+
| Trigger | When |
|
|
8
|
+
|------------|-----------------------------------------------------------------------|
|
|
9
|
+
| `startup` | once, at boot |
|
|
10
|
+
| `fsevent` | when the watcher reports a file change (debounced ~500 ms) |
|
|
11
|
+
| `lockfile` | when `composer.lock`, `package-lock.json`, or `yarn.lock` changes |
|
|
12
|
+
| `docker` | when a docker event is signalled (e.g. by the docker subsystem) |
|
|
13
|
+
|
|
14
|
+
Transitions (`ok → error`, `error → ok`) fire OS notifications according to the global `notifications:` block, optionally overridden per-check.
|
|
15
|
+
|
|
16
|
+
## Built-in check vocabulary
|
|
17
|
+
|
|
18
|
+
| Type | Required args | Optional args | Default triggers |
|
|
19
|
+
|---------------------|----------------------|------------------------------|---------------------------|
|
|
20
|
+
| `container-running` | `container` | | `startup`, `docker` |
|
|
21
|
+
| `port-open` | `port` (number) | `host` (default `127.0.0.1`) | `startup` |
|
|
22
|
+
| `http-ok` | `url` | `expectStatus` (default 200) | `startup` |
|
|
23
|
+
| `file-exists` | `path` | | `startup`, `fsevent` |
|
|
24
|
+
| `exec-zero` | `command` | `args[]`, `cwd` | `startup` |
|
|
25
|
+
|
|
26
|
+
Defaults can be overridden by setting `triggers:` and `severity:` on the entry.
|
|
27
|
+
|
|
28
|
+
## Config shape
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
health:
|
|
32
|
+
- id: api-up
|
|
33
|
+
label: API responds
|
|
34
|
+
type: http-ok
|
|
35
|
+
url: http://localhost:3000/health
|
|
36
|
+
expectStatus: 200
|
|
37
|
+
remediation:
|
|
38
|
+
key: A
|
|
39
|
+
label: restart api
|
|
40
|
+
command: docker compose restart api
|
|
41
|
+
|
|
42
|
+
- id: db-up
|
|
43
|
+
label: DB reachable
|
|
44
|
+
type: port-open
|
|
45
|
+
port: 5432
|
|
46
|
+
triggers: [startup]
|
|
47
|
+
remediation:
|
|
48
|
+
key: D
|
|
49
|
+
label: start db
|
|
50
|
+
command: docker compose up -d db
|
|
51
|
+
notify:
|
|
52
|
+
onTransitionTo: [health-failed] # only notify on going-down
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Remediations
|
|
56
|
+
|
|
57
|
+
Each health entry declares one remediation. It can be either:
|
|
58
|
+
|
|
59
|
+
- **Declarative shell command** — `command: "npm install"` (with optional `cwd`). Stdout/stderr stream to the Output pane tagged `health:<id>`.
|
|
60
|
+
- **Programmatic function** — only available to checks contributed by a profile (TypeScript code), via `run(ctx, workspaceRoot)`.
|
|
61
|
+
|
|
62
|
+
The remediation `key` is a single uppercase letter. Pressing the key (lowercase or upper) on the Health pane runs the remediation; the row shows `… (running…)` until completion, with a minimum 1.5 s banner hold so fast-finishing actions stay visible.
|
|
63
|
+
|
|
64
|
+
## Notification policy
|
|
65
|
+
|
|
66
|
+
Per-check `notify:` overrides:
|
|
67
|
+
|
|
68
|
+
| Setting | Effect |
|
|
69
|
+
|-----------------------------------------------|-------------------------------------------------------|
|
|
70
|
+
| omitted | inherit global `notifications:` policy |
|
|
71
|
+
| `notify: false` | always silent for this check |
|
|
72
|
+
| `notify: { onTransitionTo: [health-failed] }` | only notify on the listed transition events |
|
|
73
|
+
|
|
74
|
+
The session toggle (`n` keystroke) and global `notifications.enabled: false` always silence everything — per-check overrides cannot opt back in.
|
|
75
|
+
|
|
76
|
+
## Profile-contributed checks
|
|
77
|
+
|
|
78
|
+
A profile bundles pre-built checks (TypeScript with closures over project-specific helpers) via `Profile.healthChecks`. They register **after** config-driven entries and may use any custom `type` id. **Built-in `type` ids cannot be overridden** by profile checks — the registry rejects them with a warning, so the built-in vocabulary stays predictable.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import type { Profile } from 'dev-cockpit';
|
|
82
|
+
|
|
83
|
+
export const myProfile: Profile = {
|
|
84
|
+
appName: 'my-cockpit',
|
|
85
|
+
healthChecks: [
|
|
86
|
+
{
|
|
87
|
+
id: 'composer-installed',
|
|
88
|
+
label: 'Composer',
|
|
89
|
+
severity: 'error',
|
|
90
|
+
triggers: ['startup', 'lockfile'],
|
|
91
|
+
predicate: async (ctx) => { /* … */ },
|
|
92
|
+
remediation: { key: 'R', label: 'composer install', run: async (ctx, root) => { /* … */ } },
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Wiring
|
|
99
|
+
|
|
100
|
+
In your `dev` command, pass `health: { … }` to `runCockpit`:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { runCockpit, buildHealthContext } from 'dev-cockpit';
|
|
104
|
+
|
|
105
|
+
await runCockpit({
|
|
106
|
+
profile: myProfile,
|
|
107
|
+
footerLegends: { /* … */ },
|
|
108
|
+
health: {
|
|
109
|
+
workspaceRoot,
|
|
110
|
+
profileChecks: myProfile.healthChecks,
|
|
111
|
+
configEntries: config.health,
|
|
112
|
+
ctx: buildHealthContext(workspaceRoot, appendOutput),
|
|
113
|
+
notifications: config.notifications,
|
|
114
|
+
appName: 'my-cockpit',
|
|
115
|
+
subscribeFsEvents: (listener) => watcherManager.subscribeFsEvents(listener),
|
|
116
|
+
},
|
|
117
|
+
}).waitUntilExit();
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`useHealth` (called internally by `<Cockpit>`) owns the scheduler lifecycle, transition diffing, notification dispatch, and remediation routing. Consumers do not instantiate `HealthScheduler` directly.
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# dev-cockpit help
|
|
2
|
+
|
|
3
|
+
This is the in-cockpit Help tab. The pages below explain what each pane does, how to drive the cockpit from the keyboard, and how to configure it for your project.
|
|
4
|
+
|
|
5
|
+
- [Getting started](./getting-started.md) — first run and what to expect
|
|
6
|
+
- [Panes](./panes.md) — Repos, Output, Health, Help
|
|
7
|
+
- [Watchers](./watchers.md) — long-running commands streamed into Output
|
|
8
|
+
- [Health](./health.md) — checks and one-keystroke remediations
|
|
9
|
+
- [Commands](./commands.md) — the four core CLI commands + profile `setupCli`
|
|
10
|
+
- [init-config](./init-config.md) — scaffold a starter `cockpit.yaml`
|
|
11
|
+
- [Mount](./mount.md) — host ↔ container bind-mount overlay
|
|
12
|
+
- [Notifications](./notifications.md) — transition-only OS notifications
|
|
13
|
+
- [Config reference](./config-reference.md) — `cockpit.yaml` schema
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# init-config
|
|
2
|
+
|
|
3
|
+
`dev-cockpit init-config` writes a starter `cockpit.yaml` at the current working directory. Two modes:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
dev-cockpit init-config [--with-docker] [-f|--force] # static template
|
|
7
|
+
dev-cockpit init-config -i [-f|--force] # interactive wizard
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Flags
|
|
11
|
+
|
|
12
|
+
| Flag | Default | Effect |
|
|
13
|
+
|----------------------|---------|-----------------------------------------------------------------------------------------------|
|
|
14
|
+
| `-i, --interactive` | off | Walk through prompts to populate the file. Skips the static template. |
|
|
15
|
+
| `--with-docker` | off | Uncomments the `docker:` block in the static template (ignored in `--interactive` mode). |
|
|
16
|
+
| `-f, --force` | off | Overwrites an existing `cockpit.yaml` instead of refusing. |
|
|
17
|
+
|
|
18
|
+
## Static template
|
|
19
|
+
|
|
20
|
+
`version: 1` and `appName` are required and uncommented. Everything else (watchers, repos, docker, highlights, health, help, notifications, mounts) ships as a commented example. You uncomment what you need. Best when you already know what you want — fastest path from scratch to "edit me".
|
|
21
|
+
|
|
22
|
+
## Interactive wizard
|
|
23
|
+
|
|
24
|
+
The recommended path the first time you set the cockpit up on a project. It walks through seven steps, with project sniffing pre-populating sensible defaults so you usually just press enter:
|
|
25
|
+
|
|
26
|
+
| Step | Section | What it asks + auto-detects |
|
|
27
|
+
|------|---------|------------------------------|
|
|
28
|
+
| 1 | **App name** | Defaults to `package.json` `name`, then the cwd basename. |
|
|
29
|
+
| 2 | **Docker** | Detects `compose.yaml` / `docker-compose.yml` and parses its services. Offers them as a multi-select. Skip entirely for non-compose projects. |
|
|
30
|
+
| 3 | **Watchers** | Surfaces `package.json` scripts (`dev`, `watch`, `start`, `serve`) as pre-checked candidates. Anything that runs forever and writes to stdout — `vite`, `tsc --watch`, `php artisan serve`, `tail -f storage/logs/laravel.log` — fits here. |
|
|
31
|
+
| 4 | **Repos** | Detects npm/yarn/pnpm workspaces; falls back to a single `.` entry. Each entry becomes a row in the Repos pane (with `r/w/l` keystroke actions when a profile bundle wires them). |
|
|
32
|
+
| 5 | **Highlights** | Regex patterns that re-colour matching Output lines. `error`-severity matches additionally feed the Recent Errors strip + OS notifications. |
|
|
33
|
+
| 6 | **Health** | Reframed as test-and-fix pairs. Pick one of the five built-in types (`container-running`, `port-open`, `http-ok`, `file-exists`, `exec-zero`), supply the type-specific arg(s), then bind a single-keystroke remediation. Add as many checks as you like — same type repeats freely (e.g. `port-open` for 5432, 6379, 8108). `container-running` is hidden when no docker block was declared. |
|
|
34
|
+
| 7 | **Notifications** | Toggle native OS notifications on state transitions. |
|
|
35
|
+
|
|
36
|
+
After writing the file, the wizard offers to run `dev-cockpit doctor` immediately so you get instant feedback that the config parses + each declared health check has a baseline state.
|
|
37
|
+
|
|
38
|
+
The wizard scaffolds a *working* config. Anything more advanced (custom triggers, `expectStatus`, exec args, mounts, profile namespaces) is a hand-edit on the generated file.
|
|
39
|
+
|
|
40
|
+
## Profile-driven defaults
|
|
41
|
+
|
|
42
|
+
When a profile is active (e.g. `<profile-bin> init-config -i`), the wizard defaults `appName` to the profile's. Profile bundles can also register their own `init-config` via `setupCli(program)` if they want a domain-specific template — the generic command is skipped when the profile claims the slot.
|
|
43
|
+
|
|
44
|
+
## Schema reference
|
|
45
|
+
|
|
46
|
+
The full schema definition lives at [config-reference.md](./config-reference.md).
|
package/docs/mount.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Mount
|
|
2
|
+
|
|
3
|
+
`dev-cockpit mount` generates a Docker compose overlay describing host ↔ container bind mounts so you can swap a containerised dependency for a local checkout.
|
|
4
|
+
|
|
5
|
+
## When to use it
|
|
6
|
+
|
|
7
|
+
When you're developing a containerised app and want changes in a local checkout to flow live into the running container — e.g. mount your local `./packages/api` into `/srv/api` inside the `web` service.
|
|
8
|
+
|
|
9
|
+
## Mount sources
|
|
10
|
+
|
|
11
|
+
Mounts come from two places, merged on `containerPath` (config wins on collision):
|
|
12
|
+
|
|
13
|
+
1. **`config.mounts[]`** — explicit, declared in `cockpit.yaml`:
|
|
14
|
+
```yaml
|
|
15
|
+
mounts:
|
|
16
|
+
- hostPath: ./packages/api
|
|
17
|
+
containerPath: /srv/api
|
|
18
|
+
```
|
|
19
|
+
2. **`profile.mountCandidatesProvider?.()`** — discovered programmatically by the active profile (e.g. walking a manifest file to find local checkouts of declared dependencies).
|
|
20
|
+
|
|
21
|
+
## Output
|
|
22
|
+
|
|
23
|
+
- `<workspace>/docker-compose.dev-cockpit.yml` — the overlay file. Apply it alongside your existing compose file:
|
|
24
|
+
```
|
|
25
|
+
docker compose -f docker-compose.yml -f docker-compose.dev-cockpit.yml up -d
|
|
26
|
+
```
|
|
27
|
+
- `<state-dir>/mount.manifest.json` — records what was applied + when. Used by `mount status` and `mount clear`.
|
|
28
|
+
|
|
29
|
+
The state dir lives under `XDG_STATE_HOME/<appName>/<workspace-hash>/`.
|
|
30
|
+
|
|
31
|
+
## Subcommands
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
dev-cockpit mount [-s|--service <name>] # write/refresh the overlay
|
|
35
|
+
dev-cockpit mount status # print active overlay manifest
|
|
36
|
+
dev-cockpit mount clear # delete overlay + manifest
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
If `--service` is omitted, the first entry in `config.docker.services` is used. Pass `--service` explicitly when your compose file has multiple services and you want mounts attached to a non-default one.
|
|
40
|
+
|
|
41
|
+
## Profile-side discovery
|
|
42
|
+
|
|
43
|
+
A profile that scans a manifest (e.g. `composer.json`, `package.json` workspaces) for local checkouts implements `mountCandidatesProvider` and returns `Mount[]`. Discovered candidates blend with `config.mounts[]` automatically:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
export const myProfile: Profile = {
|
|
47
|
+
appName: 'my-app',
|
|
48
|
+
mountCandidatesProvider(): Mount[] {
|
|
49
|
+
return findLocalCheckouts().map((c) => ({
|
|
50
|
+
hostPath: c.hostPath,
|
|
51
|
+
containerPath: c.containerPath,
|
|
52
|
+
}));
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Notifications
|
|
2
|
+
|
|
3
|
+
OS notifications fire on **state transitions only** — never on every check, never on every log line. The transition detector compares the previous health snapshot to the new one and emits one event per check that crossed the `error` threshold.
|
|
4
|
+
|
|
5
|
+
## Layered policy
|
|
6
|
+
|
|
7
|
+
1. **Global default** — `notifications:` block in `cockpit.yaml`:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
notifications:
|
|
11
|
+
enabled: true
|
|
12
|
+
onTransitionTo:
|
|
13
|
+
- error
|
|
14
|
+
- recovered
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
2. **Per-item override** — a `notify:` field on any watcher or health check:
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
- id: lint
|
|
21
|
+
command: npx eslint . --watch
|
|
22
|
+
notify: false # mute this item
|
|
23
|
+
|
|
24
|
+
- id: api-up
|
|
25
|
+
type: http-ok
|
|
26
|
+
url: http://localhost:3000/health
|
|
27
|
+
notify:
|
|
28
|
+
onTransitionTo: [error] # only on going-down, not on recovery
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
3. **Session toggle** — press `n` in the cockpit to mute *all* notifications for the current session (overrides config). The Health pane shows the current session state at the bottom.
|
|
32
|
+
|
|
33
|
+
## Events
|
|
34
|
+
|
|
35
|
+
- `health-failed` / `health-recovered` — emitted by the health framework
|
|
36
|
+
- `build-failed` / `build-recovered` — emitted by watchers
|
|
37
|
+
- Profiles can emit their own event names; they flow through the same pipeline.
|
|
38
|
+
|
|
39
|
+
The `appName` configured in `cockpit.yaml` is used as the title prefix on every notification (e.g. `my-app — API responds`).
|
package/docs/panes.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Panes
|
|
2
|
+
|
|
3
|
+
## Repos
|
|
4
|
+
|
|
5
|
+
Glyphs:
|
|
6
|
+
|
|
7
|
+
- ● running (green)
|
|
8
|
+
- ○ idle (gray/dim)
|
|
9
|
+
- ✗ failing (red)
|
|
10
|
+
|
|
11
|
+
Lint sub-glyph (after the name): ✓ pass · ! fail · (none) unknown.
|
|
12
|
+
|
|
13
|
+
Selected row is marked with a leading `>` and the pane border is highlighted when this pane has focus.
|
|
14
|
+
|
|
15
|
+
Actions (when a row is selected):
|
|
16
|
+
|
|
17
|
+
- `r` — primary action (rebuild for repo entries, restart for Docker entries)
|
|
18
|
+
- `w` — toggle watcher (repo entries only)
|
|
19
|
+
- `l` — run lint (repo entries only)
|
|
20
|
+
|
|
21
|
+
In-flight actions show a banner at the top of the pane plus an inline `(running…)` next to the active row.
|
|
22
|
+
|
|
23
|
+
## Output
|
|
24
|
+
|
|
25
|
+
Streaming source-tagged logs.
|
|
26
|
+
|
|
27
|
+
- `↑` / `↓` — scroll one line
|
|
28
|
+
- `PgUp` / `PgDn` — scroll one page
|
|
29
|
+
- `Enter` — jump to bottom (auto-scroll resumes)
|
|
30
|
+
- `c` — clear
|
|
31
|
+
- `/` — open Search modal
|
|
32
|
+
- `f` — open Filter modal
|
|
33
|
+
- `e` — open the most recent error with a `file:line` reference in `$EDITOR`
|
|
34
|
+
|
|
35
|
+
A "Recent errors" strip appears at the top when one or more error lines have been pushed (most-recent first, capped at 3 visible).
|
|
36
|
+
|
|
37
|
+
## Health
|
|
38
|
+
|
|
39
|
+
Each row shows: severity glyph, label, remediation key (if any). `Enter` toggles the diagnostic detail for the selected row. Press the remediation letter to fix.
|
|
40
|
+
|
|
41
|
+
While a remediation runs, a banner appears at the top and the running row shows `… (running…)`.
|
|
42
|
+
|
|
43
|
+
## Help
|
|
44
|
+
|
|
45
|
+
This pane. `j` / `k` (or `Shift-Tab` / `Tab`) cycle pages. `↑` / `↓` scroll. `g` / `G` jump to top / bottom. `←` / `→` always exit Help to the next tab.
|
package/docs/watchers.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Watchers
|
|
2
|
+
|
|
3
|
+
A watcher is a long-running command (typically `tsc --watch`, `vitest --watch`, `eslint --watch`) declared in `cockpit.yaml` under `watchers:`. The cockpit spawns each watcher lazily on first focus and streams its stdout/stderr into the Output pane, prefixed by source id.
|
|
4
|
+
|
|
5
|
+
## Config shape
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
watchers:
|
|
9
|
+
- id: typecheck
|
|
10
|
+
label: tsc --watch
|
|
11
|
+
command: npx tsc --watch --noEmit
|
|
12
|
+
color: cyan
|
|
13
|
+
- id: test
|
|
14
|
+
label: vitest
|
|
15
|
+
command: npx vitest --watch
|
|
16
|
+
color: magenta
|
|
17
|
+
notify: false # mute notifications for this watcher
|
|
18
|
+
restartOn:
|
|
19
|
+
- package.json
|
|
20
|
+
- tsconfig.json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `id` is required; it appears as the source prefix on every emitted line.
|
|
24
|
+
- `restartOn` lets you re-spawn the watcher when specific files change.
|
|
25
|
+
- `notify` overrides the global notifications policy for this watcher (see notifications.md).
|
|
26
|
+
|
|
27
|
+
This page is a stub — full watcher behavior is fleshed out in subsequent phases.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# cockpit.yaml — example config
|
|
2
|
+
# Run `dev-cockpit dev` from the directory containing this file.
|
|
3
|
+
|
|
4
|
+
version: 1 # config schema version; required
|
|
5
|
+
appName: my-app # state dir: ~/.local/state/my-app/
|
|
6
|
+
|
|
7
|
+
# Watchers — long-running commands streamed into the Output pane.
|
|
8
|
+
watchers:
|
|
9
|
+
- id: typecheck
|
|
10
|
+
label: tsc --watch
|
|
11
|
+
command: npx tsc --watch --noEmit
|
|
12
|
+
color: cyan
|
|
13
|
+
- id: test
|
|
14
|
+
label: vitest
|
|
15
|
+
command: npx vitest --watch
|
|
16
|
+
color: magenta
|
|
17
|
+
- id: lint
|
|
18
|
+
label: eslint
|
|
19
|
+
command: npx eslint . --watch
|
|
20
|
+
color: yellow
|
|
21
|
+
notify: false # mute notifications for this watcher
|
|
22
|
+
restartOn:
|
|
23
|
+
- package.json
|
|
24
|
+
- .eslintrc.*
|
|
25
|
+
|
|
26
|
+
# Repos — what shows in the Repos pane (optional; default = empty).
|
|
27
|
+
repos:
|
|
28
|
+
- id: api
|
|
29
|
+
path: ./packages/api
|
|
30
|
+
label: API
|
|
31
|
+
- id: web
|
|
32
|
+
path: ./packages/web
|
|
33
|
+
label: Web
|
|
34
|
+
|
|
35
|
+
# Docker — enables container log tailing, container health checks, mount overlay.
|
|
36
|
+
# Omit this whole block for plain-Node projects; the docker tab/section won't appear.
|
|
37
|
+
docker:
|
|
38
|
+
composeFile: ./docker-compose.yml
|
|
39
|
+
services:
|
|
40
|
+
- name: web
|
|
41
|
+
tail: true
|
|
42
|
+
- name: db
|
|
43
|
+
tail: true
|
|
44
|
+
- name: redis
|
|
45
|
+
tail: false
|
|
46
|
+
|
|
47
|
+
# Highlight patterns — applied to all log streams.
|
|
48
|
+
highlights:
|
|
49
|
+
- pattern: ERROR
|
|
50
|
+
severity: error
|
|
51
|
+
- pattern: FATAL
|
|
52
|
+
severity: error
|
|
53
|
+
- pattern: WARN
|
|
54
|
+
severity: warn
|
|
55
|
+
- pattern: "Stack trace"
|
|
56
|
+
severity: error
|
|
57
|
+
|
|
58
|
+
# Health checks — `type` references a built-in or profile-registered check id.
|
|
59
|
+
# Each check has a one-keystroke remediation.
|
|
60
|
+
health:
|
|
61
|
+
- id: api-up
|
|
62
|
+
label: API responds
|
|
63
|
+
type: http-ok
|
|
64
|
+
url: http://localhost:3000/health
|
|
65
|
+
notify:
|
|
66
|
+
onTransitionTo: [error] # only notify on going-down, not on recovery
|
|
67
|
+
remediation:
|
|
68
|
+
key: a
|
|
69
|
+
label: restart api
|
|
70
|
+
command: docker compose restart web
|
|
71
|
+
|
|
72
|
+
- id: db-running
|
|
73
|
+
label: DB container running
|
|
74
|
+
type: container-running
|
|
75
|
+
container: db
|
|
76
|
+
remediation:
|
|
77
|
+
key: d
|
|
78
|
+
label: bring db up
|
|
79
|
+
command: docker compose up -d db
|
|
80
|
+
|
|
81
|
+
- id: env-file
|
|
82
|
+
label: .env exists
|
|
83
|
+
type: file-exists
|
|
84
|
+
path: ./.env
|
|
85
|
+
remediation:
|
|
86
|
+
key: e
|
|
87
|
+
label: copy .env.example
|
|
88
|
+
command: cp .env.example .env
|
|
89
|
+
|
|
90
|
+
- id: migrations
|
|
91
|
+
label: migrations applied
|
|
92
|
+
type: exec-zero
|
|
93
|
+
command: npm run db:check
|
|
94
|
+
remediation:
|
|
95
|
+
key: m
|
|
96
|
+
label: run migrations
|
|
97
|
+
command: npm run db:migrate
|
|
98
|
+
|
|
99
|
+
# Help — extra markdown directories layered on top of core's built-in docs.
|
|
100
|
+
help:
|
|
101
|
+
sources:
|
|
102
|
+
- ./docs
|
|
103
|
+
defaultPage: getting-started
|
|
104
|
+
|
|
105
|
+
# Notifications — fire on state transitions only.
|
|
106
|
+
notifications:
|
|
107
|
+
enabled: true
|
|
108
|
+
onTransitionTo:
|
|
109
|
+
- error
|
|
110
|
+
- recovered
|
|
111
|
+
|
|
112
|
+
# Profile-specific config lives under `profile.<name>` — validated by the
|
|
113
|
+
# profile's `configSchemaExt` zod extension. Generic core ignores this block.
|
|
114
|
+
# profile:
|
|
115
|
+
# myapp:
|
|
116
|
+
# someKey: value
|