dev-cockpit 0.5.0 → 0.6.1

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.
@@ -25,6 +25,25 @@ export interface MountCommandOptions {
25
25
  quiet?: boolean;
26
26
  }
27
27
  export declare function mergeMounts(configMounts: readonly Mount[], providerMounts: readonly Mount[]): Mount[];
28
+ /**
29
+ * Seed the candidate list from the currently-applied manifest.
30
+ *
31
+ * Two jobs:
32
+ * 1. Manifest entries with no discovered/config counterpart (ad-hoc custom
33
+ * mounts added via the picker's add-custom loop) are appended as
34
+ * candidates — otherwise they'd be invisible on the next `mount` run and
35
+ * silently dropped by any re-apply, with no way to toggle just one off.
36
+ * 2. Returns the applied-key set so the picker can pre-check exactly the
37
+ * mounts that are currently applied (instead of pre-checking everything,
38
+ * where an absent-minded ⏎ would apply mounts the user never had).
39
+ *
40
+ * `appliedKeys` is null when no manifest exists — first-run pickers keep the
41
+ * legacy default (everything matched pre-checked).
42
+ */
43
+ export declare function seedCandidatesFromManifest(merged: readonly Mount[], manifestMounts: readonly Mount[] | null): {
44
+ candidates: Mount[];
45
+ appliedKeys: ReadonlySet<string> | null;
46
+ };
28
47
  /**
29
48
  * Interactive picker. Lazy-imports @inquirer/prompts to keep the `dev`
30
49
  * command's startup time clean. Three phases:
@@ -39,7 +58,7 @@ export declare function mergeMounts(configMounts: readonly Mount[], providerMoun
39
58
  * Returns the final mount set in source-of-truth order. The caller writes
40
59
  * the overlay / manifest from this list.
41
60
  */
42
- export declare function pickMounts(candidates: readonly Mount[]): Promise<Mount[]>;
61
+ export declare function pickMounts(candidates: readonly Mount[], appliedKeys?: ReadonlySet<string> | null): Promise<Mount[]>;
43
62
  export declare function mountCommand(opts?: MountCommandOptions): Promise<void>;
44
63
  export declare function mountStatusCommand(opts?: MountCommandOptions): Promise<void>;
