@lpm-registry/cli 0.2.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/CHANGELOG.md +36 -0
- package/LICENSE +15 -0
- package/README.md +406 -0
- package/bin/lpm.js +334 -0
- package/index.d.ts +131 -0
- package/index.js +31 -0
- package/lib/api.js +324 -0
- package/lib/commands/add.js +1217 -0
- package/lib/commands/audit.js +283 -0
- package/lib/commands/cache.js +209 -0
- package/lib/commands/check-name.js +112 -0
- package/lib/commands/config.js +174 -0
- package/lib/commands/doctor.js +142 -0
- package/lib/commands/info.js +215 -0
- package/lib/commands/init.js +146 -0
- package/lib/commands/install.js +217 -0
- package/lib/commands/login.js +547 -0
- package/lib/commands/logout.js +94 -0
- package/lib/commands/marketplace-compare.js +164 -0
- package/lib/commands/marketplace-earnings.js +89 -0
- package/lib/commands/mcp-setup.js +363 -0
- package/lib/commands/open.js +82 -0
- package/lib/commands/outdated.js +291 -0
- package/lib/commands/pool-stats.js +100 -0
- package/lib/commands/publish.js +707 -0
- package/lib/commands/quality.js +211 -0
- package/lib/commands/remove.js +82 -0
- package/lib/commands/run.js +14 -0
- package/lib/commands/search.js +143 -0
- package/lib/commands/setup.js +92 -0
- package/lib/commands/skills.js +863 -0
- package/lib/commands/token-rotate.js +25 -0
- package/lib/commands/whoami.js +129 -0
- package/lib/config.js +240 -0
- package/lib/constants.js +190 -0
- package/lib/ecosystem.js +501 -0
- package/lib/editors.js +215 -0
- package/lib/import-rewriter.js +364 -0
- package/lib/install-targets/mcp-server.js +245 -0
- package/lib/install-targets/vscode-extension.js +178 -0
- package/lib/install-targets.js +82 -0
- package/lib/integrity.js +179 -0
- package/lib/lpm-config-prompts.js +102 -0
- package/lib/lpm-config.js +408 -0
- package/lib/project-utils.js +152 -0
- package/lib/quality/checks.js +654 -0
- package/lib/quality/display.js +139 -0
- package/lib/quality/score.js +115 -0
- package/lib/quality/swift-checks.js +447 -0
- package/lib/safe-path.js +180 -0
- package/lib/secure-store.js +288 -0
- package/lib/swift-project.js +637 -0
- package/lib/ui.js +40 -0
- package/package.json +74 -0
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
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
|
+
* Get the stored auth token from secure storage.
|
|
42
|
+
* Priority: LPM_TOKEN env var > secure storage > legacy storage
|
|
43
|
+
* @returns {Promise<string | null>}
|
|
44
|
+
*/
|
|
45
|
+
export async function getToken() {
|
|
46
|
+
// Check environment variable first (useful for CI/CD and testing)
|
|
47
|
+
const envToken = process.env.LPM_TOKEN
|
|
48
|
+
if (envToken) return envToken
|
|
49
|
+
|
|
50
|
+
// Try secure store
|
|
51
|
+
const secureToken = await secureGetToken()
|
|
52
|
+
if (secureToken) return secureToken
|
|
53
|
+
|
|
54
|
+
// Migration: check if token exists in old storage
|
|
55
|
+
const legacyToken = config.get("token")
|
|
56
|
+
if (legacyToken) {
|
|
57
|
+
// Migrate to secure storage
|
|
58
|
+
await secureSetToken(legacyToken)
|
|
59
|
+
config.delete("token")
|
|
60
|
+
return legacyToken
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Set the auth token in secure storage.
|
|
68
|
+
* @param {string | null} token
|
|
69
|
+
* @returns {Promise<void>}
|
|
70
|
+
*/
|
|
71
|
+
export async function setToken(token) {
|
|
72
|
+
if (token === null) {
|
|
73
|
+
await secureClearToken()
|
|
74
|
+
} else {
|
|
75
|
+
await secureSetToken(token)
|
|
76
|
+
}
|
|
77
|
+
// Ensure legacy token is cleared
|
|
78
|
+
config.delete("token")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Clear the auth token from secure storage.
|
|
83
|
+
* @returns {Promise<void>}
|
|
84
|
+
*/
|
|
85
|
+
export async function clearToken() {
|
|
86
|
+
await secureClearToken()
|
|
87
|
+
config.delete("token")
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Registry URL Configuration
|
|
92
|
+
// ============================================================================
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get the registry URL.
|
|
96
|
+
* Priority: LPM_REGISTRY_URL env var > stored config > default
|
|
97
|
+
* @returns {string}
|
|
98
|
+
*/
|
|
99
|
+
export function getRegistryUrl() {
|
|
100
|
+
// Check environment variables first (useful for CI/CD and testing)
|
|
101
|
+
const envUrl = process.env.LPM_REGISTRY_URL
|
|
102
|
+
if (envUrl) return envUrl
|
|
103
|
+
|
|
104
|
+
return config.get("registryUrl", DEFAULT_REGISTRY_URL)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Set the registry URL.
|
|
109
|
+
* @param {string} url
|
|
110
|
+
*/
|
|
111
|
+
export function setRegistryUrl(url) {
|
|
112
|
+
config.set("registryUrl", url)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// Timeout Configuration
|
|
117
|
+
// ============================================================================
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get the request timeout in milliseconds.
|
|
121
|
+
* @returns {number}
|
|
122
|
+
*/
|
|
123
|
+
export function getTimeout() {
|
|
124
|
+
return config.get("timeout", REQUEST_TIMEOUT_MS)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Set the request timeout in milliseconds.
|
|
129
|
+
* @param {number} ms
|
|
130
|
+
*/
|
|
131
|
+
export function setTimeout(ms) {
|
|
132
|
+
config.set("timeout", ms)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// Retry Configuration
|
|
137
|
+
// ============================================================================
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get the maximum retry count.
|
|
141
|
+
* @returns {number}
|
|
142
|
+
*/
|
|
143
|
+
export function getRetries() {
|
|
144
|
+
return config.get("retries", MAX_RETRIES)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Set the maximum retry count.
|
|
149
|
+
* @param {number} count
|
|
150
|
+
*/
|
|
151
|
+
export function setRetries(count) {
|
|
152
|
+
config.set("retries", count)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// General Configuration Access
|
|
157
|
+
// ============================================================================
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get all configuration values.
|
|
161
|
+
* @returns {Promise<Record<string, unknown>>}
|
|
162
|
+
*/
|
|
163
|
+
export async function getAllConfig() {
|
|
164
|
+
const usingKeychain = await isUsingKeychain()
|
|
165
|
+
const hasToken = !!(await getToken())
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
registryUrl: getRegistryUrl(),
|
|
169
|
+
timeout: getTimeout(),
|
|
170
|
+
retries: getRetries(),
|
|
171
|
+
secureStorage: usingKeychain ? "keychain" : "encrypted-file",
|
|
172
|
+
authenticated: hasToken,
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get a specific configuration value.
|
|
178
|
+
* @param {string} key
|
|
179
|
+
* @returns {unknown}
|
|
180
|
+
*/
|
|
181
|
+
export function getConfigValue(key) {
|
|
182
|
+
const configMap = {
|
|
183
|
+
registry: getRegistryUrl,
|
|
184
|
+
registryUrl: getRegistryUrl,
|
|
185
|
+
timeout: getTimeout,
|
|
186
|
+
retries: getRetries,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const getter = configMap[key]
|
|
190
|
+
if (getter) return getter()
|
|
191
|
+
return config.get(key)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Set a specific configuration value.
|
|
196
|
+
* @param {string} key
|
|
197
|
+
* @param {unknown} value
|
|
198
|
+
* @returns {boolean} - True if set successfully
|
|
199
|
+
*/
|
|
200
|
+
export function setConfigValue(key, value) {
|
|
201
|
+
const configMap = {
|
|
202
|
+
registry: setRegistryUrl,
|
|
203
|
+
registryUrl: setRegistryUrl,
|
|
204
|
+
timeout: val => setTimeout(Number(val)),
|
|
205
|
+
retries: val => setRetries(Number(val)),
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const setter = configMap[key]
|
|
209
|
+
if (setter) {
|
|
210
|
+
setter(value)
|
|
211
|
+
return true
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Allow setting arbitrary config values
|
|
215
|
+
config.set(key, value)
|
|
216
|
+
return true
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Delete a specific configuration value.
|
|
221
|
+
* @param {string} key
|
|
222
|
+
* @returns {boolean} - True if deleted
|
|
223
|
+
*/
|
|
224
|
+
export function deleteConfigValue(key) {
|
|
225
|
+
const protectedKeys = ["registryUrl", "registry", "timeout", "retries"]
|
|
226
|
+
if (protectedKeys.includes(key)) {
|
|
227
|
+
// Reset to default instead of deleting
|
|
228
|
+
const defaults = {
|
|
229
|
+
registryUrl: DEFAULT_REGISTRY_URL,
|
|
230
|
+
registry: DEFAULT_REGISTRY_URL,
|
|
231
|
+
timeout: REQUEST_TIMEOUT_MS,
|
|
232
|
+
retries: MAX_RETRIES,
|
|
233
|
+
}
|
|
234
|
+
config.set(key === "registry" ? "registryUrl" : key, defaults[key])
|
|
235
|
+
return true
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
config.delete(key)
|
|
239
|
+
return true
|
|
240
|
+
}
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Constants Configuration
|
|
3
|
+
* Centralized configuration for all CLI behavior.
|
|
4
|
+
*
|
|
5
|
+
* @module cli/lib/constants
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Network Configuration
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/** Maximum number of retry attempts for failed requests */
|
|
13
|
+
export const MAX_RETRIES = 3
|
|
14
|
+
|
|
15
|
+
/** Request timeout in milliseconds (30 seconds) */
|
|
16
|
+
export const REQUEST_TIMEOUT_MS = 30_000
|
|
17
|
+
|
|
18
|
+
/** Base delay for exponential backoff in milliseconds */
|
|
19
|
+
export const RETRY_BASE_DELAY_MS = 1_000
|
|
20
|
+
|
|
21
|
+
/** Maximum delay between retries in milliseconds */
|
|
22
|
+
export const RETRY_MAX_DELAY_MS = 10_000
|
|
23
|
+
|
|
24
|
+
/** Multiplier for exponential backoff */
|
|
25
|
+
export const RETRY_BACKOFF_MULTIPLIER = 2
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Cache Configuration
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
/** Cache directory name (relative to user's home) */
|
|
32
|
+
export const CACHE_DIR_NAME = ".lpm-cache"
|
|
33
|
+
|
|
34
|
+
/** Maximum cache size in bytes (500 MB) */
|
|
35
|
+
export const MAX_CACHE_SIZE_BYTES = 500 * 1024 * 1024
|
|
36
|
+
|
|
37
|
+
/** Cache entry TTL in milliseconds (7 days) */
|
|
38
|
+
export const CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Security Configuration
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
/** Keytar service name for credential storage */
|
|
45
|
+
export const KEYTAR_SERVICE_NAME = "lpm-cli"
|
|
46
|
+
|
|
47
|
+
/** Keytar account name for token storage */
|
|
48
|
+
export const KEYTAR_ACCOUNT_NAME = "auth-token"
|
|
49
|
+
|
|
50
|
+
/** Token scopes that allow publishing */
|
|
51
|
+
export const PUBLISH_SCOPES = ["publish", "write", "full"]
|
|
52
|
+
|
|
53
|
+
/** Token scopes that allow reading */
|
|
54
|
+
export const READ_SCOPES = ["read", "publish", "write", "full"]
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// API Configuration
|
|
58
|
+
// ============================================================================
|
|
59
|
+
|
|
60
|
+
/** Default registry URL */
|
|
61
|
+
export const DEFAULT_REGISTRY_URL = "https://lpm.dev"
|
|
62
|
+
|
|
63
|
+
/** API version prefix */
|
|
64
|
+
export const API_VERSION = "v1"
|
|
65
|
+
|
|
66
|
+
/** HTTP status codes that trigger retries */
|
|
67
|
+
export const RETRYABLE_STATUS_CODES = [408, 429, 500, 502, 503, 504]
|
|
68
|
+
|
|
69
|
+
/** HTTP status codes that indicate rate limiting */
|
|
70
|
+
export const RATE_LIMIT_STATUS_CODES = [429]
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// CLI Configuration
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
/** CLI name for display purposes */
|
|
77
|
+
export const CLI_NAME = "lpm"
|
|
78
|
+
|
|
79
|
+
/** Default pagination limit for list commands */
|
|
80
|
+
export const DEFAULT_PAGE_LIMIT = 20
|
|
81
|
+
|
|
82
|
+
/** Maximum pagination limit */
|
|
83
|
+
export const MAX_PAGE_LIMIT = 100
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// File System Configuration
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
/** Default source directory for component extraction */
|
|
90
|
+
export const DEFAULT_COMPONENTS_DIR = "components"
|
|
91
|
+
|
|
92
|
+
/** Allowed file extensions for source code extraction */
|
|
93
|
+
export const ALLOWED_SOURCE_EXTENSIONS = [
|
|
94
|
+
".js",
|
|
95
|
+
".jsx",
|
|
96
|
+
".ts",
|
|
97
|
+
".tsx",
|
|
98
|
+
".css",
|
|
99
|
+
".scss",
|
|
100
|
+
".json",
|
|
101
|
+
".md",
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
/** Maximum file size for source extraction (10 MB) */
|
|
105
|
+
export const MAX_SOURCE_FILE_SIZE_BYTES = 10 * 1024 * 1024
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Integrity Verification
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
/** Default hash algorithm for tarball verification */
|
|
112
|
+
export const DEFAULT_HASH_ALGORITHM = "sha512"
|
|
113
|
+
|
|
114
|
+
/** Supported hash algorithms */
|
|
115
|
+
export const SUPPORTED_HASH_ALGORITHMS = ["sha256", "sha384", "sha512"]
|
|
116
|
+
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// Spinner Messages
|
|
119
|
+
// ============================================================================
|
|
120
|
+
|
|
121
|
+
export const SPINNER_MESSAGES = {
|
|
122
|
+
authenticating: "Authenticating...",
|
|
123
|
+
downloading: "Downloading package...",
|
|
124
|
+
extracting: "Extracting files...",
|
|
125
|
+
publishing: "Publishing package...",
|
|
126
|
+
verifying: "Verifying integrity...",
|
|
127
|
+
retrying: (attempt, max) => `Retrying (${attempt}/${max})...`,
|
|
128
|
+
rateLimited: seconds => `Rate limited. Waiting ${seconds}s...`,
|
|
129
|
+
readingConfig: "Reading package configuration...",
|
|
130
|
+
filteringFiles: "Filtering files based on configuration...",
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// Error Messages
|
|
135
|
+
// ============================================================================
|
|
136
|
+
|
|
137
|
+
export const ERROR_MESSAGES = {
|
|
138
|
+
notAuthenticated: "Not authenticated. Run `lpm login` first.",
|
|
139
|
+
tokenExpired: "Token expired. Run `lpm login` to refresh.",
|
|
140
|
+
tokenMissingScope: scope =>
|
|
141
|
+
`Token missing required scope: ${scope}. Run \`lpm token-rotate --scope ${scope}\` to fix.`,
|
|
142
|
+
networkError: "Network error. Check your connection and try again.",
|
|
143
|
+
rateLimited: "Rate limited. Please wait and try again.",
|
|
144
|
+
integrityMismatch:
|
|
145
|
+
"Package integrity check failed. Download may be corrupted.",
|
|
146
|
+
pathTraversal: "Invalid path: path traversal detected.",
|
|
147
|
+
timeout:
|
|
148
|
+
"Request timed out. Try again or increase timeout with `lpm config set timeout <ms>`.",
|
|
149
|
+
invalidLpmConfig: "Invalid lpm.config.json: ",
|
|
150
|
+
invalidConfigValue: (key, value, allowed) =>
|
|
151
|
+
`Invalid value "${value}" for "${key}". Allowed: ${allowed.join(", ")}`,
|
|
152
|
+
missingRequiredConfig: key =>
|
|
153
|
+
`Required config parameter "${key}" not provided. Use ?${key}=value in the package URL.`,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ============================================================================
|
|
157
|
+
// Warning Messages
|
|
158
|
+
// ============================================================================
|
|
159
|
+
|
|
160
|
+
export const WARNING_MESSAGES = {
|
|
161
|
+
usernameNotSet: "Your personal username is not set.",
|
|
162
|
+
usernameNotSetHint: registryUrl =>
|
|
163
|
+
`Set it to publish packages under your personal owner:\n ${registryUrl}/dashboard/settings`,
|
|
164
|
+
ownerMismatch: owner =>
|
|
165
|
+
`Package owner "@lpm.dev/${owner}" doesn't match your available owners.`,
|
|
166
|
+
// Legacy - kept for backward compatibility
|
|
167
|
+
scopeMismatch: scope =>
|
|
168
|
+
`Package owner "@lpm.dev/${scope}" doesn't match your available owners.`,
|
|
169
|
+
noOrganizations: "You have no organizations.",
|
|
170
|
+
createOrgHint: registryUrl =>
|
|
171
|
+
`Create one at: ${registryUrl}/dashboard/orgs/new`,
|
|
172
|
+
ownerFixHint:
|
|
173
|
+
'Either:\n 1. Set your username/org slug to match the package owner\n 2. Change package.json "name" to use @lpm.dev/YOUR_OWNER.package-name',
|
|
174
|
+
// Legacy - kept for backward compatibility
|
|
175
|
+
scopeFixHint:
|
|
176
|
+
'Either:\n 1. Set your username/org slug to match the package owner\n 2. Change package.json "name" to use @lpm.dev/YOUR_OWNER.package-name',
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ============================================================================
|
|
180
|
+
// Success Messages
|
|
181
|
+
// ============================================================================
|
|
182
|
+
|
|
183
|
+
export const SUCCESS_MESSAGES = {
|
|
184
|
+
// Updated for new format: @lpm.dev/owner.package-name
|
|
185
|
+
// owner = username or org slug, pkgName = package name (without owner prefix)
|
|
186
|
+
publishPersonal: (registryUrl, owner, pkgName, version) =>
|
|
187
|
+
`Successfully published @lpm.dev/${owner}.${pkgName}@${version}\n ${registryUrl}/${owner}.${pkgName}`,
|
|
188
|
+
publishOrg: (registryUrl, owner, pkgName, version) =>
|
|
189
|
+
`Successfully published @lpm.dev/${owner}.${pkgName}@${version}\n ${registryUrl}/${owner}.${pkgName}`,
|
|
190
|
+
}
|