dev-cockpit 0.2.6 → 0.2.8

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 (58) hide show
  1. package/README.md +32 -32
  2. package/bin/dev-cockpit.mjs +1 -1
  3. package/dist/buildCli.d.ts.map +1 -1
  4. package/dist/{chunk-A446TCT5.js → chunk-YTMK7EXK.js} +1 -1
  5. package/dist/chunk-YTMK7EXK.js.map +7 -0
  6. package/dist/cockpit/Footer.d.ts.map +1 -1
  7. package/dist/cockpit/hooks/useGlobalKeys.d.ts.map +1 -1
  8. package/dist/cockpit/panes/FilterModal.d.ts.map +1 -1
  9. package/dist/cockpit/panes/Output.d.ts.map +1 -1
  10. package/dist/cockpit/panes/Repos.d.ts.map +1 -1
  11. package/dist/cockpit/panes/SearchModal.d.ts.map +1 -1
  12. package/dist/cockpit/state/store.d.ts.map +1 -1
  13. package/dist/commands/dev.d.ts.map +1 -1
  14. package/dist/commands/doctor.d.ts.map +1 -1
  15. package/dist/commands/init-config-wizard.d.ts.map +1 -1
  16. package/dist/commands/init-config.d.ts.map +1 -1
  17. package/dist/commands/link.d.ts.map +1 -1
  18. package/dist/commands/migrate-config.d.ts.map +1 -1
  19. package/dist/commands/mount.d.ts +1 -3
  20. package/dist/commands/mount.d.ts.map +1 -1
  21. package/dist/core/config-discovery.d.ts.map +1 -1
  22. package/dist/core/config.d.ts +27 -6
  23. package/dist/core/config.d.ts.map +1 -1
  24. package/dist/core/manifest.d.ts.map +1 -1
  25. package/dist/core/migrations.d.ts.map +1 -1
  26. package/dist/core/paths.d.ts.map +1 -1
  27. package/dist/core/subprocess.d.ts.map +1 -1
  28. package/dist/core/types.d.ts +16 -1
  29. package/dist/core/types.d.ts.map +1 -1
  30. package/dist/docker/highlights.d.ts.map +1 -1
  31. package/dist/health/builtin.d.ts.map +1 -1
  32. package/dist/index.d.ts +8 -8
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +274 -170
  35. package/dist/index.js.map +3 -3
  36. package/dist/ink.d.ts +1 -1
  37. package/dist/ink.d.ts.map +1 -1
  38. package/dist/{link-VWT2VQH6.js → link-Y7OFHOUP.js} +4 -6
  39. package/dist/link-Y7OFHOUP.js.map +7 -0
  40. package/dist/mount/compose.d.ts.map +1 -1
  41. package/dist/mount/symlinks.d.ts +4 -4
  42. package/dist/mount/symlinks.d.ts.map +1 -1
  43. package/dist/mount/types.d.ts +15 -2
  44. package/dist/mount/types.d.ts.map +1 -1
  45. package/docs/commands.md +8 -8
  46. package/docs/config-reference.md +45 -41
  47. package/docs/health.md +32 -22
  48. package/docs/init-config.md +16 -16
  49. package/docs/mount.md +14 -9
  50. package/docs/notifications.md +3 -3
  51. package/docs/panes.md +5 -5
  52. package/docs/processes.md +7 -5
  53. package/examples/cockpit.schema.json +230 -59
  54. package/examples/cockpit.yaml +8 -6
  55. package/package.json +99 -93
  56. package/prettier-config.json +7 -0
  57. package/dist/chunk-A446TCT5.js.map +0 -7
  58. package/dist/link-VWT2VQH6.js.map +0 -7
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  readManifest,
24
24
  removeMapping,
25
25
  setMapping
26
- } from "./chunk-A446TCT5.js";
26
+ } from "./chunk-YTMK7EXK.js";
27
27
  import {
28
28
  __commonJS,
29
29
  __export,
@@ -4819,7 +4819,7 @@ var require_cross_spawn = __commonJS({
4819
4819
  enoent.hookChildProcess(spawned, parsed);
4820
4820
  return spawned;
4821
4821
  }
4822
- function spawnSync2(command, args, options2) {
4822
+ function spawnSync3(command, args, options2) {
4823
4823
  const parsed = parse(command, args, options2);
4824
4824
  const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
4825
4825
  result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
@@ -4827,7 +4827,7 @@ var require_cross_spawn = __commonJS({
4827
4827
  }
4828
4828
  module.exports = spawn2;
4829
4829
  module.exports.spawn = spawn2;
4830
- module.exports.sync = spawnSync2;
4830
+ module.exports.sync = spawnSync3;
4831
4831
  module.exports._parse = parse;
4832
4832
  module.exports._enoent = enoent;
4833
4833
  }
@@ -83970,9 +83970,13 @@ var NotificationsSchema = external_exports.object({
83970
83970
  });
83971
83971
  var MountSchema = external_exports.object({
83972
83972
  hostPath: external_exports.string(),
83973
- containerPath: external_exports.string(),
83973
+ containerPath: external_exports.string().optional(),
83974
+ kind: external_exports.enum(["bind", "symlink-only"]).optional(),
83974
83975
  meta: external_exports.record(external_exports.unknown()).optional()
83975
- });
83976
+ }).refine(
83977
+ (m) => m.kind === "symlink-only" ? true : typeof m.containerPath === "string" && m.containerPath.length > 0,
83978
+ { message: "Mount with kind='bind' (default) requires containerPath" }
83979
+ );
83976
83980
  var ActionSchema = external_exports.object({
83977
83981
  id: external_exports.string(),
83978
83982
  label: external_exports.string(),
@@ -83994,7 +83998,9 @@ var MountSettingsSchema = external_exports.object({
83994
83998
  */
83995
83999
  overlayPath: external_exports.string().optional(),
83996
84000
  /** Basename inside stateDir for the manifest. */
83997
- manifestFile: external_exports.string().optional().default("mount.manifest.json")
84001
+ manifestFile: external_exports.string().optional().default("mount.manifest.json"),
84002
+ /** Target compose service. Unset = first in docker.services. */
84003
+ service: external_exports.string().optional()
83998
84004
  });
83999
84005
  var BaseCockpitConfigSchema = external_exports.object({
84000
84006
  version: external_exports.number().int(),
@@ -84176,9 +84182,7 @@ var FOCUS_ORDER = ["repos", "output", "health", "help"];
84176
84182
  function filterActions(actions, filter) {
84177
84183
  if (!filter) return actions;
84178
84184
  const f = filter.toLowerCase();
84179
- return actions.filter(
84180
- (a2) => a2.id.toLowerCase().includes(f) || a2.label.toLowerCase().includes(f)
84181
- );
84185
+ return actions.filter((a2) => a2.id.toLowerCase().includes(f) || a2.label.toLowerCase().includes(f));
84182
84186
  }
84183
84187
  var cockpitStore = createStore()((set, get2) => ({
84184
84188
  repos: {},
@@ -84447,7 +84451,7 @@ function Repos() {
84447
84451
  const heading2 = kind === "repo" ? "Repos" : kind === "docker" ? "Docker" : "Processes";
84448
84452
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
84449
84453
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
84450
- " ",
84454
+ " ",
84451
84455
  heading2
84452
84456
  ] }),
84453
84457
  entriesInKind.map(({ key, idx, entry }) => {
@@ -84484,7 +84488,7 @@ function Repos() {
84484
84488
  " ",
84485
84489
  a2.label
84486
84490
  ] }),
84487
- isDefault && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: " (default)" })
84491
+ isDefault && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: " (default)" })
84488
84492
  ] }, a2.id);