45
64
  export declare function mountClearCommand(opts?: MountCommandOptions): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/commands/mount.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAoBvD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAWD,wBAAgB,WAAW,CACzB,YAAY,EAAE,SAAS,KAAK,EAAE,EAC9B,cAAc,EAAE,SAAS,KAAK,EAAE,GAC/B,KAAK,EAAE,CAKT;AAsHD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,SAAS,KAAK,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAqK/E;AAED,wBAAsB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6FhF;AAED,wBAAsB,kBAAkB,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoEtF;AAED,wBAAsB,iBAAiB,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCrF"}
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/commands/mount.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAoBvD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAWD,wBAAgB,WAAW,CACzB,YAAY,EAAE,SAAS,KAAK,EAAE,EAC9B,cAAc,EAAE,SAAS,KAAK,EAAE,GAC/B,KAAK,EAAE,CAKT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,KAAK,EAAE,EACxB,cAAc,EAAE,SAAS,KAAK,EAAE,GAAG,IAAI,GACtC;IAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;CAAE,CAUlE;AAsHD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,UAAU,EAAE,SAAS,KAAK,EAAE,EAC5B,WAAW,GAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAW,GAC7C,OAAO,CAAC,KAAK,EAAE,CAAC,CA6KlB;AAED,wBAAsB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwGhF;AAED,wBAAsB,kBAAkB,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoEtF;AAED,wBAAsB,iBAAiB,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCrF"}
@@ -1 +1 @@
1
- {"version":3,"file":"composer-drift.d.ts","sourceRoot":"","sources":["../../../src/health/predicates/composer-drift.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAoBD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,aAAa,EAClB,IAAI,GAAE,yBAA8B,GACnC,OAAO,CAAC,YAAY,CAAC,CA0CvB;AAeD,MAAM,WAAW,+BAAgC,SAAQ,yBAAyB;IAChF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;CAC1C;AAED,wBAAgB,wBAAwB,CACtC,IAAI,GAAE,+BAAoC,GACzC,WAAW,CAiBb"}
1
+ {"version":3,"file":"composer-drift.d.ts","sourceRoot":"","sources":["../../../src/health/predicates/composer-drift.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAoBD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,aAAa,EAClB,IAAI,GAAE,yBAA8B,GACnC,OAAO,CAAC,YAAY,CAAC,CA0CvB;AAeD,MAAM,WAAW,+BAAgC,SAAQ,yBAAyB;IAChF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;CAC1C;AAED,wBAAgB,wBAAwB,CACtC,IAAI,GAAE,+BAAoC,GACzC,WAAW,CA4Bb"}
@@ -24,4 +24,15 @@ export declare function runRemediation(key: RemediationKey, checks: HealthCheck[
24
24
  */
25
25
  export declare function findRemediation(key: RemediationKey, checks: HealthCheck[]): HealthCheck | undefined;
26
26
  export declare function dispatchRemediation(remediation: Remediation, ctx: HealthContext, workspaceRoot: string, sourceId: string): Promise<void>;
27
+ /**
28
+ * Run a single command, streaming its stdout/stderr into the health output log
29
+ * under `health:<sourceId>`. Resolves with the process exit code once it exits.
30
+ *
31
+ * Shared by the `command`-shape dispatcher above and by `run`-shape
32
+ * remediations that need to invoke one or more commands with a proper per-call
33
+ * `cwd` (e.g. `composer install` in several package dirs). Keeping this the one
34
+ * way a remediation reaches a subprocess means commands stay plain argv — no
35
+ * shell string is ever parsed, so `&&` / `(cd …)` can't leak into argv.
36
+ */
37
+ export declare function streamCommand(ctx: HealthContext, sourceId: string, program: string, args: string[], cwd: string): Promise<number>;
27
38
  //# sourceMappingURL=remediations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remediations.d.ts","sourceRoot":"","sources":["../../src/health/remediations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,WAAW,EAAE,EACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACrC,WAAW,EAAE,CASf;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAevE;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,WAAW,EAAE,EACrB,GAAG,EAAE,aAAa,EAClB,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACrC,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,WAAW,EAAE,GACpB,WAAW,GAAG,SAAS,CAEzB;AAED,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,WAAW,EACxB,GAAG,EAAE,aAAa,EAClB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAiCf"}
1
+ {"version":3,"file":"remediations.d.ts","sourceRoot":"","sources":["../../src/health/remediations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,WAAW,EAAE,EACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACrC,WAAW,EAAE,CASf;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAevE;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,WAAW,EAAE,EACrB,GAAG,EAAE,aAAa,EAClB,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACrC,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,WAAW,EAAE,GACpB,WAAW,GAAG,SAAS,CAEzB;AAED,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,WAAW,EACxB,GAAG,EAAE,aAAa,EAClB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,aAAa,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,CAoBjB"}
package/dist/index.js CHANGED
@@ -88257,6 +88257,9 @@ async function dispatchRemediation(remediation, ctx, workspaceRoot, sourceId) {
88257
88257
  if (!program2) return;
88258
88258
  const args = parts.slice(1);
88259
88259
  const cwd = remediation.cwd ?? workspaceRoot;
88260
+ await streamCommand(ctx, sourceId, program2, args, cwd);
88261
+ }
88262
+ async function streamCommand(ctx, sourceId, program2, args, cwd) {
88260
88263
  const handle = spawnStream(program2, args, {
88261
88264
  cwd,
88262
88265
  onStdout: (line2) => ctx.appendOutput?.({
@@ -88272,7 +88275,7 @@ async function dispatchRemediation(remediation, ctx, workspaceRoot, sourceId) {
88272
88275
  text: line2
88273
88276
  })
88274
88277
  });
88275
- await handle.exitCode;
88278
+ return handle.exitCode;
88276
88279
  }
88277
88280
 
88278
88281
  // src/health/notify-resolver.ts
@@ -91186,17 +91189,29 @@ function status3(severity, detail) {
91186
91189
  }
91187
91190
  function createComposerDriftCheck(opts = {}) {
91188
91191
  const dirs = opts.packageDirs ?? ["."];
91189
- const defaultCommand = dirs.map((d) => d === "" || d === "." ? "composer install" : `(cd ${d} && composer install)`).join(" && ");
91192
+ const id = opts.id ?? "composer-drift";
91190
91193
  return {
91191
- id: opts.id ?? "composer-drift",
91194
+ id,
91192
91195
  label: opts.label ?? "Composer",
91193
91196
  severity: opts.severity ?? "error",
91194
91197
  triggers: opts.triggers ?? ["startup", "lockfile"],
91195
91198
  predicate: (ctx) => checkComposerDrift(ctx, opts),
91199
+ // Run `composer install` once per package dir, each in its own cwd. A
91200
+ // declarative `command` string can't express this without shell syntax
91201
+ // (`&&`, `(cd …)`), and the dispatcher runs commands as plain argv — so
91202
+ // that syntax would reach composer as bogus package arguments. The `run`
91203
+ // shape keeps each invocation a clean argv in the right directory and
91204
+ // short-circuits on the first failure, mirroring `&&`.
91196
91205
  remediation: opts.remediation ?? {
91197
91206
  key: "R",
91198
91207
  label: "Run composer install",
91199
- command: defaultCommand
91208
+ run: async (ctx, workspaceRoot) => {
91209
+ for (const d of dirs) {
91210
+ const cwd = d === "" || d === "." ? workspaceRoot : path15.join(workspaceRoot, d);
91211
+ const code = await streamCommand(ctx, id, "composer", ["install"], cwd);
91212
+ if (code !== 0) break;
91213
+ }
91214
+ }
91200
91215
  }
91201
91216
  };
91202
91217
  }
@@ -93157,6 +93172,17 @@ function mergeMounts(configMounts, providerMounts) {
93157
93172
  for (const m of configMounts) seen.set(mountKey(m), m);
93158
93173
  return Array.from(seen.values());
93159
93174
  }
93175
+ function seedCandidatesFromManifest(merged, manifestMounts) {
93176
+ if (manifestMounts === null) {
93177
+ return { candidates: [...merged], appliedKeys: null };
93178
+ }
93179
+ const mergedKeys = new Set(merged.map(mountKey));
93180
+ const extras = manifestMounts.filter((m) => !mergedKeys.has(mountKey(m)));
93181
+ return {
93182
+ candidates: [...merged, ...extras],
93183
+ appliedKeys: new Set(manifestMounts.map(mountKey))
93184
+ };
93185
+ }
93160
93186
  async function resolveContext(opts) {
93161
93187
  const profile = opts.profile;
93162
93188
  let configPath;
@@ -93243,18 +93269,19 @@ async function pickSourceForMount(m, prompts) {
93243
93269
  validate: (v) => v.trim().length > 0 && path24.isAbsolute(v.trim()) ? true : "must be an absolute path"
93244
93270
  })).trim();
93245
93271
  }
93246
- async function pickMounts(candidates) {
93272
+ async function pickMounts(candidates, appliedKeys = null) {
93247
93273
  const { checkbox, confirm, input, select } = await import("@inquirer/prompts");
93248
93274
  const prompts = { select, input };
93249
93275
  let selected = [];
93250
93276
  if (candidates.length > 0) {
93251
93277
  const choices = candidates.map((m) => {
93252
93278
  const unmatched = isUnmatched(m);
93253
- const suffix = unmatched ? " (no source found \u2014 pick one)" : ` ${m.hostPath}`;
93279
+ const applied = appliedKeys?.has(mountKey(m)) ?? false;
93280
+ const suffix = unmatched ? " (no source found \u2014 pick one)" : ` ${m.hostPath}${applied ? " [applied]" : ""}`;
93254
93281
  return {
93255
93282
  name: `${mountLabel(m)}${suffix}`,
93256
93283
  value: m,
93257
- checked: !unmatched
93284
+ checked: !unmatched && (appliedKeys ? applied : true)
93258
93285
  };
93259
93286
  });
93260
93287
  selected = await checkbox({ message: "Select mounts to apply:", choices });
@@ -93377,26 +93404,32 @@ async function mountCommand(opts = {}) {
93377
93404
  profile
93378
93405
  } = ctx;
93379
93406
  const merged = mergeMounts(configMounts, providerMounts);
93407
+ let manifestMounts = null;
93408
+ try {
93409
+ manifestMounts = readMountManifest(manifestPath2)?.mounts ?? null;
93410
+ } catch {
93411
+ }
93412
+ const { candidates, appliedKeys } = seedCandidatesFromManifest(merged, manifestMounts);
93380
93413
  let selected;
93381
93414
  if (opts.quiet || configMounts.length > 0 && providerMounts.length === 0) {
93382
- if (merged.length === 0) {
93415
+ if (candidates.length === 0) {
93383
93416
  process.stdout.write(
93384
93417
  "dev-cockpit mount: no mount candidates from config.mounts[] or profile.mountCandidatesProvider.\n"
93385
93418
  );
93386
93419
  return;
93387
93420
  }
93388
- selected = merged;
93421
+ selected = candidates;
93389
93422
  if (opts.quiet) {
93390
93423
  process.stdout.write(`dev-cockpit mount: applying all ${selected.length} candidate(s).
93391
93424
  `);
93392
93425
  }
93393
93426
  } else {
93394
- if (merged.length === 0) {
93427
+ if (candidates.length === 0) {
93395
93428
  process.stdout.write(
93396
93429
  "dev-cockpit mount: no mount candidates discovered. You can add custom mounts interactively below, or Ctrl-C to bail.\n"
93397
93430
  );
93398
93431
  }
93399
- selected = await pickMounts(merged);
93432
+ selected = await pickMounts(candidates, appliedKeys);
93400
93433
  if (selected.length === 0) {
93401
93434
  process.stdout.write("dev-cockpit mount: nothing selected; nothing to apply.\n");
93402
93435
  return;