dev-cockpit 0.1.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.
Files changed (153) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +86 -29
  3. package/bin/dev-cockpit.mjs +26 -4
  4. package/dist/actions/builtin.d.ts +25 -0
  5. package/dist/actions/builtin.d.ts.map +1 -0
  6. package/dist/actions/dispatch.d.ts +21 -0
  7. package/dist/actions/dispatch.d.ts.map +1 -0
  8. package/dist/actions/registry.d.ts +11 -0
  9. package/dist/actions/registry.d.ts.map +1 -0
  10. package/dist/actions/types.d.ts +76 -0
  11. package/dist/actions/types.d.ts.map +1 -0
  12. package/dist/buildCli.d.ts.map +1 -1
  13. package/dist/chunk-6XGHLLYT.js +46 -0
  14. package/dist/chunk-6XGHLLYT.js.map +7 -0
  15. package/dist/chunk-C4GFJDMG.js +79 -0
  16. package/dist/chunk-C4GFJDMG.js.map +7 -0
  17. package/dist/chunk-Q6677JQF.js +32609 -0
  18. package/dist/chunk-Q6677JQF.js.map +7 -0
  19. package/dist/chunk-VN6UILQW.js +1460 -0
  20. package/dist/chunk-VN6UILQW.js.map +7 -0
  21. package/dist/cockpit/Cockpit.d.ts +6 -0
  22. package/dist/cockpit/Cockpit.d.ts.map +1 -1
  23. package/dist/cockpit/Footer.d.ts +6 -4
  24. package/dist/cockpit/Footer.d.ts.map +1 -1
  25. package/dist/cockpit/TabBar.d.ts.map +1 -1
  26. package/dist/cockpit/hooks/useGlobalKeys.d.ts +15 -15
  27. package/dist/cockpit/hooks/useGlobalKeys.d.ts.map +1 -1
  28. package/dist/cockpit/hooks/useTerminalWidth.d.ts +12 -0
  29. package/dist/cockpit/hooks/useTerminalWidth.d.ts.map +1 -0
  30. package/dist/cockpit/panes/CommandModal.d.ts +18 -0
  31. package/dist/cockpit/panes/CommandModal.d.ts.map +1 -0
  32. package/dist/cockpit/panes/Help.d.ts.map +1 -1
  33. package/dist/cockpit/panes/Output.d.ts +7 -0
  34. package/dist/cockpit/panes/Output.d.ts.map +1 -1
  35. package/dist/cockpit/panes/Repos.d.ts.map +1 -1
  36. package/dist/cockpit/state/store.d.ts +14 -11
  37. package/dist/cockpit/state/store.d.ts.map +1 -1
  38. package/dist/cockpit/tab-state.d.ts +12 -0
  39. package/dist/cockpit/tab-state.d.ts.map +1 -1
  40. package/dist/commands/dev.d.ts.map +1 -1
  41. package/dist/commands/doctor.d.ts.map +1 -1
  42. package/dist/commands/init-config-wizard.d.ts +103 -2
  43. package/dist/commands/init-config-wizard.d.ts.map +1 -1
  44. package/dist/commands/init-config.d.ts +2 -0
  45. package/dist/commands/init-config.d.ts.map +1 -1
  46. package/dist/commands/link.d.ts +20 -0
  47. package/dist/commands/link.d.ts.map +1 -0
  48. package/dist/commands/migrate-config.d.ts +18 -0
  49. package/dist/commands/migrate-config.d.ts.map +1 -0
  50. package/dist/commands/mount.d.ts +17 -32
  51. package/dist/commands/mount.d.ts.map +1 -1
  52. package/dist/core/config-discovery.d.ts +39 -0
  53. package/dist/core/config-discovery.d.ts.map +1 -0
  54. package/dist/core/config.d.ts +73 -5
  55. package/dist/core/config.d.ts.map +1 -1
  56. package/dist/core/manifest.d.ts +47 -0
  57. package/dist/core/manifest.d.ts.map +1 -0
  58. package/dist/core/migrations.d.ts +33 -0
  59. package/dist/core/migrations.d.ts.map +1 -0
  60. package/dist/core/subprocess.d.ts +20 -0
  61. package/dist/core/subprocess.d.ts.map +1 -1
  62. package/dist/core/types.d.ts +36 -12
  63. package/dist/core/types.d.ts.map +1 -1
  64. package/dist/devtools-YXMW6JJ6.js +3720 -0
  65. package/dist/devtools-YXMW6JJ6.js.map +7 -0
  66. package/dist/docker/highlights.d.ts +14 -4
  67. package/dist/docker/highlights.d.ts.map +1 -1
  68. package/dist/docker/logs.d.ts +3 -2
  69. package/dist/docker/logs.d.ts.map +1 -1
  70. package/dist/health/builtin.d.ts.map +1 -1
  71. package/dist/index.d.ts +14 -3
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +92944 -53
  74. package/dist/index.js.map +7 -0
  75. package/dist/ink.js +38 -1
  76. package/dist/ink.js.map +7 -0
  77. package/dist/link-HXNII7EU.js +65 -0
  78. package/dist/link-HXNII7EU.js.map +7 -0
  79. package/dist/mount/compose.d.ts +21 -0
  80. package/dist/mount/compose.d.ts.map +1 -0
  81. package/dist/mount/discovery.d.ts +35 -0
  82. package/dist/mount/discovery.d.ts.map +1 -0
  83. package/dist/mount/git-status.d.ts +12 -0
  84. package/dist/mount/git-status.d.ts.map +1 -0
  85. package/dist/mount/manifest.d.ts +16 -0
  86. package/dist/mount/manifest.d.ts.map +1 -0
  87. package/dist/mount/symlinks.d.ts +30 -0
  88. package/dist/mount/symlinks.d.ts.map +1 -0
  89. package/dist/mount/types.d.ts +60 -0
  90. package/dist/mount/types.d.ts.map +1 -0
  91. package/dist/react.js +35 -1
  92. package/dist/react.js.map +7 -0
  93. package/dist/runCockpit.d.ts +3 -0
  94. package/dist/runCockpit.d.ts.map +1 -1
  95. package/docs/commands.md +29 -16
  96. package/docs/config-reference.md +115 -11
  97. package/docs/getting-started.md +9 -6
  98. package/docs/index.md +5 -1
  99. package/docs/init-config.md +34 -8
  100. package/docs/mount.md +198 -25
  101. package/docs/notifications.md +14 -13
  102. package/docs/panes.md +36 -15
  103. package/docs/processes.md +42 -0
  104. package/package.json +93 -90
  105. package/dist/buildCli.js +0 -107
  106. package/dist/cli.js +0 -2
  107. package/dist/cockpit/Cockpit.js +0 -73
  108. package/dist/cockpit/Footer.js +0 -33
  109. package/dist/cockpit/TabBar.js +0 -12
  110. package/dist/cockpit/help/content.js +0 -22
  111. package/dist/cockpit/help/loader.js +0 -118
  112. package/dist/cockpit/help/renderer.js +0 -35
  113. package/dist/cockpit/help/types.js +0 -1
  114. package/dist/cockpit/hooks/useCockpitStore.js +0 -5
  115. package/dist/cockpit/hooks/useGlobalKeys.js +0 -173
  116. package/dist/cockpit/panes/FilterModal.js +0 -22
  117. package/dist/cockpit/panes/Health.js +0 -30
  118. package/dist/cockpit/panes/Help.js +0 -81
  119. package/dist/cockpit/panes/Output.js +0 -108
  120. package/dist/cockpit/panes/Repos.js +0 -48
  121. package/dist/cockpit/panes/SearchModal.js +0 -31
  122. package/dist/cockpit/state/store.js +0 -111
  123. package/dist/cockpit/tab-state.js +0 -7
  124. package/dist/commands/dev.js +0 -158
  125. package/dist/commands/doctor.js +0 -66
  126. package/dist/commands/init-config-wizard.js +0 -818
  127. package/dist/commands/init-config.js +0 -131
  128. package/dist/commands/mount.js +0 -150
  129. package/dist/core/config.js +0 -152
  130. package/dist/core/logger.js +0 -38
  131. package/dist/core/notifier.js +0 -100
  132. package/dist/core/paths.js +0 -18
  133. package/dist/core/subprocess.js +0 -82
  134. package/dist/core/types.js +0 -1
  135. package/dist/docker/highlights.js +0 -79
  136. package/dist/docker/logs.js +0 -172
  137. package/dist/docker/restart.js +0 -45
  138. package/dist/docker/stack-trace.js +0 -44
  139. package/dist/health/builtin.js +0 -144
  140. package/dist/health/context.js +0 -31
  141. package/dist/health/notify-resolver.js +0 -28
  142. package/dist/health/registry.js +0 -64
  143. package/dist/health/remediations.js +0 -41
  144. package/dist/health/runner.js +0 -22
  145. package/dist/health/scheduler.js +0 -107
  146. package/dist/health/types.js +0 -1
  147. package/dist/health/useHealth.js +0 -122
  148. package/dist/lint/reactive.js +0 -131
  149. package/dist/runCockpit.js +0 -75
  150. package/dist/watchers/manager.js +0 -239
  151. package/dist/watchers/path-mapper.js +0 -29
  152. package/dist/watchers/types.js +0 -9
  153. package/docs/watchers.md +0 -27
