openclaw-skills-cli 0.1.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.
@@ -0,0 +1,209 @@
1
+ # 📦 Instrucciones para Publicar CLI en npm
2
+
3
+ ## ✅ Preparación Completada
4
+
5
+ - ✅ Package renombrado a `openclaw-skills-cli` (sin scope)
6
+ - ✅ README.md creado para página npm
7
+ - ✅ .npmignore configurado
8
+ - ✅ CLI compilado y listo
9
+ - ✅ Todas las referencias actualizadas en el código
10
+
11
+ ---
12
+
13
+ ## 🚀 Pasos para Publicar (José)
14
+
15
+ ### **Paso 1: Crear Cuenta npm** (5 minutos)
16
+
17
+ 1. Ve a https://www.npmjs.com/signup
18
+ 2. Rellena el formulario:
19
+ - **Username:** `jotajota1302` (o el que prefieras)
20
+ - **Email:** Tu email
21
+ - **Password:** Contraseña segura
22
+ 3. Verifica tu email (te llegará un correo de confirmación)
23
+
24
+ ---
25
+
26
+ ### **Paso 2: Login desde la Terminal** (1 minuto)
27
+
28
+ Abre una terminal y ejecuta:
29
+
30
+ ```bash
31
+ npm login
32
+ ```
33
+
34
+ Te pedirá:
35
+ - **Username:** (el que elegiste)
36
+ - **Password:** (tu contraseña)
37
+ - **Email:** (tu email)
38
+
39
+ **Nota:** Si te pide autenticación web, sigue el link que te muestre.
40
+
41
+ ---
42
+
43
+ ### **Paso 3: Verificar que Estás Logueado**
44
+
45
+ ```bash
46
+ npm whoami
47
+ ```
48
+
49
+ Debería mostrar tu username. Si muestra error, repite `npm login`.
50
+
51
+ ---
52
+
53
+ ### **Paso 4: Publicar el CLI** (30 segundos)
54
+
55
+ ```bash
56
+ cd ~/Desktop/PROYECTOS/activos/openclaw-skills-registry/cli
57
+ npm publish
58
+ ```
59
+
60
+ **Resultado esperado:**
61
+ ```
62
+ + openclaw-skills-cli@0.1.0
63
+ ```
64
+
65
+ **Si sale error "name already taken":**
66
+ ```bash
67
+ # Edita package.json y cambia el nombre
68
+ nano package.json
69
+ # Cambia "openclaw-skills-cli" por "openclaw-skills" o "skills-cli-openclaw"
70
+ # Luego repite: npm publish
71
+ ```
72
+
73
+ ---
74
+
75
+ ### **Paso 5: Verificar Publicación** (1 minuto)
76
+
77
+ Abre una nueva terminal (sin el proyecto) y prueba:
78
+
79
+ ```bash
80
+ npx openclaw-skills-cli@latest --version
81
+ ```
82
+
83
+ Debería mostrar: `0.1.0`
84
+
85
+ ---
86
+
87
+ ### **Paso 6: Test Completo**
88
+
89
+ Prueba instalar una skill:
90
+
91
+ ```bash
92
+ npx openclaw-skills-cli search security
93
+ ```
94
+
95
+ Debería mostrar resultados de skills de seguridad.
96
+
97
+ ---
98
+
99
+ ## ✅ Después de Publicar
100
+
101
+ Una vez publicado, **TODOS los comandos en /clients funcionarán:**
102
+
103
+ ```bash
104
+ # Esto ya funcionará:
105
+ npx openclaw-skills-cli install skillia/ai-governance-audit
106
+
107
+ # Y esto también:
108
+ npm i -g openclaw-skills-cli
109
+ openclaw-skills install skill/name
110
+ ```
111
+
112
+ ---
113
+
114
+ ## 🎯 Verificación Final
115
+
116
+ 1. Ve a: https://www.npmjs.com/package/openclaw-skills-cli
117
+ 2. Deberías ver tu paquete publicado con README, versión, stats, etc.
118
+
119
+ ---
120
+
121
+ ## 📊 Métricas npm
122
+
123
+ Después de publicar, podrás ver en npm:
124
+ - Descargas semanales
125
+ - Dependents (quién usa tu paquete)
126
+ - GitHub stars
127
+ - Versiones publicadas
128
+
129
+ ---
130
+
131
+ ## 🔄 Publicar Actualizaciones Futuras
132
+
133
+ Cuando quieras publicar una nueva versión:
134
+
135
+ ```bash
136
+ cd cli
137
+
138
+ # Opción A: Patch (0.1.0 → 0.1.1)
139
+ npm version patch
140
+
141
+ # Opción B: Minor (0.1.0 → 0.2.0)
142
+ npm version minor
143
+
144
+ # Opción C: Major (0.1.0 → 1.0.0)
145
+ npm version major
146
+
147
+ # Publicar
148
+ npm publish
149
+ ```
150
+
151
+ ---
152
+
153
+ ## ⚠️ Troubleshooting
154
+
155
+ ### Error: "You must be logged in"
156
+ ```bash
157
+ npm logout
158
+ npm login
159
+ ```
160
+
161
+ ### Error: "402 Payment Required"
162
+ Solo para scoped packages (@org/pkg). Nosotros usamos `openclaw-skills-cli` (sin scope), así que NO deberías ver este error.
163
+
164
+ ### Error: "403 Forbidden"
165
+ ```bash
166
+ # Verifica que estás logueado:
167
+ npm whoami
168
+
169
+ # Si no muestra tu usuario:
170
+ npm login
171
+ ```
172
+
173
+ ### Error: "name already taken"
174
+ Cambia el nombre en `package.json` y repite:
175
+ ```bash
176
+ # Opciones alternativas:
177
+ "name": "openclaw-skills"
178
+ "name": "skills-cli-openclaw"
179
+ "name": "openclaw-registry-cli"
180
+ ```
181
+
182
+ ---
183
+
184
+ ## 📞 Si Tienes Problemas
185
+
186
+ Avísame por Telegram y te ayudo en tiempo real.
187
+
188
+ ---
189
+
190
+ ## 🎉 Resultado Esperado
191
+
192
+ **Antes:**
193
+ ```bash
194
+ $ npx openclaw-skills-cli install skill
195
+ → 404 Not Found
196
+ ```
197
+
198
+ **Después:**
199
+ ```bash
200
+ $ npx openclaw-skills-cli install skill
201
+ → ✓ Installing skill...
202
+ ```
203
+
204
+ **Clientes podrán instalar skills sin problemas** ✅
205
+
206
+ ---
207
+
208
+ **Preparado por:** JARVIS
209
+ **Fecha:** 2026-02-27 18:05 GMT+1
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # openclaw-skills-cli
2
+
3
+ Official CLI for [Skillia](https://openclaw-skills-registry-dashboard.vercel.app) - The OpenClaw Skills Registry.
4
+
5
+ ## Installation
6
+
7
+ ### Quick Run (npx)
8
+ ```bash
9
+ npx openclaw-skills-cli install namespace/skill-name
10
+ ```
11
+
12
+ ### Global Install
13
+ ```bash
14
+ npm install -g openclaw-skills-cli
15
+ openclaw-skills --help
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### Search Skills
21
+ ```bash
22
+ openclaw-skills search security
23
+ ```
24
+
25
+ ### Install a Skill
26
+ ```bash
27
+ # Free skills
28
+ openclaw-skills install skillia/ai-governance-audit
29
+
30
+ # Paid skills (requires license token)
31
+ openclaw-skills install --license-token <token> namespace/skill-name
32
+ ```
33
+
34
+ ### Publish a Skill
35
+
36
+ 1. Create an API token at [Skillia Account](https://openclaw-skills-registry-dashboard.vercel.app/account)
37
+ 2. Login with your token:
38
+ ```bash
39
+ openclaw-skills login <your-api-token>
40
+ ```
41
+ 3. Publish your skill:
42
+ ```bash
43
+ cd my-skill-folder
44
+ openclaw-skills publish
45
+ ```
46
+
47
+ ## Commands
48
+
49
+ - `login <token>` - Authenticate with your API token
50
+ - `search [query]` - Search for skills in the registry
51
+ - `install <namespace/name>` - Install a skill
52
+ - `publish [folder]` - Publish a skill to the registry
53
+ - `import` - Import MCP servers from Anthropic registry
54
+
55
+ ## Configuration
56
+
57
+ The CLI stores configuration in `~/.config/openclaw-skills/config.json`.
58
+
59
+ ## API
60
+
61
+ Default API URL: `https://openclaw-skills-registry.onrender.com`
62
+
63
+ Override with `--api-url` flag:
64
+ ```bash
65
+ openclaw-skills --api-url http://localhost:3000 search test
66
+ ```
67
+
68
+ ## License
69
+
70
+ MIT
71
+
72
+ ## Links
73
+
74
+ - [Skillia Marketplace](https://openclaw-skills-registry-dashboard.vercel.app)
75
+ - [Documentation](https://openclaw-skills-registry-dashboard.vercel.app/developers)
76
+ - [GitHub](https://github.com/jotajota1302/skills-registry)
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createToken = createToken;
7
+ exports.listSkills = listSkills;
8
+ exports.getSkill = getSkill;
9
+ exports.publishSkill = publishSkill;
10
+ exports.getInstallConfig = getInstallConfig;
11
+ exports.getVersion = getVersion;
12
+ exports.listVersions = listVersions;
13
+ exports.publishVersion = publishVersion;
14
+ exports.registerInstall = registerInstall;
15
+ exports.triggerMcpImport = triggerMcpImport;
16
+ exports.checkHealth = checkHealth;
17
+ exports.getInstallPack = getInstallPack;
18
+ exports.consumeInstallPack = consumeInstallPack;
19
+ const axios_1 = __importDefault(require("axios"));
20
+ const config_js_1 = require("../config.js");
21
+ function createClient() {
22
+ const client = axios_1.default.create({
23
+ baseURL: (0, config_js_1.getApiUrl)(),
24
+ timeout: 15000,
25
+ });
26
+ client.interceptors.request.use((config) => {
27
+ const token = (0, config_js_1.getToken)();
28
+ if (token) {
29
+ config.headers.Authorization = `Bearer ${token}`;
30
+ }
31
+ return config;
32
+ });
33
+ return client;
34
+ }
35
+ async function createToken(clientId, secret) {
36
+ const client = createClient();
37
+ const { data } = await client.post('/v1/auth/token', { clientId, secret });
38
+ return data;
39
+ }
40
+ async function listSkills(opts) {
41
+ const client = createClient();
42
+ const params = {
43
+ q: opts.q,
44
+ keyword: opts.keyword,
45
+ page: opts.page,
46
+ limit: opts.limit,
47
+ sort: opts.sort,
48
+ type: opts.type,
49
+ tier: opts.tier,
50
+ };
51
+ if (opts.certified) {
52
+ params.certified = 'true';
53
+ }
54
+ const { data } = await client.get('/v1/skills', { params });
55
+ return data;
56
+ }
57
+ async function getSkill(namespace, name) {
58
+ const client = createClient();
59
+ const { data } = await client.get(`/v1/skills/${namespace}/${name}`);
60
+ return data;
61
+ }
62
+ async function publishSkill(skillData) {
63
+ const client = createClient();
64
+ const { data } = await client.post('/v1/skills', skillData);
65
+ return data;
66
+ }
67
+ async function getInstallConfig(namespace, name) {
68
+ const client = createClient();
69
+ const { data } = await client.get(`/v1/skills/${namespace}/${name}/install-config`);
70
+ return data;
71
+ }
72
+ async function getVersion(namespace, name, version) {
73
+ const client = createClient();
74
+ const { data } = await client.get(`/v1/skills/${namespace}/${name}/${version}`);
75
+ return data;
76
+ }
77
+ async function listVersions(namespace, name) {
78
+ const client = createClient();
79
+ const { data } = await client.get(`/v1/skills/${namespace}/${name}/versions`);
80
+ return data;
81
+ }
82
+ async function publishVersion(namespace, name, versionData) {
83
+ const client = createClient();
84
+ const { data } = await client.post(`/v1/skills/${namespace}/${name}/versions`, versionData);
85
+ return data;
86
+ }
87
+ async function registerInstall(namespace, name, version) {
88
+ const client = createClient();
89
+ const { data } = await client.post(`/v1/skills/${namespace}/${name}/installs`, version ? { version } : {});
90
+ return data;
91
+ }
92
+ async function triggerMcpImport(opts) {
93
+ const client = createClient();
94
+ const params = {
95
+ dryRun: opts.dryRun ? 'true' : undefined,
96
+ limit: opts.limit,
97
+ };
98
+ const { data } = await client.post('/v1/admin/import/mcp', undefined, { params });
99
+ return data;
100
+ }
101
+ async function checkHealth() {
102
+ const client = createClient();
103
+ const { data } = await client.get('/v1/health');
104
+ return data;
105
+ }
106
+ async function getInstallPack(token) {
107
+ const client = createClient();
108
+ const { data } = await client.get(`/v1/install-packs/${token}`);
109
+ return data;
110
+ }
111
+ async function consumeInstallPack(token) {
112
+ const client = createClient();
113
+ const { data } = await client.post(`/v1/install-packs/${token}/consume`);
114
+ return data;
115
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const config_js_1 = require("./config.js");
6
+ const login_js_1 = require("./commands/login.js");
7
+ const search_js_1 = require("./commands/search.js");
8
+ const install_js_1 = require("./commands/install.js");
9
+ const publish_js_1 = require("./commands/publish.js");
10
+ const import_js_1 = require("./commands/import.js");
11
+ const program = new commander_1.Command();
12
+ program
13
+ .name('openclaw-skills')
14
+ .description('CLI del OpenClaw Skills Registry')
15
+ .version('0.1.0')
16
+ .option('--api-url <url>', 'URL de la API del registry')
17
+ .hook('preAction', (thisCommand) => {
18
+ const opts = thisCommand.opts();
19
+ if (opts.apiUrl) {
20
+ (0, config_js_1.setApiUrl)(opts.apiUrl);
21
+ }
22
+ });
23
+ program.addCommand(login_js_1.loginCommand);
24
+ program.addCommand(search_js_1.searchCommand);
25
+ program.addCommand(install_js_1.installCommand);
26
+ program.addCommand(publish_js_1.publishCommand);
27
+ program.addCommand(import_js_1.importCommand);
28
+ program.parse();
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.importCommand = void 0;
40
+ const commander_1 = require("commander");
41
+ const chalk_1 = __importDefault(require("chalk"));
42
+ const ora_1 = __importDefault(require("ora"));
43
+ const axios_1 = __importDefault(require("axios"));
44
+ const fs = __importStar(require("node:fs"));
45
+ const path = __importStar(require("node:path"));
46
+ const client_js_1 = require("../api/client.js");
47
+ const errors_js_1 = require("../utils/errors.js");
48
+ const config_js_1 = require("../config.js");
49
+ const EXCLUDED_DIRS = new Set(['node_modules', '.git', '.next', 'dist', 'build', 'coverage']);
50
+ function readFilesRecursive(dir, baseDir) {
51
+ const files = {};
52
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
53
+ for (const entry of entries) {
54
+ if (EXCLUDED_DIRS.has(entry.name))
55
+ continue;
56
+ if (entry.name.startsWith('.env'))
57
+ continue;
58
+ const fullPath = path.join(dir, entry.name);
59
+ const relativePath = path.relative(baseDir, fullPath).replace(/\\/g, '/');
60
+ if (entry.isDirectory()) {
61
+ Object.assign(files, readFilesRecursive(fullPath, baseDir));
62
+ continue;
63
+ }
64
+ try {
65
+ files[relativePath] = fs.readFileSync(fullPath, 'utf-8');
66
+ }
67
+ catch {
68
+ // ignore binary/unreadable files
69
+ }
70
+ }
71
+ return files;
72
+ }
73
+ const mcpImportCommand = new commander_1.Command('mcp')
74
+ .description('Importar servidores MCP desde el registry oficial')
75
+ .option('--dry-run', 'Simula import sin escribir en DB')
76
+ .option('--limit <n>', 'Limita número de registros procesados')
77
+ .action(async (opts) => {
78
+ const spinner = (0, ora_1.default)('Importando MCP registry...').start();
79
+ try {
80
+ const result = await (0, client_js_1.triggerMcpImport)({
81
+ dryRun: !!opts.dryRun,
82
+ limit: opts.limit ? Number(opts.limit) : undefined,
83
+ });
84
+ spinner.succeed('Import completado');
85
+ const stats = result?.result || {};
86
+ console.log(chalk_1.default.bold('\nMCP Import Result'));
87
+ console.log(`dryRun: ${result?.dryRun ? 'true' : 'false'}`);
88
+ console.log(`scanned: ${stats.scanned ?? 0}`);
89
+ console.log(`imported: ${stats.imported ?? 0}`);
90
+ console.log(`updated: ${stats.updated ?? 0}`);
91
+ console.log(`skipped: ${stats.skipped ?? 0}`);
92
+ }
93
+ catch (error) {
94
+ spinner.fail('Error importando MCP registry');
95
+ (0, errors_js_1.handleApiError)(error);
96
+ }
97
+ });
98
+ const domainImportCommand = new commander_1.Command('domain')
99
+ .description('Publicar una domain skill local en el registry (no interactivo)')
100
+ .argument('<folder>', 'Carpeta local de la skill')
101
+ .option('--namespace <namespace>', 'Override namespace')
102
+ .option('--name <name>', 'Override nombre de skill')
103
+ .option('--version <version>', 'Override versión')
104
+ .option('--tier <tier>', 'Tier: free|pro|enterprise')
105
+ .option('--description <description>', 'Override descripción')
106
+ .action(async (folder, opts) => {
107
+ const token = (0, config_js_1.getToken)();
108
+ if (!token) {
109
+ console.log(chalk_1.default.red("Ejecuta 'openclaw-skills login <token>' primero"));
110
+ process.exit(1);
111
+ }
112
+ try {
113
+ const resolvedFolder = path.resolve(folder);
114
+ const skillJsonPath = path.join(resolvedFolder, 'skill.json');
115
+ const skillMdPath = path.join(resolvedFolder, 'SKILL.md');
116
+ if (!fs.existsSync(skillMdPath)) {
117
+ throw new Error(`No se encontró SKILL.md en ${resolvedFolder}`);
118
+ }
119
+ if (!fs.existsSync(skillJsonPath)) {
120
+ throw new Error(`No se encontró skill.json en ${resolvedFolder}`);
121
+ }
122
+ const metadata = JSON.parse(fs.readFileSync(skillJsonPath, 'utf-8'));
123
+ const namespace = opts.namespace || metadata.namespace;
124
+ const name = opts.name || metadata.name;
125
+ const version = opts.version || metadata.version;
126
+ const description = opts.description || metadata.description;
127
+ const tier = opts.tier || metadata.tier || 'free';
128
+ const keywords = metadata.keywords || [];
129
+ const type = (metadata.type || 'DOMAIN').toUpperCase();
130
+ if (!namespace || !name || !description || !version) {
131
+ throw new Error('skill.json requiere namespace, name, description y version');
132
+ }
133
+ if (type !== 'DOMAIN') {
134
+ throw new Error(`Este comando solo publica DOMAIN. Recibido: ${type}`);
135
+ }
136
+ const files = readFilesRecursive(resolvedFolder, resolvedFolder);
137
+ const spinner = (0, ora_1.default)(`Publicando domain skill ${chalk_1.default.cyan(namespace)}/${chalk_1.default.bold(name)}@${version}...`).start();
138
+ try {
139
+ await (0, client_js_1.publishSkill)({
140
+ namespace,
141
+ name,
142
+ description,
143
+ keywords,
144
+ files,
145
+ version,
146
+ type,
147
+ tier,
148
+ });
149
+ spinner.succeed(chalk_1.default.green(`Domain skill publicada: ${namespace}/${name}@${version}`));
150
+ }
151
+ catch (error) {
152
+ if (axios_1.default.isAxiosError(error) && error.response?.status === 409) {
153
+ await (0, client_js_1.publishVersion)(namespace, name, { version, files, changelog: 'Imported from local folder via CLI import domain' });
154
+ spinner.succeed(chalk_1.default.green(`Versión publicada sobre skill existente: ${namespace}/${name}@${version}`));
155
+ }
156
+ else {
157
+ spinner.fail('Error publicando domain skill');
158
+ throw error;
159
+ }
160
+ }
161
+ }
162
+ catch (error) {
163
+ (0, errors_js_1.handleApiError)(error);
164
+ }
165
+ });
166
+ exports.importCommand = new commander_1.Command('import')
167
+ .description('Comandos de importación')
168
+ .addCommand(mcpImportCommand)
169
+ .addCommand(domainImportCommand);
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.installCommand = void 0;
40
+ const commander_1 = require("commander");
41
+ const chalk_1 = __importDefault(require("chalk"));
42
+ const ora_1 = __importDefault(require("ora"));
43
+ const fs = __importStar(require("node:fs"));
44
+ const path = __importStar(require("node:path"));
45
+ const os = __importStar(require("node:os"));
46
+ const prompts_1 = require("@inquirer/prompts");
47
+ const client_js_1 = require("../api/client.js");
48
+ const errors_js_1 = require("../utils/errors.js");
49
+ function parseSkillArg(arg) {
50
+ const atIndex = arg.lastIndexOf('@');
51
+ let fullName;
52
+ let version;
53
+ if (atIndex > 0) {
54
+ fullName = arg.substring(0, atIndex);
55
+ version = arg.substring(atIndex + 1);
56
+ }
57
+ else {
58
+ fullName = arg;
59
+ }
60
+ const slashIndex = fullName.lastIndexOf('/');
61
+ if (slashIndex === -1) {
62
+ throw new Error(`Formato inválido. Usa: namespace/name[@version]`);
63
+ }
64
+ return {
65
+ namespace: fullName.substring(0, slashIndex),
66
+ name: fullName.substring(slashIndex + 1),
67
+ version,
68
+ };
69
+ }
70
+ function detectMcpClients() {
71
+ const clients = [];
72
+ const home = os.homedir();
73
+ if (fs.existsSync(path.join(home, '.claude')))
74
+ clients.push('claude');
75
+ if (fs.existsSync(path.join(home, '.codex')))
76
+ clients.push('codex');
77
+ if (fs.existsSync(path.join(home, '.cursor')))
78
+ clients.push('cursor');
79
+ return clients;
80
+ }
81
+ const MCP_CONFIG_PATHS = {
82
+ claude: '.claude/mcp.json',
83
+ codex: '.codex/mcp_config.json',
84
+ cursor: '.cursor/mcp.json',
85
+ };
86
+ function writeMcpEntry(client, skillName, mcpConfig) {
87
+ const home = os.homedir();
88
+ const configRelPath = MCP_CONFIG_PATHS[client];
89
+ if (!configRelPath)
90
+ return;
91
+ const configPath = path.join(home, configRelPath);
92
+ let existing = {};
93
+ if (fs.existsSync(configPath)) {
94
+ try {
95
+ existing = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
96
+ }
97
+ catch {
98
+ existing = {};
99
+ }
100
+ }
101
+ else {
102
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
103
+ }
104
+ const serversKey = 'mcpServers';
105
+ if (!existing[serversKey] || typeof existing[serversKey] !== 'object') {
106
+ existing[serversKey] = {};
107
+ }
108
+ const entry = {};
109
+ if (mcpConfig.url) {
110
+ entry.url = mcpConfig.url;
111
+ }
112
+ if (mcpConfig.command) {
113
+ entry.command = mcpConfig.command;
114
+ }
115
+ if (mcpConfig.args) {
116
+ entry.args = mcpConfig.args;
117
+ }
118
+ if (mcpConfig.env) {
119
+ entry.env = mcpConfig.env;
120
+ }
121
+ existing[serversKey][skillName] = entry;
122
+ fs.writeFileSync(configPath, JSON.stringify(existing, null, 2), 'utf-8');
123
+ }
124
+ async function installToolOrDomain(namespace, name, targetVersion, installConfig) {
125
+ // Check subscription requirement for DOMAIN
126
+ if (installConfig.type === 'DOMAIN' && installConfig.requiresSubscription) {
127
+ console.log(chalk_1.default.yellow('\n⚠️ Este skill requiere suscripción Pro. Contacta con registry para activarla.'));
128
+ }
129
+ const installDir = path.join(os.homedir(), '.openclaw', 'skills', namespace, name);
130
+ if (fs.existsSync(installDir)) {
131
+ const overwrite = await (0, prompts_1.confirm)({
132
+ message: `${namespace}/${name} ya está instalado. ¿Sobreescribir?`,
133
+ default: false,
134
+ });
135
+ if (!overwrite) {
136
+ console.log(chalk_1.default.yellow('Instalación cancelada'));
137
+ return;
138
+ }
139
+ fs.rmSync(installDir, { recursive: true, force: true });
140
+ }
141
+ const spinner = (0, ora_1.default)(`Instalando ${chalk_1.default.cyan(namespace)}/${chalk_1.default.bold(name)}${chalk_1.default.gray('@' + targetVersion)}...`).start();
142
+ // Try direct download from filesUrl first, fallback to API
143
+ const installConfigData = await (0, client_js_1.getInstallConfig)(namespace, name);
144
+ let files;
145
+ if ('filesUrl' in installConfigData && installConfigData.filesUrl) {
146
+ // Download from Supabase Storage
147
+ const response = await fetch(installConfigData.filesUrl);
148
+ if (response.ok) {
149
+ files = await response.json();
150
+ }
151
+ else {
152
+ // Fallback to version API
153
+ const versionData = await (0, client_js_1.getVersion)(namespace, name, targetVersion);
154
+ files = versionData.files || {};
155
+ }
156
+ }
157
+ else {
158
+ const versionData = await (0, client_js_1.getVersion)(namespace, name, targetVersion);
159
+ files = versionData.files || {};
160
+ }
161
+ for (const [filePath, content] of Object.entries(files)) {
162
+ const fullPath = path.join(installDir, filePath);
163
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
164
+ fs.writeFileSync(fullPath, content, 'utf-8');
165
+ }
166
+ try {
167
+ await (0, client_js_1.registerInstall)(namespace, name, targetVersion);
168
+ }
169
+ catch {
170
+ // Install tracking is best-effort
171
+ }
172
+ spinner.succeed(chalk_1.default.green(`Instalado en ~/.openclaw/skills/${namespace}/${name}/`));
173
+ }
174
+ async function installMcpServer(name, mcpConfig, targets, targetFlag) {
175
+ let selectedClients;
176
+ if (targetFlag === 'all') {
177
+ selectedClients = detectMcpClients();
178
+ if (selectedClients.length === 0) {
179
+ console.log(chalk_1.default.red('No se detectaron clientes MCP instalados (claude, codex, cursor)'));
180
+ return;
181
+ }
182
+ }
183
+ else if (targetFlag) {
184
+ selectedClients = [targetFlag];
185
+ }
186
+ else {
187
+ // Auto-detect and ask
188
+ const available = detectMcpClients();
189
+ if (available.length === 0) {
190
+ console.log(chalk_1.default.red('No se detectaron clientes MCP instalados (claude, codex, cursor)'));
191
+ return;
192
+ }
193
+ if (available.length === 1) {
194
+ selectedClients = available;
195
+ }
196
+ else {
197
+ const chosen = await (0, prompts_1.select)({
198
+ message: '¿En qué cliente MCP quieres instalar?',
199
+ choices: [
200
+ ...available.map((c) => ({ name: c, value: c })),
201
+ { name: 'Todos', value: 'all' },
202
+ ],
203
+ });
204
+ selectedClients = chosen === 'all' ? available : [chosen];
205
+ }
206
+ }
207
+ const spinner = (0, ora_1.default)(`Configurando MCP server ${chalk_1.default.bold(name)}...`).start();
208
+ for (const client of selectedClients) {
209
+ writeMcpEntry(client, name, mcpConfig);
210
+ }
211
+ spinner.succeed(chalk_1.default.green(`MCP server ${chalk_1.default.bold(name)} configurado en: ${selectedClients.join(', ')}`));
212
+ }
213
+ exports.installCommand = new commander_1.Command('install')
214
+ .description('Instalar un skill desde el registry')
215
+ .argument('[skill]', 'Skill a instalar (namespace/name[@version])')
216
+ .option('--target <target>', 'Cliente MCP objetivo: claude, codex, cursor, all')
217
+ .option('--license-token <token>', 'Token de licencia del install pack')
218
+ .action(async (skillArg, opts) => {
219
+ try {
220
+ let namespace;
221
+ let name;
222
+ let version;
223
+ if (opts.licenseToken) {
224
+ const consumed = await (0, client_js_1.consumeInstallPack)(opts.licenseToken);
225
+ if (!consumed.ok || !consumed.install) {
226
+ throw new Error(consumed.error || 'No se pudo validar el license token');
227
+ }
228
+ namespace = consumed.install.namespace;
229
+ name = consumed.install.name;
230
+ version = consumed.install.version || undefined;
231
+ }
232
+ else {
233
+ if (!skillArg) {
234
+ throw new Error('Debes indicar un skill o usar --license-token');
235
+ }
236
+ const parsed = parseSkillArg(skillArg);
237
+ namespace = parsed.namespace;
238
+ name = parsed.name;
239
+ version = parsed.version;
240
+ }
241
+ // Resolve version
242
+ let targetVersion = version;
243
+ if (!targetVersion) {
244
+ const skill = await (0, client_js_1.getSkill)(namespace, name);
245
+ const latest = skill.versions?.[0];
246
+ if (!latest) {
247
+ console.log(chalk_1.default.red('Este skill no tiene versiones publicadas'));
248
+ return;
249
+ }
250
+ targetVersion = latest.version;
251
+ }
252
+ // Get install config to determine type
253
+ const installConfig = await (0, client_js_1.getInstallConfig)(namespace, name);
254
+ if (installConfig.type === 'MCP_SERVER') {
255
+ await installMcpServer(name, installConfig.mcpConfig, installConfig.targets, opts.target);
256
+ }
257
+ else {
258
+ // TOOL or DOMAIN — file-based installation
259
+ await installToolOrDomain(namespace, name, targetVersion, installConfig);
260
+ }
261
+ // Register install (best-effort)
262
+ try {
263
+ await (0, client_js_1.registerInstall)(namespace, name, targetVersion);
264
+ }
265
+ catch {
266
+ // best-effort
267
+ }
268
+ }
269
+ catch (error) {
270
+ (0, errors_js_1.handleApiError)(error);
271
+ }
272
+ });
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loginCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const ora_1 = __importDefault(require("ora"));
10
+ const config_js_1 = require("../config.js");
11
+ const client_js_1 = require("../api/client.js");
12
+ const errors_js_1 = require("../utils/errors.js");
13
+ exports.loginCommand = new commander_1.Command('login')
14
+ .description('Guardar token de autenticación')
15
+ .argument('<token>', 'Token de acceso')
16
+ .action(async (token) => {
17
+ const spinner = (0, ora_1.default)('Verificando token...').start();
18
+ try {
19
+ await (0, client_js_1.checkHealth)();
20
+ (0, config_js_1.setToken)(token);
21
+ spinner.succeed(chalk_1.default.green('Token guardado correctamente'));
22
+ }
23
+ catch (error) {
24
+ spinner.fail('Error al verificar conexión con la API');
25
+ (0, errors_js_1.handleApiError)(error);
26
+ }
27
+ });
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.publishCommand = void 0;
40
+ const commander_1 = require("commander");
41
+ const chalk_1 = __importDefault(require("chalk"));
42
+ const ora_1 = __importDefault(require("ora"));
43
+ const fs = __importStar(require("node:fs"));
44
+ const path = __importStar(require("node:path"));
45
+ const prompts_1 = require("@inquirer/prompts");
46
+ const config_js_1 = require("../config.js");
47
+ const client_js_1 = require("../api/client.js");
48
+ const errors_js_1 = require("../utils/errors.js");
49
+ const axios_1 = __importDefault(require("axios"));
50
+ const EXCLUDED_DIRS = ['node_modules', '.git', '.env', 'dist', '__pycache__'];
51
+ function readFilesRecursive(dir, baseDir) {
52
+ const files = {};
53
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
54
+ for (const entry of entries) {
55
+ if (EXCLUDED_DIRS.includes(entry.name))
56
+ continue;
57
+ if (entry.name.startsWith('.env'))
58
+ continue;
59
+ const fullPath = path.join(dir, entry.name);
60
+ const relativePath = path.relative(baseDir, fullPath);
61
+ if (entry.isDirectory()) {
62
+ Object.assign(files, readFilesRecursive(fullPath, baseDir));
63
+ }
64
+ else {
65
+ try {
66
+ files[relativePath] = fs.readFileSync(fullPath, 'utf-8');
67
+ }
68
+ catch {
69
+ // Skip binary/unreadable files
70
+ }
71
+ }
72
+ }
73
+ return files;
74
+ }
75
+ exports.publishCommand = new commander_1.Command('publish')
76
+ .description('Publicar un skill en el registry')
77
+ .argument('[folder]', 'Carpeta del skill', './')
78
+ .action(async (folder) => {
79
+ try {
80
+ const token = (0, config_js_1.getToken)();
81
+ if (!token) {
82
+ console.log(chalk_1.default.red("Ejecuta 'openclaw-skills login <token>' primero"));
83
+ process.exit(1);
84
+ }
85
+ const resolvedFolder = path.resolve(folder);
86
+ const skillMdPath = path.join(resolvedFolder, 'SKILL.md');
87
+ if (!fs.existsSync(skillMdPath)) {
88
+ console.log(chalk_1.default.red('No se encontró SKILL.md en ' + resolvedFolder));
89
+ console.log(chalk_1.default.yellow('SKILL.md es obligatorio para publicar un skill'));
90
+ process.exit(1);
91
+ }
92
+ // Try to read metadata from skill.json or package.json
93
+ let metadata = {};
94
+ const skillJsonPath = path.join(resolvedFolder, 'skill.json');
95
+ const packageJsonPath = path.join(resolvedFolder, 'package.json');
96
+ if (fs.existsSync(skillJsonPath)) {
97
+ metadata = JSON.parse(fs.readFileSync(skillJsonPath, 'utf-8'));
98
+ }
99
+ else if (fs.existsSync(packageJsonPath)) {
100
+ metadata = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
101
+ }
102
+ const folderName = path.basename(resolvedFolder);
103
+ // Skill type selection
104
+ const skillType = await (0, prompts_1.select)({
105
+ message: 'Tipo de skill:',
106
+ choices: [
107
+ { name: '🔧 Tool integration', value: 'TOOL' },
108
+ { name: '⚡ MCP Server', value: 'MCP_SERVER' },
109
+ { name: '🧠 Domain skill', value: 'DOMAIN' },
110
+ ],
111
+ default: metadata.type || 'TOOL',
112
+ });
113
+ // MCP config if MCP_SERVER
114
+ let mcpConfig;
115
+ if (skillType === 'MCP_SERVER') {
116
+ console.log(chalk_1.default.cyan('\nConfiguración MCP Server:'));
117
+ const transport = await (0, prompts_1.select)({
118
+ message: 'Tipo de transporte:',
119
+ choices: [
120
+ { name: 'HTTP (URL remota)', value: 'http' },
121
+ { name: 'stdio (comando local)', value: 'stdio' },
122
+ ],
123
+ });
124
+ mcpConfig = { transport };
125
+ if (transport === 'http') {
126
+ const url = await (0, prompts_1.input)({
127
+ message: 'URL del MCP server:',
128
+ validate: (v) => (v.startsWith('http') ? true : 'Debe ser una URL válida'),
129
+ });
130
+ mcpConfig.url = url;
131
+ }
132
+ else {
133
+ const command = await (0, prompts_1.input)({
134
+ message: 'Comando a ejecutar:',
135
+ validate: (v) => (v.length > 0 ? true : 'El comando es obligatorio'),
136
+ });
137
+ mcpConfig.command = command;
138
+ const argsInput = await (0, prompts_1.input)({
139
+ message: 'Argumentos (separados por espacio, vacío para ninguno):',
140
+ });
141
+ if (argsInput.trim()) {
142
+ mcpConfig.args = argsInput.trim().split(/\s+/);
143
+ }
144
+ }
145
+ }
146
+ // Tier selection
147
+ const tier = await (0, prompts_1.select)({
148
+ message: 'Tier:',
149
+ choices: [
150
+ { name: 'Free', value: 'free' },
151
+ { name: 'Pro', value: 'pro' },
152
+ { name: 'Enterprise', value: 'enterprise' },
153
+ ],
154
+ default: metadata.tier || 'free',
155
+ });
156
+ const namespace = await (0, prompts_1.input)({
157
+ message: 'Namespace:',
158
+ default: metadata.namespace || `io.github.${process.env.USER || 'user'}`,
159
+ });
160
+ const name = await (0, prompts_1.input)({
161
+ message: 'Nombre del skill:',
162
+ default: metadata.name || folderName,
163
+ });
164
+ const description = await (0, prompts_1.input)({
165
+ message: 'Descripción:',
166
+ default: metadata.description || '',
167
+ });
168
+ const version = await (0, prompts_1.input)({
169
+ message: 'Versión:',
170
+ default: metadata.version || '1.0.0',
171
+ });
172
+ const keywordsInput = await (0, prompts_1.input)({
173
+ message: 'Keywords (separadas por coma):',
174
+ default: Array.isArray(metadata.keywords)
175
+ ? metadata.keywords.join(', ')
176
+ : '',
177
+ });
178
+ const keywords = keywordsInput
179
+ .split(',')
180
+ .map((k) => k.trim())
181
+ .filter(Boolean);
182
+ const spinner = (0, ora_1.default)(`Publicando ${chalk_1.default.bold(name)}${chalk_1.default.gray('@' + version)}...`).start();
183
+ const files = readFilesRecursive(resolvedFolder, resolvedFolder);
184
+ try {
185
+ await (0, client_js_1.publishSkill)({
186
+ namespace,
187
+ name,
188
+ description,
189
+ keywords,
190
+ files,
191
+ version,
192
+ type: skillType,
193
+ mcpConfig,
194
+ tier,
195
+ });
196
+ spinner.succeed(chalk_1.default.green(`Skill publicado: ${namespace}/${name}`));
197
+ }
198
+ catch (error) {
199
+ if (axios_1.default.isAxiosError(error) && error.response?.status === 409) {
200
+ spinner.warn(chalk_1.default.yellow(`${namespace}/${name} ya existe en el registry`));
201
+ const publishNew = await (0, prompts_1.confirm)({
202
+ message: `¿Publicar nueva versión ${version}?`,
203
+ default: true,
204
+ });
205
+ if (!publishNew) {
206
+ console.log(chalk_1.default.yellow('Publicación cancelada'));
207
+ return;
208
+ }
209
+ const versionSpinner = (0, ora_1.default)(`Publicando versión ${version}...`).start();
210
+ await (0, client_js_1.publishVersion)(namespace, name, { version, files });
211
+ versionSpinner.succeed(chalk_1.default.green(`Versión ${version} publicada: ${namespace}/${name}`));
212
+ }
213
+ else {
214
+ spinner.fail('Error al publicar');
215
+ throw error;
216
+ }
217
+ }
218
+ }
219
+ catch (error) {
220
+ (0, errors_js_1.handleApiError)(error);
221
+ }
222
+ });
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.searchCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const ora_1 = __importDefault(require("ora"));
10
+ const client_js_1 = require("../api/client.js");
11
+ const errors_js_1 = require("../utils/errors.js");
12
+ const TYPE_EMOJI = {
13
+ TOOL: '🔧',
14
+ MCP_SERVER: '⚡',
15
+ DOMAIN: '🧠',
16
+ };
17
+ exports.searchCommand = new commander_1.Command('search')
18
+ .description('Buscar skills en el registry')
19
+ .argument('[query]', 'Término de búsqueda')
20
+ .option('--keyword <kw>', 'Filtrar por keyword')
21
+ .option('--sort <sort>', 'Ordenar por: installs, recent, name', 'recent')
22
+ .option('--limit <n>', 'Número de resultados', '10')
23
+ .option('--type <type>', 'Filtrar por tipo: TOOL, MCP_SERVER, DOMAIN')
24
+ .option('--certified', 'Mostrar solo skills certificados')
25
+ .action(async (query, opts) => {
26
+ const spinner = (0, ora_1.default)('Buscando skills...').start();
27
+ try {
28
+ const result = await (0, client_js_1.listSkills)({
29
+ q: query,
30
+ keyword: opts.keyword,
31
+ sort: opts.sort,
32
+ limit: parseInt(opts.limit, 10),
33
+ type: opts.type,
34
+ certified: opts.certified || false,
35
+ });
36
+ spinner.stop();
37
+ const skills = result.data;
38
+ if (!skills || skills.length === 0) {
39
+ console.log(chalk_1.default.yellow('No se encontraron skills para esa búsqueda'));
40
+ return;
41
+ }
42
+ console.log(`\n📦 ${chalk_1.default.bold(skills.length)} skills encontrados\n`);
43
+ for (const skill of skills) {
44
+ const latestVersion = skill.versions?.[0];
45
+ const version = latestVersion?.version || 'N/A';
46
+ const keywords = skill.keywords || [];
47
+ const skillType = skill.type || 'TOOL';
48
+ const emoji = TYPE_EMOJI[skillType] || '📦';
49
+ const tier = skill.tier || 'free';
50
+ let tierBadge = '';
51
+ if (tier === 'pro') {
52
+ tierBadge = chalk_1.default.yellow(' [PRO]');
53
+ }
54
+ else if (tier === 'enterprise') {
55
+ tierBadge = chalk_1.default.yellow(' [ENTERPRISE]');
56
+ }
57
+ const certBadge = skill.certified ? chalk_1.default.green(' ✓ certified') : '';
58
+ console.log(` ${emoji} ${chalk_1.default.cyan(skill.namespace)}/${chalk_1.default.bold(skill.name)}${chalk_1.default.gray('@' + version)}${tierBadge}${certBadge}`);
59
+ console.log(` ${skill.description || 'Sin descripción'}`);
60
+ if (keywords.length > 0) {
61
+ console.log(` Keywords: ${chalk_1.default.yellow(keywords.join(', '))}`);
62
+ }
63
+ console.log(` Installs: ${skill.totalInstalls ?? skill.installCount ?? 0}`);
64
+ console.log();
65
+ }
66
+ }
67
+ catch (error) {
68
+ spinner.fail('Error al buscar skills');
69
+ (0, errors_js_1.handleApiError)(error);
70
+ }
71
+ });
package/dist/config.js ADDED
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getToken = getToken;
7
+ exports.setToken = setToken;
8
+ exports.getApiUrl = getApiUrl;
9
+ exports.setApiUrl = setApiUrl;
10
+ const conf_1 = __importDefault(require("conf"));
11
+ const config = new conf_1.default({
12
+ projectName: 'openclaw-skills',
13
+ });
14
+ function getToken() {
15
+ return config.get('token');
16
+ }
17
+ function setToken(token) {
18
+ config.set('token', token);
19
+ }
20
+ function getApiUrl() {
21
+ const envUrl = process.env.SKILLS_REGISTRY_URL;
22
+ if (envUrl)
23
+ return envUrl;
24
+ return config.get('apiUrl') || 'http://localhost:3000';
25
+ }
26
+ function setApiUrl(url) {
27
+ config.set('apiUrl', url);
28
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handleApiError = handleApiError;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const axios_1 = __importDefault(require("axios"));
9
+ function handleApiError(error) {
10
+ if (axios_1.default.isAxiosError(error)) {
11
+ const status = error.response?.status;
12
+ const message = error.response?.data?.error || error.response?.data?.message;
13
+ if (status === 401) {
14
+ console.log(chalk_1.default.red('Token inválido o expirado. Ejecuta login de nuevo.'));
15
+ }
16
+ else if (status === 404) {
17
+ console.log(chalk_1.default.red('Skill no encontrado'));
18
+ }
19
+ else if (status === 409) {
20
+ console.log(chalk_1.default.red(message || 'Conflicto: el recurso ya existe'));
21
+ }
22
+ else if (error.code === 'ECONNREFUSED') {
23
+ console.log(chalk_1.default.red('No se pudo conectar con la API. ¿Está corriendo el servidor?'));
24
+ }
25
+ else {
26
+ console.log(chalk_1.default.red(message || `Error del servidor (${status || 'desconocido'})`));
27
+ }
28
+ }
29
+ else if (error instanceof Error) {
30
+ if (error.code === 'ECONNREFUSED') {
31
+ console.log(chalk_1.default.red('No se pudo conectar con la API. ¿Está corriendo el servidor?'));
32
+ }
33
+ else {
34
+ console.log(chalk_1.default.red(error.message));
35
+ }
36
+ }
37
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "openclaw-skills-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool for OpenClaw Skills Registry",
5
+ "main": "dist/cli.js",
6
+ "bin": {
7
+ "openclaw-skills": "./dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsx src/cli.ts",
12
+ "test": "jest",
13
+ "lint": "eslint src --ext .ts"
14
+ },
15
+ "keywords": [
16
+ "openclaw",
17
+ "skills",
18
+ "cli",
19
+ "registry"
20
+ ],
21
+ "author": "OpenClaw Team",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "@inquirer/prompts": "^8.2.1",
25
+ "ajv": "^8.12.0",
26
+ "axios": "^1.6.7",
27
+ "chalk": "^5.3.0",
28
+ "cli-progress": "^3.12.0",
29
+ "commander": "^12.0.0",
30
+ "conf": "^12.0.0",
31
+ "inquirer": "^9.2.15",
32
+ "marked": "^12.0.0",
33
+ "ora": "^8.0.1"
34
+ },
35
+ "devDependencies": {
36
+ "@types/inquirer": "^9.0.7",
37
+ "@types/node": "^20.11.20",
38
+ "@typescript-eslint/eslint-plugin": "^7.0.2",
39
+ "@typescript-eslint/parser": "^7.0.2",
40
+ "eslint": "^8.56.0",
41
+ "jest": "^29.7.0",
42
+ "ts-jest": "^29.1.2",
43
+ "tsx": "^4.7.1",
44
+ "typescript": "^5.3.3"
45
+ },
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ }
49
+ }