dev-cockpit 0.2.7 → 0.2.9

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 (57) 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 +16 -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 +13 -0
  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 +8 -0
  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 +364 -159
  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.map +1 -1
  42. package/dist/mount/types.d.ts +6 -0
  43. package/dist/mount/types.d.ts.map +1 -1
  44. package/docs/commands.md +8 -8
  45. package/docs/config-reference.md +45 -41
  46. package/docs/health.md +32 -22
  47. package/docs/init-config.md +16 -16
  48. package/docs/mount.md +8 -7
  49. package/docs/notifications.md +3 -3
  50. package/docs/panes.md +5 -5
  51. package/docs/processes.md +7 -5
  52. package/examples/cockpit.schema.json +222 -57
  53. package/examples/cockpit.yaml +8 -10
  54. package/package.json +99 -93
  55. package/prettier-config.json +7 -0
  56. package/dist/chunk-A446TCT5.js.map +0 -7
  57. 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,
@@ -83972,6 +83972,7 @@ var MountSchema = external_exports.object({
83972
83972
  hostPath: external_exports.string(),
83973
83973
  containerPath: external_exports.string().optional(),
83974
83974
  kind: external_exports.enum(["bind", "symlink-only"]).optional(),
83975
+ alternativeSources: external_exports.array(external_exports.string()).optional(),
83975
83976
  meta: external_exports.record(external_exports.unknown()).optional()
83976
83977
  }).refine(
83977
83978
  (m) => m.kind === "symlink-only" ? true : typeof m.containerPath === "string" && m.containerPath.length > 0,
@@ -83998,7 +83999,9 @@ var MountSettingsSchema = external_exports.object({
83998
83999
  */
83999
84000
  overlayPath: external_exports.string().optional(),
84000
84001
  /** Basename inside stateDir for the manifest. */
84001
- manifestFile: external_exports.string().optional().default("mount.manifest.json")
84002
+ manifestFile: external_exports.string().optional().default("mount.manifest.json"),
84003
+ /** Target compose service. Unset = first in docker.services. */
84004
+ service: external_exports.string().optional()
84002
84005
  });
84003
84006
  var BaseCockpitConfigSchema = external_exports.object({
84004
84007
  version: external_exports.number().int(),
@@ -84180,9 +84183,7 @@ var FOCUS_ORDER = ["repos", "output", "health", "help"];
84180
84183
  function filterActions(actions, filter) {
84181
84184
  if (!filter) return actions;
84182
84185
  const f = filter.toLowerCase();
84183
- return actions.filter(
84184
- (a2) => a2.id.toLowerCase().includes(f) || a2.label.toLowerCase().includes(f)
84185
- );
84186
+ return actions.filter((a2) => a2.id.toLowerCase().includes(f) || a2.label.toLowerCase().includes(f));
84186
84187
  }
84187
84188
  var cockpitStore = createStore()((set, get2) => ({
84188
84189
  repos: {},
@@ -84451,7 +84452,7 @@ function Repos() {
84451
84452
  const heading2 = kind === "repo" ? "Repos" : kind === "docker" ? "Docker" : "Processes";
84452
84453
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
84453
84454
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
84454
- " ",
84455
+ " ",
84455
84456
  heading2
84456
84457
  ] }),
84457
84458
  entriesInKind.map(({ key, idx, entry }) => {
@@ -84488,7 +84489,7 @@ function Repos() {
84488
84489
  " ",
84489
84490
  a2.label
84490
84491
  ] }),
84491
- isDefault && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: " (default)" })
84492
+ isDefault && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: " (default)" })
84492
84493
  ] }, a2.id);
84493
84494
  })
84494
84495
  ]
@@ -84589,9 +84590,7 @@ function Output() {
84589
84590
  } else if (key.downArrow) {
84590
84591
  setScrollOffset((prev) => Math.max(0, prev - 1));
84591
84592
  } else if (key.pageUp) {
84592
- setScrollOffset(
84593
- (prev) => Math.min(prev + visibleLines, Math.max(0, total - visibleLines))
84594
- );
84593
+ setScrollOffset((prev) => Math.min(prev + visibleLines, Math.max(0, total - visibleLines)));
84595
84594
  } else if (key.pageDown) {
84596
84595
  setScrollOffset((prev) => Math.max(0, prev - visibleLines));
84597
84596
  } else if (key.return) {
@@ -87651,7 +87650,9 @@ function Footer({ legends } = {}) {
87651
87650
  if (!key) return NAV_HINT;
87652
87651
  const keyed = actionsForRepoRow(s.actions, key).filter((a2) => a2.key);
87653
87652
  if (keyed.length === 0) return NAV_HINT;
87654
- return [NAV_HINT, ...keyed.map((a2) => `[${a2.key}] ${shortLabel(a2.label).toLowerCase()}`)].join(" ");
87653
+ return [NAV_HINT, ...keyed.map((a2) => `[${a2.key}] ${shortLabel(a2.label).toLowerCase()}`)].join(
87654
+ " "
87655
+ );
87655
87656
  });
87656
87657
  let legend;
