@moltos/sdk 0.10.14 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js DELETED
@@ -1,857 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * MoltOS CLI
4
- *
5
- * A premium command-line interface for the MoltOS Agent Operating System.
6
- *
7
- * Features:
8
- * - Beautiful ASCII logo and gradient banners
9
- * - Animated spinners and progress indicators
10
- * - Rich tables for data display
11
- * - Interactive prompts with validation
12
- * - JSON output mode for scripting
13
- * - Real-time streaming for logs/events
14
- *
15
- * Usage: moltos <command> [options]
16
- */
17
- import { program } from 'commander';
18
- import chalk from 'chalk';
19
- import gradient from 'gradient-string';
20
- import figlet from 'figlet';
21
- import ora from 'ora';
22
- import boxen from 'boxen';
23
- import Table from 'cli-table3';
24
- import inquirer from 'inquirer';
25
- import logSymbols from 'log-symbols';
26
- import { readFileSync, existsSync, writeFileSync, mkdirSync, chmodSync } from 'fs';
27
- import { join } from 'path';
28
- import crypto from 'crypto';
29
- import { MoltOSSDK } from './index.js';
30
- const MOLTOS_API = process.env.MOLTOS_API_URL || 'https://moltos.org/api';
31
- // ============================================================================
32
- // Visual Design System
33
- // ============================================================================
34
- const colors = {
35
- primary: '#00D9FF', // Cyan
36
- secondary: '#FF6B6B', // Coral
37
- success: '#00E676', // Green
38
- warning: '#FFD93D', // Yellow
39
- error: '#FF4757', // Red
40
- muted: '#6C757D', // Gray
41
- accent: '#A855F7', // Purple
42
- };
43
- const moltosGradient = gradient(['#00D9FF', '#0099CC', '#A855F7']);
44
- const successGradient = gradient(['#00E676', '#00C853']);
45
- const errorGradient = gradient(['#FF4757', '#D32F2F']);
46
- // ============================================================================
47
- // Logo & Branding
48
- // ============================================================================
49
- function showBanner() {
50
- console.clear();
51
- const logo = figlet.textSync('MoltOS', {
52
- font: 'Small Slant',
53
- horizontalLayout: 'default',
54
- verticalLayout: 'default'
55
- });
56
- console.log(moltosGradient(logo));
57
- console.log(chalk.gray('─'.repeat(60)));
58
- console.log(chalk.dim(' The Agent Operating System v0.8.3'));
59
- console.log(chalk.gray('─'.repeat(60)));
60
- console.log();
61
- }
62
- function showMiniBanner() {
63
- console.log(moltosGradient('⚡ MoltOS') + chalk.dim(' v0.8.3'));
64
- console.log();
65
- }
66
- // ============================================================================
67
- // UI Components
68
- // ============================================================================
69
- function successBox(message, title) {
70
- console.log(boxen(message, {
71
- padding: 1,
72
- margin: { top: 1, bottom: 1 },
73
- borderStyle: 'round',
74
- borderColor: 'green',
75
- title: title ? chalk.green(title) : undefined,
76
- titleAlignment: 'center'
77
- }));
78
- }
79
- function errorBox(message, title = 'Error') {
80
- console.log(boxen(message, {
81
- padding: 1,
82
- margin: { top: 1, bottom: 1 },
83
- borderStyle: 'double',
84
- borderColor: 'red',
85
- title: chalk.red(title),
86
- titleAlignment: 'center'
87
- }));
88
- }
89
- function infoBox(message, title) {
90
- console.log(boxen(message, {
91
- padding: 1,
92
- margin: { top: 0, bottom: 1 },
93
- borderStyle: 'single',
94
- borderColor: 'cyan',
95
- title: title ? chalk.cyan(title) : undefined,
96
- titleAlignment: 'left'
97
- }));
98
- }
99
- function createDataTable(headers) {
100
- return new Table({
101
- head: headers.map(h => chalk.cyan.bold(h)),
102
- style: {
103
- head: [],
104
- border: ['gray']
105
- },
106
- chars: {
107
- 'top': '─',
108
- 'top-mid': '┬',
109
- 'top-left': '┌',
110
- 'top-right': '┐',
111
- 'bottom': '─',
112
- 'bottom-mid': '┴',
113
- 'bottom-left': '└',
114
- 'bottom-right': '┘',
115
- 'left': '│',
116
- 'left-mid': '├',
117
- 'mid': '─',
118
- 'mid-mid': '┼',
119
- 'right': '│',
120
- 'right-mid': '┤',
121
- 'middle': '│'
122
- }
123
- });
124
- }
125
- // ============================================================================
126
- // Progress & Loading
127
- // ============================================================================
128
- function createProgressBar(total, text) {
129
- let current = 0;
130
- const render = () => {
131
- const percentage = Math.round((current / total) * 100);
132
- const filled = Math.round((current / total) * 30);
133
- const empty = 30 - filled;
134
- const bar = '█'.repeat(filled) + '░'.repeat(empty);
135
- process.stdout.clearLine(0);
136
- process.stdout.cursorTo(0);
137
- process.stdout.write(`${chalk.cyan('⏳')} ${text} [${chalk.green(bar)}] ${percentage}% (${current}/${total})`);
138
- };
139
- return {
140
- increment: () => {
141
- current = Math.min(current + 1, total);
142
- render();
143
- },
144
- update: (value) => {
145
- current = Math.min(value, total);
146
- render();
147
- },
148
- complete: () => {
149
- process.stdout.clearLine(0);
150
- process.stdout.cursorTo(0);
151
- console.log(`${logSymbols.success} ${text} ${chalk.green('Complete!')}`);
152
- },
153
- fail: () => {
154
- process.stdout.clearLine(0);
155
- process.stdout.cursorTo(0);
156
- console.log(`${logSymbols.error} ${text} ${chalk.red('Failed')}`);
157
- }
158
- };
159
- }
160
- // ============================================================================
161
- // Helper Functions
162
- // ============================================================================
163
- function formatReputation(score) {
164
- if (score >= 5000)
165
- return chalk.magenta('💎 ' + score.toLocaleString());
166
- if (score >= 2000)
167
- return chalk.yellow('🥇 ' + score.toLocaleString());
168
- if (score >= 1000)
169
- return chalk.gray('🥈 ' + score.toLocaleString());
170
- return chalk.dim(score.toLocaleString());
171
- }
172
- function formatStatus(status) {
173
- const map = {
174
- 'active': chalk.green('● Active'),
175
- 'inactive': chalk.gray('○ Inactive'),
176
- 'pending': chalk.yellow('◐ Pending'),
177
- 'suspended': chalk.red('✕ Suspended'),
178
- };
179
- return map[status] || status;
180
- }
181
- function truncate(str, length) {
182
- if (str.length <= length)
183
- return str;
184
- return str.substring(0, length - 3) + '...';
185
- }
186
- // Helper to load agent config and initialize SDK
187
- async function initSDK() {
188
- const configPath = join(process.cwd(), '.moltos', 'config.json');
189
- if (!existsSync(configPath)) {
190
- throw new Error('No agent config found. Run "moltos init" first.');
191
- }
192
- const config = JSON.parse(readFileSync(configPath, 'utf-8'));
193
- if (!config.agentId || !config.apiKey) {
194
- throw new Error('Agent not registered. Run "moltos register" first.');
195
- }
196
- const sdk = new MoltOSSDK();
197
- await sdk.init(config.agentId, config.apiKey);
198
- // Attach config for ClawFS signing
199
- sdk._config = config;
200
- return sdk;
201
- }
202
- // Helper to sign ClawFS payloads with Ed25519
203
- async function signClawFSPayload(privateKeyHex, payload) {
204
- const timestamp = Date.now();
205
- // Include path and timestamp in challenge for uniqueness
206
- const challenge = crypto.randomBytes(32).toString('base64') + '_' + payload.path + '_' + timestamp;
207
- const fullPayload = {
208
- path: payload.path,
209
- content_hash: payload.content_hash,
210
- challenge,
211
- timestamp
212
- };
213
- // DEBUG: Log exact payload being signed
214
- const sortedPayload = JSON.stringify(fullPayload, Object.keys(fullPayload).sort());
215
- console.log('[SDK] Signing payload:', sortedPayload);
216
- console.log('[SDK] Message bytes (hex):', Buffer.from(new TextEncoder().encode(sortedPayload)).toString('hex'));
217
- const message = new TextEncoder().encode(sortedPayload);
218
- // Import Ed25519 from @noble/curves (ESM dynamic import)
219
- const { ed25519 } = await import('@noble/curves/ed25519.js');
220
- // Parse private key (handle both raw 32-byte and PKCS8 formats)
221
- let privateKeyBytes;
222
- const keyBuffer = Buffer.from(privateKeyHex, 'hex');
223
- if (keyBuffer.length === 32) {
224
- // Raw private key
225
- privateKeyBytes = new Uint8Array(keyBuffer);
226
- }
227
- else if (keyBuffer.length > 32) {
228
- // PKCS8 format - extract last 32 bytes (private key)
229
- privateKeyBytes = new Uint8Array(keyBuffer.slice(-32));
230
- }
231
- else {
232
- throw new Error('Invalid private key length');
233
- }
234
- // Sign with Ed25519
235
- const signatureBytes = ed25519.sign(message, privateKeyBytes);
236
- const signature = Buffer.from(signatureBytes).toString('base64');
237
- console.log('[SDK] Signature base64:', signature);
238
- console.log('[SDK] Signature bytes (hex):', Buffer.from(signatureBytes).toString('hex'));
239
- return { signature, timestamp, challenge };
240
- }
241
- // ============================================================================
242
- // Commands
243
- // ============================================================================
244
- program
245
- .name('moltos')
246
- .description('MoltOS CLI — The Agent Operating System')
247
- .version('0.8.3')
248
- .option('-j, --json', 'Output in JSON format for scripting')
249
- .option('-v, --verbose', 'Verbose output')
250
- .hook('preAction', (thisCommand) => {
251
- const options = thisCommand.opts();
252
- if (!options.json) {
253
- showMiniBanner();
254
- }
255
- });
256
- // ─────────────────────────────────────────────────────────────────────────────
257
- // Agent Commands
258
- // ─────────────────────────────────────────────────────────────────────────────
259
- program
260
- .command('init [name]')
261
- .description('Initialize a new agent configuration')
262
- .option('-n, --name <name>', 'Agent name (overrides positional arg)')
263
- .option('--non-interactive', 'Skip interactive prompts')
264
- .action(async (nameArg, options) => {
265
- const isJson = program.opts().json;
266
- if (isJson) {
267
- console.log(JSON.stringify({ error: 'Interactive command not available in JSON mode' }, null, 2));
268
- return;
269
- }
270
- showBanner();
271
- const name = options.name || nameArg || 'my-agent';
272
- const answers = options.nonInteractive
273
- ? { name, generateKeys: true }
274
- : await inquirer.prompt([
275
- {
276
- type: 'input',
277
- name: 'name',
278
- message: moltosGradient('What should we call your agent?'),
279
- default: name,
280
- validate: (input) => input.length >= 3 || 'Name must be at least 3 characters'
281
- },
282
- {
283
- type: 'confirm',
284
- name: 'generateKeys',
285
- message: 'Generate BLS12-381 keypair for attestations?',
286
- default: true
287
- }
288
- ]);
289
- const spinner = ora({
290
- text: chalk.cyan('Generating agent identity...'),
291
- spinner: 'dots'
292
- }).start();
293
- try {
294
- // Generate Ed25519 keypair using Node.js crypto
295
- const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
296
- const publicKeyHex = publicKey.export({ type: 'spki', format: 'der' }).toString('hex');
297
- const privateKeyHex = privateKey.export({ type: 'pkcs8', format: 'der' }).toString('hex');
298
- spinner.succeed(chalk.green('Identity generated!'));
299
- let blsPublicKey;
300
- if (answers.generateKeys) {
301
- const keySpinner = ora({
302
- text: chalk.cyan('Generating BLS12-381 keys (this may take a moment)...'),
303
- spinner: 'arc'
304
- }).start();
305
- // Mock BLS key for now - real implementation needs @chainsafe/blst
306
- await new Promise(resolve => setTimeout(resolve, 500));
307
- blsPublicKey = 'bls_' + crypto.randomBytes(48).toString('hex');
308
- keySpinner.succeed(chalk.green('BLS keys generated!'));
309
- }
310
- // Write config file
311
- const configDir = join(process.cwd(), '.moltos');
312
- const configPath = join(configDir, 'config.json');
313
- if (!existsSync(configDir)) {
314
- mkdirSync(configDir, { recursive: true });
315
- }
316
- const config = {
317
- agentId: null, // Will be set after registration
318
- apiKey: null,
319
- name: answers.name,
320
- publicKey: publicKeyHex.slice(-64), // Extract raw 32-byte key from DER
321
- privateKey: privateKeyHex,
322
- blsPublicKey: blsPublicKey,
323
- createdAt: new Date().toISOString()
324
- };
325
- writeFileSync(configPath, JSON.stringify(config, null, 2));
326
- chmodSync(configPath, 0o600); // Restrict permissions
327
- successBox(`Agent "${chalk.bold(answers.name)}" initialized!\n\n` +
328
- `${chalk.gray('Config saved to:')} ${chalk.dim(configPath)}\n\n` +
329
- `${chalk.gray('Next steps:')}\n` +
330
- ` ${chalk.cyan('>')} moltos register\n` +
331
- ` ${chalk.cyan('>')} moltos status`, '✨ Success');
332
- }
333
- catch (error) {
334
- spinner.fail(chalk.red('Initialization failed'));
335
- errorBox(error.message);
336
- process.exit(1);
337
- }
338
- });
339
- program
340
- .command('register')
341
- .description('Register your agent with MoltOS')
342
- .option('-n, --name <name>', 'Agent name (overrides config)')
343
- .option('-k, --public-key <key>', 'Ed25519 public key (hex, overrides config)')
344
- .action(async (options) => {
345
- const isJson = program.opts().json;
346
- // Load config
347
- const configPath = join(process.cwd(), '.moltos', 'config.json');
348
- if (!existsSync(configPath)) {
349
- const error = 'No agent config found. Run "moltos init" first.';
350
- if (isJson) {
351
- console.log(JSON.stringify({ success: false, error }, null, 2));
352
- }
353
- else {
354
- errorBox(error);
355
- }
356
- process.exit(1);
357
- }
358
- const config = JSON.parse(readFileSync(configPath, 'utf-8'));
359
- const spinner = ora({
360
- text: isJson ? undefined : chalk.cyan('Registering agent...'),
361
- spinner: 'dots'
362
- });
363
- if (!isJson)
364
- spinner.start();
365
- try {
366
- const sdk = new MoltOSSDK(MOLTOS_API);
367
- // Call registration API
368
- const name = options.name || config.name;
369
- const publicKey = options.publicKey || config.publicKey;
370
- const result = await fetch(`${MOLTOS_API}/agent/register`, {
371
- method: 'POST',
372
- headers: { 'Content-Type': 'application/json' },
373
- body: JSON.stringify({
374
- name,
375
- publicKey: publicKey,
376
- metadata: {
377
- bls_public_key: config.blsPublicKey,
378
- },
379
- }),
380
- });
381
- const data = await result.json();
382
- if (!result.ok) {
383
- throw new Error(data.error || 'Registration failed');
384
- }
385
- // Update config with credentials
386
- config.agentId = data.agent.agentId;
387
- config.apiKey = data.credentials.apiKey;
388
- writeFileSync(configPath, JSON.stringify(config, null, 2));
389
- chmodSync(configPath, 0o600);
390
- if (isJson) {
391
- console.log(JSON.stringify({
392
- success: true,
393
- agent_id: data.agent.agentId,
394
- api_key: data.credentials.apiKey,
395
- message: 'Agent registered successfully'
396
- }, null, 2));
397
- return;
398
- }
399
- spinner.succeed(chalk.green('Agent registered!'));
400
- successBox(`${chalk.bold('Your API Key:')}\n` +
401
- `${chalk.yellow(data.credentials.apiKey)}\n\n` +
402
- `${chalk.red('⚠️ Save this key! It will not be shown again.')}\n\n` +
403
- `${chalk.gray('Config updated with credentials.')}\n\n` +
404
- `${chalk.gray('Export to environment:')}\n` +
405
- ` ${chalk.cyan(`export MOLTOS_API_KEY=${data.credentials.apiKey}`)}`, '🔑 API Key');
406
- }
407
- catch (error) {
408
- if (!isJson)
409
- spinner.fail(chalk.red('Registration failed'));
410
- if (isJson) {
411
- console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
412
- }
413
- else {
414
- errorBox(error.message);
415
- }
416
- process.exit(1);
417
- }
418
- });
419
- // ─────────────────────────────────────────────────────────────────────────────
420
- // Status Command (with JSON support)
421
- // ─────────────────────────────────────────────────────────────────────────────
422
- program
423
- .command('status')
424
- .description('Check agent status and reputation')
425
- .option('-a, --agent-id <id>', 'Check specific agent')
426
- .option('--json', 'Output as JSON (for scripting)')
427
- .action(async (options) => {
428
- const isJson = options.json || program.opts().json;
429
- if (!isJson) {
430
- const spinner = ora({
431
- text: chalk.cyan('Fetching agent status...'),
432
- spinner: 'dots'
433
- }).start();
434
- await new Promise(resolve => setTimeout(resolve, 600));
435
- spinner.stop();
436
- }
437
- // Mock data for demonstration
438
- const mockStatus = {
439
- agent: {
440
- agent_id: options.agentId || 'agent_demo_123',
441
- name: 'Demo Agent',
442
- reputation: 2847,
443
- is_genesis: false,
444
- activation_status: 'active',
445
- created_at: '2025-03-15T10:30:00Z'
446
- },
447
- tap_score: {
448
- global_trust_score: 0.847,
449
- attestation_count: 156,
450
- last_calculated: '2025-03-19T08:00:00Z'
451
- }
452
- };
453
- if (isJson) {
454
- console.log(JSON.stringify(mockStatus, null, 2));
455
- return;
456
- }
457
- // Rich visual output
458
- const table = createDataTable(['Property', 'Value']);
459
- table.push([chalk.gray('Name'), chalk.bold(mockStatus.agent.name)], [chalk.gray('ID'), chalk.dim(mockStatus.agent.agent_id)], [chalk.gray('Status'), formatStatus(mockStatus.agent.activation_status)], [chalk.gray('Reputation'), formatReputation(mockStatus.agent.reputation)], [chalk.gray('TAP Score'), chalk.cyan((mockStatus.tap_score.global_trust_score * 100).toFixed(1) + '%')], [chalk.gray('Attestations'), chalk.white(mockStatus.tap_score.attestation_count.toString())], [chalk.gray('Genesis'), mockStatus.agent.is_genesis ? chalk.green('✓ Yes') : chalk.gray('No')]);
460
- infoBox(table.toString(), '📊 Agent Profile');
461
- // Reputation bar
462
- const repPercent = Math.min(mockStatus.agent.reputation / 5000 * 100, 100);
463
- const filled = Math.round(repPercent / 5);
464
- const bar = '█'.repeat(filled) + '░'.repeat(20 - filled);
465
- console.log(chalk.gray('Reputation Progress: ') + chalk.green(bar) + chalk.gray(` ${repPercent.toFixed(0)}%`));
466
- console.log();
467
- });
468
- // ─────────────────────────────────────────────────────────────────────────────
469
- // Attestation Commands
470
- // ─────────────────────────────────────────────────────────────────────────────
471
- program
472
- .command('attest')
473
- .description('Submit an attestation for another agent')
474
- .requiredOption('-t, --target <agent>', 'Target agent ID')
475
- .requiredOption('-s, --score <score>', 'Attestation score (0-100)', parseInt)
476
- .option('-c, --claim <text>', 'Attestation claim/comment')
477
- .option('--batch <file>', 'Batch attestations from JSON file')
478
- .action(async (options) => {
479
- const isJson = program.opts().json;
480
- // Batch mode
481
- if (options.batch) {
482
- console.log(chalk.cyan('📦 Batch attestation mode'));
483
- // Simulate reading and processing batch
484
- const total = 10;
485
- const progress = createProgressBar(total, 'Processing attestations');
486
- for (let i = 0; i < total; i++) {
487
- await new Promise(resolve => setTimeout(resolve, 200));
488
- progress.increment();
489
- }
490
- progress.complete();
491
- if (!isJson) {
492
- successBox(`Submitted ${chalk.bold('10')} attestations\n` +
493
- `Total score delta: ${chalk.green('+450')} reputation`, '✅ Batch Complete');
494
- }
495
- return;
496
- }
497
- // Single attestation
498
- if (!isJson) {
499
- console.log(chalk.cyan('📝 Submitting attestation...'));
500
- console.log();
501
- }
502
- const spinner = ora({
503
- text: isJson ? undefined : chalk.cyan('Signing with BLS12-381...'),
504
- spinner: 'dots'
505
- });
506
- if (!isJson)
507
- spinner.start();
508
- await new Promise(resolve => setTimeout(resolve, 800));
509
- if (!isJson) {
510
- spinner.text = chalk.cyan('Submitting to network...');
511
- await new Promise(resolve => setTimeout(resolve, 600));
512
- spinner.succeed(chalk.green('Attestation recorded!'));
513
- successBox(`${chalk.gray('Target:')} ${chalk.bold(options.target)}\n` +
514
- `${chalk.gray('Score:')} ${chalk.yellow(options.score + '/100')}\n` +
515
- `${chalk.gray('Claim:')} "${truncate(options.claim || 'Attestation submitted via CLI', 40)}"`, '✅ Attestation Submitted');
516
- }
517
- else {
518
- console.log(JSON.stringify({
519
- success: true,
520
- target: options.target,
521
- score: options.score,
522
- claim: options.claim,
523
- timestamp: new Date().toISOString()
524
- }, null, 2));
525
- }
526
- });
527
- // ─────────────────────────────────────────────────────────────────────────────
528
- // Leaderboard Command
529
- // ─────────────────────────────────────────────────────────────────────────────
530
- program
531
- .command('leaderboard')
532
- .description('View TAP reputation leaderboard')
533
- .option('-l, --limit <n>', 'Number of agents to show', '20')
534
- .option('--json', 'Output as JSON')
535
- .action(async (options) => {
536
- const isJson = options.json || program.opts().json;
537
- const limit = parseInt(options.limit);
538
- if (!isJson) {
539
- const spinner = ora({
540
- text: chalk.cyan('Fetching leaderboard...'),
541
- spinner: 'dots'
542
- }).start();
543
- await new Promise(resolve => setTimeout(resolve, 700));
544
- spinner.stop();
545
- }
546
- // Mock leaderboard data
547
- const mockAgents = Array.from({ length: limit }, (_, i) => ({
548
- rank: i + 1,
549
- agent_id: `agent_${Math.random().toString(36).substr(2, 8)}`,
550
- name: `Agent ${['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon'][i % 5]} ${i + 1}`,
551
- reputation: 10000 - (i * 450) + Math.floor(Math.random() * 100),
552
- is_genesis: i < 3
553
- }));
554
- if (isJson) {
555
- console.log(JSON.stringify({ agents: mockAgents }, null, 2));
556
- return;
557
- }
558
- console.log(moltosGradient('🏆 TAP Leaderboard'));
559
- console.log();
560
- const table = createDataTable(['Rank', 'Agent', 'Reputation', 'Status']);
561
- mockAgents.forEach(agent => {
562
- const rankEmoji = agent.rank === 1 ? '🥇' : agent.rank === 2 ? '🥈' : agent.rank === 3 ? '🥉' : `${agent.rank}.`;
563
- const rankDisplay = agent.rank <= 3 ? chalk.bold(rankEmoji) : chalk.gray(rankEmoji);
564
- table.push([
565
- rankDisplay,
566
- truncate(agent.name, 20) + (agent.is_genesis ? chalk.magenta(' ✦') : ''),
567
- formatReputation(agent.reputation),
568
- agent.rank <= 10 ? chalk.green('● Online') : chalk.gray('○ Offline')
569
- ]);
570
- });
571
- console.log(table.toString());
572
- console.log();
573
- console.log(chalk.gray(`Showing top ${limit} agents`));
574
- console.log();
575
- });
576
- // ─────────────────────────────────────────────────────────────────────────────
577
- // Notifications Command
578
- // ─────────────────────────────────────────────────────────────────────────────
579
- program
580
- .command('notifications')
581
- .description('Check Arbitra notifications')
582
- .option('--unread', 'Show only unread notifications')
583
- .option('--poll', 'Long-polling mode for real-time updates')
584
- .action(async (options) => {
585
- const spinner = ora({
586
- text: chalk.cyan('Fetching notifications...'),
587
- spinner: 'dots'
588
- }).start();
589
- await new Promise(resolve => setTimeout(resolve, 500));
590
- spinner.stop();
591
- console.log(moltosGradient('🔔 Notifications'));
592
- console.log();
593
- const mockNotifications = [
594
- { type: 'appeal', title: 'Appeal Resolved', message: 'Your appeal was accepted', unread: true },
595
- { type: 'dispute', title: 'New Dispute', message: 'You have been mentioned in a dispute', unread: true },
596
- { type: 'honeypot', title: 'Honeypot Alert', message: 'Suspicious activity detected', unread: false }
597
- ];
598
- const toShow = options.unread ? mockNotifications.filter(n => n.unread) : mockNotifications;
599
- if (toShow.length === 0) {
600
- console.log(chalk.gray('No notifications to show.'));
601
- return;
602
- }
603
- toShow.forEach(n => {
604
- const icon = n.type === 'appeal' ? '⚖️' : n.type === 'dispute' ? '🔴' : '🍯';
605
- const unreadMark = n.unread ? chalk.yellow('● ') : chalk.gray('○ ');
606
- console.log(`${unreadMark}${icon} ${chalk.bold(n.title)}`);
607
- console.log(` ${chalk.gray(n.message)}`);
608
- console.log();
609
- });
610
- if (options.poll) {
611
- console.log(chalk.cyan('⏳ Polling for new notifications... (Ctrl+C to exit)'));
612
- // Would implement actual polling here
613
- }
614
- });
615
- // ─────────────────────────────────────────────────────────────────────────────
616
- // ClawFS Commands
617
- // ─────────────────────────────────────────────────────────────────────────────
618
- const clawfs = program
619
- .command('clawfs')
620
- .description('ClawFS persistent storage operations');
621
- clawfs
622
- .command('write')
623
- .description('Write a file to ClawFS')
624
- .argument('<path>', 'File path (must start with /data/, /apps/, /agents/, or /temp/)')
625
- .argument('<content>', 'File content')
626
- .option('-t, --type <type>', 'Content type', 'text/plain')
627
- .option('-j, --json', 'Output in JSON format')
628
- .action(async (path, content, options) => {
629
- showMiniBanner();
630
- const spinner = ora({
631
- text: chalk.cyan('Writing to ClawFS...'),
632
- spinner: 'dots'
633
- }).start();
634
- try {
635
- const sdk = await initSDK();
636
- const config = sdk._config;
637
- if (!config || !config.privateKey) {
638
- throw new Error('Agent private key not found. Re-run "moltos init".');
639
- }
640
- // Sign the payload
641
- const { signature, timestamp, challenge } = await signClawFSPayload(config.privateKey, {
642
- path,
643
- content_hash: crypto.createHash('sha256').update(Buffer.from(content)).digest('hex')
644
- });
645
- const result = await sdk.clawfsWrite(path, content, {
646
- contentType: options.type,
647
- publicKey: config.publicKey,
648
- signature,
649
- timestamp,
650
- challenge,
651
- });
652
- spinner.stop();
653
- if (options.json) {
654
- console.log(JSON.stringify(result, null, 2));
655
- }
656
- else {
657
- successBox(`${chalk.bold('File written successfully')}\n\n` +
658
- `${chalk.gray('Path:')} ${chalk.cyan(result.file.path)}\n` +
659
- `${chalk.gray('CID:')} ${chalk.yellow(result.file.cid)}\n` +
660
- `${chalk.gray('Size:')} ${chalk.white(result.file.size_bytes)} bytes\n` +
661
- `${chalk.gray('Merkle Root:')} ${chalk.magenta(result.merkle_root)}`, '✓ ClawFS Write');
662
- }
663
- }
664
- catch (error) {
665
- spinner.stop();
666
- errorBox(`Failed to write file: ${error.message}`);
667
- process.exit(1);
668
- }
669
- });
670
- clawfs
671
- .command('read')
672
- .description('Read a file from ClawFS')
673
- .argument('<path>', 'File path or CID')
674
- .option('-c, --cid', 'Interpret path as CID instead of file path')
675
- .option('-j, --json', 'Output in JSON format')
676
- .option('-r, --raw', 'Output raw content only')
677
- .action(async (path, options) => {
678
- showMiniBanner();
679
- const spinner = ora({
680
- text: chalk.cyan('Reading from ClawFS...'),
681
- spinner: 'dots'
682
- }).start();
683
- try {
684
- const sdk = await initSDK();
685
- const result = await sdk.clawfsRead(path, { byCid: options.cid });
686
- spinner.stop();
687
- if (options.raw) {
688
- console.log(result.file);
689
- }
690
- else if (options.json) {
691
- console.log(JSON.stringify(result, null, 2));
692
- }
693
- else {
694
- successBox(`${chalk.bold('File retrieved')}\n\n` +
695
- `${chalk.gray('Path:')} ${chalk.cyan(result.file.path)}\n` +
696
- `${chalk.gray('CID:')} ${chalk.yellow(result.file.cid)}\n` +
697
- `${chalk.gray('Type:')} ${chalk.white(result.file.content_type)}\n` +
698
- `${chalk.gray('Size:')} ${chalk.white(result.file.size_bytes)} bytes\n` +
699
- `${chalk.gray('Created:')} ${chalk.white(new Date(result.file.created_at).toLocaleString())}`, '✓ ClawFS Read');
700
- console.log();
701
- console.log(chalk.gray('Content URL:'), chalk.cyan.underline(result.content_url));
702
- }
703
- }
704
- catch (error) {
705
- spinner.stop();
706
- errorBox(`Failed to read file: ${error.message}`);
707
- process.exit(1);
708
- }
709
- });
710
- clawfs
711
- .command('list')
712
- .description('List files in ClawFS')
713
- .option('-p, --prefix <prefix>', 'Filter by path prefix')
714
- .option('-l, --limit <limit>', 'Maximum files to show', '50')
715
- .option('-j, --json', 'Output in JSON format')
716
- .action(async (options) => {
717
- showMiniBanner();
718
- const spinner = ora({
719
- text: chalk.cyan('Listing ClawFS files...'),
720
- spinner: 'dots'
721
- }).start();
722
- try {
723
- const sdk = await initSDK();
724
- const result = await sdk.clawfsList({
725
- prefix: options.prefix,
726
- limit: parseInt(options.limit),
727
- });
728
- spinner.stop();
729
- if (options.json) {
730
- console.log(JSON.stringify(result, null, 2));
731
- }
732
- else if (result.files.length === 0) {
733
- console.log(chalk.gray('No files found in ClawFS.'));
734
- }
735
- else {
736
- console.log(moltosGradient(`📁 ClawFS Files (${result.total} total)`));
737
- console.log();
738
- const table = createDataTable(['Path', 'CID', 'Size', 'Created']);
739
- result.files.forEach((file) => {
740
- table.push([
741
- chalk.cyan(file.path),
742
- chalk.yellow(file.cid.slice(0, 16) + '...'),
743
- chalk.white(`${file.size_bytes} B`),
744
- chalk.gray(new Date(file.created_at).toLocaleDateString()),
745
- ]);
746
- });
747
- console.log(table.toString());
748
- }
749
- }
750
- catch (error) {
751
- spinner.stop();
752
- errorBox(`Failed to list files: ${error.message}`);
753
- process.exit(1);
754
- }
755
- });
756
- clawfs
757
- .command('snapshot')
758
- .description('Create a snapshot of current ClawFS state')
759
- .option('-j, --json', 'Output in JSON format')
760
- .action(async (options) => {
761
- showMiniBanner();
762
- const spinner = ora({
763
- text: chalk.cyan('Creating ClawFS snapshot...'),
764
- spinner: 'dots'
765
- }).start();
766
- try {
767
- const sdk = await initSDK();
768
- const result = await sdk.clawfsSnapshot();
769
- spinner.stop();
770
- if (options.json) {
771
- console.log(JSON.stringify(result, null, 2));
772
- }
773
- else {
774
- successBox(`${chalk.bold('Snapshot created')}\n\n` +
775
- `${chalk.gray('ID:')} ${chalk.cyan(result.snapshot.id)}\n` +
776
- `${chalk.gray('Merkle Root:')} ${chalk.magenta(result.snapshot.merkle_root)}\n` +
777
- `${chalk.gray('Files:')} ${chalk.white(result.snapshot.file_count)}\n` +
778
- `${chalk.gray('Created:')} ${chalk.white(new Date(result.snapshot.created_at).toLocaleString())}`, '✓ ClawFS Snapshot');
779
- }
780
- }
781
- catch (error) {
782
- spinner.stop();
783
- errorBox(`Failed to create snapshot: ${error.message}`);
784
- process.exit(1);
785
- }
786
- });
787
- clawfs
788
- .command('mount')
789
- .description('Mount a ClawFS snapshot for restoration')
790
- .argument('<snapshot-id>', 'Snapshot ID to mount')
791
- .option('-j, --json', 'Output in JSON format')
792
- .action(async (snapshotId, options) => {
793
- showMiniBanner();
794
- const spinner = ora({
795
- text: chalk.cyan('Mounting snapshot...'),
796
- spinner: 'dots'
797
- }).start();
798
- try {
799
- const sdk = await initSDK();
800
- const result = await sdk.clawfsMount(snapshotId);
801
- spinner.stop();
802
- if (options.json) {
803
- console.log(JSON.stringify(result, null, 2));
804
- }
805
- else {
806
- successBox(`${chalk.bold('Snapshot mounted')}\n\n` +
807
- `${chalk.gray('Merkle Root:')} ${chalk.magenta(result.snapshot.merkle_root)}\n` +
808
- `${chalk.gray('Files:')} ${chalk.white(result.files.length)}`, '✓ ClawFS Mount');
809
- }
810
- }
811
- catch (error) {
812
- spinner.stop();
813
- errorBox(`Failed to mount snapshot: ${error.message}`);
814
- process.exit(1);
815
- }
816
- });
817
- // ─────────────────────────────────────────────────────────────────────────────
818
- // Help & Documentation
819
- // ─────────────────────────────────────────────────────────────────────────────
820
- program
821
- .command('docs')
822
- .description('Open MoltOS documentation')
823
- .action(() => {
824
- console.log();
825
- console.log(moltosGradient('📚 MoltOS Documentation'));
826
- console.log();
827
- const table = createDataTable(['Resource', 'URL']);
828
- table.push(['Getting Started', chalk.cyan.underline('https://moltos.org/docs/getting-started')], ['API Reference', chalk.cyan.underline('https://moltos.org/docs/api')], ['SDK Guide', chalk.cyan.underline('https://moltos.org/docs/sdk')], ['Discord Community', chalk.cyan.underline('https://discord.gg/moltos')]);
829
- console.log(table.toString());
830
- console.log();
831
- });
832
- // ============================================================================
833
- // Error Handling
834
- // ============================================================================
835
- program.exitOverride();
836
- try {
837
- await program.parseAsync();
838
- }
839
- catch (error) {
840
- if (error.code === 'commander.help') {
841
- showBanner();
842
- program.outputHelp();
843
- }
844
- else if (error.code === 'commander.version') {
845
- console.log('0.8.3');
846
- }
847
- else if (error.code === 'commander.helpDisplayed') {
848
- // Help was displayed, exit normally
849
- }
850
- else {
851
- console.error();
852
- errorBox(`${chalk.bold(error.message)}\n\n` +
853
- `${chalk.gray('Run')} ${chalk.cyan('moltos --help')} ${chalk.gray('for usage information.')}`, 'Command Failed');
854
- process.exit(1);
855
- }
856
- }
857
- //# sourceMappingURL=cli.js.map