clawcloud 1.3.0 → 2.0.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/LICENSE +21 -0
- package/bin/clawcloud.js +3 -3
- package/package.json +1 -1
- package/src/api/vms.js +2 -1
- package/src/commands/buy.js +170 -5
- package/src/commands/ssh.js +93 -2
- package/src/commands/status.js +91 -2
- package/src/utils/config.js +2 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ClawCloud
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/bin/clawcloud.js
CHANGED
|
@@ -84,9 +84,9 @@ program
|
|
|
84
84
|
|
|
85
85
|
program
|
|
86
86
|
.command('buy')
|
|
87
|
-
.description('Purchase a VM
|
|
88
|
-
.option('-t, --tier <tier>', 'VM tier (
|
|
89
|
-
.option('-
|
|
87
|
+
.description('Purchase a VM')
|
|
88
|
+
.option('-t, --tier <tier>', 'VM tier (0-7)', parseInt)
|
|
89
|
+
.option('-d, --days <days>', 'Duration in days (1-365)', parseInt)
|
|
90
90
|
.option('-y, --yes', 'Skip confirmation')
|
|
91
91
|
.action(buy);
|
|
92
92
|
|
package/package.json
CHANGED
package/src/api/vms.js
CHANGED
|
@@ -26,7 +26,8 @@ export async function getVMs(address) {
|
|
|
26
26
|
ip_address: vm.ssh_host,
|
|
27
27
|
vm_type: vm.vm_type,
|
|
28
28
|
expires_at: vm.expires_at,
|
|
29
|
-
expires_in_days: calculateDaysRemaining(vm.expires_at)
|
|
29
|
+
expires_in_days: calculateDaysRemaining(vm.expires_at),
|
|
30
|
+
access_token: vm.access_token
|
|
30
31
|
}));
|
|
31
32
|
|
|
32
33
|
} catch (error) {
|
package/src/commands/buy.js
CHANGED
|
@@ -1,8 +1,173 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { ethers } from 'ethers';
|
|
5
|
+
import { getWalletConfig, getConfig, getTierInfo } from '../utils/config.js';
|
|
6
|
+
|
|
7
|
+
const CONTRACT_ABI = [
|
|
8
|
+
'function purchaseVM(uint8 tier, uint16 durationDays) external returns (uint256)',
|
|
9
|
+
'function getPrice(uint8 tier, uint16 durationDays) external view returns (uint256)',
|
|
10
|
+
'event VMPurchased(uint256 indexed tokenId, address indexed buyer, uint8 tier, uint16 durationDays)'
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const USDC_ABI = [
|
|
14
|
+
'function approve(address spender, uint256 amount) external returns (bool)',
|
|
15
|
+
'function allowance(address owner, address spender) external view returns (uint256)',
|
|
16
|
+
'function balanceOf(address account) external view returns (uint256)',
|
|
17
|
+
'function decimals() external view returns (uint8)'
|
|
18
|
+
];
|
|
19
|
+
|
|
2
20
|
export async function buy(options = {}) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
21
|
+
const wallet = getWalletConfig();
|
|
22
|
+
const config = getConfig();
|
|
23
|
+
const tierInfo = getTierInfo();
|
|
24
|
+
|
|
25
|
+
if (!wallet) {
|
|
26
|
+
console.log(chalk.red('\n❌ No wallet configured!\n'));
|
|
27
|
+
console.log(chalk.yellow('Run: npx clawcloud configure\n'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log(chalk.cyan.bold('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
32
|
+
console.log(chalk.cyan.bold(' PURCHASE VM'));
|
|
33
|
+
console.log(chalk.cyan.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
34
|
+
|
|
35
|
+
// Show available tiers
|
|
36
|
+
console.log(chalk.white('Available Tiers:\n'));
|
|
37
|
+
Object.entries(tierInfo).forEach(([tier, info]) => {
|
|
38
|
+
console.log(chalk.cyan(` Tier ${tier}: ${info.name}`));
|
|
39
|
+
console.log(chalk.gray(` Provider: ${info.provider}, ${info.cpu || info.gpu}\n`));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Get tier and duration
|
|
43
|
+
const answers = await inquirer.prompt([
|
|
44
|
+
{
|
|
45
|
+
type: 'list',
|
|
46
|
+
name: 'tier',
|
|
47
|
+
message: 'Select tier:',
|
|
48
|
+
choices: Object.entries(tierInfo).map(([tier, info]) => ({
|
|
49
|
+
name: `Tier ${tier}: ${info.name} (${info.provider})`,
|
|
50
|
+
value: parseInt(tier)
|
|
51
|
+
})),
|
|
52
|
+
when: !options.tier
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: 'number',
|
|
56
|
+
name: 'days',
|
|
57
|
+
message: 'Duration in days (1-365):',
|
|
58
|
+
default: 7,
|
|
59
|
+
validate: (input) => input >= 1 && input <= 365,
|
|
60
|
+
when: !options.days
|
|
61
|
+
}
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
const tier = options.tier || answers.tier;
|
|
65
|
+
const days = options.days || answers.days;
|
|
66
|
+
|
|
67
|
+
const spinner = ora('Connecting to blockchain...').start();
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
// Setup provider and signer
|
|
71
|
+
const provider = new ethers.JsonRpcProvider(config.RPC_URL);
|
|
72
|
+
const signer = new ethers.Wallet(wallet.privateKey, provider);
|
|
73
|
+
|
|
74
|
+
// Connect to contracts
|
|
75
|
+
const contract = new ethers.Contract(config.CONTRACT_ADDRESS, CONTRACT_ABI, signer);
|
|
76
|
+
const usdc = new ethers.Contract(config.USDC_ADDRESS, USDC_ABI, signer);
|
|
77
|
+
|
|
78
|
+
spinner.text = 'Checking USDC balance...';
|
|
79
|
+
|
|
80
|
+
// Get price and balance
|
|
81
|
+
const price = await contract.getPrice(tier, days);
|
|
82
|
+
const balance = await usdc.balanceOf(wallet.address);
|
|
83
|
+
const decimals = await usdc.decimals();
|
|
84
|
+
|
|
85
|
+
const priceFormatted = ethers.formatUnits(price, decimals);
|
|
86
|
+
const balanceFormatted = ethers.formatUnits(balance, decimals);
|
|
87
|
+
|
|
88
|
+
spinner.stop();
|
|
89
|
+
|
|
90
|
+
console.log(chalk.white(`\n💰 Price: ${chalk.green(`${priceFormatted} USDC`)}`));
|
|
91
|
+
console.log(chalk.white(`💼 Your Balance: ${chalk.cyan(`${balanceFormatted} USDC`)}\n`));
|
|
92
|
+
|
|
93
|
+
if (balance < price) {
|
|
94
|
+
console.log(chalk.red('❌ Insufficient USDC balance!\n'));
|
|
95
|
+
console.log(chalk.yellow('Fund your wallet:'));
|
|
96
|
+
console.log(chalk.gray(` ${wallet.address}\n`));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Confirm purchase
|
|
101
|
+
if (!options.yes) {
|
|
102
|
+
const { confirm } = await inquirer.prompt([{
|
|
103
|
+
type: 'confirm',
|
|
104
|
+
name: 'confirm',
|
|
105
|
+
message: `Purchase Tier ${tier} VM for ${days} days at ${priceFormatted} USDC?`,
|
|
106
|
+
default: false
|
|
107
|
+
}]);
|
|
108
|
+
|
|
109
|
+
if (!confirm) {
|
|
110
|
+
console.log(chalk.yellow('\nPurchase cancelled.\n'));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
spinner.start('Checking USDC allowance...');
|
|
116
|
+
|
|
117
|
+
// Check and set allowance
|
|
118
|
+
const allowance = await usdc.allowance(wallet.address, config.CONTRACT_ADDRESS);
|
|
119
|
+
|
|
120
|
+
if (allowance < price) {
|
|
121
|
+
spinner.text = 'Approving USDC...';
|
|
122
|
+
const approveTx = await usdc.approve(config.CONTRACT_ADDRESS, ethers.MaxUint256);
|
|
123
|
+
await approveTx.wait();
|
|
124
|
+
spinner.succeed('USDC approved!');
|
|
125
|
+
spinner.start('Purchasing VM...');
|
|
126
|
+
} else {
|
|
127
|
+
spinner.text = 'Purchasing VM...';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Purchase VM
|
|
131
|
+
const tx = await contract.purchaseVM(tier, days);
|
|
132
|
+
spinner.text = 'Waiting for confirmation...';
|
|
133
|
+
|
|
134
|
+
const receipt = await tx.wait();
|
|
135
|
+
|
|
136
|
+
// Find the VMPurchased event
|
|
137
|
+
const purchaseEvent = receipt.logs
|
|
138
|
+
.map(log => {
|
|
139
|
+
try {
|
|
140
|
+
return contract.interface.parseLog(log);
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
.find(event => event && event.name === 'VMPurchased');
|
|
146
|
+
|
|
147
|
+
const tokenId = purchaseEvent ? purchaseEvent.args.tokenId.toString() : 'Unknown';
|
|
148
|
+
|
|
149
|
+
spinner.succeed('VM purchased successfully!');
|
|
150
|
+
|
|
151
|
+
console.log(chalk.green.bold('\n✅ SUCCESS!\n'));
|
|
152
|
+
console.log(chalk.white(`🎨 NFT ID: ${chalk.cyan(`#${tokenId}`)}`));
|
|
153
|
+
console.log(chalk.white(`📦 Transaction: ${chalk.gray(receipt.hash)}`));
|
|
154
|
+
console.log(chalk.white(`⏳ Provisioning: ${chalk.yellow('Starting...')}\n`));
|
|
155
|
+
|
|
156
|
+
console.log(chalk.gray('Your VM is being provisioned. Check status:'));
|
|
157
|
+
console.log(chalk.cyan(` npx clawcloud status ${tokenId}\n`));
|
|
158
|
+
|
|
159
|
+
console.log(chalk.gray('SSH access will be available in 2-5 minutes.'));
|
|
160
|
+
console.log(chalk.cyan(` npx clawcloud ssh ${tokenId}\n`));
|
|
161
|
+
|
|
162
|
+
} catch (error) {
|
|
163
|
+
spinner.fail('Purchase failed');
|
|
164
|
+
|
|
165
|
+
if (error.code === 'INSUFFICIENT_FUNDS') {
|
|
166
|
+
console.error(chalk.red('\n❌ Insufficient ETH for gas fees\n'));
|
|
167
|
+
} else if (error.message.includes('user rejected')) {
|
|
168
|
+
console.log(chalk.yellow('\n⚠️ Transaction cancelled\n'));
|
|
169
|
+
} else {
|
|
170
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}\n`));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
8
173
|
}
|
package/src/commands/ssh.js
CHANGED
|
@@ -1,5 +1,96 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import { getWalletConfig, getConfig } from '../utils/config.js';
|
|
5
|
+
import { getVMs } from '../api/vms.js';
|
|
6
|
+
|
|
2
7
|
export async function ssh(nftId) {
|
|
3
|
-
|
|
4
|
-
|
|
8
|
+
const wallet = getWalletConfig();
|
|
9
|
+
const config = getConfig();
|
|
10
|
+
|
|
11
|
+
if (!wallet) {
|
|
12
|
+
console.log(chalk.red('\n❌ No wallet configured!\n'));
|
|
13
|
+
console.log(chalk.yellow('Run: npx clawcloud configure\n'));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const spinner = ora(`Connecting to VM #${nftId}...`).start();
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Get all VMs to find the access token
|
|
21
|
+
const response = await fetch(`${config.API_URL}/vms/${wallet.address}`);
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
const vmData = data.vms.find(v => v.token_id.toString() === nftId.toString());
|
|
24
|
+
|
|
25
|
+
if (!vmData) {
|
|
26
|
+
spinner.fail(`NFT #${nftId} not found`);
|
|
27
|
+
console.log(chalk.red(`\n❌ You don't own NFT #${nftId}\n`));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!vmData.access_token) {
|
|
32
|
+
spinner.fail('Access token not available');
|
|
33
|
+
console.log(chalk.red('\n❌ VM access token not found\n'));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Fetch detailed status using access token
|
|
38
|
+
const statusResponse = await fetch(`${config.API_URL}/status/${vmData.access_token}`);
|
|
39
|
+
const statusData = await statusResponse.json();
|
|
40
|
+
|
|
41
|
+
spinner.stop();
|
|
42
|
+
|
|
43
|
+
if (statusData.status !== 'running') {
|
|
44
|
+
console.log(chalk.yellow(`\n⚠️ VM is ${statusData.status}`));
|
|
45
|
+
|
|
46
|
+
if (statusData.status === 'provisioning') {
|
|
47
|
+
console.log(chalk.gray(' Still setting up your VM...'));
|
|
48
|
+
console.log(chalk.gray(' Try again in a few minutes\n'));
|
|
49
|
+
} else if (statusData.status === 'failed') {
|
|
50
|
+
console.log(chalk.red(' VM provisioning failed'));
|
|
51
|
+
if (statusData.errorMessage) {
|
|
52
|
+
console.log(chalk.gray(` Error: ${statusData.errorMessage}\n`));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!statusData.ssh) {
|
|
59
|
+
console.log(chalk.red('\n❌ SSH credentials not available\n'));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(chalk.cyan(`\n🔌 Connecting to ${statusData.ssh.host}...\n`));
|
|
64
|
+
console.log(chalk.gray(`Command: ${statusData.ssh.command}\n`));
|
|
65
|
+
|
|
66
|
+
// Spawn SSH process
|
|
67
|
+
const sshProcess = spawn('ssh', [
|
|
68
|
+
'-o', 'StrictHostKeyChecking=no',
|
|
69
|
+
'-o', 'UserKnownHostsFile=/dev/null',
|
|
70
|
+
'-p', statusData.ssh.port.toString(),
|
|
71
|
+
`${statusData.ssh.username}@${statusData.ssh.host}`
|
|
72
|
+
], {
|
|
73
|
+
stdio: 'inherit'
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
sshProcess.on('error', (err) => {
|
|
77
|
+
if (err.code === 'ENOENT') {
|
|
78
|
+
console.log(chalk.red('\n❌ SSH not found on your system\n'));
|
|
79
|
+
console.log(chalk.yellow('Install SSH client or use this command manually:'));
|
|
80
|
+
console.log(chalk.green(` ${statusData.ssh.command}\n`));
|
|
81
|
+
} else {
|
|
82
|
+
console.error(chalk.red(`\n❌ SSH error: ${err.message}\n`));
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
sshProcess.on('exit', (code) => {
|
|
87
|
+
if (code !== 0 && code !== null) {
|
|
88
|
+
console.log(chalk.gray(`\nSSH disconnected (exit code: ${code})\n`));
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
} catch (error) {
|
|
93
|
+
spinner.fail('Failed to connect');
|
|
94
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}\n`));
|
|
95
|
+
}
|
|
5
96
|
}
|
package/src/commands/status.js
CHANGED
|
@@ -1,5 +1,94 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { getWalletConfig, getConfig } from '../utils/config.js';
|
|
4
|
+
import { getVMs } from '../api/vms.js';
|
|
5
|
+
|
|
2
6
|
export async function status(nftId, options = {}) {
|
|
3
|
-
|
|
4
|
-
|
|
7
|
+
const wallet = getWalletConfig();
|
|
8
|
+
const config = getConfig();
|
|
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 status for NFT #${nftId}...`).start();
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
// First, get all VMs to find the access token
|
|
20
|
+
const vms = await getVMs(wallet.address);
|
|
21
|
+
const vm = vms.find(v => v.nft_id.toString() === nftId.toString());
|
|
22
|
+
|
|
23
|
+
if (!vm) {
|
|
24
|
+
spinner.fail(`NFT #${nftId} not found`);
|
|
25
|
+
console.log(chalk.red(`\n❌ You don't own NFT #${nftId}\n`));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Get the access token from the API
|
|
30
|
+
const response = await fetch(`${config.API_URL}/vms/${wallet.address}`);
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
const vmData = data.vms.find(v => v.token_id.toString() === nftId.toString());
|
|
33
|
+
|
|
34
|
+
if (!vmData || !vmData.access_token) {
|
|
35
|
+
spinner.fail('Access token not found');
|
|
36
|
+
console.log(chalk.red('\n❌ VM access token not available\n'));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Fetch detailed status using access token
|
|
41
|
+
const statusResponse = await fetch(`${config.API_URL}/status/${vmData.access_token}`);
|
|
42
|
+
const statusData = await statusResponse.json();
|
|
43
|
+
|
|
44
|
+
spinner.stop();
|
|
45
|
+
|
|
46
|
+
if (options.json) {
|
|
47
|
+
console.log(JSON.stringify(statusData, null, 2));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log(chalk.cyan.bold('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
52
|
+
console.log(chalk.cyan.bold(` NFT #${nftId} STATUS`));
|
|
53
|
+
console.log(chalk.cyan.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
54
|
+
|
|
55
|
+
const statusColor = statusData.status === 'running' ? chalk.green :
|
|
56
|
+
statusData.status === 'provisioning' ? chalk.yellow :
|
|
57
|
+
statusData.status === 'failed' ? chalk.red :
|
|
58
|
+
chalk.gray;
|
|
59
|
+
|
|
60
|
+
console.log(chalk.white(`🎨 NFT ID: ${chalk.cyan(`#${statusData.tokenId}`)}`));
|
|
61
|
+
console.log(chalk.white(`📊 Status: ${statusColor(statusData.status.toUpperCase())}`));
|
|
62
|
+
console.log(chalk.white(`🎯 Tier: ${chalk.cyan(statusData.tier)}`));
|
|
63
|
+
console.log(chalk.white(`☁️ Provider: ${chalk.cyan(statusData.type || 'N/A')}`));
|
|
64
|
+
console.log(chalk.white(`💼 Owner: ${chalk.gray(statusData.owner)}`));
|
|
65
|
+
console.log(chalk.white(`⏰ Expires: ${chalk.yellow(new Date(statusData.expiresAt).toLocaleString())}`));
|
|
66
|
+
|
|
67
|
+
if (statusData.ssh) {
|
|
68
|
+
console.log(chalk.green.bold('\n✅ SSH ACCESS READY:\n'));
|
|
69
|
+
console.log(chalk.white(`🌐 Host: ${chalk.cyan(statusData.ssh.host)}`));
|
|
70
|
+
console.log(chalk.white(`🔌 Port: ${chalk.cyan(statusData.ssh.port)}`));
|
|
71
|
+
console.log(chalk.white(`👤 User: ${chalk.cyan(statusData.ssh.username)}`));
|
|
72
|
+
console.log(chalk.white(`🔑 Command: ${chalk.green(statusData.ssh.command)}`));
|
|
73
|
+
} else if (statusData.status === 'provisioning') {
|
|
74
|
+
console.log(chalk.yellow('\n⏳ VM is still provisioning...'));
|
|
75
|
+
console.log(chalk.gray(' Check back in a few minutes\n'));
|
|
76
|
+
} else if (statusData.status === 'failed') {
|
|
77
|
+
console.log(chalk.red('\n❌ VM provisioning failed'));
|
|
78
|
+
if (statusData.errorMessage) {
|
|
79
|
+
console.log(chalk.gray(` Error: ${statusData.errorMessage}`));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(chalk.gray('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
84
|
+
|
|
85
|
+
if (statusData.ssh) {
|
|
86
|
+
console.log(chalk.white('Connect with:'));
|
|
87
|
+
console.log(chalk.green(` ${statusData.ssh.command}\n`));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
} catch (error) {
|
|
91
|
+
spinner.fail('Failed to fetch VM status');
|
|
92
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}\n`));
|
|
93
|
+
}
|
|
5
94
|
}
|
package/src/utils/config.js
CHANGED
|
@@ -56,10 +56,10 @@ export function getWalletConfig() {
|
|
|
56
56
|
export function getConfig() {
|
|
57
57
|
const defaultConfig = {
|
|
58
58
|
API_URL: 'https://api.clawcloud.co',
|
|
59
|
-
API_VERSION: '
|
|
59
|
+
API_VERSION: '2.0.0',
|
|
60
60
|
CONTRACT_ADDRESS: '0xF708741D37C420518852c6A15aB658066951c852',
|
|
61
61
|
USDC_ADDRESS: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
62
|
-
RPC_URL: 'https://mainnet.
|
|
62
|
+
RPC_URL: 'https://base-mainnet.infura.io/v3/8f41e4a169f14e539ff9a576b2be421c',
|
|
63
63
|
TELEGRAM_BOT: 'clawcloud_devbot',
|
|
64
64
|
NETWORK: 'base',
|
|
65
65
|
CHAIN_ID: 8453
|