87657
87658
  if (focus === "repos") {
@@ -87671,7 +87672,9 @@ var import_react5 = __toESM(require_react(), 1);
87671
87672
  var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
87672
87673
  function FilterModal() {
87673
87674
  const outputFilter = useCockpitStore((s) => s.outputFilter);
87674
- const [severityDraft, setSeverityDraft] = (0, import_react5.useState)(outputFilter.severity);
87675
+ const [severityDraft, setSeverityDraft] = (0, import_react5.useState)(
87676
+ outputFilter.severity
87677
+ );
87675
87678
  const SEVERITIES = [void 0, "info", "warn", "error"];
87676
87679
  use_input_default((input, key) => {
87677
87680
  if (input === "s") {
@@ -87684,26 +87687,16 @@ function FilterModal() {
87684
87687
  cockpitStore.getState().setActiveModal(null);
87685
87688
  }
87686
87689
  });
87687
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
87688
- Box_default,
87689
- {
87690
- flexDirection: "column",
87691
- borderStyle: "round",
87692
- borderColor: "cyan",
87693
- paddingX: 2,
87694
- paddingY: 1,
87695
- children: [
87696
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { bold: true, children: " Filter Output " }),
87697
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: " " }),
87698
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { children: [
87699
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: "Severity (s to cycle): " }),
87700
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: "cyan", children: severityDraft ?? "all" })
87701
- ] }),
87702
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: " " }),
87703
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { dimColor: true, children: "Enter = apply \xB7 Esc = cancel" })
87704
- ]
87705
- }
87706
- ) });
87690
+ 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: [
87691
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { bold: true, children: " Filter Output " }),
87692
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: " " }),
87693
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { children: [
87694
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: "Severity (s to cycle): " }),
87695
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: "cyan", children: severityDraft ?? "all" })
87696
+ ] }),
87697
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: " " }),
87698
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { dimColor: true, children: "Enter = apply \xB7 Esc = cancel" })
87699
+ ] }) });
87707
87700
  }
87708
87701
 
87709
87702
  // src/cockpit/panes/SearchModal.tsx
@@ -87733,29 +87726,19 @@ function SearchModal() {
87733
87726
  setQuery((prev) => prev + input);
87734
87727
  }
87735
87728
  });
87736
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
87737
- Box_default,
87738
- {
87739
- flexDirection: "column",
87740
- borderStyle: "round",
87741
- borderColor: "cyan",
87742
- paddingX: 2,
87743
- paddingY: 1,
87744
- children: [
87745
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { bold: true, children: " Search Output " }),
87746
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: " " }),
87747
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { children: [
87748
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: "Query: " }),
87749
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: "cyan", children: [
87750
- query,
87751
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "gray", children: "\u2588" })
87752
- ] })
87753
- ] }),
87754
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: " " }),
87755
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { dimColor: true, children: "Enter = apply \xB7 Esc = cancel \xB7 Backspace = delete" })
87756
- ]
87757
- }
87758
- ) });
87729
+ 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: [
87730
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { bold: true, children: " Search Output " }),
87731
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: " " }),
87732
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { children: [
87733
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: "Query: " }),
87734
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: "cyan", children: [
87735
+ query,
87736
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "gray", children: "\u2588" })
87737
+ ] })
87738
+ ] }),
87739
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: " " }),
87740
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { dimColor: true, children: "Enter = apply \xB7 Esc = cancel \xB7 Backspace = delete" })
87741
+ ] }) });
87759
87742
  }
87760
87743
 
87761
87744
  // src/cockpit/panes/CommandModal.tsx
@@ -87910,9 +87893,7 @@ function useGlobalKeys(opts) {
87910
87893
  return;
87911
87894
  }
