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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `dev-cockpit` are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
|
+
|
|
5
|
+
## [0.1.0] — 2026-05-10
|
|
6
|
+
|
|
7
|
+
First pre-1.0 release. The public surface (`Profile` interface with `boot` lifecycle hook, `buildCli`, the four core commands `dev / doctor / init-config / mount`, the `cockpit.yaml` schema with `version: 1`) is stable enough for first consumers to depend on. Expect minor churn until the post-1.0 roadmap items land.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Interactive `init-config` wizard** (`-i` / `--interactive`). Seven-step onboarding with project sniffing — auto-detects compose files + services, `package.json` scripts as watcher candidates, npm/yarn/pnpm workspaces as repo entries, and surfaces sensible defaults so most prompts just need an enter. Closes by offering to run `doctor` immediately.
|
|
12
|
+
- **`profile.boot` lifecycle hook**. Profiles return `{cockpitHandlers, subscribeFsEvents, cleanup}` from a single async function called after config load + workspace resolution and before the cockpit renders. Replaces the older "profile registers its own dev command" override pattern.
|
|
13
|
+
- **`profile.reposProvider(ctx)` receives the wizard context** so profiles can drive the Repos pane from their validated `profile.<appName>` namespace.
|
|
14
|
+
- **`killAllSpawned()` global subprocess registry** — every `spawnStream` auto-registers + auto-deregisters; `killAllSpawned()` SIGTERMs stragglers, awaits reap, falls back to SIGKILL after 1.5s. Eliminates the "second Ctrl-C needed to quit" class of bugs.
|
|
15
|
+
- **Periodic poll for `docker`-trigger health checks** (default 5s, configurable via `SchedulerDeps.dockerPollIntervalMs`, set 0 to disable). `docker compose down` mid-session now flips `container-running` / `port-open` checks red within the poll interval.
|
|
16
|
+
- **Synthetic test fixtures** under `tests/fixtures/`: `plain-node` (no docker block; tsc watcher; http-ok + file-exists checks) and `dockerized` (alpine heartbeat + redis; container-running + port-open checks). Smoke tests via `ink-testing-library`.
|
|
17
|
+
- **Generic CLI commands** — `dev / doctor / init-config / mount` — with `buildCli({ profile? })` factory. Profile-first registration: `setupCli(program)` runs before core, claimed names skip their core counterpart (one mechanism, no array mutation).
|
|
18
|
+
- **Health framework** with five built-in check types (`container-running`, `port-open`, `http-ok`, `file-exists`, `exec-zero`), `useHealth` hook orchestrating the scheduler + transition diffing + remediation dispatch, per-item `notify` overrides.
|
|
19
|
+
- **Watcher subsystem** (chokidar lazy-spawn, reactive lint, transition-only build notifications).
|
|
20
|
+
- **Docker subsystem** (log tailer with live-check, highlight engine, stack-trace extractor, restart helper).
|
|
21
|
+
- **Cockpit shell** — four-pane TUI (Repos / Output / Health / Help), tab nav, modals (filter / search), live-markdown Help loader.
|
|
22
|
+
- **Core modules** — config loader (zod + `version: 1` schema), state paths (`xdg_state_home/<appName>/<hash>/`), pino logger, execa subprocess wrapper, OS notifier (detached `osascript` / `notify-send` so the helper doesn't keep the parent loop alive).
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **OS notifier no longer uses `node-notifier`.** Direct `child_process.spawn` with `detached: true` + `stdio: 'ignore'` + `unref()` so notification helpers run independently of the cockpit's lifetime.
|
|
27
|
+
- **`DockerLogTailer.stop()` and `WatcherManager.stop()` await child SIGTERM reap** before resolving (previously sent the signal and returned immediately, leaving handles in the event loop).
|
|
28
|
+
- **Bin shim filters Node 22's `ExperimentalWarning: Importing JSON modules`** (cli-boxes uses `with {type: 'json'}`). Filtered at the bin level via dynamic-import-after-emitWarning-hook.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Per-phase exit cleanup so the shell prompt returns immediately on `q` — no more "second Ctrl-C needed to quit".
|
|
33
|
+
|
|
34
|
+
## [0.0.0] — early lifts
|
|
35
|
+
|
|
36
|
+
Initial scaffold + the phase 2-6 lifts of the cockpit shell + watcher / docker / health subsystems into a generic, domain-neutral library. Captured here for completeness; commit history in git is the authoritative record.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Frank Fava
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# dev-cockpit
|
|
2
|
+
|
|
3
|
+
A reusable, domain-neutral terminal UI dev cockpit. One window for the long-running processes you babysit during development: code, logs, health, and the keystrokes to fix things when they break.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
┌── Repos ── Output ── Health ── Help ────────────────── [↔ / tab] cycle ──┐
|
|
7
|
+
│ Repos │
|
|
8
|
+
│ web docker idle │
|
|
9
|
+
│ db docker idle │
|
|
10
|
+
│ cache docker idle │
|
|
11
|
+
│ │
|
|
12
|
+
│ Recent errors: │
|
|
13
|
+
│ web │ Stack trace at /srv/app/handler.ts:42 │
|
|
14
|
+
└───────────────────────────────────────────────────────────────────────────┘
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## What's in the box
|
|
18
|
+
|
|
19
|
+
- **Four-pane TUI** — Repos / Output / Health / Help, tab-cycle nav, filter + search modals.
|
|
20
|
+
- **Watchers** — long-running processes spawned once on boot; their stdout streams into Output, tagged by id.
|
|
21
|
+
- **Docker log tailing** — stream `docker compose logs -f <services>` into the same Output pane with regex-driven highlights for errors / stack traces.
|
|
22
|
+
- **Health framework** — five built-in check types (`container-running`, `port-open`, `http-ok`, `file-exists`, `exec-zero`) plus profile-contributed ones; each gets a row on the Health pane and a single-keystroke remediation.
|
|
23
|
+
- **Profile interface** — drop-in adapter so a domain-specific tool can wire its own discoverer / repos / health / commands / cockpit handlers without forking the shell.
|
|
24
|
+
- **Live markdown Help** — pages render inside the cockpit; profiles can layer their own docs over the generic ones.
|
|
25
|
+
- **Native OS notifications** — fire on state transitions only (no spam). Detached helper, never blocks process exit.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npm install -g dev-cockpit
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or `npm link` from a checkout for development.
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
cd your-project
|
|
39
|
+
dev-cockpit init-config -i # interactive wizard, 7 steps with auto-detection
|
|
40
|
+
dev-cockpit doctor # validate + print initial health
|
|
41
|
+
dev-cockpit dev # boot the cockpit
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The wizard sniffs your project: if there's a `compose.yaml` it suggests services to tail, if `package.json` has a `dev` script it offers it as a watcher, if there are workspaces it surfaces them as repo entries. Most prompts just need an enter.
|
|
45
|
+
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
Single file, `cockpit.yaml` at your project root. Schema enforced by zod (`version: 1`). Full reference: [`docs/config-reference.md`](./docs/config-reference.md). Minimal example:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
version: 1
|
|
52
|
+
appName: my-app
|
|
53
|
+
|
|
54
|
+
watchers:
|
|
55
|
+
- id: vite
|
|
56
|
+
command: npm run dev
|
|
57
|
+
|
|
58
|
+
docker:
|
|
59
|
+
composeFile: compose.yaml
|
|
60
|
+
services:
|
|
61
|
+
- { name: db }
|
|
62
|
+
- { name: cache }
|
|
63
|
+
|
|
64
|
+
health:
|
|
65
|
+
- id: app-up
|
|
66
|
+
label: app responsive
|
|
67
|
+
type: http-ok
|
|
68
|
+
url: http://localhost:3000/health
|
|
69
|
+
severity: error
|
|
70
|
+
remediation:
|
|
71
|
+
key: R
|
|
72
|
+
label: restart app
|
|
73
|
+
command: docker compose restart app
|
|
74
|
+
|
|
75
|
+
notifications:
|
|
76
|
+
enabled: true
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Profiles
|
|
80
|
+
|
|
81
|
+
A profile is a domain-specific bundle that plugs into dev-cockpit's lifecycle. It contributes any combination of:
|
|
82
|
+
|
|
83
|
+
- `discoverer` — workspace root resolution
|
|
84
|
+
- `reposProvider` — Repos-pane entries
|
|
85
|
+
- `healthChecks` — pre-built `HealthCheck[]` (programmatic, beyond the five built-ins)
|
|
86
|
+
- `setupCli` — extra CLI commands (the profile's own `select`, `release`, etc.)
|
|
87
|
+
- `helpSources` — markdown docs that layer over the generic Help
|
|
88
|
+
- `configSchemaExt` — zod schema validating the profile's `profile.<appName>` namespace
|
|
89
|
+
- `mountCandidatesProvider` — auto-detected bind-mount candidates
|
|
90
|
+
- `boot` — async lifecycle hook returning Cockpit pane handlers + `subscribeFsEvents` + `cleanup`
|
|
91
|
+
|
|
92
|
+
A profile is its own npm package — its bin entry composes `buildCli({ profile })` and that's it. The skeleton:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import type { Profile } from 'dev-cockpit';
|
|
96
|
+
|
|
97
|
+
export const myProfile: Profile = {
|
|
98
|
+
appName: 'my-app',
|
|
99
|
+
healthChecks: [...],
|
|
100
|
+
boot: ({ config, workspaceRoot }) => ({
|
|
101
|
+
cockpitHandlers: { runRepoAction, onWatchToggle, onLint, onOpenError },
|
|
102
|
+
subscribeFsEvents: (listener) => watcherManager.subscribeFsEvents(listener),
|
|
103
|
+
cleanup: () => watcherManager.stop(),
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Bin entry — that's it.
|
|
108
|
+
import { buildCli } from 'dev-cockpit';
|
|
109
|
+
buildCli({ profile: myProfile }).parse(process.argv);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Documentation
|
|
113
|
+
|
|
114
|
+
| Topic | Link |
|
|
115
|
+
|---|---|
|
|
116
|
+
| Getting started | [`docs/getting-started.md`](./docs/getting-started.md) |
|
|
117
|
+
| Commands | [`docs/commands.md`](./docs/commands.md) |
|
|
118
|
+
| `init-config` wizard | [`docs/init-config.md`](./docs/init-config.md) |
|
|
119
|
+
| Panes | [`docs/panes.md`](./docs/panes.md) |
|
|
120
|
+
| Watchers | [`docs/watchers.md`](./docs/watchers.md) |
|
|
121
|
+
| Health | [`docs/health.md`](./docs/health.md) |
|
|
122
|
+
| Mounts | [`docs/mount.md`](./docs/mount.md) |
|
|
123
|
+
| Notifications | [`docs/notifications.md`](./docs/notifications.md) |
|
|
124
|
+
| Config reference | [`docs/config-reference.md`](./docs/config-reference.md) |
|
|
125
|
+
| ADRs | [`adr/`](./adr/) |
|
|
126
|
+
| Changelog | [`CHANGELOG.md`](./CHANGELOG.md) |
|
|
127
|
+
| License | MIT |
|
|
128
|
+
|
|
129
|
+
## Status
|
|
130
|
+
|
|
131
|
+
Pre-1.0. The public surface (`Profile` interface, `buildCli`, the four core commands, `cockpit.yaml` schema with `version: 1`) is stable enough for first consumers to depend on; expect minor churn until the post-1.0 roadmap items land. See `CHANGELOG.md` for what's shipped.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Filter Node 22's ExperimentalWarning about JSON modules. The warning fires
|
|
3
|
+
// from a transitive dep (cli-boxes uses `import boxes from './boxes.json' with
|
|
4
|
+
// {type: 'json'}`) the moment the cockpit shell's import graph resolves. It's
|
|
5
|
+
// benign — Node will stabilise JSON modules — but loud on every CLI invocation.
|
|
6
|
+
// Installing the filter here, before any dynamic import, ensures it's in place
|
|
7
|
+
// before cli-boxes loads.
|
|
8
|
+
const orig = process.emitWarning.bind(process);
|
|
9
|
+
process.emitWarning = (w, ...rest) => {
|
|
10
|
+
// The JSON-modules warning fires as a plain string with the type passed
|
|
11
|
+
// separately (`process.emitWarning(text, 'ExperimentalWarning')`), so we
|
|
12
|
+
// match on the message text directly. The `.name` check from earlier
|
|
13
|
+
// only worked when the caller handed us an Error object.
|
|
14
|
+
const msg = typeof w === 'string' ? w : (w?.message ?? '');
|
|
15
|
+
if (/JSON modules/i.test(msg)) return;
|
|
16
|
+
return orig(w, ...rest);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const { buildCli } = await import('../dist/index.js');
|
|
20
|
+
buildCli().parse();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `buildCli({ profile? })` — assemble the dev-cockpit commander program.
|
|
3
|
+
*
|
|
4
|
+
* Registration order: **profile first, core last.** The profile's
|
|
5
|
+
* `setupCli(program)` runs before any core command is registered. When core
|
|
6
|
+
* goes to register a command (`dev`, `doctor`, `init-config`, `mount`),
|
|
7
|
+
* it skips any name the profile already claimed. Profiles override by
|
|
8
|
+
* registering first, not by mutating commander's internal arrays — one
|
|
9
|
+
* mechanism, no escape hatch.
|
|
10
|
+
*/
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
import type { Profile } from './core/types.js';
|
|
13
|
+
export interface BuildCliOptions {
|
|
14
|
+
profile?: Profile;
|
|
15
|
+
}
|
|
16
|
+
export declare function buildCli(opts?: BuildCliOptions): Command;
|
|
17
|
+
//# sourceMappingURL=buildCli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildCli.d.ts","sourceRoot":"","sources":["../src/buildCli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAU/C,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAkBD,wBAAgB,QAAQ,CAAC,IAAI,GAAE,eAAoB,GAAG,OAAO,CAwF5D"}
|
package/dist/buildCli.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `buildCli({ profile? })` — assemble the dev-cockpit commander program.
|
|
3
|
+
*
|
|
4
|
+
* Registration order: **profile first, core last.** The profile's
|
|
5
|
+
* `setupCli(program)` runs before any core command is registered. When core
|
|
6
|
+
* goes to register a command (`dev`, `doctor`, `init-config`, `mount`),
|
|
7
|
+
* it skips any name the profile already claimed. Profiles override by
|
|
8
|
+
* registering first, not by mutating commander's internal arrays — one
|
|
9
|
+
* mechanism, no escape hatch.
|
|
10
|
+
*/
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
|
+
import { devCommand } from './commands/dev.js';
|
|
16
|
+
import { doctorCommand } from './commands/doctor.js';
|
|
17
|
+
import { initConfigCommand } from './commands/init-config.js';
|
|
18
|
+
import { mountCommand, mountStatusCommand, mountClearCommand, } from './commands/mount.js';
|
|
19
|
+
function readPackageVersion() {
|
|
20
|
+
try {
|
|
21
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const pkg = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf-8'));
|
|
23
|
+
return pkg.version ?? '0.0.0';
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return '0.0.0';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function hasCommand(program, name) {
|
|
30
|
+
return program.commands.some((c) => c.name() === name);
|
|
31
|
+
}
|
|
32
|
+
export function buildCli(opts = {}) {
|
|
33
|
+
const program = new Command();
|
|
34
|
+
const profile = opts.profile;
|
|
35
|
+
const appName = profile?.appName ?? 'dev-cockpit';
|
|
36
|
+
program
|
|
37
|
+
.name(appName)
|
|
38
|
+
.description(profile
|
|
39
|
+
? `${appName} — built on dev-cockpit`
|
|
40
|
+
: 'Generic terminal UI dev cockpit')
|
|
41
|
+
.version(readPackageVersion(), '-V, --version', 'Output the version number');
|
|
42
|
+
// Profile registers first; its commands win by precedence.
|
|
43
|
+
if (profile?.setupCli) {
|
|
44
|
+
profile.setupCli(program);
|
|
45
|
+
}
|
|
46
|
+
// Core commands fill in any names the profile didn't claim.
|
|
47
|
+
if (!hasCommand(program, 'dev')) {
|
|
48
|
+
program
|
|
49
|
+
.command('dev')
|
|
50
|
+
.description('Boot the three-pane TUI (Repos / Output / Health / Help)')
|
|
51
|
+
.option('-c, --config <path>', 'Path to cockpit.yaml', 'cockpit.yaml')
|
|
52
|
+
.action(async (cmdOpts) => {
|
|
53
|
+
await devCommand({ config: cmdOpts.config, profile });
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (!hasCommand(program, 'doctor')) {
|
|
57
|
+
program
|
|
58
|
+
.command('doctor')
|
|
59
|
+
.description('Run all health checks once and print a status table')
|
|
60
|
+
.option('-c, --config <path>', 'Path to cockpit.yaml', 'cockpit.yaml')
|
|
61
|
+
.action(async (cmdOpts) => {
|
|
62
|
+
await doctorCommand({ config: cmdOpts.config, profile });
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (!hasCommand(program, 'init-config')) {
|
|
66
|
+
program
|
|
67
|
+
.command('init-config')
|
|
68
|
+
.description('Write a starter cockpit.yaml in the current directory')
|
|
69
|
+
.option('-f, --force', 'Overwrite an existing cockpit.yaml without prompting', false)
|
|
70
|
+
.option('--with-docker', 'Include an uncommented docker block (static template only)', false)
|
|
71
|
+
.option('-i, --interactive', 'Walk through prompts to populate the file (skips static template)', false)
|
|
72
|
+
.action(async (cmdOpts) => {
|
|
73
|
+
await initConfigCommand({
|
|
74
|
+
force: cmdOpts.force ?? false,
|
|
75
|
+
withDocker: cmdOpts.withDocker ?? false,
|
|
76
|
+
interactive: cmdOpts.interactive ?? false,
|
|
77
|
+
appName,
|
|
78
|
+
profile,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (!hasCommand(program, 'mount')) {
|
|
83
|
+
const mountCmd = program
|
|
84
|
+
.command('mount')
|
|
85
|
+
.description('Generate a Docker compose overlay for bind-mount candidates')
|
|
86
|
+
.option('-c, --config <path>', 'Path to cockpit.yaml', 'cockpit.yaml')
|
|
87
|
+
.option('-s, --service <name>', 'Target compose service (default: first in docker.services)')
|
|
88
|
+
.action(async (cmdOpts) => {
|
|
89
|
+
await mountCommand({ config: cmdOpts.config, service: cmdOpts.service, profile });
|
|
90
|
+
});
|
|
91
|
+
mountCmd
|
|
92
|
+
.command('status')
|
|
93
|
+
.description('Show the active overlay manifest')
|
|
94
|
+
.option('-c, --config <path>', 'Path to cockpit.yaml', 'cockpit.yaml')
|
|
95
|
+
.action(async (cmdOpts) => {
|
|
96
|
+
await mountStatusCommand({ config: cmdOpts.config, profile });
|
|
97
|
+
});
|
|
98
|
+
mountCmd
|
|
99
|
+
.command('clear')
|
|
100
|
+
.description('Remove the overlay file and manifest')
|
|
101
|
+
.option('-c, --config <path>', 'Path to cockpit.yaml', 'cockpit.yaml')
|
|
102
|
+
.action(async (cmdOpts) => {
|
|
103
|
+
await mountClearCommand({ config: cmdOpts.config, profile });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return program;
|
|
107
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level Ink component for the cockpit TUI.
|
|
3
|
+
*
|
|
4
|
+
* Layout:
|
|
5
|
+
* ┌──────────────────────────────────────────────┐
|
|
6
|
+
* │ TabBar (tabs + global key legend) │
|
|
7
|
+
* ├──────────────────────────────────────────────┤
|
|
8
|
+
* │ Active pane OR active modal (full area) │
|
|
9
|
+
* ├──────────────────────────────────────────────┤
|
|
10
|
+
* │ Footer (per-tab contextual hints) │
|
|
11
|
+
* └──────────────────────────────────────────────┘
|
|
12
|
+
*
|
|
13
|
+
* Modals render IN PLACE of the active pane (opaque, fills available height).
|
|
14
|
+
*
|
|
15
|
+
* Domain handlers (repo action, lint, open-error) flow in via props and are
|
|
16
|
+
* routed through useGlobalKeys. Health remediation is owned by the `health`
|
|
17
|
+
* option — useHealth produces the runRemediation dispatcher (one mechanism).
|
|
18
|
+
*/
|
|
19
|
+
import React from 'react';
|
|
20
|
+
import { type FooterLegends } from './Footer.js';
|
|
21
|
+
import { type UseGlobalKeysOptions } from './hooks/useGlobalKeys.js';
|
|
22
|
+
import { type UseHealthOptions } from '../health/useHealth.js';
|
|
23
|
+
export type CockpitProps = Omit<UseGlobalKeysOptions, 'onQuit' | 'runRemediation'> & {
|
|
24
|
+
/** Per-tab footer legend overrides. */
|
|
25
|
+
footerLegends?: FooterLegends;
|
|
26
|
+
/**
|
|
27
|
+
* Enables the integrated health framework. When provided, useHealth manages
|
|
28
|
+
* the scheduler, transitions, notifications, and remediation dispatch.
|
|
29
|
+
*/
|
|
30
|
+
health?: UseHealthOptions;
|
|
31
|
+
};
|
|
32
|
+
export declare function Cockpit(props: CockpitProps): React.ReactElement;
|
|
33
|
+
//# sourceMappingURL=Cockpit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Cockpit.d.ts","sourceRoot":"","sources":["../../src/cockpit/Cockpit.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAOnD,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEpF,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1E,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,oBAAoB,EAAE,QAAQ,GAAG,gBAAgB,CAAC,GAAG;IACnF,uCAAuC;IACvC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B,CAAC;AAEF,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,KAAK,CAAC,YAAY,CAkD/D"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Top-level Ink component for the cockpit TUI.
|
|
4
|
+
*
|
|
5
|
+
* Layout:
|
|
6
|
+
* ┌──────────────────────────────────────────────┐
|
|
7
|
+
* │ TabBar (tabs + global key legend) │
|
|
8
|
+
* ├──────────────────────────────────────────────┤
|
|
9
|
+
* │ Active pane OR active modal (full area) │
|
|
10
|
+
* ├──────────────────────────────────────────────┤
|
|
11
|
+
* │ Footer (per-tab contextual hints) │
|
|
12
|
+
* └──────────────────────────────────────────────┘
|
|
13
|
+
*
|
|
14
|
+
* Modals render IN PLACE of the active pane (opaque, fills available height).
|
|
15
|
+
*
|
|
16
|
+
* Domain handlers (repo action, lint, open-error) flow in via props and are
|
|
17
|
+
* routed through useGlobalKeys. Health remediation is owned by the `health`
|
|
18
|
+
* option — useHealth produces the runRemediation dispatcher (one mechanism).
|
|
19
|
+
*/
|
|
20
|
+
import { useEffect, useState } from 'react';
|
|
21
|
+
import { Box, useApp, useStdout } from 'ink';
|
|
22
|
+
import { Repos } from './panes/Repos.js';
|
|
23
|
+
import { Output } from './panes/Output.js';
|
|
24
|
+
import { Health } from './panes/Health.js';
|
|
25
|
+
import { Help } from './panes/Help.js';
|
|
26
|
+
import { TabBar } from './TabBar.js';
|
|
27
|
+
import { Footer } from './Footer.js';
|
|
28
|
+
import { FilterModal } from './panes/FilterModal.js';
|
|
29
|
+
import { SearchModal } from './panes/SearchModal.js';
|
|
30
|
+
import { useGlobalKeys } from './hooks/useGlobalKeys.js';
|
|
31
|
+
import { useCockpitStore } from './hooks/useCockpitStore.js';
|
|
32
|
+
import { useHealth } from '../health/useHealth.js';
|
|
33
|
+
export function Cockpit(props) {
|
|
34
|
+
const { health, footerLegends, ...rest } = props;
|
|
35
|
+
const { exit } = useApp();
|
|
36
|
+
const { stdout } = useStdout();
|
|
37
|
+
const focus = useCockpitStore((s) => s.focus);
|
|
38
|
+
const activeModal = useCockpitStore((s) => s.activeModal);
|
|
39
|
+
const [rows, setRows] = useState(stdout.rows ?? 24);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const onResize = () => setRows(stdout.rows ?? 24);
|
|
42
|
+
stdout.on('resize', onResize);
|
|
43
|
+
return () => {
|
|
44
|
+
stdout.off('resize', onResize);
|
|
45
|
+
};
|
|
46
|
+
}, [stdout]);
|
|
47
|
+
const healthHandle = useHealth(health);
|
|
48
|
+
useGlobalKeys({
|
|
49
|
+
onQuit: exit,
|
|
50
|
+
...rest,
|
|
51
|
+
runRemediation: healthHandle?.runRemediation,
|
|
52
|
+
});
|
|
53
|
+
let mainContent;
|
|
54
|
+
if (activeModal === 'filter') {
|
|
55
|
+
mainContent = _jsx(FilterModal, {});
|
|
56
|
+
}
|
|
57
|
+
else if (activeModal === 'search') {
|
|
58
|
+
mainContent = _jsx(SearchModal, {});
|
|
59
|
+
}
|
|
60
|
+
else if (focus === 'repos') {
|
|
61
|
+
mainContent = _jsx(Repos, {});
|
|
62
|
+
}
|
|
63
|
+
else if (focus === 'output') {
|
|
64
|
+
mainContent = _jsx(Output, {});
|
|
65
|
+
}
|
|
66
|
+
else if (focus === 'health') {
|
|
67
|
+
mainContent = _jsx(Health, {});
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
mainContent = _jsx(Help, {});
|
|
71
|
+
}
|
|
72
|
+
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsx(TabBar, {}), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: mainContent }), _jsx(Footer, { legends: footerLegends })] }));
|
|
73
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Footer — per-tab contextual hotkey hints.
|
|
3
|
+
*
|
|
4
|
+
* TabBar owns the global navigation legend (tab cycle); the Footer only
|
|
5
|
+
* shows what the user can do INSIDE the currently active tab.
|
|
6
|
+
*
|
|
7
|
+
* Legends are pluggable via props so profiles can override the default
|
|
8
|
+
* verbiage. Defaults are the generic shapes that apply to every consumer.
|
|
9
|
+
*/
|
|
10
|
+
import React from 'react';
|
|
11
|
+
export interface FooterLegends {
|
|
12
|
+
reposRepo?: string;
|
|
13
|
+
reposDocker?: string;
|
|
14
|
+
output?: string;
|
|
15
|
+
health?: string;
|
|
16
|
+
help?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface FooterProps {
|
|
19
|
+
legends?: FooterLegends;
|
|
20
|
+
}
|
|
21
|
+
export declare function Footer({ legends }?: FooterProps): React.ReactElement;
|
|
22
|
+
//# sourceMappingURL=Footer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../src/cockpit/Footer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAW1B,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,wBAAgB,MAAM,CAAC,EAAE,OAAO,EAAE,GAAE,WAAgB,GAAG,KAAK,CAAC,YAAY,CA2BxE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { useCockpitStore } from './hooks/useCockpitStore.js';
|
|
4
|
+
const DEFAULT_REPOS_LEGEND_REPO = '[↑↓] select [r] action [w] watch [l] lint';
|
|
5
|
+
const DEFAULT_REPOS_LEGEND_DOCKER = '[↑↓] select [r] restart';
|
|
6
|
+
const DEFAULT_OUTPUT_LEGEND = '[c] clear [/] search [f] filter';
|
|
7
|
+
const DEFAULT_HEALTH_LEGEND = '[↑↓] select [enter] details [letter] fix';
|
|
8
|
+
const DEFAULT_HELP_LEGEND = 'read-only — see TabBar for navigation';
|
|
9
|
+
export function Footer({ legends } = {}) {
|
|
10
|
+
const focus = useCockpitStore((s) => s.focus);
|
|
11
|
+
const reposLegend = useCockpitStore((s) => {
|
|
12
|
+
const key = s.repoOrder[s.selectedRepoIndex];
|
|
13
|
+
const entry = key ? s.repos[key] : undefined;
|
|
14
|
+
if (entry?.kind === 'docker') {
|
|
15
|
+
return legends?.reposDocker ?? DEFAULT_REPOS_LEGEND_DOCKER;
|
|
16
|
+
}
|
|
17
|
+
return legends?.reposRepo ?? DEFAULT_REPOS_LEGEND_REPO;
|
|
18
|
+
});
|
|
19
|
+
let legend;
|
|
20
|
+
if (focus === 'repos') {
|
|
21
|
+
legend = reposLegend;
|
|
22
|
+
}
|
|
23
|
+
else if (focus === 'output') {
|
|
24
|
+
legend = legends?.output ?? DEFAULT_OUTPUT_LEGEND;
|
|
25
|
+
}
|
|
26
|
+
else if (focus === 'health') {
|
|
27
|
+
legend = legends?.health ?? DEFAULT_HEALTH_LEGEND;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
legend = legends?.help ?? DEFAULT_HELP_LEGEND;
|
|
31
|
+
}
|
|
32
|
+
return (_jsx(Box, { borderStyle: "single", paddingX: 1, children: _jsx(Text, { dimColor: true, children: legend }) }));
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TabBar.d.ts","sourceRoot":"","sources":["../../src/cockpit/TabBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,wBAAgB,MAAM,IAAI,KAAK,CAAC,YAAY,CAwB3C"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { useCockpitStore } from './hooks/useCockpitStore.js';
|
|
4
|
+
import { TAB_ORDER, TAB_LABELS } from './tab-state.js';
|
|
5
|
+
export function TabBar() {
|
|
6
|
+
const focus = useCockpitStore((s) => s.focus);
|
|
7
|
+
return (_jsxs(Box, { borderStyle: "single", paddingX: 1, flexDirection: "row", children: [_jsx(Box, { flexDirection: "row", children: TAB_ORDER.map((tab) => {
|
|
8
|
+
const isActive = focus === tab;
|
|
9
|
+
const label = TAB_LABELS[tab];
|
|
10
|
+
return (_jsx(Box, { marginRight: 2, children: _jsxs(Text, { inverse: isActive, bold: isActive, color: isActive ? 'cyan' : undefined, children: [' ', label, ' '] }) }, tab));
|
|
11
|
+
}) }), _jsx(Box, { flexGrow: 1, justifyContent: "flex-end", children: _jsx(Text, { dimColor: true, children: "[\u2190\u2192 / tab] cycle tabs" }) })] }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compiled-in fallback for the Help tab.
|
|
3
|
+
*
|
|
4
|
+
* Source of truth is `docs/` (loaded at runtime by `loader.ts`). This
|
|
5
|
+
* fallback only renders when the loader can't locate any docs/ — typically
|
|
6
|
+
* a published tarball that forgot to whitelist the folder, or a dev build
|
|
7
|
+
* run from an unusual working directory.
|
|
8
|
+
*/
|
|
9
|
+
import type { HelpPage } from './types.js';
|
|
10
|
+
/** A pre-rendered fallback page. The pane shows this when no docs/ can be located. */
|
|
11
|
+
export declare const FALLBACK_PAGE: HelpPage;
|
|
12
|
+
//# sourceMappingURL=content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../../src/cockpit/help/content.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,sFAAsF;AACtF,eAAO,MAAM,aAAa,EAAE,QAY3B,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compiled-in fallback for the Help tab.
|
|
3
|
+
*
|
|
4
|
+
* Source of truth is `docs/` (loaded at runtime by `loader.ts`). This
|
|
5
|
+
* fallback only renders when the loader can't locate any docs/ — typically
|
|
6
|
+
* a published tarball that forgot to whitelist the folder, or a dev build
|
|
7
|
+
* run from an unusual working directory.
|
|
8
|
+
*/
|
|
9
|
+
/** A pre-rendered fallback page. The pane shows this when no docs/ can be located. */
|
|
10
|
+
export const FALLBACK_PAGE = {
|
|
11
|
+
slug: 'fallback',
|
|
12
|
+
title: 'Help (fallback)',
|
|
13
|
+
path: '',
|
|
14
|
+
body: [
|
|
15
|
+
'# Help is unavailable',
|
|
16
|
+
'',
|
|
17
|
+
'The Help tab usually loads markdown from the `docs/` folder bundled with the package.',
|
|
18
|
+
'You are seeing this fallback because the loader could not find `docs/index.md`.',
|
|
19
|
+
'Check that `docs/` ships alongside `dist/` in the install, or that the consumer profile',
|
|
20
|
+
'configured `helpSources` correctly.',
|
|
21
|
+
].join('\n'),
|
|
22
|
+
};
|