openpets 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data/api.json +6746 -4404
- package/dist/src/core/cli.js +1160 -36
- package/dist/src/core/cli.js.map +1 -1
- package/dist/src/core/discovery.d.ts +130 -0
- package/dist/src/core/discovery.d.ts.map +1 -0
- package/dist/src/core/discovery.js +327 -0
- package/dist/src/core/discovery.js.map +1 -0
- package/dist/src/core/index.d.ts +5 -5
- package/dist/src/core/index.d.ts.map +1 -1
- package/dist/src/core/index.js +7 -5
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/core/mautrix-bridge.d.ts +17 -0
- package/dist/src/core/mautrix-bridge.d.ts.map +1 -1
- package/dist/src/core/mautrix-bridge.js +257 -0
- package/dist/src/core/mautrix-bridge.js.map +1 -1
- package/dist/src/core/pet-config.d.ts +611 -0
- package/dist/src/core/pet-config.d.ts.map +1 -0
- package/dist/src/core/pet-config.js +281 -0
- package/dist/src/core/pet-config.js.map +1 -0
- package/dist/src/core/pet-downloader.d.ts +50 -0
- package/dist/src/core/pet-downloader.d.ts.map +1 -0
- package/dist/src/core/pet-downloader.js +298 -0
- package/dist/src/core/pet-downloader.js.map +1 -0
- package/dist/src/core/pet-scanner.d.ts +14 -0
- package/dist/src/core/pet-scanner.d.ts.map +1 -0
- package/dist/src/core/pet-scanner.js +87 -0
- package/dist/src/core/pet-scanner.js.map +1 -0
- package/dist/src/core/registry-client.d.ts +97 -0
- package/dist/src/core/registry-client.d.ts.map +1 -0
- package/dist/src/core/registry-client.js +283 -0
- package/dist/src/core/registry-client.js.map +1 -0
- package/dist/src/core/search-pets.d.ts.map +1 -1
- package/dist/src/core/search-pets.js +31 -1
- package/dist/src/core/search-pets.js.map +1 -1
- package/dist/src/core/validate-pet.d.ts.map +1 -1
- package/dist/src/core/validate-pet.js +17 -19
- package/dist/src/core/validate-pet.js.map +1 -1
- package/dist/src/sdk/logger.d.ts +32 -0
- package/dist/src/sdk/logger.d.ts.map +1 -0
- package/dist/src/sdk/logger.js +119 -0
- package/dist/src/sdk/logger.js.map +1 -0
- package/dist/src/sdk/plugin-factory.d.ts +104 -0
- package/dist/src/sdk/plugin-factory.d.ts.map +1 -0
- package/dist/src/sdk/plugin-factory.js +540 -0
- package/dist/src/sdk/plugin-factory.js.map +1 -0
- package/package.json +2 -2
package/dist/src/core/cli.js
CHANGED
|
@@ -2,21 +2,30 @@
|
|
|
2
2
|
import { buildPet } from './build-pet.js';
|
|
3
3
|
import { deployPet } from './deploy-pet.js';
|
|
4
4
|
import { addFolderToHistory } from './config-manager.js';
|
|
5
|
+
import { getRegistryClient } from './registry-client.js';
|
|
6
|
+
import { loadPetConfig, validatePetConfig, generateOpenpetsYamlTemplate } from './pet-config.js';
|
|
7
|
+
import { getPackageDiscovery, getRegistryAggregator } from './discovery.js';
|
|
8
|
+
import { getPetDownloader } from './pet-downloader.js';
|
|
9
|
+
import { getAllPets } from './search-pets.js';
|
|
10
|
+
import { syncEnvToConfig } from '../sdk/plugin-factory.js';
|
|
5
11
|
import { spawn } from 'child_process';
|
|
6
|
-
import { resolve } from 'path';
|
|
7
|
-
import { readFileSync, existsSync } from 'fs';
|
|
12
|
+
import { resolve, join } from 'path';
|
|
13
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
14
|
+
import * as readline from 'readline';
|
|
8
15
|
async function publishPet(petName, options = {}) {
|
|
9
16
|
const { preview = false, channel = 'latest' } = options;
|
|
10
17
|
// Find the script path - try multiple locations
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
console.error(
|
|
18
|
+
const possiblePaths = [
|
|
19
|
+
resolve(__dirname, '../../scripts/publish-pet.ts'),
|
|
20
|
+
resolve(__dirname, '../../../scripts/publish-pet.ts'),
|
|
21
|
+
resolve(process.cwd(), 'scripts/publish-pet.ts'),
|
|
22
|
+
resolve(process.cwd(), '../../scripts/publish-pet.ts')
|
|
23
|
+
];
|
|
24
|
+
let scriptPath = possiblePaths.find(p => existsSync(p));
|
|
25
|
+
if (!scriptPath) {
|
|
26
|
+
console.error(`❌ Publish script not found`);
|
|
27
|
+
console.error(` Tried:`);
|
|
28
|
+
possiblePaths.forEach(p => console.error(` - ${p}`));
|
|
20
29
|
process.exit(1);
|
|
21
30
|
}
|
|
22
31
|
// If no petName provided, try to infer from current directory
|
|
@@ -121,6 +130,14 @@ function launchManager() {
|
|
|
121
130
|
const uiDir = resolve(__dirname, '../../apps/desktop');
|
|
122
131
|
const projectDir = process.cwd();
|
|
123
132
|
addFolderToHistory(projectDir);
|
|
133
|
+
// Sync .env to .pets/config.json before launching
|
|
134
|
+
// This ensures .pets/config.json is the single source of truth for env vars
|
|
135
|
+
console.log('Syncing environment configuration...');
|
|
136
|
+
const syncResult = syncEnvToConfig(projectDir);
|
|
137
|
+
if (syncResult.success && syncResult.synced > 0) {
|
|
138
|
+
console.log(`✅ Synced ${syncResult.synced} env vars to .pets/config.json`);
|
|
139
|
+
}
|
|
140
|
+
log('Env sync result:', syncResult);
|
|
124
141
|
console.log('Launching OpenPets Plugin Manager...');
|
|
125
142
|
console.log(`Project directory: ${projectDir}`);
|
|
126
143
|
console.log(`UI directory: ${uiDir}`);
|
|
@@ -143,8 +160,8 @@ function launchManager() {
|
|
|
143
160
|
process.exit(1);
|
|
144
161
|
}
|
|
145
162
|
killPort(1420);
|
|
146
|
-
log('Spawning
|
|
147
|
-
const child = spawn('
|
|
163
|
+
log('Spawning bun process with tauri:dev');
|
|
164
|
+
const child = spawn('bun', ['run', 'tauri:dev'], {
|
|
148
165
|
cwd: uiDir,
|
|
149
166
|
stdio: 'inherit',
|
|
150
167
|
shell: true,
|
|
@@ -210,35 +227,1142 @@ else if (command === 'publish') {
|
|
|
210
227
|
process.exit(1);
|
|
211
228
|
});
|
|
212
229
|
}
|
|
230
|
+
else if (command === 'install') {
|
|
231
|
+
handleInstall(args.slice(1));
|
|
232
|
+
}
|
|
233
|
+
else if (command === 'add') {
|
|
234
|
+
handleAdd(args.slice(1));
|
|
235
|
+
}
|
|
236
|
+
else if (command === 'uninstall' || command === 'remove' || command === 'delete') {
|
|
237
|
+
handleUninstall(args.slice(1));
|
|
238
|
+
}
|
|
239
|
+
else if (command === 'inspect') {
|
|
240
|
+
handleInspect(args.slice(1));
|
|
241
|
+
}
|
|
242
|
+
else if (command === 'search') {
|
|
243
|
+
handleSearch(args.slice(1));
|
|
244
|
+
}
|
|
245
|
+
else if (command === 'list') {
|
|
246
|
+
handleList(args.slice(1));
|
|
247
|
+
}
|
|
248
|
+
else if (command === 'run') {
|
|
249
|
+
handleRun(args.slice(1));
|
|
250
|
+
}
|
|
251
|
+
else if (command === 'init') {
|
|
252
|
+
handleInit(args.slice(1));
|
|
253
|
+
}
|
|
254
|
+
else if (command === 'validate') {
|
|
255
|
+
handleValidate(args.slice(1));
|
|
256
|
+
}
|
|
257
|
+
else if (command === 'playground') {
|
|
258
|
+
handlePlayground();
|
|
259
|
+
}
|
|
260
|
+
else if (command === 'discover') {
|
|
261
|
+
handleDiscover(args.slice(1));
|
|
262
|
+
}
|
|
263
|
+
else if (command === 'registry') {
|
|
264
|
+
handleRegistry(args.slice(1));
|
|
265
|
+
}
|
|
266
|
+
else if (command === 'prune') {
|
|
267
|
+
handlePrune(args.slice(1));
|
|
268
|
+
}
|
|
269
|
+
else if (command === 'reload') {
|
|
270
|
+
handleReload(args.slice(1));
|
|
271
|
+
}
|
|
272
|
+
else if (command === 'help' || command === '--help' || command === '-h') {
|
|
273
|
+
showHelp();
|
|
274
|
+
}
|
|
213
275
|
else if (!command) {
|
|
214
276
|
log('No command provided, launching manager UI');
|
|
215
277
|
launchManager();
|
|
216
278
|
}
|
|
217
279
|
else {
|
|
218
280
|
log('Unknown command:', command);
|
|
219
|
-
|
|
220
|
-
console.error('');
|
|
221
|
-
console.error('Commands:');
|
|
222
|
-
console.error(' (none) Launch the plugin manager UI (default)');
|
|
223
|
-
console.error(' build <pet-name> Build and validate a pet package');
|
|
224
|
-
console.error(' deploy <pet-name> Build and deploy a pet package with metadata');
|
|
225
|
-
console.error(' publish [pet-name] Publish a pet package to npm (auto-detects if in pet dir)');
|
|
226
|
-
console.error('');
|
|
227
|
-
console.error('Publish Options:');
|
|
228
|
-
console.error(' --preview Dry-run mode (no actual publish)');
|
|
229
|
-
console.error(' --channel <name> Publish to a specific npm tag (default: latest)');
|
|
230
|
-
console.error('');
|
|
231
|
-
console.error('Examples:');
|
|
232
|
-
console.error(' pets # Launch the desktop plugin manager');
|
|
233
|
-
console.error(' pets build postgres');
|
|
234
|
-
console.error(' pets deploy maps');
|
|
235
|
-
console.error(' pets publish maps # From root directory');
|
|
236
|
-
console.error(' cd pets/maps && pets publish # From pet directory');
|
|
237
|
-
console.error(' pets publish --preview # Dry-run from pet directory');
|
|
238
|
-
console.error(' pets publish maps --channel beta');
|
|
239
|
-
console.error('');
|
|
240
|
-
console.error('Environment:');
|
|
241
|
-
console.error(' PETS_DEBUG=true Enable detailed debug logging');
|
|
281
|
+
showUnknownCommandError(command);
|
|
242
282
|
process.exit(1);
|
|
243
283
|
}
|
|
284
|
+
async function handleInstall(args) {
|
|
285
|
+
const packageName = args.find(a => !a.startsWith('--'));
|
|
286
|
+
if (!packageName) {
|
|
287
|
+
console.error('❌ Package name required');
|
|
288
|
+
console.error('Usage: pets install <package-name> --client <client>');
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
const clientIndex = args.indexOf('--client');
|
|
292
|
+
const client = (clientIndex >= 0 ? args[clientIndex + 1] : 'opencode');
|
|
293
|
+
const configIndex = args.indexOf('--config');
|
|
294
|
+
const configStr = configIndex >= 0 ? args[configIndex + 1] : undefined;
|
|
295
|
+
const config = configStr ? JSON.parse(configStr) : undefined;
|
|
296
|
+
const global = args.includes('--global') || args.includes('-g');
|
|
297
|
+
const versionIndex = args.indexOf('--version');
|
|
298
|
+
const version = versionIndex >= 0 ? args[versionIndex + 1] : undefined;
|
|
299
|
+
console.log(`📦 Installing ${packageName}...`);
|
|
300
|
+
log('Install options:', { client, config, global, version });
|
|
301
|
+
const registry = getRegistryClient();
|
|
302
|
+
const result = await registry.install(packageName, { client, config, global, version });
|
|
303
|
+
if (result.success) {
|
|
304
|
+
console.log(`✅ ${result.message}`);
|
|
305
|
+
if (result.path) {
|
|
306
|
+
console.log(` 📁 Installed to: ${result.path}`);
|
|
307
|
+
}
|
|
308
|
+
console.log(`\n💡 Remember to restart your AI client to apply changes.`);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
console.error(`❌ ${result.message}`);
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async function handleUninstall(args) {
|
|
316
|
+
const packageName = args.find(a => !a.startsWith('--'));
|
|
317
|
+
if (!packageName) {
|
|
318
|
+
console.error('❌ Package name required');
|
|
319
|
+
console.error('Usage: pets uninstall <package-name> --client <client>');
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
const clientIndex = args.indexOf('--client');
|
|
323
|
+
const client = (clientIndex >= 0 ? args[clientIndex + 1] : 'opencode');
|
|
324
|
+
const global = args.includes('--global') || args.includes('-g');
|
|
325
|
+
console.log(`🗑️ Uninstalling ${packageName}...`);
|
|
326
|
+
const registry = getRegistryClient();
|
|
327
|
+
const result = await registry.uninstall(packageName, { client, global });
|
|
328
|
+
if (result.success) {
|
|
329
|
+
console.log(`✅ ${result.message}`);
|
|
330
|
+
console.log(`\n💡 Remember to restart your AI client to apply changes.`);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
console.error(`❌ ${result.message}`);
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async function handleInspect(args) {
|
|
338
|
+
const packageName = args.find(a => !a.startsWith('--'));
|
|
339
|
+
if (!packageName) {
|
|
340
|
+
console.error('❌ Package name required');
|
|
341
|
+
console.error('Usage: pets inspect <package-name>');
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
console.log(`🔍 Inspecting ${packageName}...`);
|
|
345
|
+
const registry = getRegistryClient();
|
|
346
|
+
const { package: pkg, installed, versions } = await registry.inspect(packageName);
|
|
347
|
+
if (!pkg) {
|
|
348
|
+
console.error(`❌ Package not found: ${packageName}`);
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
console.log('');
|
|
352
|
+
console.log(`📦 ${pkg.name}`);
|
|
353
|
+
console.log(` Version: ${pkg.version}`);
|
|
354
|
+
console.log(` Description: ${pkg.description || 'No description'}`);
|
|
355
|
+
if (pkg.keywords && pkg.keywords.length > 0) {
|
|
356
|
+
console.log(` Keywords: ${pkg.keywords.join(', ')}`);
|
|
357
|
+
}
|
|
358
|
+
if (pkg.homepage) {
|
|
359
|
+
console.log(` Homepage: ${pkg.homepage}`);
|
|
360
|
+
}
|
|
361
|
+
if (pkg.repository) {
|
|
362
|
+
const repoUrl = typeof pkg.repository === 'string' ? pkg.repository : pkg.repository.url;
|
|
363
|
+
console.log(` Repository: ${repoUrl}`);
|
|
364
|
+
}
|
|
365
|
+
if (pkg.author) {
|
|
366
|
+
const authorStr = typeof pkg.author === 'string' ? pkg.author : pkg.author.name;
|
|
367
|
+
console.log(` Author: ${authorStr}`);
|
|
368
|
+
}
|
|
369
|
+
console.log('');
|
|
370
|
+
if (installed) {
|
|
371
|
+
console.log(`✅ Installed locally`);
|
|
372
|
+
console.log(` Version: ${installed.version}`);
|
|
373
|
+
console.log(` Path: ${installed.path}`);
|
|
374
|
+
console.log(` Installed: ${new Date(installed.installedAt).toLocaleString()}`);
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
console.log(`❌ Not installed locally`);
|
|
378
|
+
}
|
|
379
|
+
if (versions.length > 0) {
|
|
380
|
+
console.log('');
|
|
381
|
+
console.log(`📋 Available versions: ${versions.slice(0, 5).join(', ')}${versions.length > 5 ? '...' : ''}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
async function handleSearch(args) {
|
|
385
|
+
const query = args.filter(a => !a.startsWith('--')).join(' ');
|
|
386
|
+
const limitIndex = args.indexOf('--limit');
|
|
387
|
+
const limit = limitIndex >= 0 ? parseInt(args[limitIndex + 1], 10) : 20;
|
|
388
|
+
console.log(query ? `🔍 Searching for "${query}"...` : '🔍 Listing all @openpets packages...');
|
|
389
|
+
const registry = getRegistryClient();
|
|
390
|
+
const results = await registry.search(query, limit);
|
|
391
|
+
if (results.objects.length === 0) {
|
|
392
|
+
console.log('No packages found.');
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
console.log(`\nFound ${results.total} packages:\n`);
|
|
396
|
+
for (const { package: pkg, score } of results.objects) {
|
|
397
|
+
const shortName = pkg.name.replace('@openpets/', '');
|
|
398
|
+
console.log(` 📦 ${shortName}`);
|
|
399
|
+
console.log(` ${pkg.description || 'No description'}`);
|
|
400
|
+
console.log(` v${pkg.version} | Score: ${(score.final * 100).toFixed(0)}%`);
|
|
401
|
+
console.log('');
|
|
402
|
+
}
|
|
403
|
+
console.log(`💡 Use "pets install <name>" to install a package`);
|
|
404
|
+
console.log(`💡 Use "pets inspect <name>" to see more details`);
|
|
405
|
+
}
|
|
406
|
+
async function handleList(args) {
|
|
407
|
+
const listType = args[0] || 'installed';
|
|
408
|
+
const registry = getRegistryClient();
|
|
409
|
+
if (listType === 'clients') {
|
|
410
|
+
console.log('📋 Supported clients:\n');
|
|
411
|
+
const clients = registry.listClients();
|
|
412
|
+
for (const client of clients) {
|
|
413
|
+
const configPath = registry.getClientConfigPath(client);
|
|
414
|
+
const exists = configPath && existsSync(configPath);
|
|
415
|
+
console.log(` ${exists ? '✅' : '❌'} ${client}`);
|
|
416
|
+
if (configPath && configPath !== 'custom') {
|
|
417
|
+
console.log(` ${configPath}`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (listType === 'servers' || listType === 'installed') {
|
|
423
|
+
const clientIndex = args.indexOf('--client');
|
|
424
|
+
const client = (clientIndex >= 0 ? args[clientIndex + 1] : undefined);
|
|
425
|
+
console.log('📋 Installed pets:\n');
|
|
426
|
+
const opencodeJsonPath = resolve(process.cwd(), 'opencode.json');
|
|
427
|
+
const petsConfigPath = resolve(process.cwd(), '.pets', 'config.json');
|
|
428
|
+
let localPlugins = [];
|
|
429
|
+
let disabledPets = [];
|
|
430
|
+
if (existsSync(opencodeJsonPath)) {
|
|
431
|
+
try {
|
|
432
|
+
const config = JSON.parse(readFileSync(opencodeJsonPath, 'utf-8'));
|
|
433
|
+
if (config.plugin) {
|
|
434
|
+
localPlugins = Array.isArray(config.plugin) ? config.plugin : [config.plugin];
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
catch (e) {
|
|
438
|
+
log('Error reading opencode.json:', e);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (existsSync(petsConfigPath)) {
|
|
442
|
+
try {
|
|
443
|
+
const petsConfig = JSON.parse(readFileSync(petsConfigPath, 'utf-8'));
|
|
444
|
+
if (petsConfig.disabled && Array.isArray(petsConfig.disabled)) {
|
|
445
|
+
disabledPets = petsConfig.disabled;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
catch (e) {
|
|
449
|
+
log('Error reading .pets/config.json:', e);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
let remoteApiData = null;
|
|
453
|
+
const openpetsPlugins = localPlugins.filter(p => p.startsWith('@openpets/'));
|
|
454
|
+
const allPetsToLookup = [...openpetsPlugins, ...disabledPets.map(p => `@openpets/${p}`)];
|
|
455
|
+
if (allPetsToLookup.length > 0) {
|
|
456
|
+
try {
|
|
457
|
+
const allPets = await getAllPets();
|
|
458
|
+
remoteApiData = new Map();
|
|
459
|
+
for (const pet of allPets) {
|
|
460
|
+
remoteApiData.set(pet.id, pet);
|
|
461
|
+
remoteApiData.set(`@openpets/${pet.id}`, pet);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
catch (e) {
|
|
465
|
+
log('Failed to fetch remote API data:', e);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (localPlugins.length > 0) {
|
|
469
|
+
console.log(' 📁 Local plugins (from opencode.json):\n');
|
|
470
|
+
for (const plugin of localPlugins) {
|
|
471
|
+
const isOpenpetsPackage = plugin.startsWith('@openpets/');
|
|
472
|
+
if (isOpenpetsPackage) {
|
|
473
|
+
const shortName = plugin.replace('@openpets/', '');
|
|
474
|
+
const remotePet = remoteApiData?.get(shortName) || remoteApiData?.get(plugin);
|
|
475
|
+
if (remotePet) {
|
|
476
|
+
console.log(` ✅ ${remotePet.displayName || remotePet.name}`);
|
|
477
|
+
if (remotePet.description) {
|
|
478
|
+
console.log(` ${remotePet.description}`);
|
|
479
|
+
}
|
|
480
|
+
if (remotePet.version) {
|
|
481
|
+
console.log(` v${remotePet.version}`);
|
|
482
|
+
}
|
|
483
|
+
if (remotePet.tools && remotePet.tools.length > 0) {
|
|
484
|
+
console.log(` 🔧 ${remotePet.tools.length} tools`);
|
|
485
|
+
}
|
|
486
|
+
console.log(` 📦 ${plugin}`);
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
console.log(` ❓ ${plugin}`);
|
|
490
|
+
console.log(` (not found in registry)`);
|
|
491
|
+
}
|
|
492
|
+
console.log('');
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
const pluginPath = resolve(process.cwd(), plugin);
|
|
496
|
+
const exists = existsSync(pluginPath) || existsSync(pluginPath.replace('.ts', ''));
|
|
497
|
+
const status = exists ? '✅' : '❌';
|
|
498
|
+
let pkgInfo = { name: plugin, description: '', version: '' };
|
|
499
|
+
const pluginDir = pluginPath.replace(/\/index\.ts$/, '').replace(/\.ts$/, '');
|
|
500
|
+
const pkgJsonPath = join(pluginDir, 'package.json');
|
|
501
|
+
if (existsSync(pkgJsonPath)) {
|
|
502
|
+
try {
|
|
503
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
504
|
+
pkgInfo = {
|
|
505
|
+
name: pkg.title || pkg.name || plugin,
|
|
506
|
+
description: pkg.subtitle || pkg.description || '',
|
|
507
|
+
version: pkg.version || ''
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
catch (e) { }
|
|
511
|
+
}
|
|
512
|
+
console.log(` ${status} ${pkgInfo.name}`);
|
|
513
|
+
if (pkgInfo.description) {
|
|
514
|
+
console.log(` ${pkgInfo.description}`);
|
|
515
|
+
}
|
|
516
|
+
if (pkgInfo.version) {
|
|
517
|
+
console.log(` v${pkgInfo.version}`);
|
|
518
|
+
}
|
|
519
|
+
console.log(` 📁 ${plugin}`);
|
|
520
|
+
console.log('');
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
if (disabledPets.length > 0) {
|
|
524
|
+
console.log(' 🚫 Disabled pets (from .pets/config.json):\n');
|
|
525
|
+
for (const pet of disabledPets) {
|
|
526
|
+
const remotePet = remoteApiData?.get(pet) || remoteApiData?.get(`@openpets/${pet}`);
|
|
527
|
+
if (remotePet) {
|
|
528
|
+
console.log(` ❌ ${remotePet.displayName || remotePet.name}`);
|
|
529
|
+
if (remotePet.description) {
|
|
530
|
+
console.log(` ${remotePet.description}`);
|
|
531
|
+
}
|
|
532
|
+
console.log(` 📦 @openpets/${pet}`);
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
console.log(` ❌ ${pet}`);
|
|
536
|
+
}
|
|
537
|
+
console.log('');
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const installed = registry.getInstalledPets();
|
|
541
|
+
if (installed.length > 0) {
|
|
542
|
+
console.log(' 📦 Registry-installed pets:\n');
|
|
543
|
+
for (const pet of installed) {
|
|
544
|
+
const shortName = pet.name.replace('@openpets/', '');
|
|
545
|
+
console.log(` 📦 ${shortName}`);
|
|
546
|
+
console.log(` Version: ${pet.version}`);
|
|
547
|
+
console.log(` Installed: ${new Date(pet.installedAt).toLocaleDateString()}`);
|
|
548
|
+
console.log('');
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (localPlugins.length === 0 && installed.length === 0 && disabledPets.length === 0) {
|
|
552
|
+
console.log(' No pets installed yet.');
|
|
553
|
+
console.log(' Use "pets search" to find packages');
|
|
554
|
+
console.log(' Use "pets install <name>" to install');
|
|
555
|
+
}
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
console.error(`Unknown list type: ${listType}`);
|
|
559
|
+
console.log('Usage: pets list [clients|servers|installed]');
|
|
560
|
+
}
|
|
561
|
+
async function handleRun(args) {
|
|
562
|
+
const packageName = args.find(a => !a.startsWith('--'));
|
|
563
|
+
if (!packageName) {
|
|
564
|
+
console.error('❌ Package name required');
|
|
565
|
+
console.error('Usage: pets run <package-name> [--config \'{"key":"value"}\']');
|
|
566
|
+
process.exit(1);
|
|
567
|
+
}
|
|
568
|
+
const configIndex = args.indexOf('--config');
|
|
569
|
+
const configStr = configIndex >= 0 ? args[configIndex + 1] : undefined;
|
|
570
|
+
const config = configStr ? JSON.parse(configStr) : undefined;
|
|
571
|
+
const transportIndex = args.indexOf('--transport');
|
|
572
|
+
const transport = (transportIndex >= 0 ? args[transportIndex + 1] : 'stdio');
|
|
573
|
+
console.log(`🚀 Running ${packageName}...`);
|
|
574
|
+
const registry = getRegistryClient();
|
|
575
|
+
await registry.run(packageName, { config, transport });
|
|
576
|
+
}
|
|
577
|
+
async function handleInit(args) {
|
|
578
|
+
const name = args.find(a => !a.startsWith('--'));
|
|
579
|
+
const targetDir = args.includes('--dir')
|
|
580
|
+
? args[args.indexOf('--dir') + 1]
|
|
581
|
+
: process.cwd();
|
|
582
|
+
const format = args.includes('--yaml') ? 'yaml' : 'json';
|
|
583
|
+
const runtimeIndex = args.indexOf('--runtime');
|
|
584
|
+
const runtime = (runtimeIndex >= 0 ? args[runtimeIndex + 1] : 'typescript');
|
|
585
|
+
const petName = name || resolve(targetDir).split('/').pop() || 'my-pet';
|
|
586
|
+
console.log(`🐾 Initializing new pet: ${petName}`);
|
|
587
|
+
if (format === 'yaml') {
|
|
588
|
+
const yamlContent = generateOpenpetsYamlTemplate({
|
|
589
|
+
name: petName,
|
|
590
|
+
runtime
|
|
591
|
+
});
|
|
592
|
+
const yamlPath = join(targetDir, 'openpets.yaml');
|
|
593
|
+
writeFileSync(yamlPath, yamlContent);
|
|
594
|
+
console.log(`✅ Created openpets.yaml`);
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
const config = {
|
|
598
|
+
$schema: 'https://pets.studio/config.json',
|
|
599
|
+
name: `@openpets/${petName}`,
|
|
600
|
+
version: '1.0.0',
|
|
601
|
+
title: petName.charAt(0).toUpperCase() + petName.slice(1),
|
|
602
|
+
subtitle: `${petName} plugin`,
|
|
603
|
+
description: `${petName} plugin for OpenPets`,
|
|
604
|
+
categories: ['general'],
|
|
605
|
+
keywords: ['openpets', 'plugin', petName],
|
|
606
|
+
main: 'index.ts',
|
|
607
|
+
types: 'index.ts',
|
|
608
|
+
envVariables: {},
|
|
609
|
+
queries: ['example query'],
|
|
610
|
+
scenarios: {
|
|
611
|
+
'basic-test': ['example query']
|
|
612
|
+
},
|
|
613
|
+
scripts: {
|
|
614
|
+
build: 'pets build',
|
|
615
|
+
test: 'pets validate'
|
|
616
|
+
},
|
|
617
|
+
dependencies: {
|
|
618
|
+
openpets: 'workspace:*'
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
const jsonPath = join(targetDir, 'package.json');
|
|
622
|
+
writeFileSync(jsonPath, JSON.stringify(config, null, 2));
|
|
623
|
+
console.log(`✅ Created package.json`);
|
|
624
|
+
}
|
|
625
|
+
const opencodeConfig = {
|
|
626
|
+
$schema: 'https://opencode.ai/config.json',
|
|
627
|
+
plugin: ['./index.ts']
|
|
628
|
+
};
|
|
629
|
+
const opencodePath = join(targetDir, 'opencode.json');
|
|
630
|
+
writeFileSync(opencodePath, JSON.stringify(opencodeConfig, null, 2));
|
|
631
|
+
console.log(`✅ Created opencode.json`);
|
|
632
|
+
const indexPath = join(targetDir, 'index.ts');
|
|
633
|
+
if (!existsSync(indexPath)) {
|
|
634
|
+
const indexContent = `import { createPlugin } from 'openpets'
|
|
635
|
+
|
|
636
|
+
export default createPlugin({
|
|
637
|
+
name: '${petName}',
|
|
638
|
+
description: '${petName} plugin',
|
|
639
|
+
tools: [
|
|
640
|
+
{
|
|
641
|
+
name: '${petName}-hello',
|
|
642
|
+
description: 'Say hello from ${petName}',
|
|
643
|
+
schema: {
|
|
644
|
+
type: 'object',
|
|
645
|
+
properties: {
|
|
646
|
+
name: { type: 'string', description: 'Name to greet' }
|
|
647
|
+
}
|
|
648
|
+
},
|
|
649
|
+
execute: async ({ name = 'World' }) => {
|
|
650
|
+
return { message: \`Hello, \${name}! From ${petName}.\` }
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
]
|
|
654
|
+
})
|
|
655
|
+
`;
|
|
656
|
+
writeFileSync(indexPath, indexContent);
|
|
657
|
+
console.log(`✅ Created index.ts`);
|
|
658
|
+
}
|
|
659
|
+
console.log(`
|
|
660
|
+
🎉 Pet initialized! Next steps:
|
|
661
|
+
|
|
662
|
+
1. Edit index.ts to add your tools
|
|
663
|
+
2. Run "pets validate" to check your configuration
|
|
664
|
+
3. Run "pets build" to build your pet
|
|
665
|
+
4. Run "pets publish" to publish to npm
|
|
666
|
+
`);
|
|
667
|
+
}
|
|
668
|
+
async function handleValidate(args) {
|
|
669
|
+
const targetDir = args.find(a => !a.startsWith('--')) || process.cwd();
|
|
670
|
+
console.log(`🔍 Validating pet configuration in ${targetDir}...`);
|
|
671
|
+
const config = loadPetConfig(targetDir);
|
|
672
|
+
if (!config) {
|
|
673
|
+
console.error('❌ Could not load configuration');
|
|
674
|
+
process.exit(1);
|
|
675
|
+
}
|
|
676
|
+
const result = validatePetConfig(config);
|
|
677
|
+
if (result.valid) {
|
|
678
|
+
console.log('✅ Configuration is valid');
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
console.log('❌ Configuration has errors:');
|
|
682
|
+
result.errors.forEach(err => console.log(` ❌ ${err}`));
|
|
683
|
+
}
|
|
684
|
+
if (result.warnings.length > 0) {
|
|
685
|
+
console.log('\n⚠️ Warnings:');
|
|
686
|
+
result.warnings.forEach(warn => console.log(` ⚠️ ${warn}`));
|
|
687
|
+
}
|
|
688
|
+
console.log(`\n📦 Pet: ${config.name}`);
|
|
689
|
+
console.log(` Version: ${config.version}`);
|
|
690
|
+
console.log(` Description: ${config.description}`);
|
|
691
|
+
if (!result.valid) {
|
|
692
|
+
process.exit(1);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function handlePlayground() {
|
|
696
|
+
console.log('🎮 Opening OpenPets Playground...');
|
|
697
|
+
const playgroundUrl = 'https://pets.studio/playground';
|
|
698
|
+
const openCommand = process.platform === 'darwin' ? 'open' :
|
|
699
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
700
|
+
spawn(openCommand, [playgroundUrl], { stdio: 'ignore', detached: true }).unref();
|
|
701
|
+
console.log(`\n📌 Playground URL: ${playgroundUrl}`);
|
|
702
|
+
}
|
|
703
|
+
async function handleDiscover(args) {
|
|
704
|
+
const includeGithub = args.includes('--github');
|
|
705
|
+
const npmOnly = args.includes('--npm-only');
|
|
706
|
+
const limitIndex = args.indexOf('--limit');
|
|
707
|
+
const limit = limitIndex >= 0 ? parseInt(args[limitIndex + 1], 10) : 50;
|
|
708
|
+
const githubToken = process.env.GITHUB_TOKEN;
|
|
709
|
+
console.log('🔍 Discovering OpenPets-compatible packages...\n');
|
|
710
|
+
if (includeGithub && !githubToken) {
|
|
711
|
+
console.log('⚠️ GitHub discovery requires GITHUB_TOKEN environment variable');
|
|
712
|
+
console.log(' Set it to search for packages with $schema field in package.json\n');
|
|
713
|
+
}
|
|
714
|
+
const discovery = getPackageDiscovery();
|
|
715
|
+
try {
|
|
716
|
+
const packages = await discovery.discoverAll({
|
|
717
|
+
includeNpm: true,
|
|
718
|
+
includeGithub: includeGithub && !!githubToken,
|
|
719
|
+
githubToken,
|
|
720
|
+
limit
|
|
721
|
+
});
|
|
722
|
+
if (packages.length === 0) {
|
|
723
|
+
console.log('No packages found.');
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
console.log(`Found ${packages.length} compatible packages:\n`);
|
|
727
|
+
const npmPackages = packages.filter(p => p.source === 'npm');
|
|
728
|
+
const githubPackages = packages.filter(p => p.source === 'github');
|
|
729
|
+
if (npmPackages.length > 0) {
|
|
730
|
+
console.log('📦 From npm registry:');
|
|
731
|
+
console.log('─'.repeat(50));
|
|
732
|
+
for (const pkg of npmPackages.slice(0, 20)) {
|
|
733
|
+
const shortName = pkg.name.replace('@openpets/', '');
|
|
734
|
+
const score = pkg.score ? ` (score: ${(pkg.score * 100).toFixed(0)}%)` : '';
|
|
735
|
+
console.log(` ${shortName}${score}`);
|
|
736
|
+
console.log(` ${pkg.description || 'No description'}`);
|
|
737
|
+
console.log(` v${pkg.version}`);
|
|
738
|
+
console.log('');
|
|
739
|
+
}
|
|
740
|
+
if (npmPackages.length > 20) {
|
|
741
|
+
console.log(` ... and ${npmPackages.length - 20} more\n`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (githubPackages.length > 0) {
|
|
745
|
+
console.log('🐙 From GitHub repositories:');
|
|
746
|
+
console.log('─'.repeat(50));
|
|
747
|
+
for (const pkg of githubPackages.slice(0, 10)) {
|
|
748
|
+
const stars = pkg.stars ? ` ⭐ ${pkg.stars}` : '';
|
|
749
|
+
console.log(` ${pkg.name}${stars}`);
|
|
750
|
+
console.log(` ${pkg.description || 'No description'}`);
|
|
751
|
+
if (pkg.repository) {
|
|
752
|
+
console.log(` ${pkg.repository}`);
|
|
753
|
+
}
|
|
754
|
+
console.log('');
|
|
755
|
+
}
|
|
756
|
+
if (githubPackages.length > 10) {
|
|
757
|
+
console.log(` ... and ${githubPackages.length - 10} more\n`);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
console.log('─'.repeat(50));
|
|
761
|
+
console.log(`\n💡 Discovery methods used:`);
|
|
762
|
+
console.log(` • npm: Search @openpets/* scope and "openpets" keyword`);
|
|
763
|
+
if (includeGithub && githubToken) {
|
|
764
|
+
console.log(` • GitHub: Search for $schema field pointing to pets.studio`);
|
|
765
|
+
}
|
|
766
|
+
console.log(`\n💡 To include GitHub results: pets discover --github`);
|
|
767
|
+
console.log(` (requires GITHUB_TOKEN environment variable)`);
|
|
768
|
+
}
|
|
769
|
+
catch (error) {
|
|
770
|
+
console.error(`❌ Discovery failed: ${error.message}`);
|
|
771
|
+
process.exit(1);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
async function handleRegistry(args) {
|
|
775
|
+
const subcommand = args[0] || 'status';
|
|
776
|
+
const aggregator = getRegistryAggregator();
|
|
777
|
+
if (subcommand === 'update' || subcommand === 'refresh') {
|
|
778
|
+
console.log('🔄 Updating package registry...');
|
|
779
|
+
const githubToken = process.env.GITHUB_TOKEN;
|
|
780
|
+
await aggregator.updateRegistry({
|
|
781
|
+
includeNpm: true,
|
|
782
|
+
includeGithub: !!githubToken,
|
|
783
|
+
githubToken
|
|
784
|
+
});
|
|
785
|
+
const packages = await aggregator.getRegistry();
|
|
786
|
+
console.log(`✅ Registry updated: ${packages.length} packages indexed`);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (subcommand === 'export') {
|
|
790
|
+
const outputPath = args[1] || 'registry.json';
|
|
791
|
+
const json = await aggregator.exportRegistry();
|
|
792
|
+
writeFileSync(outputPath, json);
|
|
793
|
+
console.log(`✅ Registry exported to ${outputPath}`);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
if (subcommand === 'import') {
|
|
797
|
+
const inputPath = args[1];
|
|
798
|
+
if (!inputPath || !existsSync(inputPath)) {
|
|
799
|
+
console.error('❌ Input file required');
|
|
800
|
+
console.error('Usage: pets registry import <file.json>');
|
|
801
|
+
process.exit(1);
|
|
802
|
+
}
|
|
803
|
+
const json = readFileSync(inputPath, 'utf-8');
|
|
804
|
+
await aggregator.importRegistry(json);
|
|
805
|
+
console.log(`✅ Registry imported from ${inputPath}`);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (subcommand === 'status') {
|
|
809
|
+
const packages = await aggregator.getRegistry();
|
|
810
|
+
console.log('📊 Registry Status\n');
|
|
811
|
+
console.log(`Total packages: ${packages.length}`);
|
|
812
|
+
const npmCount = packages.filter(p => p.source === 'npm').length;
|
|
813
|
+
const githubCount = packages.filter(p => p.source === 'github').length;
|
|
814
|
+
console.log(` • npm packages: ${npmCount}`);
|
|
815
|
+
console.log(` • GitHub packages: ${githubCount}`);
|
|
816
|
+
const withSchema = packages.filter(p => p.schemaVersion).length;
|
|
817
|
+
console.log(` • With $schema field: ${withSchema}`);
|
|
818
|
+
console.log(`\n💡 Run "pets registry update" to refresh the registry`);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
console.error(`Unknown registry subcommand: ${subcommand}`);
|
|
822
|
+
console.log('Usage: pets registry [status|update|export|import]');
|
|
823
|
+
}
|
|
824
|
+
function ensureOpencodeJson(targetDir = process.cwd()) {
|
|
825
|
+
const opencodeJsonPath = resolve(targetDir, 'opencode.json');
|
|
826
|
+
const opencodeJsoncPath = resolve(targetDir, 'opencode.jsonc');
|
|
827
|
+
if (existsSync(opencodeJsonPath)) {
|
|
828
|
+
return opencodeJsonPath;
|
|
829
|
+
}
|
|
830
|
+
if (existsSync(opencodeJsoncPath)) {
|
|
831
|
+
return opencodeJsoncPath;
|
|
832
|
+
}
|
|
833
|
+
const defaultConfig = {
|
|
834
|
+
"$schema": "https://opencode.ai/config.json",
|
|
835
|
+
"plugin": []
|
|
836
|
+
};
|
|
837
|
+
writeFileSync(opencodeJsonPath, JSON.stringify(defaultConfig, null, 2));
|
|
838
|
+
console.log('✅ Created opencode.json');
|
|
839
|
+
return opencodeJsonPath;
|
|
840
|
+
}
|
|
841
|
+
async function handleAdd(args) {
|
|
842
|
+
const packageName = args.find(a => !a.startsWith('--'));
|
|
843
|
+
if (!packageName) {
|
|
844
|
+
console.error('❌ Package name required');
|
|
845
|
+
console.error('Usage: pets add <package-name> [--skip-config] [--version <version>]');
|
|
846
|
+
process.exit(1);
|
|
847
|
+
}
|
|
848
|
+
const skipConfig = args.includes('--skip-config');
|
|
849
|
+
const versionIndex = args.indexOf('--version');
|
|
850
|
+
const version = versionIndex >= 0 ? args[versionIndex + 1] : undefined;
|
|
851
|
+
const configIndex = args.indexOf('--config');
|
|
852
|
+
const configStr = configIndex >= 0 ? args[configIndex + 1] : undefined;
|
|
853
|
+
const presetEnv = configStr ? JSON.parse(configStr) : undefined;
|
|
854
|
+
ensureOpencodeJson();
|
|
855
|
+
console.log(`\n📦 Adding ${packageName}...`);
|
|
856
|
+
const downloader = getPetDownloader();
|
|
857
|
+
const result = await downloader.add(packageName, {
|
|
858
|
+
targetDir: process.cwd(),
|
|
859
|
+
version,
|
|
860
|
+
skipConfig,
|
|
861
|
+
envValues: presetEnv
|
|
862
|
+
});
|
|
863
|
+
if (!result.success) {
|
|
864
|
+
console.error(`\n❌ ${result.message}`);
|
|
865
|
+
process.exit(1);
|
|
866
|
+
}
|
|
867
|
+
console.log(`\n✅ ${result.message}`);
|
|
868
|
+
if (result.packagePath) {
|
|
869
|
+
console.log(` 📁 Downloaded to: ${result.packagePath}`);
|
|
870
|
+
}
|
|
871
|
+
if (result.config) {
|
|
872
|
+
console.log(` 📋 ${result.config.title || result.config.name}`);
|
|
873
|
+
if (result.config.description) {
|
|
874
|
+
console.log(` ${result.config.description}`);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
if (result.envVariables && result.envVariables.length > 0 && !skipConfig) {
|
|
878
|
+
console.log('\n🔧 Configuration required:\n');
|
|
879
|
+
await promptForConfiguration(result.envVariables, result.packagePath);
|
|
880
|
+
}
|
|
881
|
+
else if (result.envVariables && result.envVariables.length > 0) {
|
|
882
|
+
console.log('\n⚠️ Environment variables needed (skipped with --skip-config):');
|
|
883
|
+
for (const env of result.envVariables) {
|
|
884
|
+
const status = env.currentValue ? '✅' : (env.required ? '❌' : '⚪');
|
|
885
|
+
console.log(` ${status} ${env.name}: ${env.description}`);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
console.log('\n💡 Plugin added to opencode.json');
|
|
889
|
+
console.log(' To apply changes:');
|
|
890
|
+
console.log(' • New session: restart opencode');
|
|
891
|
+
console.log(' • Keep session: run "opencode --continue" or "pets reload"\n');
|
|
892
|
+
}
|
|
893
|
+
async function promptForConfiguration(envVariables, packagePath) {
|
|
894
|
+
const rl = readline.createInterface({
|
|
895
|
+
input: process.stdin,
|
|
896
|
+
output: process.stdout
|
|
897
|
+
});
|
|
898
|
+
const prompt = (question) => {
|
|
899
|
+
return new Promise((resolve) => {
|
|
900
|
+
rl.question(question, (answer) => {
|
|
901
|
+
resolve(answer);
|
|
902
|
+
});
|
|
903
|
+
});
|
|
904
|
+
};
|
|
905
|
+
const envValues = {};
|
|
906
|
+
const requiredVars = envVariables.filter(v => v.required);
|
|
907
|
+
const optionalVars = envVariables.filter(v => !v.required);
|
|
908
|
+
if (requiredVars.length > 0) {
|
|
909
|
+
console.log('Required environment variables:\n');
|
|
910
|
+
for (const env of requiredVars) {
|
|
911
|
+
const currentValue = env.currentValue ? ` [current: ${env.secret ? '***' : env.currentValue}]` : '';
|
|
912
|
+
const providerInfo = env.provider ? ` (${env.provider})` : '';
|
|
913
|
+
console.log(` ${env.name}${providerInfo}`);
|
|
914
|
+
console.log(` ${env.description}`);
|
|
915
|
+
const value = await prompt(` Enter value${currentValue}: `);
|
|
916
|
+
if (value.trim()) {
|
|
917
|
+
envValues[env.name] = value.trim();
|
|
918
|
+
}
|
|
919
|
+
console.log('');
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
if (optionalVars.length > 0) {
|
|
923
|
+
const configureOptional = await prompt('Configure optional variables? (y/N): ');
|
|
924
|
+
if (configureOptional.toLowerCase() === 'y') {
|
|
925
|
+
console.log('\nOptional environment variables:\n');
|
|
926
|
+
for (const env of optionalVars) {
|
|
927
|
+
const currentValue = env.currentValue ? ` [current: ${env.secret ? '***' : env.currentValue}]` : '';
|
|
928
|
+
const providerInfo = env.provider ? ` (${env.provider})` : '';
|
|
929
|
+
console.log(` ${env.name}${providerInfo}`);
|
|
930
|
+
console.log(` ${env.description}`);
|
|
931
|
+
const value = await prompt(` Enter value${currentValue} (press Enter to skip): `);
|
|
932
|
+
if (value.trim()) {
|
|
933
|
+
envValues[env.name] = value.trim();
|
|
934
|
+
}
|
|
935
|
+
console.log('');
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
rl.close();
|
|
940
|
+
if (Object.keys(envValues).length > 0) {
|
|
941
|
+
// Extract petId from the package path (e.g., /path/to/@openpets/maps -> maps)
|
|
942
|
+
const petId = packagePath.split('/').pop() || 'unknown';
|
|
943
|
+
// Save to .pets/config.json (proper location for pet environment variables)
|
|
944
|
+
const petsConfigDir = resolve(process.cwd(), '.pets');
|
|
945
|
+
const petsConfigPath = resolve(petsConfigDir, 'config.json');
|
|
946
|
+
try {
|
|
947
|
+
// Ensure .pets directory exists
|
|
948
|
+
if (!existsSync(petsConfigDir)) {
|
|
949
|
+
mkdirSync(petsConfigDir, { recursive: true });
|
|
950
|
+
}
|
|
951
|
+
// Load existing config or create new one
|
|
952
|
+
let petsConfig = {
|
|
953
|
+
enabled: [],
|
|
954
|
+
disabled: [],
|
|
955
|
+
envConfig: {},
|
|
956
|
+
};
|
|
957
|
+
if (existsSync(petsConfigPath)) {
|
|
958
|
+
try {
|
|
959
|
+
const existing = JSON.parse(readFileSync(petsConfigPath, 'utf-8'));
|
|
960
|
+
petsConfig = {
|
|
961
|
+
enabled: existing.enabled || [],
|
|
962
|
+
disabled: existing.disabled || [],
|
|
963
|
+
envConfig: existing.envConfig || {},
|
|
964
|
+
...existing
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
catch (e) {
|
|
968
|
+
log('Could not parse existing .pets/config.json, creating new structure');
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
// Ensure envConfig structure exists
|
|
972
|
+
if (!petsConfig.envConfig) {
|
|
973
|
+
petsConfig.envConfig = {};
|
|
974
|
+
}
|
|
975
|
+
// Create or update pet-specific env config
|
|
976
|
+
if (!petsConfig.envConfig[petId]) {
|
|
977
|
+
petsConfig.envConfig[petId] = {};
|
|
978
|
+
}
|
|
979
|
+
// Set the environment variables
|
|
980
|
+
Object.assign(petsConfig.envConfig[petId], envValues);
|
|
981
|
+
// Update timestamp
|
|
982
|
+
petsConfig.last_updated = new Date().toISOString();
|
|
983
|
+
// Write config
|
|
984
|
+
writeFileSync(petsConfigPath, JSON.stringify(petsConfig, null, 2));
|
|
985
|
+
console.log('✅ Configuration saved to .pets/config.json');
|
|
986
|
+
}
|
|
987
|
+
catch (error) {
|
|
988
|
+
console.error(`⚠️ Could not save configuration to .pets/config.json: ${error.message}`);
|
|
989
|
+
console.log('\nAdd these to your .env file or .pets/config.json manually:');
|
|
990
|
+
for (const [key, value] of Object.entries(envValues)) {
|
|
991
|
+
console.log(` ${key}=${value}`);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
async function launchConfigModal(packageName, envVariables) {
|
|
997
|
+
const uiDir = resolve(__dirname, '../../apps/desktop');
|
|
998
|
+
if (!existsSync(uiDir)) {
|
|
999
|
+
console.log('Desktop UI not available, using CLI configuration...');
|
|
1000
|
+
await promptForConfiguration(envVariables, '');
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
console.log('🖥️ Launching configuration modal...');
|
|
1004
|
+
const configData = {
|
|
1005
|
+
packageName,
|
|
1006
|
+
envVariables,
|
|
1007
|
+
projectDir: process.cwd()
|
|
1008
|
+
};
|
|
1009
|
+
const child = spawn('bun', ['run', 'tauri:dev'], {
|
|
1010
|
+
cwd: uiDir,
|
|
1011
|
+
stdio: 'inherit',
|
|
1012
|
+
shell: true,
|
|
1013
|
+
env: {
|
|
1014
|
+
...process.env,
|
|
1015
|
+
OPENPETS_CONFIG_MODE: 'true',
|
|
1016
|
+
OPENPETS_CONFIG_DATA: JSON.stringify(configData),
|
|
1017
|
+
OPENPETS_PROJECT_DIR: process.cwd()
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
child.on('error', (error) => {
|
|
1021
|
+
console.error('Failed to launch configuration modal:', error);
|
|
1022
|
+
console.log('Falling back to CLI configuration...');
|
|
1023
|
+
promptForConfiguration(envVariables, '');
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
async function handlePrune(args) {
|
|
1027
|
+
const keepPets = args.filter(a => !a.startsWith('--'));
|
|
1028
|
+
const dryRun = args.includes('--dry-run');
|
|
1029
|
+
if (keepPets.length === 0) {
|
|
1030
|
+
console.error('❌ At least one pet name required');
|
|
1031
|
+
console.error('Usage: pets prune <pet-name> [pet-name2...] [--dry-run]');
|
|
1032
|
+
console.error('\nExamples:');
|
|
1033
|
+
console.error(' pets prune maps # Keep only maps, disable all others');
|
|
1034
|
+
console.error(' pets prune maps github # Keep maps and github, disable all others');
|
|
1035
|
+
console.error(' pets prune maps --dry-run # Preview changes without applying');
|
|
1036
|
+
process.exit(1);
|
|
1037
|
+
}
|
|
1038
|
+
const opencodeJsonPath = ensureOpencodeJson();
|
|
1039
|
+
const petsConfigDir = resolve(process.cwd(), '.pets');
|
|
1040
|
+
const petsConfigPath = resolve(petsConfigDir, 'config.json');
|
|
1041
|
+
console.log(`\n🔍 Pruning plugins to keep only: ${keepPets.join(', ')}`);
|
|
1042
|
+
if (dryRun) {
|
|
1043
|
+
console.log(' (dry-run mode - no changes will be made)\n');
|
|
1044
|
+
}
|
|
1045
|
+
else {
|
|
1046
|
+
console.log('');
|
|
1047
|
+
}
|
|
1048
|
+
const opencodeConfig = JSON.parse(readFileSync(opencodeJsonPath, 'utf-8'));
|
|
1049
|
+
const currentPlugins = Array.isArray(opencodeConfig.plugin)
|
|
1050
|
+
? opencodeConfig.plugin
|
|
1051
|
+
: (opencodeConfig.plugin ? [opencodeConfig.plugin] : []);
|
|
1052
|
+
const normalizeId = (id) => {
|
|
1053
|
+
return id
|
|
1054
|
+
.replace(/^@openpets\//, '')
|
|
1055
|
+
.replace(/^openpets\//, '')
|
|
1056
|
+
.replace(/^pets\//, '')
|
|
1057
|
+
.replace(/^\.\/pets\//, '')
|
|
1058
|
+
.replace(/\/index\.ts$/, '')
|
|
1059
|
+
.replace(/\/index\.js$/, '');
|
|
1060
|
+
};
|
|
1061
|
+
const keepNormalized = new Set(keepPets.map(normalizeId));
|
|
1062
|
+
const petsToVerify = keepPets.map(normalizeId);
|
|
1063
|
+
const toKeep = [];
|
|
1064
|
+
const toDisable = [];
|
|
1065
|
+
for (const plugin of currentPlugins) {
|
|
1066
|
+
const normalized = normalizeId(plugin);
|
|
1067
|
+
if (!keepNormalized.has(normalized)) {
|
|
1068
|
+
toDisable.push(plugin);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
if (dryRun) {
|
|
1072
|
+
console.log('📦 Plugins to VERIFY and KEEP (will check registry):');
|
|
1073
|
+
for (const pet of petsToVerify) {
|
|
1074
|
+
console.log(` ✅ @openpets/${pet}`);
|
|
1075
|
+
}
|
|
1076
|
+
console.log('\n🚫 Plugins to DISABLE:');
|
|
1077
|
+
if (toDisable.length === 0) {
|
|
1078
|
+
console.log(' (none)');
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
for (const plugin of toDisable) {
|
|
1082
|
+
console.log(` ❌ ${plugin}`);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
console.log('\n💡 Run without --dry-run to apply these changes');
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
console.log('📦 Verifying plugins from registry...\n');
|
|
1089
|
+
const downloader = getPetDownloader();
|
|
1090
|
+
for (const petName of petsToVerify) {
|
|
1091
|
+
console.log(` 📦 ${petName}...`);
|
|
1092
|
+
const result = await downloader.add(petName, {
|
|
1093
|
+
targetDir: process.cwd(),
|
|
1094
|
+
skipConfig: true
|
|
1095
|
+
});
|
|
1096
|
+
if (!result.success) {
|
|
1097
|
+
console.error(`\n❌ ${result.message}`);
|
|
1098
|
+
console.error(`\n⚠️ Prune aborted - fix the above error and try again`);
|
|
1099
|
+
process.exit(1);
|
|
1100
|
+
}
|
|
1101
|
+
console.log(` ✅ ${result.message}`);
|
|
1102
|
+
if (result.packagePath) {
|
|
1103
|
+
console.log(` 📁 ${result.packagePath}`);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
console.log('');
|
|
1107
|
+
const updatedConfig = JSON.parse(readFileSync(opencodeJsonPath, 'utf-8'));
|
|
1108
|
+
const updatedPlugins = Array.isArray(updatedConfig.plugin)
|
|
1109
|
+
? updatedConfig.plugin
|
|
1110
|
+
: (updatedConfig.plugin ? [updatedConfig.plugin] : []);
|
|
1111
|
+
for (const plugin of updatedPlugins) {
|
|
1112
|
+
const normalized = normalizeId(plugin);
|
|
1113
|
+
if (keepNormalized.has(normalized)) {
|
|
1114
|
+
toKeep.push(plugin);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
console.log('📦 Plugins KEPT enabled:');
|
|
1118
|
+
for (const plugin of toKeep) {
|
|
1119
|
+
console.log(` ✅ ${plugin}`);
|
|
1120
|
+
}
|
|
1121
|
+
console.log('\n🚫 Plugins to DISABLE:');
|
|
1122
|
+
if (toDisable.length === 0) {
|
|
1123
|
+
console.log(' (none)');
|
|
1124
|
+
}
|
|
1125
|
+
else {
|
|
1126
|
+
for (const plugin of toDisable) {
|
|
1127
|
+
console.log(` ❌ ${plugin}`);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
const finalConfig = JSON.parse(readFileSync(opencodeJsonPath, 'utf-8'));
|
|
1131
|
+
finalConfig.plugin = toKeep;
|
|
1132
|
+
writeFileSync(opencodeJsonPath, JSON.stringify(finalConfig, null, 2));
|
|
1133
|
+
console.log('\n✅ Updated opencode.json');
|
|
1134
|
+
let petsConfig = {
|
|
1135
|
+
enabled: [],
|
|
1136
|
+
disabled: [],
|
|
1137
|
+
};
|
|
1138
|
+
if (existsSync(petsConfigPath)) {
|
|
1139
|
+
try {
|
|
1140
|
+
petsConfig = JSON.parse(readFileSync(petsConfigPath, 'utf-8'));
|
|
1141
|
+
}
|
|
1142
|
+
catch (e) {
|
|
1143
|
+
log('Could not parse existing .pets/config.json, creating new one');
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
if (!Array.isArray(petsConfig.enabled))
|
|
1147
|
+
petsConfig.enabled = [];
|
|
1148
|
+
if (!Array.isArray(petsConfig.disabled))
|
|
1149
|
+
petsConfig.disabled = [];
|
|
1150
|
+
for (const plugin of toKeep) {
|
|
1151
|
+
const normalized = normalizeId(plugin);
|
|
1152
|
+
petsConfig.disabled = petsConfig.disabled.filter(p => normalizeId(p) !== normalized);
|
|
1153
|
+
if (!petsConfig.enabled.some(p => normalizeId(p) === normalized)) {
|
|
1154
|
+
petsConfig.enabled.push(normalized);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
for (const plugin of toDisable) {
|
|
1158
|
+
const normalized = normalizeId(plugin);
|
|
1159
|
+
petsConfig.enabled = petsConfig.enabled.filter(p => normalizeId(p) !== normalized);
|
|
1160
|
+
if (!petsConfig.disabled.some(p => normalizeId(p) === normalized)) {
|
|
1161
|
+
petsConfig.disabled.push(normalized);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
petsConfig.last_updated = new Date().toISOString();
|
|
1165
|
+
if (!existsSync(petsConfigDir)) {
|
|
1166
|
+
mkdirSync(petsConfigDir, { recursive: true });
|
|
1167
|
+
}
|
|
1168
|
+
writeFileSync(petsConfigPath, JSON.stringify(petsConfig, null, 2));
|
|
1169
|
+
console.log('✅ Updated .pets/config.json');
|
|
1170
|
+
console.log(`\n🎉 Pruned ${toDisable.length} plugin(s), kept ${toKeep.length} plugin(s)`);
|
|
1171
|
+
console.log('💡 To apply changes:');
|
|
1172
|
+
console.log(' • New session: restart opencode');
|
|
1173
|
+
console.log(' • Keep session: run "opencode --continue" or "pets reload"');
|
|
1174
|
+
}
|
|
1175
|
+
async function handleReload(args) {
|
|
1176
|
+
const sessionIndex = args.indexOf('--session');
|
|
1177
|
+
const sessionId = sessionIndex >= 0 ? args[sessionIndex + 1] : undefined;
|
|
1178
|
+
const message = args.filter(a => !a.startsWith('--') && a !== sessionId).join(' ') || undefined;
|
|
1179
|
+
console.log('🔄 Reloading OpenCode session with updated plugins...\n');
|
|
1180
|
+
const opencodeJsonPath = resolve(process.cwd(), 'opencode.json');
|
|
1181
|
+
const opencodeJsoncPath = resolve(process.cwd(), 'opencode.jsonc');
|
|
1182
|
+
if (!existsSync(opencodeJsonPath) && !existsSync(opencodeJsoncPath)) {
|
|
1183
|
+
console.error('❌ No opencode.json found in current directory');
|
|
1184
|
+
console.error(' Run this command from a directory with an opencode.json file');
|
|
1185
|
+
process.exit(1);
|
|
1186
|
+
}
|
|
1187
|
+
const configPath = existsSync(opencodeJsonPath) ? opencodeJsonPath : opencodeJsoncPath;
|
|
1188
|
+
try {
|
|
1189
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
1190
|
+
const plugins = Array.isArray(config.plugin) ? config.plugin : (config.plugin ? [config.plugin] : []);
|
|
1191
|
+
if (plugins.length > 0) {
|
|
1192
|
+
console.log(`📦 Plugins to load: ${plugins.length}`);
|
|
1193
|
+
for (const p of plugins.slice(0, 5)) {
|
|
1194
|
+
const shortName = typeof p === 'string' ? p.replace('@openpets/', '').replace('./pets/', '') : p;
|
|
1195
|
+
console.log(` • ${shortName}`);
|
|
1196
|
+
}
|
|
1197
|
+
if (plugins.length > 5) {
|
|
1198
|
+
console.log(` ... and ${plugins.length - 5} more`);
|
|
1199
|
+
}
|
|
1200
|
+
console.log('');
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
catch (e) {
|
|
1204
|
+
log('Could not read opencode.json:', e);
|
|
1205
|
+
}
|
|
1206
|
+
const opencodeArgs = ['--continue'];
|
|
1207
|
+
if (sessionId) {
|
|
1208
|
+
opencodeArgs.push('--session', sessionId);
|
|
1209
|
+
}
|
|
1210
|
+
if (message) {
|
|
1211
|
+
console.log(`💬 Starting with message: "${message}"\n`);
|
|
1212
|
+
const child = spawn('opencode', ['run', message, ...opencodeArgs], {
|
|
1213
|
+
stdio: 'inherit',
|
|
1214
|
+
shell: true,
|
|
1215
|
+
cwd: process.cwd()
|
|
1216
|
+
});
|
|
1217
|
+
child.on('error', (error) => {
|
|
1218
|
+
console.error('❌ Failed to start opencode:', error.message);
|
|
1219
|
+
process.exit(1);
|
|
1220
|
+
});
|
|
1221
|
+
child.on('exit', (code) => {
|
|
1222
|
+
process.exit(code || 0);
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
else {
|
|
1226
|
+
console.log('🚀 Launching OpenCode TUI...\n');
|
|
1227
|
+
const child = spawn('opencode', opencodeArgs, {
|
|
1228
|
+
stdio: 'inherit',
|
|
1229
|
+
shell: true,
|
|
1230
|
+
cwd: process.cwd()
|
|
1231
|
+
});
|
|
1232
|
+
child.on('error', (error) => {
|
|
1233
|
+
console.error('❌ Failed to start opencode:', error.message);
|
|
1234
|
+
process.exit(1);
|
|
1235
|
+
});
|
|
1236
|
+
child.on('exit', (code) => {
|
|
1237
|
+
process.exit(code || 0);
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
function showUnknownCommandError(command) {
|
|
1242
|
+
const validCommands = [
|
|
1243
|
+
'add', 'install', 'uninstall', 'remove', 'delete', 'inspect', 'search', 'list', 'run', 'prune', 'reload',
|
|
1244
|
+
'init', 'build', 'deploy', 'validate', 'publish', 'playground',
|
|
1245
|
+
'discover', 'registry', 'help'
|
|
1246
|
+
];
|
|
1247
|
+
const similar = validCommands.find(cmd => cmd.startsWith(command.slice(0, 2)) ||
|
|
1248
|
+
command.startsWith(cmd.slice(0, 2)));
|
|
1249
|
+
console.error(`❌ Unknown command: "${command}"`);
|
|
1250
|
+
if (similar) {
|
|
1251
|
+
console.error(` Did you mean: pets ${similar}?`);
|
|
1252
|
+
}
|
|
1253
|
+
console.error(`\nRun "pets help" for available commands.`);
|
|
1254
|
+
}
|
|
1255
|
+
function showHelp() {
|
|
1256
|
+
console.log(`
|
|
1257
|
+
🐾 OpenPets CLI - Plugin Registry & Management Tool
|
|
1258
|
+
|
|
1259
|
+
USAGE:
|
|
1260
|
+
pets <command> [options]
|
|
1261
|
+
|
|
1262
|
+
REGISTRY COMMANDS:
|
|
1263
|
+
add <name> Download pet and add to local opencode.json
|
|
1264
|
+
--version <version> Install specific version
|
|
1265
|
+
--skip-config Skip environment variable configuration
|
|
1266
|
+
--config '{"k":"v"}' Pre-set environment variables
|
|
1267
|
+
|
|
1268
|
+
install <name> Install a pet from the registry (npm install)
|
|
1269
|
+
--client <client> Target client (claude, opencode, cursor, vscode)
|
|
1270
|
+
--config '{"k":"v"}' Pre-configure environment variables
|
|
1271
|
+
--global, -g Install globally
|
|
1272
|
+
--version <version> Install specific version
|
|
1273
|
+
|
|
1274
|
+
uninstall <name> Uninstall a pet (aliases: remove, delete)
|
|
1275
|
+
--client <client> Target client to remove from
|
|
1276
|
+
--global, -g Uninstall globally
|
|
1277
|
+
|
|
1278
|
+
inspect <name> Show detailed package information
|
|
1279
|
+
search [query] Search for pets in the registry
|
|
1280
|
+
--limit <n> Limit results (default: 20)
|
|
1281
|
+
|
|
1282
|
+
list [type] List resources
|
|
1283
|
+
clients Show supported AI clients
|
|
1284
|
+
servers Show installed pets for a client
|
|
1285
|
+
installed Show all installed pets (default)
|
|
1286
|
+
--client <client> Filter by client
|
|
1287
|
+
|
|
1288
|
+
run <name> Run a pet locally
|
|
1289
|
+
--config '{"k":"v"}' Configuration to pass
|
|
1290
|
+
--transport <type> Transport type (stdio, http)
|
|
1291
|
+
|
|
1292
|
+
prune <pet> [pets...] Keep only specified pets, disable all others
|
|
1293
|
+
--dry-run Preview changes without applying
|
|
1294
|
+
|
|
1295
|
+
reload [message] Reload OpenCode with updated plugins (continues session)
|
|
1296
|
+
--session <id> Continue a specific session instead of the last one
|
|
1297
|
+
|
|
1298
|
+
DEVELOPMENT COMMANDS:
|
|
1299
|
+
init [name] Initialize a new pet project
|
|
1300
|
+
--yaml Create openpets.yaml instead of package.json
|
|
1301
|
+
--runtime <type> Runtime type (typescript, bun, container)
|
|
1302
|
+
--dir <path> Target directory
|
|
1303
|
+
|
|
1304
|
+
build <pet-name> Build and validate a pet package
|
|
1305
|
+
deploy <pet-name> Build and deploy with metadata
|
|
1306
|
+
validate [dir] Validate pet configuration
|
|
1307
|
+
|
|
1308
|
+
publish [pet-name] Publish a pet to npm
|
|
1309
|
+
--preview Dry-run mode
|
|
1310
|
+
--channel <name> npm tag (default: latest)
|
|
1311
|
+
|
|
1312
|
+
playground Open the web playground
|
|
1313
|
+
|
|
1314
|
+
DISCOVERY COMMANDS:
|
|
1315
|
+
discover Find OpenPets-compatible packages
|
|
1316
|
+
--github Include GitHub repository search
|
|
1317
|
+
--limit <n> Maximum results (default: 50)
|
|
1318
|
+
|
|
1319
|
+
registry [subcommand] Manage the package registry
|
|
1320
|
+
status Show registry statistics (default)
|
|
1321
|
+
update Refresh the registry from sources
|
|
1322
|
+
export [file] Export registry to JSON file
|
|
1323
|
+
import <file> Import registry from JSON file
|
|
1324
|
+
|
|
1325
|
+
OTHER:
|
|
1326
|
+
(no command) Launch the desktop plugin manager
|
|
1327
|
+
help, --help, -h Show this help message
|
|
1328
|
+
|
|
1329
|
+
EXAMPLES:
|
|
1330
|
+
pets add maps # Download @openpets/maps and configure
|
|
1331
|
+
pets add @openpets/maps --skip-config # Download without configuration prompts
|
|
1332
|
+
pets add postgres --config '{"DATABASE_URL":"postgres://..."}'
|
|
1333
|
+
pets install maps --client claude # Install via npm for specific client
|
|
1334
|
+
pets search github
|
|
1335
|
+
pets inspect linear
|
|
1336
|
+
pets list clients
|
|
1337
|
+
pets list servers --client opencode
|
|
1338
|
+
pets run hackernews
|
|
1339
|
+
pets init my-awesome-pet
|
|
1340
|
+
pets validate ./pets/my-pet
|
|
1341
|
+
pets publish --preview
|
|
1342
|
+
pets discover --github
|
|
1343
|
+
pets registry update
|
|
1344
|
+
pets prune maps # Keep only maps, disable all others
|
|
1345
|
+
pets prune maps github --dry-run # Preview pruning to maps and github
|
|
1346
|
+
pets reload # Reload OpenCode with updated plugins
|
|
1347
|
+
pets reload "test notion" # Reload and send a message
|
|
1348
|
+
|
|
1349
|
+
ENVIRONMENT:
|
|
1350
|
+
PETS_DEBUG=true Enable detailed debug logging
|
|
1351
|
+
GITHUB_TOKEN=<token> GitHub token for repository discovery
|
|
1352
|
+
|
|
1353
|
+
HOW DISCOVERY WORKS:
|
|
1354
|
+
The discovery system finds OpenPets-compatible packages via:
|
|
1355
|
+
|
|
1356
|
+
1. npm Registry: Searches @openpets/* scope and "openpets" keyword
|
|
1357
|
+
2. GitHub (optional): Searches for package.json files containing
|
|
1358
|
+
"$schema": "https://pets.studio/config.json"
|
|
1359
|
+
|
|
1360
|
+
To make your package discoverable:
|
|
1361
|
+
- Publish to npm under @openpets/* scope, OR
|
|
1362
|
+
- Add "openpets" to your package.json keywords, OR
|
|
1363
|
+
- Add "$schema": "https://pets.studio/config.json" to package.json
|
|
1364
|
+
|
|
1365
|
+
Learn more: https://pets.studio/docs
|
|
1366
|
+
`);
|
|
1367
|
+
}
|
|
244
1368
|
//# sourceMappingURL=cli.js.map
|