@sstar/skill-install 1.0.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/LICENSE +21 -0
- package/README.md +296 -0
- package/dist/archive/archive-extractor.d.ts +29 -0
- package/dist/archive/archive-extractor.js +123 -0
- package/dist/auth/auth-service.d.ts +21 -0
- package/dist/auth/auth-service.js +260 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +459 -0
- package/dist/core/errors.d.ts +19 -0
- package/dist/core/errors.js +29 -0
- package/dist/core/logger.d.ts +14 -0
- package/dist/core/logger.js +68 -0
- package/dist/download/download-manager.d.ts +41 -0
- package/dist/download/download-manager.js +184 -0
- package/dist/downloader.d.ts +29 -0
- package/dist/downloader.js +163 -0
- package/dist/http/http-client.d.ts +25 -0
- package/dist/http/http-client.js +172 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +14 -0
- package/dist/installer/install-service.d.ts +40 -0
- package/dist/installer/install-service.js +184 -0
- package/dist/skills/skill-validator.d.ts +33 -0
- package/dist/skills/skill-validator.js +124 -0
- package/dist/skills/skills-manager.d.ts +54 -0
- package/dist/skills/skills-manager.js +127 -0
- package/dist/source/source-detector.d.ts +29 -0
- package/dist/source/source-detector.js +88 -0
- package/package.json +62 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const readline_1 = __importDefault(require("readline"));
|
|
9
|
+
const install_service_1 = require("./installer/install-service");
|
|
10
|
+
const skills_manager_1 = require("./skills/skills-manager");
|
|
11
|
+
const logger_1 = require("./core/logger");
|
|
12
|
+
const program = new commander_1.Command();
|
|
13
|
+
const skillsManager = new skills_manager_1.SkillsManager();
|
|
14
|
+
program
|
|
15
|
+
.name('skill-install')
|
|
16
|
+
.description('Agent Skill installation tool - download, extract, validate, and install skills')
|
|
17
|
+
.version('1.0.0')
|
|
18
|
+
.option('-t, --tool <tool>', 'AI tool: claude or codex (skip prompt if specified)')
|
|
19
|
+
.option('-s, --skills-dir <path>', 'Custom skills directory')
|
|
20
|
+
.option('-v, --verbose', 'Enable verbose logging');
|
|
21
|
+
// Install command
|
|
22
|
+
program
|
|
23
|
+
.command('install <source>')
|
|
24
|
+
.description('Install a skill from URL or local archive file')
|
|
25
|
+
.option('-u, --username <username>', 'Wiki username (for wiki URLs)')
|
|
26
|
+
.option('-p, --password <password>', 'Wiki password (not recommended, use interactive input instead)')
|
|
27
|
+
.option('--allow-self-signed', 'Allow self-signed SSL certificates (default: true)', true)
|
|
28
|
+
.option('-f, --force', 'Reinstall if skill already exists')
|
|
29
|
+
.option('-g, --global', 'Install to global directory')
|
|
30
|
+
.option('-l, --local', 'Install to local directory')
|
|
31
|
+
.action(async (source, options, cmd) => {
|
|
32
|
+
const globalOpts = cmd.parent.opts();
|
|
33
|
+
const logger = new logger_1.Logger('CLI');
|
|
34
|
+
if (globalOpts.verbose) {
|
|
35
|
+
logger_1.Logger.setLevel('debug');
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
// Determine AI tool - prompt if not specified
|
|
39
|
+
let aiTool;
|
|
40
|
+
if (globalOpts.tool) {
|
|
41
|
+
aiTool = globalOpts.tool;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
aiTool = await promptAiTool();
|
|
45
|
+
}
|
|
46
|
+
// Determine skills directory
|
|
47
|
+
let skillsDir = globalOpts.skillsDir;
|
|
48
|
+
// If neither --global nor --local nor explicit --skills-dir, prompt interactively
|
|
49
|
+
if (!skillsDir && !options.global && !options.local) {
|
|
50
|
+
skillsDir = await promptSkillsDir(aiTool);
|
|
51
|
+
}
|
|
52
|
+
else if (options.global) {
|
|
53
|
+
skillsDir = skillsManager.getGlobalSkillsDir(aiTool);
|
|
54
|
+
}
|
|
55
|
+
else if (options.local) {
|
|
56
|
+
skillsDir = skillsManager.getLocalSkillsDir(aiTool);
|
|
57
|
+
}
|
|
58
|
+
console.log(`AI Tool: ${aiTool}`);
|
|
59
|
+
console.log(`Installing to: ${skillsDir}`);
|
|
60
|
+
console.log('');
|
|
61
|
+
// Get username/password for wiki URLs
|
|
62
|
+
let username = options.username || process.env.WIKI_USERNAME || '';
|
|
63
|
+
let password = options.password || process.env.WIKI_PASSWORD || '';
|
|
64
|
+
// Check if source appears to be a wiki URL
|
|
65
|
+
const needsAuth = source.includes('/download/attachments/') ||
|
|
66
|
+
source.includes('/wiki/download/') ||
|
|
67
|
+
source.includes('/confluence/download/');
|
|
68
|
+
if (needsAuth && !username) {
|
|
69
|
+
console.error('Error: Wiki URL detected. Username is required. Use -u option or set WIKI_USERNAME environment variable.');
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
if (needsAuth && !password) {
|
|
73
|
+
password = await promptPassword();
|
|
74
|
+
}
|
|
75
|
+
console.log(`Installing skill from: ${source}`);
|
|
76
|
+
if (needsAuth) {
|
|
77
|
+
console.log(`Username: ${username}`);
|
|
78
|
+
}
|
|
79
|
+
console.log('');
|
|
80
|
+
const installer = new install_service_1.InstallService();
|
|
81
|
+
const result = await installer.install({
|
|
82
|
+
source,
|
|
83
|
+
skillsDir,
|
|
84
|
+
username,
|
|
85
|
+
password,
|
|
86
|
+
allowSelfSigned: options.allowSelfSigned,
|
|
87
|
+
force: options.force
|
|
88
|
+
});
|
|
89
|
+
console.log('');
|
|
90
|
+
if (result.success) {
|
|
91
|
+
console.log(`✓ Skill installed successfully!`);
|
|
92
|
+
console.log(` Name: ${result.skillName}`);
|
|
93
|
+
console.log(` Path: ${result.skillPath}`);
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.error(`✗ Installation failed!`);
|
|
98
|
+
console.error(` Error: ${result.error || 'Unknown error'}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
104
|
+
console.error(`Error: ${errorMsg}`);
|
|
105
|
+
if (globalOpts.verbose) {
|
|
106
|
+
console.error(error);
|
|
107
|
+
}
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// List command
|
|
112
|
+
program
|
|
113
|
+
.command('list')
|
|
114
|
+
.description('List installed skills')
|
|
115
|
+
.option('-a, --all', 'List skills from both global and local directories')
|
|
116
|
+
.action(async (options, cmd) => {
|
|
117
|
+
const globalOpts = cmd.parent.opts();
|
|
118
|
+
if (globalOpts.verbose) {
|
|
119
|
+
logger_1.Logger.setLevel('debug');
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
// Determine AI tool - prompt if not specified
|
|
123
|
+
let aiTool;
|
|
124
|
+
if (globalOpts.tool) {
|
|
125
|
+
aiTool = globalOpts.tool;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
aiTool = await promptAiTool();
|
|
129
|
+
}
|
|
130
|
+
// If no specific directory and --all flag, show both directories
|
|
131
|
+
if (!globalOpts.skillsDir && options.all) {
|
|
132
|
+
await listSkillsFromBothDirectories(aiTool);
|
|
133
|
+
process.exit(0);
|
|
134
|
+
}
|
|
135
|
+
const skillsDir = skillsManager.getEffectiveSkillsDir(globalOpts.skillsDir, aiTool);
|
|
136
|
+
console.log(`AI Tool: ${aiTool}`);
|
|
137
|
+
console.log(`Skills directory: ${skillsDir}`);
|
|
138
|
+
console.log('');
|
|
139
|
+
const skills = await skillsManager.listInstalled(globalOpts.skillsDir, aiTool);
|
|
140
|
+
if (skills.length === 0) {
|
|
141
|
+
console.log('No skills installed.');
|
|
142
|
+
process.exit(0);
|
|
143
|
+
}
|
|
144
|
+
console.log(`Installed skills (${skills.length}):`);
|
|
145
|
+
console.log('');
|
|
146
|
+
for (const skill of skills) {
|
|
147
|
+
console.log(` ${skill.name}`);
|
|
148
|
+
console.log(` Description: ${skill.description}`);
|
|
149
|
+
console.log(` Path: ${skill.path}`);
|
|
150
|
+
console.log('');
|
|
151
|
+
}
|
|
152
|
+
process.exit(0);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
156
|
+
console.error(`Error: ${errorMsg}`);
|
|
157
|
+
if (globalOpts.verbose) {
|
|
158
|
+
console.error(error);
|
|
159
|
+
}
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
// Uninstall command
|
|
164
|
+
program
|
|
165
|
+
.command('uninstall [name]')
|
|
166
|
+
.description('Uninstall a skill (leave empty to select from list)')
|
|
167
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
168
|
+
.action(async (name, options, cmd) => {
|
|
169
|
+
const globalOpts = cmd.parent.opts();
|
|
170
|
+
if (globalOpts.verbose) {
|
|
171
|
+
logger_1.Logger.setLevel('debug');
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
// Determine AI tool - prompt if not specified
|
|
175
|
+
let aiTool;
|
|
176
|
+
if (globalOpts.tool) {
|
|
177
|
+
aiTool = globalOpts.tool;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
aiTool = await promptAiTool();
|
|
181
|
+
}
|
|
182
|
+
const skills = await skillsManager.listInstalled(globalOpts.skillsDir, aiTool);
|
|
183
|
+
if (skills.length === 0) {
|
|
184
|
+
console.log('No skills installed.');
|
|
185
|
+
process.exit(0);
|
|
186
|
+
}
|
|
187
|
+
// If no name provided, prompt to select from list
|
|
188
|
+
if (!name) {
|
|
189
|
+
const selectedIndices = await promptSkillSelection(skills);
|
|
190
|
+
if (selectedIndices.length === 0) {
|
|
191
|
+
console.log('No skills selected.');
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
// Uninstall each selected skill
|
|
195
|
+
let successCount = 0;
|
|
196
|
+
let failCount = 0;
|
|
197
|
+
for (const index of selectedIndices) {
|
|
198
|
+
const skill = skills[index];
|
|
199
|
+
try {
|
|
200
|
+
// Confirm uninstall unless --yes flag is provided
|
|
201
|
+
if (!options.yes) {
|
|
202
|
+
const confirmed = await confirm(`Uninstall skill "${skill.name}" from ${aiTool}?`);
|
|
203
|
+
if (!confirmed) {
|
|
204
|
+
console.log(`Skipped: ${skill.name}`);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
await skillsManager.uninstall(skill.name, globalOpts.skillsDir, aiTool);
|
|
209
|
+
console.log(`✓ Skill "${skill.name}" uninstalled successfully.`);
|
|
210
|
+
successCount++;
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
console.error(`✗ Failed to uninstall "${skill.name}": ${error.message}`);
|
|
214
|
+
failCount++;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
console.log('');
|
|
218
|
+
console.log(`Uninstall complete: ${successCount} succeeded, ${failCount} failed.`);
|
|
219
|
+
process.exit(0);
|
|
220
|
+
}
|
|
221
|
+
// Single skill uninstall (existing behavior)
|
|
222
|
+
const skill = skills.find(s => s.name === name);
|
|
223
|
+
if (!skill) {
|
|
224
|
+
console.error(`Error: Skill "${name}" not found.`);
|
|
225
|
+
const availableSkills = skills.map(s => s.name).join(', ');
|
|
226
|
+
if (availableSkills) {
|
|
227
|
+
console.log(`Available skills: ${availableSkills}`);
|
|
228
|
+
}
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
// Confirm uninstall unless --yes flag is provided
|
|
232
|
+
if (!options.yes) {
|
|
233
|
+
const confirmed = await confirm(`Uninstall skill "${name}" from ${aiTool}?`);
|
|
234
|
+
if (!confirmed) {
|
|
235
|
+
console.log('Uninstall cancelled.');
|
|
236
|
+
process.exit(0);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
await skillsManager.uninstall(name, globalOpts.skillsDir, aiTool);
|
|
240
|
+
console.log(`✓ Skill "${name}" uninstalled successfully.`);
|
|
241
|
+
process.exit(0);
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
245
|
+
console.error(`Error: ${errorMsg}`);
|
|
246
|
+
if (globalOpts.verbose) {
|
|
247
|
+
console.error(error);
|
|
248
|
+
}
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
function promptPassword() {
|
|
253
|
+
return new Promise((resolve, reject) => {
|
|
254
|
+
// Check if setRawMode is available (TTY required)
|
|
255
|
+
if (typeof process.stdin.setRawMode !== 'function') {
|
|
256
|
+
console.error('\nError: Interactive password input requires a TTY.');
|
|
257
|
+
console.error('Please use -p option or WIKI_PASSWORD environment variable.');
|
|
258
|
+
reject(new Error('TTY not available for interactive password input'));
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const rl = readline_1.default.createInterface({
|
|
262
|
+
input: process.stdin,
|
|
263
|
+
output: process.stdout
|
|
264
|
+
});
|
|
265
|
+
// Turn off echo
|
|
266
|
+
process.stdout.write('Password: ');
|
|
267
|
+
process.stdin.setRawMode(true);
|
|
268
|
+
let password = '';
|
|
269
|
+
const onData = (char) => {
|
|
270
|
+
const str = char.toString();
|
|
271
|
+
if (str === '\n' || str === '\r' || str === '\u0004') {
|
|
272
|
+
// Enter or Ctrl-D
|
|
273
|
+
process.stdin.setRawMode(false);
|
|
274
|
+
process.stdin.removeListener('data', onData);
|
|
275
|
+
process.stdout.write('\n');
|
|
276
|
+
rl.close();
|
|
277
|
+
resolve(password);
|
|
278
|
+
}
|
|
279
|
+
else if (str === '\u0003') {
|
|
280
|
+
// Ctrl-C
|
|
281
|
+
process.stdout.write('\n');
|
|
282
|
+
process.exit(1);
|
|
283
|
+
}
|
|
284
|
+
else if (str === '\u007f') {
|
|
285
|
+
// Backspace
|
|
286
|
+
if (password.length > 0) {
|
|
287
|
+
password = password.slice(0, -1);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
// Regular character
|
|
292
|
+
password += str;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
process.stdin.on('data', onData);
|
|
296
|
+
process.stdin.setRawMode(true);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
function confirm(message) {
|
|
300
|
+
return new Promise((resolve) => {
|
|
301
|
+
const rl = readline_1.default.createInterface({
|
|
302
|
+
input: process.stdin,
|
|
303
|
+
output: process.stdout
|
|
304
|
+
});
|
|
305
|
+
rl.question(`${message} (y/N): `, (answer) => {
|
|
306
|
+
rl.close();
|
|
307
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
function promptSkillsDir(aiTool) {
|
|
312
|
+
return new Promise((resolve) => {
|
|
313
|
+
const rl = readline_1.default.createInterface({
|
|
314
|
+
input: process.stdin,
|
|
315
|
+
output: process.stdout
|
|
316
|
+
});
|
|
317
|
+
const globalDir = skillsManager.getGlobalSkillsDir(aiTool);
|
|
318
|
+
const localDir = skillsManager.getLocalSkillsDir(aiTool);
|
|
319
|
+
console.log('');
|
|
320
|
+
console.log('Select installation directory:');
|
|
321
|
+
console.log(` 1. Global: ${globalDir}`);
|
|
322
|
+
console.log(` 2. Local: ${localDir}`);
|
|
323
|
+
console.log('');
|
|
324
|
+
rl.question('Choose [1/2] (default: 1): ', (answer) => {
|
|
325
|
+
rl.close();
|
|
326
|
+
const choice = answer.trim() || '1';
|
|
327
|
+
if (choice === '1') {
|
|
328
|
+
resolve(globalDir);
|
|
329
|
+
}
|
|
330
|
+
else if (choice === '2') {
|
|
331
|
+
resolve(localDir);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
console.log('Invalid choice, using global directory.');
|
|
335
|
+
resolve(globalDir);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
function promptAiTool() {
|
|
341
|
+
return new Promise((resolve) => {
|
|
342
|
+
const rl = readline_1.default.createInterface({
|
|
343
|
+
input: process.stdin,
|
|
344
|
+
output: process.stdout
|
|
345
|
+
});
|
|
346
|
+
console.log('');
|
|
347
|
+
console.log('Select AI tool:');
|
|
348
|
+
console.log(' 1. Claude Code (~/.claude/skills/)');
|
|
349
|
+
console.log(' 2. Codex (~/.codex/skills/)');
|
|
350
|
+
console.log('');
|
|
351
|
+
rl.question('Choose [1/2] (default: 1): ', (answer) => {
|
|
352
|
+
rl.close();
|
|
353
|
+
const choice = answer.trim() || '1';
|
|
354
|
+
if (choice === '1') {
|
|
355
|
+
resolve('claude');
|
|
356
|
+
}
|
|
357
|
+
else if (choice === '2') {
|
|
358
|
+
resolve('codex');
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
console.log('Invalid choice, using claude.');
|
|
362
|
+
resolve('claude');
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
function promptSkillSelection(skills) {
|
|
368
|
+
return new Promise((resolve) => {
|
|
369
|
+
const rl = readline_1.default.createInterface({
|
|
370
|
+
input: process.stdin,
|
|
371
|
+
output: process.stdout
|
|
372
|
+
});
|
|
373
|
+
console.log('');
|
|
374
|
+
console.log('Installed skills:');
|
|
375
|
+
console.log('');
|
|
376
|
+
for (let i = 0; i < skills.length; i++) {
|
|
377
|
+
const skill = skills[i];
|
|
378
|
+
console.log(` [${i + 1}] ${skill.name}`);
|
|
379
|
+
console.log(` ${skill.description}`);
|
|
380
|
+
console.log('');
|
|
381
|
+
}
|
|
382
|
+
console.log('Enter the numbers of the skills to uninstall (separated by spaces, e.g., "1 3 5"):');
|
|
383
|
+
console.log('Press Enter to cancel:');
|
|
384
|
+
rl.question('Your choice: ', (answer) => {
|
|
385
|
+
rl.close();
|
|
386
|
+
const input = answer.trim();
|
|
387
|
+
if (!input) {
|
|
388
|
+
resolve([]);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
// Parse the input and extract valid indices
|
|
392
|
+
const selectedNumbers = input.split(/\s+/).map(s => parseInt(s, 10));
|
|
393
|
+
const validIndices = [];
|
|
394
|
+
for (const num of selectedNumbers) {
|
|
395
|
+
if (isNaN(num)) {
|
|
396
|
+
console.log(`Skipping invalid number: ${input}`);
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (num < 1 || num > skills.length) {
|
|
400
|
+
console.log(`Skipping out of range number: ${num}`);
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
validIndices.push(num - 1); // Convert to 0-based index
|
|
404
|
+
}
|
|
405
|
+
// Remove duplicates
|
|
406
|
+
const uniqueIndices = [...new Set(validIndices)];
|
|
407
|
+
if (uniqueIndices.length === 0) {
|
|
408
|
+
console.log('No valid skills selected.');
|
|
409
|
+
resolve([]);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
console.log('');
|
|
413
|
+
console.log(`Selected skills: ${uniqueIndices.map(i => skills[i].name).join(', ')}`);
|
|
414
|
+
resolve(uniqueIndices);
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
async function listSkillsFromBothDirectories(aiTool) {
|
|
419
|
+
const globalDir = skillsManager.getGlobalSkillsDir(aiTool);
|
|
420
|
+
const localDir = skillsManager.getLocalSkillsDir(aiTool);
|
|
421
|
+
const globalSkills = await skillsManager.listInstalled(globalDir, aiTool);
|
|
422
|
+
const localSkills = await skillsManager.listInstalled(localDir, aiTool);
|
|
423
|
+
let hasAny = false;
|
|
424
|
+
console.log(`AI Tool: ${aiTool}`);
|
|
425
|
+
console.log('');
|
|
426
|
+
// Global skills
|
|
427
|
+
console.log(`Global skills (${globalDir}):`);
|
|
428
|
+
console.log('');
|
|
429
|
+
if (globalSkills.length === 0) {
|
|
430
|
+
console.log(' No skills installed.');
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
hasAny = true;
|
|
434
|
+
for (const skill of globalSkills) {
|
|
435
|
+
console.log(` ${skill.name}`);
|
|
436
|
+
console.log(` Description: ${skill.description}`);
|
|
437
|
+
console.log('');
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
console.log('');
|
|
441
|
+
// Local skills
|
|
442
|
+
console.log(`Local skills (${localDir}):`);
|
|
443
|
+
console.log('');
|
|
444
|
+
if (localSkills.length === 0) {
|
|
445
|
+
console.log(' No skills installed.');
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
hasAny = true;
|
|
449
|
+
for (const skill of localSkills) {
|
|
450
|
+
console.log(` ${skill.name}`);
|
|
451
|
+
console.log(` Description: ${skill.description}`);
|
|
452
|
+
console.log('');
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (!hasAny) {
|
|
456
|
+
console.log('No skills installed in either location.');
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
program.parse();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare enum ErrorType {
|
|
2
|
+
AUTHENTICATION = "AUTHENTICATION",
|
|
3
|
+
ACCESS_DENIED = "ACCESS_DENIED",
|
|
4
|
+
INVALID_INPUT = "INVALID_INPUT",
|
|
5
|
+
NETWORK = "NETWORK",
|
|
6
|
+
NOT_FOUND = "NOT_FOUND",
|
|
7
|
+
CONTENT_VALIDATION = "CONTENT_VALIDATION",
|
|
8
|
+
ATTACHMENT = "ATTACHMENT",
|
|
9
|
+
INVALID_SKILL = "INVALID_SKILL",
|
|
10
|
+
EXTRACTION_FAILED = "EXTRACTION_FAILED",
|
|
11
|
+
ALREADY_INSTALLED = "ALREADY_INSTALLED",
|
|
12
|
+
UNKNOWN = "UNKNOWN"
|
|
13
|
+
}
|
|
14
|
+
export declare class WikiError extends Error {
|
|
15
|
+
readonly type: ErrorType;
|
|
16
|
+
readonly cause?: unknown | undefined;
|
|
17
|
+
constructor(type: ErrorType, message: string, cause?: unknown | undefined);
|
|
18
|
+
static is(error: unknown): error is WikiError;
|
|
19
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WikiError = exports.ErrorType = void 0;
|
|
4
|
+
var ErrorType;
|
|
5
|
+
(function (ErrorType) {
|
|
6
|
+
ErrorType["AUTHENTICATION"] = "AUTHENTICATION";
|
|
7
|
+
ErrorType["ACCESS_DENIED"] = "ACCESS_DENIED";
|
|
8
|
+
ErrorType["INVALID_INPUT"] = "INVALID_INPUT";
|
|
9
|
+
ErrorType["NETWORK"] = "NETWORK";
|
|
10
|
+
ErrorType["NOT_FOUND"] = "NOT_FOUND";
|
|
11
|
+
ErrorType["CONTENT_VALIDATION"] = "CONTENT_VALIDATION";
|
|
12
|
+
ErrorType["ATTACHMENT"] = "ATTACHMENT";
|
|
13
|
+
ErrorType["INVALID_SKILL"] = "INVALID_SKILL";
|
|
14
|
+
ErrorType["EXTRACTION_FAILED"] = "EXTRACTION_FAILED";
|
|
15
|
+
ErrorType["ALREADY_INSTALLED"] = "ALREADY_INSTALLED";
|
|
16
|
+
ErrorType["UNKNOWN"] = "UNKNOWN";
|
|
17
|
+
})(ErrorType || (exports.ErrorType = ErrorType = {}));
|
|
18
|
+
class WikiError extends Error {
|
|
19
|
+
constructor(type, message, cause) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.type = type;
|
|
22
|
+
this.cause = cause;
|
|
23
|
+
this.name = 'WikiError';
|
|
24
|
+
}
|
|
25
|
+
static is(error) {
|
|
26
|
+
return error instanceof WikiError;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.WikiError = WikiError;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type LogLevelName = 'debug' | 'info' | 'warn' | 'error';
|
|
2
|
+
export declare class Logger {
|
|
3
|
+
private readonly scope;
|
|
4
|
+
private static globalLevel;
|
|
5
|
+
constructor(scope: string);
|
|
6
|
+
static setLevel(level: LogLevelName): void;
|
|
7
|
+
private shouldLog;
|
|
8
|
+
private format;
|
|
9
|
+
private stringify;
|
|
10
|
+
debug(message: string, ...extra: unknown[]): void;
|
|
11
|
+
info(message: string, ...extra: unknown[]): void;
|
|
12
|
+
warn(message: string, ...extra: unknown[]): void;
|
|
13
|
+
error(message: string, ...extra: unknown[]): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Logger = void 0;
|
|
4
|
+
const levelWeights = {
|
|
5
|
+
debug: 10,
|
|
6
|
+
info: 20,
|
|
7
|
+
warn: 30,
|
|
8
|
+
error: 40
|
|
9
|
+
};
|
|
10
|
+
class Logger {
|
|
11
|
+
constructor(scope) {
|
|
12
|
+
this.scope = scope;
|
|
13
|
+
}
|
|
14
|
+
static setLevel(level) {
|
|
15
|
+
if (levelWeights[level] !== undefined) {
|
|
16
|
+
Logger.globalLevel = level;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
shouldLog(level) {
|
|
20
|
+
return levelWeights[level] >= levelWeights[Logger.globalLevel];
|
|
21
|
+
}
|
|
22
|
+
format(level, message, extra) {
|
|
23
|
+
const timestamp = new Date().toISOString();
|
|
24
|
+
const context = extra && extra.length ? ' ' + extra.map(this.stringify).join(' ') : '';
|
|
25
|
+
return `[${timestamp}] [${level.toUpperCase()}] [${this.scope}] ${message}${context}`;
|
|
26
|
+
}
|
|
27
|
+
stringify(value) {
|
|
28
|
+
if (typeof value === 'string') {
|
|
29
|
+
if (value.length > 400) {
|
|
30
|
+
return `${value.slice(0, 400)}…`;
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
return JSON.stringify(value, (key, val) => {
|
|
36
|
+
if (typeof key === 'string' && key.toLowerCase().includes('password')) {
|
|
37
|
+
return '[REDACTED]';
|
|
38
|
+
}
|
|
39
|
+
return val;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return String(value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
debug(message, ...extra) {
|
|
47
|
+
if (this.shouldLog('debug')) {
|
|
48
|
+
process.stderr.write(this.format('debug', message, extra) + '\n');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
info(message, ...extra) {
|
|
52
|
+
if (this.shouldLog('info')) {
|
|
53
|
+
process.stderr.write(this.format('info', message, extra) + '\n');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
warn(message, ...extra) {
|
|
57
|
+
if (this.shouldLog('warn')) {
|
|
58
|
+
process.stderr.write(this.format('warn', message, extra) + '\n');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
error(message, ...extra) {
|
|
62
|
+
if (this.shouldLog('error')) {
|
|
63
|
+
process.stderr.write(this.format('error', message, extra) + '\n');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.Logger = Logger;
|
|
68
|
+
Logger.globalLevel = 'info';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface DownloadOptions {
|
|
2
|
+
url: string;
|
|
3
|
+
outputPath: string;
|
|
4
|
+
username?: string;
|
|
5
|
+
password?: string;
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
allowSelfSigned?: boolean;
|
|
8
|
+
requireAuth?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface DownloadResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
filepath: string;
|
|
13
|
+
size: number;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Download Manager with support for both authenticated (wiki) and public downloads
|
|
18
|
+
*/
|
|
19
|
+
export declare class DownloadManager {
|
|
20
|
+
private readonly logger;
|
|
21
|
+
private http?;
|
|
22
|
+
private auth?;
|
|
23
|
+
/**
|
|
24
|
+
* Download a file from a URL
|
|
25
|
+
* If requireAuth is true, use the AuthService for authentication
|
|
26
|
+
*/
|
|
27
|
+
download(options: DownloadOptions): Promise<DownloadResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Download with authentication (for wiki URLs)
|
|
30
|
+
*/
|
|
31
|
+
private downloadWithAuth;
|
|
32
|
+
/**
|
|
33
|
+
* Direct download without authentication (for public URLs)
|
|
34
|
+
*/
|
|
35
|
+
private downloadDirect;
|
|
36
|
+
private formatSize;
|
|
37
|
+
/**
|
|
38
|
+
* Extract filename from URL
|
|
39
|
+
*/
|
|
40
|
+
extractFilenameFromUrl(url: string): string | null;
|
|
41
|
+
}
|