package/CHANGELOG.md CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
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
4
 
5
+ ## [0.2.2] — 2026-05-12
6
+
7
+ External `cockpit.yaml` — the config can now live anywhere, with a per-machine registry mapping workspaces to their YAML file.
8
+
9
+ ### Added
10
+
11
+ - **`cockpit.yaml` discovery chain.** Resolves in this order: `--config <path>` CLI flag → `DEV_COCKPIT_CONFIG` env var → `<cwd>/cockpit.yaml` → `$XDG_DATA_HOME/dev-cockpit/manifest.json` lookup keyed by canonical `cwd` → friendly error pointing at `dev-cockpit link`. Lets the config live outside the workspace without losing the "just run from the workspace root" UX.
12
+ - **`dev-cockpit link` subcommand tree.** `link <config-path>` registers the current directory in the manifest; `link list` prints all mappings sorted by workspace; `link remove` unregisters the current directory. Atomic write (`.tmp` + rename), symlink-canonical keys (so `~/work` and `/Volumes/.../work` hash to the same entry).
13
+ - **`init-config -o, --output <path>`.** Write the starter config anywhere, not just `<cwd>/cockpit.yaml`. Honours the existing `cwd` parameter as the anchor when `--output` is relative.
14
+ - **`DEV_COCKPIT_CONFIG` env var** picked up by all commands as the second-precedence fallback.
15
+ - **`src/core/manifest.ts`** — read / lookup / atomic write of the project→config map. Defensive: returns `null` for missing / malformed / wrong-version files (treated as "not registered yet" rather than an error).
16
+ - **`src/core/config-discovery.ts`** — pure resolver returning `{ configPath, workspaceRoot, source }`. Profile `discoverer` hook still overrides `workspaceRoot` (e.g. awc-cockpit walks up to find the wrapper); generic users get `canonical(cwd)`.
17
+
18
+ ### Changed
19
+
20
+ - **`dev` / `doctor` / `mount` (+ `status`, `clear`) now derive workspace from `canonical(cwd)`**, not `path.dirname(configPath)`. So `--config /tmp/x.yaml` from inside a wrapper keeps the workspace as the wrapper — the whole point of external configs.
21
+ - **`-c, --config` no longer defaults to `'cockpit.yaml'`** at the Commander level. Default is `undefined` so the discovery chain sees the "no explicit flag" signal and walks the precedence list. Help text describes the fallback chain.
22
+
23
+ ### Fixed
24
+
25
+ - **CI publish workflow.** `npm install -g npm@latest` on Node 22's bundled npm could leave global state where `Cannot find module 'promise-retry'` fires from the half-installed npm. Switched the publish workflow to Node 24, which ships npm 11.x natively (satisfies the npm Trusted Publishing >= 11.5.1 requirement without a self-upgrade).
26
+
27
+ ## [0.2.1] — 2026-05-11
28
+
29
+ Post-1.0 roadmap landing: action registry, processes (renamed from watchers), repos pane redesign, profile wizardSteps, accessibility (severity glyph + NO_COLOR), narrow-terminal responsiveness, lift-mount + bundled dist, plus a wave of fit-and-finish.
30
+
31
+ ### Added
32
+
33
+ - **Action registry** with `:` command palette. `ActionConfig` has `id`, `label`, `scope` (`global` | `repos` | `repos:<id>`), `key` (single char), and a programmatic `invoke` callback. Replaces the legacy Repos `r/w/l` hardcoding — the Footer derives its legend from the registry, so profile-defined actions show up automatically.
34
+ - **`Profile.wizardSteps` extension point.** Profiles inject custom steps into `init-config -i` (e.g. awc-cockpit asks for `gitlab.host` + `gitlab.topic` during the wizard).
35
+ - **`defaultPane` config field** (`repos | output | health | help`). Profile-overridable.
36
+ - **Processes (renamed from watchers).** `config.processes[]` is the new spelling; old `config.watchers[]` is auto-migrated via the new schema migration tooling on load. `CONFIG_VERSION` bumped 1 → 2.
37
+ - **`dev-cockpit migrate-config`** persists the in-memory v1 → v2 migration to disk.
38
+ - **Repos pane redesign.** Split layout: row list on the left, per-row action panel on the right. Process-kind rows with built-in `r → restart` actions wired through the action registry. Grouped list (Repos / Docker / Processes subheadings) for navigability.
39
+ - **`dev-cockpit doctor --paths`** prints state-dir paths in `KEY=PATH` form (eval-friendly).
40
+ - **`subprocess` tag lookup.** `spawnStream({ tag })` registers the handle in a `BY_TAG` map; `findSpawnedByTag(tag)` / `killSpawnedByTag(tag)` look it up. Used by per-process restart actions.
41
+ - **`spawnStream` stdin option.** Opt-in `stdin?: 'pipe' | 'ignore' | 'inherit'` on `SpawnOptions` — pass `'ignore'` for non-interactive subprocesses so any `read`/prompt in the child gets EOF instead of blocking forever on an unwritten pipe.
42
+ - **Severity glyph** at the start of every Output line — accessibility for users who run with `NO_COLOR=1`.
43
+ - **`NO_COLOR` full-strip** — every ANSI escape is filtered when the env var is set.
44
+ - **Narrow-terminal responsiveness.** `useTerminalWidth` hook; TabBar / Help / Repos collapse cleanly under ~200 cols (legend says `j/k` instead of arrows when collapsed).
45
+ - **Wizard auto-seeds `actions[]`** from `package.json` scripts during `init-config -i`.
46
+ - **Smart defaults** for health-check `id` + `label` in the wizard (derived from check type + target).
47
+ - **Container-running health predicate uses `docker ps --filter name=<value> --format {{.State}}`** — compose-aware naming, so `name: laravel.test` matches `crossroads-laravel.test-1`.
48
+ - **Engine guard.** Friendly error message when Node < 20.12.
49
+
50
+ ### Changed
51
+
52
+ - **`Repos` pane label → `Targets`** (display only; internal enum stays `repos`).
53
+ - **Mount machinery lifted into dev-cockpit core.** `mountCandidatesProvider` + `mountSymlinks` + `onMountApply` / `onMountClear` hooks on `Profile`; awc-cockpit kept its own `mount` command for its richer interactive UX (composer-aware clear semantics).
54
+ - **Bundled dist.** esbuild produces a single self-contained `dist/index.js` + helpers; consumer installs no longer need to resolve dev-cockpit's full source tree.
55
+ - **OIDC trusted publishing** on the publish workflow. No more `NODE_AUTH_TOKEN` — npm verifies the GitHub OIDC token issued by the workflow's `id-token: write` permission.
56
+
57
+ ### Fixed
58
+
59
+ - **`killSpawnedByTag` race.** Promise.race against the exitCode + a SIGKILL guard could return before the `BY_TAG` finalizer ran on slower CI runners. Now awaits `exitCode` unconditionally after the race so the finalizer completes before return.
60
+ - **Repos pane title duplication** — dropped the redundant pane title; TabBar already labels the tab.
61
+ - **Wizard Repos step copy** — reflects the post-redesign reality (action panel + grouped list).
62
+
5
63
  ## [0.1.0] — 2026-05-10