84489
84493
  })
84490
84494
  ]
@@ -84585,9 +84589,7 @@ function Output() {
84585
84589
  } else if (key.downArrow) {
84586
84590
  setScrollOffset((prev) => Math.max(0, prev - 1));
84587
84591
  } else if (key.pageUp) {
84588
- setScrollOffset(
84589
- (prev) => Math.min(prev + visibleLines, Math.max(0, total - visibleLines))
84590
- );
84592
+ setScrollOffset((prev) => Math.min(prev + visibleLines, Math.max(0, total - visibleLines)));
84591
84593
  } else if (key.pageDown) {
84592
84594
  setScrollOffset((prev) => Math.max(0, prev - visibleLines));
84593
84595
  } else if (key.return) {
@@ -87647,7 +87649,9 @@ function Footer({ legends } = {}) {
87647
87649
  if (!key) return NAV_HINT;
87648
87650
  const keyed = actionsForRepoRow(s.actions, key).filter((a2) => a2.key);
87649
87651
  if (keyed.length === 0) return NAV_HINT;
87650
- return [NAV_HINT, ...keyed.map((a2) => `[${a2.key}] ${shortLabel(a2.label).toLowerCase()}`)].join(" ");
87652
+ return [NAV_HINT, ...keyed.map((a2) => `[${a2.key}] ${shortLabel(a2.label).toLowerCase()}`)].join(
87653
+ " "
87654
+ );
87651
87655
  });
87652
87656
  let legend;
87653
87657
  if (focus === "repos") {
@@ -87667,7 +87671,9 @@ var import_react5 = __toESM(require_react(), 1);
87667
87671
  var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
87668
87672
  function FilterModal() {
87669
87673
  const outputFilter = useCockpitStore((s) => s.outputFilter);
87670
- const [severityDraft, setSeverityDraft] = (0, import_react5.useState)(outputFilter.severity);
87674
+ const [severityDraft, setSeverityDraft] = (0, import_react5.useState)(
87675
+ outputFilter.severity
87676
+ );
87671
87677
  const SEVERITIES = [void 0, "info", "warn", "error"];
87672
87678
  use_input_default((input, key) => {
87673
87679
  if (input === "s") {
@@ -87680,26 +87686,16 @@ function FilterModal() {
87680
87686
  cockpitStore.getState().setActiveModal(null);
87681
87687
  }
87682
87688
  });
87683
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
87684
- Box_default,
87685
- {
87686
- flexDirection: "column",
87687
- borderStyle: "round",
87688
- borderColor: "cyan",
87689
- paddingX: 2,
87690
- paddingY: 1,
87691
- children: [
87692
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { bold: true, children: " Filter Output " }),
87693
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: " " }),
87694
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { children: [
87695
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: "Severity (s to cycle): " }),
87696
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: "cyan", children: severityDraft ?? "all" })
87697
- ] }),
87698
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: " " }),
87699
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { dimColor: true, children: "Enter = apply \xB7 Esc = cancel" })
87700
- ]
87701
- }
87702
- ) });
87689
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
87690
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { bold: true, children: " Filter Output " }),
87691
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: " " }),
87692
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { children: [
87693
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: "Severity (s to cycle): " }),
87694
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: "cyan", children: severityDraft ?? "all" })
87695
+ ] }),
87696
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: " " }),
87697
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { dimColor: true, children: "Enter = apply \xB7 Esc = cancel" })
87698
+ ] }) });
87703
87699
  }
87704
87700
 
87705
87701
  // src/cockpit/panes/SearchModal.tsx
@@ -87729,29 +87725,19 @@ function SearchModal() {
87729
87725
  setQuery((prev) => prev + input);
87730
87726
  }
87731
87727
  });
