@lenne.tech/cli 1.20.0 → 1.21.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.
@@ -0,0 +1,304 @@
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
+ exports.SLOT_PORT_RANGE_END = exports.SLOT_MAX = exports.SLOT_STEP = exports.SLOT_BASE_API = void 0;
13
+ exports.allocatedSlots = allocatedSlots;
14
+ exports.allocateSlot = allocateSlot;
15
+ exports.checkPortInUse = checkPortInUse;
16
+ exports.clearLocalState = clearLocalState;
17
+ exports.isPidAlive = isPidAlive;
18
+ exports.isValidPid = isValidPid;
19
+ exports.listenSnapshot = listenSnapshot;
20
+ exports.loadLocalState = loadLocalState;
21
+ exports.loadRegistry = loadRegistry;
22
+ exports.localStatePath = localStatePath;
23
+ exports.portsForSlot = portsForSlot;
24
+ exports.projectSlug = projectSlug;
25
+ exports.registryPath = registryPath;
26
+ exports.saveLocalState = saveLocalState;
27
+ exports.saveRegistry = saveRegistry;
28
+ exports.slotFromSlug = slotFromSlug;
29
+ /**
30
+ * Port registry helpers for `lt local` and `lt ports`.
31
+ *
32
+ * Provides:
33
+ * - Deterministic slot allocation from a project slug (hash-based, reproducible across machines)
34
+ * - Persistent registry at ~/.lenneTech/ports.json
35
+ * - Live port introspection via `lsof` (single-call snapshot for batch checks)
36
+ * - Process state tracking under <project>/.lt-local/state.json
37
+ */
38
+ const child_process_1 = require("child_process");
39
+ const fs_1 = require("fs");
40
+ const os_1 = require("os");
41
+ const path_1 = require("path");
42
+ /** Lowest API port: slot 0 maps to 3000/3001, slot 1 to 3010/3011, … */
43
+ exports.SLOT_BASE_API = 3000;
44
+ /** Distance between two adjacent slots' API ports. */
45
+ exports.SLOT_STEP = 10;
46
+ /** Number of slots [0..SLOT_MAX). API range = [SLOT_BASE_API, SLOT_BASE_API + SLOT_MAX*SLOT_STEP). */
47
+ exports.SLOT_MAX = 90;
48
+ /** Highest port (exclusive) covered by the slot range. Useful for live-port sweeps. */
49
+ exports.SLOT_PORT_RANGE_END = exports.SLOT_BASE_API + exports.SLOT_MAX * exports.SLOT_STEP;
50
+ /** All currently allocated slots in the registry. */
51
+ function allocatedSlots(registry) {
52
+ return new Set(Object.values(registry.projects).map((p) => p.slot));
53
+ }
54
+ /**
55
+ * Allocate a slot for a project. Returns deterministic slot from slug if free,
56
+ * otherwise scans linearly until a free slot is found. Throws if all are taken.
57
+ *
58
+ * The linear scan loops `i in [1, SLOT_MAX)` (not `<=`) because `i = 0` is the
59
+ * preferred slot already checked in the fast path above.
60
+ */
61
+ function allocateSlot(slug, registry) {
62
+ const taken = allocatedSlots(registry);
63
+ const preferred = slotFromSlug(slug);
64
+ if (!taken.has(preferred)) {
65
+ return preferred;
66
+ }
67
+ for (let i = 1; i < exports.SLOT_MAX; i++) {
68
+ const candidate = (preferred + i) % exports.SLOT_MAX;
69
+ if (!taken.has(candidate)) {
70
+ return candidate;
71
+ }
72
+ }
73
+ throw new Error('No free port slot available (all 90 slots are taken).');
74
+ }
75
+ /**
76
+ * Check via `lsof` whether a single TCP port is currently bound by a LISTEN socket.
77
+ *
78
+ * For multi-port checks prefer {@link listenSnapshot} — it issues a single `lsof`
79
+ * call instead of one per port (~50ms vs ~50ms × N).
80
+ *
81
+ * Note: `-iTCP:<port>` selects connections by *service port* — both LISTEN
82
+ * sockets and remote endpoints. We filter explicitly to LISTEN via
83
+ * `-sTCP:LISTEN` AND post-filter the NAME column for `*:<port>` /
84
+ * `<addr>:<port>` (LISTEN) so outgoing connections whose remote port is
85
+ * `<port>` don't trigger a false positive.
86
+ *
87
+ * Returns null if lsof is unavailable.
88
+ */
89
+ function checkPortInUse(port) {
90
+ return __awaiter(this, void 0, void 0, function* () {
91
+ return new Promise((resolve) => {
92
+ var _a;
93
+ const child = (0, child_process_1.spawn)('lsof', ['-iTCP', `-sTCP:LISTEN`, '-nP', `-iTCP:${port}`], {
94
+ stdio: ['ignore', 'pipe', 'ignore'],
95
+ });
96
+ let out = '';
97
+ (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (chunk) => (out += chunk.toString()));
98
+ child.on('error', () => resolve(null));
99
+ child.on('close', () => {
100
+ const lines = out.trim().split('\n').slice(1); // skip header
101
+ const portRe = new RegExp(`:${port}\\s+\\(LISTEN\\)\\s*$`);
102
+ const match = lines.find((l) => portRe.test(l));
103
+ if (!match) {
104
+ resolve({ inUse: false });
105
+ return;
106
+ }
107
+ const cols = match.split(/\s+/);
108
+ resolve({ command: cols[0], inUse: true, pid: Number(cols[1]) });
109
+ });
110
+ });
111
+ });
112
+ }
113
+ /** Reset state file to an empty record. Called by `lt local down` after stopping processes. */
114
+ function clearLocalState(projectPath) {
115
+ const path = localStatePath(projectPath);
116
+ if ((0, fs_1.existsSync)(path)) {
117
+ (0, fs_1.writeFileSync)(path, `${JSON.stringify({ pids: {}, ports: { api: 0, app: 0 }, startedAt: '' }, null, 2)}\n`, 'utf8');
118
+ }
119
+ }
120
+ /**
121
+ * Check whether a PID is still alive without sending any signal.
122
+ * `process.kill(pid, 0)` performs a permission check; ESRCH means dead.
123
+ *
124
+ * Refuses non-positive / non-integer PIDs to prevent accidental probes
125
+ * of process groups (negative PID) or every user-owned process (PID 0/-1).
126
+ */
127
+ function isPidAlive(pid) {
128
+ if (!Number.isInteger(pid) || pid <= 0)
129
+ return false;
130
+ try {
131
+ process.kill(pid, 0);
132
+ return true;
133
+ }
134
+ catch (e) {
135
+ return e.code === 'EPERM';
136
+ }
137
+ }
138
+ /**
139
+ * Validate a value parsed from `state.json` as a plausible PID.
140
+ *
141
+ * Accepts: positive integer in [100, 2^31 - 1] or undefined.
142
+ * The lower bound 100 excludes init / kernel / login PIDs that should
143
+ * never be the result of a `pnpm start` spawn.
144
+ */
145
+ function isValidPid(value) {
146
+ if (value === undefined)
147
+ return true;
148
+ if (typeof value !== 'number')
149
+ return false;
150
+ return Number.isInteger(value) && value >= 100 && value <= 0x7fffffff;
151
+ }
152
+ /**
153
+ * One-shot listener snapshot for an arbitrary set of ports.
154
+ *
155
+ * Issues a single `lsof -iTCP -sTCP:LISTEN -nP` call and filters in memory.
156
+ * ~50ms total regardless of port count, vs ~50ms × N for sequential
157
+ * {@link checkPortInUse} calls.
158
+ *
159
+ * Returns an empty Map if `lsof` is unavailable.
160
+ */
161
+ function listenSnapshot(ports) {
162
+ return __awaiter(this, void 0, void 0, function* () {
163
+ return new Promise((resolve) => {
164
+ var _a;
165
+ const child = (0, child_process_1.spawn)('lsof', ['-iTCP', '-sTCP:LISTEN', '-nP'], { stdio: ['ignore', 'pipe', 'ignore'] });
166
+ let out = '';
167
+ (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (chunk) => (out += chunk.toString()));
168
+ child.on('error', () => resolve(new Map()));
169
+ child.on('close', () => {
170
+ const wanted = new Set(ports);
171
+ const result = new Map();
172
+ const lines = out.trim().split('\n').slice(1);
173
+ const re = /:(\d+)\s+\(LISTEN\)\s*$/;
174
+ for (const line of lines) {
175
+ const m = re.exec(line);
176
+ if (!m)
177
+ continue;
178
+ const port = Number(m[1]);
179
+ if (!wanted.has(port))
180
+ continue;
181
+ if (result.has(port))
182
+ continue; // first hit wins (IPv4 before IPv6)
183
+ const cols = line.split(/\s+/);
184
+ result.set(port, { command: cols[0], pid: Number(cols[1]) });
185
+ }
186
+ resolve(result);
187
+ });
188
+ });
189
+ });
190
+ }
191
+ /**
192
+ * Load the local state JSON for a project.
193
+ *
194
+ * Returns null when the file is missing, unreadable, malformed, or contains
195
+ * structurally invalid data (see {@link isValidPid}). The schema validation
196
+ * here is the authoritative gate: it prevents `process.kill(-pid, …)` in
197
+ * `lt local down` from receiving anything but a plausible PID we ourselves
198
+ * could have written.
199
+ */
200
+ function loadLocalState(projectPath) {
201
+ const path = localStatePath(projectPath);
202
+ if (!(0, fs_1.existsSync)(path))
203
+ return null;
204
+ try {
205
+ const parsed = JSON.parse((0, fs_1.readFileSync)(path, 'utf8'));
206
+ if (!parsed || typeof parsed !== 'object')
207
+ return null;
208
+ const obj = parsed;
209
+ const pids = obj.pids;
210
+ const ports = obj.ports;
211
+ if (!pids || typeof pids !== 'object' || !ports || typeof ports !== 'object')
212
+ return null;
213
+ if (!isValidPid(pids.api) || !isValidPid(pids.app))
214
+ return null;
215
+ if (typeof ports.api !== 'number' || typeof ports.app !== 'number')
216
+ return null;
217
+ if (typeof obj.startedAt !== 'string')
218
+ return null;
219
+ return {
220
+ pids: { api: pids.api, app: pids.app },
221
+ ports: { api: ports.api, app: ports.app },
222
+ startedAt: obj.startedAt,
223
+ };
224
+ }
225
+ catch (_a) {
226
+ return null;
227
+ }
228
+ }
229
+ /**
230
+ * Load registry; returns empty if missing or corrupt.
231
+ *
232
+ * Prints a warning when a corrupt or schema-incompatible file is encountered
233
+ * so the user notices the silent reset rather than discovering stale port
234
+ * allocations later.
235
+ */
236
+ function loadRegistry() {
237
+ const path = registryPath();
238
+ if (!(0, fs_1.existsSync)(path)) {
239
+ return { projects: {}, version: 1 };
240
+ }
241
+ try {
242
+ const raw = (0, fs_1.readFileSync)(path, 'utf8');
243
+ const parsed = JSON.parse(raw);
244
+ if ((parsed === null || parsed === void 0 ? void 0 : parsed.version) !== 1 || typeof (parsed === null || parsed === void 0 ? void 0 : parsed.projects) !== 'object') {
245
+ console.warn(`[lt] ports.json has wrong schema (got version=${parsed === null || parsed === void 0 ? void 0 : parsed.version}); starting with empty registry.`);
246
+ return { projects: {}, version: 1 };
247
+ }
248
+ return parsed;
249
+ }
250
+ catch (e) {
251
+ console.warn(`[lt] ports.json was unreadable (${e.message}); starting with empty registry.`);
252
+ return { projects: {}, version: 1 };
253
+ }
254
+ }
255
+ /** Path to the local state file inside a project. */
256
+ function localStatePath(projectPath) {
257
+ return (0, path_1.join)(projectPath, '.lt-local', 'state.json');
258
+ }
259
+ /** Convert a slot to its API+App port pair. */
260
+ function portsForSlot(slot) {
261
+ const api = exports.SLOT_BASE_API + slot * exports.SLOT_STEP;
262
+ return { api, app: api + 1 };
263
+ }
264
+ /** Convert any project path → a stable slug (basename, lowercase, alpha-num + dashes). */
265
+ function projectSlug(projectPath) {
266
+ const base = projectPath.replace(/\/+$/, '').split('/').pop() || 'project';
267
+ return base
268
+ .toLowerCase()
269
+ .replace(/[^a-z0-9]+/g, '-')
270
+ .replace(/^-+|-+$/g, '');
271
+ }
272
+ /**
273
+ * Path to the central registry.
274
+ *
275
+ * Honors `LT_PORTS_REGISTRY_PATH` for tests / non-default workspaces;
276
+ * falls back to `~/.lenneTech/ports.json`.
277
+ */
278
+ function registryPath() {
279
+ return process.env.LT_PORTS_REGISTRY_PATH || (0, path_1.join)((0, os_1.homedir)(), '.lenneTech', 'ports.json');
280
+ }
281
+ /** Persist local state to <project>/.lt-local/state.json (creates the parent directory if needed). */
282
+ function saveLocalState(projectPath, state) {
283
+ const path = localStatePath(projectPath);
284
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(path), { recursive: true });
285
+ (0, fs_1.writeFileSync)(path, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
286
+ }
287
+ /** Save registry, creating parent directory if needed. */
288
+ function saveRegistry(registry) {
289
+ const path = registryPath();
290
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(path), { recursive: true });
291
+ (0, fs_1.writeFileSync)(path, `${JSON.stringify(registry, null, 2)}\n`, 'utf8');
292
+ }
293
+ /**
294
+ * Deterministic slot from project slug — same slug yields the same slot on every machine.
295
+ * Uses the lower 32 bits of FNV-1a then modulo SLOT_MAX.
296
+ */
297
+ function slotFromSlug(slug) {
298
+ let hash = 2166136261; // FNV-1a 32-bit offset basis
299
+ for (let i = 0; i < slug.length; i++) {
300
+ hash ^= slug.charCodeAt(i);
301
+ hash = Math.imul(hash, 16777619);
302
+ }
303
+ return Math.abs(hash | 0) % exports.SLOT_MAX;
304
+ }
package/docs/commands.md CHANGED
@@ -12,6 +12,8 @@ This document provides a comprehensive reference for all `lt` CLI commands. For
12
12
 
