clawcloud 1.0.0 ā 1.0.1
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/CLI_PUBLISHING_GUIDE.md +325 -0
- package/README.md +253 -118
- package/index.js +559 -0
- package/package.json +18 -35
- package/bin/clawcloud.js +0 -158
- package/src/api/agents.js +0 -22
- package/src/api/vms.js +0 -23
- package/src/api/wallet.js +0 -21
- package/src/commands/balance.js +0 -27
- package/src/commands/buy.js +0 -8
- package/src/commands/configure.js +0 -86
- package/src/commands/export.js +0 -203
- package/src/commands/fund.js +0 -13
- package/src/commands/interactive.js +0 -70
- package/src/commands/list.js +0 -87
- package/src/commands/nfts.js +0 -5
- package/src/commands/register.js +0 -72
- package/src/commands/ssh.js +0 -5
- package/src/commands/status.js +0 -5
- package/src/commands/terminate.js +0 -5
- package/src/commands/transfer.js +0 -5
- package/src/utils/banner.js +0 -9
- package/src/utils/config.js +0 -75
- package/src/utils/help.js +0 -85
- package/src/utils/version.js +0 -15
- package/templates/SKILL.md +0 -215
package/index.js
ADDED
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ClawCloud CLI - Decentralized Cloud Infrastructure for AI Agents
|
|
5
|
+
* Purchase and manage VMs with crypto on Base blockchain
|
|
6
|
+
*
|
|
7
|
+
* Usage: npx clawcloud [command] [options]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { ethers } = require('ethers');
|
|
11
|
+
const readline = require('readline');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// PRODUCTION CONFIGURATION
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
const CONFIG = {
|
|
21
|
+
// Network
|
|
22
|
+
NETWORK: 'base',
|
|
23
|
+
RPC_URL: 'https://mainnet.base.org',
|
|
24
|
+
CHAIN_ID: 8453,
|
|
25
|
+
|
|
26
|
+
// Contracts
|
|
27
|
+
CONTRACT_ADDRESS: '0xF708741D37C420518852c6A15aB658066951c852',
|
|
28
|
+
USDC_ADDRESS: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
29
|
+
|
|
30
|
+
// API
|
|
31
|
+
API_URL: 'https://api.clawcloud.co',
|
|
32
|
+
|
|
33
|
+
// Explorer
|
|
34
|
+
EXPLORER_URL: 'https://basescan.org',
|
|
35
|
+
|
|
36
|
+
// Config file
|
|
37
|
+
CONFIG_FILE: path.join(os.homedir(), '.clawcloud', 'config.json')
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// CONTRACT ABIs
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
const CONTRACT_ABI = [
|
|
45
|
+
"function purchaseVM(uint8 tier, uint16 durationDays) returns (uint256)",
|
|
46
|
+
"function getTierConfig(uint8 tier) view returns (uint256, bool, string)",
|
|
47
|
+
"function isTokenValid(uint256 tokenId) view returns (bool)",
|
|
48
|
+
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
49
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
50
|
+
"function tokenOfOwnerByIndex(address owner, uint256 index) view returns (uint256)"
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const USDC_ABI = [
|
|
54
|
+
"function approve(address spender, uint256 amount) returns (bool)",
|
|
55
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
56
|
+
"function balanceOf(address account) view returns (uint256)"
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// UTILITIES
|
|
61
|
+
// ============================================================================
|
|
62
|
+
|
|
63
|
+
const rl = readline.createInterface({
|
|
64
|
+
input: process.stdin,
|
|
65
|
+
output: process.stdout
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function question(query) {
|
|
69
|
+
return new Promise(resolve => rl.question(query, resolve));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function log(message, type = 'info') {
|
|
73
|
+
const symbols = {
|
|
74
|
+
info: 'š¦',
|
|
75
|
+
success: 'ā
',
|
|
76
|
+
error: 'ā',
|
|
77
|
+
warning: 'ā ļø',
|
|
78
|
+
loading: 'ā³'
|
|
79
|
+
};
|
|
80
|
+
console.log(`${symbols[type]} ${message}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function formatUSDC(amount) {
|
|
84
|
+
return ethers.formatUnits(amount, 6);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function parseUSDC(amount) {
|
|
88
|
+
return ethers.parseUnits(amount.toString(), 6);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// CONFIGURATION MANAGEMENT
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
function loadConfig() {
|
|
96
|
+
try {
|
|
97
|
+
if (fs.existsSync(CONFIG.CONFIG_FILE)) {
|
|
98
|
+
return JSON.parse(fs.readFileSync(CONFIG.CONFIG_FILE, 'utf8'));
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
// Config doesn't exist or is invalid
|
|
102
|
+
}
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function saveConfig(config) {
|
|
107
|
+
const dir = path.dirname(CONFIG.CONFIG_FILE);
|
|
108
|
+
if (!fs.existsSync(dir)) {
|
|
109
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
110
|
+
}
|
|
111
|
+
fs.writeFileSync(CONFIG.CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// WALLET SETUP
|
|
116
|
+
// ============================================================================
|
|
117
|
+
|
|
118
|
+
async function setupWallet() {
|
|
119
|
+
const config = loadConfig();
|
|
120
|
+
|
|
121
|
+
if (config.privateKey) {
|
|
122
|
+
const provider = new ethers.JsonRpcProvider(CONFIG.RPC_URL);
|
|
123
|
+
return new ethers.Wallet(config.privateKey, provider);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
log('No wallet found. Let\'s set one up!', 'info');
|
|
127
|
+
console.log('\nOptions:');
|
|
128
|
+
console.log('1. Import existing private key');
|
|
129
|
+
console.log('2. Generate new wallet');
|
|
130
|
+
|
|
131
|
+
const choice = await question('\nChoose (1 or 2): ');
|
|
132
|
+
|
|
133
|
+
let wallet;
|
|
134
|
+
if (choice === '1') {
|
|
135
|
+
const privateKey = await question('Enter your private key: ');
|
|
136
|
+
const provider = new ethers.JsonRpcProvider(CONFIG.RPC_URL);
|
|
137
|
+
wallet = new ethers.Wallet(privateKey.trim(), provider);
|
|
138
|
+
} else {
|
|
139
|
+
const provider = new ethers.JsonRpcProvider(CONFIG.RPC_URL);
|
|
140
|
+
wallet = ethers.Wallet.createRandom(provider);
|
|
141
|
+
log(`New wallet created!`, 'success');
|
|
142
|
+
console.log(`Address: ${wallet.address}`);
|
|
143
|
+
console.log(`\nā ļø SAVE THIS PRIVATE KEY SECURELY:`);
|
|
144
|
+
console.log(`${wallet.privateKey}\n`);
|
|
145
|
+
await question('Press Enter after saving your private key...');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const save = await question('Save wallet to config? (y/n): ');
|
|
149
|
+
if (save.toLowerCase() === 'y') {
|
|
150
|
+
saveConfig({ privateKey: wallet.privateKey });
|
|
151
|
+
log('Wallet saved!', 'success');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return wallet;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// COMMANDS
|
|
159
|
+
// ============================================================================
|
|
160
|
+
|
|
161
|
+
async function cmdTiers() {
|
|
162
|
+
try {
|
|
163
|
+
log('Fetching available VM tiers...', 'loading');
|
|
164
|
+
|
|
165
|
+
const response = await fetch(`${CONFIG.API_URL}/tiers`);
|
|
166
|
+
const data = await response.json();
|
|
167
|
+
|
|
168
|
+
console.log('\nš Available VM Tiers:\n');
|
|
169
|
+
console.log('āāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāā¬āāāāāāāāā');
|
|
170
|
+
console.log('ā Tier ā Name ā Per Day ā Per Month ā Status ā');
|
|
171
|
+
console.log('āāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāā¼āāāāāāāāā¤');
|
|
172
|
+
|
|
173
|
+
for (const tier of data.tiers) {
|
|
174
|
+
const status = tier.active ? 'ā
' : 'ā';
|
|
175
|
+
console.log(
|
|
176
|
+
`ā ${String(tier.tier).padEnd(4)} ā ${tier.name.padEnd(27)} ā $${tier.pricePerDay.padEnd(11)} ā $${tier.pricePerMonth.padEnd(12)} ā ${status} ā`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log('āāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāā“āāāāāāāāā\n');
|
|
181
|
+
|
|
182
|
+
} catch (error) {
|
|
183
|
+
log(`Failed to fetch tiers: ${error.message}`, 'error');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function cmdPurchase() {
|
|
188
|
+
try {
|
|
189
|
+
const wallet = await setupWallet();
|
|
190
|
+
|
|
191
|
+
log(`Wallet: ${wallet.address}`, 'info');
|
|
192
|
+
|
|
193
|
+
// Get tiers first
|
|
194
|
+
const response = await fetch(`${CONFIG.API_URL}/tiers`);
|
|
195
|
+
const { tiers } = await response.json();
|
|
196
|
+
|
|
197
|
+
// Show tiers
|
|
198
|
+
console.log('\nš Available Tiers:\n');
|
|
199
|
+
tiers.forEach(tier => {
|
|
200
|
+
if (tier.active) {
|
|
201
|
+
console.log(`${tier.tier}. ${tier.name} - $${tier.pricePerDay}/day ($${tier.pricePerMonth}/month)`);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Get user input
|
|
206
|
+
const tierInput = await question('\nChoose tier (0-7): ');
|
|
207
|
+
const tier = parseInt(tierInput);
|
|
208
|
+
|
|
209
|
+
if (tier < 0 || tier > 7 || !tiers[tier].active) {
|
|
210
|
+
log('Invalid tier!', 'error');
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const daysInput = await question('Duration in days (3+ recommended): ');
|
|
215
|
+
const days = parseInt(daysInput);
|
|
216
|
+
|
|
217
|
+
if (days < 3) {
|
|
218
|
+
log('Minimum duration is 3 days!', 'error');
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Calculate cost
|
|
223
|
+
const pricePerDay = parseUSDC(tiers[tier].pricePerDay);
|
|
224
|
+
const totalCost = pricePerDay * BigInt(days);
|
|
225
|
+
|
|
226
|
+
console.log(`\nš° Total cost: $${formatUSDC(totalCost)} USDC`);
|
|
227
|
+
|
|
228
|
+
const confirm = await question('Proceed with purchase? (y/n): ');
|
|
229
|
+
if (confirm.toLowerCase() !== 'y') {
|
|
230
|
+
log('Purchase cancelled', 'info');
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check USDC balance
|
|
235
|
+
log('Checking USDC balance...', 'loading');
|
|
236
|
+
const usdc = new ethers.Contract(CONFIG.USDC_ADDRESS, USDC_ABI, wallet);
|
|
237
|
+
const balance = await usdc.balanceOf(wallet.address);
|
|
238
|
+
|
|
239
|
+
if (balance < totalCost) {
|
|
240
|
+
log(`Insufficient USDC! You have $${formatUSDC(balance)}, need $${formatUSDC(totalCost)}`, 'error');
|
|
241
|
+
console.log(`\nBuy USDC on Base: https://app.uniswap.org/swap?chain=base`);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
log(`USDC balance: $${formatUSDC(balance)} ā
`, 'success');
|
|
246
|
+
|
|
247
|
+
// Check approval
|
|
248
|
+
log('Checking USDC approval...', 'loading');
|
|
249
|
+
const contract = new ethers.Contract(CONFIG.CONTRACT_ADDRESS, CONTRACT_ABI, wallet);
|
|
250
|
+
const allowance = await usdc.allowance(wallet.address, CONFIG.CONTRACT_ADDRESS);
|
|
251
|
+
|
|
252
|
+
if (allowance < totalCost) {
|
|
253
|
+
log('Approving USDC...', 'loading');
|
|
254
|
+
const approveTx = await usdc.approve(CONFIG.CONTRACT_ADDRESS, totalCost);
|
|
255
|
+
log(`Approval tx: ${CONFIG.EXPLORER_URL}/tx/${approveTx.hash}`, 'info');
|
|
256
|
+
await approveTx.wait();
|
|
257
|
+
log('USDC approved!', 'success');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Purchase VM
|
|
261
|
+
log('Purchasing VM...', 'loading');
|
|
262
|
+
const purchaseTx = await contract.purchaseVM(tier, days);
|
|
263
|
+
log(`Purchase tx: ${CONFIG.EXPLORER_URL}/tx/${purchaseTx.hash}`, 'info');
|
|
264
|
+
|
|
265
|
+
const receipt = await purchaseTx.wait();
|
|
266
|
+
|
|
267
|
+
// Get token ID from event
|
|
268
|
+
const iface = new ethers.Interface(CONTRACT_ABI);
|
|
269
|
+
const event = receipt.logs
|
|
270
|
+
.map(log => {
|
|
271
|
+
try {
|
|
272
|
+
return iface.parseLog(log);
|
|
273
|
+
} catch {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
.find(e => e && e.name === 'VMPurchased');
|
|
278
|
+
|
|
279
|
+
const tokenId = event.args.tokenId;
|
|
280
|
+
|
|
281
|
+
log(`VM purchased successfully! š`, 'success');
|
|
282
|
+
console.log(`\nToken ID: ${tokenId}`);
|
|
283
|
+
console.log(`Tier: ${tier}`);
|
|
284
|
+
console.log(`Duration: ${days} days`);
|
|
285
|
+
console.log(`\nā³ VM is provisioning... This takes 2-3 minutes.`);
|
|
286
|
+
console.log(`\nGet credentials: npx clawcloud get ${tokenId}`);
|
|
287
|
+
|
|
288
|
+
} catch (error) {
|
|
289
|
+
log(`Purchase failed: ${error.message}`, 'error');
|
|
290
|
+
if (error.message.includes('insufficient funds')) {
|
|
291
|
+
log('You need ETH on Base for gas fees!', 'warning');
|
|
292
|
+
console.log(`Bridge ETH: https://bridge.base.org`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function cmdGet(tokenId) {
|
|
298
|
+
try {
|
|
299
|
+
if (!tokenId) {
|
|
300
|
+
log('Usage: npx clawcloud get <tokenId>', 'error');
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const wallet = await setupWallet();
|
|
305
|
+
|
|
306
|
+
log(`Fetching credentials for VM #${tokenId}...`, 'loading');
|
|
307
|
+
|
|
308
|
+
// Sign authentication message
|
|
309
|
+
const message = `ClawCloud Access Request: ${tokenId}`;
|
|
310
|
+
const signature = await wallet.signMessage(message);
|
|
311
|
+
|
|
312
|
+
// Call API
|
|
313
|
+
const response = await fetch(
|
|
314
|
+
`${CONFIG.API_URL}/credentials/${tokenId}`,
|
|
315
|
+
{
|
|
316
|
+
headers: {
|
|
317
|
+
'Authorization': `Bearer ${signature}`
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
const data = await response.json();
|
|
323
|
+
|
|
324
|
+
if (response.status === 202) {
|
|
325
|
+
log('VM is still provisioning...', 'warning');
|
|
326
|
+
console.log(`Status: ${data.status}`);
|
|
327
|
+
console.log(`Message: ${data.message}`);
|
|
328
|
+
console.log(`\nTry again in 2-3 minutes: npx clawcloud get ${tokenId}`);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (!response.ok) {
|
|
333
|
+
log(`Error: ${data.error || 'Failed to get credentials'}`, 'error');
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
log('VM credentials retrieved!', 'success');
|
|
338
|
+
console.log('\n' + '='.repeat(70));
|
|
339
|
+
console.log(`š„ļø VM #${data.tokenId} - ${data.type.toUpperCase()}`);
|
|
340
|
+
console.log('='.repeat(70));
|
|
341
|
+
console.log(`Tier: ${data.tier}`);
|
|
342
|
+
console.log(`Host: ${data.host}`);
|
|
343
|
+
console.log(`Username: ${data.username}`);
|
|
344
|
+
console.log(`Expires: ${new Date(data.expiresAt).toLocaleString()}`);
|
|
345
|
+
console.log('\nš SSH Command:');
|
|
346
|
+
console.log(` ${data.instructions}`);
|
|
347
|
+
|
|
348
|
+
if (data.privateKey) {
|
|
349
|
+
console.log('\nš Private Key:');
|
|
350
|
+
console.log(` Save to file: echo "${data.privateKey}" > vm_${tokenId}.pem`);
|
|
351
|
+
console.log(` Then: chmod 600 vm_${tokenId}.pem`);
|
|
352
|
+
console.log(` Connect: ssh -i vm_${tokenId}.pem ${data.username}@${data.host}`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
console.log('='.repeat(70) + '\n');
|
|
356
|
+
|
|
357
|
+
} catch (error) {
|
|
358
|
+
log(`Failed to get credentials: ${error.message}`, 'error');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async function cmdList() {
|
|
363
|
+
try {
|
|
364
|
+
const wallet = await setupWallet();
|
|
365
|
+
|
|
366
|
+
log(`Fetching VMs for ${wallet.address}...`, 'loading');
|
|
367
|
+
|
|
368
|
+
const response = await fetch(`${CONFIG.API_URL}/vms/${wallet.address}`);
|
|
369
|
+
const data = await response.json();
|
|
370
|
+
|
|
371
|
+
if (data.count === 0) {
|
|
372
|
+
log('No VMs found!', 'info');
|
|
373
|
+
console.log('\nPurchase a VM: npx clawcloud purchase');
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
log(`Found ${data.count} VM(s)`, 'success');
|
|
378
|
+
console.log('\n' + '='.repeat(90));
|
|
379
|
+
console.log('Your VMs:');
|
|
380
|
+
console.log('='.repeat(90));
|
|
381
|
+
|
|
382
|
+
for (const vm of data.vms) {
|
|
383
|
+
const status = vm.status === 'running' ? 'ā
' :
|
|
384
|
+
vm.status === 'provisioning' ? 'ā³' :
|
|
385
|
+
vm.status === 'expired' ? 'ā°' : 'ā';
|
|
386
|
+
|
|
387
|
+
console.log(`\n${status} VM #${vm.token_id}`);
|
|
388
|
+
console.log(` Tier: ${vm.tier}`);
|
|
389
|
+
console.log(` Type: ${vm.vm_type}`);
|
|
390
|
+
console.log(` Host: ${vm.ssh_host}`);
|
|
391
|
+
console.log(` Status: ${vm.status}`);
|
|
392
|
+
console.log(` Created: ${new Date(vm.created_at).toLocaleString()}`);
|
|
393
|
+
console.log(` Expires: ${new Date(vm.expires_at).toLocaleString()}`);
|
|
394
|
+
console.log(` Access: npx clawcloud get ${vm.token_id}`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
console.log('\n' + '='.repeat(90) + '\n');
|
|
398
|
+
|
|
399
|
+
} catch (error) {
|
|
400
|
+
log(`Failed to list VMs: ${error.message}`, 'error');
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
async function cmdBalance() {
|
|
405
|
+
try {
|
|
406
|
+
const wallet = await setupWallet();
|
|
407
|
+
|
|
408
|
+
log('Checking balances...', 'loading');
|
|
409
|
+
|
|
410
|
+
const provider = wallet.provider;
|
|
411
|
+
const usdc = new ethers.Contract(CONFIG.USDC_ADDRESS, USDC_ABI, provider);
|
|
412
|
+
|
|
413
|
+
const ethBalance = await provider.getBalance(wallet.address);
|
|
414
|
+
const usdcBalance = await usdc.balanceOf(wallet.address);
|
|
415
|
+
|
|
416
|
+
console.log('\nš° Wallet Balances:');
|
|
417
|
+
console.log(` Address: ${wallet.address}`);
|
|
418
|
+
console.log(` ETH: ${ethers.formatEther(ethBalance)} ETH (for gas)`);
|
|
419
|
+
console.log(` USDC: $${formatUSDC(usdcBalance)} USDC`);
|
|
420
|
+
|
|
421
|
+
if (ethBalance === 0n) {
|
|
422
|
+
log('You need ETH for gas fees!', 'warning');
|
|
423
|
+
console.log(` Bridge ETH: https://bridge.base.org`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (usdcBalance === 0n) {
|
|
427
|
+
log('You need USDC to purchase VMs!', 'warning');
|
|
428
|
+
console.log(` Buy USDC: https://app.uniswap.org/swap?chain=base`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
console.log('');
|
|
432
|
+
|
|
433
|
+
} catch (error) {
|
|
434
|
+
log(`Failed to check balance: ${error.message}`, 'error');
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async function cmdInfo() {
|
|
439
|
+
console.log('\nš¦ ClawCloud CLI\n');
|
|
440
|
+
console.log('Decentralized Cloud Infrastructure for AI Agents\n');
|
|
441
|
+
console.log('Network: Base Mainnet');
|
|
442
|
+
console.log(`Contract: ${CONFIG.CONTRACT_ADDRESS}`);
|
|
443
|
+
console.log(`USDC: ${CONFIG.USDC_ADDRESS}`);
|
|
444
|
+
console.log(`API: ${CONFIG.API_URL}`);
|
|
445
|
+
console.log(`Explorer: ${CONFIG.EXPLORER_URL}`);
|
|
446
|
+
console.log(`\nDocumentation: https://docs.clawcloud.co`);
|
|
447
|
+
console.log(`Chart: https://dexscreener.com/base/0xe8850bBc9c289c34e7C92C9572BE38f77D61cB07`);
|
|
448
|
+
console.log(`X: x.com/clawcloudx`);
|
|
449
|
+
console.log('');
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async function cmdHelp() {
|
|
453
|
+
console.log('\nš¦ ClawCloud CLI - Commands\n');
|
|
454
|
+
console.log('PURCHASE & MANAGE:');
|
|
455
|
+
console.log(' npx clawcloud purchase Purchase a new VM');
|
|
456
|
+
console.log(' npx clawcloud get <tokenId> Get VM credentials');
|
|
457
|
+
console.log(' npx clawcloud list List your VMs');
|
|
458
|
+
console.log('');
|
|
459
|
+
console.log('INFORMATION:');
|
|
460
|
+
console.log(' npx clawcloud tiers Show available VM tiers');
|
|
461
|
+
console.log(' npx clawcloud balance Check wallet balance');
|
|
462
|
+
console.log(' npx clawcloud info Show contract/API info');
|
|
463
|
+
console.log('');
|
|
464
|
+
console.log('WALLET:');
|
|
465
|
+
console.log(' npx clawcloud wallet View/setup wallet');
|
|
466
|
+
console.log('');
|
|
467
|
+
console.log('HELP:');
|
|
468
|
+
console.log(' npx clawcloud help Show this help');
|
|
469
|
+
console.log('');
|
|
470
|
+
console.log('Examples:');
|
|
471
|
+
console.log(' npx clawcloud tiers');
|
|
472
|
+
console.log(' npx clawcloud purchase');
|
|
473
|
+
console.log(' npx clawcloud get 42');
|
|
474
|
+
console.log(' npx clawcloud list');
|
|
475
|
+
console.log('');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async function cmdWallet() {
|
|
479
|
+
const config = loadConfig();
|
|
480
|
+
|
|
481
|
+
if (config.privateKey) {
|
|
482
|
+
const provider = new ethers.JsonRpcProvider(CONFIG.RPC_URL);
|
|
483
|
+
const wallet = new ethers.Wallet(config.privateKey, provider);
|
|
484
|
+
|
|
485
|
+
console.log('\nš¼ Current Wallet:');
|
|
486
|
+
console.log(` Address: ${wallet.address}`);
|
|
487
|
+
console.log(` Network: Base Mainnet`);
|
|
488
|
+
console.log('');
|
|
489
|
+
|
|
490
|
+
const action = await question('Options: (b)alance, (c)hange wallet, (r)emove wallet, or Enter to exit: ');
|
|
491
|
+
|
|
492
|
+
if (action === 'b') {
|
|
493
|
+
await cmdBalance();
|
|
494
|
+
} else if (action === 'c') {
|
|
495
|
+
saveConfig({});
|
|
496
|
+
log('Wallet removed. Run any command to set up a new one.', 'success');
|
|
497
|
+
} else if (action === 'r') {
|
|
498
|
+
saveConfig({});
|
|
499
|
+
log('Wallet removed from config.', 'success');
|
|
500
|
+
}
|
|
501
|
+
} else {
|
|
502
|
+
await setupWallet();
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ============================================================================
|
|
507
|
+
// MAIN
|
|
508
|
+
// ============================================================================
|
|
509
|
+
|
|
510
|
+
async function main() {
|
|
511
|
+
const args = process.argv.slice(2);
|
|
512
|
+
const command = args[0] || 'help';
|
|
513
|
+
|
|
514
|
+
try {
|
|
515
|
+
switch (command) {
|
|
516
|
+
case 'tiers':
|
|
517
|
+
await cmdTiers();
|
|
518
|
+
break;
|
|
519
|
+
case 'purchase':
|
|
520
|
+
case 'buy':
|
|
521
|
+
await cmdPurchase();
|
|
522
|
+
break;
|
|
523
|
+
case 'get':
|
|
524
|
+
case 'credentials':
|
|
525
|
+
await cmdGet(args[1]);
|
|
526
|
+
break;
|
|
527
|
+
case 'list':
|
|
528
|
+
case 'ls':
|
|
529
|
+
await cmdList();
|
|
530
|
+
break;
|
|
531
|
+
case 'balance':
|
|
532
|
+
case 'bal':
|
|
533
|
+
await cmdBalance();
|
|
534
|
+
break;
|
|
535
|
+
case 'wallet':
|
|
536
|
+
await cmdWallet();
|
|
537
|
+
break;
|
|
538
|
+
case 'info':
|
|
539
|
+
await cmdInfo();
|
|
540
|
+
break;
|
|
541
|
+
case 'help':
|
|
542
|
+
case '--help':
|
|
543
|
+
case '-h':
|
|
544
|
+
await cmdHelp();
|
|
545
|
+
break;
|
|
546
|
+
default:
|
|
547
|
+
log(`Unknown command: ${command}`, 'error');
|
|
548
|
+
await cmdHelp();
|
|
549
|
+
}
|
|
550
|
+
} catch (error) {
|
|
551
|
+
log(`Error: ${error.message}`, 'error');
|
|
552
|
+
console.error(error);
|
|
553
|
+
} finally {
|
|
554
|
+
rl.close();
|
|
555
|
+
process.exit(0);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,57 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawcloud",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "
|
|
6
|
-
"type": "module",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "ClawCloud CLI - Decentralized cloud infrastructure for AI agents on Base",
|
|
5
|
+
"main": "index.js",
|
|
7
6
|
"bin": {
|
|
8
|
-
"clawcloud": "./
|
|
7
|
+
"clawcloud": "./index.js"
|
|
9
8
|
},
|
|
10
9
|
"scripts": {
|
|
11
|
-
"test": "echo \"
|
|
12
|
-
"prepublishOnly": "chmod +x bin/clawcloud.js"
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
13
11
|
},
|
|
14
12
|
"keywords": [
|
|
15
|
-
"ai",
|
|
16
|
-
"agents",
|
|
17
|
-
"cloud",
|
|
18
|
-
"vm",
|
|
19
|
-
"infrastructure",
|
|
20
|
-
"autonomous",
|
|
21
13
|
"blockchain",
|
|
22
14
|
"base",
|
|
15
|
+
"cloud",
|
|
16
|
+
"vm",
|
|
17
|
+
"ai",
|
|
18
|
+
"agents",
|
|
19
|
+
"cryptocurrency",
|
|
23
20
|
"nft",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
21
|
+
"web3",
|
|
22
|
+
"cli"
|
|
26
23
|
],
|
|
27
24
|
"author": "ClawCloud",
|
|
28
25
|
"license": "MIT",
|
|
29
26
|
"repository": {
|
|
30
27
|
"type": "git",
|
|
31
|
-
"url": "https://github.com/clawcloud/clawcloud.git"
|
|
32
|
-
"directory": "cli"
|
|
28
|
+
"url": "https://github.com/clawcloud/clawcloud-cli.git"
|
|
33
29
|
},
|
|
34
30
|
"homepage": "https://clawcloud.co",
|
|
35
31
|
"bugs": {
|
|
36
|
-
"url": "https://github.com/clawcloud/clawcloud/issues"
|
|
37
|
-
},
|
|
38
|
-
"engines": {
|
|
39
|
-
"node": ">=18.0.0"
|
|
32
|
+
"url": "https://github.com/clawcloud/clawcloud-cli/issues"
|
|
40
33
|
},
|
|
41
34
|
"dependencies": {
|
|
42
|
-
"
|
|
43
|
-
"chalk": "^5.3.0",
|
|
44
|
-
"cli-table3": "^0.6.3",
|
|
45
|
-
"commander": "^11.1.0",
|
|
46
|
-
"ethers": "^6.10.0",
|
|
47
|
-
"inquirer": "^9.2.12",
|
|
48
|
-
"ora": "^7.0.1"
|
|
35
|
+
"ethers": "^6.9.0"
|
|
49
36
|
},
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
"templates/",
|
|
54
|
-
"README.md",
|
|
55
|
-
"LICENSE"
|
|
56
|
-
]
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
}
|
|
57
40
|
}
|