@monoes/monomindcli 1.10.1 → 1.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Plugin Registry Publisher
4
+ *
5
+ * Publishes the plugin registry to IPFS via Pinata and updates IPNS pointer.
6
+ *
7
+ * Setup:
8
+ * 1. Create Pinata account at https://pinata.cloud
9
+ * 2. Generate API keys (JWT)
10
+ * 3. Set environment variables:
11
+ * - PINATA_JWT: Your Pinata JWT token
12
+ * - REGISTRY_PRIVATE_KEY: Ed25519 private key (hex) for signing
13
+ *
14
+ * Usage:
15
+ * npx tsx scripts/publish-registry.ts
16
+ * npx tsx scripts/publish-registry.ts --dry-run
17
+ * npx tsx scripts/publish-registry.ts --registry ./custom-registry.json
18
+ */
19
+
20
+ import * as fs from 'fs';
21
+ import * as path from 'path';
22
+ import * as crypto from 'crypto';
23
+ import { fileURLToPath } from 'url';
24
+
25
+ const __filename = fileURLToPath(import.meta.url);
26
+ const __dirname = path.dirname(__filename);
27
+
28
+ // Types
29
+ interface PluginEntry {
30
+ id: string;
31
+ name: string;
32
+ displayName: string;
33
+ description: string;
34
+ version: string;
35
+ cid?: string;
36
+ size: number;
37
+ checksum: string;
38
+ author: {
39
+ id: string;
40
+ displayName: string;
41
+ verified: boolean;
42
+ };
43
+ license: string;
44
+ categories: string[];
45
+ tags: string[];
46
+ downloads: number;
47
+ rating: number;
48
+ lastUpdated: string;
49
+ minMonomindVersion: string;
50
+ type: string;
51
+ hooks: string[];
52
+ commands: string[];
53
+ permissions: string[];
54
+ exports: string[];
55
+ verified: boolean;
56
+ trustLevel: string;
57
+ }
58
+
59
+ interface PluginRegistry {
60
+ version: string;
61
+ type: 'plugins';
62
+ updatedAt: string;
63
+ ipnsName: string;
64
+ plugins: PluginEntry[];
65
+ categories: Array<{ id: string; name: string; description: string; pluginCount: number }>;
66
+ totalPlugins: number;
67
+ totalDownloads: number;
68
+ featured: string[];
69
+ trending: string[];
70
+ newest: string[];
71
+ official: string[];
72
+ registrySignature?: string;
73
+ registryPublicKey?: string;
74
+ }
75
+
76
+ interface PinataResponse {
77
+ IpfsHash: string;
78
+ PinSize: number;
79
+ Timestamp: string;
80
+ }
81
+
82
+ // Configuration
83
+ const PINATA_API_URL = 'https://api.pinata.cloud';
84
+ const DEFAULT_REGISTRY_PATH = path.join(__dirname, '../src/plugins/store/registry.json');
85
+
86
+ // Parse command line arguments
87
+ const args = process.argv.slice(2);
88
+ const isDryRun = args.includes('--dry-run');
89
+ const registryPathArg = args.find(a => a.startsWith('--registry='));
90
+ const registryPath = registryPathArg
91
+ ? registryPathArg.split('=')[1]
92
+ : DEFAULT_REGISTRY_PATH;
93
+
94
+ /**
95
+ * Fetch npm stats for a package
96
+ */
97
+ async function fetchNpmStats(packageName: string): Promise<{ downloads: number; version: string } | null> {
98
+ try {
99
+ const downloadsUrl = `https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(packageName)}`;
100
+ const downloadsRes = await fetch(downloadsUrl, { signal: AbortSignal.timeout(5000) });
101
+
102
+ if (!downloadsRes.ok) return null;
103
+
104
+ const downloadsData = await downloadsRes.json() as { downloads?: number };
105
+
106
+ const packageUrl = `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`;
107
+ const packageRes = await fetch(packageUrl, { signal: AbortSignal.timeout(5000) });
108
+
109
+ let version = 'unknown';
110
+ if (packageRes.ok) {
111
+ const packageData = await packageRes.json() as { version?: string };
112
+ version = packageData.version || 'unknown';
113
+ }
114
+
115
+ return {
116
+ downloads: downloadsData.downloads || 0,
117
+ version,
118
+ };
119
+ } catch {
120
+ return null;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Sign registry with Ed25519
126
+ */
127
+ async function signRegistry(registry: PluginRegistry, privateKeyHex: string): Promise<{
128
+ signature: string;
129
+ publicKey: string;
130
+ }> {
131
+ const ed = await import('@noble/ed25519');
132
+
133
+ const privateKey = Buffer.from(privateKeyHex, 'hex');
134
+ const publicKey = await ed.getPublicKeyAsync(privateKey);
135
+
136
+ // Create a copy without signature fields for signing
137
+ const registryToSign = { ...registry };
138
+ delete registryToSign.registrySignature;
139
+ delete registryToSign.registryPublicKey;
140
+
141
+ const message = JSON.stringify(registryToSign);
142
+ const signature = await ed.signAsync(
143
+ new TextEncoder().encode(message),
144
+ privateKey
145
+ );
146
+
147
+ return {
148
+ signature: Buffer.from(signature).toString('hex'),
149
+ publicKey: `ed25519:${Buffer.from(publicKey).toString('hex')}`,
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Pin JSON to IPFS via Pinata
155
+ */
156
+ async function pinToIPFS(data: unknown, name: string, jwt: string): Promise<PinataResponse> {
157
+ const response = await fetch(`${PINATA_API_URL}/pinning/pinJSONToIPFS`, {
158
+ method: 'POST',
159
+ headers: {
160
+ 'Content-Type': 'application/json',
161
+ 'Authorization': `Bearer ${jwt}`,
162
+ },
163
+ body: JSON.stringify({
164
+ pinataContent: data,
165
+ pinataMetadata: {
166
+ name,
167
+ keyvalues: {
168
+ type: 'plugin-registry',
169
+ publishedAt: new Date().toISOString(),
170
+ },
171
+ },
172
+ pinataOptions: {
173
+ cidVersion: 1,
174
+ },
175
+ }),
176
+ });
177
+
178
+ if (!response.ok) {
179
+ const error = await response.text();
180
+ throw new Error(`Pinata error: ${response.status} - ${error}`);
181
+ }
182
+
183
+ return response.json() as Promise<PinataResponse>;
184
+ }
185
+
186
+ /**
187
+ * Generate a demo registry from npm packages
188
+ */
189
+ async function generateRegistry(): Promise<PluginRegistry> {
190
+ console.log('šŸ“¦ Fetching npm stats for plugins...');
191
+
192
+ const officialPackages = [
193
+ '@monomind/plugin-agentic-qe',
194
+ '@monomind/plugin-prime-radiant',
195
+ '@monomind/plugin-gastown-bridge',
196
+ '@monomind/security',
197
+ '@monomind/claims',
198
+ '@monomind/embeddings',
199
+ '@monomind/neural',
200
+ '@monomind/performance',
201
+ '@monomind/plugins',
202
+ ];
203
+
204
+ const plugins: PluginEntry[] = [];
205
+ const now = new Date().toISOString();
206
+
207
+ for (const pkg of officialPackages) {
208
+ console.log(` Fetching ${pkg}...`);
209
+ const stats = await fetchNpmStats(pkg);
210
+
211
+ plugins.push({
212
+ id: pkg,
213
+ name: pkg,
214
+ displayName: pkg.replace('@monomind/plugin-', '').replace('@monomind/', ''),
215
+ description: `Official Monomind plugin: ${pkg}`,
216
+ version: stats?.version || '0.0.0',
217
+ size: 100000,
218
+ checksum: `sha256:${crypto.randomBytes(32).toString('hex')}`,
219
+ author: {
220
+ id: 'monomind-team',
221
+ displayName: 'Monomind Team',
222
+ verified: true,
223
+ },
224
+ license: 'MIT',
225
+ categories: ['official'],
226
+ tags: [pkg.split('/').pop() || ''],
227
+ downloads: stats?.downloads || 0,
228
+ rating: 0,
229
+ lastUpdated: now,
230
+ minMonomindVersion: '3.0.0',
231
+ type: 'integration',
232
+ hooks: [],
233
+ commands: [],
234
+ permissions: ['memory', 'filesystem'],
235
+ exports: [],
236
+ verified: true,
237
+ trustLevel: 'official',
238
+ });
239
+ }
240
+
241
+ const totalDownloads = plugins.reduce((sum, p) => sum + p.downloads, 0);
242
+
243
+ return {
244
+ version: '1.0.0',
245
+ type: 'plugins',
246
+ updatedAt: now,
247
+ ipnsName: '', // Will be set after publishing
248
+ plugins,
249
+ categories: [
250
+ { id: 'official', name: 'Official', description: 'Official Monomind plugins', pluginCount: plugins.length },
251
+ ],
252
+ totalPlugins: plugins.length,
253
+ totalDownloads,
254
+ featured: plugins.slice(0, 3).map(p => p.id),
255
+ trending: plugins.sort((a, b) => b.downloads - a.downloads).slice(0, 3).map(p => p.id),
256
+ newest: plugins.slice(-3).map(p => p.id),
257
+ official: plugins.map(p => p.id),
258
+ };
259
+ }
260
+
261
+ /**
262
+ * Main publish function
263
+ */
264
+ async function main() {
265
+ console.log('šŸš€ Plugin Registry Publisher\n');
266
+
267
+ // Check environment
268
+ const jwt = process.env.PINATA_JWT;
269
+ const privateKey = process.env.REGISTRY_PRIVATE_KEY;
270
+
271
+ if (!jwt) {
272
+ console.error('āŒ PINATA_JWT environment variable is required');
273
+ console.log('\nGet your JWT from https://pinata.cloud/keys');
274
+ process.exit(1);
275
+ }
276
+
277
+ // Load or generate registry
278
+ let registry: PluginRegistry;
279
+
280
+ if (fs.existsSync(registryPath)) {
281
+ console.log(`šŸ“„ Loading registry from ${registryPath}`);
282
+ const content = fs.readFileSync(registryPath, 'utf-8');
283
+ registry = JSON.parse(content);
284
+ } else {
285
+ console.log('šŸ“„ Generating registry from npm packages...');
286
+ registry = await generateRegistry();
287
+ }
288
+
289
+ // Update timestamp
290
+ registry.updatedAt = new Date().toISOString();
291
+
292
+ console.log(`\nšŸ“Š Registry Stats:`);
293
+ console.log(` Plugins: ${registry.plugins.length}`);
294
+ console.log(` Total Downloads: ${registry.totalDownloads.toLocaleString()}`);
295
+ console.log(` Updated: ${registry.updatedAt}`);
296
+
297
+ // Sign registry if private key is available
298
+ if (privateKey) {
299
+ console.log('\nšŸ” Signing registry with Ed25519...');
300
+ const { signature, publicKey } = await signRegistry(registry, privateKey);
301
+ registry.registrySignature = signature;
302
+ registry.registryPublicKey = publicKey;
303
+ console.log(` Public Key: ${publicKey.slice(0, 30)}...`);
304
+ } else {
305
+ console.log('\nāš ļø No REGISTRY_PRIVATE_KEY set, skipping signature');
306
+ }
307
+
308
+ if (isDryRun) {
309
+ console.log('\nšŸ” Dry run - would publish:');
310
+ console.log(JSON.stringify(registry, null, 2).slice(0, 1000) + '...');
311
+ return;
312
+ }
313
+
314
+ // Pin to IPFS
315
+ console.log('\nšŸ“Œ Pinning to IPFS via Pinata...');
316
+ try {
317
+ const result = await pinToIPFS(registry, 'monomind-plugin-registry', jwt);
318
+
319
+ console.log('\nāœ… Published successfully!');
320
+ console.log(` CID: ${result.IpfsHash}`);
321
+ console.log(` Size: ${(result.PinSize / 1024).toFixed(2)} KB`);
322
+ console.log(`\n🌐 Gateway URLs:`);
323
+ console.log(` https://gateway.pinata.cloud/ipfs/${result.IpfsHash}`);
324
+ console.log(` https://ipfs.io/ipfs/${result.IpfsHash}`);
325
+ console.log(` https://cloudflare-ipfs.com/ipfs/${result.IpfsHash}`);
326
+ console.log(` https://dweb.link/ipfs/${result.IpfsHash}`);
327
+
328
+ // Save CID for reference
329
+ const cidFile = path.join(__dirname, '../.registry-cid');
330
+ fs.writeFileSync(cidFile, result.IpfsHash);
331
+ console.log(`\nšŸ’¾ CID saved to ${cidFile}`);
332
+
333
+ // Update discovery.ts config (manual step reminder)
334
+ console.log('\nšŸ“ Next steps:');
335
+ console.log(' 1. Update DEFAULT_PLUGIN_STORE_CONFIG in discovery.ts with the new CID');
336
+ console.log(' 2. If using IPNS, update the IPNS pointer via Pinata dashboard');
337
+ console.log(' 3. Test with: npx monomind@latest plugins list');
338
+ } catch (error) {
339
+ console.error('\nāŒ Publish failed:', error);
340
+ process.exit(1);
341
+ }
342
+ }
343
+
344
+ // Run
345
+ main().catch(console.error);
@@ -0,0 +1,55 @@
1
+ #!/bin/bash
2
+ # Publish script for @monomind/cli
3
+ # Publishes to both @monomind/cli@alpha AND monomind@alpha
4
+
5
+ set -e
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ CLI_DIR="$(dirname "$SCRIPT_DIR")"
9
+
10
+ cd "$CLI_DIR"
11
+
12
+ # Get current version
13
+ VERSION=$(node -p "require('./package.json').version")
14
+ echo "Publishing version: $VERSION"
15
+
16
+ # 1. Publish @monomind/cli with alpha tag
17
+ echo ""
18
+ echo "=== Publishing @monomind/cli@$VERSION (alpha tag) ==="
19
+ npm publish --tag alpha
20
+
21
+ # 2. Publish to monomind with alpha tag
22
+ echo ""
23
+ echo "=== Publishing monomind@$VERSION (alpha tag) ==="
24
+
25
+ # Create temp directory
26
+ TEMP_DIR=$(mktemp -d)
27
+ trap "rm -rf $TEMP_DIR" EXIT
28
+
29
+ # Copy necessary files
30
+ cp -r dist bin src package.json README.md "$TEMP_DIR/"
31
+
32
+ # Change package name to unscoped
33
+ cd "$TEMP_DIR"
34
+ sed -i 's/"name": "@monomind\/cli"/"name": "monomind"/' package.json
35
+
36
+ # Publish with alpha tag
37
+ npm publish --tag alpha
38
+
39
+ echo ""
40
+ echo "=== Updating dist-tags ==="
41
+
42
+ # Update all tags to point to the new version
43
+ npm dist-tag add @monomind/cli@$VERSION alpha
44
+ npm dist-tag add @monomind/cli@$VERSION latest
45
+ npm dist-tag add monomind@$VERSION alpha
46
+ npm dist-tag add monomind@$VERSION latest
47
+
48
+ echo ""
49
+ echo "=== Published successfully ==="
50
+ echo " @monomind/cli@$VERSION (alpha, latest)"
51
+ echo " monomind@$VERSION (alpha, latest)"
52
+ echo ""
53
+ echo "Install with:"
54
+ echo " npx monomind@alpha"
55
+ echo " npx @monomind/cli@latest"