@wwkit/opm 1.0.8 → 1.0.9
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 +10 -17
- package/docs/managers/apt.md +2 -3
- package/docs/managers/brew.md +2 -6
- package/docs/managers/bun.md +2 -4
- package/docs/managers/composer.md +2 -6
- package/docs/managers/dnf.md +2 -3
- package/docs/managers/npm.md +2 -6
- package/docs/managers/pip.md +2 -6
- package/docs/opm.html +7 -20
- package/package.json +3 -3
- package/src/cli/groups/overlay.js +169 -0
- package/src/cli/groups/package.js +42 -18
- package/src/cli/index.js +2 -0
- package/src/config.json5 +1 -0
- package/src/harnesses/opencode/index.js +1 -1
- package/src/index.js +1 -0
- package/src/managers/apt.js +3 -12
- package/src/managers/base.js +17 -2
- package/src/managers/brew.js +3 -5
- package/src/managers/bun.js +0 -18
- package/src/managers/composer.js +65 -9
- package/src/managers/dnf.js +3 -12
- package/src/managers/npm.js +51 -45
- package/src/managers/pip.js +24 -18
- package/src/managers/winget.js +3 -13
package/src/managers/npm.js
CHANGED
|
@@ -290,8 +290,31 @@ export class NpmManager extends PackageManager {
|
|
|
290
290
|
return null
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
/**
|
|
294
|
+
* 通过 PATH 查找 bin 路径(Linux: which / Windows: where)
|
|
295
|
+
* @param {string} name - bin 名
|
|
296
|
+
* @returns {string|null} bin 路径或 null
|
|
297
|
+
* @private
|
|
298
|
+
*/
|
|
299
|
+
_whichBin(name) {
|
|
300
|
+
const cmd = process.platform === 'win32' ? `where ${name}` : `which ${name}`
|
|
301
|
+
try {
|
|
302
|
+
const out = execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] })
|
|
303
|
+
const lines = out.trim().split(/\r?\n/).filter(Boolean)
|
|
304
|
+
return lines[0] || null
|
|
305
|
+
} catch {
|
|
306
|
+
return null
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
293
310
|
/**
|
|
294
311
|
* 查询全局安装的包
|
|
312
|
+
*
|
|
313
|
+
* 三段式检查:
|
|
314
|
+
* 1. npm list -g <name>(当前激活 node 全局)
|
|
315
|
+
* 2. npm root -g 目录直查(同上,兜底)
|
|
316
|
+
* 3. PATH 回退:which <name> → realpath → 推导全局 node_modules
|
|
317
|
+
* 覆盖通过其他 node 版本/路径安装的包(如非 nvm 管理的独立 node)
|
|
295
318
|
* @param {string} name - 包名
|
|
296
319
|
* @returns {Promise<InstalledResult>}
|
|
297
320
|
* @private
|
|
@@ -316,6 +339,30 @@ export class NpmManager extends PackageManager {
|
|
|
316
339
|
}
|
|
317
340
|
} catch {}
|
|
318
341
|
|
|
342
|
+
// PATH 回退:which <name> → realpath → 推导全局 node_modules
|
|
343
|
+
try {
|
|
344
|
+
const binPath = this._whichBin(name)
|
|
345
|
+
if (binPath) {
|
|
346
|
+
const realBin = fs.realpathSync(binPath)
|
|
347
|
+
const binDir = path.dirname(realBin)
|
|
348
|
+
const candidates = [
|
|
349
|
+
path.join(binDir, '..', 'package.json'),
|
|
350
|
+
path.join(binDir, '..', 'lib', 'node_modules', name, 'package.json'),
|
|
351
|
+
path.join(binDir, '..', 'node_modules', name, 'package.json')
|
|
352
|
+
]
|
|
353
|
+
for (const pkgPath of candidates) {
|
|
354
|
+
try {
|
|
355
|
+
if (fs.existsSync(pkgPath)) {
|
|
356
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
|
357
|
+
return { name, installed: true, version: pkg.version, path: path.dirname(pkgPath), global: true }
|
|
358
|
+
}
|
|
359
|
+
} catch {}
|
|
360
|
+
}
|
|
361
|
+
// 找到 bin 但推导不到 package.json(极端情况)
|
|
362
|
+
return { name, installed: true, global: true }
|
|
363
|
+
}
|
|
364
|
+
} catch {}
|
|
365
|
+
|
|
319
366
|
return { name, installed: false, global: true }
|
|
320
367
|
}
|
|
321
368
|
|
|
@@ -375,48 +422,6 @@ export class NpmManager extends PackageManager {
|
|
|
375
422
|
return { name, versions: [], registry }
|
|
376
423
|
}
|
|
377
424
|
|
|
378
|
-
async listInstalled(opts = {}) {
|
|
379
|
-
if (opts.global) {
|
|
380
|
-
try {
|
|
381
|
-
const data = this._execJson(['list', '-g', '--depth=0'])
|
|
382
|
-
const deps = data?.dependencies || {}
|
|
383
|
-
return Object.entries(deps)
|
|
384
|
-
.filter(([, info]) => info && info.version)
|
|
385
|
-
.map(([name, info]) => ({ name, version: info.version, global: true }))
|
|
386
|
-
} catch (err) {
|
|
387
|
-
throw new Error(`npm list -g failed: ${err.message}`)
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const declared = this._readDeclaredDeps()
|
|
392
|
-
const workspaces = this._readWorkspacePackages()
|
|
393
|
-
const items = []
|
|
394
|
-
const seen = new Set()
|
|
395
|
-
|
|
396
|
-
for (const { name, version, dir } of workspaces) {
|
|
397
|
-
if (seen.has(name)) continue
|
|
398
|
-
seen.add(name)
|
|
399
|
-
const resolved = this._resolveInstalled(name)
|
|
400
|
-
items.push({
|
|
401
|
-
name,
|
|
402
|
-
version: resolved ? resolved.version : version,
|
|
403
|
-
path: resolved ? resolved.path : dir
|
|
404
|
-
})
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
for (const [name, declaredVersion] of Object.entries(declared)) {
|
|
408
|
-
if (seen.has(name)) continue
|
|
409
|
-
seen.add(name)
|
|
410
|
-
const resolved = this._resolveInstalled(name)
|
|
411
|
-
items.push({
|
|
412
|
-
name,
|
|
413
|
-
version: resolved ? resolved.version : declaredVersion
|
|
414
|
-
})
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return items
|
|
418
|
-
}
|
|
419
|
-
|
|
420
425
|
async listOutdated(opts = {}) {
|
|
421
426
|
try {
|
|
422
427
|
const args = ['outdated']
|
|
@@ -455,7 +460,8 @@ export class NpmManager extends PackageManager {
|
|
|
455
460
|
|
|
456
461
|
/**
|
|
457
462
|
* 构造 --dangerously-allow-all-scripts 参数(仅 npm >= 11 支持)
|
|
458
|
-
* npm < 11 默认放行所有脚本,无需此 flag;npm >= 11
|
|
463
|
+
* npm < 11 默认放行所有脚本,无需此 flag;npm >= 11 默认阻止,需显式放行。
|
|
464
|
+
* install/upgrade 始终追加,保持与 npm < 11 一致的脚本行为(postinstall 可执行)。
|
|
459
465
|
* @param {string} name - 包名(保留签名兼容,未使用)
|
|
460
466
|
* @returns {string[]}
|
|
461
467
|
* @private
|
|
@@ -470,7 +476,7 @@ export class NpmManager extends PackageManager {
|
|
|
470
476
|
if (opts.global) args.push('-g')
|
|
471
477
|
args.push(...this._proxyArgs(opts.proxy))
|
|
472
478
|
args.push(...this._registryArgs())
|
|
473
|
-
|
|
479
|
+
args.push(...this._allowScriptsArgs(name))
|
|
474
480
|
|
|
475
481
|
this._execInherit(args)
|
|
476
482
|
return { name, version: version || 'latest', action: 'installed' }
|
|
@@ -489,7 +495,7 @@ export class NpmManager extends PackageManager {
|
|
|
489
495
|
if (opts.global) args.push('-g')
|
|
490
496
|
args.push(...this._proxyArgs(opts.proxy))
|
|
491
497
|
args.push(...this._registryArgs())
|
|
492
|
-
|
|
498
|
+
args.push(...this._allowScriptsArgs(name))
|
|
493
499
|
|
|
494
500
|
this._execInherit(args)
|
|
495
501
|
return { name, version: 'latest', action: 'upgraded' }
|
package/src/managers/pip.js
CHANGED
|
@@ -429,6 +429,23 @@ export class PipManager extends PackageManager {
|
|
|
429
429
|
return null
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
/**
|
|
433
|
+
* 通过 PATH 查找 bin 路径(Linux: which / Windows: where)
|
|
434
|
+
* @param {string} name - bin 名
|
|
435
|
+
* @returns {string|null} bin 路径或 null
|
|
436
|
+
* @private
|
|
437
|
+
*/
|
|
438
|
+
_whichBin(name) {
|
|
439
|
+
const cmd = process.platform === 'win32' ? `where ${name}` : `which ${name}`
|
|
440
|
+
try {
|
|
441
|
+
const out = execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] })
|
|
442
|
+
const lines = out.trim().split(/\r?\n/).filter(Boolean)
|
|
443
|
+
return lines[0] || null
|
|
444
|
+
} catch {
|
|
445
|
+
return null
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
432
449
|
async isInstalled(name, opts = {}) {
|
|
433
450
|
const info = this._parsePipShow(name, opts)
|
|
434
451
|
if (info) {
|
|
@@ -441,6 +458,13 @@ export class PipManager extends PackageManager {
|
|
|
441
458
|
}
|
|
442
459
|
}
|
|
443
460
|
|
|
461
|
+
if (opts.global) {
|
|
462
|
+
const binPath = this._whichBin(name)
|
|
463
|
+
if (binPath) {
|
|
464
|
+
return { name, installed: true, global: true, path: binPath }
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
444
468
|
return { name, installed: false, ...(opts.global ? { global: true } : {}) }
|
|
445
469
|
}
|
|
446
470
|
|
|
@@ -487,24 +511,6 @@ export class PipManager extends PackageManager {
|
|
|
487
511
|
return { name, versions: [], registry }
|
|
488
512
|
}
|
|
489
513
|
|
|
490
|
-
async listInstalled(opts = {}) {
|
|
491
|
-
try {
|
|
492
|
-
const out = this._execPip(['list', '--format=freeze'], opts)
|
|
493
|
-
const items = []
|
|
494
|
-
for (const line of out.split('\n')) {
|
|
495
|
-
const trimmed = line.trim()
|
|
496
|
-
if (!trimmed) continue
|
|
497
|
-
const match = trimmed.match(/^([a-zA-Z0-9_-]+)==(.+)$/)
|
|
498
|
-
if (match) {
|
|
499
|
-
items.push({ name: match[1], version: match[2], ...(opts.global ? { global: true } : {}) })
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
return items
|
|
503
|
-
} catch (err) {
|
|
504
|
-
throw new Error(`pip list failed: ${err.message}`)
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
514
|
async listOutdated(opts = {}) {
|
|
509
515
|
try {
|
|
510
516
|
const args = ['list', '--outdated', '--format=json']
|
package/src/managers/winget.js
CHANGED
|
@@ -175,6 +175,9 @@ export class WingetManager extends PackageManager {
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
async isInstalled(name, opts = {}) {
|
|
178
|
+
if (opts.global === false) {
|
|
179
|
+
return { name, installed: false, hint: 'no project scope (system-level manager)' }
|
|
180
|
+
}
|
|
178
181
|
const out = this._exec(
|
|
179
182
|
['list', name, '--accept-source-agreements', '--disable-interactivity'],
|
|
180
183
|
{ allowNonZero: true }
|
|
@@ -218,19 +221,6 @@ export class WingetManager extends PackageManager {
|
|
|
218
221
|
}
|
|
219
222
|
}
|
|
220
223
|
|
|
221
|
-
async listInstalled(opts = {}) {
|
|
222
|
-
const out = this._exec(
|
|
223
|
-
['list', '--accept-source-agreements', '--disable-interactivity'],
|
|
224
|
-
{ allowNonZero: true }
|
|
225
|
-
)
|
|
226
|
-
return this._parseTable(out).map((r) => ({
|
|
227
|
-
name: r.Name || r.Id,
|
|
228
|
-
id: r.Id,
|
|
229
|
-
version: r.Version || 'unknown',
|
|
230
|
-
source: r.Source || '',
|
|
231
|
-
}))
|
|
232
|
-
}
|
|
233
|
-
|
|
234
224
|
async listOutdated(opts = {}) {
|
|
235
225
|
const out = this._exec(
|
|
236
226
|
['list', '--upgrade-available', '--accept-source-agreements', '--disable-interactivity'],
|