@lpm-registry/cli 0.57.0 → 0.59.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.
Files changed (62) hide show
  1. package/README.md +21 -45
  2. package/bin/lpm.js +2 -354
  3. package/bin/lpx.js +4 -0
  4. package/bin/native.js +154 -0
  5. package/package.json +40 -82
  6. package/CHANGELOG.md +0 -197
  7. package/LICENSE +0 -15
  8. package/index.d.ts +0 -131
  9. package/index.js +0 -31
  10. package/lib/api.js +0 -398
  11. package/lib/commands/add.js +0 -1217
  12. package/lib/commands/audit.js +0 -283
  13. package/lib/commands/cache.js +0 -209
  14. package/lib/commands/check-name.js +0 -112
  15. package/lib/commands/config.js +0 -186
  16. package/lib/commands/doctor.js +0 -140
  17. package/lib/commands/info.js +0 -215
  18. package/lib/commands/init.js +0 -146
  19. package/lib/commands/install.js +0 -264
  20. package/lib/commands/login.js +0 -595
  21. package/lib/commands/logout.js +0 -94
  22. package/lib/commands/marketplace-compare.js +0 -164
  23. package/lib/commands/marketplace-earnings.js +0 -89
  24. package/lib/commands/mcp-setup.js +0 -363
  25. package/lib/commands/npmrc.js +0 -178
  26. package/lib/commands/open.js +0 -82
  27. package/lib/commands/outdated.js +0 -291
  28. package/lib/commands/pool-stats.js +0 -100
  29. package/lib/commands/publish.js +0 -949
  30. package/lib/commands/quality.js +0 -211
  31. package/lib/commands/remove.js +0 -82
  32. package/lib/commands/run.js +0 -14
  33. package/lib/commands/search.js +0 -143
  34. package/lib/commands/setup.js +0 -221
  35. package/lib/commands/skills.js +0 -898
  36. package/lib/commands/token-rotate.js +0 -25
  37. package/lib/commands/uninstall.js +0 -107
  38. package/lib/commands/whoami.js +0 -129
  39. package/lib/config.js +0 -328
  40. package/lib/constants.js +0 -190
  41. package/lib/ecosystem.js +0 -501
  42. package/lib/editor-skills.js +0 -321
  43. package/lib/editors.js +0 -215
  44. package/lib/import-rewriter.js +0 -364
  45. package/lib/install-targets/mcp-server.js +0 -245
  46. package/lib/install-targets/vscode-extension.js +0 -178
  47. package/lib/install-targets.js +0 -82
  48. package/lib/integrity.js +0 -179
  49. package/lib/lpm-config-prompts.js +0 -102
  50. package/lib/lpm-config.js +0 -408
  51. package/lib/oidc.js +0 -166
  52. package/lib/project-utils.js +0 -152
  53. package/lib/quality/checks.js +0 -687
  54. package/lib/quality/display.js +0 -155
  55. package/lib/quality/score.js +0 -118
  56. package/lib/quality/swift-checks.js +0 -447
  57. package/lib/safe-path.js +0 -180
  58. package/lib/secure-store.js +0 -321
  59. package/lib/swift-project.js +0 -637
  60. package/lib/ui.js +0 -40
  61. package/scripts/__tests__/install-binary.test.js +0 -346
  62. package/scripts/install-binary.js +0 -571
