@botskill/cli 1.0.3 → 1.0.4
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 +159 -159
- package/package.json +1 -1
- package/scripts/build.js +35 -35
- package/scripts/postinstall.js +25 -25
- package/scripts/release.js +81 -81
- package/src/commands/config.js +64 -64
- package/src/commands/get.js +82 -88
- package/src/commands/help.js +24 -33
- package/src/commands/info.js +75 -79
- package/src/commands/init.js +128 -128
- package/src/commands/list.js +79 -84
- package/src/commands/login.js +73 -75
- package/src/commands/logout.js +21 -19
- package/src/commands/publish.js +60 -62
- package/src/commands/push.js +60 -62
- package/src/commands/search.js +57 -61
- package/src/index.js +44 -44
- package/src/lib/auth.js +118 -95
- package/src/lib/constants.js +10 -10
- package/src/lib/formatError.js +113 -0
- package/src/lib/uploadSkill.js +93 -91
package/src/commands/config.js
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { getApiUrl, setApiUrl, getToken, getUser, clearAuth, getConfigPath } from '../lib/auth.js';
|
|
3
|
-
import { getDefaultApiUrl } from '../lib/constants.js';
|
|
4
|
-
|
|
5
|
-
const configCommand = new Command('config');
|
|
6
|
-
configCommand
|
|
7
|
-
.description('Manage CLI configuration')
|
|
8
|
-
.option('-g, --get <key>', 'Get configuration value')
|
|
9
|
-
.option('-s, --set <key=value>', 'Set configuration value')
|
|
10
|
-
.option('-l, --list', 'List all configurations')
|
|
11
|
-
.option('-p, --path', 'Show config file path')
|
|
12
|
-
.option('--reset', 'Reset configuration to defaults')
|
|
13
|
-
.action(async (options) => {
|
|
14
|
-
if (options.path) {
|
|
15
|
-
console.log(getConfigPath());
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
if (options.list) {
|
|
19
|
-
const apiUrl = getApiUrl();
|
|
20
|
-
const token = getToken();
|
|
21
|
-
const user = getUser();
|
|
22
|
-
console.log('Current configuration:');
|
|
23
|
-
console.log(` config: ${getConfigPath()}`);
|
|
24
|
-
console.log(` apiUrl: ${apiUrl}`);
|
|
25
|
-
console.log(` token: ${token ? '***' : '(not set)'}`);
|
|
26
|
-
if (user) {
|
|
27
|
-
console.log(` user: ${user.username || user.email || user.id}`);
|
|
28
|
-
}
|
|
29
|
-
} else if (options.get) {
|
|
30
|
-
const key = options.get;
|
|
31
|
-
if (key === 'apiUrl') {
|
|
32
|
-
console.log(getApiUrl());
|
|
33
|
-
} else if (key === 'token') {
|
|
34
|
-
console.log(getToken() ? '***' : '(not set)');
|
|
35
|
-
} else {
|
|
36
|
-
console.error(`Unknown key: ${key}`);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
} else if (options.set) {
|
|
40
|
-
const [key, value] = options.set.split('=');
|
|
41
|
-
if (!key || value === undefined) {
|
|
42
|
-
console.error('Use --set key=value');
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
if (key === 'apiUrl') {
|
|
46
|
-
setApiUrl(value.trim());
|
|
47
|
-
console.log(`apiUrl set to ${value}`);
|
|
48
|
-
} else {
|
|
49
|
-
console.error(`Unknown key: ${key}`);
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
} else if (options.reset) {
|
|
53
|
-
clearAuth();
|
|
54
|
-
setApiUrl(getDefaultApiUrl());
|
|
55
|
-
console.log('Configuration reset to defaults.');
|
|
56
|
-
} else {
|
|
57
|
-
console.log('Usage:');
|
|
58
|
-
console.log(' skm config --list List configuration');
|
|
59
|
-
console.log(' skm config --get apiUrl Get apiUrl');
|
|
60
|
-
console.log(' skm config --set apiUrl=URL Set apiUrl');
|
|
61
|
-
console.log(' skm config --reset Reset to defaults');
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getApiUrl, setApiUrl, getToken, getUser, clearAuth, getConfigPath } from '../lib/auth.js';
|
|
3
|
+
import { getDefaultApiUrl } from '../lib/constants.js';
|
|
4
|
+
|
|
5
|
+
const configCommand = new Command('config');
|
|
6
|
+
configCommand
|
|
7
|
+
.description('Manage CLI configuration')
|
|
8
|
+
.option('-g, --get <key>', 'Get configuration value')
|
|
9
|
+
.option('-s, --set <key=value>', 'Set configuration value')
|
|
10
|
+
.option('-l, --list', 'List all configurations')
|
|
11
|
+
.option('-p, --path', 'Show config file path')
|
|
12
|
+
.option('--reset', 'Reset configuration to defaults')
|
|
13
|
+
.action(async (options) => {
|
|
14
|
+
if (options.path) {
|
|
15
|
+
console.log(getConfigPath());
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (options.list) {
|
|
19
|
+
const apiUrl = getApiUrl();
|
|
20
|
+
const token = getToken();
|
|
21
|
+
const user = getUser();
|
|
22
|
+
console.log('Current configuration:');
|
|
23
|
+
console.log(` config: ${getConfigPath()}`);
|
|
24
|
+
console.log(` apiUrl: ${apiUrl}`);
|
|
25
|
+
console.log(` token: ${token ? '***' : '(not set)'}`);
|
|
26
|
+
if (user) {
|
|
27
|
+
console.log(` user: ${user.username || user.email || user.id}`);
|
|
28
|
+
}
|
|
29
|
+
} else if (options.get) {
|
|
30
|
+
const key = options.get;
|
|
31
|
+
if (key === 'apiUrl') {
|
|
32
|
+
console.log(getApiUrl());
|
|
33
|
+
} else if (key === 'token') {
|
|
34
|
+
console.log(getToken() ? '***' : '(not set)');
|
|
35
|
+
} else {
|
|
36
|
+
console.error(`Unknown key: ${key}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
} else if (options.set) {
|
|
40
|
+
const [key, value] = options.set.split('=');
|
|
41
|
+
if (!key || value === undefined) {
|
|
42
|
+
console.error('Use --set key=value');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
if (key === 'apiUrl') {
|
|
46
|
+
setApiUrl(value.trim());
|
|
47
|
+
console.log(`apiUrl set to ${value}`);
|
|
48
|
+
} else {
|
|
49
|
+
console.error(`Unknown key: ${key}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
} else if (options.reset) {
|
|
53
|
+
clearAuth();
|
|
54
|
+
setApiUrl(getDefaultApiUrl());
|
|
55
|
+
console.log('Configuration reset to defaults.');
|
|
56
|
+
} else {
|
|
57
|
+
console.log('Usage:');
|
|
58
|
+
console.log(' skm config --list List configuration');
|
|
59
|
+
console.log(' skm config --get apiUrl Get apiUrl');
|
|
60
|
+
console.log(' skm config --set apiUrl=URL Set apiUrl');
|
|
61
|
+
console.log(' skm config --reset Reset to defaults');
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
65
|
export { configCommand };
|
package/src/commands/get.js
CHANGED
|
@@ -1,88 +1,82 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import AdmZip from 'adm-zip';
|
|
4
|
-
import { createApiClient } from '../lib/auth.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
getCommand
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
.
|
|
25
|
-
.option('--
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
console.log('[DRY RUN]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
console.log(`
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
console.error('Download failed:', msg);
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
export { getCommand };
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import AdmZip from 'adm-zip';
|
|
4
|
+
import { createApiClient } from '../lib/auth.js';
|
|
5
|
+
import { printApiError } from '../lib/formatError.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse specifier: name@version or name
|
|
9
|
+
* Returns { name, version } for API
|
|
10
|
+
*/
|
|
11
|
+
function parseSpecifier(spec) {
|
|
12
|
+
const s = spec.trim();
|
|
13
|
+
const atIdx = s.lastIndexOf('@');
|
|
14
|
+
if (atIdx < 0) return { name: s, version: undefined };
|
|
15
|
+
return {
|
|
16
|
+
name: s.slice(0, atIdx).trim(),
|
|
17
|
+
version: s.slice(atIdx + 1).trim() || undefined,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const getCommand = new Command('get');
|
|
22
|
+
getCommand
|
|
23
|
+
.description('Download a skill from BotSkill and extract to directory')
|
|
24
|
+
.argument('<specifier>', 'Skill name or name@version (e.g. pdf-parser or pdf-parser@1.0.0)')
|
|
25
|
+
.option('-o, --output <dir>', 'Output directory (default: current directory)')
|
|
26
|
+
.option('--dry-run', 'Show what would be downloaded without actually downloading')
|
|
27
|
+
.option('--api-url <url>', 'API base URL (overrides config for this command)')
|
|
28
|
+
.action(async (specifier, options) => {
|
|
29
|
+
const { name, version } = parseSpecifier(specifier);
|
|
30
|
+
const outputDir = path.resolve(options.output || process.cwd());
|
|
31
|
+
|
|
32
|
+
if (!name) {
|
|
33
|
+
console.error('Error: skill name is required');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (options.dryRun) {
|
|
38
|
+
console.log('[DRY RUN] Would download skill:', name);
|
|
39
|
+
console.log('[DRY RUN] Version:', version || 'latest');
|
|
40
|
+
console.log('[DRY RUN] Output:', outputDir);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const api = createApiClient(options.apiUrl);
|
|
46
|
+
|
|
47
|
+
const fullSpec = version ? `${name}@${version}` : name;
|
|
48
|
+
console.log(`Downloading skill: ${fullSpec}`);
|
|
49
|
+
console.log(`Version: ${version || 'latest'}`);
|
|
50
|
+
console.log(`Output: ${outputDir}`);
|
|
51
|
+
|
|
52
|
+
const resolveRes = await api.get(`/skills/by-name/${encodeURIComponent(fullSpec)}`);
|
|
53
|
+
const skill = resolveRes.data?.skill ?? resolveRes.data;
|
|
54
|
+
if (!skill?._id) {
|
|
55
|
+
console.error('Download failed: Skill not found');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const versionParam = version ? `?version=${encodeURIComponent(version)}` : '';
|
|
60
|
+
const url = `/skills/${encodeURIComponent(skill._id)}/download${versionParam}`;
|
|
61
|
+
const res = await api.get(url, { responseType: 'arraybuffer' });
|
|
62
|
+
const buffer = Buffer.from(res.data);
|
|
63
|
+
|
|
64
|
+
const zip = new AdmZip(buffer);
|
|
65
|
+
zip.extractAllTo(outputDir, true);
|
|
66
|
+
|
|
67
|
+
const entries = zip.getEntries();
|
|
68
|
+
const skillDir = entries.find(e => e.isDirectory)?.entryName || entries[0]?.entryName?.split('/')[0] || 'skill';
|
|
69
|
+
const targetPath = path.join(outputDir, skillDir);
|
|
70
|
+
|
|
71
|
+
console.log(`\nSkill downloaded successfully!`);
|
|
72
|
+
console.log(`Location: ${targetPath}`);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
if (err.response?.data && !Buffer.isBuffer(err.response.data)) {
|
|
75
|
+
const d = err.response.data;
|
|
76
|
+
err._overrideMsg = d.error || d.message;
|
|
77
|
+
}
|
|
78
|
+
printApiError(err, { prefix: 'Download failed' });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
export { getCommand };
|
package/src/commands/help.js
CHANGED
|
@@ -1,34 +1,25 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
|
|
3
|
-
const helpCommand = new Command('help');
|
|
4
|
-
helpCommand
|
|
5
|
-
.description('Show help
|
|
6
|
-
.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
console.log(' skm init --name my-skill');
|
|
26
|
-
console.log(' skm login --token abc123');
|
|
27
|
-
console.log(' skm list --category ai');
|
|
28
|
-
console.log(' skm get gpt-translator');
|
|
29
|
-
console.log(' skm push --public');
|
|
30
|
-
console.log('');
|
|
31
|
-
console.log('For detailed help on any command, use: skm [command] --help');
|
|
32
|
-
});
|
|
33
|
-
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
|
|
3
|
+
const helpCommand = new Command('help');
|
|
4
|
+
helpCommand
|
|
5
|
+
.description('Show help for a command')
|
|
6
|
+
.argument('[command]', 'Command to show help for')
|
|
7
|
+
.action((cmdName, opts, command) => {
|
|
8
|
+
const program = command.parent;
|
|
9
|
+
if (cmdName) {
|
|
10
|
+
const sub = program.commands.find(
|
|
11
|
+
(c) => c.name() === cmdName || (c.aliases && c.aliases().includes(cmdName))
|
|
12
|
+
);
|
|
13
|
+
if (sub) {
|
|
14
|
+
sub.outputHelp();
|
|
15
|
+
} else {
|
|
16
|
+
console.error(`Unknown command: ${cmdName}`);
|
|
17
|
+
console.error(`Run "skm help" to see available commands.`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
program.outputHelp();
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
34
25
|
export { helpCommand };
|
package/src/commands/info.js
CHANGED
|
@@ -1,79 +1,75 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createApiClient } from '../lib/auth.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
return {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
infoCommand
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
console.log('─'.repeat(50));
|
|
40
|
-
console.log(`
|
|
41
|
-
|
|
42
|
-
console.log(`
|
|
43
|
-
|
|
44
|
-
console.log(`
|
|
45
|
-
console.log(`
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
export { infoCommand };
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createApiClient } from '../lib/auth.js';
|
|
3
|
+
import { printApiError } from '../lib/formatError.js';
|
|
4
|
+
|
|
5
|
+
function parseSpecifier(spec) {
|
|
6
|
+
const s = spec.trim();
|
|
7
|
+
const atIdx = s.lastIndexOf('@');
|
|
8
|
+
if (atIdx < 0) return { name: s, version: undefined };
|
|
9
|
+
return {
|
|
10
|
+
name: s.slice(0, atIdx).trim(),
|
|
11
|
+
version: s.slice(atIdx + 1).trim() || undefined,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const infoCommand = new Command('info');
|
|
16
|
+
infoCommand
|
|
17
|
+
.description('Show skill details from BotSkill')
|
|
18
|
+
.argument('<specifier>', 'Skill name or name@version')
|
|
19
|
+
.option('--api-url <url>', 'API base URL (overrides config for this command)')
|
|
20
|
+
.action(async (specifier, options = {}) => {
|
|
21
|
+
const { name, version } = parseSpecifier(specifier);
|
|
22
|
+
|
|
23
|
+
if (!name) {
|
|
24
|
+
console.error('Error: skill name is required');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const api = createApiClient(options.apiUrl);
|
|
30
|
+
const fullSpec = version ? `${name}@${version}` : name;
|
|
31
|
+
|
|
32
|
+
const resolveRes = await api.get(`/skills/by-name/${encodeURIComponent(fullSpec)}`);
|
|
33
|
+
const skill = resolveRes.data?.skill ?? resolveRes.data;
|
|
34
|
+
if (!skill?._id) {
|
|
35
|
+
console.error('Skill not found');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log('\n' + '─'.repeat(50));
|
|
40
|
+
console.log(` ${skill.name}`);
|
|
41
|
+
console.log('─'.repeat(50));
|
|
42
|
+
console.log(` Description: ${skill.description || '—'}`);
|
|
43
|
+
const author = skill.author?.username || skill.author?.fullName || '—';
|
|
44
|
+
console.log(` Author: ${author}`);
|
|
45
|
+
console.log(` Category: ${skill.category || '—'}`);
|
|
46
|
+
console.log(` Downloads: ${(skill.downloads ?? 0).toLocaleString()}`);
|
|
47
|
+
console.log(` License: ${skill.license || 'MIT'}`);
|
|
48
|
+
if (skill.tags?.length) {
|
|
49
|
+
console.log(` Tags: ${skill.tags.join(', ')}`);
|
|
50
|
+
}
|
|
51
|
+
if (skill.repositoryUrl) {
|
|
52
|
+
console.log(` Repository: ${skill.repositoryUrl}`);
|
|
53
|
+
}
|
|
54
|
+
if (skill.documentationUrl) {
|
|
55
|
+
console.log(` Docs: ${skill.documentationUrl}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const versions = skill.versions || [];
|
|
59
|
+
const versionList = versions.length > 0 ? versions : (skill.version ? [{ version: skill.version, description: skill.description }] : []);
|
|
60
|
+
if (versionList.length > 0) {
|
|
61
|
+
console.log('\n Versions:');
|
|
62
|
+
versionList.forEach((v) => {
|
|
63
|
+
const date = v.createdAt ? new Date(v.createdAt).toLocaleDateString() : '';
|
|
64
|
+
console.log(` - ${v.version}${date ? ` (${date})` : ''}`);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log('\n Use "skm get name" or "skm get name@version" to download.');
|
|
69
|
+
console.log('');
|
|
70
|
+
} catch (err) {
|
|
71
|
+
printApiError(err, { prefix: 'Info failed' });
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export { infoCommand };
|