@tdk-landscape/tdk-cli-core 1.3.144 → 1.3.145

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.
@@ -10,6 +10,7 @@ import { DEVCONTAINER_DOCKER_FIX, detectHost, isContainerHost, WEBCONTAINER_DOCS
10
10
  import { projectNeedsBun } from "../utils/bun-requirement.js";
11
11
  import { AUTOGENERATED_DIR, CONTAINER_RUNTIME_CHECK, MASTER_CONFIG_FILES, PACKAGE_JSON, REQUIRED_PACKAGE_SCRIPTS, } from "../utils/constants.js";
12
12
  import { isPathDiscovered, readDiscoveryPaths } from "../utils/discovery-paths.js";
13
+ import { checkDiskSpace, dockerDesktopStuck } from "../utils/doctor-host.js";
13
14
  import { collectDoctorChecks, createDoctorReport, getDoctorExitCode, } from "../utils/doctor-report.js";
14
15
  import { checkHealthRoutes, checkIngressPorts, checkPrivateNpmRegistry, checkTiltResourceHealth, projectConfigEnablesVerdaccio, summarizeServiceProbes, } from "../utils/doctor-runtime.js";
15
16
  import { checkStarlarkLoadExports } from "../utils/doctor-starlark.js";
@@ -202,6 +203,10 @@ export async function checkDockerRuntime() {
202
203
  };
203
204
  }
204
205
  }
206
+ // Docker Desktop open but its engine down or stuck: say so instead of "not running".
207
+ const stuck = dockerDesktopStuck(await succeeds("pgrep -f com.docker.backend"));
208
+ if (stuck)
209
+ return stuck;
205
210
  // Docker not running: check for Colima, then Podman
206
211
  if (await succeeds("colima status")) {
207
212
  return {
@@ -1026,6 +1031,7 @@ export const doctorCommand = new Command("doctor")
1026
1031
  message: "Ingress port plan is unavailable",
1027
1032
  },
1028
1033
  checkDockerRuntime,
1034
+ () => checkDiskSpace(),
1029
1035
  checkDockerOperatingSystem,
1030
1036
  checkTilt,
1031
1037
  checkDockerCompose,
@@ -11,7 +11,7 @@ import { createMachineEnvelope, writeMachineError } from "../utils/machine-outpu
11
11
  import { findProjectRoot } from "../utils/paths.js";
12
12
  import { deriveServiceStates } from "../utils/service-runtime-state.js";
13
13
  import { buildServicePorts, buildStackPorts } from "../utils/status-ports.js";
14
- import { getTiltfilePath, isTiltAvailable, runTilt } from "../utils/tilt.js";
14
+ import { isTiltAvailable, runTilt } from "../utils/tilt.js";
15
15
  import { getTiltPollingPort } from "../utils/tilt-startup.js";
16
16
  import { evaluateTiltReadiness, tiltGetUiResources } from "../utils/up-readiness.js";
17
17
  export const statusCommand = new Command("status")
@@ -43,22 +43,6 @@ export const statusCommand = new Command("status")
43
43
  }
44
44
  let tiltResources = null;
45
45
  let queryError = null;
46
- if (options.json && options.tilt && tiltAvailable) {
47
- const result = await runTilt("get", ["-f", getTiltfilePath(), "resources", "--output=json"], {
48
- inheritStdio: false,
49
- });
50
- if (result.exitCode === 0) {
51
- try {
52
- tiltResources = JSON.parse(result.stdout);
53
- }
54
- catch (error) {
55
- queryError = `Tilt returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`;
56
- }
57
- }
58
- else {
59
- queryError = result.stderr || "Could not retrieve Tilt resource status";
60
- }
61
- }
62
46
  // A single look at the running Tilt, so a caller that started `tdk up` detached can poll for readiness. Null when no
63
47
  // Tilt answers or its output cannot be read.
64
48
  let readiness = null;
@@ -67,6 +51,20 @@ export const statusCommand = new Command("status")
67
51
  const tiltPort = getTiltPollingPort(process.env.TILT_PORT);
