@wwkit/opm 1.0.14 → 1.0.16
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 +33 -8
- package/docs/managers/apt.md +0 -1
- package/docs/managers/brew.md +0 -4
- package/docs/managers/bun.md +0 -2
- package/docs/managers/composer.md +0 -4
- package/docs/managers/dnf.md +0 -1
- package/docs/managers/npm.md +0 -4
- package/docs/managers/pip.md +0 -4
- package/docs/opm.html +8 -21
- package/docs/test/linux-environment-test-report.md +0 -1
- package/package.json +13 -4
- package/src/binaries/base.js +1 -1
- package/src/cli/groups/env.js +451 -0
- package/src/cli/groups/package.js +30 -10
- package/src/cli/groups/ping.js +60 -20
- package/src/cli/index.js +3 -1
- package/src/index.js +2 -1
- package/src/managers/apt.js +0 -17
- package/src/managers/base.js +0 -15
- package/src/managers/brew.js +1 -32
- package/src/managers/bun.js +7 -58
- package/src/managers/composer.js +1 -16
- package/src/managers/dnf.js +0 -17
- package/src/managers/npm.js +0 -20
- package/src/managers/pip.js +1 -22
- package/src/managers/proc.js +7 -1
- package/src/managers/runtime.js +0 -4
- package/src/managers/winget.js +0 -14
- package/src/managers/http.js +0 -432
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* env 命令组 — 跨平台环境变量便捷管理
|
|
3
|
+
*
|
|
4
|
+
* 用法:
|
|
5
|
+
* opm env list [--all] 列出 opm 管理的环境变量(含配置文件路径);--all 追加 process.env 全量
|
|
6
|
+
* opm env get <key> 根据 key 取值(先查 opm 管理的配置源,回退 process.env)
|
|
7
|
+
* opm env set <key> <value> 设置值(替换已存在的)
|
|
8
|
+
* opm env help 通用介绍
|
|
9
|
+
*
|
|
10
|
+
* 跨平台策略(复用 @wwkit/shared 的 Shell):
|
|
11
|
+
* Unix: 读写 shell profile(bash/zsh/fish),用 `# >>> wwkit NAME >>>` 标记块管理
|
|
12
|
+
* Windows: setx 写 user 级注册表;list 用 reg query 读取;用 manifest 文件
|
|
13
|
+
* (~/.config/opm/env-managed.json)识别哪些 user 级变量由 opm 管理
|
|
14
|
+
* (setx 不留标记,无法从注册表本身区分)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'node:fs'
|
|
18
|
+
import path from 'node:path'
|
|
19
|
+
import { spawnSync } from 'node:child_process'
|
|
20
|
+
import { Shell } from '@wwkit/shared'
|
|
21
|
+
import { parseFlags } from '../helpers/args.js'
|
|
22
|
+
import { output } from '../../formatter.js'
|
|
23
|
+
import { getUserConfigDir } from '../../config.js'
|
|
24
|
+
|
|
25
|
+
/** Windows 注册表环境变量键路径 */
|
|
26
|
+
const WIN_USER_KEY = 'HKCU\\Environment'
|
|
27
|
+
|
|
28
|
+
/** manifest 文件名(Windows 记录 opm 管理的 user 级变量 key) */
|
|
29
|
+
const MANIFEST_FILE = 'env-managed.json'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 反转 Shell.setEnvVar 的转义([$`\\"] → \\$&)
|
|
33
|
+
* @param {string} s
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
function unescapeEnvValue(s) {
|
|
37
|
+
return s.replace(/\\([$`\\"])/g, '$1')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 从一行 export/set 语句中提取变量值
|
|
42
|
+
* @param {string} line
|
|
43
|
+
* @param {string} key
|
|
44
|
+
* @returns {string|null}
|
|
45
|
+
*/
|
|
46
|
+
function parseEnvLine(line, key) {
|
|
47
|
+
const exportQuoted = line.match(new RegExp(`^export\\s+${key}="(.*)"\\s*$`))
|
|
48
|
+
if (exportQuoted) return unescapeEnvValue(exportQuoted[1])
|
|
49
|
+
const fishQuoted = line.match(new RegExp(`^set -gx\\s+${key}\\s+"(.*)"\\s*$`))
|
|
50
|
+
if (fishQuoted) return unescapeEnvValue(fishQuoted[1])
|
|
51
|
+
const exportBare = line.match(new RegExp(`^export\\s+${key}=(\\S+)\\s*$`))
|
|
52
|
+
if (exportBare) return exportBare[1]
|
|
53
|
+
const fishBare = line.match(new RegExp(`^set -gx\\s+${key}\\s+(\\S+)\\s*$`))
|
|
54
|
+
if (fishBare) return fishBare[1]
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 解析 profile 文件内容中的 wwkit 标记块,提取 opm 管理的环境变量
|
|
60
|
+
* @param {string} content
|
|
61
|
+
* @returns {{ key: string, value: string }[]}
|
|
62
|
+
*/
|
|
63
|
+
export function parseEnvBlocks(content) {
|
|
64
|
+
if (!content) return []
|
|
65
|
+
const lines = content.split('\n')
|
|
66
|
+
const result = []
|
|
67
|
+
let i = 0
|
|
68
|
+
while (i < lines.length) {
|
|
69
|
+
const marker = lines[i].match(/^# >>> wwkit (\S+) >>>/)
|
|
70
|
+
if (marker) {
|
|
71
|
+
const key = marker[1]
|
|
72
|
+
// 块体:标记行之后到下一个 wwkit 标记或 EOF 之间的非空非注释行
|
|
73
|
+
let j = i + 1
|
|
74
|
+
while (j < lines.length && lines[j].trim() === '') j++
|
|
75
|
+
if (j < lines.length && !lines[j].startsWith('# >>> wwkit')) {
|
|
76
|
+
const value = parseEnvLine(lines[j], key)
|
|
77
|
+
if (value !== null) result.push({ key, value })
|
|
78
|
+
}
|
|
79
|
+
i = j + 1
|
|
80
|
+
} else {
|
|
81
|
+
i++
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return result
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 解析 Windows `reg query` 输出,提取环境变量条目
|
|
89
|
+
* @param {string} output
|
|
90
|
+
* @returns {{ key: string, type: string, value: string }[]}
|
|
91
|
+
*/
|
|
92
|
+
export function parseRegOutput(output) {
|
|
93
|
+
if (!output) return []
|
|
94
|
+
const result = []
|
|
95
|
+
for (const line of output.split(/\r?\n/)) {
|
|
96
|
+
const m = line.match(/^\s+(\S+)\s+(REG_\S+)\s+(.*)$/)
|
|
97
|
+
if (m) result.push({ key: m[1], type: m[2], value: m[3] })
|
|
98
|
+
}
|
|
99
|
+
return result
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 校验环境变量名合法性(字母/下划线开头,后接字母数字下划线)
|
|
104
|
+
* @param {string} key
|
|
105
|
+
* @returns {boolean}
|
|
106
|
+
*/
|
|
107
|
+
export function isValidEnvKey(key) {
|
|
108
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export class EnvGroup {
|
|
112
|
+
/**
|
|
113
|
+
* @param {{ shell?: Shell, configDir?: string }} [opts] - shell/configDir 注入用于测试
|
|
114
|
+
*/
|
|
115
|
+
constructor(opts = {}) {
|
|
116
|
+
this.name = 'env'
|
|
117
|
+
this.desc = 'Manage environment variables (list/get/set) across platforms'
|
|
118
|
+
this.shell = opts.shell || new Shell()
|
|
119
|
+
this.configDir = opts.configDir || ''
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** @returns {string} manifest 文件绝对路径 */
|
|
123
|
+
_manifestPath() {
|
|
124
|
+
const dir = this.configDir || getUserConfigDir()
|
|
125
|
+
return path.join(dir, MANIFEST_FILE)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 读取 manifest 中的 opm 管理 key 集合(Windows 用)
|
|
130
|
+
* @returns {Set<string>}
|
|
131
|
+
*/
|
|
132
|
+
_readManifest() {
|
|
133
|
+
try {
|
|
134
|
+
const raw = fs.readFileSync(this._manifestPath(), 'utf8')
|
|
135
|
+
const data = JSON.parse(raw)
|
|
136
|
+
return new Set(Array.isArray(data.keys) ? data.keys : [])
|
|
137
|
+
} catch {
|
|
138
|
+
return new Set()
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 将 key 登记到 manifest(Windows set 时调用)
|
|
144
|
+
* @param {string} key
|
|
145
|
+
*/
|
|
146
|
+
_addManagedKey(key) {
|
|
147
|
+
const keys = this._readManifest()
|
|
148
|
+
keys.add(key)
|
|
149
|
+
const dir = this.configDir || getUserConfigDir()
|
|
150
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
151
|
+
fs.writeFileSync(this._manifestPath(), JSON.stringify({ keys: [...keys] }, null, 2), 'utf8')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 列出 opm 管理的环境变量(默认 list 的数据源)
|
|
156
|
+
* Unix: 解析所有 profile 的 wwkit 标记块
|
|
157
|
+
* Windows: 读 manifest → 查 user 级注册表当前值
|
|
158
|
+
* @returns {object[]}
|
|
159
|
+
*/
|
|
160
|
+
_managedItems() {
|
|
161
|
+
return this.shell.isWindows ? this._listWindowsManaged() : this._listUnix()
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
_listUnix() {
|
|
165
|
+
const items = []
|
|
166
|
+
for (const { path: profilePath, syntax } of this.shell.getProfiles()) {
|
|
167
|
+
let content = ''
|
|
168
|
+
try {
|
|
169
|
+
content = fs.readFileSync(profilePath, 'utf8')
|
|
170
|
+
} catch {}
|
|
171
|
+
for (const { key, value } of parseEnvBlocks(content)) {
|
|
172
|
+
items.push({ key, value, scope: 'user', source: profilePath, syntax })
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return items
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
_listWindowsManaged() {
|
|
179
|
+
const managedKeys = this._readManifest()
|
|
180
|
+
if (managedKeys.size === 0) return []
|
|
181
|
+
const userVars = this._regQuery(WIN_USER_KEY)
|
|
182
|
+
const byKey = new Map(userVars.map((v) => [v.key, v]))
|
|
183
|
+
const items = []
|
|
184
|
+
for (const key of managedKeys) {
|
|
185
|
+
const v = byKey.get(key)
|
|
186
|
+
if (v) {
|
|
187
|
+
items.push({ key, value: v.value, scope: 'user', source: 'registry (user)', type: v.type })
|
|
188
|
+
} else {
|
|
189
|
+
// manifest 登记过但注册表中已不存在(外部删除)——标注缺失
|
|
190
|
+
items.push({ key, value: null, scope: 'user', source: 'registry (user)', missing: true })
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return items
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
_regQuery(keyPath) {
|
|
197
|
+
try {
|
|
198
|
+
const result = spawnSync('reg', ['query', keyPath], {
|
|
199
|
+
encoding: 'utf8',
|
|
200
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
201
|
+
})
|
|
202
|
+
if (result.status !== 0) return []
|
|
203
|
+
return parseRegOutput(result.stdout || '')
|
|
204
|
+
} catch {
|
|
205
|
+
return []
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* --all:opm 管理的 ∪ process.env 全量,每条标注 managed
|
|
211
|
+
* @returns {object[]}
|
|
212
|
+
*/
|
|
213
|
+
_listAll() {
|
|
214
|
+
const managedItems = this._managedItems()
|
|
215
|
+
const seen = new Set()
|
|
216
|
+
const items = []
|
|
217
|
+
for (const m of managedItems) {
|
|
218
|
+
items.push({ key: m.key, value: m.value, managed: true, source: m.source, scope: m.scope })
|
|
219
|
+
seen.add(m.key)
|
|
220
|
+
}
|
|
221
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
222
|
+
if (!seen.has(key)) {
|
|
223
|
+
items.push({ key, value, managed: false, source: 'process.env' })
|
|
224
|
+
seen.add(key)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return items
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async run(argv) {
|
|
231
|
+
const [action, ...rest] = argv
|
|
232
|
+
|
|
233
|
+
if (!action || action === '-h' || action === '--help' || action === 'help') {
|
|
234
|
+
this.printHelp()
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const parsed = parseFlags(rest)
|
|
239
|
+
|
|
240
|
+
switch (action) {
|
|
241
|
+
case 'list':
|
|
242
|
+
return this._list(parsed)
|
|
243
|
+
case 'get':
|
|
244
|
+
return this._get(parsed)
|
|
245
|
+
case 'set':
|
|
246
|
+
return this._set(parsed)
|
|
247
|
+
case 'search':
|
|
248
|
+
return this._search(parsed)
|
|
249
|
+
default:
|
|
250
|
+
console.error(`Unknown action: ${action}`)
|
|
251
|
+
this.printHelp()
|
|
252
|
+
process.exit(1)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 是否输出 JSON(--json / -j)
|
|
258
|
+
* @param {{ flags: object, positional: string[] }} parsed
|
|
259
|
+
* @returns {boolean}
|
|
260
|
+
*/
|
|
261
|
+
_useJson(parsed) {
|
|
262
|
+
return parsed.flags.json === 'true' || parsed.flags.j === 'true'
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* 统一输出:--json 走 JSON,否则走 plain 格式化函数
|
|
267
|
+
* @param {{ flags: object, positional: string[] }} parsed
|
|
268
|
+
* @param {*} data - JSON 输出数据
|
|
269
|
+
* @param {() => string} plain - 简化字符串输出函数
|
|
270
|
+
*/
|
|
271
|
+
_out(parsed, data, plain) {
|
|
272
|
+
if (this._useJson(parsed)) {
|
|
273
|
+
output(data)
|
|
274
|
+
} else {
|
|
275
|
+
console.log(plain())
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* list 简化格式:按 source 分组,每组标题为来源,下面 key = value 行
|
|
281
|
+
* @param {object[]} items
|
|
282
|
+
* @returns {string}
|
|
283
|
+
*/
|
|
284
|
+
_formatListPlain(items) {
|
|
285
|
+
const groups = []
|
|
286
|
+
const groupMap = new Map()
|
|
287
|
+
for (const item of items) {
|
|
288
|
+
const source = item.source || 'unknown'
|
|
289
|
+
if (!groupMap.has(source)) {
|
|
290
|
+
const g = { source, entries: [] }
|
|
291
|
+
groupMap.set(source, g)
|
|
292
|
+
groups.push(g)
|
|
293
|
+
}
|
|
294
|
+
groupMap.get(source).entries.push(item)
|
|
295
|
+
}
|
|
296
|
+
return groups
|
|
297
|
+
.map((g) => [
|
|
298
|
+
g.source,
|
|
299
|
+
...g.entries.map((e) => `${e.key} = ${e.value === null || e.value === undefined ? '(not set)' : e.value}`),
|
|
300
|
+
].join('\n'))
|
|
301
|
+
.join('\n\n')
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* 列出环境变量
|
|
306
|
+
* 默认: 只显示 opm 管理的(Unix 标记块 / Windows manifest)
|
|
307
|
+
* --all: 追加 process.env 全量,每条标注 managed: true/false
|
|
308
|
+
* 输出: 默认简化文本(按 source 分组),--json 输出 JSON
|
|
309
|
+
* @param {{ flags: object, positional: string[] }} parsed
|
|
310
|
+
*/
|
|
311
|
+
async _list(parsed) {
|
|
312
|
+
const all = parsed.flags.all === 'true' || parsed.flags.a === 'true'
|
|
313
|
+
const items = all ? this._listAll() : this._managedItems()
|
|
314
|
+
this._out(parsed, items, () => this._formatListPlain(items))
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* 根据 key 取值:先查 opm 管理的配置源,回退 process.env
|
|
319
|
+
* 输出: 默认 `key = value`(未找到 `key = (not found)`),--json 输出 JSON
|
|
320
|
+
* @param {{ flags: object, positional: string[] }} parsed
|
|
321
|
+
*/
|
|
322
|
+
async _get(parsed) {
|
|
323
|
+
const key = parsed.positional[0] || parsed.flags.k || parsed.flags.key || ''
|
|
324
|
+
if (!key) {
|
|
325
|
+
console.error('Usage: opm env get <key>')
|
|
326
|
+
console.error('Missing required <key>')
|
|
327
|
+
process.exit(1)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const configured = this._managedItems()
|
|
331
|
+
const hit = configured.find((e) => e.key === key)
|
|
332
|
+
if (hit) {
|
|
333
|
+
this._out(parsed, { key, value: hit.value, found: true, source: hit.source, scope: hit.scope }, () => `${key} = ${hit.value}`)
|
|
334
|
+
return
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (process.env[key] !== undefined) {
|
|
338
|
+
const value = process.env[key]
|
|
339
|
+
this._out(parsed, { key, value, found: true, source: 'process.env' }, () => `${key} = ${value}`)
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
this._out(parsed, { key, value: null, found: false }, () => `${key} = (not found)`)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* 设置环境变量(替换已存在的)
|
|
348
|
+
* Unix: 写入 shell profile 标记块;Windows: setx 写 user 级注册表 + manifest 登记
|
|
349
|
+
* @param {{ flags: object, positional: string[] }} parsed
|
|
350
|
+
*/
|
|
351
|
+
async _set(parsed) {
|
|
352
|
+
const key = parsed.positional[0] || parsed.flags.k || parsed.flags.key || ''
|
|
353
|
+
const value = parsed.positional[1] ?? parsed.flags.v ?? parsed.flags.value ?? ''
|
|
354
|
+
|
|
355
|
+
if (!key) {
|
|
356
|
+
console.error('Usage: opm env set <key> <value>')
|
|
357
|
+
console.error('Missing required <key>')
|
|
358
|
+
process.exit(1)
|
|
359
|
+
}
|
|
360
|
+
if (!isValidEnvKey(key)) {
|
|
361
|
+
console.error(`Invalid env key: "${key}" (must start with letter/underscore, only letters/digits/underscore)`)
|
|
362
|
+
process.exit(1)
|
|
363
|
+
}
|
|
364
|
+
if (parsed.positional.length < 2 && !(parsed.flags.v || parsed.flags.value)) {
|
|
365
|
+
console.error('Usage: opm env set <key> <value>')
|
|
366
|
+
console.error('Missing required <value>')
|
|
367
|
+
process.exit(1)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const written = this.shell.setEnvVar(key, value)
|
|
371
|
+
if (this.shell.isWindows) {
|
|
372
|
+
this._addManagedKey(key)
|
|
373
|
+
}
|
|
374
|
+
const source = this.shell.isWindows
|
|
375
|
+
? 'registry (user)'
|
|
376
|
+
: this.shell.getProfiles().map((p) => p.path)
|
|
377
|
+
const result = { key, value, written, source }
|
|
378
|
+
this._out(parsed, result, () => `${key} = ${value}${written ? '' : ' (no change)'}`)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* 按关键词搜索环境变量(匹配 key 或 value,忽略大小写)
|
|
383
|
+
* 默认: 在 opm 管理的变量中搜索;--all 扩展至 process.env 全量
|
|
384
|
+
* 输出: 默认简化文本(按 source 分组),--json 输出 JSON
|
|
385
|
+
* @param {{ flags: object, positional: string[] }} parsed
|
|
386
|
+
*/
|
|
387
|
+
async _search(parsed) {
|
|
388
|
+
const keyword = parsed.positional[0] || parsed.flags.q || parsed.flags.query || ''
|
|
389
|
+
if (!keyword) {
|
|
390
|
+
console.error('Usage: opm env search <keyword>')
|
|
391
|
+
console.error('Missing required <keyword>')
|
|
392
|
+
process.exit(1)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const all = parsed.flags.all === 'true' || parsed.flags.a === 'true'
|
|
396
|
+
const source = all ? this._listAll() : this._managedItems()
|
|
397
|
+
const kw = keyword.toLowerCase()
|
|
398
|
+
const matches = source.filter((e) => {
|
|
399
|
+
const k = String(e.key).toLowerCase()
|
|
400
|
+
const v = e.value === null || e.value === undefined ? '' : String(e.value).toLowerCase()
|
|
401
|
+
return k.includes(kw) || v.includes(kw)
|
|
402
|
+
})
|
|
403
|
+
this._out(parsed, matches, () => this._formatListPlain(matches))
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
printHelp() {
|
|
407
|
+
console.log(`
|
|
408
|
+
Usage: opm env <action> [args] [options]
|
|
409
|
+
|
|
410
|
+
Actions:
|
|
411
|
+
list [--all] List opm-managed env vars with their config file path
|
|
412
|
+
(default: only vars set via "opm env set";
|
|
413
|
+
--all appends all process.env vars, each tagged managed: true/false)
|
|
414
|
+
get <key> Get value by key (checks opm-managed sources first, then process.env)
|
|
415
|
+
set <key> <value> Set value, replacing any existing one
|
|
416
|
+
(Unix: shell profile marker block; Windows: setx user-level registry)
|
|
417
|
+
search <keyword> [--all] Search env vars whose key or value contains keyword (case-insensitive)
|
|
418
|
+
(default: opm-managed vars; --all also searches process.env)
|
|
419
|
+
help Show this help
|
|
420
|
+
|
|
421
|
+
Options:
|
|
422
|
+
-k, --key <name> Key (alternative to positional for get/set)
|
|
423
|
+
-v, --value <val> Value (alternative to positional for set)
|
|
424
|
+
-q, --query <kw> Keyword (alternative to positional for search)
|
|
425
|
+
-a, --all Also list all process.env vars (tagged managed: true/false)
|
|
426
|
+
-j, --json Output JSON (default: plain text)
|
|
427
|
+
-h, --help Show this help
|
|
428
|
+
|
|
429
|
+
Output:
|
|
430
|
+
default (no -j) Plain text: list groups by source then "key = value" lines;
|
|
431
|
+
get/set print a single "key = value" line
|
|
432
|
+
-j / --json JSON object/array with full metadata
|
|
433
|
+
|
|
434
|
+
Platform behavior:
|
|
435
|
+
Unix read/write shell profiles (.zshrc/.bashrc/.profile/fish config)
|
|
436
|
+
via "# >>> wwkit NAME >>>" marker blocks
|
|
437
|
+
Windows read/write user-level registry (HKCU\\Environment); writes via setx;
|
|
438
|
+
opm-managed keys tracked in ~/.config/opm/env-managed.json
|
|
439
|
+
|
|
440
|
+
Examples:
|
|
441
|
+
opm env list
|
|
442
|
+
opm env list --all # all process.env vars, tagged managed
|
|
443
|
+
opm env get PATH
|
|
444
|
+
opm env set FOO bar
|
|
445
|
+
opm env set MY_API_KEY secret123
|
|
446
|
+
opm env search api # match key or value containing "api" (case-insensitive)
|
|
447
|
+
opm env search api --all # also search process.env
|
|
448
|
+
opm env help
|
|
449
|
+
`)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
@@ -57,7 +57,6 @@ const ACTIONS = {
|
|
|
57
57
|
view: { desc: 'Check package in global and project scopes' },
|
|
58
58
|
info: { desc: 'Show package details' },
|
|
59
59
|
versions: { desc: 'List all available versions of a package' },
|
|
60
|
-
outdated: { desc: 'List outdated packages' },
|
|
61
60
|
install: { desc: 'Install a package' },
|
|
62
61
|
uninstall: { desc: 'Uninstall a package' },
|
|
63
62
|
upgrade: { desc: 'Upgrade a package' },
|
|
@@ -99,6 +98,26 @@ export class PackageGroup {
|
|
|
99
98
|
await this._dispatch(manager, action, parsed, { global, proxy })
|
|
100
99
|
}
|
|
101
100
|
|
|
101
|
+
/**
|
|
102
|
+
* 打印当前 registry 和 proxy(诊断信息,输出到 stderr)
|
|
103
|
+
* 在涉及网络请求的命令执行前调用,便于连接失败时定位。
|
|
104
|
+
* 输出到 stderr 不污染 JSON stdout。
|
|
105
|
+
* @param {PackageManager} manager
|
|
106
|
+
* @param {string} [proxy]
|
|
107
|
+
* @private
|
|
108
|
+
*/
|
|
109
|
+
async _printEndpoint(manager, proxy) {
|
|
110
|
+
try {
|
|
111
|
+
const { registry } = await manager.getRegistry()
|
|
112
|
+
if (registry) {
|
|
113
|
+
console.error(`[opm] registry: ${registry}`)
|
|
114
|
+
}
|
|
115
|
+
} catch {}
|
|
116
|
+
if (proxy) {
|
|
117
|
+
console.error(`[opm] proxy: ${proxy}`)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
102
121
|
async _dispatch(manager, action, parsed, opts) {
|
|
103
122
|
const { positional } = parsed
|
|
104
123
|
|
|
@@ -152,6 +171,7 @@ export class PackageGroup {
|
|
|
152
171
|
console.error(`Usage: opm ${this.name} search <name> [version]`)
|
|
153
172
|
process.exit(1)
|
|
154
173
|
}
|
|
174
|
+
await this._printEndpoint(manager, opts.proxy)
|
|
155
175
|
const result = await manager.searchPackage(name, version, { proxy: opts.proxy })
|
|
156
176
|
output(result)
|
|
157
177
|
break
|
|
@@ -174,6 +194,7 @@ export class PackageGroup {
|
|
|
174
194
|
console.error(`Usage: opm ${this.name} info <name>`)
|
|
175
195
|
process.exit(1)
|
|
176
196
|
}
|
|
197
|
+
await this._printEndpoint(manager, opts.proxy)
|
|
177
198
|
const result = await manager.packageInfo(name, { proxy: opts.proxy })
|
|
178
199
|
output(result)
|
|
179
200
|
break
|
|
@@ -185,17 +206,12 @@ export class PackageGroup {
|
|
|
185
206
|
console.error(`Usage: opm ${this.name} versions <name>`)
|
|
186
207
|
process.exit(1)
|
|
187
208
|
}
|
|
209
|
+
await this._printEndpoint(manager, opts.proxy)
|
|
188
210
|
const result = await manager.packageVersions(name, { proxy: opts.proxy })
|
|
189
211
|
output(result)
|
|
190
212
|
break
|
|
191
213
|
}
|
|
192
214
|
|
|
193
|
-
case 'outdated': {
|
|
194
|
-
const result = await manager.listOutdated({ global: !!opts.global, proxy: opts.proxy })
|
|
195
|
-
output(result)
|
|
196
|
-
break
|
|
197
|
-
}
|
|
198
|
-
|
|
199
215
|
case 'install': {
|
|
200
216
|
const name = positional[0]
|
|
201
217
|
const version = positional[1]
|
|
@@ -203,6 +219,7 @@ export class PackageGroup {
|
|
|
203
219
|
console.error(`Usage: opm ${this.name} install <name> [version]`)
|
|
204
220
|
process.exit(1)
|
|
205
221
|
}
|
|
222
|
+
await this._printEndpoint(manager, opts.proxy)
|
|
206
223
|
const result = await manager.install(name, version, { global: !!opts.global, proxy: opts.proxy })
|
|
207
224
|
output(result)
|
|
208
225
|
break
|
|
@@ -225,6 +242,7 @@ export class PackageGroup {
|
|
|
225
242
|
console.error(`Usage: opm ${this.name} upgrade <name>`)
|
|
226
243
|
process.exit(1)
|
|
227
244
|
}
|
|
245
|
+
await this._printEndpoint(manager, opts.proxy)
|
|
228
246
|
const result = await manager.upgrade(name, { global: !!opts.global, proxy: opts.proxy })
|
|
229
247
|
output(result)
|
|
230
248
|
break
|
|
@@ -266,7 +284,6 @@ Actions:
|
|
|
266
284
|
view <name> Show package in global and project scopes
|
|
267
285
|
info <name> Show package details
|
|
268
286
|
versions <name> List all available versions
|
|
269
|
-
outdated [-g] List outdated packages
|
|
270
287
|
install <name> [version] Install a package
|
|
271
288
|
uninstall <name> Uninstall a package
|
|
272
289
|
upgrade <name> Upgrade a package to latest
|
|
@@ -276,7 +293,7 @@ Actions:
|
|
|
276
293
|
|
|
277
294
|
Options:
|
|
278
295
|
-g, --global Global operation (npm: global node_modules; pip: system Python)
|
|
279
|
-
-p, --proxy <url> Proxy address (search/info/versions/
|
|
296
|
+
-p, --proxy <url> Proxy address (search/info/versions/install/upgrade);
|
|
280
297
|
overrides config proxy.active when provided
|
|
281
298
|
-h, --help Show this help
|
|
282
299
|
|
|
@@ -287,7 +304,6 @@ Examples:
|
|
|
287
304
|
opm ${this.name} view <name>
|
|
288
305
|
opm ${this.name} info <name>
|
|
289
306
|
opm ${this.name} versions <name>
|
|
290
|
-
opm ${this.name} outdated
|
|
291
307
|
opm ${this.name} install <name>
|
|
292
308
|
opm ${this.name} uninstall <name>
|
|
293
309
|
opm ${this.name} upgrade <name>
|
|
@@ -438,12 +454,14 @@ export class RuntimeGroup extends PackageGroup {
|
|
|
438
454
|
console.error(`Usage: opm ${this.name} install <version>`)
|
|
439
455
|
process.exit(1)
|
|
440
456
|
}
|
|
457
|
+
await this._printEndpoint(manager, opts.proxy)
|
|
441
458
|
const result = await manager.install(this.name, version, { proxy: opts.proxy })
|
|
442
459
|
output(result)
|
|
443
460
|
break
|
|
444
461
|
}
|
|
445
462
|
|
|
446
463
|
case 'upgrade': {
|
|
464
|
+
await this._printEndpoint(manager, opts.proxy)
|
|
447
465
|
const result = await manager.upgrade(this.name, { proxy: opts.proxy })
|
|
448
466
|
output(result)
|
|
449
467
|
break
|
|
@@ -455,12 +473,14 @@ export class RuntimeGroup extends PackageGroup {
|
|
|
455
473
|
console.error(`Usage: opm ${this.name} search <version>`)
|
|
456
474
|
process.exit(1)
|
|
457
475
|
}
|
|
476
|
+
await this._printEndpoint(manager, opts.proxy)
|
|
458
477
|
const result = await manager.searchPackage(this.name, version, { proxy: opts.proxy })
|
|
459
478
|
output(result)
|
|
460
479
|
break
|
|
461
480
|
}
|
|
462
481
|
|
|
463
482
|
case 'versions': {
|
|
483
|
+
await this._printEndpoint(manager, opts.proxy)
|
|
464
484
|
const result = await manager.packageVersions(this.name, { proxy: opts.proxy })
|
|
465
485
|
output(result)
|
|
466
486
|
break
|