6
64
 
7
65
  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.
package/README.md CHANGED
@@ -3,26 +3,29 @@
3
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
4
 
5
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
- └───────────────────────────────────────────────────────────────────────────┘
6
+ ┌── Targets ── Output ── Health ── Help ────────────────────────────────────┐
7
+ Targets Actions for `web` (docker) │
8
+ web [docker] [r] Restart web (default) │
9
+ ticker [process] [L] Tail logs │
10
+ api
11
+
12
+
13
+ └────────────────────────────────┴────────────────────────────────────────┘
14
+ [↑↓] select [r] restart [L] tail
15
15
  ```
16
16
 
17
17
  ## What's in the box
18
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.
19
+ - **Four-pane TUI** — Targets / Output / Health / Help, tab-cycle navigation, filter + search modals, narrow-terminal layouts.
20
+ - **Processes** — long-running commands spawned once on boot; stdout/stderr streamed into Output, tagged by id, with shell-out execution so pipes/redirects/quoted args just work.
21
+ - **Docker log tailing** — stream `docker compose logs -f <services>` into Output with regex-driven highlights (configurable severity per pattern).
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 + a single-keystroke remediation.
23
+ - **Mount overlay manager** — `dev-cockpit mount` writes a docker-compose overlay + manifest, with an interactive picker, branch / dirty / broken-symlink status table, and profile hooks for restart + dependency-restore on clear.
24
+ - **Actions library** — `config.actions[]` + `Profile.actions` (declarative shell-out OR programmatic `invoke` callbacks). Reach via the vim-style `:` palette or single-keystroke binding scoped to a Targets row.
25
+ - **Profile interface** — drop-in adapter so a domain-specific tool plugs in its discoverer / repos / health / actions / commands / cockpit handlers without forking the shell.
24
26
  - **Live markdown Help** — pages render inside the cockpit; profiles can layer their own docs over the generic ones.
25
27
  - **Native OS notifications** — fire on state transitions only (no spam). Detached helper, never blocks process exit.
28
+ - **A11y** — `NO_COLOR` strips all styles; semantic glyphs (`✓` `⚠` `✗` `●` `○`) carry severity / status info without color.
26
29
 
27
30
  ## Install
28
31
 
@@ -32,29 +35,41 @@ npm install -g dev-cockpit
32
35
 
33
36
  Or `npm link` from a checkout for development.
34
37
 
38
+ Requires Node ≥ 20.12 (chalk + inquirer use `node:util.styleText`). The bin guards this at startup with a friendly error message.
39
+
35
40
  ## Quick start
36
41
 
37
42
  ```sh
