@tdk-landscape/tdk-cli-core 1.3.75 → 1.3.77
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/CHANGELOG.md +6 -0
- package/README.md +59 -13
- package/cli/bin/tdk.js +22 -2
- package/cli/dist/cli.js +9 -2
- package/cli/dist/commands/completion.js +30 -3
- package/cli/dist/commands/config.js +59 -1
- package/cli/dist/commands/doctor.js +295 -98
- package/cli/dist/commands/eject.js +77 -0
- package/cli/dist/commands/networks.js +17 -4
- package/cli/dist/commands/project.js +28 -5
- package/cli/dist/commands/resource.js +118 -10
- package/cli/dist/commands/up.js +93 -21
- package/cli/dist/commands/upgrade.js +163 -68
- package/cli/dist/generator/template-engine.js +77 -13
- package/cli/dist/types/index.js +6 -1
- package/cli/dist/utils/cold-preflight.js +198 -0
- package/cli/dist/utils/constants.js +4 -0
- package/cli/dist/utils/docker.js +15 -1
- package/cli/dist/utils/doctor-runtime.js +48 -16
- package/cli/dist/utils/doctor-wiring.js +269 -0
- package/cli/dist/utils/errors.js +5 -5
- package/cli/dist/utils/exec-async.js +30 -0
- package/cli/dist/utils/generated-paths.js +49 -0
- package/cli/dist/utils/platform.js +53 -0
- package/cli/dist/utils/port-assignment.js +13 -20
- package/cli/dist/utils/project-templates.js +7 -2
- package/cli/dist/utils/resource-features.js +6 -2
- package/cli/dist/utils/service-manifest.js +73 -0
- package/cli/dist/utils/services.js +8 -1
- package/cli/dist/utils/tar.js +27 -5
- package/cli/dist/utils/tilt-process.js +53 -0
- package/cli/dist/utils/tilt.js +3 -1
- package/cli/dist/utils/which.js +26 -0
- package/cli/dist/utils/windows-doctor.js +175 -0
- package/cli/package.json +1 -1
- package/cli/templates/TILT_RESOURCE_DEFAULTS.star.hbs +1 -2
- package/cli/templates/TILT_TECH_STACK.star.hbs +0 -1
- package/cli/templates/Tiltfile.hbs +24 -4
- package/cli/templates/spec.master.hbs +1 -2
- package/discovery/AGENTS.md +1 -1
- package/discovery/constants.star +2 -2
- package/discovery/discovery_orchestrator.star +2 -2
- package/discovery/manifest/constants.star +1 -1
- package/discovery/manifest/loading.star +7 -3
- package/discovery/registry.star +17 -3
- package/engine/README.md +2 -0
- package/engine/schemas/service-schema.json +47 -21
- package/engine/scripts/generate-manifest.sh +2 -1
- package/engine/topologies/platform/docker/compose/compose.star +37 -5
- package/engine/topologies/tilt/common/utils.star +54 -11
- package/engine/topologies/tilt/manifest/AGENTS.md +1 -1
- package/engine/topologies/tilt/manifest/constants.star +3 -0
- package/engine/topologies/tilt/manifest/schema.star +2 -4
- package/engine/topologies/tilt/manifest/validator.star +1 -1
- package/engine/topologies/tilt/resources/infra-loader.star +14 -3
- package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +15 -11
- package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +15 -6
- package/examples/tdk-example/LICENSE +21 -0
- package/examples/tdk-example/README.md +23 -0
- package/examples/tdk-example/TILT_SERVICE_DEFAULTS.star +42 -0
- package/examples/tdk-example/TILT_TECH_STACK.star +67 -0
- package/examples/tdk-example/Tiltfile +25 -0
- package/examples/tdk-example/services/platform/messaging/docker-compose.yml +26 -0
- package/examples/tdk-example/services/shop/orders-api/package.json +20 -0
- package/examples/tdk-example/services/shop/orders-api/service.json +14 -0
- package/examples/tdk-example/services/shop/orders-api/src/index.ts +81 -0
- package/examples/tdk-example/services/shop/orders-app/index.html +12 -0
- package/examples/tdk-example/services/shop/orders-app/package.json +20 -0
- package/examples/tdk-example/services/shop/orders-app/public/.gitkeep +0 -0
- package/examples/tdk-example/services/shop/orders-app/service.json +9 -0
- package/examples/tdk-example/services/shop/orders-app/src/main.tsx +9 -0
- package/examples/tdk-example/services/shop/orders-app/src/orders.tsx +67 -0
- package/examples/tdk-example/services/shop/orders-app/src/style.css +15 -0
- package/examples/tdk-example/services/shop/orders-worker/package.json +18 -0
- package/examples/tdk-example/services/shop/orders-worker/service.json +13 -0
- package/examples/tdk-example/services/shop/orders-worker/src/index.ts +33 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ Notable changes to the `tdk` CLI. Versions match [npm](https://www.npmjs.com/pac
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
- `tdk up` no longer leaves `nats` (and any other service in `services/platform/messaging/docker-compose.yml`, e.g. a local mail catcher) disabled in Tilt. The pre-alpha default always goes through a focus-filter path that only re-enabled `init-networks`/`postgres`/`traefik`/`golden-layers-build`, so a resource with the `nats` feature retried its broker connection forever until you ran `tilt enable nats` by hand. Fixes #155.
|
|
8
|
+
|
|
9
|
+
- `tdk doctor` now catches wiring mistakes that used to surface minutes into `tdk up`: a resource with no `package.json` (the image build stops at "COPY ... not found"), a `params` URL such as `http://auth-emulator:4000` whose port is not the one the target listens on (its `service.json` `port`), a frontend calling a backend through `localhost:<port>` (TDK publishes no backend ports on localhost; use the Traefik route), the `nats` feature without `services/platform/messaging/docker-compose.yml` (TDK does not generate it, so no NATS server starts), two Tilt processes running for one project, and Docker having no address pool left for the networks a project needs. Found while building [tdk-ecommerce-example](https://github.com/tdk-landscape/tdk-ecommerce-example) and a queue/auth example.
|
|
10
|
+
|
|
11
|
+
- `tdk project ecommerce` clones [tdk-ecommerce-example](https://github.com/tdk-landscape/tdk-ecommerce-example), a Vue 3 storefront and Hono catalog API. Before, only restaurant, saas, erp, user-management and example were accepted.
|
|
12
|
+
|
|
7
13
|
## 1.3.73 (2026-09-28)
|
|
8
14
|
|
|
9
15
|
- Free-tier projects no longer start the `sablier` and `wake-gateway` containers, and Traefik no longer downloads the Sablier plugin from GitHub on every start. They were always launched (the `sablier` one with a read-write Docker socket) even though the license only gated the per-service labels, so nothing could use them. They are now generated only when a license grants Sablier. Fewer containers, one image build less, and no network fetch at Traefik boot. Spotted in the ERP CI run log.
|
package/README.md
CHANGED
|
@@ -2,14 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# TDK — Tilt Development Kit
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
TDK is a local development kit. It scaffolds services and runs a stack on your
|
|
6
|
+
laptop with Docker and Tilt (hot reload, health, Traefik, Postgres).
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
It is not a Kubernetes packager. It does not replace Helm, Argo CD,
|
|
9
|
+
Kustomize, or your production charts.
|
|
10
|
+
|
|
11
|
+
Use TDK when local bring-up of many services is painful and you do not want a
|
|
12
|
+
cluster on the laptop.
|
|
13
|
+
|
|
14
|
+
Skip TDK if `helm install` (or your existing compose/Tilt/Skaffold) already
|
|
15
|
+
gives you a working local or shared-dev environment.
|
|
16
|
+
|
|
17
|
+
## Status
|
|
18
|
+
|
|
19
|
+
Supported: macOS, Linux, Windows via WSL2 Ubuntu.
|
|
20
|
+
Native Windows: CLI inspect only; `tdk up` unsupported.
|
|
21
|
+
WSL2 smoke: local script only (no GitHub WSL runner).
|
|
22
|
+
Stability: 1.x — generated files are a contract; verify in CI.
|
|
23
|
+
Check your machine: `tdk doctor`
|
|
24
|
+
Known limits: [docs/compare-honest.md](docs/compare-honest.md)
|
|
8
25
|
|
|
9
26
|
[](https://www.npmjs.com/package/@tdk-landscape/tdk-cli-core)
|
|
10
27
|
[](https://github.com/tdk-landscape/tdk-cli-core/actions/workflows/ci.yml)
|
|
11
28
|
[](https://github.com/tdk-landscape/tdk-cli-core/actions/workflows/quickstart-e2e.yml)
|
|
12
|
-
[](https://github.com/tdk-landscape/tdk-cli-core/actions/workflows/erp-scale-e2e.yml)
|
|
29
|
+
[](https://github.com/tdk-landscape/tdk-cli-core/actions/workflows/erp-scale-e2e.yml)
|
|
13
30
|
[](https://github.com/tdk-landscape/tdk-cli-core/actions/workflows/examples-e2e.yml)
|
|
14
31
|
[](https://snyk.io/test/github/tdk-landscape/tdk-cli-core)
|
|
15
32
|
[](https://socket.dev/npm/package/@tdk-landscape/tdk-cli-core/overview)
|
|
@@ -19,8 +36,23 @@ One CLI that scaffolds your services and runs the whole landscape locally with h
|
|
|
19
36
|
|
|
20
37
|
</div>
|
|
21
38
|
|
|
39
|
+
## When not to use TDK
|
|
40
|
+
|
|
41
|
+
- Production or shared-cluster deploy is Helm/Argo/Kustomize — keep it.
|
|
42
|
+
- Your local or namespace-per-dev environment already works.
|
|
43
|
+
- You will not adopt `service.json` and generated files.
|
|
44
|
+
|
|
45
|
+
## TDK is for you if
|
|
46
|
+
|
|
47
|
+
- You have many services and want `tdk up <one stack>` on Docker.
|
|
48
|
+
- You do not want kind or minikube as your daily path.
|
|
49
|
+
- You want to generate services, run `tdk config verify`, and use Traefik `*.localhost` routes.
|
|
50
|
+
|
|
51
|
+
Start with the [bundled product example](examples/tdk-example/README.md); its write path is covered by [example-e2e](.github/workflows/example-e2e.yml).
|
|
52
|
+
|
|
22
53
|
```bash
|
|
23
54
|
npm install -g @tdk-landscape/tdk-cli-core
|
|
55
|
+
npx @tdk-landscape/tdk-cli-core
|
|
24
56
|
mkdir shop && cd shop # tdk writes files into the current directory
|
|
25
57
|
tdk project --yes # set up the project
|
|
26
58
|
tdk resource orders-api --type backend --stack shop # scaffold a service
|
|
@@ -29,9 +61,11 @@ tdk up shop # run it with hot reload
|
|
|
29
61
|
|
|
30
62
|

|
|
31
63
|
|
|
64
|
+
Want to inspect a stack before starting containers? `tdk up shop --dry-run` shows the services and local URLs without Docker or Tilt installed.
|
|
65
|
+
|
|
32
66
|

|
|
33
67
|
|
|
34
|
-
**Requirements:** Docker (Desktop, OrbStack or Colima; Engine 25+, Compose 2.20+), [Tilt](https://docs.tilt.dev/install.html), [Bun](https://bun.sh) for the generated services, and Node.js 22.12+ for
|
|
68
|
+
**Requirements:** Docker (Desktop, OrbStack or Colima; Engine 25+, Compose 2.20+), [Tilt](https://docs.tilt.dev/install.html), [Bun](https://bun.sh) for the generated services, and Node.js 22.12+ for npm installs. Ports 80, 443 and 5432 must be free (a local Postgres is the usual conflict). macOS and Linux releases are available. Windows native is unsupported. WSL2 Ubuntu is supported. See docs/wsl2.md. Run `tdk doctor` to check your setup.
|
|
35
69
|
|
|
36
70
|
This is the core monorepo for TDK: the `tdk` CLI, the Starlark-based Tilt orchestration engine that powers it, and the discovery system that turns a directory of services into a running local landscape.
|
|
37
71
|
|
|
@@ -48,9 +82,9 @@ This is the core monorepo for TDK: the `tdk` CLI, the Starlark-based Tilt orches
|
|
|
48
82
|
|
|
49
83
|
TDK is not a replacement for Tilt: it generates the Tiltfile, Dockerfiles and compose files and then runs Tilt. If you already have a hand-written Tiltfile or compose setup you're happy with, TDK mostly saves you the boilerplate for the next service.
|
|
50
84
|
|
|
51
|
-
##
|
|
85
|
+
## Fixture bench: 100 generated services on one laptop
|
|
52
86
|
|
|
53
|
-
Measured with [`scripts/benchmark/container-scale.ts`](scripts/benchmark/README.md) against the 100-service [
|
|
87
|
+
Measured with [`scripts/benchmark/container-scale.ts`](scripts/benchmark/README.md) against the 100-service [`tdk-erp-system` fixture bench](https://github.com/tdk-landscape/tdk-erp-system), a scale fixture of generated `/health` services rather than an ERP product, on a 16 GB machine (Docker VM: 7.75 GiB). Raw results are in [`benchmarks/results/`](benchmarks/results).
|
|
54
88
|
|
|
55
89
|
| Services | All healthy after | Total memory | Avg memory / service | Crashes / OOM kills |
|
|
56
90
|
|---:|---:|---:|---:|---:|
|
|
@@ -70,9 +104,9 @@ What this does and doesn't measure:
|
|
|
70
104
|
The table above is a warm start. Two workflows install the published CLI on a fresh GitHub-hosted Ubuntu runner and run `tdk up` from nothing, building every image:
|
|
71
105
|
|
|
72
106
|
- [**Quickstart E2E**](.github/workflows/quickstart-e2e.yml): the commands from the top of this README, one scaffolded backend, until it answers `/health` through Traefik. About 4 minutes. Runs after each release and daily.
|
|
73
|
-
- [**ERP scale E2E**](.github/workflows/erp-scale-e2e.yml): the 100-service
|
|
107
|
+
- [**ERP fixture scale E2E**](.github/workflows/erp-scale-e2e.yml): the 100-service generated-health fixture bench. [Run 36395860088](https://github.com/tdk-landscape/tdk-cli-core/actions/runs/36395860088) (CLI 1.3.72, 4 vCPU / 15 GB): **100 / 100 backends healthy through Traefik after 472 s**, 111 containers, 1.8 GiB used by all containers. Runs weekly.
|
|
74
108
|
|
|
75
|
-
Both are
|
|
109
|
+
Both are Linux/Ubuntu CI fixtures: generated services answering `/health`, not a real business app. A green run means TDK can generate, build, route and health-check a landscape of that size on a clean Ubuntu runner. Windows now has a separate smoke workflow for CLI startup and unit tests, but it does not boot a full landscape on Windows. These CI runs also don't cover Apple Silicon or an existing repo you bring yourself.
|
|
76
110
|
|
|
77
111
|
## What is TDK?
|
|
78
112
|
|
|
@@ -166,7 +200,9 @@ TDK has no telemetry or analytics. The CLI only goes online when you ask it to:
|
|
|
166
200
|
|
|
167
201
|
**How is this different from Skaffold, Garden, DevSpace or Tilt itself?** Skaffold, Garden and DevSpace are built around Kubernetes, and plain Tilt leaves the Tiltfile to you. TDK runs on plain Docker with no cluster, and generates both the services and the Tilt config from a `service.json` per service.
|
|
168
202
|
|
|
169
|
-
|
|
203
|
+
See the [honest comparison](docs/compare-honest.md) for tradeoffs and cases where TDK may not fit.
|
|
204
|
+
|
|
205
|
+
**Do I need 100 microservices?** No. Two services work the same way. The 100-service fixture bench is a stress test to show the overhead stays flat; it is not an ERP product example.
|
|
170
206
|
|
|
171
207
|
**Is TDK affiliated with Tilt or Docker?** No. TDK is an independent project built on top of [Tilt](https://tilt.dev), which is maintained by Docker, Inc.
|
|
172
208
|
|
|
@@ -179,22 +215,32 @@ make test-fast # fast unit tests, no external deps
|
|
|
179
215
|
make pre-commit-run # run pre-commit hooks on all files
|
|
180
216
|
```
|
|
181
217
|
|
|
182
|
-
`npm run typecheck` / `npm run lint` / `npm run test` delegate to the `cli/` workspace.
|
|
218
|
+
`npm run typecheck` / `npm run lint` / `npm run test` delegate to the `cli/` workspace. If you're contributing, start with the [step-by-step contributor guide](docs/contributing/README.md) for subsystem-specific instructions.
|
|
183
219
|
|
|
184
220
|
## Environment check
|
|
185
221
|
|
|
222
|
+
### Cold npx / first run
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
npx @tdk-landscape/tdk-cli-core
|
|
226
|
+
npx @tdk-landscape/tdk-cli-core doctor
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Ranked first failures: Node 22.12+, Docker Engine 25+, Compose 2.20.2+, Tilt 0.25+, Bun 1.2+, and ports 80/443/5432.
|
|
230
|
+
Install Bun 1.2+ as a machine prerequisite. `tdk project` generates the stack configuration; Prisma and NATS are project-specific, and NATS is required only when the feature is enabled.
|
|
231
|
+
|
|
186
232
|
```bash
|
|
187
233
|
tdk doctor
|
|
188
234
|
```
|
|
189
235
|
|
|
190
|
-
Checks Docker, Compose and Tilt. Inside a project it also checks the generated configs, `.env`, service scripts and that the ingress ports are free, before you run `tdk up`.
|
|
236
|
+
Checks Docker, Compose and Tilt. Inside a project it also checks the generated configs, `.env`, service scripts and that the ingress ports are free, before you run `tdk up`. It also catches wiring mistakes: a resource without `package.json`, a `params` URL that uses the wrong port for another service, a frontend calling a backend on `localhost:<port>`, the `nats` feature without a broker to start, duplicate Tilt processes, and Docker running out of network address space.
|
|
191
237
|
|
|
192
238
|
## Community
|
|
193
239
|
|
|
194
240
|
- **Questions and ideas:** [open an issue](https://github.com/tdk-landscape/tdk-cli-core/issues/new/choose).
|
|
195
|
-
- **Contributing:** see [CONTRIBUTING.md](CONTRIBUTING.md). Issues labeled [`good first issue`](https://github.com/tdk-landscape/tdk-cli-core/labels/good%20first%20issue) are a good place to start.
|
|
241
|
+
- **Contributing:** see [CONTRIBUTING.md](CONTRIBUTING.md) and the [step-by-step guide](docs/contributing/README.md). Issues labeled [`good first issue`](https://github.com/tdk-landscape/tdk-cli-core/labels/good%20first%20issue) are a good place to start.
|
|
196
242
|
- **Security:** see [SECURITY.md](SECURITY.md). Please don't report vulnerabilities in public issues.
|
|
197
|
-
- **Examples:** [ERP
|
|
243
|
+
- **Examples:** bundled [TDK product example](examples/tdk-example), [ERP fixture bench (100 generated services)](https://github.com/tdk-landscape/tdk-erp-system), [SaaS starter](https://github.com/tdk-landscape/tdk-saas-starter), [restaurant](https://github.com/tdk-landscape/tdk-restaurant-example), [ecommerce (Vue + Hono)](https://github.com/tdk-landscape/tdk-ecommerce-example).
|
|
198
244
|
|
|
199
245
|
## License
|
|
200
246
|
|
package/cli/bin/tdk.js
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
|
|
6
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = dirname(__filename);
|
|
8
8
|
|
|
9
|
+
function nodeTooOld(version) {
|
|
10
|
+
const [major = 0, minor = 0] = String(version)
|
|
11
|
+
.split(".")
|
|
12
|
+
.map((part) => Number.parseInt(part, 10));
|
|
13
|
+
return (
|
|
14
|
+
!Number.isFinite(major) || !Number.isFinite(minor) || major < 22 || (major === 22 && minor < 12)
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (nodeTooOld(process.versions.node)) {
|
|
19
|
+
console.error(
|
|
20
|
+
"TDK CLI needs Node.js 22.12+ (current: " +
|
|
21
|
+
process.versions.node +
|
|
22
|
+
").\n" +
|
|
23
|
+
"This is the first cold-npx failure. Bun/Prisma/NATS are not involved yet.\n" +
|
|
24
|
+
"Install Node 22.12+ and rerun.",
|
|
25
|
+
);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
9
29
|
const cliPath = join(__dirname, "..", "dist", "cli.js");
|
|
10
30
|
|
|
11
|
-
import(cliPath).catch((err) => {
|
|
31
|
+
import(pathToFileURL(cliPath).href).catch((err) => {
|
|
12
32
|
console.error("Failed to start TDK:", err);
|
|
13
33
|
process.exit(1);
|
|
14
34
|
});
|
package/cli/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { completionCommand } from "./commands/completion.js";
|
|
|
6
6
|
import { configCommand } from "./commands/config.js";
|
|
7
7
|
import { doctorCommand } from "./commands/doctor.js";
|
|
8
8
|
import { downCommand } from "./commands/down.js";
|
|
9
|
+
import { ejectCommand } from "./commands/eject.js";
|
|
9
10
|
import { showHelp } from "./commands/help.js";
|
|
10
11
|
import { networksCommand } from "./commands/networks.js";
|
|
11
12
|
import { projectCommand } from "./commands/project.js";
|
|
@@ -19,6 +20,7 @@ import { uiCommand } from "./commands/ui.js";
|
|
|
19
20
|
import { upCommand } from "./commands/up.js";
|
|
20
21
|
import { upgradeCommand } from "./commands/upgrade.js";
|
|
21
22
|
import { versionCommand } from "./commands/version.js";
|
|
23
|
+
import { formatColdPreflight, runColdPreflight } from "./utils/cold-preflight.js";
|
|
22
24
|
const program = new Command();
|
|
23
25
|
program
|
|
24
26
|
.name("tdk")
|
|
@@ -40,6 +42,7 @@ program.addCommand(resourcesCommand);
|
|
|
40
42
|
program.addCommand(projectsCommand);
|
|
41
43
|
program.addCommand(upCommand);
|
|
42
44
|
program.addCommand(downCommand);
|
|
45
|
+
program.addCommand(ejectCommand);
|
|
43
46
|
program.addCommand(statusCommand);
|
|
44
47
|
program.addCommand(stackCommand);
|
|
45
48
|
program.addCommand(resourceCommand);
|
|
@@ -53,9 +56,13 @@ program.addCommand(upgradeCommand);
|
|
|
53
56
|
program.addCommand(networksCommand);
|
|
54
57
|
// Default: show help if no command provided
|
|
55
58
|
if (process.argv.length === 2) {
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
const result = await runColdPreflight();
|
|
60
|
+
console.log(formatColdPreflight(result));
|
|
61
|
+
console.log("\nCommands: tdk doctor | tdk project --yes | tdk --help");
|
|
62
|
+
process.exit(result.ok ? 0 : 1);
|
|
58
63
|
}
|
|
64
|
+
if (process.argv[2] === "--doctor")
|
|
65
|
+
process.argv[2] = "doctor";
|
|
59
66
|
if (process.argv.length === 3 && ["-h", "--help", "help"].includes(process.argv[2])) {
|
|
60
67
|
showHelp();
|
|
61
68
|
process.exit(0);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import chalk from "chalk";
|
|
@@ -213,9 +213,16 @@ complete -c tdk -n '__fish_seen_subcommand_from doctor' -l fix -d 'Attempt to fi
|
|
|
213
213
|
complete -c tdk -n '__fish_seen_subcommand_from upgrade' -l force -d 'Force upgrade even if on latest'
|
|
214
214
|
complete -c tdk -n '__fish_seen_subcommand_from upgrade' -l dry-run -d 'Show what would be upgraded'
|
|
215
215
|
`;
|
|
216
|
+
const POWERSHELL_COMPLETION = `Register-ArgumentCompleter -Native -CommandName tdk -ScriptBlock {
|
|
217
|
+
param($wordToComplete)
|
|
218
|
+
$cmds = @('up','down','doctor','project','resource','stacks','status','networks','upgrade','version','ui','config')
|
|
219
|
+
$cmds | Where-Object { $_ -like "$wordToComplete*" }
|
|
220
|
+
}`;
|
|
221
|
+
const POWERSHELL_START = "# TDK-CLI-COMPLETION-START";
|
|
222
|
+
const POWERSHELL_END = "# TDK-CLI-COMPLETION-END";
|
|
216
223
|
export const completionCommand = new Command("completion")
|
|
217
224
|
.description("Generate shell completion scripts")
|
|
218
|
-
.option("-s, --shell <shell>", "Target shell (bash, zsh, fish)", "bash")
|
|
225
|
+
.option("-s, --shell <shell>", "Target shell (bash, zsh, fish, powershell)", "bash")
|
|
219
226
|
.option("-o, --output <path>", "Output file path (default: stdout)")
|
|
220
227
|
.option("--install", "Install to shell config automatically")
|
|
221
228
|
.action((options) => {
|
|
@@ -235,9 +242,13 @@ export const completionCommand = new Command("completion")
|
|
|
235
242
|
completionScript = FISH_COMPLETION;
|
|
236
243
|
filename = "tdk.fish";
|
|
237
244
|
break;
|
|
245
|
+
case "powershell":
|
|
246
|
+
completionScript = `${POWERSHELL_START}\n${POWERSHELL_COMPLETION}\n${POWERSHELL_END}`;
|
|
247
|
+
filename = "Microsoft.PowerShell_profile.ps1";
|
|
248
|
+
break;
|
|
238
249
|
default:
|
|
239
250
|
console.error(chalk.red(`❌ Unsupported shell: ${shell}`));
|
|
240
|
-
console.log(chalk.gray("Supported shells: bash, zsh, fish"));
|
|
251
|
+
console.log(chalk.gray("Supported shells: bash, zsh, fish, powershell"));
|
|
241
252
|
process.exit(1);
|
|
242
253
|
}
|
|
243
254
|
if (options.install) {
|
|
@@ -245,6 +256,22 @@ export const completionCommand = new Command("completion")
|
|
|
245
256
|
let installPath;
|
|
246
257
|
let installInstructions;
|
|
247
258
|
switch (shell) {
|
|
259
|
+
case "powershell": {
|
|
260
|
+
const docs = join(home, "Documents");
|
|
261
|
+
const ps7 = process.env.PSModulePath?.includes(join("Documents", "PowerShell", "Modules"));
|
|
262
|
+
const profileDir = join(docs, ps7 ? "PowerShell" : "WindowsPowerShell");
|
|
263
|
+
installPath = join(profileDir, filename);
|
|
264
|
+
mkdirSync(profileDir, { recursive: true });
|
|
265
|
+
const existing = existsSync(installPath) ? readFileSync(installPath, "utf-8") : "";
|
|
266
|
+
const block = `${POWERSHELL_START}\n${POWERSHELL_COMPLETION}\n${POWERSHELL_END}`;
|
|
267
|
+
const markerPattern = new RegExp(`${POWERSHELL_START}[\\s\\S]*?${POWERSHELL_END}`, "m");
|
|
268
|
+
const updated = markerPattern.test(existing)
|
|
269
|
+
? existing.replace(markerPattern, block)
|
|
270
|
+
: `${existing}${existing && !existing.endsWith("\n") ? "\n" : ""}${block}\n`;
|
|
271
|
+
writeFileSync(installPath, updated, "utf-8");
|
|
272
|
+
installInstructions = "\n# Close and reopen PowerShell to load the completion.";
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
248
275
|
case "bash": {
|
|
249
276
|
installPath = join(home, ".bash_completion.d", filename);
|
|
250
277
|
const bashDir = join(home, ".bash_completion.d");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { Command } from "commander";
|
|
@@ -10,6 +10,8 @@ import { assertValid } from "../utils/command-helpers.js";
|
|
|
10
10
|
import { MASTER_CONFIG_FILES } from "../utils/constants.js";
|
|
11
11
|
import { requireProjectRoot, runCommand } from "../utils/errors.js";
|
|
12
12
|
import { writeJsonFile } from "../utils/file-helpers.js";
|
|
13
|
+
import { SERVICE_MANIFEST_SCHEMA_VERSION, validateServiceManifest, } from "../utils/service-manifest.js";
|
|
14
|
+
import { discoverServiceManifestPaths } from "../utils/services.js";
|
|
13
15
|
import { validateOptionalInfraService } from "../utils/validation.js";
|
|
14
16
|
/**
|
|
15
17
|
* Serialize ProjectConfig to JSON-safe value.
|
|
@@ -115,6 +117,59 @@ export const configCommand = new Command("config")
|
|
|
115
117
|
await generateMasterConfigs(projectRoot);
|
|
116
118
|
console.log(chalk.green("\n✅ Configuration regenerated!"));
|
|
117
119
|
});
|
|
120
|
+
}))
|
|
121
|
+
.addCommand(new Command("migrate")
|
|
122
|
+
.description("Migrate service.json files to the current schema version")
|
|
123
|
+
.action(async () => {
|
|
124
|
+
await runCommand(async () => {
|
|
125
|
+
const projectRoot = requireProjectRoot();
|
|
126
|
+
const failures = [];
|
|
127
|
+
const pendingMigrations = [];
|
|
128
|
+
for (const filePath of discoverServiceManifestPaths(projectRoot)) {
|
|
129
|
+
let manifest;
|
|
130
|
+
try {
|
|
131
|
+
manifest = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
failures.push(`${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
138
|
+
failures.push(`${filePath}: expected a JSON object`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const record = manifest;
|
|
142
|
+
if (record.appType === undefined && typeof record.type === "string") {
|
|
143
|
+
record.appType = record.type;
|
|
144
|
+
}
|
|
145
|
+
if (record.schemaVersion === SERVICE_MANIFEST_SCHEMA_VERSION)
|
|
146
|
+
continue;
|
|
147
|
+
if (record.schemaVersion !== undefined) {
|
|
148
|
+
failures.push(`${filePath}.schemaVersion: cannot migrate unsupported version ${String(record.schemaVersion)} automatically`);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const validation = validateServiceManifest({ ...record, schemaVersion: SERVICE_MANIFEST_SCHEMA_VERSION }, filePath);
|
|
152
|
+
if (validation.errors.length > 0) {
|
|
153
|
+
failures.push(...validation.errors);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
for (const warning of validation.warnings) {
|
|
157
|
+
console.warn(chalk.yellow(`⚠️ ${warning}`));
|
|
158
|
+
}
|
|
159
|
+
record.schemaVersion = SERVICE_MANIFEST_SCHEMA_VERSION;
|
|
160
|
+
pendingMigrations.push({ filePath, manifest: record });
|
|
161
|
+
}
|
|
162
|
+
for (const failure of failures)
|
|
163
|
+
console.error(chalk.red(`❌ ${failure}`));
|
|
164
|
+
if (failures.length > 0)
|
|
165
|
+
process.exit(1);
|
|
166
|
+
for (const migration of pendingMigrations) {
|
|
167
|
+
writeFileSync(migration.filePath, `${JSON.stringify(migration.manifest, null, 2)}\n`);
|
|
168
|
+
}
|
|
169
|
+
const migrated = pendingMigrations.length;
|
|
170
|
+
console.log(chalk.green(`✅ Migrated ${migrated} service.json file${migrated === 1 ? "" : "s"} to schema version ${SERVICE_MANIFEST_SCHEMA_VERSION}.`));
|
|
171
|
+
console.log(chalk.yellow("Review the diff; unknown fields were preserved."));
|
|
172
|
+
});
|
|
118
173
|
}))
|
|
119
174
|
.addCommand(new Command("verify")
|
|
120
175
|
.description("Verify that generated files match .tdk/project.json")
|
|
@@ -123,6 +178,9 @@ export const configCommand = new Command("config")
|
|
|
123
178
|
const projectRoot = requireProjectRoot();
|
|
124
179
|
console.log(chalk.blue("🔍 Verifying configuration...\n"));
|
|
125
180
|
const result = verifyMasterConfigs(projectRoot);
|
|
181
|
+
for (const warning of result.warnings) {
|
|
182
|
+
console.warn(chalk.yellow(`⚠️ ${warning}`));
|
|
183
|
+
}
|
|
126
184
|
if (result.valid) {
|
|
127
185
|
console.log(chalk.green("✅ All files are in sync!"));
|
|
128
186
|
return;
|