clawcloud 1.1.0 → 1.3.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/src/api/vms.js ADDED
@@ -0,0 +1,71 @@
1
+ import { getConfig } from '../utils/config.js';
2
+
3
+ export async function getVMs(address) {
4
+ const config = getConfig();
5
+
6
+ try {
7
+ const response = await fetch(`${config.API_URL}/vms/${address}`);
8
+
9
+ // Handle rate limiting
10
+ if (response.status === 429) {
11
+ const data = await response.json();
12
+ throw new Error(`Rate limit exceeded. Try again in ${data.retryAfter || 60} seconds.`);
13
+ }
14
+
15
+ if (!response.ok) {
16
+ throw new Error(`API error: ${response.status}`);
17
+ }
18
+
19
+ const data = await response.json();
20
+
21
+ // Transform API response to our format
22
+ return data.vms.map(vm => ({
23
+ nft_id: vm.token_id,
24
+ tier: vm.tier,
25
+ status: vm.status,
26
+ ip_address: vm.ssh_host,
27
+ vm_type: vm.vm_type,
28
+ expires_at: vm.expires_at,
29
+ expires_in_days: calculateDaysRemaining(vm.expires_at)
30
+ }));
31
+
32
+ } catch (error) {
33
+ if (error.message.includes('fetch')) {
34
+ throw new Error('Cannot connect to ClawCloud API. Check your internet connection.');
35
+ }
36
+ throw error;
37
+ }
38
+ }
39
+
40
+ function calculateDaysRemaining(expiresAt) {
41
+ const now = new Date();
42
+ const expires = new Date(expiresAt);
43
+ const diff = expires - now;
44
+ return Math.max(0, Math.ceil(diff / (1000 * 60 * 60 * 24)));
45
+ }
46
+
47
+ export async function getTiers() {
48
+ const config = getConfig();
49
+
50
+ try {
51
+ const response = await fetch(`${config.API_URL}/tiers`);
52
+
53
+ if (response.status === 429) {
54
+ const data = await response.json();
55
+ throw new Error(`Rate limit exceeded. Try again in ${data.retryAfter || 60} seconds.`);
56
+ }
57
+
58
+ if (!response.ok) {
59
+ throw new Error(`API error: ${response.status}`);
60
+ }
61
+
62
+ const data = await response.json();
63
+ return data.tiers;
64
+
65
+ } catch (error) {
66
+ if (error.message.includes('fetch')) {
67
+ throw new Error('Cannot connect to ClawCloud API. Check your internet connection.');
68
+ }
69
+ throw error;
70
+ }
71
+ }
@@ -0,0 +1,21 @@
1
+ import { ethers } from 'ethers';
2
+ import { getConfig } from '../utils/config.js';
3
+
4
+ export async function getBalance(address) {
5
+ const config = getConfig();
6
+
7
+ try {
8
+ const provider = new ethers.JsonRpcProvider(config.RPC_URL);
9
+ const usdcContract = new ethers.Contract(
10
+ config.USDC_ADDRESS,
11
+ ['function balanceOf(address) view returns (uint256)'],
12
+ provider
13
+ );
14
+
15
+ const balance = await usdcContract.balanceOf(address);
16
+ return ethers.formatUnits(balance, 6); // USDC has 6 decimals
17
+ } catch (error) {
18
+ // Fallback for development
19
+ return '0.00';
20
+ }
21
+ }
@@ -0,0 +1,27 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { getWalletConfig } from '../utils/config.js';
4
+ import { getBalance } from '../api/wallet.js';
5
+
6
+ export async function balance(options = {}) {
7
+ const wallet = getWalletConfig();
8
+
9
+ if (!wallet) {
10
+ console.log(chalk.red('\n❌ No wallet configured!\n'));
11
+ return;
12
+ }
13
+
14
+ const spinner = ora('Checking balance...').start();
15
+
16
+ try {
17
+ const bal = await getBalance(wallet.address);
18
+ spinner.succeed(`Balance: ${chalk.yellow(bal)} USDC`);
19
+
20
+ if (options.json) {
21
+ console.log(JSON.stringify({ balance: bal, currency: 'USDC' }));
22
+ }
23
+ } catch (error) {
24
+ spinner.fail('Failed to check balance');
25
+ console.error(chalk.red(`Error: ${error.message}`));
26
+ }
27
+ }
@@ -0,0 +1,8 @@
1
+ import chalk from 'chalk';
2
+ export async function buy(options = {}) {
3
+ console.log(chalk.yellow('\n⚠️ Manual Purchase Mode\n'));
4
+ console.log(chalk.white('Note: ClawCloud is designed for AUTONOMOUS agents.'));
5
+ console.log(chalk.white(' Your agent can purchase VMs on its own!\n'));
6
+ console.log(chalk.cyan('Run: npx clawcloud export\n'));
7
+ console.log(chalk.gray('Manual purchase coming soon...\n'));
8
+ }
@@ -0,0 +1,86 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import boxen from 'boxen';
5
+ import { ethers } from 'ethers';
6
+ import { saveWallet, getAgent } from '../utils/config.js';
7
+ import { getBalance } from '../api/wallet.js';
8
+
9
+ export async function configure() {
10
+ console.log(chalk.cyan.bold('\n🔐 Configure Agent Wallet\n'));
11
+
12
+ const answers = await inquirer.prompt([
13
+ {
14
+ type: 'input',
15
+ name: 'agentId',
16
+ message: 'Agent ID:',
17
+ validate: (input) => input.startsWith('agent_') || 'Invalid agent ID format'
18
+ },
19
+ {
20
+ type: 'password',
21
+ name: 'privateKey',
22
+ message: 'Private Key (from Telegram bot):',
23
+ validate: (input) => {
24
+ try {
25
+ new ethers.Wallet(input);
26
+ return true;
27
+ } catch {
28
+ return 'Invalid private key';
29
+ }
30
+ }
31
+ }
32
+ ]);
33
+
34
+ const spinner = ora('Verifying...').start();
35
+
36
+ try {
37
+ // Create wallet from private key
38
+ const wallet = new ethers.Wallet(answers.privateKey);
39
+ const address = wallet.address;
40
+
41
+ // Get agent info
42
+ const agent = getAgent(answers.agentId);
43
+
44
+ if (!agent) {
45
+ spinner.fail('Agent not found');
46
+ console.log(chalk.yellow(`\n⚠️ Agent ID not found locally. Did you run 'npx clawcloud register'?\n`));
47
+ return;
48
+ }
49
+
50
+ // Save wallet configuration
51
+ saveWallet(answers.agentId, answers.privateKey, address);
52
+
53
+ // Check balance
54
+ const balance = await getBalance(address);
55
+
56
+ spinner.succeed('Agent Configured!');
57
+
58
+ console.log(boxen(
59
+ chalk.bold.white(`Agent: ${chalk.cyan(agent.name)}\n`) +
60
+ chalk.white(`Wallet: ${chalk.green(address)}\n`) +
61
+ chalk.white(`Balance: ${chalk.yellow(balance)} USDC\n`) +
62
+ chalk.white(`Network: ${chalk.blue('Base')}\n\n`) +
63
+ chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n') +
64
+ (balance === '0.00' ?
65
+ chalk.white.bold('💰 Fund your agent:\n') +
66
+ chalk.blue(` https://clawcloud.co/fund/${answers.agentId}\n\n`) :
67
+ chalk.green.bold('✅ Agent is funded and ready!\n\n')
68
+ ) +
69
+ chalk.white('Next steps:\n') +
70
+ chalk.cyan(' npx clawcloud balance') + chalk.gray(' - Check USDC balance\n') +
71
+ chalk.cyan(' npx clawcloud export') + chalk.gray(' - Enable autonomous mode'),
72
+ {
73
+ padding: 1,
74
+ margin: 1,
75
+ borderStyle: 'round',
76
+ borderColor: 'green'
77
+ }
78
+ ));
79
+
80
+ console.log(chalk.gray(`\n🔒 Wallet saved securely to: ~/.clawcloud/wallet.json\n`));
81
+
82
+ } catch (error) {
83
+ spinner.fail('Configuration failed');
84
+ console.error(chalk.red(`\n❌ Error: ${error.message}\n`));
85
+ }
86
+ }
@@ -0,0 +1,203 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import boxen from 'boxen';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import os from 'os';
8
+ import { getWalletConfig } from '../utils/config.js';
9
+
10
+ export async function exportConfig(options = {}) {
11
+ console.log(chalk.cyan.bold('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
12
+ console.log(chalk.cyan.bold(' AUTONOMOUS AGENT CONFIGURATION'));
13
+ console.log(chalk.cyan.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
14
+
15
+ console.log(chalk.white('This enables your AI agent to:\n'));
16
+ console.log(chalk.green(' ✓ Purchase VMs autonomously'));
17
+ console.log(chalk.green(' ✓ Check balance and make decisions'));
18
+ console.log(chalk.green(' ✓ Deploy code automatically'));
19
+ console.log(chalk.green(' ✓ Scale compute based on needs\n'));
20
+
21
+ // Get wallet config
22
+ const walletConfig = getWalletConfig();
23
+
24
+ if (!walletConfig) {
25
+ console.log(chalk.red('❌ No wallet configured!\n'));
26
+ console.log(chalk.yellow('Run: npx clawcloud configure\n'));
27
+ return;
28
+ }
29
+
30
+ let framework = options.framework;
31
+
32
+ if (!framework) {
33
+ const { selectedFramework } = await inquirer.prompt([
34
+ {
35
+ type: 'list',
36
+ name: 'selectedFramework',
37
+ message: 'Agent framework:',
38
+ choices: [
39
+ { name: '🦞 OpenClaw / Clawd / Molt (Skill)', value: 'openclaw' },
40
+ { name: '📦 Node.js SDK', value: 'nodejs' },
41
+ { name: '🐍 Python SDK', value: 'python' },
42
+ { name: '🔧 Environment Variables (Generic)', value: 'env' }
43
+ ]
44
+ }
45
+ ]);
46
+ framework = selectedFramework;
47
+ }
48
+
49
+ const spinner = ora('Exporting configuration...').start();
50
+
51
+ try {
52
+ switch (framework) {
53
+ case 'openclaw':
54
+ await exportOpenClaw(walletConfig, spinner);
55
+ break;
56
+ case 'nodejs':
57
+ await exportNodeJS(walletConfig, spinner);
58
+ break;
59
+ case 'python':
60
+ await exportPython(walletConfig, spinner);
61
+ break;
62
+ case 'env':
63
+ await exportEnv(walletConfig, spinner);
64
+ break;
65
+ }
66
+ } catch (error) {
67
+ spinner.fail('Export failed');
68
+ console.error(chalk.red(`\n❌ Error: ${error.message}\n`));
69
+ }
70
+ }
71
+
72
+ async function exportOpenClaw(config, spinner) {
73
+ const { workspace } = await inquirer.prompt([
74
+ {
75
+ type: 'input',
76
+ name: 'workspace',
77
+ message: 'OpenClaw workspace path:',
78
+ default: path.join(os.homedir(), '.openclaw', 'workspace')
79
+ }
80
+ ]);
81
+
82
+ const skillPath = path.join(workspace, 'skills', 'clawcloud');
83
+
84
+ // Create skill directory
85
+ if (!fs.existsSync(skillPath)) {
86
+ fs.mkdirSync(skillPath, { recursive: true });
87
+ }
88
+
89
+ // Copy SKILL.md template
90
+ const templatePath = path.join(__dirname, '..', '..', 'templates', 'SKILL.md');
91
+ const skillContent = fs.readFileSync(templatePath, 'utf-8')
92
+ .replace('{{WALLET_ADDRESS}}', config.address)
93
+ .replace('{{PRIVATE_KEY}}', config.privateKey)
94
+ .replace('{{AGENT_ID}}', config.agentId);
95
+
96
+ fs.writeFileSync(path.join(skillPath, 'SKILL.md'), skillContent);
97
+
98
+ spinner.succeed('Skill Installed!');
99
+
100
+ console.log(boxen(
101
+ chalk.bold.white(`Location: ${chalk.cyan(skillPath)}\n`) +
102
+ chalk.white(`Version: ${chalk.yellow('1.0.0')}\n\n`) +
103
+ chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n') +
104
+ chalk.bold.white(' YOUR AGENT CAN NOW:\n') +
105
+ chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n') +
106
+ chalk.white('🗣️ Talk to your agent:\n\n') +
107
+ chalk.cyan(' "How much USDC do I have?"\n') +
108
+ chalk.cyan(' "Buy me a SMALL VM for 1 month"\n') +
109
+ chalk.cyan(' "Check my VMs"\n') +
110
+ chalk.cyan(' "I need more compute for backtesting"\n\n') +
111
+ chalk.white('🤖 The agent will:\n') +
112
+ chalk.gray(' • Decide when it needs compute\n') +
113
+ chalk.gray(' • Purchase VMs automatically\n') +
114
+ chalk.gray(' • Deploy code on its own\n') +
115
+ chalk.gray(' • Manage infrastructure 24/7\n\n') +
116
+ chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n') +
117
+ chalk.white(`View skill: ${chalk.gray(`cat ${skillPath}/SKILL.md`)}\n`) +
118
+ chalk.white(`Test agent: ${chalk.gray('molt chat')}`),
119
+ {
120
+ padding: 1,
121
+ margin: 1,
122
+ borderStyle: 'round',
123
+ borderColor: 'green'
124
+ }
125
+ ));
126
+ }
127
+
128
+ async function exportNodeJS(config, spinner) {
129
+ const configPath = path.join(os.homedir(), '.clawcloud', 'autonomous-config.json');
130
+
131
+ fs.writeFileSync(configPath, JSON.stringify({
132
+ agentId: config.agentId,
133
+ walletAddress: config.address,
134
+ privateKey: config.privateKey,
135
+ network: 'base',
136
+ apiUrl: 'https://api.clawcloud.co/v1'
137
+ }, null, 2));
138
+
139
+ spinner.succeed('Configuration Exported!');
140
+
141
+ console.log(boxen(
142
+ chalk.bold.white(`File: ${chalk.cyan(configPath)}\n\n`) +
143
+ chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n') +
144
+ chalk.bold.white(' AUTONOMOUS AGENT CODE\n') +
145
+ chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n') +
146
+ chalk.gray('import') + chalk.white(' ClawCloud ') + chalk.gray('from') + chalk.green(' \'@clawcloud/sdk\'' + chalk.white(';\n\n')) +
147
+ chalk.gray('const') + chalk.white(' cloud = ') + chalk.gray('new') + chalk.white(' ClawCloud({\n') +
148
+ chalk.white(' configPath: ') + chalk.green(`'${configPath}'`) + chalk.white('\n});\n\n') +
149
+ chalk.gray('// Agent checks if it needs compute\n') +
150
+ chalk.gray('async function') + chalk.white(' run() {\n') +
151
+ chalk.white(' ') + chalk.gray('const') + chalk.white(' balance = ') + chalk.gray('await') + chalk.white(' cloud.wallet.balance();\n') +
152
+ chalk.white(' console.log(') + chalk.green('`I have ${balance} USDC`') + chalk.white(');\n\n') +
153
+ chalk.white(' ') + chalk.gray('if') + chalk.white(' (needsCompute) {\n') +
154
+ chalk.white(' ') + chalk.gray('const') + chalk.white(' vm = ') + chalk.gray('await') + chalk.white(' cloud.vms.purchase({\n') +
155
+ chalk.white(' tier: ') + chalk.green('\'SMALL\'') + chalk.white(',\n') +
156
+ chalk.white(' months: ') + chalk.yellow('1') + chalk.white('\n') +
157
+ chalk.white(' });\n\n') +
158
+ chalk.white(' ') + chalk.gray('await') + chalk.white(' vm.waitReady();\n') +
159
+ chalk.white(' ') + chalk.gray('await') + chalk.white(' vm.deployCode(') + chalk.green('\'./strategy\'') + chalk.white(');\n') +
160
+ chalk.white(' }\n}\n\n') +
161
+ chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n') +
162
+ chalk.white('Install SDK: ') + chalk.cyan('npm install @clawcloud/sdk\n') +
163
+ chalk.white('Run agent: ') + chalk.cyan('node agent.js'),
164
+ {
165
+ padding: 1,
166
+ margin: 1,
167
+ borderStyle: 'round',
168
+ borderColor: 'blue'
169
+ }
170
+ ));
171
+
172
+ console.log(chalk.gray(`\n⚠️ Keep this file secure! Contains private key.\n`));
173
+ }
174
+
175
+ async function exportPython(config, spinner) {
176
+ spinner.info('Python SDK coming soon!');
177
+ console.log(chalk.yellow('\n🐍 Python SDK is under development.\n'));
178
+ console.log(chalk.white('For now, use:\n'));
179
+ console.log(chalk.cyan(' • Node.js SDK'));
180
+ console.log(chalk.cyan(' • Environment variables'));
181
+ console.log(chalk.gray('\nStar the repo to get notified: github.com/clawcloud/clawcloud\n'));
182
+ }
183
+
184
+ async function exportEnv(config, spinner) {
185
+ spinner.succeed('Environment variables exported!');
186
+
187
+ console.log(boxen(
188
+ chalk.white('Add these to your ') + chalk.cyan('.env') + chalk.white(' file:\n\n') +
189
+ chalk.gray('CLAWCLOUD_AGENT_ID') + chalk.white('=') + chalk.green(config.agentId) + chalk.white('\n') +
190
+ chalk.gray('CLAWCLOUD_WALLET_ADDRESS') + chalk.white('=') + chalk.green(config.address) + chalk.white('\n') +
191
+ chalk.gray('CLAWCLOUD_PRIVATE_KEY') + chalk.white('=') + chalk.green(config.privateKey) + chalk.white('\n') +
192
+ chalk.gray('CLAWCLOUD_NETWORK') + chalk.white('=') + chalk.green('base') + chalk.white('\n') +
193
+ chalk.gray('CLAWCLOUD_API_URL') + chalk.white('=') + chalk.green('https://api.clawcloud.co/v1'),
194
+ {
195
+ padding: 1,
196
+ margin: 1,
197
+ borderStyle: 'round',
198
+ borderColor: 'yellow'
199
+ }
200
+ ));
201
+
202
+ console.log(chalk.gray(`\n⚠️ Never commit .env files to git!\n`));
203
+ }
@@ -0,0 +1,13 @@
1
+ import chalk from 'chalk';
2
+ import { getWalletConfig } from '../utils/config.js';
3
+ export async function fund() {
4
+ const wallet = getWalletConfig();
5
+ if (!wallet) {
6
+ console.log(chalk.red('\n❌ No wallet configured!\n'));
7
+ return;
8
+ }
9
+ console.log(chalk.cyan.bold('\n💰 Fund Your Agent\n'));
10
+ console.log(chalk.white(`Wallet: ${chalk.green(wallet.address)}\n`));
11
+ console.log(chalk.white('Send USDC (Base network) to the address above.\n'));
12
+ console.log(chalk.blue(`Or visit: https://clawcloud.co/fund/${wallet.agentId}\n`));
13
+ }
@@ -0,0 +1,70 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import { register } from './register.js';
4
+ import { configure } from './configure.js';
5
+ import { exportConfig } from './export.js';
6
+ import { buy } from './buy.js';
7
+ import { balance } from './balance.js';
8
+ import { list } from './list.js';
9
+ import { fund } from './fund.js';
10
+ import { displayHelp } from '../utils/help.js';
11
+ import { displayBanner } from '../utils/banner.js';
12
+
13
+ export async function interactive() {
14
+ displayBanner();
15
+
16
+ const { action } = await inquirer.prompt([
17
+ {
18
+ type: 'list',
19
+ name: 'action',
20
+ message: 'What would you like to do?',
21
+ choices: [
22
+ { name: '🤖 Register New Agent', value: 'register' },
23
+ { name: '🔐 Configure Agent (after Telegram)', value: 'configure' },
24
+ { name: '🚀 Export for Autonomous Use', value: 'export' },
25
+ new inquirer.Separator(),
26
+ { name: '💰 Check Balance', value: 'balance' },
27
+ { name: '💸 Fund Agent', value: 'fund' },
28
+ { name: '🛒 Purchase VM (Manual)', value: 'buy' },
29
+ { name: '📋 List VMs & NFTs', value: 'list' },
30
+ new inquirer.Separator(),
31
+ { name: '📖 View Documentation', value: 'docs' },
32
+ { name: '❓ Help', value: 'help' },
33
+ { name: '🚪 Exit', value: 'exit' }
34
+ ]
35
+ }
36
+ ]);
37
+
38
+ switch (action) {
39
+ case 'register':
40
+ await register();
41
+ break;
42
+ case 'configure':
43
+ await configure();
44
+ break;
45
+ case 'export':
46
+ await exportConfig();
47
+ break;
48
+ case 'balance':
49
+ await balance();
50
+ break;
51
+ case 'fund':
52
+ await fund();
53
+ break;
54
+ case 'buy':
55
+ await buy();
56
+ break;
57
+ case 'list':
58
+ await list();
59
+ break;
60
+ case 'docs':
61
+ console.log(chalk.blue('\n📖 Documentation: https://docs.clawcloud.co\n'));
62
+ break;
63
+ case 'help':
64
+ displayHelp();
65
+ break;
66
+ case 'exit':
67
+ console.log(chalk.gray('\n👋 Goodbye!\n'));
68
+ process.exit(0);
69
+ }
70
+ }
@@ -0,0 +1,90 @@
1
+ import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
+ import ora from 'ora';
4
+ import { getWalletConfig, getTierInfo } from '../utils/config.js';
5
+ import { getVMs } from '../api/vms.js';
6
+
7
+ export async function list(options = {}) {
8
+ const wallet = getWalletConfig();
9
+
10
+ if (!wallet) {
11
+ console.log(chalk.red('\n❌ No wallet configured!\n'));
12
+ console.log(chalk.yellow('Run: npx clawcloud configure\n'));
13
+ return;
14
+ }
15
+
16
+ const spinner = ora('Fetching your VMs and NFTs...').start();
17
+
18
+ try {
19
+ const vms = await getVMs(wallet.address);
20
+ const tierInfo = getTierInfo();
21
+
22
+ spinner.stop();
23
+
24
+ if (options.json) {
25
+ console.log(JSON.stringify(vms, null, 2));
26
+ return;
27
+ }
28
+
29
+ console.log(chalk.cyan.bold('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
30
+ console.log(chalk.cyan.bold(' YOUR AGENT\'S INFRASTRUCTURE'));
31
+ console.log(chalk.cyan.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
32
+
33
+ console.log(chalk.white(`🤖 Agent: ${chalk.cyan(wallet.agentId)}`));
34
+ console.log(chalk.white(`💼 Wallet: ${chalk.gray(wallet.address.substring(0, 10))}...${chalk.gray(wallet.address.substring(38))}`));
35
+ console.log(chalk.white(`🎨 NFTs Owned: ${chalk.green(vms.length)}\n`));
36
+
37
+ if (vms.length === 0) {
38
+ console.log(chalk.yellow('No VMs yet. Purchase one:\n'));
39
+ console.log(chalk.cyan(' npx clawcloud buy\n'));
40
+ return;
41
+ }
42
+
43
+ const table = new Table({
44
+ head: [
45
+ chalk.white('NFT'),
46
+ chalk.white('Name'),
47
+ chalk.white('Status'),
48
+ chalk.white('SSH Host'),
49
+ chalk.white('Expires')
50
+ ],
51
+ style: {
52
+ head: [],
53
+ border: ['gray']
54
+ }
55
+ });
56
+
57
+ vms.forEach(vm => {
58
+ const statusColor = vm.status === 'running' ? chalk.green :
59
+ vm.status === 'provisioning' ? chalk.yellow :
60
+ chalk.red;
61
+
62
+ const tier = tierInfo[vm.tier];
63
+ const tierName = tier ? tier.name : `Tier ${vm.tier}`;
64
+
65
+ const expiresIn = vm.expires_in_days > 0 ?
66
+ `${vm.expires_in_days} days` :
67
+ chalk.red('Expired');
68
+
69
+ table.push([
70
+ chalk.cyan(`#${vm.nft_id}`),
71
+ tierName,
72
+ statusColor(vm.status),
73
+ vm.ip_address || chalk.gray('provisioning...'),
74
+ expiresIn
75
+ ]);
76
+ });
77
+
78
+ console.log(table.toString());
79
+
80
+ console.log(chalk.gray('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
81
+ console.log(chalk.white('Commands:'));
82
+ console.log(chalk.cyan(' npx clawcloud status <nft-id>') + chalk.gray(' - View details'));
83
+ console.log(chalk.cyan(' npx clawcloud ssh <nft-id>') + chalk.gray(' - Connect'));
84
+ console.log(chalk.gray('\n'));
85
+
86
+ } catch (error) {
87
+ spinner.fail('Failed to fetch VMs');
88
+ console.error(chalk.red(`\n❌ Error: ${error.message}\n`));
89
+ }
90
+ }
@@ -0,0 +1,5 @@
1
+ import chalk from 'chalk';
2
+ export async function nfts() {
3
+ console.log(chalk.cyan('\n🎨 Your NFT Collection\n'));
4
+ console.log(chalk.yellow('Coming soon!\n'));
5
+ }
@@ -0,0 +1,72 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import boxen from 'boxen';
5
+ import { saveAgent } from '../utils/config.js';
6
+ import { registerAgent } from '../api/agents.js';
7
+
8
+ export async function register() {
9
+ console.log(chalk.cyan.bold('\n🤖 Register AI Agent\n'));
10
+
11
+ const answers = await inquirer.prompt([
12
+ {
13
+ type: 'input',
14
+ name: 'name',
15
+ message: 'Agent name:',
16
+ validate: (input) => input.length > 0 || 'Name is required'
17
+ },
18
+ {
19
+ type: 'input',
20
+ name: 'description',
21
+ message: 'Description:',
22
+ validate: (input) => input.length > 0 || 'Description is required'
23
+ }
24
+ ]);
25
+
26
+ const spinner = ora('Registering agent...').start();
27
+
28
+ try {
29
+ // Call API to register agent
30
+ const result = await registerAgent(answers.name, answers.description);
31
+
32
+ spinner.succeed('Agent Registered!');
33
+
34
+ // Save agent ID locally
35
+ saveAgent({
36
+ agentId: result.agent_id,
37
+ name: answers.name,
38
+ description: answers.description,
39
+ createdAt: result.created_at
40
+ });
41
+
42
+ // Display next steps
43
+ console.log(boxen(
44
+ chalk.bold.white(`Agent ID: ${chalk.cyan(result.agent_id)}\n\n`) +
45
+ chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n') +
46
+ chalk.white.bold('📱 NEXT STEP: Get Your Wallet\n\n') +
47
+ chalk.white(`1. Open Telegram: ${chalk.blue('https://t.me/clawcloud_devbot')}\n\n`) +
48
+ chalk.white(`2. Send this command:\n ${chalk.green(`/start ${result.agent_id}`)}\n\n`) +
49
+ chalk.white('3. Bot will verify your agent and create a wallet\n\n') +
50
+ chalk.white('4. You\'ll receive:\n') +
51
+ chalk.gray(' • Wallet address\n') +
52
+ chalk.gray(' • Private key (keep secure!)\n') +
53
+ chalk.gray(' • Funding instructions\n\n') +
54
+ chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n') +
55
+ chalk.white('After Telegram setup, run:\n') +
56
+ chalk.cyan(' npx clawcloud configure') + chalk.gray(' - Link wallet to CLI\n') +
57
+ chalk.cyan(' npx clawcloud export') + chalk.gray(' - Make agent autonomous'),
58
+ {
59
+ padding: 1,
60
+ margin: 1,
61
+ borderStyle: 'round',
62
+ borderColor: 'cyan'
63
+ }
64
+ ));
65
+
66
+ console.log(chalk.gray(`\nAgent ID saved to: ~/.clawcloud/agents.json\n`));
67
+
68
+ } catch (error) {
69
+ spinner.fail('Registration failed');
70
+ console.error(chalk.red(`\n❌ Error: ${error.message}\n`));
71
+ }
72
+ }
@@ -0,0 +1,5 @@
1
+ import chalk from 'chalk';
2
+ export async function ssh(nftId) {
3
+ console.log(chalk.gray(`\nConnecting to VM (NFT #${nftId})...\n`));
4
+ console.log(chalk.yellow('Coming soon!\n'));
5
+ }