38
43
  cd your-project
39
- dev-cockpit init-config -i # interactive wizard, 7 steps with auto-detection
44
+ dev-cockpit init-config -i # interactive wizard, 8 steps with auto-detection
40
45
  dev-cockpit doctor # validate + print initial health
41
46
  dev-cockpit dev # boot the cockpit
42
47
  ```
43
48
 
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.
49
+ The wizard sniffs your project: docker compose services from `compose.yaml`, long-running processes from `package.json` (`dev`, `watch`), one-shot actions from the rest of your scripts (`test`, `build`, `lint`, `format`), workspaces as repo entries. Most prompts just need an enter.
45
50
 
46
51
  ## Configuration
47
52
 
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:
53
+ Single file, `cockpit.yaml` at your project root. Schema enforced by zod (`version: 2`). Full reference: [`docs/config-reference.md`](./docs/config-reference.md). Minimal example:
49
54
 
50
55
  ```yaml
51
- version: 1
56
+ version: 2
52
57
  appName: my-app
53
58
 
54
- watchers:
59
+ processes:
55
60
  - id: vite
56
61
  command: npm run dev
57
62
 
63
+ actions:
64
+ - id: test
65
+ label: Run tests
66
+ command: npm test
67
+ key: t # `:` palette + single-keystroke binding
68
+ - id: lint
69
+ label: Lint
70
+ command: npm run lint
71
+ key: l
72
+
58
73
  docker:
59
74
  composeFile: compose.yaml
60
75
  services:
@@ -76,20 +91,51 @@ notifications:
76
91
  enabled: true
77
92
  ```
78
93
 
94
+ Wrappers upgrading from v1 (pre-rename) configs run `dev-cockpit migrate-config` to persist the v1 → v2 migration (auto-applied in memory on load with a stderr warning).
95
+
96
+ ## Config discovery
97
+
98
+ dev-cockpit resolves cockpit.yaml in this order:
99
+
100
+ 1. `--config <path>` CLI flag
101
+ 2. `DEV_COCKPIT_CONFIG` env var
102
+ 3. `<cwd>/cockpit.yaml`
103
+ 4. `$XDG_DATA_HOME/dev-cockpit/manifest.json` lookup keyed by canonical `cwd`
104
+
105
+ All paths inside cockpit.yaml resolve against the **workspace** (your cwd, or whatever a profile's `discoverer`
106
+ returns) — never against the config file's directory. Lets one config serve multiple wrappers without rewriting
107
+ paths.
108
+
109
+ To use a config that lives outside your project:
110
+
111
+ cd ~/code/my-wrapper
112
+ dev-cockpit link ~/configs/my-wrapper.cockpit.yaml
113
+ dev-cockpit dev # picks up the mapping
114
+
115
+ dev-cockpit link list # show all mappings
116
+ dev-cockpit link remove # unregister the current cwd
117
+
79
118
  ## Profiles
80
119
 
81
120
  A profile is a domain-specific bundle that plugs into dev-cockpit's lifecycle. It contributes any combination of:
82
121
 
83
122
  - `discoverer` — workspace root resolution
84
- - `reposProvider` — Repos-pane entries
123
+ - `reposProvider` — Targets-pane entries (kind: `repo`)
85
124
  - `healthChecks` — pre-built `HealthCheck[]` (programmatic, beyond the five built-ins)
86
125
  - `setupCli` — extra CLI commands (the profile's own `select`, `release`, etc.)
87
126
  - `helpSources` — markdown docs that layer over the generic Help
127
+ - `defaultHelpPage` — landing page slug for the Help tab
128
+ - `defaultPane` — which tab the cockpit lands on (overrides `config.defaultPane`)
88
129
  - `configSchemaExt` — zod schema validating the profile's `profile.<appName>` namespace
130
+ - `actions` — static action defaults merged into the registry
131
+ - `wizardSteps` — extra `init-config -i` steps; output lands under `profile.<appName>:`
89
132
  - `mountCandidatesProvider` — auto-detected bind-mount candidates
90
- - `boot` async lifecycle hook returning Cockpit pane handlers + `subscribeFsEvents` + `cleanup`
133
+ - `mountSymlinks` IDE-facing symlink strategy for mount apply / clear
134
+ - `onMountApply` / `onMountClear` — post-mount lifecycle hooks (restart docker, run installer, etc.)
135
+ - `mountStatusEnricher` — extra columns in `mount status`
136
+ - `boot` — async lifecycle hook returning Cockpit pane handlers + dynamic `actions` (per-row invoke callbacks) + `subscribeFsEvents` + `cleanup`
91
137
 
92
- A profile is its own npm package — its bin entry composes `buildCli({ profile })` and that's it. The skeleton:
138
+ A profile is its own npm package — its bin entry composes `buildCli({ profile })` and that's it.
93
139
 
94
140
  ```ts
95
141
  import type { Profile } from 'dev-cockpit';
