@lenne.tech/cli 1.21.0 → 1.22.0
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/README.md +16 -10
- package/build/commands/{local/local.js → dev/dev.js} +11 -9
- package/build/commands/dev/doctor.js +110 -0
- package/build/commands/dev/down.js +76 -0
- package/build/commands/dev/install.js +113 -0
- package/build/commands/dev/migrate.js +96 -0
- package/build/commands/dev/status.js +153 -0
- package/build/commands/dev/test.js +191 -0
- package/build/commands/dev/up.js +228 -0
- package/build/commands/fullstack/init.js +36 -2
- package/build/commands/status.js +16 -16
- package/build/lib/caddy.js +169 -0
- package/build/lib/dev-env-bridge.js +109 -0
- package/build/lib/dev-env.js +35 -0
- package/build/lib/dev-identity.js +100 -0
- package/build/lib/dev-migrate-helper.js +81 -0
- package/build/lib/dev-patches.js +190 -0
- package/build/lib/dev-process.js +152 -0
- package/build/lib/{local-project.js → dev-project.js} +23 -38
- package/build/lib/dev-state.js +152 -0
- package/docs/commands.md +111 -98
- package/package.json +1 -1
- package/build/commands/local/down.js +0 -71
- package/build/commands/local/init.js +0 -162
- package/build/commands/local/status.js +0 -69
- package/build/commands/local/up.js +0 -148
- package/build/commands/ports/ports.js +0 -118
- package/build/commands/ports/scan.js +0 -131
- package/build/lib/local-patches.js +0 -175
- package/build/lib/port-registry.js +0 -304
package/docs/commands.md
CHANGED
|
@@ -286,188 +286,201 @@ For mode-aware update workflows after conversion, use:
|
|
|
286
286
|
|
|
287
287
|
## Local Development Commands
|
|
288
288
|
|
|
289
|
-
|
|
289
|
+
Run multiple lt projects in parallel without port collisions or cross-wiring.
|
|
290
|
+
**URL-first**: every project gets stable HTTPS URLs (`<slug>.localhost`,
|
|
291
|
+
`api.<slug>.localhost`) served by Caddy. Internal ports are opaque and
|
|
292
|
+
auto-allocated. Cross-project state (database, storage, cookies) is
|
|
293
|
+
namespaced by slug so projects cannot accidentally interfere.
|
|
290
294
|
|
|
291
|
-
### `lt
|
|
295
|
+
### `lt dev`
|
|
292
296
|
|
|
293
297
|
Open the local-orchestration submenu.
|
|
294
298
|
|
|
295
299
|
**Usage:**
|
|
296
300
|
```bash
|
|
297
|
-
lt
|
|
301
|
+
lt dev
|
|
298
302
|
```
|
|
299
303
|
|
|
300
|
-
**Alias:** `lt
|
|
304
|
+
**Alias:** `lt d`
|
|
301
305
|
|
|
302
306
|
---
|
|
303
307
|
|
|
304
|
-
### `lt
|
|
308
|
+
### `lt dev install`
|
|
305
309
|
|
|
306
|
-
|
|
310
|
+
One-time per-machine setup. Idempotent — re-run anytime to diagnose what's missing.
|
|
307
311
|
|
|
308
312
|
**Usage:**
|
|
309
313
|
```bash
|
|
310
|
-
lt
|
|
314
|
+
lt dev install
|
|
311
315
|
```
|
|
312
316
|
|
|
313
|
-
**Alias:** `lt
|
|
314
|
-
|
|
315
|
-
**Options:**
|
|
316
|
-
| Option | Description |
|
|
317
|
-
|--------|-------------|
|
|
318
|
-
| `--slot <n>` | Force a specific slot index (0..89) instead of the deterministic slug hash |
|
|
319
|
-
| `--patch` | Apply env-aware port patches non-interactively |
|
|
320
|
-
| `--no-patch` | Skip the patch detection / prompt entirely |
|
|
321
|
-
| `--noConfirm` | Skip confirmation prompts (without `--patch`, patches are skipped) |
|
|
317
|
+
**Alias:** `lt d i`
|
|
322
318
|
|
|
323
319
|
**What it does:**
|
|
324
|
-
1.
|
|
325
|
-
2.
|
|
326
|
-
3.
|
|
327
|
-
4.
|
|
328
|
-
5.
|
|
329
|
-
6. Injects (or refreshes) a "Local Development (lt local)" port block into `CLAUDE.md` files at the workspace root and inside each subproject — bracketed by HTML comment markers so it can be replaced cleanly when ports change.
|
|
320
|
+
1. Verifies `caddy` is on PATH (suggests `brew install caddy` if missing).
|
|
321
|
+
2. Creates `~/.lenneTech/Caddyfile` stub if absent.
|
|
322
|
+
3. Verifies the Caddy daemon is running (suggests `brew services start caddy`).
|
|
323
|
+
4. Validates the Caddyfile.
|
|
324
|
+
5. Reminds you to run `sudo caddy trust` once so browsers accept `https://*.localhost`.
|
|
330
325
|
|
|
331
|
-
|
|
332
|
-
```bash
|
|
333
|
-
# Inside a workspace or standalone project
|
|
334
|
-
lt local init
|
|
326
|
+
---
|
|
335
327
|
|
|
336
|
-
|
|
337
|
-
lt local init --slot 5 --noConfirm --patch
|
|
328
|
+
### `lt dev migrate`
|
|
338
329
|
|
|
339
|
-
|
|
340
|
-
|
|
330
|
+
Register an existing project with `lt dev` and apply idempotent env-aware patches. Safe to run multiple times; safe to run after `lt fullstack init`.
|
|
331
|
+
|
|
332
|
+
**Usage:**
|
|
333
|
+
```bash
|
|
334
|
+
lt dev migrate
|
|
341
335
|
```
|
|
342
336
|
|
|
337
|
+
**Alias:** `lt d m`
|
|
338
|
+
|
|
339
|
+
**What it does:**
|
|
340
|
+
1. Detects the workspace layout (monorepo `projects/api`+`projects/app`, or standalone).
|
|
341
|
+
2. Builds the project identity (slug from `package.json` "name", subdomains).
|
|
342
|
+
3. Patches legacy hardcoded ports in `config.env.ts` (`port: 3000`), `nuxt.config.ts` (`port: 3001`, vite proxy target), `playwright.config.ts` (`baseURL`/`host`/`url`) to env-overridable form. Defaults preserved, idempotent.
|
|
343
|
+
4. Persists the entry to `~/.lenneTech/projects.json` (override path via `LT_DEV_REGISTRY_PATH`).
|
|
344
|
+
5. Adds `.lt-dev/` to the project's `.gitignore`.
|
|
345
|
+
6. Injects (or refreshes) a "Local Development (lt dev)" URL block into `CLAUDE.md` files at the workspace root and inside each subproject — bracketed by HTML comment markers.
|
|
346
|
+
|
|
343
347
|
---
|
|
344
348
|
|
|
345
|
-
### `lt
|
|
349
|
+
### `lt dev up`
|
|
346
350
|
|
|
347
|
-
Start
|
|
351
|
+
Start API + App behind Caddy. Allocates internal ports (4000+), spawns processes detached, persists PIDs to `<root>/.lt-dev/state.json`.
|
|
348
352
|
|
|
349
353
|
**Usage:**
|
|
350
354
|
```bash
|
|
351
|
-
lt
|
|
355
|
+
lt dev up
|
|
352
356
|
```
|
|
353
357
|
|
|
354
|
-
**Alias:** `lt
|
|
358
|
+
**Alias:** `lt d u`
|
|
355
359
|
|
|
356
|
-
**Environment variables injected
|
|
360
|
+
**Environment variables injected:**
|
|
357
361
|
| Variable | Consumer | Example value |
|
|
358
362
|
|----------|----------|---------------|
|
|
359
|
-
| `PORT` | Nest (api) / Nuxt
|
|
360
|
-
| `BASE_URL` | nest-server
|
|
361
|
-
| `APP_URL` | nest-server
|
|
362
|
-
| `NUXT_API_URL` | Nuxt vite-proxy target for `/api`, `/iam`, … | `
|
|
363
|
-
| `NUXT_PUBLIC_API_URL` | Nuxt `useRuntimeConfig().public.apiUrl` | `
|
|
364
|
-
| `NUXT_PUBLIC_SITE_URL` | Nuxt `useRuntimeConfig().public.siteUrl` + Playwright | `
|
|
365
|
-
| `NUXT_PUBLIC_STORAGE_PREFIX` | namespaces sessionStorage/localStorage
|
|
366
|
-
| `
|
|
367
|
-
|
|
368
|
-
|
|
363
|
+
| `PORT` | Nest (api) / Nuxt (app) | auto-allocated 4000+ |
|
|
364
|
+
| `BASE_URL` / `NSC__BASE_URL` | nest-server canonical API URL | `https://api.crm.localhost` |
|
|
365
|
+
| `APP_URL` / `NSC__APP_URL` | nest-server frontend origin (CORS, BetterAuth) | `https://crm.localhost` |
|
|
366
|
+
| `NUXT_API_URL` | Nuxt vite-proxy target for `/api`, `/iam`, … | `https://api.crm.localhost` |
|
|
367
|
+
| `NUXT_PUBLIC_API_URL` | Nuxt `useRuntimeConfig().public.apiUrl` | `https://api.crm.localhost` |
|
|
368
|
+
| `NUXT_PUBLIC_SITE_URL` | Nuxt `useRuntimeConfig().public.siteUrl` + Playwright | `https://crm.localhost` |
|
|
369
|
+
| `NUXT_PUBLIC_STORAGE_PREFIX` | namespaces sessionStorage/localStorage | `crm` |
|
|
370
|
+
| `NUXT_PUBLIC_API_PROXY` | always `false` — Caddy + cookie-domain make it obsolete | `false` |
|
|
371
|
+
| `NSC__MONGOOSE__URI` | nest-server Mongoose URI | `mongodb://127.0.0.1/crm-local` |
|
|
372
|
+
| `DATABASE_URL` | Postgres convenience URL (for nest-base-style projects) | `postgresql://crm-local:crm-local@localhost:5432/crm-local` |
|
|
373
|
+
|
|
374
|
+
**Override the binary** for both spawns via `LT_PNPM_BIN` (e.g. `LT_PNPM_BIN=/usr/local/bin/pnpm lt dev up`).
|
|
369
375
|
|
|
370
376
|
**Pre-flight guards (exit code 1 each):**
|
|
371
|
-
-
|
|
372
|
-
-
|
|
373
|
-
-
|
|
377
|
+
- Caddy not installed (`lt dev install` first)
|
|
378
|
+
- Caddy daemon not running (`brew services start caddy`)
|
|
379
|
+
- Already running for this project (`lt dev down` first)
|
|
380
|
+
- Internal port already in use
|
|
374
381
|
|
|
375
|
-
**Logs:** `<root>/.lt-
|
|
382
|
+
**Logs:** `<root>/.lt-dev/api.log`, `<root>/.lt-dev/app.log` (append-mode).
|
|
376
383
|
|
|
377
384
|
---
|
|
378
385
|
|
|
379
|
-
### `lt
|
|
386
|
+
### `lt dev down`
|
|
380
387
|
|
|
381
|
-
Stop processes started by `lt
|
|
388
|
+
Stop processes started by `lt dev up` and remove the project's Caddy block.
|
|
382
389
|
|
|
383
390
|
**Usage:**
|
|
384
391
|
```bash
|
|
385
|
-
lt
|
|
392
|
+
lt dev down
|
|
386
393
|
```
|
|
387
394
|
|
|
388
|
-
**Alias:** `lt
|
|
395
|
+
**Alias:** `lt d d`
|
|
389
396
|
|
|
390
|
-
|
|
397
|
+
Sends `SIGTERM` to the detached process group (negative PID) so descendants — Vite, the Nest watcher, etc. — receive the signal too. PID values from `state.json` are validated before signaling. Best-effort: removes the project's Caddy block and reloads even if no session was active.
|
|
391
398
|
|
|
392
399
|
---
|
|
393
400
|
|
|
394
|
-
### `lt
|
|
401
|
+
### `lt dev status`
|
|
395
402
|
|
|
396
|
-
Show what is registered + running
|
|
403
|
+
Show what is registered + running.
|
|
397
404
|
|
|
398
405
|
**Usage:**
|
|
399
406
|
```bash
|
|
400
|
-
lt
|
|
407
|
+
lt dev status # current project
|
|
408
|
+
lt dev status --all # every project in the registry
|
|
401
409
|
```
|
|
402
410
|
|
|
403
|
-
**Alias:** `lt
|
|
404
|
-
|
|
405
|
-
---
|
|
411
|
+
**Alias:** `lt d s`
|
|
406
412
|
|
|
407
|
-
|
|
413
|
+
The current-project view shows subdomains → upstream ports, db URI, session PIDs (alive/dead), and live `lsof` state. The `--all` view lists every project, with a `●`/`○` indicator for running state.
|
|
408
414
|
|
|
409
|
-
|
|
415
|
+
---
|
|
410
416
|
|
|
411
|
-
### `lt
|
|
417
|
+
### `lt dev doctor`
|
|
412
418
|
|
|
413
|
-
|
|
419
|
+
Diagnose Caddy / CA / DNS / port issues. Exit code 0 = all green, 1 = at least one FAIL.
|
|
414
420
|
|
|
415
421
|
**Usage:**
|
|
416
422
|
```bash
|
|
417
|
-
lt
|
|
423
|
+
lt dev doctor
|
|
418
424
|
```
|
|
419
425
|
|
|
420
|
-
**Alias:** `lt
|
|
426
|
+
**Alias:** `lt d doc`
|
|
421
427
|
|
|
422
|
-
**
|
|
423
|
-
1.
|
|
424
|
-
2.
|
|
428
|
+
**Checks:**
|
|
429
|
+
1. `caddy` on PATH
|
|
430
|
+
2. Caddy daemon running (admin endpoint `:2019` reachable)
|
|
431
|
+
3. Caddyfile validates
|
|
432
|
+
4. Ports 80 + 443 free or held by Caddy
|
|
433
|
+
5. `*.localhost` resolves to `127.0.0.1` (RFC 6761)
|
|
434
|
+
6. Registry status
|
|
425
435
|
|
|
426
436
|
---
|
|
427
437
|
|
|
428
|
-
### `lt
|
|
438
|
+
### `lt dev test`
|
|
429
439
|
|
|
430
|
-
|
|
440
|
+
One-shot E2E wrapper: ensure `up`, wait for the App URL, run `pnpm run test:e2e` with the `.lt-dev/.env` bridge loaded. Optional teardown after.
|
|
431
441
|
|
|
432
442
|
**Usage:**
|
|
433
443
|
```bash
|
|
434
|
-
lt
|
|
444
|
+
lt dev test # App E2E (projects/app)
|
|
445
|
+
lt dev test --api # API E2E (projects/api) — no Caddy required
|
|
446
|
+
lt dev test --teardown # plus `lt dev down` after
|
|
447
|
+
lt dev test --debug # PWDEBUG=1 + HEADED=1
|
|
448
|
+
lt dev test -- --ui spec.ts # everything after `--` is forwarded to playwright
|
|
435
449
|
```
|
|
436
450
|
|
|
437
|
-
**
|
|
438
|
-
| Code | Meaning |
|
|
439
|
-
|------|---------|
|
|
440
|
-
| `0` | Port is free |
|
|
441
|
-
| `1` | Port is in use |
|
|
442
|
-
| `2` | `lsof` not available, or `<port>` argument missing/invalid |
|
|
451
|
+
**Alias:** `lt d t`
|
|
443
452
|
|
|
444
|
-
**
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
453
|
+
**Behaviour:**
|
|
454
|
+
1. Pre-flight: Caddy installed + daemon running (App mode only).
|
|
455
|
+
2. If no `lt dev up` session is alive: invokes `lt dev up` first.
|
|
456
|
+
3. Waits up to 30 s for the App URL to respond.
|
|
457
|
+
4. Reads `<root>/.lt-dev/.env` and merges into the spawn env (existing process.env wins for keys it defines).
|
|
458
|
+
5. Spawns `pnpm run test:e2e [forwarded args]` in `projects/api` (with `--api`) or `projects/app` (default).
|
|
459
|
+
6. With `--teardown`, runs `lt dev down` after.
|
|
460
|
+
|
|
461
|
+
**When to use this vs. `pnpm run test:e2e` directly:**
|
|
462
|
+
- Use **`lt dev test`** for TDD loops, ad-hoc reproduction, or when you want a single-command "ensure-up + run + teardown" flow.
|
|
463
|
+
- Use **direct `pnpm run test:e2e`** (or VS Code Playwright Extension, IDE test runners) for everyday work — the auto-injected `playwright.config.ts` bridge loads the `.lt-dev/.env` automatically, so the env is correct without the wrapper.
|
|
452
464
|
|
|
453
465
|
---
|
|
454
466
|
|
|
455
|
-
###
|
|
467
|
+
### ENV bridge for external test runners
|
|
456
468
|
|
|
457
|
-
|
|
469
|
+
`lt dev up` writes a `<root>/.lt-dev/.env` file with the following keys:
|
|
458
470
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
471
|
+
| Key | Source |
|
|
472
|
+
|-----|--------|
|
|
473
|
+
| `BASE_URL`, `APP_URL`, `NSC__BASE_URL`, `NSC__APP_URL` | Identity → `https://api.<slug>.localhost` / `https://<slug>.localhost` |
|
|
474
|
+
| `NUXT_API_URL`, `NUXT_PUBLIC_API_URL`, `NUXT_PUBLIC_SITE_URL` | Same URLs for Nuxt |
|
|
475
|
+
| `NUXT_PUBLIC_STORAGE_PREFIX` | Project slug |
|
|
476
|
+
| `NUXT_PUBLIC_API_PROXY` | Always `false` under `lt dev` |
|
|
477
|
+
| `NSC__MONGOOSE__URI`, `DATABASE_URL` | Project-namespaced DB URI (when `dbName` known) |
|
|
478
|
+
| `LT_DEV_ACTIVE`, `LT_DEV_DB_NAME` | Marker keys for consumers |
|
|
479
|
+
| `NODE_EXTRA_CA_CERTS` | Path to Caddy's root CA cert (auto-detected) |
|
|
463
480
|
|
|
464
|
-
|
|
465
|
-
```bash
|
|
466
|
-
lt ports scan # scan from cwd
|
|
467
|
-
lt ports scan ~/code/lenneTech # scan a specific tree
|
|
468
|
-
```
|
|
481
|
+
`lt dev migrate` injects a tiny `// >>> lt-dev:bridge >>>` block at the top of `playwright.config.ts` that loads this file at config-load time — making Playwright (CLI, IDE, VS Code extension) automatically use the `lt dev` URLs and trust the local CA, without inheriting the parent shell.
|
|
469
482
|
|
|
470
|
-
|
|
483
|
+
`lt dev down` removes the bridge file so subsequent runs without `lt dev up` fall back cleanly to the classic `localhost:3000`/`localhost:3001` defaults.
|
|
471
484
|
|
|
472
485
|
---
|
|
473
486
|
|
package/package.json
CHANGED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
const local_project_1 = require("../../lib/local-project");
|
|
13
|
-
const port_registry_1 = require("../../lib/port-registry");
|
|
14
|
-
/**
|
|
15
|
-
* Stop processes started by `lt local up`. Sends SIGTERM to the
|
|
16
|
-
* detached process group (negative PID) so descendants — Vite,
|
|
17
|
-
* the Nest watcher etc. — receive the signal too.
|
|
18
|
-
*/
|
|
19
|
-
const DownCommand = {
|
|
20
|
-
alias: ['d'],
|
|
21
|
-
description: 'Stop API + App',
|
|
22
|
-
hidden: false,
|
|
23
|
-
name: 'down',
|
|
24
|
-
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
-
const { filesystem, parameters, print: { colors, info, success, warning }, } = toolbox;
|
|
26
|
-
const layout = (0, local_project_1.resolveLayout)(filesystem.cwd(), filesystem);
|
|
27
|
-
const state = (0, port_registry_1.loadLocalState)(layout.root);
|
|
28
|
-
if (!state || (!state.pids.api && !state.pids.app)) {
|
|
29
|
-
info(colors.dim('No running processes registered for this project.'));
|
|
30
|
-
if (!parameters.options.fromGluegunMenu)
|
|
31
|
-
process.exit();
|
|
32
|
-
return 'local down: nothing to stop';
|
|
33
|
-
}
|
|
34
|
-
const stopped = [];
|
|
35
|
-
for (const [name, pid] of Object.entries(state.pids)) {
|
|
36
|
-
if (!pid)
|
|
37
|
-
continue;
|
|
38
|
-
// Defense-in-depth: refuse anything that loadLocalState's schema gate
|
|
39
|
-
// wouldn't have accepted. Prevents a tampered state.json from causing
|
|
40
|
-
// process.kill(-pid, …) to signal arbitrary process groups.
|
|
41
|
-
if (!(0, port_registry_1.isValidPid)(pid)) {
|
|
42
|
-
warning(`Refusing to signal suspicious pid ${pid} for ${name} (state.json tampered?)`);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
if (!(0, port_registry_1.isPidAlive)(pid)) {
|
|
46
|
-
stopped.push(`${name} (pid ${pid}, already dead)`);
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
try {
|
|
50
|
-
// Negative PID kills the process group of a detached process.
|
|
51
|
-
process.kill(-pid, 'SIGTERM');
|
|
52
|
-
stopped.push(`${name} (pid ${pid})`);
|
|
53
|
-
}
|
|
54
|
-
catch (_a) {
|
|
55
|
-
try {
|
|
56
|
-
process.kill(pid, 'SIGTERM');
|
|
57
|
-
stopped.push(`${name} (pid ${pid}, single)`);
|
|
58
|
-
}
|
|
59
|
-
catch (_b) {
|
|
60
|
-
warning(`Failed to stop ${name} (pid ${pid})`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
(0, port_registry_1.clearLocalState)(layout.root);
|
|
65
|
-
success(`Stopped: ${stopped.join(', ')}`);
|
|
66
|
-
if (!parameters.options.fromGluegunMenu)
|
|
67
|
-
process.exit();
|
|
68
|
-
return `local down: ${stopped.length} stopped`;
|
|
69
|
-
}),
|
|
70
|
-
};
|
|
71
|
-
module.exports = DownCommand;
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
const fs_1 = require("fs");
|
|
13
|
-
const path_1 = require("path");
|
|
14
|
-
const local_patches_1 = require("../../lib/local-patches");
|
|
15
|
-
const local_project_1 = require("../../lib/local-project");
|
|
16
|
-
const port_registry_1 = require("../../lib/port-registry");
|
|
17
|
-
/**
|
|
18
|
-
* Register a port slot for the current project + optionally patch
|
|
19
|
-
* legacy hardcoded ports.
|
|
20
|
-
*/
|
|
21
|
-
const InitCommand = {
|
|
22
|
-
alias: ['i'],
|
|
23
|
-
description: 'Register port slot',
|
|
24
|
-
hidden: false,
|
|
25
|
-
name: 'init',
|
|
26
|
-
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
|
-
const { filesystem, parameters, print: { colors, error, info, success, warning }, prompt, } = toolbox;
|
|
28
|
-
const layout = (0, local_project_1.resolveLayout)(filesystem.cwd(), filesystem);
|
|
29
|
-
if (!layout.apiDir && !layout.appDir) {
|
|
30
|
-
error('No API (src/config.env.ts) or App (nuxt.config.ts) project detected at this path.');
|
|
31
|
-
if (!parameters.options.fromGluegunMenu)
|
|
32
|
-
process.exit(1);
|
|
33
|
-
return 'local init: not a project';
|
|
34
|
-
}
|
|
35
|
-
const slug = (0, port_registry_1.projectSlug)(layout.root);
|
|
36
|
-
const registry = (0, port_registry_1.loadRegistry)();
|
|
37
|
-
// Determine slot: existing entry > CLI flag > deterministic from slug.
|
|
38
|
-
let slot;
|
|
39
|
-
const cliSlot = parameters.options.slot !== undefined ? Number(parameters.options.slot) : null;
|
|
40
|
-
if (registry.projects[slug]) {
|
|
41
|
-
slot = registry.projects[slug].slot;
|
|
42
|
-
info(`Project "${slug}" already registered with slot ${slot}.`);
|
|
43
|
-
}
|
|
44
|
-
else if (cliSlot !== null && Number.isFinite(cliSlot)) {
|
|
45
|
-
slot = cliSlot;
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
slot = (0, port_registry_1.allocateSlot)(slug, registry);
|
|
49
|
-
}
|
|
50
|
-
const ports = (0, port_registry_1.portsForSlot)(slot);
|
|
51
|
-
info('');
|
|
52
|
-
info(colors.bold('Project layout'));
|
|
53
|
-
info(colors.dim('─'.repeat(50)));
|
|
54
|
-
info(` name: ${slug}`);
|
|
55
|
-
info(` root: ${layout.root}`);
|
|
56
|
-
if (layout.apiDir)
|
|
57
|
-
info(` api: ${layout.apiDir}`);
|
|
58
|
-
if (layout.appDir)
|
|
59
|
-
info(` app: ${layout.appDir}`);
|
|
60
|
-
info(` slot: ${slot}`);
|
|
61
|
-
info(` ports: api=${ports.api} app=${ports.app}`);
|
|
62
|
-
// Detect legacy hardcoded ports
|
|
63
|
-
const apiPatchFile = layout.apiDir ? (0, local_project_1.apiNeedsPortPatch)(layout.apiDir) : null;
|
|
64
|
-
const appPatchFiles = layout.appDir ? (0, local_project_1.appNeedsPortPatch)(layout.appDir) : [];
|
|
65
|
-
const filesToPatch = [apiPatchFile, ...appPatchFiles].filter((f) => Boolean(f));
|
|
66
|
-
const noConfirm = Boolean(parameters.options.noConfirm);
|
|
67
|
-
const noPatch = Boolean(parameters.options.noPatch);
|
|
68
|
-
const forcePatch = Boolean(parameters.options.patch);
|
|
69
|
-
if (filesToPatch.length > 0 && !noPatch) {
|
|
70
|
-
info('');
|
|
71
|
-
warning('Files with legacy hardcoded ports detected:');
|
|
72
|
-
filesToPatch.forEach((f) => info(` - ${f}`));
|
|
73
|
-
info(colors.dim('Patch makes them env-overridable: `process.env.PORT || 3000` etc. — defaults preserved.'));
|
|
74
|
-
let doPatch = forcePatch;
|
|
75
|
-
if (!doPatch && !noConfirm) {
|
|
76
|
-
const ans = yield prompt.confirm('Apply env-aware patches now?', true);
|
|
77
|
-
doPatch = Boolean(ans);
|
|
78
|
-
}
|
|
79
|
-
else if (!doPatch && noConfirm) {
|
|
80
|
-
info(colors.dim('Skipping patches (--noConfirm without --patch). Pass --patch to auto-apply.'));
|
|
81
|
-
}
|
|
82
|
-
if (doPatch) {
|
|
83
|
-
for (const file of filesToPatch) {
|
|
84
|
-
const result = (0, local_patches_1.autoPatch)(file);
|
|
85
|
-
if (result.patched) {
|
|
86
|
-
success(`patched ${result.replacements}× in ${file}`);
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
info(colors.dim(`skipped (already patched): ${file}`));
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
else if (filesToPatch.length === 0) {
|
|
95
|
-
info(colors.dim(' patches: not needed (already env-aware)'));
|
|
96
|
-
}
|
|
97
|
-
// Persist to registry only when something actually changed — avoids
|
|
98
|
-
// mtime churn on ~/.lenneTech/ports.json for cloud-sync tools (Dropbox,
|
|
99
|
-
// iCloud Drive, Syncthing) and editor "file changed externally" prompts.
|
|
100
|
-
const dbName = deriveDbName(layout, slug);
|
|
101
|
-
const existing = registry.projects[slug];
|
|
102
|
-
const next = { dbName, path: layout.root, ports, slot };
|
|
103
|
-
const changed = !existing ||
|
|
104
|
-
existing.path !== next.path ||
|
|
105
|
-
existing.slot !== next.slot ||
|
|
106
|
-
existing.dbName !== next.dbName ||
|
|
107
|
-
existing.ports.api !== next.ports.api ||
|
|
108
|
-
existing.ports.app !== next.ports.app;
|
|
109
|
-
if (changed) {
|
|
110
|
-
registry.projects[slug] = next;
|
|
111
|
-
(0, port_registry_1.saveRegistry)(registry);
|
|
112
|
-
}
|
|
113
|
-
// Add .lt-local/ to .gitignore (idempotent)
|
|
114
|
-
addToGitignore(layout.root, '.lt-local/');
|
|
115
|
-
// Patch CLAUDE.md files (workspace + each subproject) with the active
|
|
116
|
-
// port block so future Claude Code sessions read the correct ports
|
|
117
|
-
// even when the lt-dev plugin's hook is not active.
|
|
118
|
-
const claudeMdCandidates = [
|
|
119
|
-
(0, path_1.join)(layout.root, 'CLAUDE.md'),
|
|
120
|
-
...(layout.apiDir ? [(0, path_1.join)(layout.apiDir, 'CLAUDE.md')] : []),
|
|
121
|
-
...(layout.appDir ? [(0, path_1.join)(layout.appDir, 'CLAUDE.md')] : []),
|
|
122
|
-
];
|
|
123
|
-
const claudePatches = claudeMdCandidates
|
|
124
|
-
.map((file) => (0, local_patches_1.patchClaudeMd)(file, { apiPort: ports.api, appPort: ports.app, dbName, slug }))
|
|
125
|
-
.filter((r) => r.patched);
|
|
126
|
-
if (claudePatches.length > 0) {
|
|
127
|
-
claudePatches.forEach((r) => success(`updated CLAUDE.md port block: ${r.file}`));
|
|
128
|
-
}
|
|
129
|
-
info('');
|
|
130
|
-
success(`Registered. Run \`lt local up\` to start.`);
|
|
131
|
-
if (!parameters.options.fromGluegunMenu)
|
|
132
|
-
process.exit();
|
|
133
|
-
return `local init ${slug} slot=${slot}`;
|
|
134
|
-
}),
|
|
135
|
-
};
|
|
136
|
-
/** Append entry to .gitignore if not already present. */
|
|
137
|
-
function addToGitignore(root, entry) {
|
|
138
|
-
const path = (0, path_1.join)(root, '.gitignore');
|
|
139
|
-
let content = '';
|
|
140
|
-
if ((0, fs_1.existsSync)(path))
|
|
141
|
-
content = (0, fs_1.readFileSync)(path, 'utf8');
|
|
142
|
-
const lines = content.split(/\r?\n/);
|
|
143
|
-
if (lines.some((l) => l.trim() === entry || l.trim() === entry.replace(/\/$/, '')))
|
|
144
|
-
return;
|
|
145
|
-
const ensured = `${(content.endsWith('\n') || content.length === 0 ? content : `${content}\n`) + entry}\n`;
|
|
146
|
-
(0, fs_1.writeFileSync)(path, ensured, 'utf8');
|
|
147
|
-
}
|
|
148
|
-
/** Derive a sensible default DB name from project + workspace shape. */
|
|
149
|
-
function deriveDbName(layout, slug) {
|
|
150
|
-
// Reuse existing dbName from the API config if it is the default `${slug}-local`
|
|
151
|
-
if (layout.apiDir) {
|
|
152
|
-
const cfg = (0, path_1.join)(layout.apiDir, 'src', 'config.env.ts');
|
|
153
|
-
if ((0, fs_1.existsSync)(cfg)) {
|
|
154
|
-
const content = (0, fs_1.readFileSync)(cfg, 'utf8');
|
|
155
|
-
const match = content.match(/dbName:\s*['"`]([^'"`]+)['"`]/);
|
|
156
|
-
if (match)
|
|
157
|
-
return match[1];
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return `${slug}-local`;
|
|
161
|
-
}
|
|
162
|
-
module.exports = InitCommand;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
const local_project_1 = require("../../lib/local-project");
|
|
13
|
-
const port_registry_1 = require("../../lib/port-registry");
|
|
14
|
-
/**
|
|
15
|
-
* Show what is running for the current project.
|
|
16
|
-
*/
|
|
17
|
-
const StatusCommand = {
|
|
18
|
-
alias: ['s'],
|
|
19
|
-
description: 'Show local status',
|
|
20
|
-
hidden: false,
|
|
21
|
-
name: 'status',
|
|
22
|
-
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
|
-
var _a, _b;
|
|
24
|
-
const { filesystem, parameters, print: { colors, info, warning }, } = toolbox;
|
|
25
|
-
const layout = (0, local_project_1.resolveLayout)(filesystem.cwd(), filesystem);
|
|
26
|
-
const slug = (0, port_registry_1.projectSlug)(layout.root);
|
|
27
|
-
const registry = (0, port_registry_1.loadRegistry)();
|
|
28
|
-
const entry = registry.projects[slug];
|
|
29
|
-
info('');
|
|
30
|
-
info(colors.bold(`Local status: ${slug}`));
|
|
31
|
-
info(colors.dim('─'.repeat(50)));
|
|
32
|
-
if (!entry) {
|
|
33
|
-
warning('Not registered. Run `lt local init` first.');
|
|
34
|
-
if (!parameters.options.fromGluegunMenu)
|
|
35
|
-
process.exit();
|
|
36
|
-
return 'local status: not registered';
|
|
37
|
-
}
|
|
38
|
-
const ports = (0, port_registry_1.portsForSlot)(entry.slot);
|
|
39
|
-
info(` slot: ${entry.slot}`);
|
|
40
|
-
info(` api: http://localhost:${ports.api}`);
|
|
41
|
-
info(` app: http://localhost:${ports.app}`);
|
|
42
|
-
if (entry.dbName)
|
|
43
|
-
info(` db: mongodb://127.0.0.1/${entry.dbName}`);
|
|
44
|
-
const state = (0, port_registry_1.loadLocalState)(layout.root);
|
|
45
|
-
info('');
|
|
46
|
-
if (!state || (!state.pids.api && !state.pids.app)) {
|
|
47
|
-
info(colors.dim(' no `lt local up` session active'));
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
const apiAlive = state.pids.api ? (0, port_registry_1.isPidAlive)(state.pids.api) : false;
|
|
51
|
-
const appAlive = state.pids.app ? (0, port_registry_1.isPidAlive)(state.pids.app) : false;
|
|
52
|
-
info(` api: ${apiAlive ? colors.green('running') : colors.red('dead')} (pid ${(_a = state.pids.api) !== null && _a !== void 0 ? _a : '-'})`);
|
|
53
|
-
info(` app: ${appAlive ? colors.green('running') : colors.red('dead')} (pid ${(_b = state.pids.app) !== null && _b !== void 0 ? _b : '-'})`);
|
|
54
|
-
info(colors.dim(` started: ${state.startedAt}`));
|
|
55
|
-
}
|
|
56
|
-
info('');
|
|
57
|
-
info(colors.bold('Live port state'));
|
|
58
|
-
const liveSnapshot = yield (0, port_registry_1.listenSnapshot)([ports.api, ports.app]);
|
|
59
|
-
for (const port of [ports.api, ports.app]) {
|
|
60
|
-
const r = liveSnapshot.get(port);
|
|
61
|
-
info(` ${port}: ${r ? colors.green(`bound to ${r.command} (pid ${r.pid})`) : colors.dim('free')}`);
|
|
62
|
-
}
|
|
63
|
-
info('');
|
|
64
|
-
if (!parameters.options.fromGluegunMenu)
|
|
65
|
-
process.exit();
|
|
66
|
-
return `local status ${slug}`;
|
|
67
|
-
}),
|
|
68
|
-
};
|
|
69
|
-
module.exports = StatusCommand;
|