87732
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
87733
- Box_default,
87734
- {
87735
- flexDirection: "column",
87736
- borderStyle: "round",
87737
- borderColor: "cyan",
87738
- paddingX: 2,
87739
- paddingY: 1,
87740
- children: [
87741
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { bold: true, children: " Search Output " }),
87742
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: " " }),
87743
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { children: [
87744
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: "Query: " }),
87745
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: "cyan", children: [
87746
- query,
87747
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "gray", children: "\u2588" })
87748
- ] })
87749
- ] }),
87750
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: " " }),
87751
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { dimColor: true, children: "Enter = apply \xB7 Esc = cancel \xB7 Backspace = delete" })
87752
- ]
87753
- }
87754
- ) });
87728
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
87729
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { bold: true, children: " Search Output " }),
87730
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: " " }),
87731
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { children: [
87732
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: "Query: " }),
87733
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: "cyan", children: [
87734
+ query,
87735
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "gray", children: "\u2588" })
87736
+ ] })
87737
+ ] }),
87738
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: " " }),
87739
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { dimColor: true, children: "Enter = apply \xB7 Esc = cancel \xB7 Backspace = delete" })
87740
+ ] }) });
87755
87741
  }
87756
87742
 
87757
87743
  // src/cockpit/panes/CommandModal.tsx
@@ -87906,9 +87892,7 @@ function useGlobalKeys(opts) {
87906
87892
  return;
87907
87893
  }
