@tdk-landscape/tdk-cli-core 1.3.143 → 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.
- package/cli/dist/commands/doctor.js +6 -0
- package/cli/dist/commands/status.js +21 -23
- package/cli/dist/commands/ui.js +3 -2
- package/cli/dist/commands/up.js +10 -1
- package/cli/dist/generator/template-engine.js +6 -3
- package/cli/dist/utils/doctor-host.js +66 -0
- package/cli/dist/utils/smoke.js +6 -1
- package/cli/dist/utils/tilt-startup.js +7 -1
- package/cli/dist-bundle/{chunk-FFPLKZUH.js → chunk-47CDYJ5E.js} +57 -56
- package/cli/dist-bundle/cli.js +254 -254
- package/cli/dist-bundle/{cold-preflight-DUL4575P.js → cold-preflight-F2FM46HK.js} +1 -1
- package/cli/package.json +1 -1
- package/engine/assets/docker/wake-gateway/Dockerfile +1 -0
- package/engine/assets/docker/wake-gateway/server.ts +3 -0
- package/engine/topologies/tilt/generators/npmrc.star +4 -7
- package/ext/ide-components/shared/templates/base.html +4 -8
- package/package.json +1 -1
|
@@ -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,8 @@ 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 {
|
|
14
|
+
import { isTiltAvailable, runTilt } from "../utils/tilt.js";
|
|
15
|
+
import { getTiltPollingPort } from "../utils/tilt-startup.js";
|
|
15
16
|
import { evaluateTiltReadiness, tiltGetUiResources } from "../utils/up-readiness.js";
|
|
16
17
|
export const statusCommand = new Command("status")
|
|
17
18
|
.description("Show status of resources and stacks")
|
|
@@ -42,30 +43,28 @@ export const statusCommand = new Command("status")
|
|
|
42
43
|
}
|
|
43
44
|
let tiltResources = null;
|
|
44
45
|
let queryError = null;
|
|
45
|
-
if (options.json && options.tilt && tiltAvailable) {
|
|
46
|
-
const result = await runTilt("get", ["-f", getTiltfilePath(), "resources", "--output=json"], {
|
|
47
|
-
inheritStdio: false,
|
|
48
|
-
});
|
|
49
|
-
if (result.exitCode === 0) {
|
|
50
|
-
try {
|
|
51
|
-
tiltResources = JSON.parse(result.stdout);
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
queryError = `Tilt returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
queryError = result.stderr || "Could not retrieve Tilt resource status";
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
46
|
// A single look at the running Tilt, so a caller that started `tdk up` detached can poll for readiness. Null when no
|
|
62
47
|
// Tilt answers or its output cannot be read.
|
|
63
48
|
let readiness = null;
|
|
64
49
|
// The same look at Tilt feeds readiness and each service's state, so `tdk status` and `tdk ui` cannot disagree.
|
|
65
50
|
let serviceStates = null;
|
|
51
|
+
const tiltPort = getTiltPollingPort(process.env.TILT_PORT);
|
|
66
52
|
if (tiltAvailable) {
|
|
67
|
-
const
|
|
68
|
-
|
|
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
|
+
}
|
|
69
68
|
if (text) {
|
|
70
69
|
const deferred = getDeferredResourceNames();
|
|
71
70
|
try {
|
|
@@ -115,7 +114,7 @@ export const statusCommand = new Command("status")
|
|
|
115
114
|
? { blockedBy: serviceStates[resource.name]?.blockedBy }
|
|
116
115
|
: {}),
|
|
117
116
|
})),
|
|
118
|
-
ports: buildStackPorts(portPlan,
|
|
117
|
+
ports: buildStackPorts(portPlan, tiltPort),
|
|
119
118
|
stacks: discovery.stacks.map((stack) => ({
|
|
120
119
|
name: stack.name,
|
|
121
120
|
resourceCount: stack.resourceCount,
|
|
@@ -170,15 +169,14 @@ export const statusCommand = new Command("status")
|
|
|
170
169
|
if (options.tilt && tiltAvailable) {
|
|
171
170
|
console.log();
|
|
172
171
|
showStep("Tilt Resources:");
|
|
173
|
-
const
|
|
174
|
-
const result = await runTilt("get", ["-f", tiltfilePath, "resources"], {
|
|
172
|
+
const result = await runTilt("get", ["uiresources", "--port", String(tiltPort)], {
|
|
175
173
|
inheritStdio: false,
|
|
176
174
|
});
|
|
177
175
|
if (result.exitCode === 0) {
|
|
178
176
|
console.log(result.stdout || chalk.gray(" No active tilt resources"));
|
|
179
177
|
}
|
|
180
178
|
else {
|
|
181
|
-
showDetail(
|
|
179
|
+
showDetail(`No Tilt is answering on port ${tiltPort}. Start it with: tdk up`);
|
|
182
180
|
}
|
|
183
181
|
}
|
|
184
182
|
console.log();
|
package/cli/dist/commands/ui.js
CHANGED
|
@@ -16,6 +16,7 @@ import { createStatusMessageController } from "../utils/status-message.js";
|
|
|
16
16
|
import { getListRowFromMouseY, getTerminalRuleWidth } from "../utils/terminal-layout.js";
|
|
17
17
|
import { isTiltAvailable } from "../utils/tilt.js";
|
|
18
18
|
import { loadTiltEvents, stripTerminalControls, TiltEventsLoadError, } from "../utils/tilt-events.js";
|
|
19
|
+
import { getTiltPollingPort } from "../utils/tilt-startup.js";
|
|
19
20
|
import { formatUiKeyHint, formatUiKeyNames, UI_KEYMAP } from "../utils/ui-keymap.js";
|
|
20
21
|
import { applyServiceStates, fetchServiceStates, } from "../utils/ui-service-state.js";
|
|
21
22
|
// biome-ignore lint/correctness/noUnusedFunctionParameters: reserved callback prop kept in the component API
|
|
@@ -116,14 +117,14 @@ export const TUIApp = ({ animated = true }) => {
|
|
|
116
117
|
const [serviceStates, setServiceStates] = useState({});
|
|
117
118
|
useEffect(() => {
|
|
118
119
|
let cancelled = false;
|
|
119
|
-
const tiltPort =
|
|
120
|
+
const tiltPort = getTiltPollingPort(process.env.TILT_PORT);
|
|
120
121
|
// A Tilt call can outlast the 5 s interval; never start a poll while the last one is still running.
|
|
121
122
|
const run = singleFlight((next) => {
|
|
122
123
|
if (cancelled)
|
|
123
124
|
return;
|
|
124
125
|
setServiceStates((previous) => JSON.stringify(previous) === JSON.stringify(next) ? previous : next);
|
|
125
126
|
});
|
|
126
|
-
const poll = () => run(() => fetchServiceStates(services,
|
|
127
|
+
const poll = () => run(() => fetchServiceStates(services, tiltPort));
|
|
127
128
|
void poll();
|
|
128
129
|
const timer = setInterval(() => void poll(), 5000);
|
|
129
130
|
return () => {
|
package/cli/dist/commands/up.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/cli/dist/utils/smoke.js
CHANGED
|
@@ -116,7 +116,12 @@ function readPath(value, path) {
|
|
|
116
116
|
return undefined;
|
|
117
117
|
if (!Object.hasOwn(current, part))
|
|
118
118
|
return undefined;
|
|
119
|
-
|
|
119
|
+
// Resolve only own data properties. Besides avoiding inherited properties, this
|
|
120
|
+
// avoids invoking accessors on values supplied by smoke-test responses.
|
|
121
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, part);
|
|
122
|
+
if (!descriptor || !("value" in descriptor))
|
|
123
|
+
return undefined;
|
|
124
|
+
current = descriptor.value;
|
|
120
125
|
}
|
|
121
126
|
return current;
|
|
122
127
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// Copyright (c) 2026 TDK Landscape contributors
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { STANDARD_PORTS } from "./constants.js";
|
|
3
4
|
import { isValidPort } from "./validation.js";
|
|
4
5
|
export function parseTiltPort(value) {
|
|
5
6
|
if (value === undefined || value === "")
|
|
6
7
|
return { ok: true, port: undefined };
|
|
7
8
|
const port = Number(value);
|
|
8
|
-
if (
|
|
9
|
+
if (/[^0-9]/.test(value) || !isValidPort(port)) {
|
|
9
10
|
return {
|
|
10
11
|
ok: false,
|
|
11
12
|
message: `Invalid TILT_PORT "${value}": expected an integer from 1 to 65535.`,
|
|
@@ -13,6 +14,11 @@ export function parseTiltPort(value) {
|
|
|
13
14
|
}
|
|
14
15
|
return { ok: true, port };
|
|
15
16
|
}
|
|
17
|
+
/** Resolve the port for read-only polling, falling back when TILT_PORT is absent or invalid. */
|
|
18
|
+
export function getTiltPollingPort(value) {
|
|
19
|
+
const result = parseTiltPort(value);
|
|
20
|
+
return result.ok ? (result.port ?? STANDARD_PORTS.tiltUi) : STANDARD_PORTS.tiltUi;
|
|
21
|
+
}
|
|
16
22
|
export function secondUpAction(input) {
|
|
17
23
|
if (input.runningPorts.length === 0 || input.force)
|
|
18
24
|
return { action: "start" };
|