13
13
  - [CLI Commands](#cli-commands)
14
14
  - [Server Commands](#server-commands)
15
+ - [Local Development Commands](#local-development-commands)
16
+ - [Ports Commands](#ports-commands)
15
17
  - [Git Commands](#git-commands)
16
18
  - [Fullstack Commands](#fullstack-commands)
17
19
  - [Deployment Commands](#deployment-commands)
@@ -282,6 +284,193 @@ For mode-aware update workflows after conversion, use:
282
284
 
283
285
  ---
284
286
 
287
+ ## Local Development Commands
288
+
289
+ Orchestrate parallel lt projects on the same machine without port collisions. Each project gets a deterministic port slot derived from its slug; API/App ports are always slot-paired (`3000+slot*10` / `3001+slot*10`). Slot allocation is reproducible across machines (FNV-1a hash) and persisted in `~/.lenneTech/ports.json`.
290
+
291
+ ### `lt local`
292
+
293
+ Open the local-orchestration submenu.
294
+
295
+ **Usage:**
296
+ ```bash
297
+ lt local
298
+ ```
299
+
300
+ **Alias:** `lt l`
301
+
302
+ ---
303
+
304
+ ### `lt local init`
305
+
306
+ Register the current project in the central port registry, optionally patching legacy hardcoded ports to be env-aware.
307
+
308
+ **Usage:**
309
+ ```bash
310
+ lt local init [options]
311
+ ```
312
+
313
+ **Alias:** `lt l i`
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) |
322
+
323
+ **What it does:**
324
+ 1. Detects the workspace layout (monorepo with `projects/api/` + `projects/app/`, or standalone API/App project).
325
+ 2. Looks up or allocates a slot via FNV-1a hash of the project slug. If the slot is taken, falls through linearly to the next free slot.
326
+ 3. Detects legacy hardcoded ports in `config.env.ts` (`port: 3000`), `nuxt.config.ts` (`port: 3001`, vite proxy `target: 'http://localhost:3000'`), and `playwright.config.ts` (`baseURL`/`host`/`url: 'http://localhost:3001'`). If `--patch` (or interactive confirm), rewrites them to env-overridable form (`Number(process.env.PORT) || 3000`, `process.env.NUXT_API_URL || …`, etc.) — defaults preserved, idempotent.
327
+ 4. Persists the entry to `~/.lenneTech/ports.json`.
328
+ 5. Adds `.lt-local/` to the project's `.gitignore`.
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.
330
+
331
+ **Examples:**
332
+ ```bash
333
+ # Inside a workspace or standalone project
334
+ lt local init
335
+
336
+ # Force slot 5 for predictable cross-team ports
337
+ lt local init --slot 5 --noConfirm --patch
338
+
339
+ # Register without touching any source files
340
+ lt local init --no-patch --noConfirm
341
+ ```
342
+
343
+ ---
344
+
345
+ ### `lt local up`
346
+
347
+ Start the API + App with project-specific ports. Spawns `pnpm start` (api) and `pnpm dev` (app) detached; persists PIDs to `<root>/.lt-local/state.json`.
348
+
349
+ **Usage:**
350
+ ```bash
351
+ lt local up
352
+ ```
353
+
354
+ **Alias:** `lt l u`
355
+
356
+ **Environment variables injected into both children:**
357
+ | Variable | Consumer | Example value |
358
+ |----------|----------|---------------|
359
+ | `PORT` | Nest (api) / Nuxt dev server (app) | slot-derived |
360
+ | `BASE_URL` | nest-server config.env.ts (canonical API base) | `http://localhost:3030` |
361
+ | `APP_URL` | nest-server config.env.ts (frontend origin for redirects/CORS) | `http://localhost:3031` |
362
+ | `NUXT_API_URL` | Nuxt vite-proxy target for `/api`, `/iam`, … | `http://localhost:3030` |
363
+ | `NUXT_PUBLIC_API_URL` | Nuxt `useRuntimeConfig().public.apiUrl` | `http://localhost:3030` |
364
+ | `NUXT_PUBLIC_SITE_URL` | Nuxt `useRuntimeConfig().public.siteUrl` + Playwright | `http://localhost:3031` |
365
+ | `NUXT_PUBLIC_STORAGE_PREFIX` | namespaces sessionStorage/localStorage so parallel projects don't share auth tokens | `crm-local` |
366
+ | `NSC__MONGOOSE__URI` | nest-server-config Mongoose URI (only when `dbName` is known) | `mongodb://127.0.0.1/crm-local` |
367
+
368
+ **Override the binary** used for both spawns by setting `LT_PNPM_BIN` (e.g. `LT_PNPM_BIN=/usr/local/bin/pnpm lt local up`).
369
+
370
+ **Pre-flight guards (exit code 1 each):**
371
+ - Project not registered (`lt local init` first)
372
+ - Already running (run `lt local down` first)
373
+ - Port already in use by another process
374
+
375
+ **Logs:** `<root>/.lt-local/api.log`, `<root>/.lt-local/app.log` (append-mode).
376
+
377
+ ---
378
+
379
+ ### `lt local down`
380
+
381
+ Stop processes started by `lt local up`. Sends `SIGTERM` to the detached process group (negative PID) so descendants — Vite, the Nest watcher, etc. — receive the signal too. Falls back to single-PID kill if the process group send fails (`EPERM`).
382
+
383
+ **Usage:**
384
+ ```bash
385
+ lt local down
386
+ ```
387
+
388
+ **Alias:** `lt l d`
389
+
390
+ PID values from `state.json` are validated (positive integer in `[100, 2^31)`) before any signal is sent, so a tampered state file cannot cause `lt local down` to signal arbitrary process groups.
391
+
392
+ ---
393
+
394
+ ### `lt local status`
395
+
396
+ Show what is registered + running for the current project: slot, ports, db URI, PIDs (alive/dead), and live `lsof` state of the assigned ports.
397
+
398
+ **Usage:**
399
+ ```bash
400
+ lt local status
401
+ ```
402
+
403
+ **Alias:** `lt l s`
404
+
405
+ ---
406
+
407
+ ## Ports Commands
408
+
409
+ Inspect the port registry and currently-bound dev ports across all your lt projects. Useful for diagnosing collisions and rebuilding the registry from disk.
410
+
411
+ ### `lt ports`
412
+
413
+ List all reserved registry entries side-by-side with the live `lsof` state. Issues a single `lsof` call internally for the entire slot range (3000–3899) instead of per port — runs in ~150ms regardless of how many projects are registered.
414
+
415
+ **Usage:**
416
+ ```bash
417
+ lt ports
418
+ ```
419
+
420
+ **Alias:** `lt p`
421
+
422
+ **Output sections:**
423
+ 1. **Reserved ports (registry)** — every project entry with a `●` (bound) or `○` (free) indicator per port.
424
+ 2. **Currently bound dev ports (3000–3899)** — every port in the slot range that currently has a LISTEN socket, with command + PID + owning registry entry (if any).
425
+
426
+ ---
427
+
428
+ ### `lt ports check <port>`
429
+
430
+ Exit-coded port probe — useful in shell scripts.
431
+
432
+ **Usage:**
433
+ ```bash
434
+ lt ports check <port>
435
+ ```
436
+
437
+ **Exit codes:**
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 |
443
+
444
+ **Example:**
445
+ ```bash
446
+ if lt ports check 3000; then
447
+ echo "API port free"
448
+ else
449
+ echo "API port already bound"
450
+ fi
451
+ ```
452
+
453
+ ---
454
+
455
+ ### `lt ports scan [dir]`
456
+
457
+ Rebuild the registry from the filesystem. Walks the given directory (default: cwd) up to depth 3, looking for `lt.config.json` + `package.json` pairs or workspace markers (`pnpm-workspace.yaml`, `projects/`). Re-allocates a slot for new projects; preserves slots for existing entries (only refreshing the path if it moved). Writes only when the registry actually changed (no mtime churn for cloud-sync tools).
458
+
459
+ **Usage:**
460
+ ```bash
461
+ lt ports scan [dir]
462
+ ```
463
+
464
+ **Examples:**
465
+ ```bash
466
+ lt ports scan # scan from cwd
467
+ lt ports scan ~/code/lenneTech # scan a specific tree
468
+ ```
469
+
470
+ Symlinks are skipped to avoid traversal loops; dotdirs and `node_modules` are not descended into.
471
+
472
+ ---
473
+
285
474
  ## Git Commands
286
475
 
287
476
  All git commands support the `--noConfirm` flag and can be configured via `defaults.noConfirm` or `commands.git.noConfirm`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/cli",
3
- "version": "1.20.0",
3
+ "version": "1.21.0",
4
4
  "description": "lenne.Tech CLI: lt",
5
5
  "keywords": [
6
6
  "lenne.Tech",
@@ -58,16 +58,16 @@
58
58
  "bin"
59
59
  ],
60
60
  "dependencies": {
61
- "@aws-sdk/client-s3": "3.1032.0",
61
+ "@aws-sdk/client-s3": "3.1045.0",
62
62
  "@lenne.tech/cli-plugin-helper": "0.0.14",
63
- "axios": "1.15.0",
63
+ "axios": "1.16.0",
64
64
  "bcrypt": "6.0.0",
65
- "defuddle": "0.17.0",
65
+ "defuddle": "0.18.1",
66
66
  "glob": "13.0.6",
67
67
  "gluegun": "5.2.2",
68
68
  "js-sha256": "0.11.1",
69
69
  "js-yaml": "4.1.1",
70
- "jsdom": "29.0.2",
70
+ "jsdom": "29.1.1",
71
71
  "lodash": "4.18.1",
72
72
  "open": "11.0.0",
73
73
  "playwright-core": "1.59.1",
@@ -85,15 +85,12 @@
85
85
  "@types/js-yaml": "4.0.9",
86
86
  "@types/jsdom": "28.0.1",
87
87
  "@types/lodash": "4.17.24",
88
- "@types/node": "25.6.0",
88
+ "@types/node": "25.6.2",
89
89
  "@types/turndown": "5.0.6",
90
- "@typescript-eslint/eslint-plugin": "8.58.2",
91
- "@typescript-eslint/parser": "8.58.2",
92
90
  "ejs": "5.0.2",
93
91
  "eslint": "9.39.4",
94
- "eslint-config-prettier": "10.1.8",
95
92
  "husky": "9.1.7",
96
- "jest": "30.3.0",
93
+ "jest": "30.4.2",
97
94
  "prettier": "3.8.3",
98
95
  "rimraf": "6.1.3",
99
96
  "standard-version": "9.5.0",