@zincapp/znvault-cli 2.11.0 → 2.12.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/dist/commands/completion.d.ts.map +1 -1
- package/dist/commands/completion.js +35 -1
- package/dist/commands/completion.js.map +1 -1
- package/dist/commands/plugin.d.ts +8 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js +548 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/index.js +30 -8
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +21 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +59 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/plugins/index.d.ts +3 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +5 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/loader.d.ts +63 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +221 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/types.d.ts +69 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +4 -0
- package/dist/plugins/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/commands/completion.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/commands/completion.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAqWzC,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyCjE"}
|
|
@@ -13,7 +13,7 @@ _znvault_completions() {
|
|
|
13
13
|
_init_completion || return
|
|
14
14
|
|
|
15
15
|
# Top-level commands
|
|
16
|
-
local commands="login logout whoami config profile health status cluster tenant user superadmin lockdown audit emergency cert agent update apikey policy permissions secret kms role backup notification tui dashboard self-update advisor completion version help"
|
|
16
|
+
local commands="login logout whoami config profile health status cluster tenant user superadmin lockdown audit emergency cert agent update apikey policy permissions secret kms role backup notification tui dashboard self-update advisor crypto device unseal completion version help"
|
|
17
17
|
|
|
18
18
|
# Subcommands for each command group
|
|
19
19
|
local cluster_cmds="status takeover release promote maintenance"
|
|
@@ -35,6 +35,9 @@ _znvault_completions() {
|
|
|
35
35
|
local advisor_cmds="audit rules suggest llm"
|
|
36
36
|
local advisor_llm_cmds="status get config test delete"
|
|
37
37
|
local profile_cmds="list create delete use rename"
|
|
38
|
+
local crypto_cmds="status list grant revoke transfer-root"
|
|
39
|
+
local device_cmds="list revoke enroll"
|
|
40
|
+
local unseal_cmds="status seal"
|
|
38
41
|
local completion_cmds="bash zsh"
|
|
39
42
|
|
|
40
43
|
case "\${cword}" in
|
|
@@ -60,6 +63,9 @@ _znvault_completions() {
|
|
|
60
63
|
notification) COMPREPLY=( \$(compgen -W "\${notification_cmds}" -- "\${cur}") ) ;;
|
|
61
64
|
advisor) COMPREPLY=( \$(compgen -W "\${advisor_cmds}" -- "\${cur}") ) ;;
|
|
62
65
|
profile) COMPREPLY=( \$(compgen -W "\${profile_cmds}" -- "\${cur}") ) ;;
|
|
66
|
+
crypto) COMPREPLY=( \$(compgen -W "\${crypto_cmds}" -- "\${cur}") ) ;;
|
|
67
|
+
device) COMPREPLY=( \$(compgen -W "\${device_cmds}" -- "\${cur}") ) ;;
|
|
68
|
+
unseal) COMPREPLY=( \$(compgen -W "\${unseal_cmds}" -- "\${cur}") ) ;;
|
|
63
69
|
completion) COMPREPLY=( \$(compgen -W "\${completion_cmds}" -- "\${cur}") ) ;;
|
|
64
70
|
*) ;;
|
|
65
71
|
esac
|
|
@@ -178,6 +184,9 @@ _znvault() {
|
|
|
178
184
|
'dashboard:Launch interactive dashboard'
|
|
179
185
|
'self-update:Update znvault CLI'
|
|
180
186
|
'advisor:AI-powered security advisor'
|
|
187
|
+
'crypto:Manage admin crypto access grants'
|
|
188
|
+
'device:Manage enrolled devices'
|
|
189
|
+
'unseal:Unseal the vault for crypto operations'
|
|
181
190
|
'completion:Generate shell completion scripts'
|
|
182
191
|
'version:Show version'
|
|
183
192
|
'help:Display help'
|
|
@@ -300,6 +309,31 @@ _znvault() {
|
|
|
300
309
|
fi
|
|
301
310
|
_describe -t subcommands 'subcommand' subcommands
|
|
302
311
|
;;
|
|
312
|
+
crypto)
|
|
313
|
+
subcommands=(
|
|
314
|
+
'status:Show crypto mode status'
|
|
315
|
+
'list:List crypto grants'
|
|
316
|
+
'grant:Grant crypto access to an admin'
|
|
317
|
+
'revoke:Revoke crypto access from an admin'
|
|
318
|
+
'transfer-root:Transfer tenant root to another user'
|
|
319
|
+
)
|
|
320
|
+
_describe -t subcommands 'subcommand' subcommands
|
|
321
|
+
;;
|
|
322
|
+
device)
|
|
323
|
+
subcommands=(
|
|
324
|
+
'list:List enrolled devices'
|
|
325
|
+
'revoke:Revoke a device'
|
|
326
|
+
'enroll:Show enrollment instructions'
|
|
327
|
+
)
|
|
328
|
+
_describe -t subcommands 'subcommand' subcommands
|
|
329
|
+
;;
|
|
330
|
+
unseal)
|
|
331
|
+
subcommands=(
|
|
332
|
+
'status:Check unseal status'
|
|
333
|
+
'seal:Seal the vault'
|
|
334
|
+
)
|
|
335
|
+
_describe -t subcommands 'subcommand' subcommands
|
|
336
|
+
;;
|
|
303
337
|
completion)
|
|
304
338
|
subcommands=(
|
|
305
339
|
'bash:Generate bash completion'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completion.js","sourceRoot":"","sources":["../../src/commands/completion.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAG/C,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAE3C;;GAEG;AACH,SAAS,sBAAsB;IAC7B,OAAO
|
|
1
|
+
{"version":3,"file":"completion.js","sourceRoot":"","sources":["../../src/commands/completion.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAG/C,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAE3C;;GAEG;AACH,SAAS,sBAAsB;IAC7B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiHR,CAAC;AACF,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB;IAC5B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmOR,CAAC;AACF,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,OAAgB;IACzD,MAAM,UAAU,GAAG,OAAO;SACvB,OAAO,CAAC,YAAY,CAAC;SACrB,WAAW,CAAC,mCAAmC,CAAC,CAAC;IAEpD,UAAU;SACP,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,iCAAiC,CAAC;SAC9C,MAAM,CAAC,GAAG,EAAE;QACX,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEL,UAAU;SACP,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,gCAAgC,CAAC;SAC7C,MAAM,CAAC,GAAG,EAAE;QACX,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEL,iDAAiD;IACjD,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE;QACrB,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;QACzC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QAC3D,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;QAC5E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,iFAAiF,CAAC,CAAC;QAC/F,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC,+DAA+D,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Management Commands
|
|
3
|
+
*
|
|
4
|
+
* Commands for installing, uninstalling, and managing CLI plugins.
|
|
5
|
+
*/
|
|
6
|
+
import { type Command } from 'commander';
|
|
7
|
+
export declare function registerPluginCommands(program: Command): void;
|
|
8
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/commands/plugin.ts"],"names":[],"mappings":"AACA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AA2MzC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAic7D"}
|
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
// Path: znvault-cli/src/commands/plugin.ts
|
|
2
|
+
/**
|
|
3
|
+
* Plugin Management Commands
|
|
4
|
+
*
|
|
5
|
+
* Commands for installing, uninstalling, and managing CLI plugins.
|
|
6
|
+
*/
|
|
7
|
+
import { execSync, spawn } from 'node:child_process';
|
|
8
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
|
9
|
+
import { join, dirname } from 'node:path';
|
|
10
|
+
import ora from 'ora';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import * as output from '../lib/output.js';
|
|
13
|
+
import { getPlugins, addPlugin, removePlugin, setPluginEnabled, getConfigPath, } from '../lib/config.js';
|
|
14
|
+
// ZincApp plugin namespace
|
|
15
|
+
const ZINCAPP_PREFIX = '@zincapp/znvault-plugin-';
|
|
16
|
+
/**
|
|
17
|
+
* Get the plugins directory path (alongside config.json)
|
|
18
|
+
*/
|
|
19
|
+
function getPluginsDir() {
|
|
20
|
+
const configPath = getConfigPath();
|
|
21
|
+
return join(dirname(configPath), 'plugins');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Ensure plugins directory exists with package.json
|
|
25
|
+
*/
|
|
26
|
+
function ensurePluginsDir() {
|
|
27
|
+
const pluginsDir = getPluginsDir();
|
|
28
|
+
if (!existsSync(pluginsDir)) {
|
|
29
|
+
mkdirSync(pluginsDir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
const packageJsonPath = join(pluginsDir, 'package.json');
|
|
32
|
+
if (!existsSync(packageJsonPath)) {
|
|
33
|
+
writeFileSync(packageJsonPath, JSON.stringify({
|
|
34
|
+
name: 'znvault-plugins',
|
|
35
|
+
version: '1.0.0',
|
|
36
|
+
private: true,
|
|
37
|
+
description: 'ZNVault CLI plugins',
|
|
38
|
+
type: 'module',
|
|
39
|
+
}, null, 2));
|
|
40
|
+
}
|
|
41
|
+
return pluginsDir;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve plugin name to full package name
|
|
45
|
+
* - "payara" -> "@zincapp/znvault-plugin-payara"
|
|
46
|
+
* - "@zincapp/znvault-plugin-payara" -> "@zincapp/znvault-plugin-payara"
|
|
47
|
+
* - "@other/plugin" -> "@other/plugin"
|
|
48
|
+
*/
|
|
49
|
+
function resolvePluginName(name) {
|
|
50
|
+
// Already a scoped or full package name
|
|
51
|
+
if (name.startsWith('@') || name.includes('/')) {
|
|
52
|
+
return name;
|
|
53
|
+
}
|
|
54
|
+
// Check if it's a simple name, prepend ZincApp prefix
|
|
55
|
+
return `${ZINCAPP_PREFIX}${name}`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get short name from full package name
|
|
59
|
+
*/
|
|
60
|
+
function getShortName(packageName) {
|
|
61
|
+
if (packageName.startsWith(ZINCAPP_PREFIX)) {
|
|
62
|
+
return packageName.slice(ZINCAPP_PREFIX.length);
|
|
63
|
+
}
|
|
64
|
+
return packageName;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if a package exists on npm
|
|
68
|
+
*/
|
|
69
|
+
async function packageExists(packageName) {
|
|
70
|
+
try {
|
|
71
|
+
execSync(`npm view ${packageName} version`, { stdio: 'pipe' });
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get package version from npm
|
|
80
|
+
*/
|
|
81
|
+
function getPackageVersion(packageName) {
|
|
82
|
+
try {
|
|
83
|
+
return execSync(`npm view ${packageName} version`, { stdio: 'pipe' }).toString().trim();
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get installed package version
|
|
91
|
+
*/
|
|
92
|
+
function getInstalledVersion(packageName, pluginsDir) {
|
|
93
|
+
try {
|
|
94
|
+
const packageJsonPath = join(pluginsDir, 'node_modules', packageName, 'package.json');
|
|
95
|
+
if (existsSync(packageJsonPath)) {
|
|
96
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
97
|
+
return pkg.version;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Ignore
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Run npm command in plugins directory
|
|
107
|
+
*/
|
|
108
|
+
function runNpm(args, pluginsDir) {
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
const npm = spawn('npm', args, {
|
|
111
|
+
cwd: pluginsDir,
|
|
112
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
113
|
+
});
|
|
114
|
+
let stdout = '';
|
|
115
|
+
let stderr = '';
|
|
116
|
+
npm.stdout.on('data', (data) => {
|
|
117
|
+
stdout += data.toString();
|
|
118
|
+
});
|
|
119
|
+
npm.stderr.on('data', (data) => {
|
|
120
|
+
stderr += data.toString();
|
|
121
|
+
});
|
|
122
|
+
npm.on('close', (code) => {
|
|
123
|
+
resolve({
|
|
124
|
+
success: code === 0,
|
|
125
|
+
output: stdout + stderr,
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
npm.on('error', (err) => {
|
|
129
|
+
resolve({
|
|
130
|
+
success: false,
|
|
131
|
+
output: err.message,
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Validate that a package is a valid znvault CLI plugin
|
|
138
|
+
*/
|
|
139
|
+
async function validatePlugin(packageName, pluginsDir) {
|
|
140
|
+
try {
|
|
141
|
+
const packageJsonPath = join(pluginsDir, 'node_modules', packageName, 'package.json');
|
|
142
|
+
if (!existsSync(packageJsonPath)) {
|
|
143
|
+
return { valid: false, error: 'Package not found after installation' };
|
|
144
|
+
}
|
|
145
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
146
|
+
// Check for CLI plugin export
|
|
147
|
+
const hasCliExport = pkg.exports?.['./cli'] || pkg.main;
|
|
148
|
+
if (!hasCliExport) {
|
|
149
|
+
return { valid: false, error: 'Package does not export a CLI plugin' };
|
|
150
|
+
}
|
|
151
|
+
// Try to dynamically import and validate
|
|
152
|
+
try {
|
|
153
|
+
const modulePath = join(pluginsDir, 'node_modules', packageName);
|
|
154
|
+
const cliPath = pkg.exports?.['./cli']?.import || pkg.exports?.['./cli'] || './dist/cli.js';
|
|
155
|
+
const fullPath = join(modulePath, cliPath);
|
|
156
|
+
// Check if the CLI module exists
|
|
157
|
+
if (!existsSync(fullPath.replace(/\.js$/, '.js')) && !existsSync(fullPath)) {
|
|
158
|
+
// Try without ./cli export - main export might have createPayaraCLIPlugin
|
|
159
|
+
const mainPath = join(modulePath, pkg.exports?.['.']?.import || pkg.main || 'dist/index.js');
|
|
160
|
+
if (!existsSync(mainPath)) {
|
|
161
|
+
return { valid: false, error: 'CLI module not found' };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return { valid: true };
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
return { valid: false, error: `Failed to validate plugin: ${err}` };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
return { valid: false, error: `Validation error: ${err}` };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export function registerPluginCommands(program) {
|
|
175
|
+
const plugin = program
|
|
176
|
+
.command('plugin')
|
|
177
|
+
.description('Manage CLI plugins');
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// plugin install <name>
|
|
180
|
+
// ============================================================================
|
|
181
|
+
plugin
|
|
182
|
+
.command('install <name>')
|
|
183
|
+
.alias('add')
|
|
184
|
+
.description('Install a CLI plugin')
|
|
185
|
+
.option('-f, --force', 'Force reinstall even if already installed')
|
|
186
|
+
.option('-g, --global', 'Install globally instead of in plugins directory')
|
|
187
|
+
.action(async (name, options) => {
|
|
188
|
+
const spinner = ora('Resolving plugin...').start();
|
|
189
|
+
try {
|
|
190
|
+
// Resolve plugin name
|
|
191
|
+
let packageName = resolvePluginName(name);
|
|
192
|
+
let foundWithPrefix = true;
|
|
193
|
+
// Check if @zincapp prefixed version exists
|
|
194
|
+
if (packageName.startsWith(ZINCAPP_PREFIX)) {
|
|
195
|
+
const exists = await packageExists(packageName);
|
|
196
|
+
if (!exists) {
|
|
197
|
+
// Try the original name directly
|
|
198
|
+
const directExists = await packageExists(name);
|
|
199
|
+
if (directExists) {
|
|
200
|
+
packageName = name;
|
|
201
|
+
foundWithPrefix = false;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
spinner.fail(`Plugin not found: ${packageName}`);
|
|
205
|
+
output.error(`Could not find '${packageName}' or '${name}' on npm.`);
|
|
206
|
+
console.log();
|
|
207
|
+
console.log('Available ZincApp plugins:');
|
|
208
|
+
console.log(` ${chalk.cyan('payara')} - Payara WAR deployment`);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
spinner.text = `Installing ${packageName}...`;
|
|
214
|
+
// Check if already configured
|
|
215
|
+
const existingPlugins = getPlugins();
|
|
216
|
+
const alreadyConfigured = existingPlugins.some(p => p.package === packageName);
|
|
217
|
+
if (alreadyConfigured && !options.force) {
|
|
218
|
+
spinner.info(`Plugin ${chalk.cyan(getShortName(packageName))} is already installed.`);
|
|
219
|
+
console.log(`Use ${chalk.cyan('--force')} to reinstall.`);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
// Ensure plugins directory
|
|
223
|
+
const pluginsDir = ensurePluginsDir();
|
|
224
|
+
// Install package
|
|
225
|
+
spinner.text = `Installing ${packageName}...`;
|
|
226
|
+
const installResult = await runNpm(['install', packageName], pluginsDir);
|
|
227
|
+
if (!installResult.success) {
|
|
228
|
+
spinner.fail(`Failed to install ${packageName}`);
|
|
229
|
+
output.error(installResult.output);
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
// Validate it's a valid plugin
|
|
233
|
+
spinner.text = 'Validating plugin...';
|
|
234
|
+
const validation = await validatePlugin(packageName, pluginsDir);
|
|
235
|
+
if (!validation.valid) {
|
|
236
|
+
spinner.fail(`Invalid plugin: ${validation.error}`);
|
|
237
|
+
// Uninstall the invalid package
|
|
238
|
+
await runNpm(['uninstall', packageName], pluginsDir);
|
|
239
|
+
output.error('The package was installed but is not a valid znvault CLI plugin.');
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
// Add to config
|
|
243
|
+
addPlugin({ package: packageName, enabled: true });
|
|
244
|
+
const version = getInstalledVersion(packageName, pluginsDir);
|
|
245
|
+
spinner.succeed(`Installed ${chalk.cyan(getShortName(packageName))}${version ? ` v${version}` : ''}`);
|
|
246
|
+
console.log();
|
|
247
|
+
console.log(chalk.dim('Plugin will be loaded on next command execution.'));
|
|
248
|
+
if (foundWithPrefix && name !== packageName) {
|
|
249
|
+
console.log(chalk.dim(`Resolved '${name}' to '${packageName}'`));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
spinner.fail('Installation failed');
|
|
254
|
+
output.error(err instanceof Error ? err.message : String(err));
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
// ============================================================================
|
|
259
|
+
// plugin uninstall <name>
|
|
260
|
+
// ============================================================================
|
|
261
|
+
plugin
|
|
262
|
+
.command('uninstall <name>')
|
|
263
|
+
.alias('remove')
|
|
264
|
+
.description('Uninstall a CLI plugin')
|
|
265
|
+
.action(async (name) => {
|
|
266
|
+
const spinner = ora('Uninstalling plugin...').start();
|
|
267
|
+
try {
|
|
268
|
+
// Resolve plugin name
|
|
269
|
+
const packageName = resolvePluginName(name);
|
|
270
|
+
// Check if configured
|
|
271
|
+
const plugins = getPlugins();
|
|
272
|
+
const found = plugins.find(p => p.package === packageName ||
|
|
273
|
+
p.package === name ||
|
|
274
|
+
getShortName(p.package ?? '') === name);
|
|
275
|
+
if (!found) {
|
|
276
|
+
spinner.fail(`Plugin not found: ${name}`);
|
|
277
|
+
output.error('Plugin is not installed. Use "znvault plugin list" to see installed plugins.');
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
const actualPackage = found.package;
|
|
281
|
+
// Remove from plugins directory
|
|
282
|
+
const pluginsDir = getPluginsDir();
|
|
283
|
+
if (existsSync(join(pluginsDir, 'node_modules', actualPackage))) {
|
|
284
|
+
spinner.text = `Removing ${actualPackage}...`;
|
|
285
|
+
const uninstallResult = await runNpm(['uninstall', actualPackage], pluginsDir);
|
|
286
|
+
if (!uninstallResult.success) {
|
|
287
|
+
spinner.warn('Failed to uninstall npm package, but removing from config...');
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Remove from config
|
|
291
|
+
removePlugin(actualPackage);
|
|
292
|
+
spinner.succeed(`Uninstalled ${chalk.cyan(getShortName(actualPackage))}`);
|
|
293
|
+
}
|
|
294
|
+
catch (err) {
|
|
295
|
+
spinner.fail('Uninstall failed');
|
|
296
|
+
output.error(err instanceof Error ? err.message : String(err));
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
// ============================================================================
|
|
301
|
+
// plugin list
|
|
302
|
+
// ============================================================================
|
|
303
|
+
plugin
|
|
304
|
+
.command('list')
|
|
305
|
+
.alias('ls')
|
|
306
|
+
.description('List installed plugins')
|
|
307
|
+
.option('--json', 'Output as JSON')
|
|
308
|
+
.action((options) => {
|
|
309
|
+
const plugins = getPlugins();
|
|
310
|
+
const pluginsDir = getPluginsDir();
|
|
311
|
+
if (plugins.length === 0) {
|
|
312
|
+
if (options.json) {
|
|
313
|
+
console.log(JSON.stringify([], null, 2));
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
console.log(chalk.dim('No plugins installed.'));
|
|
317
|
+
console.log();
|
|
318
|
+
console.log(`Install a plugin with: ${chalk.cyan('znvault plugin install <name>')}`);
|
|
319
|
+
console.log();
|
|
320
|
+
console.log('Available plugins:');
|
|
321
|
+
console.log(` ${chalk.cyan('payara')} - Payara WAR deployment`);
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const pluginList = plugins.map(p => {
|
|
326
|
+
const packageName = p.package || p.path || 'unknown';
|
|
327
|
+
const shortName = getShortName(packageName);
|
|
328
|
+
const version = p.package ? getInstalledVersion(p.package, pluginsDir) : null;
|
|
329
|
+
const enabled = p.enabled !== false;
|
|
330
|
+
return {
|
|
331
|
+
name: shortName,
|
|
332
|
+
package: packageName,
|
|
333
|
+
version: version || 'unknown',
|
|
334
|
+
enabled,
|
|
335
|
+
source: p.path ? 'local' : 'npm',
|
|
336
|
+
};
|
|
337
|
+
});
|
|
338
|
+
if (options.json) {
|
|
339
|
+
console.log(JSON.stringify(pluginList, null, 2));
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
console.log(chalk.bold('Installed Plugins'));
|
|
343
|
+
console.log();
|
|
344
|
+
for (const p of pluginList) {
|
|
345
|
+
const status = p.enabled ? chalk.green('●') : chalk.gray('○');
|
|
346
|
+
const versionStr = chalk.dim(`v${p.version}`);
|
|
347
|
+
const disabledStr = p.enabled ? '' : chalk.yellow(' (disabled)');
|
|
348
|
+
console.log(` ${status} ${chalk.cyan(p.name)} ${versionStr}${disabledStr}`);
|
|
349
|
+
if (p.package !== p.name) {
|
|
350
|
+
console.log(` ${chalk.dim(p.package)}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
console.log();
|
|
354
|
+
console.log(chalk.dim(`Plugins directory: ${pluginsDir}`));
|
|
355
|
+
});
|
|
356
|
+
// ============================================================================
|
|
357
|
+
// plugin update [name]
|
|
358
|
+
// ============================================================================
|
|
359
|
+
plugin
|
|
360
|
+
.command('update [name]')
|
|
361
|
+
.alias('upgrade')
|
|
362
|
+
.description('Update plugins (all or specific)')
|
|
363
|
+
.action(async (name) => {
|
|
364
|
+
const plugins = getPlugins();
|
|
365
|
+
if (plugins.length === 0) {
|
|
366
|
+
console.log(chalk.dim('No plugins installed.'));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const pluginsDir = getPluginsDir();
|
|
370
|
+
const spinner = ora('Checking for updates...').start();
|
|
371
|
+
try {
|
|
372
|
+
// Filter to specific plugin if name provided
|
|
373
|
+
let toUpdate = plugins.filter(p => p.package);
|
|
374
|
+
if (name) {
|
|
375
|
+
const packageName = resolvePluginName(name);
|
|
376
|
+
toUpdate = toUpdate.filter(p => p.package === packageName ||
|
|
377
|
+
p.package === name ||
|
|
378
|
+
getShortName(p.package ?? '') === name);
|
|
379
|
+
if (toUpdate.length === 0) {
|
|
380
|
+
spinner.fail(`Plugin not found: ${name}`);
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const updates = [];
|
|
385
|
+
for (const p of toUpdate) {
|
|
386
|
+
const packageName = p.package;
|
|
387
|
+
const currentVersion = getInstalledVersion(packageName, pluginsDir);
|
|
388
|
+
const latestVersion = getPackageVersion(packageName);
|
|
389
|
+
if (currentVersion && latestVersion && currentVersion !== latestVersion) {
|
|
390
|
+
updates.push({
|
|
391
|
+
name: getShortName(packageName),
|
|
392
|
+
from: currentVersion,
|
|
393
|
+
to: latestVersion,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (updates.length === 0) {
|
|
398
|
+
spinner.succeed('All plugins are up to date.');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
spinner.text = `Updating ${updates.length} plugin(s)...`;
|
|
402
|
+
// Update all at once
|
|
403
|
+
const packagesToUpdate = toUpdate.map(p => p.package);
|
|
404
|
+
const updateResult = await runNpm(['update', ...packagesToUpdate], pluginsDir);
|
|
405
|
+
if (!updateResult.success) {
|
|
406
|
+
spinner.fail('Update failed');
|
|
407
|
+
output.error(updateResult.output);
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
spinner.succeed('Plugins updated');
|
|
411
|
+
console.log();
|
|
412
|
+
for (const u of updates) {
|
|
413
|
+
console.log(` ${chalk.cyan(u.name)}: ${u.from} → ${chalk.green(u.to)}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
catch (err) {
|
|
417
|
+
spinner.fail('Update failed');
|
|
418
|
+
output.error(err instanceof Error ? err.message : String(err));
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
// ============================================================================
|
|
423
|
+
// plugin enable <name>
|
|
424
|
+
// ============================================================================
|
|
425
|
+
plugin
|
|
426
|
+
.command('enable <name>')
|
|
427
|
+
.description('Enable a disabled plugin')
|
|
428
|
+
.action((name) => {
|
|
429
|
+
const plugins = getPlugins();
|
|
430
|
+
const packageName = resolvePluginName(name);
|
|
431
|
+
const found = plugins.find(p => p.package === packageName ||
|
|
432
|
+
p.package === name ||
|
|
433
|
+
getShortName(p.package ?? '') === name);
|
|
434
|
+
if (!found) {
|
|
435
|
+
output.error(`Plugin not found: ${name}`);
|
|
436
|
+
console.log('Use "znvault plugin list" to see installed plugins.');
|
|
437
|
+
process.exit(1);
|
|
438
|
+
}
|
|
439
|
+
if (found.enabled !== false) {
|
|
440
|
+
console.log(`Plugin ${chalk.cyan(getShortName(found.package ?? name))} is already enabled.`);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
setPluginEnabled(found.package ?? found.path ?? '', true);
|
|
444
|
+
output.success(`Enabled plugin: ${getShortName(found.package ?? name)}`);
|
|
445
|
+
});
|
|
446
|
+
// ============================================================================
|
|
447
|
+
// plugin disable <name>
|
|
448
|
+
// ============================================================================
|
|
449
|
+
plugin
|
|
450
|
+
.command('disable <name>')
|
|
451
|
+
.description('Disable a plugin without uninstalling')
|
|
452
|
+
.action((name) => {
|
|
453
|
+
const plugins = getPlugins();
|
|
454
|
+
const packageName = resolvePluginName(name);
|
|
455
|
+
const found = plugins.find(p => p.package === packageName ||
|
|
456
|
+
p.package === name ||
|
|
457
|
+
getShortName(p.package ?? '') === name);
|
|
458
|
+
if (!found) {
|
|
459
|
+
output.error(`Plugin not found: ${name}`);
|
|
460
|
+
console.log('Use "znvault plugin list" to see installed plugins.');
|
|
461
|
+
process.exit(1);
|
|
462
|
+
}
|
|
463
|
+
if (found.enabled === false) {
|
|
464
|
+
console.log(`Plugin ${chalk.cyan(getShortName(found.package ?? name))} is already disabled.`);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
setPluginEnabled(found.package ?? found.path ?? '', false);
|
|
468
|
+
output.success(`Disabled plugin: ${getShortName(found.package ?? name)}`);
|
|
469
|
+
});
|
|
470
|
+
// ============================================================================
|
|
471
|
+
// plugin info <name>
|
|
472
|
+
// ============================================================================
|
|
473
|
+
plugin
|
|
474
|
+
.command('info <name>')
|
|
475
|
+
.description('Show plugin information')
|
|
476
|
+
.action(async (name) => {
|
|
477
|
+
const spinner = ora('Fetching plugin info...').start();
|
|
478
|
+
try {
|
|
479
|
+
const packageName = resolvePluginName(name);
|
|
480
|
+
const plugins = getPlugins();
|
|
481
|
+
const pluginsDir = getPluginsDir();
|
|
482
|
+
// Check if installed locally
|
|
483
|
+
const installed = plugins.find(p => p.package === packageName ||
|
|
484
|
+
p.package === name ||
|
|
485
|
+
getShortName(p.package ?? '') === name);
|
|
486
|
+
// Get npm info
|
|
487
|
+
let npmInfo = null;
|
|
488
|
+
try {
|
|
489
|
+
const infoStr = execSync(`npm view ${packageName} --json`, { stdio: 'pipe' }).toString();
|
|
490
|
+
npmInfo = JSON.parse(infoStr);
|
|
491
|
+
}
|
|
492
|
+
catch {
|
|
493
|
+
// Try without prefix
|
|
494
|
+
if (packageName !== name) {
|
|
495
|
+
try {
|
|
496
|
+
const infoStr = execSync(`npm view ${name} --json`, { stdio: 'pipe' }).toString();
|
|
497
|
+
npmInfo = JSON.parse(infoStr);
|
|
498
|
+
}
|
|
499
|
+
catch {
|
|
500
|
+
// Not found
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
spinner.stop();
|
|
505
|
+
if (!installed && !npmInfo) {
|
|
506
|
+
output.error(`Plugin not found: ${name}`);
|
|
507
|
+
process.exit(1);
|
|
508
|
+
}
|
|
509
|
+
console.log();
|
|
510
|
+
console.log(chalk.bold(getShortName(packageName)));
|
|
511
|
+
console.log();
|
|
512
|
+
if (npmInfo) {
|
|
513
|
+
console.log(` Package: ${chalk.cyan(packageName)}`);
|
|
514
|
+
console.log(` Version: ${npmInfo.version}`);
|
|
515
|
+
if (npmInfo.description) {
|
|
516
|
+
console.log(` Description: ${npmInfo.description}`);
|
|
517
|
+
}
|
|
518
|
+
if (npmInfo.homepage) {
|
|
519
|
+
console.log(` Homepage: ${chalk.blue(npmInfo.homepage)}`);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (installed) {
|
|
523
|
+
const localVersion = getInstalledVersion(installed.package, pluginsDir);
|
|
524
|
+
console.log();
|
|
525
|
+
console.log(chalk.dim('Local Installation:'));
|
|
526
|
+
console.log(` Installed: ${chalk.green('Yes')}`);
|
|
527
|
+
console.log(` Version: ${localVersion || 'unknown'}`);
|
|
528
|
+
console.log(` Enabled: ${installed.enabled !== false ? chalk.green('Yes') : chalk.yellow('No')}`);
|
|
529
|
+
if (npmInfo && localVersion && localVersion !== npmInfo.version) {
|
|
530
|
+
console.log();
|
|
531
|
+
console.log(chalk.yellow(` Update available: ${localVersion} → ${npmInfo.version}`));
|
|
532
|
+
console.log(chalk.dim(` Run: znvault plugin update ${getShortName(packageName)}`));
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
console.log();
|
|
537
|
+
console.log(chalk.dim('Not installed locally.'));
|
|
538
|
+
console.log(`Install with: ${chalk.cyan(`znvault plugin install ${getShortName(packageName)}`)}`);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
catch (err) {
|
|
542
|
+
spinner.fail('Failed to fetch info');
|
|
543
|
+
output.error(err instanceof Error ? err.message : String(err));
|
|
544
|
+
process.exit(1);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
//# sourceMappingURL=plugin.js.map
|