@@ -97,11 +143,22 @@ import type { Profile } from 'dev-cockpit';
97
143
  export const myProfile: Profile = {
98
144
  appName: 'my-app',
99
145
  healthChecks: [...],
100
- boot: ({ config, workspaceRoot }) => ({
101
- cockpitHandlers: { runRepoAction, onWatchToggle, onLint, onOpenError },
102
- subscribeFsEvents: (listener) => watcherManager.subscribeFsEvents(listener),
103
- cleanup: () => watcherManager.stop(),
104
- }),
146
+ boot: ({ config, workspaceRoot }) => {
147
+ const manager = new WatcherManager(...);
148
+ manager.start();
149
+ return {
150
+ // Per-repo programmatic actions: closure over the live WatcherManager.
151
+ actions: managedRepos.map((repo) => ({
152
+ id: `repo-rebuild:${repo.name}`,
153
+ label: `Rebuild ${repo.name}`,
154
+ scope: `repos:${repo.name}`,
155
+ key: 'r',
156
+ invoke: () => manager.rebuild(repo.name),
157
+ })),
158
+ subscribeFsEvents: (listener) => manager.subscribeFsEvents(listener),
159
+ cleanup: () => manager.stop(),
160
+ };
161
+ },
105
162
  };
106
163
 
107
164
  // Bin entry — that's it.
@@ -117,7 +174,7 @@ buildCli({ profile: myProfile }).parse(process.argv);
117
174
  | Commands | [`docs/commands.md`](./docs/commands.md) |
118
175
  | `init-config` wizard | [`docs/init-config.md`](./docs/init-config.md) |
119
176
  | Panes | [`docs/panes.md`](./docs/panes.md) |
120
- | Watchers | [`docs/watchers.md`](./docs/watchers.md) |
177
+ | Processes | [`docs/processes.md`](./docs/processes.md) |
121
178
  | Health | [`docs/health.md`](./docs/health.md) |
122
179
  | Mounts | [`docs/mount.md`](./docs/mount.md) |
123
180
  | Notifications | [`docs/notifications.md`](./docs/notifications.md) |
@@ -128,4 +185,4 @@ buildCli({ profile: myProfile }).parse(process.argv);
128
185
 
129
186
  ## Status
130
187
 
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.
188
+ Pre-1.0. The public surface (`Profile` interface, `buildCli`, the four core commands plus `migrate-config`, `cockpit.yaml` schema with `version: 2`) is stable enough for first consumers to depend on; expect minor churn until the rest of the post-1.0 roadmap items land. See `CHANGELOG.md` for what's shipped.
@@ -1,4 +1,30 @@
1
1
  #!/usr/bin/env node
2
+
3
+ // Engine guard. dev-cockpit's import graph (inquirer, ink/@inquirer/core
4
+ // transitives) uses `node:util.styleText` — added in Node 20.12 — at parse
5
+ // time, so older Node fails with a cryptic `SyntaxError: ... does not
6
+ // provide an export named 'styleText'`. Catch it here with a friendly
7
+ // message before any heavy import resolves.
8
+ {
9
+ const [major, minor] = process.versions.node.split('.').map(Number);
10
+ if (major < 20 || (major === 20 && minor < 12)) {
11
+ process.stderr.write(
12
+ `\ndev-cockpit: Node 20.12+ required (you are on ${process.versions.node}).\n` +
13
+ ` Run \`nvm use 22\` (or install Node >= 20.12) and retry.\n\n`,
14
+ );
15
+ process.exit(1);
16
+ }
17
+ }
18
+
19
+ // A11y: honour NO_COLOR strictly. chalk reads NO_COLOR at module-load time
20
+ // and disables foreground/background colours, but it keeps "style" modifiers
21
+ // (bold, inverse, dim) intact. Some terminals still render those visibly.
22
+ // Forcing FORCE_COLOR=0 before any import ensures chalk's level drops to 0,
23
+ // stripping styles too — true monochrome under NO_COLOR.
24
+ if (process.env.NO_COLOR !== undefined && process.env.NO_COLOR !== '') {
25
+ process.env.FORCE_COLOR = '0';
26
+ }
27
+
2
28
  // Filter Node 22's ExperimentalWarning about JSON modules. The warning fires
3
29
  // from a transitive dep (cli-boxes uses `import boxes from './boxes.json' with
4
30
  // {type: 'json'}`) the moment the cockpit shell's import graph resolves. It's
@@ -7,10 +33,6 @@
7
33
  // before cli-boxes loads.
8
34
  const orig = process.emitWarning.bind(process);
9
35
  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
36
  const msg = typeof w === 'string' ? w : (w?.message ?? '');
15
37
  if (/JSON modules/i.test(msg)) return;