@@ -1,571 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * LPM CLI Binary Installer — 3-tier fallback + hard-link optimization.
5
- *
6
- * Runs as a postinstall hook. Resolution order:
7
- *
8
- * 1. optionalDependencies — platform package installed by npm (fastest, cached).
9
- * Integrity story: npm provenance attestation, verified by the npm registry
10
- * at install time. No extra check needed here.
11
- * 2. GitHub Releases — direct download if optionalDependencies missing
12
- * (--no-optional, corporate proxies blocking scoped packages, resolution
13
- * failure). Integrity story: every byte is anchored to a Sigstore-signed
14
- * `SHA256SUMS.txt` manifest published per release. The `sigstore` npm
15
- * package is consulted as an optional dependency — when resolvable, full
16
- * Sigstore bundle verification runs; when not, SHA-256 against the signed
17
- * manifest is the floor. Fail-closed on any integrity gate.
18
- * 3. Graceful fallback — JS CLI handles commands if Tier 2 returns null.
19
- *
20
- * After resolving the binary, hard-links it over bin/lpm.js so subsequent
21
- * runs execute the native binary directly (zero Node.js overhead).
22
- *
23
- * Set `LPM_INSTALL_INSECURE=1` to skip every Tier-2 integrity check (escape
24
- * valve for emergency installs from releases that predate the signed-install
25
- * gate). Emits a loud WARN on every use.
26
- *
27
- * Follows the esbuild postinstall pattern: https://github.com/evanw/esbuild
28
- */
29
-
30
- import { execSync } from "node:child_process"
31
- import crypto from "node:crypto"
32
- import fs from "node:fs"
33
- import https from "node:https"
34
- import { createRequire } from "node:module"
35
- import os from "node:os"
36
- import path from "node:path"
37
- import { fileURLToPath, pathToFileURL } from "node:url"
38
-
39
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
40
- const pkgDir = path.join(__dirname, "..")
41
- const binDir = path.join(pkgDir, "bin")
42
- const require = createRequire(import.meta.url)
43
-
44
- const PLATFORMS = {
45
- "darwin-arm64": { pkg: "@lpm-registry/cli-darwin-arm64", binary: "lpm" },
46
- "darwin-x64": { pkg: "@lpm-registry/cli-darwin-x64", binary: "lpm" },
47
- "linux-x64": { pkg: "@lpm-registry/cli-linux-x64", binary: "lpm" },
48
- "linux-arm64": { pkg: "@lpm-registry/cli-linux-arm64", binary: "lpm" },
49
- "win32-x64": { pkg: "@lpm-registry/cli-win32-x64", binary: "lpm.exe" },
50
- }
51
-
52
- const GITHUB_RELEASES =
53
- "https://github.com/lpm-dev/rust-client/releases/download"
54
- const GITHUB_BINARY_NAMES = {
55
- "darwin-arm64": "lpm-darwin-arm64",
56
- "darwin-x64": "lpm-darwin-x64",
57
- "linux-x64": "lpm-linux-x64",
58
- "linux-arm64": "lpm-linux-arm64",
59
- "win32-x64": "lpm-win32-x64.exe",
60
- }
61
-
62
- const MANIFEST_MAX_BYTES = 64 * 1024
63
- const BUNDLE_MAX_BYTES = 1024 * 1024
64
- const ASSET_MAX_BYTES = 256 * 1024 * 1024
65
-
66
- const SIGSTORE_IDENTITY = {
67
- certificateIdentityRegExp:
68
- "^https://github\\.com/lpm-dev/rust-client/\\.github/workflows/release\\.yml@.+",
69
- certificateOIDCIssuer: "https://token.actions.githubusercontent.com",
70
- }
71
-
72
- export async function main() {
73
- const platform = `${process.platform}-${os.arch()}`
74
- const platformInfo = PLATFORMS[platform]
75
-
76
- if (!platformInfo) {
77
- console.log(
78
- `[lpm] No pre-built binary for ${platform}. Using JS CLI fallback.`,
79
- )
80
- return
81
- }
82
-
83
- const pkg = JSON.parse(
84
- fs.readFileSync(path.join(pkgDir, "package.json"), "utf-8"),
85
- )
86
-
87
- let binaryPath = tryOptionalDependency(platformInfo)
88
-
89
- if (!binaryPath) {
90
- const githubBinaryName = GITHUB_BINARY_NAMES[platform]
91
- if (githubBinaryName) {
92
- binaryPath = await tryGitHubDownload(pkg.version, githubBinaryName)
93
- }
94
- }
95
-
96
- if (!binaryPath) {
97
- console.log("[lpm] Binary not available. Using JS CLI fallback.")
98
- return
99
- }
100
-
101
- if (process.platform !== "win32") {
102
- tryHardLink(binaryPath)
103
- } else {
104
- tryCopyBinary(binaryPath)
105
- }
106
- }
107
-
108
- function tryOptionalDependency(platformInfo) {
109
- try {
110
- const pkgJsonPath = require.resolve(`${platformInfo.pkg}/package.json`)
111
- const platformPkgDir = path.dirname(pkgJsonPath)
112
- const binaryPath = path.join(platformPkgDir, platformInfo.binary)
113
-
114
- if (fs.existsSync(binaryPath)) {
115
- try {
116
- execSync(`"${binaryPath}" --version`, {
117
- stdio: "ignore",
118
- timeout: 5000,
119
- })
120
- console.log(`[lpm] Using native binary from ${platformInfo.pkg}`)
121
- return binaryPath
122
- } catch {
123
- console.log(
124
- `[lpm] Binary from ${platformInfo.pkg} exists but not executable.`,
125
- )
126
- }
127
- }
128
- } catch {
129
- // Package not installed (--no-optional, or resolution failed).
130
- }
131
-
132
- return null
133
- }
134
-
135
- /**
136
- * Tier 2: download binary from GitHub Releases with end-to-end integrity.
137
- *
138
- * Returns the path to the verified, executable binary on success, or null
139
- * on any failure (manifest 404, SHA mismatch, Sigstore rejection, network
140
- * error). `null` falls through to the JS CLI fallback in `main` — Tier 2
141
- * never silently installs an unverified binary.
142
- *
143
- * Exported for testability — `vi.mock` of the network calls drives this
144
- * function directly.
145
- */
146
- export async function tryGitHubDownload(version, githubBinaryName, deps = {}) {
147
- const downloadFn = deps.download || download
148
- const downloadToBufferFn = deps.downloadToBuffer || downloadToBuffer
149
- const sha256FileFn = deps.sha256File || sha256File
150
- const verifyFn = deps.tryVerifyManifestSignature || tryVerifyManifestSignature
151
- const destDir = deps.destDir || binDir
152
- const insecure = process.env.LPM_INSTALL_INSECURE === "1"
153
-
154
- const manifestUrl = `${GITHUB_RELEASES}/v${version}/SHA256SUMS.txt`
155
- const bundleUrl = `${GITHUB_RELEASES}/v${version}/SHA256SUMS.txt.sigstore`
156
- const assetUrl = `${GITHUB_RELEASES}/v${version}/${githubBinaryName}`
157
-
158
- let manifestBody = null
159
- let bundleBody = null
160
-
161
- if (insecure) {
162
- console.log(
163
- "[lpm] WARN: LPM_INSTALL_INSECURE=1 — skipping ALL integrity verification on Tier-2 download",
164
- )
165
- } else {
166
- try {
167
- manifestBody = await downloadToBufferFn(manifestUrl, MANIFEST_MAX_BYTES)
168
- } catch (err) {
169
- if (err && err.statusCode === 404) {
170
- console.log(
171
- `[lpm] ERROR: release v${version} does not ship SHA256SUMS.txt — this release predates LPM's signed-install gate.`,
172
- )
173
- console.log(
174
- `[lpm] Install manually from https://github.com/lpm-dev/rust-client/releases/v${version}`,
175
- )
176
- console.log(
177
- "[lpm] Or set LPM_INSTALL_INSECURE=1 to skip integrity verification (NOT recommended).",
178
- )
179
- } else {
180
- console.log(
181
- `[lpm] ERROR: could not fetch signed manifest: ${err.message}`,
182
- )
183
- }
184
- return null
185
- }
186
- try {
187
- bundleBody = await downloadToBufferFn(bundleUrl, BUNDLE_MAX_BYTES)
188
- } catch {
189
- // Bundle 404 / fetch failure is not an attack signal by itself —
190
- // the SHA-256 gate against the manifest still runs. cosign /
191
- // sigstore verification is opportunistic on this channel.
192
- bundleBody = null
193
- }
194
-
195
- if (bundleBody) {
196
- const verdict = await verifyFn(manifestBody, bundleBody)
197
- if (verdict === "verified") {
198
- console.log(
199
- "[lpm] Verified Sigstore signature on manifest (identity-pinned to release.yml)",
200
- )
201
- } else if (verdict === "rejected") {
202
- // Verifier ran and refused — strong attack signal.
203
- console.log(
204
- "[lpm] ERROR: Sigstore verifier rejected the manifest signature.",
205
- )
206
- console.log(
207
- "[lpm] Refusing to install. See https://github.com/lpm-dev/rust-client/issues",
208
- )
209
- return null
210
- } else {
211
- // Optional `sigstore` package not resolvable — degrade to
212
- // SHA-only with a loud WARN so the user knows what to
213
- // install for the stronger posture.
214
- console.log(
215
- "[lpm] WARN: optional 'sigstore' package not available; falling back to SHA-256 against the signed manifest only.",
216
- )
217
- console.log(
218
- "[lpm] For Sigstore verification, run `npm install --include=optional` or unblock the sigstore package.",
219
- )
220
- }
221
- }
222
- }
223
-
224
- const destName = process.platform === "win32" ? "lpm-bin.exe" : "lpm-bin"
225
- const destPath = path.join(destDir, destName)
226
- const tmpPath = `${destPath}.tmp.${process.pid}`
227
-
228
- try {
229
- console.log("[lpm] Downloading native binary from GitHub Releases...")
230
- await downloadFn(assetUrl, tmpPath, 0, ASSET_MAX_BYTES)
231
- } catch (err) {
232
- console.log(`[lpm] Download failed: ${err.message}. Using JS CLI fallback.`)
233
- safeUnlink(tmpPath)
234
- return null
235
- }
236
-
237
- if (manifestBody) {
238
- const actualSha = await sha256FileFn(tmpPath)
239
- const expectedSha = parseManifestEntry(
240
- manifestBody.toString("utf-8"),
241
- githubBinaryName,
242
- )
243
- if (!expectedSha) {
244
- console.log(
245
- `[lpm] ERROR: manifest does not enumerate ${githubBinaryName}; release-pipeline bug.`,
246
- )
247
- safeUnlink(tmpPath)
248
- return null
249
- }
250
- if (actualSha !== expectedSha) {
251
- console.log(`[lpm] ERROR: SHA-256 mismatch for ${githubBinaryName}`)
252
- console.log(
253
- `[lpm] expected: ${expectedSha} (from signed manifest)`,
254
- )
255
- console.log(`[lpm] actual: ${actualSha}`)
256
- console.log("[lpm] Refusing to install — strong tampering signal.")
257
- safeUnlink(tmpPath)
258
- return null
259
- }
260
- console.log(`[lpm] Verified SHA-256: ${actualSha}`)
261
- }
262
-
263
- try {
264
- fs.renameSync(tmpPath, destPath)
265
- fs.chmodSync(destPath, 0o755)
266
- } catch (err) {
267
- console.log(`[lpm] Could not install verified binary: ${err.message}`)
268
- safeUnlink(tmpPath)
269
- return null
270
- }
271
-
272
- console.log("[lpm] Native binary installed from GitHub Releases.")
273
- return destPath
274
- }
275
-
276
- function tryHardLink(binaryPath) {
277
- const shimPath = path.join(binDir, "lpm.js")
278
- const backupPath = path.join(binDir, "lpm.js.bak")
279
-
280
- try {
281
- if (fs.existsSync(shimPath) && !fs.existsSync(backupPath)) {
282
- fs.copyFileSync(shimPath, backupPath)
283
- }
284
-
285
- if (fs.existsSync(shimPath)) {
286
- fs.unlinkSync(shimPath)
287
- }
288
- fs.linkSync(binaryPath, shimPath)
289
-
290
- console.log("[lpm] Hard-linked native binary (zero Node.js overhead)")
291
- } catch (err) {
292
- if (fs.existsSync(backupPath) && !fs.existsSync(shimPath)) {
293
- fs.copyFileSync(backupPath, shimPath)
294
- }
295
- console.log(`[lpm] Hard-link failed (${err.code}). Using JS wrapper.`)
296
- }
297
- }
298
-
299
- function tryCopyBinary(binaryPath) {
300
- const destPath = path.join(binDir, "lpm-bin.exe")
301
- try {
302
- fs.copyFileSync(binaryPath, destPath)
303
- console.log("[lpm] Copied native binary to bin/")
304
- } catch (err) {
305
- console.log(`[lpm] Copy failed: ${err.message}. Using JS wrapper.`)
306
- }
307
- }
308
-
309
- /**
310
- * Download a file from a URL, following redirects (GitHub → S3).
311
- *
312
- * HTTPS-only end-to-end: the entry check rejects any non-`https://`
313
- * URL, and redirect Locations are resolved against the current URL
314
- * before re-entering the function — so an attacker-controlled mirror
315
- * cannot downgrade transport mid-chain via `Location: http://…`.
316
- *
317
- * Streams to disk with a per-response byte cap so a compromised mirror
318
- * cannot exhaust disk by serving an unbounded body. The 5-redirect cap
319
- * is preserved from the pre-integrity-gate shape.
320
- *
321
- * Exported for testability.
322
- */
323
- export function download(url, dest, redirects = 0, maxBytes = ASSET_MAX_BYTES) {
324
- if (redirects > 5) return Promise.reject(new Error("Too many redirects"))
325
- if (!url.startsWith("https://")) {
326
- return Promise.reject(
327
- new Error(
328
- `refusing non-HTTPS transport: ${url} (Tier-2 download requires HTTPS end-to-end)`,
329
- ),
330
- )
331
- }
332
-
333
- return new Promise((resolve, reject) => {
334
- const req = https.get(url, res => {
335
- if (
336
- res.statusCode >= 300 &&
337
- res.statusCode < 400 &&
338
- res.headers.location
339
- ) {
340
- res.resume()
341
- let nextUrl
342
- try {
343
- nextUrl = new URL(res.headers.location, url).href
344
- } catch {
345
- reject(
346
- new Error(`invalid redirect Location: ${res.headers.location}`),
347
- )
348
- return
349
- }
350
- resolve(download(nextUrl, dest, redirects + 1, maxBytes))
351
- return
352
- }
353
-
354
- if (res.statusCode !== 200) {
355
- const err = new Error(`HTTP ${res.statusCode}`)
356
- err.statusCode = res.statusCode
357
- res.resume()
358
- reject(err)
359
- return
360
- }
361
-
362
- const advertised = Number(res.headers["content-length"])
363
- if (Number.isFinite(advertised) && advertised > maxBytes) {
364
- res.resume()
365
- reject(
366
- new Error(
367
- `response advertises ${advertised} bytes; cap of ${maxBytes} would be exceeded`,
368
- ),
369
- )
370
- return
371
- }
372
-
373
- let received = 0
374
- const out = fs.createWriteStream(dest)
375
- res.on("data", chunk => {
376
- received += chunk.length
377
- if (received > maxBytes) {
378
- out.destroy()
379
- res.destroy()
380
- safeUnlink(dest)
381
- reject(
382
- new Error(
383
- `response body exceeded cap of ${maxBytes} bytes mid-stream`,
384
- ),
385
- )
386
- }
387
- })
388
- res.pipe(out)
389
- out.on("finish", () => {
390
- out.close()
391
- resolve()
392
- })
393
- out.on("error", reject)
394
- })
395
- req.on("error", reject)
396
- })
397
- }
398
-
399
- /**
400
- * Buffered variant of [`download`] — fetches a bounded body into memory
401
- * and returns it as a Buffer. Used for the manifest + Sigstore bundle,
402
- * both of which are small and must be inspected as a whole before any
403
- * other byte is honored.
404
- *
405
- * HTTPS-only: identical transport-hardening posture as [`download`].
406
- *
407
- * Exported for testability.
408
- */
409
- export function downloadToBuffer(url, maxBytes, redirects = 0) {
410
- if (redirects > 5) return Promise.reject(new Error("Too many redirects"))
411
- if (!url.startsWith("https://")) {
412
- return Promise.reject(
413
- new Error(
414
- `refusing non-HTTPS transport: ${url} (Tier-2 download requires HTTPS end-to-end)`,
415
- ),
416
- )
417
- }
418
-
419
- return new Promise((resolve, reject) => {
420
- const req = https.get(url, res => {
421
- if (
422
- res.statusCode >= 300 &&
423
- res.statusCode < 400 &&
424
- res.headers.location
425
- ) {
426
- res.resume()
427
- let nextUrl
428
- try {
429
- nextUrl = new URL(res.headers.location, url).href
430
- } catch {
431
- reject(
432
- new Error(`invalid redirect Location: ${res.headers.location}`),
433
- )
434
- return
435
- }
436
- resolve(downloadToBuffer(nextUrl, maxBytes, redirects + 1))
437
- return
438
- }
439
-
440
- if (res.statusCode !== 200) {
441
- const err = new Error(`HTTP ${res.statusCode}`)
442
- err.statusCode = res.statusCode
443
- res.resume()
444
- reject(err)
445
- return
446
- }
447
-
448
- const advertised = Number(res.headers["content-length"])
449
- if (Number.isFinite(advertised) && advertised > maxBytes) {
450
- res.resume()
451
- reject(
452
- new Error(
453
- `response advertises ${advertised} bytes; cap of ${maxBytes} would be exceeded`,
454
- ),
455
- )
456
- return
457
- }
458
-
459
- const chunks = []
460
- let received = 0
461
- res.on("data", chunk => {
462
- received += chunk.length
463
- if (received > maxBytes) {
464
- res.destroy()
465
- reject(
466
- new Error(
467
- `response body exceeded cap of ${maxBytes} bytes mid-stream`,
468
- ),
469
- )
470
- return
471
- }
472
- chunks.push(chunk)
473
- })
474
- res.on("end", () => resolve(Buffer.concat(chunks)))
475
- res.on("error", reject)
476
- })
477
- req.on("error", reject)
478
- })
479
- }
480
-
481
- /**
482
- * Compute the SHA-256 of a file as a lowercase hex string, streaming
483
- * the file so we never buffer the whole asset in memory.
484
- *
485
- * Exported for testability.
486
- */
487
- export function sha256File(filePath) {
488
- return new Promise((resolve, reject) => {
489
- const hash = crypto.createHash("sha256")
490
- const stream = fs.createReadStream(filePath)
491
- stream.on("data", chunk => hash.update(chunk))
492
- stream.on("end", () => resolve(hash.digest("hex")))
493
- stream.on("error", reject)
494
- })
495
- }
496
-
497
- /**
498
- * Look up the SHA-256 for `filename` in a parsed SHA256SUMS.txt body.
499
- *
500
- * Format: `<64-hex> <filename>\n` per line, two ASCII spaces between
501
- * digest and name (GNU coreutils default; macOS shasum -a 256 matches).
502
- * Malformed lines return null — a malformed manifest is an integrity
503
- * failure, not a silent miss; callers should refuse to install.
504
- *
505
- * Exported for testability.
506
- */
507
- export function parseManifestEntry(manifestText, filename) {
508
- const lines = manifestText.split("\n")
509
- for (const raw of lines) {
510
- const line = raw.replace(/\r$/, "")
511
- if (line.length === 0) continue
512
- const idx = line.indexOf(" ")
513
- if (idx !== 64) return null
514
- const digest = line.slice(0, 64).toLowerCase()
515
- if (!/^[0-9a-f]{64}$/.test(digest)) return null
516
- const name = line.slice(idx + 2).trim()
517
- if (name.length === 0) return null
518
- if (name === filename) return digest
519
- }
520
- return null
521
- }
522
-
523
- function safeUnlink(p) {
524
- try {
525
- if (fs.existsSync(p)) fs.unlinkSync(p)
526
- } catch {
527
- // Best-effort.
528
- }
529
- }
530
-
531
- /**
532
- * Verify a Sigstore bundle against a manifest body using the optional
533
- * `sigstore` package. Returns:
534
- *
535
- * "verified" — bundle verified under the lpm-dev/rust-client
536
- * release.yml identity pin.
537
- * "rejected" — sigstore package present and refused the bundle.
538
- * "unavailable" — sigstore package not resolvable (--no-optional,
539
- * firewalled, install failure). Caller degrades to
540
- * SHA-only.
541
- *
542
- * Exported for testability.
543
- */
544
- export async function tryVerifyManifestSignature(manifestBody, bundleBody) {
545
- let sigstore
546
- try {
547
- sigstore = await import("sigstore")
548
- } catch {
549
- return "unavailable"
550
- }
551
- try {
552
- const bundle = JSON.parse(bundleBody.toString("utf-8"))
553
- await sigstore.verify(bundle, manifestBody, SIGSTORE_IDENTITY)
554
- return "verified"
555
- } catch {
556
- return "rejected"
557
- }
558
- }
559
-
560
- // Only run `main` when this module is executed as a script (the
561
- // postinstall hook). Test files that `import { tryGitHubDownload, … }`
562
- // from this module should not trigger a real install attempt during
563
- // suite setup.
564
- const invokedAsScript =
565
- process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href
566
-
567
- if (invokedAsScript) {
568
- main().catch(() => {
569
- // Silently fail — JS CLI fallback will handle all commands.
570
- })
571
- }