68
52
  if (tiltAvailable) {
69
53
  const text = await tiltGetUiResources(tiltPort);
54
+ // `tilt get` has no -f flag; resources come from the running Tilt's API on its port, the same source as readiness.
55
+ if (options.json && options.tilt) {
56
+ if (text) {
57
+ try {
58
+ tiltResources = JSON.parse(text);
59
+ }
60
+ catch (error) {
61
+ queryError = `Tilt returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`;
62
+ }
63
+ }
64
+ else {
65
+ queryError = `No Tilt is answering on port ${tiltPort}. Start it with: tdk up`;
66
+ }
67
+ }
70
68
  if (text) {
71
69
  const deferred = getDeferredResourceNames();
72
70
  try {
@@ -171,15 +169,14 @@ export const statusCommand = new Command("status")
171
169
  if (options.tilt && tiltAvailable) {
172
170
  console.log();
173
171
  showStep("Tilt Resources:");
174
- const tiltfilePath = getTiltfilePath();
175
- const result = await runTilt("get", ["-f", tiltfilePath, "resources"], {
172
+ const result = await runTilt("get", ["uiresources", "--port", String(tiltPort)], {
176
173
  inheritStdio: false,
177
174
  });
178
175
  if (result.exitCode === 0) {
179
176
  console.log(result.stdout || chalk.gray(" No active tilt resources"));
180
177
  }
181
178
  else {
182
- showDetail("Could not retrieve tilt resource status");
179
+ showDetail(`No Tilt is answering on port ${tiltPort}. Start it with: tdk up`);
183
180
  }
184
181
  }
185
182
  console.log();
@@ -1,10 +1,11 @@
1
1
  // Copyright (c) 2026 TDK Landscape contributors
2
2
  // SPDX-License-Identifier: MIT
3
+ import { existsSync } from "node:fs";
3
4
  import { connect } from "node:net";
4
5
  import { join } from "node:path";
5
6
  import chalk from "chalk";
6
7
  import { Command } from "commander";
7
- import { ensureProjectRuntimeAssets, ProjectConfigNotFoundError, verifyMasterConfigs, } from "../generator/template-engine.js";
8
+ import { ensureProjectRuntimeAssets, generateMasterConfigs, ProjectConfigNotFoundError, verifyMasterConfigs, } from "../generator/template-engine.js";
8
9
  import { DEVCONTAINER_DOCKER_FIX, detectHost, isContainerHost, WEBCONTAINER_UP_MESSAGE, } from "../utils/agent-host.js";
9
10
  import { handleDryRun } from "../utils/command-helpers.js";
10
11
  import { STANDARD_PORTS } from "../utils/constants.js";
@@ -256,6 +257,14 @@ export const upCommand = new Command("up")
256
257
  console.log(chalk.gray(`Added ${addedEnvKeys.join(", ")} to .env (generated; existing values unchanged)`));
257
258
  }
258
259
  }
260
+ // A project made by `tdk import` has .tdk/project.json but no generated files yet. Generate them rather than letting
261
+ // Tilt fail on a missing Tiltfile.
262
+ if (foundRoot && !existsSync(join(projectRoot, ".tdk", ".tdk-out", "Tiltfile"))) {
263
+ if (!options.quiet) {
264
+ console.log(chalk.gray("No generated Tiltfile yet; generating .tdk/.tdk-out/ ..."));
265
+ }
266
+ await generateMasterConfigs(projectRoot, { quiet: options.quiet });
267
+ }
259
268
  const copiedAssets = ensureProjectRuntimeAssets(projectRoot);
260
269
  if (copiedAssets.length > 0 && options.verbose && !options.quiet) {
261
270
  console.log(chalk.gray(`Refreshed runtime assets: ${copiedAssets.join(", ")}`));
@@ -428,7 +428,7 @@ export async function generateMasterConfigs(projectRoot, options = {}) {
428
428
  throw new Error(`Refusing to generate config from unsafe service.json values:\n${unsafeFields.map((error) => ` ${error}`).join("\n")}`);
429
429
  }
430
430
  await warnIfSablierUnlicensed(projectRoot);
431
- if (ensureRootWorkspaceManifest(projectRoot, projectConfig)) {
431
+ if (ensureRootWorkspaceManifest(projectRoot, projectConfig) && !options.quiet) {
432
432
  console.log("✓ Generated: package.json (workspace root)");
433
433
  }
434
434
  // Verdaccio is Premium, but unlike the old posture here, this is NOT the
@@ -461,14 +461,17 @@ export async function generateMasterConfigs(projectRoot, options = {}) {
461
461
  const content = files[filename];
462
462
  const filePath = assertTdkGeneratedPath(projectRoot, path.join(".tdk", ".tdk-out", filename));
463
463
  writeTextFileAtomic(filePath, content);
464
- console.log(`✓ Generated: .tdk/.tdk-out/${filename}`);
464
+ if (!options.quiet)
465
+ console.log(`✓ Generated: .tdk/.tdk-out/${filename}`);
465
466
  }
466
467
  if (isStackFeatureEnabledInStacks(projectConfig.phases, DATABASE_MANAGEMENT_FEATURE)) {
467
468
  const composePath = assertTdkGeneratedPath(projectRoot, path.join("services", "platform", DATABASE_MANAGEMENT_FEATURE, DOCKER_COMPOSE_FILE));
468
469
  const composeDir = path.dirname(composePath);
469
470
  fs.mkdirSync(composeDir, { recursive: true });
470
471
  writeTextFileAtomic(composePath, generateDatabaseManagementCompose(projectConfig));
471
- console.log("✓ Generated: services/platform/database-management/docker-compose.yml");
472
+ if (!options.quiet) {
473
+ console.log("✓ Generated: services/platform/database-management/docker-compose.yml");
474
+ }
472
475
  }
473
476
  // Copy .tiltignore to project root so Tilt uses it
474
477
  const tiltignoreSource = path.join(outputDir, TILTIGNORE_FILE);
@@ -0,0 +1,66 @@
1
+ // Copyright (c) 2026 TDK Landscape contributors
2
+ // SPDX-License-Identifier: MIT
3
+ import { statfsSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ export const DISK_SPACE_CHECK = "Disk Space";
6
+ const GIB = 1024 ** 3;
7
+ // Docker Desktop keeps images and containers in one large disk image. When the host disk fills up, the engine
8
+ // stops answering while the Docker Desktop app keeps running, so `tdk up` waits with no visible error.
9
+ export const DISK_FAIL_GIB = 2;
10
+ export const DISK_WARN_GIB = 10;
11
+ export const DISK_CLEANUP_FIX = [
12
+ "Free disk space, then retry: tdk doctor",
13
+ " docker system prune -af # unused images and build cache (needs Docker running)",
14
+ " npm cache clean --force",
15
+ " bun pm cache rm",
16
+ " brew cleanup -s",
17
+ ].join("\n");
18
+ export function checkDiskSpace(statfs = statfsSync, path = homedir()) {
19
+ let freeGib;
20
+ try {
21
+ const stats = statfs(path);
22
+ freeGib = (Number(stats.bavail) * Number(stats.bsize)) / GIB;
23
+ }
24
+ catch {
25
+ return {
26
+ name: DISK_SPACE_CHECK,
27
+ didPass: true,
28
+ isSkipped: true,
29
+ message: "Could not read free disk space",
30
+ };
31
+ }
32
+ const shown = `${freeGib.toFixed(1)} GB free`;
33
+ if (freeGib < DISK_FAIL_GIB) {
34
+ return {
35
+ name: DISK_SPACE_CHECK,
36
+ didPass: false,
37
+ message: `${shown}: Docker cannot build or start containers`,
38
+ fix: DISK_CLEANUP_FIX,
39
+ };
40
+ }
41
+ if (freeGib < DISK_WARN_GIB) {
42
+ return {
43
+ name: DISK_SPACE_CHECK,
44
+ didPass: false,
45
+ isWarning: true,
46
+ message: `${shown}: image builds may fail`,
47
+ fix: DISK_CLEANUP_FIX,
48
+ };
49
+ }
50
+ return { name: DISK_SPACE_CHECK, didPass: true, message: shown };
51
+ }
52
+ export const DOCKER_DESKTOP_STUCK_FIX = "Quit Docker Desktop completely (whale icon > Quit Docker Desktop), reopen it, and wait until it shows Running. If it keeps getting stuck, check free disk space. Then retry: tdk doctor";
53
+ /**
54
+ * Docker Desktop's app process is up but `docker ps` fails: the engine is stopped or stuck, which needs a different fix
55
+ * than "start Docker". Returns the failing check, or null when that is not the situation.
56
+ */
57
+ export function dockerDesktopStuck(desktopRunning, platform = process.platform) {
58
+ if (platform !== "darwin" || !desktopRunning)
59
+ return null;
60
+ return {
61
+ name: "Container Runtime",
62
+ didPass: false,
63
+ message: "Docker Desktop is open, but its engine is not responding",
64
+ fix: DOCKER_DESKTOP_STUCK_FIX,
65
+ };
66
+ }