87908
87894
  if (input === "e" && onOpenError) {
87909
- const target = state.recentErrors.find(
87910
- (er) => er.file && typeof er.line === "number"
87911
- );
87895
+ const target = state.recentErrors.find((er) => er.file && typeof er.line === "number");
87912
87896
  if (target?.file && typeof target.line === "number") {
87913
87897
  onOpenError({
87914
87898
  file: target.file,
@@ -87946,10 +87930,7 @@ function useGlobalKeys(opts) {
87946
87930
  healthId: dispatch.healthId,
87947
87931
  healthLabel: dispatch.healthLabel
87948
87932
  });
87949
- withMinHold(
87950
- dispatch.promise,
87951
- () => cockpitStore.getState().setActiveRemediation(null)
87952
- );
87933
+ withMinHold(dispatch.promise, () => cockpitStore.getState().setActiveRemediation(null));
87953
87934
  return;
87954
87935
  }
87955
87936
  }
@@ -90963,11 +90944,7 @@ async function devCommand(opts = {}) {
90963
90944
  const builtinActions = buildBuiltinActions(config);
90964
90945
  const actions = buildActionRegistry(
90965
90946
  [...config.actions],
90966
- [
90967
- ...builtinActions,
90968
- ...profile?.actions ?? [],
90969
- ...bootResult.actions ?? []
90970
- ]
90947
+ [...builtinActions, ...profile?.actions ?? [], ...bootResult.actions ?? []]
90971
90948
  );
90972
90949
  const [shell, shellFlag] = process.platform === "win32" ? ["cmd", "/c"] : ["/bin/sh", "-c"];
90973
90950
  const highlightMatcher = compileHighlights(config.highlights);
@@ -91040,7 +91017,12 @@ async function devCommand(opts = {}) {
91040
91017
  }
91041
91018
  if (action.id.startsWith(BUILTIN_RESTART_PROCESS)) {
91042
91019
  const procId = action.id.slice(BUILTIN_RESTART_PROCESS.length);
91043
- store.appendOutput({ ts: Date.now(), source: procId, severity: "info", text: `> restart ${procId}` });
91020
+ store.appendOutput({
91021
+ ts: Date.now(),
91022
+ source: procId,
91023
+ severity: "info",
91024
+ text: `> restart ${procId}`
91025
+ });
91044
91026
  await killSpawnedByTag(procId);
91045
91027
  spawnProcessById(procId);
91046
91028
  store.setFocus("output");
@@ -91334,10 +91316,7 @@ function renderWizardYaml(result) {
91334
91316
  if (health.length > 0) blocks.push(health);
91335
91317
  const actions = renderActions(result.actions);
91336
91318
  if (actions.length > 0) blocks.push(actions);
91337
- blocks.push([
91338
- "notifications:",
91339
- indent(`enabled: ${result.notifications.enabled}`, 1)
91340
- ]);
91319
+ blocks.push(["notifications:", indent(`enabled: ${result.notifications.enabled}`, 1)]);
91341
91320
  if (result.profileLines && result.profileLines.length > 0) {
91342
91321
  blocks.push([
91343
91322
  "profile:",
@@ -91406,7 +91385,12 @@ function safeReadJson(p) {
91406
91385
  }
91407
91386
  }
91408
91387
  function detectComposeFile(cwd) {
91409
- for (const candidate of ["compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"]) {
91388
+ for (const candidate of [
91389
+ "compose.yaml",
91390
+ "compose.yml",
91391
+ "docker-compose.yml",
91392
+ "docker-compose.yaml"
91393
+ ]) {
91410
91394
  if (fs11.existsSync(path15.join(cwd, candidate))) return candidate;
91411
91395
  }
91412
91396
  return void 0;
@@ -91444,7 +91428,10 @@ function detectRepoSuggestions(cwd, pkg) {
91444
91428
  }
91445
91429
  for (const glob of workspaceGlobs) {
91446
91430
  for (const entry of expandSimpleGlob(cwd, glob)) {
91447
- out.push({ id: path15.basename(entry), path: path15.relative(cwd, path15.join(cwd, entry)) || "." });
91431
+ out.push({
91432
+ id: path15.basename(entry),
91433
+ path: path15.relative(cwd, path15.join(cwd, entry)) || "."
91434
+ });
91448
91435
  }
91449
91436
  }
91450
91437
  const pnpmFile = path15.join(cwd, "pnpm-workspace.yaml");
@@ -91787,7 +91774,9 @@ async function promptHealthChecks(prompts, hints, hasDocker) {
91787
91774
  ].join("\n")
91788
91775
  );
91789
91776
  const out = [];
91790
- let typeChoices = baseHealthTypeChoices(Boolean(hints.composeFile && hints.composeServices.length > 0));
91777
+ let typeChoices = baseHealthTypeChoices(
91778
+ Boolean(hints.composeFile && hints.composeServices.length > 0)
91779
+ );
91791
91780
  if (!hasDocker) {
91792
91781
  typeChoices = typeChoices.filter((c3) => c3.value !== "container-running");
91793
91782
  }
@@ -91852,7 +91841,14 @@ async function promptOneHealthCheck(prompts, type, hints) {
91852
91841
  })).trim();
91853
91842
  idDefault = `${slugify(container)}-up`;
91854
91843
  labelDefault = `Container \`${container}\` running`;
91855
- typeArgs = { type, container, id: "", label: "", severity: "error", remediation: { key: "", label: "", command: "" } };
91844
+ typeArgs = {
91845
+ type,
91846
+ container,
91847
+ id: "",
91848
+ label: "",
91849
+ severity: "error",
91850
+ remediation: { key: "", label: "", command: "" }
91851
+ };
91856
91852
  break;
91857
91853
  }
91858
91854
  case "port-open": {
@@ -91861,7 +91857,15 @@ async function promptOneHealthCheck(prompts, type, hints) {
91861
91857
  const port = portRaw ?? 0;
91862
91858
  idDefault = `port-${port}-open`;
91863
91859
  labelDefault = `Port ${port} open`;
91864
- typeArgs = { type, host, port, id: "", label: "", severity: "error", remediation: { key: "", label: "", command: "" } };
91860
+ typeArgs = {
91861
+ type,
91862
+ host,
91863
+ port,
91864
+ id: "",
91865
+ label: "",
91866
+ severity: "error",
91867
+ remediation: { key: "", label: "", command: "" }
91868
+ };
91865
91869
  break;
91866
91870
  }
91867
91871
  case "http-ok": {
@@ -91873,7 +91877,14 @@ async function promptOneHealthCheck(prompts, type, hints) {
91873
91877
  const pathPart = url.replace(/^https?:\/\/[^/]+/, "").replace(/^\//, "") || "root";
91874
91878
  idDefault = `http-${slugify(pathPart)}`;
91875
91879
  labelDefault = `GET ${url} returns 2xx`;
91876
- typeArgs = { type, url, id: "", label: "", severity: "error", remediation: { key: "", label: "", command: "" } };
91880
+ typeArgs = {
91881
+ type,
91882
+ url,
91883
+ id: "",
91884
+ label: "",
91885
+ severity: "error",
91886
+ remediation: { key: "", label: "", command: "" }
91887
+ };
91877
91888
  break;
91878
91889
  }
91879
91890
  case "file-exists": {
@@ -91885,16 +91896,33 @@ async function promptOneHealthCheck(prompts, type, hints) {
91885
91896
  const basename3 = filePath.split("/").filter(Boolean).pop() ?? filePath;
91886
91897
  idDefault = `${slugify(basename3)}-exists`;
91887
91898
  labelDefault = `\`${filePath}\` exists`;
91888
- typeArgs = { type, path: filePath, id: "", label: "", severity: "error", remediation: { key: "", label: "", command: "" } };
91899
+ typeArgs = {
91900
+ type,
91901
+ path: filePath,
91902
+ id: "",
91903
+ label: "",
91904
+ severity: "error",
91905
+ remediation: { key: "", label: "", command: "" }
91906
+ };
91889
91907
  break;
91890
91908
  }
91891
91909
  case "exec-zero": {
91892
91910
  explain(" The command runs once at startup. Exit code 0 = pass, anything else = fail.");
91893
- const command = (await prompts.input({ message: "command", validate: (s) => s.trim().length > 0 ? true : "cannot be empty" })).trim();
91911
+ const command = (await prompts.input({
91912
+ message: "command",
91913
+ validate: (s) => s.trim().length > 0 ? true : "cannot be empty"
91914
+ })).trim();
91894
91915
  const verb = slugify(command.split(/\s+/)[0] ?? "exec");
91895
91916
  idDefault = `${verb}-ok`;
91896
91917
  labelDefault = `\`${command.length > 40 ? command.slice(0, 37) + "\u2026" : command}\` exits 0`;
91897
- typeArgs = { type, command, id: "", label: "", severity: "error", remediation: { key: "", label: "", command: "" } };
91918
+ typeArgs = {
91919
+ type,
91920
+ command,
91921
+ id: "",
91922
+ label: "",
91923
+ severity: "error",
91924
+ remediation: { key: "", label: "", command: "" }
91925
+ };
91898
91926
  break;
91899
91927
  }
91900
91928
  }
@@ -91918,7 +91946,11 @@ async function promptOneHealthCheck(prompts, type, hints) {
91918
91946
  });
91919
91947
  const typed = { ...typeArgs, id, label, severity };
91920
91948
  blank();
91921
- line(source_default.bold(" Remediation \u2014 what should pressing a key in the cockpit do when this check fails?"));
91949
+ line(
91950
+ source_default.bold(
91951
+ " Remediation \u2014 what should pressing a key in the cockpit do when this check fails?"
91952
+ )
91953
+ );
91922
91954
  explain(
91923
91955
  [
91924
91956
  " Three pieces:",
@@ -91973,7 +92005,15 @@ function defaultRemediationCommand(type, hints) {
91973
92005
  return 'echo "edit cockpit.yaml to set the fix command"';
91974
92006
  }
91975
92007
  }
91976
- var ACTION_SCRIPT_CANDIDATES = ["test", "build", "lint", "format", "typecheck", "type-check", "check"];
92008
+ var ACTION_SCRIPT_CANDIDATES = [
92009
+ "test",
92010
+ "build",
92011
+ "lint",
92012
+ "format",
92013
+ "typecheck",
92014
+ "type-check",
92015
+ "check"
92016
+ ];
91977
92017
  function defaultKeyFor(scriptName) {
91978
92018
  const map = {
91979
92019
  test: "t",
@@ -91993,9 +92033,7 @@ async function promptActions(prompts, hints) {
91993
92033
  "from `processes` (long-running streams) and `health` (state-checked)."
91994
92034
  ].join("\n")
91995
92035
  );
91996
- const suggestionScripts = ACTION_SCRIPT_CANDIDATES.filter(
91997
- (s) => hints.packageJsonScripts[s]
91998
- );
92036
+ const suggestionScripts = ACTION_SCRIPT_CANDIDATES.filter((s) => hints.packageJsonScripts[s]);
91999
92037
  const actions = [];
92000
92038
  if (suggestionScripts.length > 0) {
92001
92039
  blank();
@@ -92011,7 +92049,10 @@ async function promptActions(prompts, hints) {
92011
92049
  const key = defaultKeyFor(s);
92012
92050
  const keyHint = key ? ` [${key}]` : "";
92013
92051
  return {
92014
- name: `${s}${keyHint} \u2192 npm run ${s} (${hints.packageJsonScripts[s] ?? ""})`.slice(0, 80),
92052
+ name: `${s}${keyHint} \u2192 npm run ${s} (${hints.packageJsonScripts[s] ?? ""})`.slice(
92053
+ 0,
92054
+ 80
92055
+ ),
92015
92056
  value: s,
92016
92057
  checked: true
92017
92058
  };
@@ -92102,7 +92143,9 @@ async function runInitWizard(opts) {
92102
92143
  line(source_default.dim(" 8. Notifications \u2014 native OS toasts on state changes"));
92103
92144
  blank();
92104
92145
  line(" Anything you skip you can add later by hand-editing cockpit.yaml.");
92105
- line(` Press ${source_default.cyan("Ctrl-C")} to quit; press ${source_default.cyan("enter")} to accept any default.`);
92146
+ line(
92147
+ ` Press ${source_default.cyan("Ctrl-C")} to quit; press ${source_default.cyan("enter")} to accept any default.`
92148
+ );
92106
92149
  blank();
92107
92150
  const proceed = await prompts.confirm({ message: "Ready?", default: true });
92108
92151
  if (!proceed) {
@@ -92264,14 +92307,18 @@ dev-cockpit init-config: wrote ${target}
92264
92307
  try {
92265
92308
  await doctorCommand({ config: target, profile: opts.profile });
92266
92309
  } catch (err) {
92267
- process.stderr.write(`
92310
+ process.stderr.write(
92311
+ `
92268
92312
  doctor failed: ${err instanceof Error ? err.message : String(err)}
92269
- `);
92313
+ `
92314
+ );
92270
92315
  }
92271
92316
  }
92272
92317
  process.stdout.write("\nNext:\n");
92273
92318
  process.stdout.write(" \u2022 Edit cockpit.yaml any time, then re-run `dev-cockpit doctor`.\n");
92274
- process.stdout.write(" \u2022 Run `dev-cockpit dev` to launch the cockpit (Tab/arrows cycle panes, q quits).\n");
92319
+ process.stdout.write(
92320
+ " \u2022 Run `dev-cockpit dev` to launch the cockpit (Tab/arrows cycle panes, q quits).\n"
92321
+ );
92275
92322
  process.stdout.write(" \u2022 The Help tab inside the cockpit has the full docs.\n");
92276
92323
  } else {
92277
92324
  process.stdout.write(" Run `dev-cockpit doctor` to verify the config loads cleanly.\n");
@@ -92346,14 +92393,19 @@ function validateMountManifest(filePath, raw) {
92346
92393
  // src/mount/compose.ts
92347
92394
  function renderDockerOverlay(mounts, opts) {
92348
92395
  const lines = [];
92349
- lines.push(opts.banner ?? "# Generated by `dev-cockpit mount`. Do not edit by hand \u2014 re-run the command.");
92396
+ lines.push(
92397
+ opts.banner ?? "# Generated by `dev-cockpit mount`. Do not edit by hand \u2014 re-run the command."
92398
+ );
92350
92399
  lines.push("services:");
92351
92400
  lines.push(` ${opts.service}:`);
92352
- if (mounts.length === 0) {
92401
+ const binds = mounts.filter(
92402
+ (m) => m.kind !== "symlink-only" && typeof m.containerPath === "string"
92403
+ );
92404
+ if (binds.length === 0) {
92353
92405
  lines.push(" volumes: []");
92354
92406
  } else {
92355
92407
  lines.push(" volumes:");
92356
- for (const m of mounts) {
92408
+ for (const m of binds) {
92357
92409
  const source = m.hostPath.replace(/\\/g, "/");
92358
92410
  lines.push(" - type: bind");
92359
92411
  lines.push(` source: ${source}`);
@@ -92379,35 +92431,66 @@ function resolveOverlayPath(opts) {
92379
92431
  // src/mount/symlinks.ts
92380
92432
  import fs14 from "node:fs";
92381
92433
  import path19 from "node:path";
92434
+ import { spawnSync as spawnSync2 } from "node:child_process";
92382
92435
  function applyManagedSymlinks(workspaceRoot, mounts, strategy) {
92383
92436
  const report = { created: [], replaced: [], skipped: [] };
92384
92437
  for (const m of mounts) {
92385
92438
  const linkPath = strategy.linkPath(m, workspaceRoot);
92386
92439
  if (!linkPath) continue;
92387
- let preExistingRealDir = false;
92388
- try {
92389
- const lstat3 = fs14.lstatSync(linkPath);
92390
- if (lstat3.isSymbolicLink()) {
92391
- const existing = fs14.readlinkSync(linkPath);
92392
- if (existing === m.hostPath) {
92393
- report.created.push(linkPath);
92394
- continue;
92395
- }
92440
+ const outcome = applyOneSymlink(linkPath, m.hostPath);
92441
+ if (outcome === "created") report.created.push(linkPath);
92442
+ else if (outcome === "replaced") report.replaced.push(linkPath);
92443
+ else report.skipped.push(linkPath);
92444
+ }
92445
+ return report;
92446
+ }
92447
+ function applyOneSymlink(linkPath, hostPath) {
92448
+ let preExistingRealDir = false;
92449
+ let existing = null;
92450
+ try {
92451
+ existing = fs14.lstatSync(linkPath);
92452
+ } catch {
92453
+ }
92454
+ if (existing) {
92455
+ if (existing.isSymbolicLink()) {
92456
+ try {
92457
+ if (fs14.readlinkSync(linkPath) === hostPath) return "created";
92396
92458
  fs14.unlinkSync(linkPath);
92397
- } else if (lstat3.isDirectory()) {
92398
- preExistingRealDir = true;
92399
- fs14.rmSync(linkPath, { recursive: true, force: true });
92400
- } else {
92459
+ } catch {
92460
+ return "skipped";
92461
+ }
92462
+ } else if (existing.isDirectory()) {
92463
+ preExistingRealDir = true;
92464
+ if (!tryRemoveDir(linkPath)) return "skipped";
92465
+ } else {
92466
+ try {
92401
92467
  fs14.unlinkSync(linkPath);
92468
+ } catch {
92469
+ return "skipped";
92402
92470
  }
92403
- } catch {
92404
92471
  }
92472
+ }
92473
+ try {
92405
92474
  fs14.mkdirSync(path19.dirname(linkPath), { recursive: true });
92406
- fs14.symlinkSync(m.hostPath, linkPath);
92407
- if (preExistingRealDir) report.replaced.push(linkPath);
92408
- else report.created.push(linkPath);
92475
+ fs14.symlinkSync(hostPath, linkPath);
92476
+ } catch {
92477
+ return "skipped";
92409
92478
  }
92410
- return report;
92479
+ return preExistingRealDir ? "replaced" : "created";
92480
+ }
92481
+ function tryRemoveDir(p) {
92482
+ try {
92483
+ fs14.rmSync(p, { recursive: true, force: true });
92484
+ } catch {
92485
+ if (process.platform === "darwin") {
92486
+ spawnSync2("chmod", ["-RN", p], { stdio: "ignore" });
92487
+ try {
92488
+ fs14.rmSync(p, { recursive: true, force: true });
92489
+ } catch {
92490
+ }
92491
+ }
92492
+ }
92493
+ return !fs14.existsSync(p);
92411
92494
  }
92412
92495
  function removeManagedSymlinks(workspaceRoot, mounts, strategy) {
92413
92496
  for (const m of mounts) {
@@ -92449,23 +92532,19 @@ function detectBrokenSymlinks(workspaceRoot, mounts, strategy) {
92449
92532
  async function readGitInfo(hostPath) {
92450
92533
  try {
92451
92534
  let branchOut = "";
92452
- const branchHandle = spawnStream(
92453
- "git",
92454
- ["-C", hostPath, "rev-parse", "--abbrev-ref", "HEAD"],
92455
- { onStdout: (line2) => {
92535
+ const branchHandle = spawnStream("git", ["-C", hostPath, "rev-parse", "--abbrev-ref", "HEAD"], {
92536
+ onStdout: (line2) => {
92456
92537
  branchOut += line2;
92457
- } }
92458
- );
92538
+ }
92539
+ });
92459
92540
  const branchCode = await branchHandle.exitCode;
92460
92541
  if (branchCode !== 0) return null;
92461
92542
  let statusOut = "";
92462
- const statusHandle = spawnStream(
92463
- "git",
92464
- ["-C", hostPath, "status", "--porcelain"],
92465
- { onStdout: (line2) => {
92543
+ const statusHandle = spawnStream("git", ["-C", hostPath, "status", "--porcelain"], {
92544
+ onStdout: (line2) => {
92466
92545
  statusOut += line2;
92467
- } }
92468
- );
92546
+ }
92547
+ });
92469
92548
  await statusHandle.exitCode;
92470
92549
  return {
92471
92550
  branch: branchOut.trim() || "?",
@@ -92477,10 +92556,14 @@ async function readGitInfo(hostPath) {
92477
92556
  }
92478
92557
 
92479
92558
  // src/commands/mount.ts
92559
+ function mountKey(m) {
92560
+ if (m.kind === "symlink-only") return `symlink:${m.hostPath}`;
92561
+ return `bind:${m.containerPath ?? m.hostPath}`;
92562
+ }
92480
92563
  function mergeMounts(configMounts, providerMounts) {
92481
92564
  const seen = /* @__PURE__ */ new Map();
92482
- for (const m of providerMounts) seen.set(m.containerPath, m);
92483
- for (const m of configMounts) seen.set(m.containerPath, m);
92565
+ for (const m of providerMounts) seen.set(mountKey(m), m);
92566
+ for (const m of configMounts) seen.set(mountKey(m), m);
92484
92567
  return Array.from(seen.values());
92485
92568
  }
92486
92569
  async function resolveContext(opts) {
@@ -92503,20 +92586,18 @@ async function resolveContext(opts) {
92503
92586
  throw err;
92504
92587
  }
92505
92588
  if (!fs15.existsSync(configPath)) {
92506
- process.stderr.write(
92507
- `dev-cockpit mount: cockpit.yaml at ${configPath} doesn't exist.
92508
- `
92509
- );
92589
+ process.stderr.write(`dev-cockpit mount: cockpit.yaml at ${configPath} doesn't exist.
92590
+ `);
92510
92591
  process.exit(1);
92511
92592
  }
92512
92593
  const config = loadConfig(configPath, {
92513
92594
  configSchemaExt: profile?.configSchemaExt,
92514
92595
  profileKey: profile?.appName
92515
92596
  });
92516
- const explicitService = opts.service ?? config.docker?.services?.[0]?.name;
92597
+ const explicitService = opts.service ?? config.mount.service ?? config.docker?.services?.[0]?.name;
92517
92598
  if (!explicitService) {
92518
92599
  process.stderr.write(
92519
- "dev-cockpit mount: no target service. Pass --service <name> or set docker.services in cockpit.yaml.\n"
92600
+ "dev-cockpit mount: no target service. Pass --service <name>, set mount.service in cockpit.yaml, or set docker.services in cockpit.yaml.\n"
92520
92601
  );
92521
92602
  process.exit(1);
92522
92603
  }
@@ -92544,18 +92625,11 @@ var stdoutSink = (line2) => process.stdout.write(`${line2}
92544
92625
  `);
92545
92626
  var stderrSink = (line2) => process.stderr.write(`${line2}
92546
92627
  `);
92547
- function applyExclude(mounts, exclude) {
92548
- if (exclude.length === 0) return [...mounts];
92549
- const set = new Set(exclude);
92550
- return mounts.filter((m) => {
92551
- const name = m.meta?.["name"] ?? "";
92552
- return !set.has(name);
92553
- });
92554
- }
92555
92628
  async function pickMounts(candidates) {
92556
92629
  const { checkbox } = await import("@inquirer/prompts");
92557
92630
  const choices = candidates.map((m) => {
92558
- const name = m.meta?.["name"] ?? m.containerPath;
92631
+ const fallbackName = m.containerPath ?? (m.kind === "symlink-only" ? `(symlink) ${m.hostPath}` : m.hostPath);
92632
+ const name = m.meta?.["name"] ?? fallbackName;
92559
92633
  const type = m.meta?.["type"];
92560
92634
  const label = type ? `${name} (${type}) ${m.hostPath}` : `${name} ${m.hostPath}`;
92561
92635
  return { name: label, value: m, checked: true };
@@ -92567,8 +92641,17 @@ async function pickMounts(candidates) {
92567
92641
  }
92568
92642
  async function mountCommand(opts = {}) {
92569
92643
  const ctx = await resolveContext(opts);
92570
- const { workspaceRoot, appName, service, configMounts, providerMounts, overlayPath, manifestPath: manifestPath2, profile } = ctx;
92571
- const merged = applyExclude(mergeMounts(configMounts, providerMounts), opts.exclude ?? []);
92644
+ const {
92645
+ workspaceRoot,
92646
+ appName,
92647
+ service,
92648
+ configMounts,
92649
+ providerMounts,
92650
+ overlayPath,
92651
+ manifestPath: manifestPath2,
92652
+ profile
92653
+ } = ctx;
92654
+ const merged = mergeMounts(configMounts, providerMounts);
92572
92655
  if (merged.length === 0) {
92573
92656
  process.stdout.write(
92574
92657
  "dev-cockpit mount: no mount candidates from config.mounts[] or profile.mountCandidatesProvider.\n"
@@ -92615,7 +92698,8 @@ async function mountCommand(opts = {}) {
92615
92698
  `
92616
92699
  );
92617
92700
  }
92618
- for (const p of report.skipped) process.stdout.write(`dev-cockpit mount: skipped (could not replace) \u2192 ${p}
92701
+ for (const p of report.skipped)
92702
+ process.stdout.write(`dev-cockpit mount: skipped (could not replace) \u2192 ${p}
92619
92703
  `);
92620
92704
  }
92621
92705
  if (profile?.onMountApply) {
@@ -92674,7 +92758,7 @@ dev-cockpit mount status \u2014 service: ${manifest.service}
92674
92758
  const headers = ["NAME", "BRANCH", "DIRTY", ...Array.from(extraKeys)];
92675
92759
  const widths = headers.map((h2) => h2.length);
92676
92760
  const dataRows = rows.map((r) => {
92677
- const name = r.mount.meta?.["name"] ?? r.mount.containerPath;
92761
+ const name = r.mount.meta?.["name"] ?? r.mount.containerPath ?? r.mount.hostPath;
92678
92762
  const branch = (r.info.branch ?? "?").slice(0, 24);
92679
92763
  const dirty = r.info.dirty === void 0 ? "?" : r.info.dirty ? "yes" : "no";
92680
92764
  const extras = Array.from(extraKeys).map((k) => r.info.extra?.[k] ?? "");
@@ -92757,8 +92841,10 @@ async function migrateConfigCommand(opts = {}) {
92757
92841
  }
92758
92842
  const fromVersion = raw["version"];
92759
92843
  if (fromVersion === CONFIG_VERSION) {
92760
- process.stdout.write(`dev-cockpit migrate-config: already at v${CONFIG_VERSION}; nothing to do.
92761
- `);
92844
+ process.stdout.write(
92845
+ `dev-cockpit migrate-config: already at v${CONFIG_VERSION}; nothing to do.
92846
+ `
92847
+ );
92762
92848
  return;
92763
92849
  }
92764
92850
  if (fromVersion > CONFIG_VERSION) {
@@ -92810,24 +92896,32 @@ function buildCli(opts = {}) {
92810
92896
  const program2 = new Command();
92811
92897
  const profile = opts.profile;
92812
92898
  const appName = profile?.appName ?? "dev-cockpit";
92813
- program2.name(appName).description(
92814
- profile ? `${appName} \u2014 built on dev-cockpit` : "Generic terminal UI dev cockpit"
92815
- ).version(readPackageVersion(), "-V, --version", "Output the version number");
92899
+ program2.name(appName).description(profile ? `${appName} \u2014 built on dev-cockpit` : "Generic terminal UI dev cockpit").version(readPackageVersion(), "-V, --version", "Output the version number");
92816
92900
  if (profile?.setupCli) {
92817
92901
  profile.setupCli(program2);
92818
92902
  }
92819
92903
  if (!hasCommand(program2, "dev")) {
92820
- program2.command("dev").description("Boot the three-pane TUI (Repos / Output / Health / Help)").option("-c, --config <path>", "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)").action(async (cmdOpts) => {
92904
+ program2.command("dev").description("Boot the three-pane TUI (Repos / Output / Health / Help)").option(
92905
+ "-c, --config <path>",
92906
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
92907
+ ).action(async (cmdOpts) => {
92821
92908
  await devCommand({ config: cmdOpts.config, profile });
92822
92909
  });
92823
92910
  }
92824
92911
  if (!hasCommand(program2, "doctor")) {
92825
- program2.command("doctor").description("Run all health checks once and print a status table").option("-c, --config <path>", "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)").action(async (cmdOpts) => {
92912
+ program2.command("doctor").description("Run all health checks once and print a status table").option(
92913
+ "-c, --config <path>",
92914
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
92915
+ ).action(async (cmdOpts) => {
92826
92916
  await doctorCommand({ config: cmdOpts.config, profile });
92827
92917
  });
92828
92918
  }
92829
92919
  if (!hasCommand(program2, "init-config")) {
92830
- program2.command("init-config").description("Write a starter cockpit.yaml in the current directory").option("-f, --force", "Overwrite an existing cockpit.yaml without prompting", false).option("--with-docker", "Include an uncommented docker block (static template only)", false).option("-i, --interactive", "Walk through prompts to populate the file (skips static template)", false).option("-o, --output <path>", "Write the file to this path instead of <cwd>/cockpit.yaml").action(
92920
+ program2.command("init-config").description("Write a starter cockpit.yaml in the current directory").option("-f, --force", "Overwrite an existing cockpit.yaml without prompting", false).option("--with-docker", "Include an uncommented docker block (static template only)", false).option(
92921
+ "-i, --interactive",
92922
+ "Walk through prompts to populate the file (skips static template)",
92923
+ false
92924
+ ).option("-o, --output <path>", "Write the file to this path instead of <cwd>/cockpit.yaml").action(
92831
92925
  async (cmdOpts) => {
92832
92926
  await initConfigCommand({
92833
92927
  force: cmdOpts.force ?? false,
@@ -92842,42 +92936,52 @@ function buildCli(opts = {}) {
92842
92936
  }
92843
92937
  if (!hasCommand(program2, "link")) {
92844
92938
  const linkCmd = program2.command("link").description("Register the current directory as using a cockpit.yaml that lives elsewhere").argument("<config-path>", "Path to the cockpit.yaml to associate with this directory").action(async (configPath) => {
92845
- const { linkAddCommand } = await import("./link-VWT2VQH6.js");
92939
+ const { linkAddCommand } = await import("./link-Y7OFHOUP.js");
92846
92940
  linkAddCommand({ configPath });
92847
92941
  });
92848
92942
  linkCmd.command("list").description("Print all registered project \u2192 config mappings").action(async () => {
92849
- const { linkListCommand } = await import("./link-VWT2VQH6.js");
92943
+ const { linkListCommand } = await import("./link-Y7OFHOUP.js");
92850
92944
  linkListCommand();
92851
92945
  });
92852
92946
  linkCmd.command("remove").description("Unregister the current directory from the manifest").action(async () => {
92853
- const { linkRemoveCommand } = await import("./link-VWT2VQH6.js");
92947
+ const { linkRemoveCommand } = await import("./link-Y7OFHOUP.js");
92854
92948
  linkRemoveCommand();
92855
92949
  });
92856
92950
  }
92857
92951
  if (!hasCommand(program2, "mount")) {
92858
- const mountCmd = program2.command("mount").description("Apply a docker-compose bind-mount overlay (interactive picker by default)").option("-c, --config <path>", "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)").option("-s, --service <name>", "Target compose service (default: first in docker.services)").option("-q, --quiet", "Skip the interactive picker; apply all candidates").option(
92859
- "-e, --exclude <name>",
92860
- "Exclude a candidate by meta.name (repeatable)",
92861
- (val, prev) => [...prev, val],
92862
- []
92863
- ).action(async (cmdOpts) => {
92952
+ const mountCmd = program2.command("mount").description(
92953
+ `Apply a docker-compose bind-mount overlay (interactive picker by default)
92954
+
92955
+ This creates a docker-compose overlay file. The location of the overlay file is configurable in the cockpit.yaml file. You must then include the overlay file in your compose file as a wildcard include. eg. \`docker-compose.yml -f docker/compose/web/docker-compose.dev-link.yml\``
92956
+ ).option(
92957
+ "-c, --config <path>",
92958
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
92959
+ ).option("-s, --service <name>", "Target compose service (default: first in docker.services)").option("-q, --quiet", "Skip the interactive picker; apply all candidates").action(async (cmdOpts) => {
92864
92960
  await mountCommand({
92865
92961
  config: cmdOpts.config,
92866
92962
  service: cmdOpts.service,
92867
92963
  quiet: cmdOpts.quiet ?? false,
92868
- exclude: cmdOpts.exclude ?? [],
92869
92964
  profile
92870
92965
  });
92871
92966
  });
92872
- mountCmd.command("status").description("Show active mounts with branch, dirty, and broken-symlink status").option("-c, --config <path>", "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)").action(async (cmdOpts) => {
92967
+ mountCmd.command("status").description("Show active mounts with branch, dirty, and broken-symlink status").option(
92968
+ "-c, --config <path>",
92969
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
92970
+ ).action(async (cmdOpts) => {
92873
92971
  await mountStatusCommand({ config: cmdOpts.config, profile });
92874
92972
  });
92875
- mountCmd.command("clear").description("Remove the overlay, manifest, and managed symlinks; runs profile.onMountClear").option("-c, --config <path>", "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)").action(async (cmdOpts) => {
92973
+ mountCmd.command("clear").description("Remove the overlay, manifest, and managed symlinks; runs profile.onMountClear").option(
92974
+ "-c, --config <path>",
92975
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
92976
+ ).action(async (cmdOpts) => {
92876
92977
  await mountClearCommand({ config: cmdOpts.config, profile });
92877
92978
  });
92878
92979
  }
92879
92980
  if (!hasCommand(program2, "migrate-config")) {
92880
- program2.command("migrate-config").description("Rewrite cockpit.yaml at the current CONFIG_VERSION").option("-c, --config <path>", "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)").option("--dry-run", "Print the migrated YAML to stdout instead of writing").action(async (cmdOpts) => {
92981
+ program2.command("migrate-config").description("Rewrite cockpit.yaml at the current CONFIG_VERSION").option(
92982
+ "-c, --config <path>",
92983
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
92984
+ ).option("--dry-run", "Print the migrated YAML to stdout instead of writing").action(async (cmdOpts) => {
92881
92985
  await migrateConfigCommand({ config: cmdOpts.config, dryRun: cmdOpts.dryRun });
92882
92986
  });
92883
92987
  }