87912
87895
  if (input === "e" && onOpenError) {
87913
- const target = state.recentErrors.find(
87914
- (er) => er.file && typeof er.line === "number"
87915
- );
87896
+ const target = state.recentErrors.find((er) => er.file && typeof er.line === "number");
87916
87897
  if (target?.file && typeof target.line === "number") {
87917
87898
  onOpenError({
87918
87899
  file: target.file,
@@ -87950,10 +87931,7 @@ function useGlobalKeys(opts) {
87950
87931
  healthId: dispatch.healthId,
87951
87932
  healthLabel: dispatch.healthLabel
87952
87933
  });
87953
- withMinHold(
87954
- dispatch.promise,
87955
- () => cockpitStore.getState().setActiveRemediation(null)
87956
- );
87934
+ withMinHold(dispatch.promise, () => cockpitStore.getState().setActiveRemediation(null));
87957
87935
  return;
87958
87936
  }
87959
87937
  }
@@ -90967,11 +90945,7 @@ async function devCommand(opts = {}) {
90967
90945
  const builtinActions = buildBuiltinActions(config);
90968
90946
  const actions = buildActionRegistry(
90969
90947
  [...config.actions],
90970
- [
90971
- ...builtinActions,
90972
- ...profile?.actions ?? [],
90973
- ...bootResult.actions ?? []
90974
- ]
90948
+ [...builtinActions, ...profile?.actions ?? [], ...bootResult.actions ?? []]
90975
90949
  );
90976
90950
  const [shell, shellFlag] = process.platform === "win32" ? ["cmd", "/c"] : ["/bin/sh", "-c"];
90977
90951
  const highlightMatcher = compileHighlights(config.highlights);
@@ -91044,7 +91018,12 @@ async function devCommand(opts = {}) {
91044
91018
  }
91045
91019
  if (action.id.startsWith(BUILTIN_RESTART_PROCESS)) {
91046
91020
  const procId = action.id.slice(BUILTIN_RESTART_PROCESS.length);
91047
- store.appendOutput({ ts: Date.now(), source: procId, severity: "info", text: `> restart ${procId}` });
91021
+ store.appendOutput({
91022
+ ts: Date.now(),
91023
+ source: procId,
91024
+ severity: "info",
91025
+ text: `> restart ${procId}`
91026
+ });
91048
91027
  await killSpawnedByTag(procId);
91049
91028
  spawnProcessById(procId);
91050
91029
  store.setFocus("output");
@@ -91338,10 +91317,7 @@ function renderWizardYaml(result) {
91338
91317
  if (health.length > 0) blocks.push(health);
91339
91318
  const actions = renderActions(result.actions);
91340
91319
  if (actions.length > 0) blocks.push(actions);
91341
- blocks.push([
91342
- "notifications:",
91343
- indent(`enabled: ${result.notifications.enabled}`, 1)
91344
- ]);
91320
+ blocks.push(["notifications:", indent(`enabled: ${result.notifications.enabled}`, 1)]);
91345
91321
  if (result.profileLines && result.profileLines.length > 0) {
91346
91322
  blocks.push([
91347
91323
  "profile:",
@@ -91410,7 +91386,12 @@ function safeReadJson(p) {
91410
91386
  }
91411
91387
  }
91412
91388
  function detectComposeFile(cwd) {
91413
- for (const candidate of ["compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"]) {
91389
+ for (const candidate of [
91390
+ "compose.yaml",
91391
+ "compose.yml",
91392
+ "docker-compose.yml",
91393
+ "docker-compose.yaml"
91394
+ ]) {
91414
91395
  if (fs11.existsSync(path15.join(cwd, candidate))) return candidate;
91415
91396
  }
91416
91397
  return void 0;
@@ -91448,7 +91429,10 @@ function detectRepoSuggestions(cwd, pkg) {
91448
91429
  }
91449
91430
  for (const glob of workspaceGlobs) {
91450
91431
  for (const entry of expandSimpleGlob(cwd, glob)) {
91451
- out.push({ id: path15.basename(entry), path: path15.relative(cwd, path15.join(cwd, entry)) || "." });
91432
+ out.push({
91433
+ id: path15.basename(entry),
91434
+ path: path15.relative(cwd, path15.join(cwd, entry)) || "."
91435
+ });
91452
91436
  }
91453
91437
  }
91454
91438
  const pnpmFile = path15.join(cwd, "pnpm-workspace.yaml");
@@ -91791,7 +91775,9 @@ async function promptHealthChecks(prompts, hints, hasDocker) {
91791
91775
  ].join("\n")
91792
91776
  );
91793
91777
  const out = [];
91794
- let typeChoices = baseHealthTypeChoices(Boolean(hints.composeFile && hints.composeServices.length > 0));
91778
+ let typeChoices = baseHealthTypeChoices(
91779
+ Boolean(hints.composeFile && hints.composeServices.length > 0)
91780
+ );
91795
91781
  if (!hasDocker) {
91796
91782
  typeChoices = typeChoices.filter((c3) => c3.value !== "container-running");
91797
91783
  }
@@ -91856,7 +91842,14 @@ async function promptOneHealthCheck(prompts, type, hints) {
91856
91842
  })).trim();
91857
91843
  idDefault = `${slugify(container)}-up`;
91858
91844
  labelDefault = `Container \`${container}\` running`;
91859
- typeArgs = { type, container, id: "", label: "", severity: "error", remediation: { key: "", label: "", command: "" } };
91845
+ typeArgs = {
91846
+ type,
91847
+ container,
91848
+ id: "",
91849
+ label: "",
91850
+ severity: "error",
91851
+ remediation: { key: "", label: "", command: "" }
91852
+ };
91860
91853
  break;
91861
91854
  }
91862
91855
  case "port-open": {
@@ -91865,7 +91858,15 @@ async function promptOneHealthCheck(prompts, type, hints) {
91865
91858
  const port = portRaw ?? 0;
91866
91859
  idDefault = `port-${port}-open`;
91867
91860
  labelDefault = `Port ${port} open`;
91868
- typeArgs = { type, host, port, id: "", label: "", severity: "error", remediation: { key: "", label: "", command: "" } };
91861
+ typeArgs = {
91862
+ type,
91863
+ host,
91864
+ port,
91865
+ id: "",
91866
+ label: "",
91867
+ severity: "error",
91868
+ remediation: { key: "", label: "", command: "" }
91869
+ };
91869
91870
  break;
91870
91871
  }
91871
91872
  case "http-ok": {
@@ -91877,7 +91878,14 @@ async function promptOneHealthCheck(prompts, type, hints) {
91877
91878
  const pathPart = url.replace(/^https?:\/\/[^/]+/, "").replace(/^\//, "") || "root";
91878
91879
  idDefault = `http-${slugify(pathPart)}`;
91879
91880
  labelDefault = `GET ${url} returns 2xx`;
91880
- typeArgs = { type, url, id: "", label: "", severity: "error", remediation: { key: "", label: "", command: "" } };
91881
+ typeArgs = {
91882
+ type,
91883
+ url,
91884
+ id: "",
91885
+ label: "",
91886
+ severity: "error",
91887
+ remediation: { key: "", label: "", command: "" }
91888
+ };
91881
91889
  break;
91882
91890
  }
91883
91891
  case "file-exists": {
@@ -91889,16 +91897,33 @@ async function promptOneHealthCheck(prompts, type, hints) {
91889
91897
  const basename3 = filePath.split("/").filter(Boolean).pop() ?? filePath;
91890
91898
  idDefault = `${slugify(basename3)}-exists`;
91891
91899
  labelDefault = `\`${filePath}\` exists`;
91892
- typeArgs = { type, path: filePath, id: "", label: "", severity: "error", remediation: { key: "", label: "", command: "" } };
91900
+ typeArgs = {
91901
+ type,
91902
+ path: filePath,
91903
+ id: "",
91904
+ label: "",
91905
+ severity: "error",
91906
+ remediation: { key: "", label: "", command: "" }
91907
+ };
91893
91908
  break;
91894
91909
  }
91895
91910
  case "exec-zero": {
91896
91911
  explain(" The command runs once at startup. Exit code 0 = pass, anything else = fail.");
91897
- const command = (await prompts.input({ message: "command", validate: (s) => s.trim().length > 0 ? true : "cannot be empty" })).trim();
91912
+ const command = (await prompts.input({
91913
+ message: "command",
91914
+ validate: (s) => s.trim().length > 0 ? true : "cannot be empty"
91915
+ })).trim();
91898
91916
  const verb = slugify(command.split(/\s+/)[0] ?? "exec");
91899
91917
  idDefault = `${verb}-ok`;
91900
91918
  labelDefault = `\`${command.length > 40 ? command.slice(0, 37) + "\u2026" : command}\` exits 0`;
91901
- typeArgs = { type, command, id: "", label: "", severity: "error", remediation: { key: "", label: "", command: "" } };
91919
+ typeArgs = {
91920
+ type,
91921
+ command,
91922
+ id: "",
91923
+ label: "",
91924
+ severity: "error",
91925
+ remediation: { key: "", label: "", command: "" }
91926
+ };
91902
91927
  break;
91903
91928
  }
91904
91929
  }
@@ -91922,7 +91947,11 @@ async function promptOneHealthCheck(prompts, type, hints) {
91922
91947
  });
91923
91948
  const typed = { ...typeArgs, id, label, severity };
91924
91949
  blank();
91925
- line(source_default.bold(" Remediation \u2014 what should pressing a key in the cockpit do when this check fails?"));
91950
+ line(
91951
+ source_default.bold(
91952
+ " Remediation \u2014 what should pressing a key in the cockpit do when this check fails?"
91953
+ )
91954
+ );
91926
91955
  explain(
91927
91956
  [
91928
91957
  " Three pieces:",
@@ -91977,7 +92006,15 @@ function defaultRemediationCommand(type, hints) {
91977
92006
  return 'echo "edit cockpit.yaml to set the fix command"';
91978
92007
  }
91979
92008
  }
91980
- var ACTION_SCRIPT_CANDIDATES = ["test", "build", "lint", "format", "typecheck", "type-check", "check"];
92009
+ var ACTION_SCRIPT_CANDIDATES = [
92010
+ "test",
92011
+ "build",
92012
+ "lint",
92013
+ "format",
92014
+ "typecheck",
92015
+ "type-check",
92016
+ "check"
92017
+ ];
91981
92018
  function defaultKeyFor(scriptName) {
91982
92019
  const map = {
91983
92020
  test: "t",
@@ -91997,9 +92034,7 @@ async function promptActions(prompts, hints) {
91997
92034
  "from `processes` (long-running streams) and `health` (state-checked)."
91998
92035
  ].join("\n")
91999
92036
  );
92000
- const suggestionScripts = ACTION_SCRIPT_CANDIDATES.filter(
92001
- (s) => hints.packageJsonScripts[s]
92002
- );
92037
+ const suggestionScripts = ACTION_SCRIPT_CANDIDATES.filter((s) => hints.packageJsonScripts[s]);
92003
92038
  const actions = [];
92004
92039
  if (suggestionScripts.length > 0) {
92005
92040
  blank();
@@ -92015,7 +92050,10 @@ async function promptActions(prompts, hints) {
92015
92050
  const key = defaultKeyFor(s);
92016
92051
  const keyHint = key ? ` [${key}]` : "";
92017
92052
  return {
92018
- name: `${s}${keyHint} \u2192 npm run ${s} (${hints.packageJsonScripts[s] ?? ""})`.slice(0, 80),
92053
+ name: `${s}${keyHint} \u2192 npm run ${s} (${hints.packageJsonScripts[s] ?? ""})`.slice(
92054
+ 0,
92055
+ 80
92056
+ ),
92019
92057
  value: s,
92020
92058
  checked: true
92021
92059
  };
@@ -92106,7 +92144,9 @@ async function runInitWizard(opts) {
92106
92144
  line(source_default.dim(" 8. Notifications \u2014 native OS toasts on state changes"));
92107
92145
  blank();
92108
92146
  line(" Anything you skip you can add later by hand-editing cockpit.yaml.");
92109
- line(` Press ${source_default.cyan("Ctrl-C")} to quit; press ${source_default.cyan("enter")} to accept any default.`);
92147
+ line(
92148
+ ` Press ${source_default.cyan("Ctrl-C")} to quit; press ${source_default.cyan("enter")} to accept any default.`
92149
+ );
92110
92150
  blank();
92111
92151
  const proceed = await prompts.confirm({ message: "Ready?", default: true });
92112
92152
  if (!proceed) {
@@ -92268,14 +92308,18 @@ dev-cockpit init-config: wrote ${target}
92268
92308
  try {
92269
92309
  await doctorCommand({ config: target, profile: opts.profile });
92270
92310
  } catch (err) {
92271
- process.stderr.write(`
92311
+ process.stderr.write(
92312
+ `
92272
92313
  doctor failed: ${err instanceof Error ? err.message : String(err)}
92273
- `);
92314
+ `
92315
+ );
92274
92316
  }
92275
92317
  }
92276
92318
  process.stdout.write("\nNext:\n");
92277
92319
  process.stdout.write(" \u2022 Edit cockpit.yaml any time, then re-run `dev-cockpit doctor`.\n");
92278
- process.stdout.write(" \u2022 Run `dev-cockpit dev` to launch the cockpit (Tab/arrows cycle panes, q quits).\n");
92320
+ process.stdout.write(
92321
+ " \u2022 Run `dev-cockpit dev` to launch the cockpit (Tab/arrows cycle panes, q quits).\n"
92322
+ );
92279
92323
  process.stdout.write(" \u2022 The Help tab inside the cockpit has the full docs.\n");
92280
92324
  } else {
92281
92325
  process.stdout.write(" Run `dev-cockpit doctor` to verify the config loads cleanly.\n");
@@ -92350,10 +92394,14 @@ function validateMountManifest(filePath, raw) {
92350
92394
  // src/mount/compose.ts
92351
92395
  function renderDockerOverlay(mounts, opts) {
92352
92396
  const lines = [];
92353
- lines.push(opts.banner ?? "# Generated by `dev-cockpit mount`. Do not edit by hand \u2014 re-run the command.");
92397
+ lines.push(
92398
+ opts.banner ?? "# Generated by `dev-cockpit mount`. Do not edit by hand \u2014 re-run the command."
92399
+ );
92354
92400
  lines.push("services:");
92355
92401
  lines.push(` ${opts.service}:`);
92356
- const binds = mounts.filter((m) => m.kind !== "symlink-only" && typeof m.containerPath === "string");
92402
+ const binds = mounts.filter(
92403
+ (m) => m.kind !== "symlink-only" && typeof m.containerPath === "string"
92404
+ );
92357
92405
  if (binds.length === 0) {
92358
92406
  lines.push(" volumes: []");
92359
92407
  } else {
@@ -92485,23 +92533,19 @@ function detectBrokenSymlinks(workspaceRoot, mounts, strategy) {
92485
92533
  async function readGitInfo(hostPath) {
92486
92534
  try {
92487
92535
  let branchOut = "";
92488
- const branchHandle = spawnStream(
92489
- "git",
92490
- ["-C", hostPath, "rev-parse", "--abbrev-ref", "HEAD"],
92491
- { onStdout: (line2) => {
92536
+ const branchHandle = spawnStream("git", ["-C", hostPath, "rev-parse", "--abbrev-ref", "HEAD"], {
92537
+ onStdout: (line2) => {
92492
92538
  branchOut += line2;
92493
- } }
92494
- );
92539
+ }
92540
+ });
92495
92541
  const branchCode = await branchHandle.exitCode;
92496
92542
  if (branchCode !== 0) return null;
92497
92543
  let statusOut = "";
92498
- const statusHandle = spawnStream(
92499
- "git",
92500
- ["-C", hostPath, "status", "--porcelain"],
92501
- { onStdout: (line2) => {
92544
+ const statusHandle = spawnStream("git", ["-C", hostPath, "status", "--porcelain"], {
92545
+ onStdout: (line2) => {
92502
92546
  statusOut += line2;
92503
- } }
92504
- );
92547
+ }
92548
+ });
92505
92549
  await statusHandle.exitCode;
92506
92550
  return {
92507
92551
  branch: branchOut.trim() || "?",
@@ -92543,20 +92587,18 @@ async function resolveContext(opts) {
92543
92587
  throw err;
92544
92588
  }
92545
92589
  if (!fs15.existsSync(configPath)) {
92546
- process.stderr.write(
92547
- `dev-cockpit mount: cockpit.yaml at ${configPath} doesn't exist.
92548
- `
92549
- );
92590
+ process.stderr.write(`dev-cockpit mount: cockpit.yaml at ${configPath} doesn't exist.
92591
+ `);
92550
92592
  process.exit(1);
92551
92593
  }
92552
92594
  const config = loadConfig(configPath, {
92553
92595
  configSchemaExt: profile?.configSchemaExt,
92554
92596
  profileKey: profile?.appName
92555
92597
  });
92556
- const explicitService = opts.service ?? config.docker?.services?.[0]?.name;
92598
+ const explicitService = opts.service ?? config.mount.service ?? config.docker?.services?.[0]?.name;
92557
92599
  if (!explicitService) {
92558
92600
  process.stderr.write(
92559
- "dev-cockpit mount: no target service. Pass --service <name> or set docker.services in cockpit.yaml.\n"
92601
+ "dev-cockpit mount: no target service. Pass --service <name>, set mount.service in cockpit.yaml, or set docker.services in cockpit.yaml.\n"
92560
92602
  );
92561
92603
  process.exit(1);
92562
92604
  }
@@ -92584,46 +92626,188 @@ var stdoutSink = (line2) => process.stdout.write(`${line2}
92584
92626
  `);
92585
92627
  var stderrSink = (line2) => process.stderr.write(`${line2}
92586
92628
  `);
92587
- function applyExclude(mounts, exclude) {
92588
- if (exclude.length === 0) return [...mounts];
92589
- const set = new Set(exclude);
92590
- return mounts.filter((m) => {
92591
- const name = m.meta?.["name"] ?? "";
92592
- return !set.has(name);
92629
+ function mountLabel(m) {
92630
+ const fallbackName = m.containerPath ?? (m.kind === "symlink-only" ? `(symlink) ${m.hostPath}` : m.hostPath);
92631
+ const name = m.meta?.["name"] ?? fallbackName;
92632
+ const type = m.meta?.["type"];
92633
+ return type ? `${name} (${type})` : name;
92634
+ }
92635
+ function isUnmatched(m) {
92636
+ return m.meta?.["unmatched"] === true;
92637
+ }
92638
+ var CUSTOM_PATH_SENTINEL = "__custom__";
92639
+ async function pickSourceForMount(m, prompts) {
92640
+ const alts = m.alternativeSources ?? [];
92641
+ const choices = [
92642
+ ...alts.map((s) => ({ name: s, value: s })),
92643
+ { name: "Custom path...", value: CUSTOM_PATH_SENTINEL }
92644
+ ];
92645
+ const chosen = await prompts.select({
92646
+ message: `Source for ${mountLabel(m)}:`,
92647
+ choices,
92648
+ default: alts.includes(m.hostPath) ? m.hostPath : void 0
92593
92649
  });
92650
+ if (chosen !== CUSTOM_PATH_SENTINEL) return chosen;
92651
+ return (await prompts.input({
92652
+ message: "Host path (absolute):",
92653
+ validate: (v) => v.trim().length > 0 && path20.isAbsolute(v.trim()) ? true : "must be an absolute path"
92654
+ })).trim();
92594
92655
  }
92595
92656
  async function pickMounts(candidates) {
92596
- const { checkbox } = await import("@inquirer/prompts");
92597
- const choices = candidates.map((m) => {
92598
- const fallbackName = m.containerPath ?? (m.kind === "symlink-only" ? `(symlink) ${m.hostPath}` : m.hostPath);
92599
- const name = m.meta?.["name"] ?? fallbackName;
92600
- const type = m.meta?.["type"];
92601
- const label = type ? `${name} (${type}) ${m.hostPath}` : `${name} ${m.hostPath}`;
92602
- return { name: label, value: m, checked: true };
92603
- });
92604
- return checkbox({
92605
- message: "Select mounts to apply:",
92606
- choices
92607
- });
92657
+ const { checkbox, confirm, input, select } = await import("@inquirer/prompts");
92658
+ const prompts = { select, input };
92659
+ let selected = [];
92660
+ if (candidates.length > 0) {
92661
+ const choices = candidates.map((m) => {
92662
+ const unmatched = isUnmatched(m);
92663
+ const suffix = unmatched ? " (no source found \u2014 pick one)" : ` ${m.hostPath}`;
92664
+ return {
92665
+ name: `${mountLabel(m)}${suffix}`,
92666
+ value: m,
92667
+ checked: !unmatched
92668
+ };
92669
+ });
92670
+ selected = await checkbox({ message: "Select mounts to apply:", choices });
92671
+ }
92672
+ const unmatchedRows = selected.filter(isUnmatched);
92673
+ const matchedWithAlts = selected.filter(
92674
+ (m) => !isUnmatched(m) && (m.alternativeSources?.length ?? 0) > 0
92675
+ );
92676
+ for (const m of unmatchedRows) {
92677
+ const chosen = await pickSourceForMount(m, prompts);
92678
+ const next = { ...m, hostPath: chosen };
92679
+ if (next.meta && "unmatched" in next.meta) {
92680
+ const cleaned = { ...next.meta };
92681
+ delete cleaned["unmatched"];
92682
+ next.meta = cleaned;
92683
+ }
92684
+ const idx = selected.indexOf(m);
92685
+ if (idx >= 0) selected[idx] = next;
92686
+ }
92687
+ const overridable = matchedWithAlts;
92688
+ if (overridable.length > 0) {
92689
+ const wantOverride = await confirm({
92690
+ message: "Override the source dir for any of the selected mounts?",
92691
+ default: false
92692
+ });
92693
+ if (wantOverride) {
92694
+ const toEdit = await checkbox({
92695
+ message: "Pick the mounts whose source you want to change:",
92696
+ choices: overridable.map((m) => ({
92697
+ name: `${mountLabel(m)} (current: ${m.hostPath})`,
92698
+ value: m,
92699
+ checked: false
92700
+ }))
92701
+ });
92702
+ for (const m of toEdit) {
92703
+ const chosen = await pickSourceForMount(m, prompts);
92704
+ const idx = selected.indexOf(m);
92705
+ if (idx >= 0) selected[idx] = { ...m, hostPath: chosen };
92706
+ }
92707
+ }
92708
+ }
92709
+ const knownContainerPaths = Array.from(
92710
+ new Set(
92711
+ candidates.map((c3) => c3.containerPath).filter((p) => typeof p === "string" && p.length > 0)
92712
+ )
92713
+ );
92714
+ const knownHostPaths = Array.from(
92715
+ new Set(candidates.flatMap((c3) => c3.alternativeSources ?? []))
92716
+ );
92717
+ while (await confirm({
92718
+ message: candidates.length === 0 ? "Add a custom mount?" : "Add another (custom) mount?",
92719
+ default: false
92720
+ })) {
92721
+ let hostPath;
92722
+ if (knownHostPaths.length > 0) {
92723
+ const picked = await select({
92724
+ message: "Host path:",
92725
+ choices: [
92726
+ ...knownHostPaths.map((s) => ({ name: s, value: s })),
92727
+ { name: "Custom path...", value: CUSTOM_PATH_SENTINEL }
92728
+ ]
92729
+ });
92730
+ if (picked === CUSTOM_PATH_SENTINEL) {
92731
+ hostPath = (await input({
92732
+ message: "Host path (absolute):",
92733
+ validate: (v) => v.trim().length > 0 && path20.isAbsolute(v.trim()) ? true : "must be an absolute path"
92734
+ })).trim();
92735
+ } else {
92736
+ hostPath = picked;
92737
+ }
92738
+ } else {
92739
+ hostPath = (await input({
92740
+ message: "Host path (absolute):",
92741
+ validate: (v) => v.trim().length > 0 && path20.isAbsolute(v.trim()) ? true : "must be an absolute path"
92742
+ })).trim();
92743
+ }
92744
+ let containerPath;
92745
+ if (knownContainerPaths.length > 0) {
92746
+ const picked = await select({
92747
+ message: "Container path:",
92748
+ choices: [
92749
+ ...knownContainerPaths.map((p) => ({ name: p, value: p })),
92750
+ { name: "Custom path...", value: CUSTOM_PATH_SENTINEL }
92751
+ ]
92752
+ });
92753
+ if (picked === CUSTOM_PATH_SENTINEL) {
92754
+ containerPath = (await input({
92755
+ message: "Container path (absolute):",
92756
+ validate: (v) => v.trim().length > 0 && path20.isAbsolute(v.trim()) ? true : "must be an absolute path"
92757
+ })).trim();
92758
+ } else {
92759
+ containerPath = picked;
92760
+ }
92761
+ } else {
92762
+ containerPath = (await input({
92763
+ message: "Container path (absolute):",
92764
+ validate: (v) => v.trim().length > 0 && path20.isAbsolute(v.trim()) ? true : "must be an absolute path"
92765
+ })).trim();
92766
+ }
92767
+ const name = (await input({
92768
+ message: "Name (optional, used for status table + log lines):"
92769
+ })).trim();
92770
+ selected.push({
92771
+ hostPath,
92772
+ containerPath,
92773
+ kind: "bind",
92774
+ ...name ? { meta: { name } } : {}
92775
+ });
92776
+ }
92777
+ return selected;
92608
92778
  }
92609
92779
  async function mountCommand(opts = {}) {
92610
92780
  const ctx = await resolveContext(opts);
92611
- const { workspaceRoot, appName, service, configMounts, providerMounts, overlayPath, manifestPath: manifestPath2, profile } = ctx;
92612
- const merged = applyExclude(mergeMounts(configMounts, providerMounts), opts.exclude ?? []);
92613
- if (merged.length === 0) {
92614
- process.stdout.write(
92615
- "dev-cockpit mount: no mount candidates from config.mounts[] or profile.mountCandidatesProvider.\n"
92616
- );
92617
- return;
92618
- }
92781
+ const {
92782
+ workspaceRoot,
92783
+ appName,
92784
+ service,
92785
+ configMounts,
92786
+ providerMounts,
92787
+ overlayPath,
92788
+ manifestPath: manifestPath2,
92789
+ profile
92790
+ } = ctx;
92791
+ const merged = mergeMounts(configMounts, providerMounts);
92619
92792
  let selected;
92620
92793
  if (opts.quiet || configMounts.length > 0 && providerMounts.length === 0) {
92794
+ if (merged.length === 0) {
92795
+ process.stdout.write(
92796
+ "dev-cockpit mount: no mount candidates from config.mounts[] or profile.mountCandidatesProvider.\n"
92797
+ );
92798
+ return;
92799
+ }
92621
92800
  selected = merged;
92622
92801
  if (opts.quiet) {
92623
92802
  process.stdout.write(`dev-cockpit mount: applying all ${selected.length} candidate(s).
92624
92803
  `);
92625
92804
  }
92626
92805
  } else {
92806
+ if (merged.length === 0) {
92807
+ process.stdout.write(
92808
+ "dev-cockpit mount: no mount candidates discovered. You can add custom mounts interactively below, or Ctrl-C to bail.\n"
92809
+ );
92810
+ }
92627
92811
  selected = await pickMounts(merged);
92628
92812
  if (selected.length === 0) {
92629
92813
  process.stdout.write("dev-cockpit mount: nothing selected; nothing to apply.\n");
@@ -92656,7 +92840,8 @@ async function mountCommand(opts = {}) {
92656
92840
  `
92657
92841
  );
92658
92842
  }
92659
- for (const p of report.skipped) process.stdout.write(`dev-cockpit mount: skipped (could not replace) \u2192 ${p}
92843
+ for (const p of report.skipped)
92844
+ process.stdout.write(`dev-cockpit mount: skipped (could not replace) \u2192 ${p}
92660
92845
  `);
92661
92846
  }
92662
92847
  if (profile?.onMountApply) {
@@ -92798,8 +92983,10 @@ async function migrateConfigCommand(opts = {}) {
92798
92983
  }
92799
92984
  const fromVersion = raw["version"];
92800
92985
  if (fromVersion === CONFIG_VERSION) {
92801
- process.stdout.write(`dev-cockpit migrate-config: already at v${CONFIG_VERSION}; nothing to do.
92802
- `);
92986
+ process.stdout.write(
92987
+ `dev-cockpit migrate-config: already at v${CONFIG_VERSION}; nothing to do.
92988
+ `
92989
+ );
92803
92990
  return;
92804
92991
  }
92805
92992
  if (fromVersion > CONFIG_VERSION) {
@@ -92851,24 +93038,32 @@ function buildCli(opts = {}) {
92851
93038
  const program2 = new Command();
92852
93039
  const profile = opts.profile;
92853
93040
  const appName = profile?.appName ?? "dev-cockpit";
92854
- program2.name(appName).description(
92855
- profile ? `${appName} \u2014 built on dev-cockpit` : "Generic terminal UI dev cockpit"
92856
- ).version(readPackageVersion(), "-V, --version", "Output the version number");
93041
+ program2.name(appName).description(profile ? `${appName} \u2014 built on dev-cockpit` : "Generic terminal UI dev cockpit").version(readPackageVersion(), "-V, --version", "Output the version number");
92857
93042
  if (profile?.setupCli) {
92858
93043
  profile.setupCli(program2);
92859
93044
  }
92860
93045
  if (!hasCommand(program2, "dev")) {
92861
- 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) => {
93046
+ program2.command("dev").description("Boot the three-pane TUI (Repos / Output / Health / Help)").option(
93047
+ "-c, --config <path>",
93048
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
93049
+ ).action(async (cmdOpts) => {
92862
93050
  await devCommand({ config: cmdOpts.config, profile });
92863
93051
  });
92864
93052
  }
92865
93053
  if (!hasCommand(program2, "doctor")) {
92866
- 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) => {
93054
+ program2.command("doctor").description("Run all health checks once and print a status table").option(
93055
+ "-c, --config <path>",
93056
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
93057
+ ).action(async (cmdOpts) => {
92867
93058
  await doctorCommand({ config: cmdOpts.config, profile });
92868
93059
  });
92869
93060
  }
92870
93061
  if (!hasCommand(program2, "init-config")) {
92871
- 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(
93062
+ 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(
93063
+ "-i, --interactive",
93064
+ "Walk through prompts to populate the file (skips static template)",
93065
+ false
93066
+ ).option("-o, --output <path>", "Write the file to this path instead of <cwd>/cockpit.yaml").action(
92872
93067
  async (cmdOpts) => {
92873
93068
  await initConfigCommand({
92874
93069
  force: cmdOpts.force ?? false,
@@ -92883,42 +93078,52 @@ function buildCli(opts = {}) {
92883
93078
  }
92884
93079
  if (!hasCommand(program2, "link")) {
92885
93080
  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) => {
92886
- const { linkAddCommand } = await import("./link-VWT2VQH6.js");
93081
+ const { linkAddCommand } = await import("./link-Y7OFHOUP.js");
92887
93082
  linkAddCommand({ configPath });
92888
93083
  });
92889
93084
  linkCmd.command("list").description("Print all registered project \u2192 config mappings").action(async () => {
92890
- const { linkListCommand } = await import("./link-VWT2VQH6.js");
93085
+ const { linkListCommand } = await import("./link-Y7OFHOUP.js");
92891
93086
  linkListCommand();
92892
93087
  });
92893
93088
  linkCmd.command("remove").description("Unregister the current directory from the manifest").action(async () => {
92894
- const { linkRemoveCommand } = await import("./link-VWT2VQH6.js");
93089
+ const { linkRemoveCommand } = await import("./link-Y7OFHOUP.js");
92895
93090
  linkRemoveCommand();
92896
93091
  });
92897
93092
  }
92898
93093
  if (!hasCommand(program2, "mount")) {
92899
- 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(
92900
- "-e, --exclude <name>",
92901
- "Exclude a candidate by meta.name (repeatable)",
92902
- (val, prev) => [...prev, val],
92903
- []
92904
- ).action(async (cmdOpts) => {
93094
+ const mountCmd = program2.command("mount").description(
93095
+ `Apply a docker-compose bind-mount overlay (interactive picker by default)
93096
+
93097
+ 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\``
93098
+ ).option(
93099
+ "-c, --config <path>",
93100
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
93101
+ ).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) => {
92905
93102
  await mountCommand({
92906
93103
  config: cmdOpts.config,
92907
93104
  service: cmdOpts.service,
92908
93105
  quiet: cmdOpts.quiet ?? false,
92909
- exclude: cmdOpts.exclude ?? [],
92910
93106
  profile
92911
93107
  });
92912
93108
  });
92913
- 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) => {
93109
+ mountCmd.command("status").description("Show active mounts with branch, dirty, and broken-symlink status").option(
93110
+ "-c, --config <path>",
93111
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
93112
+ ).action(async (cmdOpts) => {
92914
93113
  await mountStatusCommand({ config: cmdOpts.config, profile });
92915
93114
  });
92916
- 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) => {
93115
+ mountCmd.command("clear").description("Remove the overlay, manifest, and managed symlinks; runs profile.onMountClear").option(
93116
+ "-c, --config <path>",
93117
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
93118
+ ).action(async (cmdOpts) => {
92917
93119
  await mountClearCommand({ config: cmdOpts.config, profile });
92918
93120
  });
92919
93121
  }
92920
93122
  if (!hasCommand(program2, "migrate-config")) {
92921
- 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) => {
93123
+ program2.command("migrate-config").description("Rewrite cockpit.yaml at the current CONFIG_VERSION").option(
93124
+ "-c, --config <path>",
93125
+ "Path to cockpit.yaml (default: <cwd>/cockpit.yaml or registered via `link`)"
93126
+ ).option("--dry-run", "Print the migrated YAML to stdout instead of writing").action(async (cmdOpts) => {
92922
93127
  await migrateConfigCommand({ config: cmdOpts.config, dryRun: cmdOpts.dryRun });
92923
93128
  });
92924
93129
  }