@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,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outdated Command
|
|
3
|
+
*
|
|
4
|
+
* Check for outdated dependencies in the project.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* lpm outdated [options]
|
|
8
|
+
*
|
|
9
|
+
* Options:
|
|
10
|
+
* --json Output in JSON format
|
|
11
|
+
* --all Show all dependencies, not just outdated ones
|
|
12
|
+
*
|
|
13
|
+
* @module cli/lib/commands/outdated
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
17
|
+
import { join } from "node:path"
|
|
18
|
+
import chalk from "chalk"
|
|
19
|
+
import ora from "ora"
|
|
20
|
+
import { post } from "../api.js"
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Read and parse package.json from current directory.
|
|
24
|
+
* @returns {Object | null}
|
|
25
|
+
*/
|
|
26
|
+
function readPackageJson() {
|
|
27
|
+
const packageJsonPath = join(process.cwd(), "package.json")
|
|
28
|
+
|
|
29
|
+
if (!existsSync(packageJsonPath)) {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const content = readFileSync(packageJsonPath, "utf8")
|
|
35
|
+
return JSON.parse(content)
|
|
36
|
+
} catch {
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Read package-lock.json for current versions.
|
|
43
|
+
* @returns {Object | null}
|
|
44
|
+
*/
|
|
45
|
+
function readPackageLock() {
|
|
46
|
+
const lockPath = join(process.cwd(), "package-lock.json")
|
|
47
|
+
|
|
48
|
+
if (!existsSync(lockPath)) {
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const content = readFileSync(lockPath, "utf8")
|
|
54
|
+
const lock = JSON.parse(content)
|
|
55
|
+
return lock.packages || lock.dependencies || null
|
|
56
|
+
} catch {
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Compare semver versions.
|
|
63
|
+
* @param {string} current
|
|
64
|
+
* @param {string} latest
|
|
65
|
+
* @returns {'major' | 'minor' | 'patch' | 'same' | 'unknown'}
|
|
66
|
+
*/
|
|
67
|
+
function getUpdateType(current, latest) {
|
|
68
|
+
const parseVersion = v => {
|
|
69
|
+
const match = v.match(/^(\d+)\.(\d+)\.(\d+)/)
|
|
70
|
+
if (!match) return null
|
|
71
|
+
return [
|
|
72
|
+
parseInt(match[1], 10),
|
|
73
|
+
parseInt(match[2], 10),
|
|
74
|
+
parseInt(match[3], 10),
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const currentParts = parseVersion(current)
|
|
79
|
+
const latestParts = parseVersion(latest)
|
|
80
|
+
|
|
81
|
+
if (!currentParts || !latestParts) return "unknown"
|
|
82
|
+
|
|
83
|
+
if (latestParts[0] > currentParts[0]) return "major"
|
|
84
|
+
if (latestParts[1] > currentParts[1]) return "minor"
|
|
85
|
+
if (latestParts[2] > currentParts[2]) return "patch"
|
|
86
|
+
|
|
87
|
+
return "same"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get color for update type.
|
|
92
|
+
* @param {string} type
|
|
93
|
+
* @returns {Function}
|
|
94
|
+
*/
|
|
95
|
+
function getUpdateColor(type) {
|
|
96
|
+
switch (type) {
|
|
97
|
+
case "major":
|
|
98
|
+
return chalk.red
|
|
99
|
+
case "minor":
|
|
100
|
+
return chalk.yellow
|
|
101
|
+
case "patch":
|
|
102
|
+
return chalk.green
|
|
103
|
+
default:
|
|
104
|
+
return chalk.dim
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Execute the outdated command.
|
|
110
|
+
*
|
|
111
|
+
* @param {Object} options - Command options
|
|
112
|
+
* @param {boolean} [options.json] - Output as JSON
|
|
113
|
+
* @param {boolean} [options.all] - Show all dependencies
|
|
114
|
+
*/
|
|
115
|
+
export async function outdated(options = {}) {
|
|
116
|
+
const spinner = ora("Reading dependencies...").start()
|
|
117
|
+
|
|
118
|
+
// Read package.json
|
|
119
|
+
const packageJson = readPackageJson()
|
|
120
|
+
|
|
121
|
+
if (!packageJson) {
|
|
122
|
+
spinner.fail(chalk.red("No package.json found in current directory."))
|
|
123
|
+
process.exit(1)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const dependencies = {
|
|
127
|
+
...(packageJson.dependencies || {}),
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const devDependencies = {
|
|
131
|
+
...(packageJson.devDependencies || {}),
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const allDeps = { ...dependencies, ...devDependencies }
|
|
135
|
+
const depCount = Object.keys(allDeps).length
|
|
136
|
+
|
|
137
|
+
if (depCount === 0) {
|
|
138
|
+
spinner.succeed(chalk.green("No dependencies to check."))
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Get current versions from lock file
|
|
143
|
+
const lockData = readPackageLock()
|
|
144
|
+
|
|
145
|
+
// Build dependency list
|
|
146
|
+
const depList = Object.entries(allDeps).map(([name, version]) => {
|
|
147
|
+
let currentVersion = version.replace(/^[\^~]/, "")
|
|
148
|
+
|
|
149
|
+
if (lockData) {
|
|
150
|
+
const lockEntry = lockData[name] || lockData[`node_modules/${name}`]
|
|
151
|
+
if (lockEntry?.version) {
|
|
152
|
+
currentVersion = lockEntry.version
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
name,
|
|
158
|
+
currentVersion,
|
|
159
|
+
wantedVersion: version,
|
|
160
|
+
isDev: name in devDependencies,
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
spinner.text = `Checking ${depCount} dependencies...`
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const response = await post(
|
|
168
|
+
"/outdated",
|
|
169
|
+
{
|
|
170
|
+
dependencies: depList.map(d => ({
|
|
171
|
+
name: d.name,
|
|
172
|
+
version: d.currentVersion,
|
|
173
|
+
})),
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
onRetry: (attempt, max) => {
|
|
177
|
+
spinner.text = `Checking (retry ${attempt}/${max})...`
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
const data = await response.json().catch(() => ({}))
|
|
184
|
+
throw new Error(data.error || "Outdated check failed.")
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const data = await response.json()
|
|
188
|
+
const latestVersions = data.packages || {}
|
|
189
|
+
|
|
190
|
+
spinner.stop()
|
|
191
|
+
|
|
192
|
+
// Merge latest versions with our data
|
|
193
|
+
const results = depList.map(dep => {
|
|
194
|
+
const latest = latestVersions[dep.name]?.latest || dep.currentVersion
|
|
195
|
+
const updateType = getUpdateType(dep.currentVersion, latest)
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
...dep,
|
|
199
|
+
latestVersion: latest,
|
|
200
|
+
updateType,
|
|
201
|
+
isOutdated: updateType !== "same" && updateType !== "unknown",
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
// Filter if not showing all
|
|
206
|
+
const displayResults = options.all
|
|
207
|
+
? results
|
|
208
|
+
: results.filter(r => r.isOutdated)
|
|
209
|
+
|
|
210
|
+
// JSON output
|
|
211
|
+
if (options.json) {
|
|
212
|
+
console.log(JSON.stringify(displayResults, null, 2))
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// No outdated packages
|
|
217
|
+
if (displayResults.length === 0) {
|
|
218
|
+
console.log(
|
|
219
|
+
chalk.green(`\n✓ All ${depCount} dependencies are up to date.\n`),
|
|
220
|
+
)
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Display results
|
|
225
|
+
const outdatedCount = results.filter(r => r.isOutdated).length
|
|
226
|
+
console.log(
|
|
227
|
+
chalk.bold(
|
|
228
|
+
`\n${outdatedCount} outdated package${outdatedCount > 1 ? "s" : ""} in ${depCount} dependencies.\n`,
|
|
229
|
+
),
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
// Calculate column widths
|
|
233
|
+
const maxNameLen = Math.max(...displayResults.map(r => r.name.length), 10)
|
|
234
|
+
const maxCurrentLen = Math.max(
|
|
235
|
+
...displayResults.map(r => r.currentVersion.length),
|
|
236
|
+
7,
|
|
237
|
+
)
|
|
238
|
+
const maxLatestLen = Math.max(
|
|
239
|
+
...displayResults.map(r => r.latestVersion.length),
|
|
240
|
+
6,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
// Header
|
|
244
|
+
console.log(
|
|
245
|
+
` ${chalk.dim("Package".padEnd(maxNameLen))} ${chalk.dim("Current".padEnd(maxCurrentLen))} ${chalk.dim("Latest".padEnd(maxLatestLen))} ${chalk.dim("Type")}`,
|
|
246
|
+
)
|
|
247
|
+
console.log(
|
|
248
|
+
chalk.dim(
|
|
249
|
+
` ${"─".repeat(maxNameLen + maxCurrentLen + maxLatestLen + 20)}`,
|
|
250
|
+
),
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
// Rows
|
|
254
|
+
for (const result of displayResults) {
|
|
255
|
+
const name = result.name.padEnd(maxNameLen)
|
|
256
|
+
const current = result.currentVersion.padEnd(maxCurrentLen)
|
|
257
|
+
const latest = result.latestVersion.padEnd(maxLatestLen)
|
|
258
|
+
const color = getUpdateColor(result.updateType)
|
|
259
|
+
const devTag = result.isDev ? chalk.dim(" (dev)") : ""
|
|
260
|
+
|
|
261
|
+
console.log(
|
|
262
|
+
` ${chalk.cyan(name)} ${chalk.dim(current)} ${color(latest)} ${color(result.updateType)}${devTag}`,
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
console.log("")
|
|
267
|
+
|
|
268
|
+
// Summary
|
|
269
|
+
const majorCount = results.filter(r => r.updateType === "major").length
|
|
270
|
+
const minorCount = results.filter(r => r.updateType === "minor").length
|
|
271
|
+
const patchCount = results.filter(r => r.updateType === "patch").length
|
|
272
|
+
|
|
273
|
+
const parts = []
|
|
274
|
+
if (majorCount > 0) parts.push(chalk.red(`${majorCount} major`))
|
|
275
|
+
if (minorCount > 0) parts.push(chalk.yellow(`${minorCount} minor`))
|
|
276
|
+
if (patchCount > 0) parts.push(chalk.green(`${patchCount} patch`))
|
|
277
|
+
|
|
278
|
+
if (parts.length > 0) {
|
|
279
|
+
console.log(
|
|
280
|
+
` ${parts.join(", ")} update${outdatedCount > 1 ? "s" : ""} available.`,
|
|
281
|
+
)
|
|
282
|
+
console.log("")
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
spinner.fail(chalk.red("Outdated check failed."))
|
|
286
|
+
console.error(chalk.red(` ${error.message}`))
|
|
287
|
+
process.exit(1)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export default outdated
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import chalk from "chalk"
|
|
2
|
+
import ora from "ora"
|
|
3
|
+
import { get } from "../api.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Format cents to a dollar string.
|
|
7
|
+
* @param {number} cents
|
|
8
|
+
* @returns {string}
|
|
9
|
+
*/
|
|
10
|
+
function formatCents(cents) {
|
|
11
|
+
if (!cents && cents !== 0) return "$0.00"
|
|
12
|
+
return `$${(cents / 100).toFixed(2)}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Fetch and display Pool earnings statistics for the authenticated user.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} [options]
|
|
19
|
+
* @param {boolean} [options.json] - Output as JSON
|
|
20
|
+
*/
|
|
21
|
+
export async function poolStats(options = {}) {
|
|
22
|
+
const spinner = options.json
|
|
23
|
+
? null
|
|
24
|
+
: ora("Fetching pool statistics...").start()
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const response = await get("/pool/stats", {
|
|
28
|
+
skipRetry: false,
|
|
29
|
+
onRetry: spinner
|
|
30
|
+
? (attempt, max) => {
|
|
31
|
+
spinner.text = `Fetching (retry ${attempt}/${max})...`
|
|
32
|
+
}
|
|
33
|
+
: undefined,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const data = await response.json().catch(() => ({}))
|
|
37
|
+
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
throw new Error(data.error || `Request failed: ${response.status}`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (options.json) {
|
|
43
|
+
console.log(JSON.stringify(data, null, 2))
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (spinner) spinner.stop()
|
|
48
|
+
|
|
49
|
+
const packages = data.packages || []
|
|
50
|
+
|
|
51
|
+
console.log("")
|
|
52
|
+
console.log(chalk.bold(" Pool Earnings — Current Month"))
|
|
53
|
+
console.log(chalk.dim(` Period: ${data.billingPeriod || "N/A"}`))
|
|
54
|
+
console.log("")
|
|
55
|
+
|
|
56
|
+
// Summary stats
|
|
57
|
+
console.log(
|
|
58
|
+
` ${chalk.bold("Your Estimate:")} ${chalk.green(formatCents(data.estimatedEarningsCents))}`,
|
|
59
|
+
)
|
|
60
|
+
console.log("")
|
|
61
|
+
|
|
62
|
+
if (packages.length === 0) {
|
|
63
|
+
console.log(chalk.dim(" No pool packages found."))
|
|
64
|
+
console.log(
|
|
65
|
+
chalk.dim(
|
|
66
|
+
" Publish a package with Pool distribution to start earning.",
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
console.log("")
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Table header
|
|
74
|
+
const nameWidth = Math.max(30, ...packages.map(p => p.name.length + 2))
|
|
75
|
+
console.log(
|
|
76
|
+
` ${chalk.dim("Package".padEnd(nameWidth))}${chalk.dim("Installs".padStart(10))}${chalk.dim("Share %".padStart(10))}${chalk.dim("Earnings".padStart(12))}`,
|
|
77
|
+
)
|
|
78
|
+
console.log(chalk.dim(` ${"─".repeat(nameWidth + 32)}`))
|
|
79
|
+
|
|
80
|
+
for (const pkg of packages) {
|
|
81
|
+
const name = pkg.name.padEnd(nameWidth)
|
|
82
|
+
const installs = (pkg.installCount || 0).toLocaleString().padStart(10)
|
|
83
|
+
const share = `${(pkg.sharePercentage || 0).toFixed(2)}%`.padStart(10)
|
|
84
|
+
const earnings = formatCents(pkg.estimatedEarningsCents).padStart(12)
|
|
85
|
+
console.log(` ${name}${installs}${share}${chalk.green(earnings)}`)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log("")
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (spinner) spinner.fail(chalk.red("Failed to fetch pool statistics."))
|
|
91
|
+
if (options.json) {
|
|
92
|
+
console.log(JSON.stringify({ error: error.message }))
|
|
93
|
+
} else {
|
|
94
|
+
console.error(chalk.red(` ${error.message}`))
|
|
95
|
+
}
|
|
96
|
+
process.exit(1)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export default poolStats
|