vcluster-yaml-mcp-server 1.0.1 → 1.0.3
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 -1
- package/package.json +1 -1
- package/src/cli-utils.js +62 -0
- package/src/cli.js +99 -7
- package/src/completions.js +234 -0
package/README.md
CHANGED
|
@@ -76,9 +76,18 @@ npx -p vcluster-yaml-mcp-server vcluster-yaml query sync --format table
|
|
|
76
76
|
# Or install globally
|
|
77
77
|
npm install -g vcluster-yaml-mcp-server
|
|
78
78
|
vcluster-yaml query sync --format table
|
|
79
|
+
|
|
80
|
+
# Validate configurations with ease
|
|
81
|
+
vcluster-yaml validate my-config.yaml
|
|
82
|
+
cat my-config.yaml | vcluster-yaml validate -
|
|
83
|
+
vcluster-yaml validate my-config.yaml --schema-version v0.24.0
|
|
84
|
+
|
|
85
|
+
# Shell completion (bash/zsh)
|
|
86
|
+
vcluster-yaml completion bash 2>/dev/null > ~/.vcluster-yaml-completion.bash
|
|
87
|
+
vcluster-yaml completion zsh 2>/dev/null > ~/.zsh/completion/_vcluster-yaml
|
|
79
88
|
```
|
|
80
89
|
|
|
81
|
-
📖 **[Full CLI Documentation →](docs/CLI.md)**
|
|
90
|
+
📖 **[Full CLI Documentation →](docs/CLI.md)**
|
|
82
91
|
|
|
83
92
|
## Available Tools
|
|
84
93
|
|
package/package.json
CHANGED
package/src/cli-utils.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI utility functions
|
|
3
|
+
* Provides helpers for stdin reading and file operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile } from 'fs/promises';
|
|
7
|
+
import { stdin } from 'process';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Read content from stdin
|
|
11
|
+
* Used for piping content to CLI commands
|
|
12
|
+
* @returns {Promise<string>} The content read from stdin
|
|
13
|
+
*/
|
|
14
|
+
export async function readStdin() {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
let data = '';
|
|
17
|
+
|
|
18
|
+
stdin.setEncoding('utf8');
|
|
19
|
+
stdin.on('data', chunk => data += chunk);
|
|
20
|
+
stdin.on('end', () => resolve(data));
|
|
21
|
+
stdin.on('error', reject);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Read content from a file or stdin based on the file argument
|
|
27
|
+
* @param {string|undefined} file - File path, '-' for stdin, or undefined for stdin
|
|
28
|
+
* @returns {Promise<{content: string, source: string}>} The content and its source
|
|
29
|
+
*/
|
|
30
|
+
export async function readContentSource(file) {
|
|
31
|
+
// Read from stdin if no file provided or file is '-'
|
|
32
|
+
if (!file || file === '-') {
|
|
33
|
+
// If no file argument provided (not even '-'), check if we're in interactive mode
|
|
34
|
+
// In interactive mode (TTY), we should not wait for stdin
|
|
35
|
+
if (!file) {
|
|
36
|
+
// stdin.isTTY is true when running in interactive terminal
|
|
37
|
+
// stdin.isTTY is false/undefined when piping or redirecting
|
|
38
|
+
const isInteractive = stdin.isTTY === true;
|
|
39
|
+
|
|
40
|
+
if (isInteractive) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
'No input provided. Please specify a file or pipe content via stdin.\n' +
|
|
43
|
+
'Examples:\n' +
|
|
44
|
+
' vcluster-yaml validate my-config.yaml\n' +
|
|
45
|
+
' cat my-config.yaml | vcluster-yaml validate -\n' +
|
|
46
|
+
' vcluster-yaml validate --help'
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const content = await readStdin();
|
|
52
|
+
return { content, source: 'stdin' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Read from file
|
|
56
|
+
try {
|
|
57
|
+
const content = await readFile(file, 'utf-8');
|
|
58
|
+
return { content, source: file };
|
|
59
|
+
} catch (error) {
|
|
60
|
+
throw new Error(`Cannot read file '${file}': ${error.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -7,23 +7,41 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { Command } from 'commander';
|
|
10
|
+
import { readFile } from 'fs/promises';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
import { dirname, join } from 'path';
|
|
10
13
|
import { handleQuery, handleListVersions, handleValidate } from './cli-handlers.js';
|
|
11
14
|
import { formatOutput } from './formatters.js';
|
|
15
|
+
import { readContentSource } from './cli-utils.js';
|
|
16
|
+
import { generateBashCompletion, generateZshCompletion, getInstallInstructions } from './completions.js';
|
|
17
|
+
|
|
18
|
+
// Get package.json version
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = dirname(__filename);
|
|
21
|
+
const packageJson = JSON.parse(
|
|
22
|
+
await readFile(join(__dirname, '../package.json'), 'utf-8')
|
|
23
|
+
);
|
|
12
24
|
|
|
13
25
|
const program = new Command();
|
|
14
26
|
|
|
15
27
|
program
|
|
16
28
|
.name('vcluster-yaml')
|
|
17
29
|
.description('vCluster YAML configuration CLI')
|
|
18
|
-
.version(
|
|
30
|
+
.version(packageJson.version);
|
|
19
31
|
|
|
20
32
|
// Query command
|
|
21
33
|
program
|
|
22
34
|
.command('query <query>')
|
|
23
35
|
.description('Search for vCluster configuration fields')
|
|
24
36
|
.option('--file <file>', 'Configuration file to search', 'chart/values.yaml')
|
|
25
|
-
.option('--version <version>', 'vCluster version or branch', 'main')
|
|
37
|
+
.option('-s, --schema-version <version>', 'vCluster version or branch', 'main')
|
|
26
38
|
.option('-f, --format <format>', 'Output format (json, yaml, table)', 'json')
|
|
39
|
+
.addHelpText('after', `
|
|
40
|
+
Examples:
|
|
41
|
+
$ vcluster-yaml query sync
|
|
42
|
+
$ vcluster-yaml query sync --schema-version v0.24.0
|
|
43
|
+
$ vcluster-yaml query "controlPlane.replicas" --format table
|
|
44
|
+
`)
|
|
27
45
|
.action(async (query, options) => {
|
|
28
46
|
try {
|
|
29
47
|
// Validate format option
|
|
@@ -32,9 +50,15 @@ program
|
|
|
32
50
|
process.exit(1);
|
|
33
51
|
}
|
|
34
52
|
|
|
53
|
+
// Add validation for empty query
|
|
54
|
+
if (!query || query.trim() === '') {
|
|
55
|
+
console.error(`Error: Query cannot be empty. Try 'vcluster-yaml query sync' or see examples with --help`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
35
59
|
const result = await handleQuery(query, {
|
|
36
60
|
file: options.file,
|
|
37
|
-
version: options.
|
|
61
|
+
version: options.schemaVersion
|
|
38
62
|
});
|
|
39
63
|
|
|
40
64
|
if (!result.success) {
|
|
@@ -91,11 +115,22 @@ program
|
|
|
91
115
|
|
|
92
116
|
// Validate command
|
|
93
117
|
program
|
|
94
|
-
.command('validate
|
|
118
|
+
.command('validate [file]')
|
|
95
119
|
.description('Validate vCluster configuration')
|
|
96
|
-
.option('--version <version>', 'vCluster version for schema', 'main')
|
|
120
|
+
.option('-s, --schema-version <version>', 'vCluster version for schema', 'main')
|
|
97
121
|
.option('-f, --format <format>', 'Output format (json, yaml, table)', 'json')
|
|
98
|
-
.
|
|
122
|
+
.addHelpText('after', `
|
|
123
|
+
Arguments:
|
|
124
|
+
file YAML file to validate (use '-' for stdin, omit to read from stdin)
|
|
125
|
+
|
|
126
|
+
Examples:
|
|
127
|
+
$ vcluster-yaml validate vcluster.yaml
|
|
128
|
+
$ vcluster-yaml validate vcluster.yaml --schema-version v0.24.0
|
|
129
|
+
$ cat vcluster.yaml | vcluster-yaml validate -
|
|
130
|
+
$ vcluster-yaml validate - < vcluster.yaml
|
|
131
|
+
$ vcluster-yaml validate vcluster.yaml --format table
|
|
132
|
+
`)
|
|
133
|
+
.action(async (file, options) => {
|
|
99
134
|
try {
|
|
100
135
|
// Validate format option
|
|
101
136
|
if (!['json', 'yaml', 'table'].includes(options.format)) {
|
|
@@ -103,8 +138,24 @@ program
|
|
|
103
138
|
process.exit(1);
|
|
104
139
|
}
|
|
105
140
|
|
|
141
|
+
// Read content from file or stdin
|
|
142
|
+
let content;
|
|
143
|
+
try {
|
|
144
|
+
const result = await readContentSource(file);
|
|
145
|
+
content = result.content;
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(`Error: ${error.message}`);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check for empty content
|
|
152
|
+
if (!content || content.trim() === '') {
|
|
153
|
+
console.error(`Error: No content to validate. Please provide a file path or pipe content via stdin.`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
106
157
|
const result = await handleValidate(content, {
|
|
107
|
-
version: options.
|
|
158
|
+
version: options.schemaVersion
|
|
108
159
|
});
|
|
109
160
|
|
|
110
161
|
// For validation, always output the result (even if not successful)
|
|
@@ -124,5 +175,46 @@ program
|
|
|
124
175
|
}
|
|
125
176
|
});
|
|
126
177
|
|
|
178
|
+
// Completion command
|
|
179
|
+
program
|
|
180
|
+
.command('completion <shell>')
|
|
181
|
+
.description('Generate shell completion script')
|
|
182
|
+
.addHelpText('after', `
|
|
183
|
+
Supported shells:
|
|
184
|
+
bash Bash completion script
|
|
185
|
+
zsh Zsh completion script
|
|
186
|
+
|
|
187
|
+
Examples:
|
|
188
|
+
$ vcluster-yaml completion bash > ~/.vcluster-yaml-completion.bash
|
|
189
|
+
$ vcluster-yaml completion zsh > ~/.zsh/completion/_vcluster-yaml
|
|
190
|
+
$ vcluster-yaml completion bash --help
|
|
191
|
+
`)
|
|
192
|
+
.action((shell) => {
|
|
193
|
+
const validShells = ['bash', 'zsh'];
|
|
194
|
+
|
|
195
|
+
if (!validShells.includes(shell)) {
|
|
196
|
+
console.error(`Error: Unsupported shell "${shell}". Supported shells: ${validShells.join(', ')}`);
|
|
197
|
+
console.error('');
|
|
198
|
+
console.error('Examples:');
|
|
199
|
+
console.error(' vcluster-yaml completion bash > ~/.vcluster-yaml-completion.bash');
|
|
200
|
+
console.error(' vcluster-yaml completion zsh > ~/.zsh/completion/_vcluster-yaml');
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let script;
|
|
205
|
+
if (shell === 'bash') {
|
|
206
|
+
script = generateBashCompletion();
|
|
207
|
+
} else if (shell === 'zsh') {
|
|
208
|
+
script = generateZshCompletion();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Output the script
|
|
212
|
+
console.log(script);
|
|
213
|
+
|
|
214
|
+
// Add installation instructions to stderr so they don't end up in the script
|
|
215
|
+
console.error('');
|
|
216
|
+
console.error(getInstallInstructions(shell));
|
|
217
|
+
});
|
|
218
|
+
|
|
127
219
|
// Parse arguments
|
|
128
220
|
program.parse();
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell completion generators
|
|
3
|
+
* Generates bash and zsh completion scripts for vcluster-yaml CLI
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate bash completion script
|
|
8
|
+
* @returns {string} Bash completion script
|
|
9
|
+
*/
|
|
10
|
+
export function generateBashCompletion() {
|
|
11
|
+
return `# vcluster-yaml bash completion
|
|
12
|
+
|
|
13
|
+
_vcluster_yaml_completions() {
|
|
14
|
+
local cur prev words cword
|
|
15
|
+
_init_completion || return
|
|
16
|
+
|
|
17
|
+
# Available commands
|
|
18
|
+
local commands="query list-versions validate help"
|
|
19
|
+
|
|
20
|
+
# Available format options
|
|
21
|
+
local formats="json yaml table"
|
|
22
|
+
|
|
23
|
+
case "\${cword}" in
|
|
24
|
+
1)
|
|
25
|
+
# First argument: complete commands
|
|
26
|
+
COMPREPLY=($(compgen -W "\${commands}" -- "\${cur}"))
|
|
27
|
+
return 0
|
|
28
|
+
;;
|
|
29
|
+
*)
|
|
30
|
+
# Handle options based on previous word
|
|
31
|
+
case "\${prev}" in
|
|
32
|
+
-f|--format)
|
|
33
|
+
# Complete format options
|
|
34
|
+
COMPREPLY=($(compgen -W "\${formats}" -- "\${cur}"))
|
|
35
|
+
return 0
|
|
36
|
+
;;
|
|
37
|
+
-s|--schema-version)
|
|
38
|
+
# Complete common versions (user can type others)
|
|
39
|
+
COMPREPLY=($(compgen -W "main v0.28.0 v0.29.0 v0.30.0" -- "\${cur}"))
|
|
40
|
+
return 0
|
|
41
|
+
;;
|
|
42
|
+
--file)
|
|
43
|
+
# Complete YAML files
|
|
44
|
+
COMPREPLY=($(compgen -f -X '!*.yaml' -- "\${cur}"))
|
|
45
|
+
COMPREPLY+=($(compgen -f -X '!*.yml' -- "\${cur}"))
|
|
46
|
+
return 0
|
|
47
|
+
;;
|
|
48
|
+
validate)
|
|
49
|
+
# After validate command, complete YAML files or options
|
|
50
|
+
if [[ "\${cur}" == -* ]]; then
|
|
51
|
+
COMPREPLY=($(compgen -W "-s --schema-version -f --format -h --help" -- "\${cur}"))
|
|
52
|
+
else
|
|
53
|
+
COMPREPLY=($(compgen -f -X '!*.yaml' -- "\${cur}"))
|
|
54
|
+
COMPREPLY+=($(compgen -f -X '!*.yml' -- "\${cur}"))
|
|
55
|
+
[[ "\${cur}" == "" || "\${cur}" == "-" ]] && COMPREPLY+=("-")
|
|
56
|
+
fi
|
|
57
|
+
return 0
|
|
58
|
+
;;
|
|
59
|
+
query)
|
|
60
|
+
# After query command, complete options
|
|
61
|
+
if [[ "\${cur}" == -* ]]; then
|
|
62
|
+
COMPREPLY=($(compgen -W "--file -s --schema-version -f --format -h --help" -- "\${cur}"))
|
|
63
|
+
fi
|
|
64
|
+
return 0
|
|
65
|
+
;;
|
|
66
|
+
list-versions)
|
|
67
|
+
# After list-versions command, complete options
|
|
68
|
+
COMPREPLY=($(compgen -W "-f --format -h --help" -- "\${cur}"))
|
|
69
|
+
return 0
|
|
70
|
+
;;
|
|
71
|
+
esac
|
|
72
|
+
;;
|
|
73
|
+
esac
|
|
74
|
+
|
|
75
|
+
# Default: suggest options if starting with -
|
|
76
|
+
if [[ "\${cur}" == -* ]]; then
|
|
77
|
+
COMPREPLY=($(compgen -W "-V --version -h --help" -- "\${cur}"))
|
|
78
|
+
return 0
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
complete -F _vcluster_yaml_completions vcluster-yaml
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Generate zsh completion script
|
|
88
|
+
* @returns {string} Zsh completion script
|
|
89
|
+
*/
|
|
90
|
+
export function generateZshCompletion() {
|
|
91
|
+
return `#compdef vcluster-yaml
|
|
92
|
+
|
|
93
|
+
# vcluster-yaml zsh completion
|
|
94
|
+
|
|
95
|
+
_vcluster_yaml() {
|
|
96
|
+
local -a commands
|
|
97
|
+
commands=(
|
|
98
|
+
'query:Search for vCluster configuration fields'
|
|
99
|
+
'list-versions:List available vCluster versions'
|
|
100
|
+
'validate:Validate vCluster configuration'
|
|
101
|
+
'help:Display help for command'
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
local -a formats
|
|
105
|
+
formats=(
|
|
106
|
+
'json:JSON output format'
|
|
107
|
+
'yaml:YAML output format'
|
|
108
|
+
'table:Table output format'
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
local -a versions
|
|
112
|
+
versions=(
|
|
113
|
+
'main:Latest development version'
|
|
114
|
+
'v0.28.0:Version 0.28.0'
|
|
115
|
+
'v0.29.0:Version 0.29.0'
|
|
116
|
+
'v0.30.0:Version 0.30.0'
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
_arguments -C \\
|
|
120
|
+
'(-V --version)'{-V,--version}'[output the version number]' \\
|
|
121
|
+
'(-h --help)'{-h,--help}'[display help for command]' \\
|
|
122
|
+
'1: :->command' \\
|
|
123
|
+
'*:: :->args'
|
|
124
|
+
|
|
125
|
+
case $state in
|
|
126
|
+
command)
|
|
127
|
+
_describe 'vcluster-yaml commands' commands
|
|
128
|
+
;;
|
|
129
|
+
args)
|
|
130
|
+
case $words[1] in
|
|
131
|
+
query)
|
|
132
|
+
_arguments \\
|
|
133
|
+
'--file[Configuration file to search]:file:_files -g "*.yaml *.yml"' \\
|
|
134
|
+
'(-s --schema-version)'{-s,--schema-version}'[vCluster version or branch]:version:->versions' \\
|
|
135
|
+
'(-f --format)'{-f,--format}'[Output format]:format:->formats' \\
|
|
136
|
+
'(-h --help)'{-h,--help}'[display help]' \\
|
|
137
|
+
'1:query string:'
|
|
138
|
+
|
|
139
|
+
case $state in
|
|
140
|
+
formats)
|
|
141
|
+
_describe 'output formats' formats
|
|
142
|
+
;;
|
|
143
|
+
versions)
|
|
144
|
+
_describe 'vCluster versions' versions
|
|
145
|
+
;;
|
|
146
|
+
esac
|
|
147
|
+
;;
|
|
148
|
+
list-versions)
|
|
149
|
+
_arguments \\
|
|
150
|
+
'(-f --format)'{-f,--format}'[Output format]:format:->formats' \\
|
|
151
|
+
'(-h --help)'{-h,--help}'[display help]'
|
|
152
|
+
|
|
153
|
+
case $state in
|
|
154
|
+
formats)
|
|
155
|
+
_describe 'output formats' formats
|
|
156
|
+
;;
|
|
157
|
+
esac
|
|
158
|
+
;;
|
|
159
|
+
validate)
|
|
160
|
+
_arguments \\
|
|
161
|
+
'(-s --schema-version)'{-s,--schema-version}'[vCluster version for schema]:version:->versions' \\
|
|
162
|
+
'(-f --format)'{-f,--format}'[Output format]:format:->formats' \\
|
|
163
|
+
'(-h --help)'{-h,--help}'[display help]' \\
|
|
164
|
+
'1:file:_files -g "*.yaml *.yml"'
|
|
165
|
+
|
|
166
|
+
case $state in
|
|
167
|
+
formats)
|
|
168
|
+
_describe 'output formats' formats
|
|
169
|
+
;;
|
|
170
|
+
versions)
|
|
171
|
+
_describe 'vCluster versions' versions
|
|
172
|
+
;;
|
|
173
|
+
esac
|
|
174
|
+
;;
|
|
175
|
+
help)
|
|
176
|
+
_describe 'vcluster-yaml commands' commands
|
|
177
|
+
;;
|
|
178
|
+
esac
|
|
179
|
+
;;
|
|
180
|
+
esac
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
_vcluster_yaml "$@"
|
|
184
|
+
`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Display installation instructions for a shell
|
|
189
|
+
* @param {string} shell - Shell type (bash or zsh)
|
|
190
|
+
* @returns {string} Installation instructions
|
|
191
|
+
*/
|
|
192
|
+
export function getInstallInstructions(shell) {
|
|
193
|
+
if (shell === 'bash') {
|
|
194
|
+
return `
|
|
195
|
+
Bash Completion Installation:
|
|
196
|
+
|
|
197
|
+
1. Save the completion script:
|
|
198
|
+
$ vcluster-yaml completion bash > ~/.vcluster-yaml-completion.bash
|
|
199
|
+
|
|
200
|
+
2. Source it in your ~/.bashrc:
|
|
201
|
+
$ echo 'source ~/.vcluster-yaml-completion.bash' >> ~/.bashrc
|
|
202
|
+
|
|
203
|
+
3. Reload your shell:
|
|
204
|
+
$ source ~/.bashrc
|
|
205
|
+
|
|
206
|
+
Or install system-wide (requires sudo):
|
|
207
|
+
$ sudo vcluster-yaml completion bash > /etc/bash_completion.d/vcluster-yaml
|
|
208
|
+
`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (shell === 'zsh') {
|
|
212
|
+
return `
|
|
213
|
+
Zsh Completion Installation:
|
|
214
|
+
|
|
215
|
+
1. Create completion directory if needed:
|
|
216
|
+
$ mkdir -p ~/.zsh/completion
|
|
217
|
+
|
|
218
|
+
2. Save the completion script:
|
|
219
|
+
$ vcluster-yaml completion zsh > ~/.zsh/completion/_vcluster-yaml
|
|
220
|
+
|
|
221
|
+
3. Add to fpath in your ~/.zshrc (before compinit):
|
|
222
|
+
fpath=(~/.zsh/completion $fpath)
|
|
223
|
+
autoload -Uz compinit && compinit
|
|
224
|
+
|
|
225
|
+
4. Reload your shell:
|
|
226
|
+
$ exec zsh
|
|
227
|
+
|
|
228
|
+
Or install system-wide (requires sudo):
|
|
229
|
+
$ sudo vcluster-yaml completion zsh > /usr/local/share/zsh/site-functions/_vcluster-yaml
|
|
230
|
+
`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return 'Unsupported shell. Available: bash, zsh';
|
|
234
|
+
}
|