@tdk-landscape/tdk-cli-core 1.3.106 → 1.3.107
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.
|
@@ -11,7 +11,7 @@ _tdk_completions() {
|
|
|
11
11
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
12
12
|
|
|
13
13
|
# Main commands
|
|
14
|
-
local commands="project projects stack stacks resource resources up down status ui doctor runtime version completion help upgrade"
|
|
14
|
+
local commands="project projects stack stacks resource resources up down status ui doctor runtime version completion help upgrade mcp logs eject import config maintainers networks traefik"
|
|
15
15
|
|
|
16
16
|
# Options for specific commands
|
|
17
17
|
case "\${prev}" in
|
|
@@ -142,6 +142,14 @@ _tdk_commands() {
|
|
|
142
142
|
'version:Show version'
|
|
143
143
|
'upgrade:Upgrade TDK CLI'
|
|
144
144
|
'completion:Generate shell completions'
|
|
145
|
+
'mcp:Run a Model Context Protocol server over stdio for coding agents'
|
|
146
|
+
'logs:Print a bounded snapshot of recent logs from the running stack'
|
|
147
|
+
'eject:Take ownership of the generated Tilt and Docker files'
|
|
148
|
+
'import:Import the services a directory describes (runs tdk-import)'
|
|
149
|
+
'config:Manage project configuration and regenerate master files'
|
|
150
|
+
'maintainers:Maintainer file checks'
|
|
151
|
+
'networks:Show Traefik-routed URLs for all services'
|
|
152
|
+
'traefik:Show Traefik-routed URLs for all services'
|
|
145
153
|
'help:Show help'
|
|
146
154
|
)
|
|
147
155
|
_describe -t commands 'tdk command' commands
|
|
@@ -187,6 +195,14 @@ complete -c tdk -n '__fish_use_subcommand' -a 'runtime' -d 'Inspect packaged run
|
|
|
187
195
|
complete -c tdk -n '__fish_use_subcommand' -a 'version' -d 'Show version'
|
|
188
196
|
complete -c tdk -n '__fish_use_subcommand' -a 'upgrade' -d 'Upgrade TDK CLI'
|
|
189
197
|
complete -c tdk -n '__fish_use_subcommand' -a 'completion' -d 'Generate shell completions'
|
|
198
|
+
complete -c tdk -n '__fish_use_subcommand' -a 'mcp' -d 'Run a Model Context Protocol server over stdio for coding agents'
|
|
199
|
+
complete -c tdk -n '__fish_use_subcommand' -a 'logs' -d 'Print a bounded snapshot of recent logs from the running stack'
|
|
200
|
+
complete -c tdk -n '__fish_use_subcommand' -a 'eject' -d 'Take ownership of the generated Tilt and Docker files'
|
|
201
|
+
complete -c tdk -n '__fish_use_subcommand' -a 'import' -d 'Import the services a directory describes (runs tdk-import)'
|
|
202
|
+
complete -c tdk -n '__fish_use_subcommand' -a 'config' -d 'Manage project configuration and regenerate master files'
|
|
203
|
+
complete -c tdk -n '__fish_use_subcommand' -a 'maintainers' -d 'Maintainer file checks'
|
|
204
|
+
complete -c tdk -n '__fish_use_subcommand' -a 'networks' -d 'Show Traefik-routed URLs for all services'
|
|
205
|
+
complete -c tdk -n '__fish_use_subcommand' -a 'traefik' -d 'Show Traefik-routed URLs for all services'
|
|
190
206
|
complete -c tdk -n '__fish_use_subcommand' -a 'help' -d 'Show help'
|
|
191
207
|
|
|
192
208
|
# Global options
|
package/cli/dist/commands/ui.js
CHANGED
|
@@ -6,6 +6,7 @@ import { DetailPanel, FileTree, ResourceSelectInput, ResourceTable, TabBar, } fr
|
|
|
6
6
|
import { errorFactories, requireProjectRoot } from "../utils/errors.js";
|
|
7
7
|
import { findProjectRoot } from "../utils/paths.js";
|
|
8
8
|
import { clearMetadataCache, discoverResources, discoverStacks, getResourceMetadata, getStackMetadata, } from "../utils/services.js";
|
|
9
|
+
import { createStatusMessageController } from "../utils/status-message.js";
|
|
9
10
|
import { isTiltAvailable } from "../utils/tilt.js";
|
|
10
11
|
// biome-ignore lint/correctness/noUnusedFunctionParameters: reserved callback prop kept in the component API
|
|
11
12
|
const HelpPanel = ({ onClose }) => (_jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 2, paddingY: 1, flexDirection: "column", width: 60, children: [_jsx(Text, { bold: true, color: "cyan", children: "Keyboard Shortcuts" }), _jsxs(Box, { marginY: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, underline: true, children: "Navigation" }), _jsx(Text, { children: " \u2191/\u2193 Navigate list items" }), _jsx(Text, { children: " Enter Select item / Open detail" }), _jsx(Text, { children: " Space Toggle expand (tree view)" }), _jsx(Text, { children: " Tab Next tab" }), _jsx(Text, { children: " 1-5 Direct tab access" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { bold: true, underline: true, children: "Actions" }) }), _jsx(Text, { children: " m Toggle mouse support" }), _jsx(Text, { children: " t Toggle tooltips" }), _jsx(Text, { children: " e Toggle enabled/disabled services" }), _jsx(Text, { children: " r Refresh data" }), _jsx(Text, { children: " / Search/filter" }), _jsx(Text, { children: " ? Show this help" }), _jsx(Text, { children: " q Quit \u2502 Esc Back" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Press any key to close..." }) })] }));
|
|
@@ -32,6 +33,8 @@ const TUIApp = () => {
|
|
|
32
33
|
const [error, setError] = useState(null);
|
|
33
34
|
const [showTooltips, setShowTooltips] = useState(true);
|
|
34
35
|
const [showEnabledOnly, setShowEnabledOnly] = useState(true);
|
|
36
|
+
const statusMessage = useMemo(() => createStatusMessageController(setMessage), []);
|
|
37
|
+
useEffect(() => () => statusMessage.dispose(), [statusMessage]);
|
|
35
38
|
const projectRoot = findProjectRoot() || "unknown";
|
|
36
39
|
const [{ stacks, services }, setDiscovered] = useState({ stacks: [], services: [] });
|
|
37
40
|
// Re-read service.json files from disk. An empty project is not an error:
|
|
@@ -158,14 +161,12 @@ const TUIApp = () => {
|
|
|
158
161
|
if (activeTab === "overview") {
|
|
159
162
|
setSelectedStack(item.value);
|
|
160
163
|
setSelectedService(null);
|
|
161
|
-
|
|
162
|
-
setTimeout(() => setMessage(""), 2000);
|
|
164
|
+
statusMessage.show(`Selected stack: ${item.value}`, 2000);
|
|
163
165
|
}
|
|
164
166
|
else if (activeTab === "resources") {
|
|
165
167
|
if (selectedStack && !selectedService) {
|
|
166
168
|
setSelectedService(item.value);
|
|
167
|
-
|
|
168
|
-
setTimeout(() => setMessage(""), 2000);
|
|
169
|
+
statusMessage.show(`Selected service: ${item.value}`, 2000);
|
|
169
170
|
}
|
|
170
171
|
else {
|
|
171
172
|
setSelectedStack(item.value);
|
|
@@ -175,21 +176,18 @@ const TUIApp = () => {
|
|
|
175
176
|
else if (activeTab === "files") {
|
|
176
177
|
if (selectedService) {
|
|
177
178
|
setSelectedFile(item.value);
|
|
178
|
-
|
|
179
|
-
setTimeout(() => setMessage(""), 2000);
|
|
179
|
+
statusMessage.show(`Selected file: ${item.label}`, 2000);
|
|
180
180
|
}
|
|
181
181
|
else {
|
|
182
182
|
setSelectedService(item.value);
|
|
183
|
-
|
|
184
|
-
setTimeout(() => setMessage(""), 2000);
|
|
183
|
+
statusMessage.show(`Selected service: ${item.value}`, 2000);
|
|
185
184
|
}
|
|
186
185
|
}
|
|
187
186
|
else if (activeTab === "config") {
|
|
188
187
|
setSelectedService(item.value);
|
|
189
|
-
|
|
190
|
-
setTimeout(() => setMessage(""), 2000);
|
|
188
|
+
statusMessage.show(`Viewing config for: ${item.value}`, 2000);
|
|
191
189
|
}
|
|
192
|
-
}, [activeTab, selectedStack, selectedService]);
|
|
190
|
+
}, [activeTab, selectedStack, selectedService, statusMessage]);
|
|
193
191
|
useEffect(() => {
|
|
194
192
|
setRawMode(true);
|
|
195
193
|
stdout.write("\x1b[?1000h");
|
|
@@ -297,8 +295,7 @@ const TUIApp = () => {
|
|
|
297
295
|
setSelectedStack(null);
|
|
298
296
|
return;
|
|
299
297
|
}
|
|
300
|
-
|
|
301
|
-
setTimeout(() => setMessage(""), 2000);
|
|
298
|
+
statusMessage.show("Press q to quit", 2000);
|
|
302
299
|
return;
|
|
303
300
|
}
|
|
304
301
|
if (input === "?") {
|
|
@@ -307,8 +304,7 @@ const TUIApp = () => {
|
|
|
307
304
|
}
|
|
308
305
|
if (input === "r") {
|
|
309
306
|
if (refresh()) {
|
|
310
|
-
|
|
311
|
-
setTimeout(() => setMessage(""), 1500);
|
|
307
|
+
statusMessage.show("Data refreshed", 1500);
|
|
312
308
|
}
|
|
313
309
|
return;
|
|
314
310
|
}
|
|
@@ -321,28 +317,25 @@ const TUIApp = () => {
|
|
|
321
317
|
if (input === "m") {
|
|
322
318
|
setMouseEnabled((prev) => {
|
|
323
319
|
const newState = !prev;
|
|
324
|
-
|
|
320
|
+
statusMessage.show(newState ? "Mouse support enabled" : "Mouse support disabled");
|
|
325
321
|
return newState;
|
|
326
322
|
});
|
|
327
|
-
setTimeout(() => setMessage(""), 1500);
|
|
328
323
|
return;
|
|
329
324
|
}
|
|
330
325
|
if (input === "t") {
|
|
331
326
|
setShowTooltips((prev) => {
|
|
332
327
|
const newState = !prev;
|
|
333
|
-
|
|
328
|
+
statusMessage.show(newState ? "Tooltips enabled" : "Tooltips disabled");
|
|
334
329
|
return newState;
|
|
335
330
|
});
|
|
336
|
-
setTimeout(() => setMessage(""), 1500);
|
|
337
331
|
return;
|
|
338
332
|
}
|
|
339
333
|
if (input === "e") {
|
|
340
334
|
setShowEnabledOnly((prev) => {
|
|
341
335
|
const newState = !prev;
|
|
342
|
-
|
|
336
|
+
statusMessage.show(newState ? "Showing enabled services only" : "Showing all services (including disabled)", 1500);
|
|
343
337
|
return newState;
|
|
344
338
|
});
|
|
345
|
-
setTimeout(() => setMessage(""), 1500);
|
|
346
339
|
return;
|
|
347
340
|
}
|
|
348
341
|
if (key.tab) {
|
package/cli/dist/commands/up.js
CHANGED
|
@@ -20,6 +20,7 @@ import { discoverResources, discoverResourcesStrict, discoverStacks, stackExists
|
|
|
20
20
|
import { buildSmokePlans, formatSmokeFailure, runSmokePlans } from "../utils/smoke.js";
|
|
21
21
|
import { buildTiltUpArgs, runTilt } from "../utils/tilt.js";
|
|
22
22
|
import { stopTiltOnPort } from "../utils/tilt-process.js";
|
|
23
|
+
import { parseTiltPort, resolveTiltPort, stopTiltForUp } from "../utils/tilt-startup.js";
|
|
23
24
|
import { findUnknownServices, resolveOnlySelection } from "../utils/up-only.js";
|
|
24
25
|
import { tiltGetUiResources, waitForTiltResourcesReady } from "../utils/up-readiness.js";
|
|
25
26
|
import { enableDiscoveredStacks } from "./project.js";
|
|
@@ -89,6 +90,12 @@ export const upCommand = new Command("up")
|
|
|
89
90
|
const emit = options.json ? createJsonEmitter("UP_FAILED", "tdk up") : undefined;
|
|
90
91
|
if (options.json)
|
|
91
92
|
options.quiet = true;
|
|
93
|
+
const portSetting = parseTiltPort(process.env.TILT_PORT);
|
|
94
|
+
if (!portSetting.ok) {
|
|
95
|
+
emit?.({ ok: false }, [{ code: "INVALID_TILT_PORT", message: portSetting.message }]);
|
|
96
|
+
showErrorAndExit(portSetting.message);
|
|
97
|
+
}
|
|
98
|
+
const configuredTiltPort = portSetting.port;
|
|
92
99
|
const hostRefusal = options.dryRun ? null : await hostUpRefusal();
|
|
93
100
|
if (hostRefusal) {
|
|
94
101
|
emit?.({ ok: false }, [{ code: "HOST_UNSUPPORTED", message: hostRefusal }]);
|
|
@@ -229,14 +236,12 @@ export const upCommand = new Command("up")
|
|
|
229
236
|
exportHostPortPlan(hostPortPlan);
|
|
230
237
|
writeSavedHostPortPlan(projectRoot, hostPortPlan);
|
|
231
238
|
const basePort = 10350;
|
|
232
|
-
let port = basePort;
|
|
233
239
|
// A second Tilt would apply a different selection to the same containers, whatever port it listens on, so `--only`
|
|
234
240
|
// looks for a Tilt that answers on the default port and on TILT_PORT before it chooses one. A listener that is not
|
|
235
241
|
// Tilt does not count: the port is then picked below as usual.
|
|
236
242
|
if (options.only) {
|
|
237
|
-
const envPort = process.env.TILT_PORT ? Number.parseInt(process.env.TILT_PORT, 10) : NaN;
|
|
238
243
|
const candidatePorts = [
|
|
239
|
-
...new Set([basePort, ...(
|
|
244
|
+
...new Set([basePort, ...(configuredTiltPort === undefined ? [] : [configuredTiltPort])]),
|
|
240
245
|
];
|
|
241
246
|
const running = [];
|
|
242
247
|
for (const candidate of candidatePorts) {
|
|
@@ -251,27 +256,23 @@ export const upCommand = new Command("up")
|
|
|
251
256
|
for (const runningPort of running)
|
|
252
257
|
stopTiltOnPort(runningPort);
|
|
253
258
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
}
|
|
259
|
+
const resolution = await resolveTiltPort({
|
|
260
|
+
configuredPort: configuredTiltPort,
|
|
261
|
+
force: options.force,
|
|
262
|
+
basePort,
|
|
263
|
+
findAvailablePort,
|
|
264
|
+
});
|
|
265
|
+
const port = resolution.port;
|
|
266
|
+
if (resolution.autoSwitched && !options.quiet) {
|
|
267
|
+
console.log(chalk.yellow(`⚠️ Port ${basePort} is already in use`));
|
|
268
|
+
console.log(chalk.blue(`🔄 Auto-switching to port ${port}\n`));
|
|
266
269
|
}
|
|
267
270
|
process.env.TILT_PORT = port.toString();
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
274
|
-
}
|
|
271
|
+
await stopTiltForUp({ force: options.force, quiet: options.quiet, port }, {
|
|
272
|
+
stop: stopTiltOnPort,
|
|
273
|
+
log: (message) => console.log(chalk.yellow(message)),
|
|
274
|
+
wait: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
|
|
275
|
+
});
|
|
275
276
|
if (!options.quiet) {
|
|
276
277
|
console.log(chalk.gray("\nRunning tilt up..."));
|
|
277
278
|
console.log(chalk.gray(`Using Tiltfile: .tdk/.tdk-out/Tiltfile`));
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function createStatusMessageController(setMessage) {
|
|
2
|
+
let timer;
|
|
3
|
+
return {
|
|
4
|
+
show(message, durationMs = 1500) {
|
|
5
|
+
if (timer !== undefined) {
|
|
6
|
+
clearTimeout(timer);
|
|
7
|
+
}
|
|
8
|
+
setMessage(message);
|
|
9
|
+
timer = setTimeout(() => {
|
|
10
|
+
timer = undefined;
|
|
11
|
+
setMessage("");
|
|
12
|
+
}, durationMs);
|
|
13
|
+
},
|
|
14
|
+
dispose() {
|
|
15
|
+
if (timer !== undefined) {
|
|
16
|
+
clearTimeout(timer);
|
|
17
|
+
timer = undefined;
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=status-message.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { isValidPort } from "./validation.js";
|
|
2
|
+
export function parseTiltPort(value) {
|
|
3
|
+
if (value === undefined || value === "")
|
|
4
|
+
return { ok: true, port: undefined };
|
|
5
|
+
const port = Number(value);
|
|
6
|
+
if (!/^[0-9]+$/.test(value) || !isValidPort(port)) {
|
|
7
|
+
return {
|
|
8
|
+
ok: false,
|
|
9
|
+
message: `Invalid TILT_PORT "${value}": expected an integer from 1 to 65535.`,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return { ok: true, port };
|
|
13
|
+
}
|
|
14
|
+
export async function resolveTiltPort(options) {
|
|
15
|
+
if (options.configuredPort !== undefined) {
|
|
16
|
+
return { port: options.configuredPort, autoSwitched: false };
|
|
17
|
+
}
|
|
18
|
+
if (options.force)
|
|
19
|
+
return { port: options.basePort, autoSwitched: false };
|
|
20
|
+
const availablePort = await options.findAvailablePort(options.basePort, 10);
|
|
21
|
+
if (availablePort && availablePort !== options.basePort) {
|
|
22
|
+
return { port: availablePort, autoSwitched: true };
|
|
23
|
+
}
|
|
24
|
+
return { port: options.basePort, autoSwitched: false };
|
|
25
|
+
}
|
|
26
|
+
export async function stopTiltForUp(options, dependencies) {
|
|
27
|
+
if (!options.force)
|
|
28
|
+
return;
|
|
29
|
+
if (!options.quiet) {
|
|
30
|
+
dependencies.log(`Force flag set - stopping Tilt on port ${options.port} if running...`);
|
|
31
|
+
}
|
|
32
|
+
dependencies.stop(options.port);
|
|
33
|
+
await dependencies.wait(2000);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=tilt-startup.js.map
|
package/cli/package.json
CHANGED