16
38
  return orig(w, ...rest);
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Built-in action synthesis.
3
+ *
4
+ * Generates default ActionConfig entries for kinds that have a natural
5
+ * single-keystroke action — docker services and processes both get `r` to
6
+ * restart by default. Users can shadow them by declaring an action with
7
+ * the same scope + key (declared wins during dispatch).
8
+ *
9
+ * Built-in ids are prefixed with `__builtin:` so the executor can detect
10
+ * them and run the right programmatic handler instead of shelling out.
11
+ */
12
+ import type { BaseCockpitConfig } from '../core/config.js';
13
+ import type { ActionConfig } from './types.js';
14
+ export declare const BUILTIN_PREFIX = "__builtin:";
15
+ export declare const BUILTIN_RESTART_DOCKER = "__builtin:restart-docker:";
16
+ export declare const BUILTIN_RESTART_PROCESS = "__builtin:restart-process:";
17
+ /**
18
+ * Synthesize built-in restart actions for every docker service and process
19
+ * declared in the config. Each action lives at `scope: repos:<id>` with
20
+ * `key: 'r'`. Commands carry a sentinel string only; the executor branches
21
+ * on the action's id, not its command.
22
+ */
23
+ export declare function buildBuiltinActions(config: BaseCockpitConfig): ActionConfig[];
24
+ export declare function isBuiltinAction(action: ActionConfig): boolean;
25
+ //# sourceMappingURL=builtin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin.d.ts","sourceRoot":"","sources":["../../src/actions/builtin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,eAAO,MAAM,cAAc,eAAe,CAAC;AAC3C,eAAO,MAAM,sBAAsB,8BAAqC,CAAC;AACzE,eAAO,MAAM,uBAAuB,+BAAsC,CAAC;AAE3E;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,YAAY,EAAE,CA0B7E;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAE7D"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Action dispatch helpers — pure lookup functions used by pane keystroke
3
+ * handlers and the right-hand Repos action panel.
4
+ *
5
+ * Precedence rule: user-declared (non-builtin) wins over built-in on
6
+ * matching (scope, key) collisions. This lets a user shadow the default
7
+ * `r` on a docker/process row with their own command.
8
+ */
9
+ import type { ActionConfig } from './types.js';
10
+ /**
11
+ * Actions that apply when the given row key is selected in Repos. Matches:
12
+ * - scope === `repos:${rowKey}` (row-specific)
13
+ * - scope === 'repos' (any row)
14
+ *
15
+ * Built-ins are placed AFTER declared so callers that take the first match
16
+ * get the user override automatically.
17
+ */
18
+ export declare function actionsForRepoRow(actions: readonly ActionConfig[], rowKey: string): ActionConfig[];
19
+ /** First action in scope matching the keypress, or undefined. */
20
+ export declare function findActionByKey(actions: readonly ActionConfig[], rowKey: string, keypress: string): ActionConfig | undefined;
21
+ //# sourceMappingURL=dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/actions/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,MAAM,EAAE,MAAM,GACb,YAAY,EAAE,CAehB;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAC7B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,YAAY,GAAG,SAAS,CAE1B"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Build the action registry from config + profile sources.
3
+ *
4
+ * Merge rule: config entries win on `id` collision. Profile actions act
5
+ * as defaults the wrapper can override or remove (by re-declaring with
6
+ * the same id and a different command, or by omitting from config to
7
+ * keep the profile default).
8
+ */
9
+ import type { ActionConfig } from './types.js';
10
+ export declare function buildActionRegistry(configActions: readonly ActionConfig[], profileActions?: readonly ActionConfig[]): ActionConfig[];
11
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/actions/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,SAAS,YAAY,EAAE,EACtC,cAAc,GAAE,SAAS,YAAY,EAAO,GAC3C,YAAY,EAAE,CAKhB"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Action subsystem types.
3
+ *
4
+ * An "action" is a named, optionally keystroke-bound shell command surfaced
5
+ * through the cockpit's `:` command palette and/or via direct keypress in a
6
+ * pane (when `key` + `scope` are set). Actions land via two sources merged
7
+ * at boot:
8
+ * 1. config.actions[] in cockpit.yaml — user/wrapper declarations
9
+ * 2. profile.actions — profile-bundle defaults
10
+ *
11
+ * Config entries win on `id` collision.
12
+ *
13
+ * Scope semantics:
14
+ * - 'global' — appears in the `:` palette regardless of focus
15
+ * - 'repos' — appears in `:` AND fires on Repos pane regardless of
16
+ * which row is selected (key dispatch applies to any
17
+ * Repos selection)
18
+ * - 'repos:<id>' — only fires on Repos pane when that specific row is
19
+ * selected; appears in the right-hand action panel
20
+ *
21
+ * If `key` is set, the action is dispatchable by a single keypress in the
22
+ * matching scope. Without `key`, the action is only reachable via `:`.
23
+ */
24
+ /**
25
+ * Action scope string. Runtime values:
26
+ * - 'global'
27
+ * - 'repos'
28
+ * - 'repos:<id>' where <id> matches /^[\w-]+$/
29
+ * Validated at the schema boundary; expressed as plain string in TS to keep
30
+ * the zod inference aligned with the runtime type (template-literal types
31
+ * can't be produced by `.regex()`).
32
+ */
33
+ export type ActionScope = string;
34
+ /** Per-invocation context passed to programmatic action handlers. */
35
+ export interface ActionInvokeContext {
36
+ /** Workspace root resolved by the cockpit's discoverer / config path. */
37
+ workspaceRoot: string;
38
+ /** Action that was matched (the same object the executor received). */
39
+ action: ActionConfig;
40
+ /** The Repos row id that was selected when the action fired, if any. */
41
+ rowKey?: string;
42
+ /** Push a line into the Output pane, tagged by the action's id. */
43
+ log: (line: string) => void;
44
+ /** Push a stderr-shaped warn line into the Output pane. */
45
+ errLog: (line: string) => void;
46
+ }
47
+ export interface ActionConfig {
48
+ /** Stable identifier — used by config-vs-profile dedup. */
49
+ id: string;
50
+ /** Display label in the `:` palette and the Repos action panel. */
51
+ label: string;
52
+ /**
53
+ * Shell command. Executed via `sh -c <command>` (cmd /c on Windows) so
54
+ * users can write pipes, redirects, env-prefixes, and quoted args. Mutually
55
+ * exclusive with `invoke`.
56
+ */
57
+ command?: string;
58
+ /**
59
+ * Programmatic handler — fires instead of shelling out. Profile-only:
60
+ * functions don't survive a YAML round-trip, so `config.actions[]` entries
61
+ * never carry this. Use for actions that mutate in-memory cockpit / profile
62
+ * state (rebuild via WatcherManager, toggle a chokidar watcher, etc.).
63
+ */
64
+ invoke?: (ctx: ActionInvokeContext) => void | Promise<void>;
65
+ /** Working directory; relative paths resolve against workspaceRoot. */
66
+ cwd?: string;
67
+ /** Action visibility / dispatch context. Default: 'global'. */
68
+ scope?: ActionScope;
69
+ /**
70
+ * Optional single-keystroke binding. Matched against the keypress within
71
+ * the action's scope. Case-sensitive at dispatch time so users can declare
72
+ * uppercase-only bindings. If absent, the action is reachable only via `:`.
73
+ */
74
+ key?: string;
75
+ }
76
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/actions/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC,qEAAqE;AACrE,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,MAAM,EAAE,YAAY,CAAC;IACrB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,2DAA2D;IAC3D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,EAAE,EAAE,MAAM,CAAC;IACX,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd"}
@@ -1 +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"}
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;AAW/C,MAAM,WAAW,eAAe;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAkBD,wBAAgB,QAAQ,CAAC,IAAI,GAAE,eAAoB,GAAG,OAAO,CA6I5D"}
@@ -0,0 +1,46 @@
1
+ import { createRequire as __createRequireDC } from 'node:module';
2
+ const require = __createRequireDC(import.meta.url);
3
+
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
11
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
12
+ }) : x)(function(x) {
13
+ if (typeof require !== "undefined") return require.apply(this, arguments);
14
+ throw Error('Dynamic require of "' + x + '" is not supported');
15
+ });
16
+ var __commonJS = (cb, mod) => function __require2() {
17
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
+ };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, { get: all[name], enumerable: true });
22
+ };
23
+ var __copyProps = (to, from, except, desc) => {
24
+ if (from && typeof from === "object" || typeof from === "function") {
25
+ for (let key of __getOwnPropNames(from))
26
+ if (!__hasOwnProp.call(to, key) && key !== except)
27
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
28
+ }
29
+ return to;
30
+ };
31
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
32
+ // If the importer is in node compatibility mode or this is not an ESM
33
+ // file that has been converted to a CommonJS file using a Babel-
34
+ // compatible transform (i.e. "__esModule" has not been set), then set
35
+ // "default" to the CommonJS "module.exports" for node compatibility.
36
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
37
+ mod
38
+ ));
39
+
40
+ export {
41
+ __require,
42
+ __commonJS,
43
+ __export,
44
+ __toESM
45
+ };
46
+ //# sourceMappingURL=chunk-6XGHLLYT.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,79 @@
1
+ import { createRequire as __createRequireDC } from 'node:module';
2
+ const require = __createRequireDC(import.meta.url);
3
+
4
+
5
+ // src/core/manifest.ts
6
+ import fs from "node:fs";
7
+ import os from "node:os";
8
+ import path from "node:path";
9
+ var MANIFEST_VERSION = 1;
10
+ function manifestPath() {
11
+ const xdgDataHome = process.env["XDG_DATA_HOME"] || path.join(os.homedir(), ".local", "share");
12
+ return path.join(xdgDataHome, "dev-cockpit", "manifest.json");
13
+ }
14
+ function canonical(p) {
15
+ const abs = path.resolve(p);
16
+ try {
17
+ return fs.realpathSync(abs);
18
+ } catch {
19
+ return abs;
20
+ }
21
+ }
22
+ function readManifest() {
23
+ const filePath = manifestPath();
24
+ if (!fs.existsSync(filePath)) return null;
25
+ try {
26
+ const raw = JSON.parse(fs.readFileSync(filePath, "utf8"));
27
+ if (raw.version !== MANIFEST_VERSION) return null;
28
+ if (!raw.projects || typeof raw.projects !== "object") return null;
29
+ const projects = {};
30
+ for (const [k, v] of Object.entries(raw.projects)) {
31
+ if (typeof k === "string" && typeof v === "string") projects[k] = v;
32
+ }
33
+ return { version: MANIFEST_VERSION, projects };
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
38
+ function lookupConfig(workspaceRoot) {
39
+ const m = readManifest();
40
+ if (!m) return null;
41
+ return m.projects[canonical(workspaceRoot)] ?? null;
42
+ }
43
+ function writeManifest(m) {
44
+ const filePath = manifestPath();
45
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
46
+ const tmp = `${filePath}.tmp`;
47
+ fs.writeFileSync(tmp, `${JSON.stringify(m, null, 2)}
48
+ `, "utf8");
49
+ fs.renameSync(tmp, filePath);
50
+ }
51
+ function setMapping(workspaceRoot, configPath) {
52
+ const existing = readManifest() ?? { version: MANIFEST_VERSION, projects: {} };
53
+ existing.projects[canonical(workspaceRoot)] = canonical(configPath);
54
+ writeManifest(existing);
55
+ }
56
+ function removeMapping(workspaceRoot) {
57
+ const existing = readManifest();
58
+ if (!existing) return false;
59
+ const key = canonical(workspaceRoot);
60
+ if (!(key in existing.projects)) return false;
61
+ delete existing.projects[key];
62
+ writeManifest(existing);
63
+ return true;
64
+ }
65
+ function listMappings() {
66
+ const m = readManifest();
67
+ if (!m) return [];
68
+ return Object.entries(m.projects).map(([workspace, config]) => ({ workspace, config })).sort((a, b) => a.workspace.localeCompare(b.workspace));
69
+ }
70
+
71
+ export {
72
+ manifestPath,
73
+ canonical,
74
+ lookupConfig,
75
+ setMapping,
76
+ removeMapping,
77
+ listMappings
78
+ };
79
+ //# sourceMappingURL=chunk-C4GFJDMG.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/core/manifest.ts"],
4
+ "sourcesContent": ["/**\n * Read/write the global project\u2192config manifest at\n * `$XDG_DATA_HOME/dev-cockpit/manifest.json`. Keys are canonical\n * absolute paths to a workspace root; values are absolute paths to\n * the cockpit.yaml that workspace should use.\n *\n * Used by core/config-discovery.ts as the last-resort lookup when\n * the workspace doesn't carry its own cockpit.yaml at the root.\n */\n\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst MANIFEST_VERSION = 1 as const;\n\nexport interface Manifest {\n version: typeof MANIFEST_VERSION;\n projects: Record<string, string>;\n}\n\n/** Absolute path of `$XDG_DATA_HOME/dev-cockpit/manifest.json`. */\nexport function manifestPath(): string {\n // `||` (not `??`) so an empty-string env var falls through to the default.\n const xdgDataHome =\n process.env['XDG_DATA_HOME'] || path.join(os.homedir(), '.local', 'share');\n return path.join(xdgDataHome, 'dev-cockpit', 'manifest.json');\n}\n\n/**\n * Canonicalise an absolute path \u2014 resolves symlinks if the path exists,\n * falls back to `path.resolve()` otherwise. Ensures a symlinked workspace\n * dir (`~/work`) and its real target (`/Users/me/code/work`) hash to the\n * same manifest key.\n */\nexport function canonical(p: string): string {\n const abs = path.resolve(p);\n try {\n return fs.realpathSync(abs);\n } catch {\n return abs;\n }\n}\n\n/**\n * Read the manifest. Returns `null` when the file is missing, unreadable,\n * malformed JSON, or doesn't match the expected shape. Callers treat null\n * as \"no mapping registered\" rather than an error \u2014 the user simply\n * hasn't run `dev-cockpit link` yet.\n */\nexport function readManifest(): Manifest | null {\n const filePath = manifestPath();\n if (!fs.existsSync(filePath)) return null;\n try {\n const raw = JSON.parse(fs.readFileSync(filePath, 'utf8')) as Partial<Manifest>;\n if (raw.version !== MANIFEST_VERSION) return null;\n if (!raw.projects || typeof raw.projects !== 'object') return null;\n const projects: Record<string, string> = {};\n for (const [k, v] of Object.entries(raw.projects)) {\n if (typeof k === 'string' && typeof v === 'string') projects[k] = v;\n }\n return { version: MANIFEST_VERSION, projects };\n } catch {\n return null;\n }\n}\n\n/** Look up a workspace's config path. Returns `null` when unmapped. */\nexport function lookupConfig(workspaceRoot: string): string | null {\n const m = readManifest();\n if (!m) return null;\n return m.projects[canonical(workspaceRoot)] ?? null;\n}\n\n/**\n * Atomic write: serialise to `<file>.tmp` next to the manifest, then\n * rename onto the real path. Creates the parent dir if missing. Same\n * pattern used by dev-cockpit's mount-manifest writer.\n */\nfunction writeManifest(m: Manifest): void {\n const filePath = manifestPath();\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const tmp = `${filePath}.tmp`;\n fs.writeFileSync(tmp, `${JSON.stringify(m, null, 2)}\\n`, 'utf8');\n fs.renameSync(tmp, filePath);\n}\n\n/**\n * Register a mapping. Both inputs are canonicalised first \u2014 the\n * workspace key is resolved through symlinks so a `~/work` symlink and\n * `/Users/me/code/work` real path resolve to the same entry.\n */\nexport function setMapping(workspaceRoot: string, configPath: string): void {\n const existing = readManifest() ?? { version: MANIFEST_VERSION, projects: {} };\n existing.projects[canonical(workspaceRoot)] = canonical(configPath);\n writeManifest(existing);\n}\n\n/** Remove a mapping. Returns `true` when an entry was deleted. */\nexport function removeMapping(workspaceRoot: string): boolean {\n const existing = readManifest();\n if (!existing) return false;\n const key = canonical(workspaceRoot);\n if (!(key in existing.projects)) return false;\n delete existing.projects[key];\n writeManifest(existing);\n return true;\n}\n\n/** Sorted mapping list, for `dev-cockpit link list`. */\nexport function listMappings(): Array<{ workspace: string; config: string }> {\n const m = readManifest();\n if (!m) return [];\n return Object.entries(m.projects)\n .map(([workspace, config]) => ({ workspace, config }))\n .sort((a, b) => a.workspace.localeCompare(b.workspace));\n}\n"],
5
+ "mappings": ";;;;;AAUA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB,IAAM,mBAAmB;AAQlB,SAAS,eAAuB;AAErC,QAAM,cACJ,QAAQ,IAAI,eAAe,KAAK,KAAK,KAAK,GAAG,QAAQ,GAAG,UAAU,OAAO;AAC3E,SAAO,KAAK,KAAK,aAAa,eAAe,eAAe;AAC9D;AAQO,SAAS,UAAU,GAAmB;AAC3C,QAAM,MAAM,KAAK,QAAQ,CAAC;AAC1B,MAAI;AACF,WAAO,GAAG,aAAa,GAAG;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,eAAgC;AAC9C,QAAM,WAAW,aAAa;AAC9B,MAAI,CAAC,GAAG,WAAW,QAAQ,EAAG,QAAO;AACrC,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,GAAG,aAAa,UAAU,MAAM,CAAC;AACxD,QAAI,IAAI,YAAY,iBAAkB,QAAO;AAC7C,QAAI,CAAC,IAAI,YAAY,OAAO,IAAI,aAAa,SAAU,QAAO;AAC9D,UAAM,WAAmC,CAAC;AAC1C,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,QAAQ,GAAG;AACjD,UAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SAAU,UAAS,CAAC,IAAI;AAAA,IACpE;AACA,WAAO,EAAE,SAAS,kBAAkB,SAAS;AAAA,EAC/C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,aAAa,eAAsC;AACjE,QAAM,IAAI,aAAa;AACvB,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,SAAS,UAAU,aAAa,CAAC,KAAK;AACjD;AAOA,SAAS,cAAc,GAAmB;AACxC,QAAM,WAAW,aAAa;AAC9B,KAAG,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,QAAM,MAAM,GAAG,QAAQ;AACvB,KAAG,cAAc,KAAK,GAAG,KAAK,UAAU,GAAG,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAC/D,KAAG,WAAW,KAAK,QAAQ;AAC7B;AAOO,SAAS,WAAW,eAAuB,YAA0B;AAC1E,QAAM,WAAW,aAAa,KAAK,EAAE,SAAS,kBAAkB,UAAU,CAAC,EAAE;AAC7E,WAAS,SAAS,UAAU,aAAa,CAAC,IAAI,UAAU,UAAU;AAClE,gBAAc,QAAQ;AACxB;AAGO,SAAS,cAAc,eAAgC;AAC5D,QAAM,WAAW,aAAa;AAC9B,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,MAAM,UAAU,aAAa;AACnC,MAAI,EAAE,OAAO,SAAS,UAAW,QAAO;AACxC,SAAO,SAAS,SAAS,GAAG;AAC5B,gBAAc,QAAQ;AACtB,SAAO;AACT;AAGO,SAAS,eAA6D;AAC3E,QAAM,IAAI,aAAa;AACvB,MAAI,CAAC,EAAG,QAAO,CAAC;AAChB,SAAO,OAAO,QAAQ,EAAE,QAAQ,EAC7B,IAAI,CAAC,CAAC,WAAW,MAAM,OAAO,EAAE,WAAW,OAAO,EAAE,EACpD,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,SAAS,CAAC;AAC1D;",
6
+ "names": []
7
+ }