@lpm-registry/cli 0.58.0 → 0.59.1
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 +25 -44
- package/bin/lpm.js +2 -354
- package/bin/lpx.js +4 -0
- package/bin/native.js +154 -0
- package/package.json +42 -82
- package/scripts/install-binary.js +219 -550
- package/CHANGELOG.md +0 -197
- package/LICENSE +0 -15
- package/index.d.ts +0 -131
- package/index.js +0 -31
- package/lib/api.js +0 -398
- package/lib/commands/add.js +0 -1217
- package/lib/commands/audit.js +0 -283
- package/lib/commands/cache.js +0 -209
- package/lib/commands/check-name.js +0 -112
- package/lib/commands/config.js +0 -186
- package/lib/commands/doctor.js +0 -140
- package/lib/commands/info.js +0 -215
- package/lib/commands/init.js +0 -146
- package/lib/commands/install.js +0 -264
- package/lib/commands/login.js +0 -595
- package/lib/commands/logout.js +0 -94
- package/lib/commands/marketplace-compare.js +0 -164
- package/lib/commands/marketplace-earnings.js +0 -89
- package/lib/commands/mcp-setup.js +0 -363
- package/lib/commands/npmrc.js +0 -178
- package/lib/commands/open.js +0 -82
- package/lib/commands/outdated.js +0 -291
- package/lib/commands/pool-stats.js +0 -100
- package/lib/commands/publish.js +0 -949
- package/lib/commands/quality.js +0 -211
- package/lib/commands/remove.js +0 -82
- package/lib/commands/run.js +0 -14
- package/lib/commands/search.js +0 -143
- package/lib/commands/setup.js +0 -221
- package/lib/commands/skills.js +0 -898
- package/lib/commands/token-rotate.js +0 -25
- package/lib/commands/uninstall.js +0 -107
- package/lib/commands/whoami.js +0 -129
- package/lib/config.js +0 -328
- package/lib/constants.js +0 -190
- package/lib/ecosystem.js +0 -501
- package/lib/editor-skills.js +0 -321
- package/lib/editors.js +0 -215
- package/lib/import-rewriter.js +0 -364
- package/lib/install-targets/mcp-server.js +0 -245
- package/lib/install-targets/vscode-extension.js +0 -178
- package/lib/install-targets.js +0 -82
- package/lib/integrity.js +0 -179
- package/lib/lpm-config-prompts.js +0 -102
- package/lib/lpm-config.js +0 -408
- package/lib/oidc.js +0 -166
- package/lib/project-utils.js +0 -152
- package/lib/quality/checks.js +0 -687
- package/lib/quality/display.js +0 -155
- package/lib/quality/score.js +0 -118
- package/lib/quality/swift-checks.js +0 -447
- package/lib/safe-path.js +0 -180
- package/lib/secure-store.js +0 -321
- package/lib/swift-project.js +0 -637
- package/lib/ui.js +0 -40
- package/scripts/__tests__/install-binary.test.js +0 -346
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { request } from "../api.js"
|
|
2
|
-
import { setToken } from "../config.js"
|
|
3
|
-
import { createSpinner, printHeader } from "../ui.js"
|
|
4
|
-
|
|
5
|
-
export async function rotateToken() {
|
|
6
|
-
printHeader()
|
|
7
|
-
const spinner = createSpinner("Rotating token...").start()
|
|
8
|
-
try {
|
|
9
|
-
const response = await request("/-/token/rotate", {
|
|
10
|
-
method: "POST",
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
if (!response.ok) {
|
|
14
|
-
throw new Error(`Request failed with status ${response.status}`)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const data = await response.json()
|
|
18
|
-
const newToken = data.token
|
|
19
|
-
|
|
20
|
-
await setToken(newToken)
|
|
21
|
-
spinner.succeed("Token rotated successfully! Local config updated.")
|
|
22
|
-
} catch (error) {
|
|
23
|
-
spinner.fail(`Error: ${error.message}`)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process"
|
|
2
|
-
import fs from "node:fs"
|
|
3
|
-
import path from "node:path"
|
|
4
|
-
import { getPackageManager, getRegistryUrl, getToken } from "../config.js"
|
|
5
|
-
import { createSpinner, log, printHeader } from "../ui.js"
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Uninstall LPM packages.
|
|
9
|
-
* Creates a temporary .npmrc with auth, runs the package manager's uninstall, and cleans up.
|
|
10
|
-
*
|
|
11
|
-
* @param {string[]} packages - Package names to uninstall
|
|
12
|
-
* @param {object} [options]
|
|
13
|
-
* @param {string} [options.pm] - Package manager override (npm, pnpm, yarn, bun)
|
|
14
|
-
*/
|
|
15
|
-
export async function uninstall(packages, options) {
|
|
16
|
-
printHeader()
|
|
17
|
-
|
|
18
|
-
if (!packages || packages.length === 0) {
|
|
19
|
-
log.error("Please specify at least one package to uninstall.")
|
|
20
|
-
log.info("Usage: lpm uninstall @lpm.dev/owner.package-name")
|
|
21
|
-
process.exit(1)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const token = await getToken()
|
|
25
|
-
if (!token) {
|
|
26
|
-
log.error(
|
|
27
|
-
'You must be logged in to uninstall packages. Run "lpm login" first.',
|
|
28
|
-
)
|
|
29
|
-
process.exit(1)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const pm = getPackageManager(options?.pm)
|
|
33
|
-
const spinner = createSpinner(
|
|
34
|
-
`Preparing to uninstall ${packages.join(", ")}...`,
|
|
35
|
-
).start()
|
|
36
|
-
|
|
37
|
-
const baseRegistryUrl = getRegistryUrl()
|
|
38
|
-
const registryUrl = baseRegistryUrl.endsWith("/api/registry")
|
|
39
|
-
? baseRegistryUrl
|
|
40
|
-
: `${baseRegistryUrl}/api/registry`
|
|
41
|
-
const registryHost = registryUrl.replace(/^https?:/, "")
|
|
42
|
-
|
|
43
|
-
const npmrcContent = `${registryHost}/:_authToken=${token}
|
|
44
|
-
@lpm.dev:registry=${registryUrl}
|
|
45
|
-
`
|
|
46
|
-
|
|
47
|
-
const projectNpmrcPath = path.resolve(process.cwd(), ".npmrc")
|
|
48
|
-
let existingNpmrc = null
|
|
49
|
-
let tempNpmrcPath = null
|
|
50
|
-
|
|
51
|
-
if (pm === "npm") {
|
|
52
|
-
tempNpmrcPath = path.resolve(process.cwd(), `.npmrc.lpm-${Date.now()}`)
|
|
53
|
-
fs.writeFileSync(tempNpmrcPath, npmrcContent)
|
|
54
|
-
} else {
|
|
55
|
-
if (fs.existsSync(projectNpmrcPath)) {
|
|
56
|
-
existingNpmrc = fs.readFileSync(projectNpmrcPath, "utf8")
|
|
57
|
-
}
|
|
58
|
-
fs.writeFileSync(projectNpmrcPath, npmrcContent)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
spinner.succeed(`Configuration generated. Running ${pm} uninstall...`)
|
|
62
|
-
|
|
63
|
-
const pmArgs =
|
|
64
|
-
pm === "npm"
|
|
65
|
-
? ["uninstall", ...packages, "--userconfig", tempNpmrcPath]
|
|
66
|
-
: ["uninstall", ...packages]
|
|
67
|
-
|
|
68
|
-
const child = spawn(pm, pmArgs, {
|
|
69
|
-
stdio: "inherit",
|
|
70
|
-
env: { ...process.env, LPM_TOKEN: token },
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
const cleanup = () => {
|
|
74
|
-
if (pm === "npm") {
|
|
75
|
-
if (tempNpmrcPath && fs.existsSync(tempNpmrcPath)) {
|
|
76
|
-
fs.unlinkSync(tempNpmrcPath)
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
if (existingNpmrc !== null) {
|
|
80
|
-
fs.writeFileSync(projectNpmrcPath, existingNpmrc)
|
|
81
|
-
} else if (fs.existsSync(projectNpmrcPath)) {
|
|
82
|
-
fs.unlinkSync(projectNpmrcPath)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
child.on("close", code => {
|
|
88
|
-
cleanup()
|
|
89
|
-
if (code !== 0) {
|
|
90
|
-
log.error(`${pm} uninstall failed with code ${code}`)
|
|
91
|
-
process.exit(code)
|
|
92
|
-
} else {
|
|
93
|
-
log.success("Packages uninstalled successfully.")
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
child.on("error", err => {
|
|
98
|
-
log.error(`Failed to start ${pm}: ${err.message}`)
|
|
99
|
-
cleanup()
|
|
100
|
-
process.exit(1)
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
process.on("SIGINT", () => {
|
|
104
|
-
cleanup()
|
|
105
|
-
process.exit()
|
|
106
|
-
})
|
|
107
|
-
}
|
package/lib/commands/whoami.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { request } from "../api.js"
|
|
2
|
-
import { getRegistryUrl } from "../config.js"
|
|
3
|
-
import { log, printHeader } from "../ui.js"
|
|
4
|
-
|
|
5
|
-
export async function whoami(options = {}) {
|
|
6
|
-
if (!options.json) printHeader()
|
|
7
|
-
try {
|
|
8
|
-
const response = await request("/-/whoami")
|
|
9
|
-
|
|
10
|
-
if (!response.ok) {
|
|
11
|
-
throw new Error(`Request failed with status ${response.status}`)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const data = await response.json()
|
|
15
|
-
|
|
16
|
-
if (options.json) {
|
|
17
|
-
const result = {
|
|
18
|
-
username: data.username,
|
|
19
|
-
profileUsername: data.profile_username || null,
|
|
20
|
-
email: data.email || null,
|
|
21
|
-
plan: data.plan_tier || null,
|
|
22
|
-
hasPoolAccess: data.has_pool_access || false,
|
|
23
|
-
usage: data.usage || null,
|
|
24
|
-
limits: data.limits || null,
|
|
25
|
-
orgs: (data.organizations || []).map(org => ({
|
|
26
|
-
slug: org.slug,
|
|
27
|
-
name: org.name,
|
|
28
|
-
role: org.role,
|
|
29
|
-
})),
|
|
30
|
-
}
|
|
31
|
-
console.log(JSON.stringify(result, null, 2))
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
log.success(`Logged in as: ${data.username}`)
|
|
36
|
-
|
|
37
|
-
if (data.plan_tier) {
|
|
38
|
-
console.log("") // Spacer
|
|
39
|
-
log.info(`Plan: ${data.plan_tier.toUpperCase()}`)
|
|
40
|
-
|
|
41
|
-
// Pool subscription status
|
|
42
|
-
if (data.has_pool_access) {
|
|
43
|
-
log.success("Pool: Active")
|
|
44
|
-
} else {
|
|
45
|
-
log.info("Pool: Not subscribed")
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (data.usage) {
|
|
49
|
-
const storageMB = (data.usage.storage_bytes / (1024 * 1024)).toFixed(2)
|
|
50
|
-
const limits = data.limits || {}
|
|
51
|
-
|
|
52
|
-
// Storage Check - backend returns storageBytes (not storage_gb)
|
|
53
|
-
if (limits.storageBytes) {
|
|
54
|
-
const limitMB = (limits.storageBytes / (1024 * 1024)).toFixed(0)
|
|
55
|
-
const storageMsg = `Storage Used: ${storageMB}MB / ${limitMB}MB`
|
|
56
|
-
if (data.usage.storage_bytes > limits.storageBytes) {
|
|
57
|
-
log.error(`${storageMsg} (OVER LIMIT)`)
|
|
58
|
-
} else {
|
|
59
|
-
log.info(storageMsg)
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
log.info(`Storage Used: ${storageMB}MB`)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Package Count Check - backend returns privatePackages (not private_packages)
|
|
66
|
-
if (limits.privatePackages !== undefined) {
|
|
67
|
-
if (
|
|
68
|
-
limits.privatePackages === Number.POSITIVE_INFINITY ||
|
|
69
|
-
limits.privatePackages === null
|
|
70
|
-
) {
|
|
71
|
-
log.info(
|
|
72
|
-
`Private Packages: ${data.usage.private_packages} (Unlimited)`,
|
|
73
|
-
)
|
|
74
|
-
} else {
|
|
75
|
-
const pkgMsg = `Private Packages: ${data.usage.private_packages} / ${limits.privatePackages}`
|
|
76
|
-
if (data.usage.private_packages > limits.privatePackages) {
|
|
77
|
-
log.error(`${pkgMsg} (OVER LIMIT)`)
|
|
78
|
-
} else {
|
|
79
|
-
log.info(pkgMsg)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
} else {
|
|
83
|
-
log.info(`Private Packages: ${data.usage.private_packages}`)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Over limit warning
|
|
87
|
-
const overStorage =
|
|
88
|
-
limits.storageBytes && data.usage.storage_bytes > limits.storageBytes
|
|
89
|
-
const overPackages =
|
|
90
|
-
limits.privatePackages &&
|
|
91
|
-
limits.privatePackages !== Number.POSITIVE_INFINITY &&
|
|
92
|
-
limits.privatePackages !== null &&
|
|
93
|
-
data.usage.private_packages > limits.privatePackages
|
|
94
|
-
|
|
95
|
-
if (overStorage || overPackages) {
|
|
96
|
-
const registryUrl = getRegistryUrl()
|
|
97
|
-
console.log("")
|
|
98
|
-
log.warn("Your account is over its plan limits.")
|
|
99
|
-
log.warn("Write access (publishing, inviting members) is restricted.")
|
|
100
|
-
log.warn(
|
|
101
|
-
`Upgrade your plan: ${registryUrl}/dashboard/settings/billing`,
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Display available scopes for publishing
|
|
108
|
-
const registryUrl = getRegistryUrl()
|
|
109
|
-
console.log("")
|
|
110
|
-
log.info("Available Scopes:")
|
|
111
|
-
|
|
112
|
-
// Personal scope
|
|
113
|
-
if (data.profile_username) {
|
|
114
|
-
log.info(` Personal: @lpm.dev/${data.profile_username}.*`)
|
|
115
|
-
} else {
|
|
116
|
-
log.warn(` Personal: Not set (${registryUrl}/dashboard/settings)`)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Organization scopes
|
|
120
|
-
if (data.organizations?.length > 0) {
|
|
121
|
-
log.info(" Organizations:")
|
|
122
|
-
for (const org of data.organizations) {
|
|
123
|
-
log.info(` @lpm.dev/${org.slug}.* (${org.role})`)
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
} catch (error) {
|
|
127
|
-
log.error(`Error: ${error.message}`)
|
|
128
|
-
}
|
|
129
|
-
}
|
package/lib/config.js
DELETED
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Configuration Management
|
|
3
|
-
*
|
|
4
|
-
* Handles both secure credential storage and general configuration.
|
|
5
|
-
* Token storage migrated to secure-store for keychain integration.
|
|
6
|
-
*
|
|
7
|
-
* @module cli/lib/config
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import Conf from "conf"
|
|
11
|
-
import {
|
|
12
|
-
DEFAULT_REGISTRY_URL,
|
|
13
|
-
MAX_RETRIES,
|
|
14
|
-
REQUEST_TIMEOUT_MS,
|
|
15
|
-
} from "./constants.js"
|
|
16
|
-
import {
|
|
17
|
-
isUsingKeychain,
|
|
18
|
-
clearToken as secureClearToken,
|
|
19
|
-
getToken as secureGetToken,
|
|
20
|
-
setToken as secureSetToken,
|
|
21
|
-
} from "./secure-store.js"
|
|
22
|
-
|
|
23
|
-
// ============================================================================
|
|
24
|
-
// General Configuration Store (non-sensitive data)
|
|
25
|
-
// ============================================================================
|
|
26
|
-
|
|
27
|
-
const config = new Conf({
|
|
28
|
-
projectName: "lpm-cli",
|
|
29
|
-
defaults: {
|
|
30
|
-
registryUrl: DEFAULT_REGISTRY_URL,
|
|
31
|
-
timeout: REQUEST_TIMEOUT_MS,
|
|
32
|
-
retries: MAX_RETRIES,
|
|
33
|
-
},
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
// ============================================================================
|
|
37
|
-
// Token Management (Secure Storage)
|
|
38
|
-
// ============================================================================
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Transient token — set for the duration of an OIDC-authenticated publish.
|
|
42
|
-
* Takes priority over all other token sources so the short-lived OIDC token
|
|
43
|
-
* is used without writing to keychain or mutating process.env.
|
|
44
|
-
* @type {string | null}
|
|
45
|
-
*/
|
|
46
|
-
let _transientToken = null
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Set a transient token that overrides all other token sources.
|
|
50
|
-
* Used by OIDC token exchange to inject a short-lived publish token.
|
|
51
|
-
* @param {string} token
|
|
52
|
-
*/
|
|
53
|
-
export function setTransientToken(token) {
|
|
54
|
-
_transientToken = token
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Clear the transient token after use.
|
|
59
|
-
*/
|
|
60
|
-
export function clearTransientToken() {
|
|
61
|
-
_transientToken = null
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Get the stored auth token from secure storage.
|
|
66
|
-
* Priority: transient (OIDC) > LPM_TOKEN env var > secure storage > legacy storage
|
|
67
|
-
* Tokens are scoped per registry URL so dev and production don't collide.
|
|
68
|
-
* @returns {Promise<string | null>}
|
|
69
|
-
*/
|
|
70
|
-
export async function getToken() {
|
|
71
|
-
// OIDC-exchanged short-lived token takes highest priority
|
|
72
|
-
if (_transientToken) return _transientToken
|
|
73
|
-
|
|
74
|
-
// Check environment variable first (useful for CI/CD and testing)
|
|
75
|
-
const envToken = process.env.LPM_TOKEN
|
|
76
|
-
if (envToken) return envToken
|
|
77
|
-
|
|
78
|
-
const registryUrl = getRegistryUrl()
|
|
79
|
-
|
|
80
|
-
// Try secure store (registry-scoped)
|
|
81
|
-
const secureToken = await secureGetToken(registryUrl)
|
|
82
|
-
if (secureToken) return secureToken
|
|
83
|
-
|
|
84
|
-
// Migration: check if token exists in old Conf storage
|
|
85
|
-
const legacyToken = config.get("token")
|
|
86
|
-
if (legacyToken) {
|
|
87
|
-
// Migrate to secure storage (scoped to current registry)
|
|
88
|
-
await secureSetToken(legacyToken, registryUrl)
|
|
89
|
-
config.delete("token")
|
|
90
|
-
return legacyToken
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return null
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Set the auth token in secure storage (scoped to current registry).
|
|
98
|
-
* @param {string | null} token
|
|
99
|
-
* @returns {Promise<void>}
|
|
100
|
-
*/
|
|
101
|
-
export async function setToken(token) {
|
|
102
|
-
const registryUrl = getRegistryUrl()
|
|
103
|
-
if (token === null) {
|
|
104
|
-
await secureClearToken(registryUrl)
|
|
105
|
-
} else {
|
|
106
|
-
await secureSetToken(token, registryUrl)
|
|
107
|
-
}
|
|
108
|
-
// Ensure legacy token is cleared
|
|
109
|
-
config.delete("token")
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Clear the auth token from secure storage (scoped to current registry).
|
|
114
|
-
* @returns {Promise<void>}
|
|
115
|
-
*/
|
|
116
|
-
export async function clearToken() {
|
|
117
|
-
await secureClearToken(getRegistryUrl())
|
|
118
|
-
config.delete("token")
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Store a refresh token in secure storage (Feature 44 Part B).
|
|
123
|
-
* Uses the same secure-store as auth tokens with a "refresh:" prefix.
|
|
124
|
-
* @param {string} refreshToken
|
|
125
|
-
* @returns {Promise<void>}
|
|
126
|
-
*/
|
|
127
|
-
export async function setRefreshToken(refreshToken) {
|
|
128
|
-
const registryUrl = getRegistryUrl()
|
|
129
|
-
await secureSetToken(refreshToken, `refresh:${registryUrl}`)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Get the stored refresh token from secure storage.
|
|
134
|
-
* @returns {Promise<string|null>}
|
|
135
|
-
*/
|
|
136
|
-
export async function getRefreshToken() {
|
|
137
|
-
const registryUrl = getRegistryUrl()
|
|
138
|
-
return secureGetToken(`refresh:${registryUrl}`)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ============================================================================
|
|
142
|
-
// Registry URL Configuration
|
|
143
|
-
// ============================================================================
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Get the registry URL.
|
|
147
|
-
* Priority: LPM_REGISTRY_URL env var > stored config > default
|
|
148
|
-
* @returns {string}
|
|
149
|
-
*/
|
|
150
|
-
export function getRegistryUrl() {
|
|
151
|
-
// Check environment variables first (useful for CI/CD and testing)
|
|
152
|
-
const envUrl = process.env.LPM_REGISTRY_URL
|
|
153
|
-
if (envUrl) return envUrl
|
|
154
|
-
|
|
155
|
-
return config.get("registryUrl", DEFAULT_REGISTRY_URL)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Set the registry URL.
|
|
160
|
-
* @param {string} url
|
|
161
|
-
*/
|
|
162
|
-
export function setRegistryUrl(url) {
|
|
163
|
-
config.set("registryUrl", url)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// ============================================================================
|
|
167
|
-
// Timeout Configuration
|
|
168
|
-
// ============================================================================
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Get the request timeout in milliseconds.
|
|
172
|
-
* @returns {number}
|
|
173
|
-
*/
|
|
174
|
-
export function getTimeout() {
|
|
175
|
-
return config.get("timeout", REQUEST_TIMEOUT_MS)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Set the request timeout in milliseconds.
|
|
180
|
-
* @param {number} ms
|
|
181
|
-
*/
|
|
182
|
-
export function setTimeout(ms) {
|
|
183
|
-
config.set("timeout", ms)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// ============================================================================
|
|
187
|
-
// Retry Configuration
|
|
188
|
-
// ============================================================================
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Get the maximum retry count.
|
|
192
|
-
* @returns {number}
|
|
193
|
-
*/
|
|
194
|
-
export function getRetries() {
|
|
195
|
-
return config.get("retries", MAX_RETRIES)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Set the maximum retry count.
|
|
200
|
-
* @param {number} count
|
|
201
|
-
*/
|
|
202
|
-
export function setRetries(count) {
|
|
203
|
-
config.set("retries", count)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// ============================================================================
|
|
207
|
-
// Package Manager Configuration
|
|
208
|
-
// ============================================================================
|
|
209
|
-
|
|
210
|
-
const VALID_PACKAGE_MANAGERS = new Set(["npm", "pnpm", "yarn", "bun"])
|
|
211
|
-
const DEFAULT_PACKAGE_MANAGER = "npm"
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Get the configured package manager for install/uninstall operations.
|
|
215
|
-
* Priority: --pm flag > LPM_PACKAGE_MANAGER env > config > "npm"
|
|
216
|
-
* @param {string} [override] - CLI flag override (--pm value)
|
|
217
|
-
* @returns {string}
|
|
218
|
-
*/
|
|
219
|
-
export function getPackageManager(override) {
|
|
220
|
-
const pm =
|
|
221
|
-
override ||
|
|
222
|
-
process.env.LPM_PACKAGE_MANAGER ||
|
|
223
|
-
config.get("packageManager", DEFAULT_PACKAGE_MANAGER)
|
|
224
|
-
|
|
225
|
-
if (!VALID_PACKAGE_MANAGERS.has(pm)) {
|
|
226
|
-
return DEFAULT_PACKAGE_MANAGER
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return pm
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// ============================================================================
|
|
233
|
-
// General Configuration Access
|
|
234
|
-
// ============================================================================
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Get all configuration values.
|
|
238
|
-
* @returns {Promise<Record<string, unknown>>}
|
|
239
|
-
*/
|
|
240
|
-
export async function getAllConfig() {
|
|
241
|
-
const usingKeychain = await isUsingKeychain()
|
|
242
|
-
const hasToken = !!(await getToken())
|
|
243
|
-
|
|
244
|
-
return {
|
|
245
|
-
registryUrl: getRegistryUrl(),
|
|
246
|
-
packageManager: getPackageManager(),
|
|
247
|
-
timeout: getTimeout(),
|
|
248
|
-
retries: getRetries(),
|
|
249
|
-
secureStorage: usingKeychain ? "keychain" : "encrypted-file",
|
|
250
|
-
authenticated: hasToken,
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Get a specific configuration value.
|
|
256
|
-
* @param {string} key
|
|
257
|
-
* @returns {unknown}
|
|
258
|
-
*/
|
|
259
|
-
export function getConfigValue(key) {
|
|
260
|
-
const configMap = {
|
|
261
|
-
registry: getRegistryUrl,
|
|
262
|
-
registryUrl: getRegistryUrl,
|
|
263
|
-
packageManager: getPackageManager,
|
|
264
|
-
timeout: getTimeout,
|
|
265
|
-
retries: getRetries,
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const getter = configMap[key]
|
|
269
|
-
if (getter) return getter()
|
|
270
|
-
return config.get(key)
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Set a specific configuration value.
|
|
275
|
-
* @param {string} key
|
|
276
|
-
* @param {unknown} value
|
|
277
|
-
* @returns {boolean} - True if set successfully
|
|
278
|
-
*/
|
|
279
|
-
export function setConfigValue(key, value) {
|
|
280
|
-
const configMap = {
|
|
281
|
-
registry: setRegistryUrl,
|
|
282
|
-
registryUrl: setRegistryUrl,
|
|
283
|
-
packageManager: val => {
|
|
284
|
-
if (VALID_PACKAGE_MANAGERS.has(val)) {
|
|
285
|
-
config.set("packageManager", val)
|
|
286
|
-
} else {
|
|
287
|
-
throw new Error(
|
|
288
|
-
`Invalid package manager "${val}". Valid options: ${[...VALID_PACKAGE_MANAGERS].join(", ")}`,
|
|
289
|
-
)
|
|
290
|
-
}
|
|
291
|
-
},
|
|
292
|
-
timeout: val => setTimeout(Number(val)),
|
|
293
|
-
retries: val => setRetries(Number(val)),
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const setter = configMap[key]
|
|
297
|
-
if (setter) {
|
|
298
|
-
setter(value)
|
|
299
|
-
return true
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Allow setting arbitrary config values
|
|
303
|
-
config.set(key, value)
|
|
304
|
-
return true
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Delete a specific configuration value.
|
|
309
|
-
* @param {string} key
|
|
310
|
-
* @returns {boolean} - True if deleted
|
|
311
|
-
*/
|
|
312
|
-
export function deleteConfigValue(key) {
|
|
313
|
-
const protectedKeys = ["registryUrl", "registry", "timeout", "retries"]
|
|
314
|
-
if (protectedKeys.includes(key)) {
|
|
315
|
-
// Reset to default instead of deleting
|
|
316
|
-
const defaults = {
|
|
317
|
-
registryUrl: DEFAULT_REGISTRY_URL,
|
|
318
|
-
registry: DEFAULT_REGISTRY_URL,
|
|
319
|
-
timeout: REQUEST_TIMEOUT_MS,
|
|
320
|
-
retries: MAX_RETRIES,
|
|
321
|
-
}
|
|
322
|
-
config.set(key === "registry" ? "registryUrl" : key, defaults[key])
|
|
323
|
-
return true
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
config.delete(key)
|
|
327
|
-
return true
|
|
328
|
-
}
|