puter-cli 1.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/.idx/dev.nix +53 -0
- package/LICENSE.md +209 -0
- package/README.md +292 -0
- package/bin/index.js +38 -0
- package/commands/apps.js +421 -0
- package/commands/auth.js +233 -0
- package/commands/commons.js +296 -0
- package/commands/executor.js +306 -0
- package/commands/files.js +1290 -0
- package/commands/init.js +67 -0
- package/commands/shell.js +56 -0
- package/commands/sites.js +214 -0
- package/commands/subdomains.js +103 -0
- package/commands/utils.js +92 -0
- package/package.json +37 -0
- package/tests/login.test.js +268 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getAuthToken } from './auth.js';
|
|
3
|
+
|
|
4
|
+
export const PROJECT_NAME = 'puter-cli';
|
|
5
|
+
export const API_BASE = 'https://api.puter.com';
|
|
6
|
+
export const BASE_URL = 'https://puter.com';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get headers with the correct Content-Type for multipart form data.
|
|
10
|
+
* @param {string} contentType - The "Content-Type" argument for the header ('application/json' is the default)
|
|
11
|
+
* Use the multipart form data for upload a file.
|
|
12
|
+
* @returns {Object} The headers object.
|
|
13
|
+
*/
|
|
14
|
+
export function getHeaders(contentType = 'application/json') {
|
|
15
|
+
return {
|
|
16
|
+
'Accept': '*/*',
|
|
17
|
+
'Accept-Language': 'en-US,en;q=0.9',
|
|
18
|
+
'Authorization': `Bearer ${getAuthToken()}`,
|
|
19
|
+
'Connection': 'keep-alive',
|
|
20
|
+
// 'Host': 'api.puter.com',
|
|
21
|
+
'Content-Type': contentType,
|
|
22
|
+
'Origin': `${BASE_URL}`,
|
|
23
|
+
'Referer': `${BASE_URL}/`,
|
|
24
|
+
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generate a random app name
|
|
30
|
+
* @returns a random app name or null if it fails
|
|
31
|
+
* @see: [randName](https://github.com/HeyPuter/puter/blob/06a67a3b223a6cbd7ec2e16853b6d2304f621a88/src/puter-js/src/index.js#L389)
|
|
32
|
+
*/
|
|
33
|
+
export function generateAppName(separateWith = '-'){
|
|
34
|
+
console.log(chalk.cyan('Generating random app name...'));
|
|
35
|
+
try {
|
|
36
|
+
const first_adj = ['helpful','sensible', 'loyal', 'honest', 'clever', 'capable','calm', 'smart', 'genius', 'bright', 'charming', 'creative', 'diligent', 'elegant', 'fancy',
|
|
37
|
+
'colorful', 'avid', 'active', 'gentle', 'happy', 'intelligent', 'jolly', 'kind', 'lively', 'merry', 'nice', 'optimistic', 'polite',
|
|
38
|
+
'quiet', 'relaxed', 'silly', 'victorious', 'witty', 'young', 'zealous', 'strong', 'brave', 'agile', 'bold'];
|
|
39
|
+
|
|
40
|
+
const nouns = ['street', 'roof', 'floor', 'tv', 'idea', 'morning', 'game', 'wheel', 'shoe', 'bag', 'clock', 'pencil', 'pen',
|
|
41
|
+
'magnet', 'chair', 'table', 'house', 'dog', 'room', 'book', 'car', 'cat', 'tree',
|
|
42
|
+
'flower', 'bird', 'fish', 'sun', 'moon', 'star', 'cloud', 'rain', 'snow', 'wind', 'mountain',
|
|
43
|
+
'river', 'lake', 'sea', 'ocean', 'island', 'bridge', 'road', 'train', 'plane', 'ship', 'bicycle',
|
|
44
|
+
'horse', 'elephant', 'lion', 'tiger', 'bear', 'zebra', 'giraffe', 'monkey', 'snake', 'rabbit', 'duck',
|
|
45
|
+
'goose', 'penguin', 'frog', 'crab', 'shrimp', 'whale', 'octopus', 'spider', 'ant', 'bee', 'butterfly', 'dragonfly',
|
|
46
|
+
'ladybug', 'snail', 'camel', 'kangaroo', 'koala', 'panda', 'piglet', 'sheep', 'wolf', 'fox', 'deer', 'mouse', 'seal',
|
|
47
|
+
'chicken', 'cow', 'dinosaur', 'puppy', 'kitten', 'circle', 'square', 'garden', 'otter', 'bunny', 'meerkat', 'harp']
|
|
48
|
+
|
|
49
|
+
// return a random combination of first_adj + noun + number (between 0 and 9999)
|
|
50
|
+
// e.g. clever-idea-123
|
|
51
|
+
const appName = first_adj[Math.floor(Math.random() * first_adj.length)] + separateWith + nouns[Math.floor(Math.random() * nouns.length)] + separateWith + Math.floor(Math.random() * 10000);
|
|
52
|
+
console.log(chalk.green(`AppName: "${appName}"`));
|
|
53
|
+
return appName;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(`Error: ${error.message}`);
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Display data in a structured format
|
|
62
|
+
* @param {Array} data - The data to display
|
|
63
|
+
* @param {Object} options - Display options
|
|
64
|
+
* @param {Array} options.headers - Headers for the table
|
|
65
|
+
* @param {Array} options.columns - Columns to display
|
|
66
|
+
* @param {number} options.columnWidth - Width of each column
|
|
67
|
+
*/
|
|
68
|
+
export function displayTable(data, options = {}) {
|
|
69
|
+
const { headers = [], columns = [], columnWidth = 20 } = options;
|
|
70
|
+
|
|
71
|
+
// Create the header row
|
|
72
|
+
const headerRow = headers.map(header => chalk.cyan(header.padEnd(columnWidth))).join(' | ');
|
|
73
|
+
console.log(headerRow);
|
|
74
|
+
console.log(chalk.dim('-'.repeat(headerRow.length)));
|
|
75
|
+
|
|
76
|
+
// Create and display each row of data
|
|
77
|
+
data.forEach(item => {
|
|
78
|
+
const row = columns.map(col => {
|
|
79
|
+
const value = item[col] || 'N/A';
|
|
80
|
+
return value.toString().padEnd(columnWidth);
|
|
81
|
+
}).join(' | ');
|
|
82
|
+
console.log(row);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Display structured ouput of disk usage informations
|
|
88
|
+
*/
|
|
89
|
+
export function showDiskSpaceUsage(data) {
|
|
90
|
+
const freeSpace = parseInt(data.capacity) - parseInt(data.used);
|
|
91
|
+
const usagePercentage = (parseInt(data.used) / parseInt(data.capacity)) * 100;
|
|
92
|
+
console.log(chalk.cyan('Disk Usage Information:'));
|
|
93
|
+
console.log(chalk.dim('----------------------------------------'));
|
|
94
|
+
console.log(chalk.cyan(`Total Capacity: `) + chalk.white(formatSize(data.capacity)));
|
|
95
|
+
console.log(chalk.cyan(`Used Space: `) + chalk.white(formatSize(data.used)));
|
|
96
|
+
console.log(chalk.cyan(`Free Space: `) + chalk.white(formatSize(freeSpace)));
|
|
97
|
+
// format the usagePercentage with 2 decimal floating point value:
|
|
98
|
+
console.log(chalk.cyan(`Usage Percentage: `) + chalk.white(`${usagePercentage.toFixed(2)}%`));
|
|
99
|
+
console.log(chalk.dim('----------------------------------------'));
|
|
100
|
+
console.log(chalk.green('Done.'));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Resolve a relative path to an absolute path
|
|
105
|
+
* @param {string} currentPath - The current working directory
|
|
106
|
+
* @param {string} relativePath - The relative path to resolve
|
|
107
|
+
* @returns {string} The resolved absolute path
|
|
108
|
+
*/
|
|
109
|
+
export function resolvePath(currentPath, relativePath) {
|
|
110
|
+
// Normalize the current path (remove trailing slashes)
|
|
111
|
+
currentPath = currentPath.replace(/\/+$/, '');
|
|
112
|
+
|
|
113
|
+
// Split the relative path into parts
|
|
114
|
+
const parts = relativePath.split('/').filter(p => p); // Remove empty parts
|
|
115
|
+
|
|
116
|
+
// Handle each part of the relative path
|
|
117
|
+
for (const part of parts) {
|
|
118
|
+
if (part === '..') {
|
|
119
|
+
// Move one level up
|
|
120
|
+
const currentParts = currentPath.split('/').filter(p => p);
|
|
121
|
+
if (currentParts.length > 0) {
|
|
122
|
+
currentParts.pop(); // Remove the last part
|
|
123
|
+
}
|
|
124
|
+
currentPath = '/' + currentParts.join('/');
|
|
125
|
+
} else if (part === '.') {
|
|
126
|
+
// Stay in the current directory (no change)
|
|
127
|
+
continue;
|
|
128
|
+
} else {
|
|
129
|
+
// Move into a subdirectory
|
|
130
|
+
currentPath += `/${part}`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Normalize the final path (remove duplicate slashes)
|
|
135
|
+
currentPath = currentPath.replace(/\/+/g, '/');
|
|
136
|
+
|
|
137
|
+
// Ensure the path ends with a slash if it's the root
|
|
138
|
+
if (currentPath === '') {
|
|
139
|
+
currentPath = '/';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return currentPath;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Checks if a given string is a valid app name.
|
|
147
|
+
* The name must:
|
|
148
|
+
* - Not be '.' or '..'
|
|
149
|
+
* - Not contain path separators ('/' or '\\')
|
|
150
|
+
* - Not contain wildcard characters ('*')
|
|
151
|
+
* - (Optional) Contain only allowed characters (letters, numbers, spaces, underscores, hyphens)
|
|
152
|
+
*
|
|
153
|
+
* @param {string} name - The app name to validate.
|
|
154
|
+
* @returns {boolean} - Returns true if valid, false otherwise.
|
|
155
|
+
*/
|
|
156
|
+
export function isValidAppName(name) {
|
|
157
|
+
// Ensure the name is a non-empty string
|
|
158
|
+
if (typeof name !== 'string' || name.trim().length === 0) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Trim whitespace from both ends
|
|
163
|
+
const trimmedName = name.trim();
|
|
164
|
+
|
|
165
|
+
// Reject reserved names
|
|
166
|
+
if (trimmedName === '.' || trimmedName === '..') {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Regex patterns for invalid characters
|
|
171
|
+
const invalidPattern = /[\/\\*]/; // Disallow /, \, and *
|
|
172
|
+
|
|
173
|
+
if (invalidPattern.test(trimmedName)) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Optional: Define allowed characters pattern
|
|
178
|
+
// Uncomment the following lines if you want to enforce allowed characters
|
|
179
|
+
/*
|
|
180
|
+
const allowedPattern = /^[A-Za-z0-9 _-]+$/;
|
|
181
|
+
if (!allowedPattern.test(trimmedName)) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
*/
|
|
185
|
+
|
|
186
|
+
// All checks passed
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function getDefaultHomePage(appName) {
|
|
191
|
+
const defaultIndexContent = `<!DOCTYPE html>
|
|
192
|
+
<html lang="en">
|
|
193
|
+
<head>
|
|
194
|
+
<meta charset="UTF-8">
|
|
195
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
196
|
+
<title>${appName}</title>
|
|
197
|
+
<style>
|
|
198
|
+
body {
|
|
199
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
200
|
+
line-height: 1.6;
|
|
201
|
+
max-width: 800px;
|
|
202
|
+
margin: 0 auto;
|
|
203
|
+
padding: 20px;
|
|
204
|
+
background: #f9fafb;
|
|
205
|
+
color: #1f2937;
|
|
206
|
+
}
|
|
207
|
+
.container {
|
|
208
|
+
background: white;
|
|
209
|
+
padding: 2rem;
|
|
210
|
+
border-radius: 8px;
|
|
211
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
212
|
+
}
|
|
213
|
+
h1 {
|
|
214
|
+
color: #2563eb;
|
|
215
|
+
margin-bottom: 1rem;
|
|
216
|
+
}
|
|
217
|
+
.code-block {
|
|
218
|
+
background: #f1f5f9;
|
|
219
|
+
padding: 1rem;
|
|
220
|
+
border-radius: 4px;
|
|
221
|
+
font-family: monospace;
|
|
222
|
+
overflow-x: auto;
|
|
223
|
+
}
|
|
224
|
+
.tip {
|
|
225
|
+
background: #dbeafe;
|
|
226
|
+
border-left: 4px solid #2563eb;
|
|
227
|
+
padding: 1rem;
|
|
228
|
+
margin: 1rem 0;
|
|
229
|
+
}
|
|
230
|
+
.links {
|
|
231
|
+
display: flex;
|
|
232
|
+
gap: 1rem;
|
|
233
|
+
margin-top: 2rem;
|
|
234
|
+
}
|
|
235
|
+
.links a {
|
|
236
|
+
color: #2563eb;
|
|
237
|
+
text-decoration: none;
|
|
238
|
+
}
|
|
239
|
+
.links a:hover {
|
|
240
|
+
text-decoration: underline;
|
|
241
|
+
}
|
|
242
|
+
.footer {
|
|
243
|
+
text-align: center;
|
|
244
|
+
margin-top: 50px;
|
|
245
|
+
color: var(--color-grey);
|
|
246
|
+
font-size: 0.9rem;
|
|
247
|
+
}
|
|
248
|
+
</style>
|
|
249
|
+
</head>
|
|
250
|
+
<body>
|
|
251
|
+
<div class="container">
|
|
252
|
+
<h1>🚀 Welcome to ${appName}!</h1>
|
|
253
|
+
|
|
254
|
+
<p>This is your new website powered by Puter. You can start customizing it right away!</p>
|
|
255
|
+
|
|
256
|
+
<div class="tip">
|
|
257
|
+
<strong>Quick Tip:</strong> Replace this content with your own by editing the <code>index.html</code> file.
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<h2>🌟 Getting Started</h2>
|
|
261
|
+
|
|
262
|
+
<p>Here's a simple example using Puter.js:</p>
|
|
263
|
+
|
|
264
|
+
<div class="code-block">
|
|
265
|
+
<script src="https://js.puter.com/v2/"></script>
|
|
266
|
+
<script>
|
|
267
|
+
// Create a new file in the cloud
|
|
268
|
+
puter.fs.write('hello.txt', 'Hello, Puter!')
|
|
269
|
+
.then(file => console.log(\`File created at: \${file.path}\`));
|
|
270
|
+
</script>
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
<h2>💡 Key Features</h2>
|
|
274
|
+
<ul>
|
|
275
|
+
<li>Cloud Storage</li>
|
|
276
|
+
<li>AI Services (GPT-4, DALL-E)</li>
|
|
277
|
+
<li>Static Website Hosting</li>
|
|
278
|
+
<li>Key-Value Store</li>
|
|
279
|
+
<li>Authentication</li>
|
|
280
|
+
</ul>
|
|
281
|
+
|
|
282
|
+
<div class="links">
|
|
283
|
+
<a href="https://docs.puter.com" target="_blank">📚 Documentation</a>
|
|
284
|
+
<a href="https://discord.gg/puter" target="_blank">💬 Discord Community</a>
|
|
285
|
+
<a href="https://github.com/HeyPuter" target="_blank">👩💻 GitHub</a>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
|
|
289
|
+
<footer class="footer">
|
|
290
|
+
© 2025 ${appName}. All rights reserved.
|
|
291
|
+
</footer>
|
|
292
|
+
</body>
|
|
293
|
+
</html>`;
|
|
294
|
+
|
|
295
|
+
return defaultIndexContent;
|
|
296
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import Conf from 'conf';
|
|
4
|
+
import { listApps, appInfo, createApp, updateApp, deleteApp } from './apps.js';
|
|
5
|
+
import { listSites, createSite, deleteSite, infoSite } from './sites.js';
|
|
6
|
+
import { listFiles, makeDirectory, renameFileOrDirectory,
|
|
7
|
+
removeFileOrDirectory, emptyTrash, changeDirectory, showCwd,
|
|
8
|
+
getInfo, getDiskUsage, createFile, readFile, uploadFile,
|
|
9
|
+
downloadFile, copyFile, syncDirectory } from './files.js';
|
|
10
|
+
import { getUserInfo, getUsageInfo } from './auth.js';
|
|
11
|
+
import { PROJECT_NAME, API_BASE, getHeaders } from './commons.js';
|
|
12
|
+
import inquirer from 'inquirer';
|
|
13
|
+
import { exec } from 'node:child_process';
|
|
14
|
+
|
|
15
|
+
const config = new Conf({ projectName: PROJECT_NAME });
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Update the prompt function
|
|
19
|
+
* @returns The current prompt
|
|
20
|
+
*/
|
|
21
|
+
export function getPrompt() {
|
|
22
|
+
return chalk.cyan(`puter@${config.get('cwd').slice(1)}> `);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const commands = {
|
|
26
|
+
help: showHelp,
|
|
27
|
+
exit: () => process.exit(0),
|
|
28
|
+
logout: async () => {
|
|
29
|
+
await import('./auth.js').then(m => m.logout());
|
|
30
|
+
process.exit(0);
|
|
31
|
+
},
|
|
32
|
+
whoami: getUserInfo,
|
|
33
|
+
stat: getInfo,
|
|
34
|
+
apps: async (args) => {
|
|
35
|
+
await listApps({
|
|
36
|
+
statsPeriod: args[0] || 'all'
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
app: appInfo,
|
|
40
|
+
'app:create': async (args) => {
|
|
41
|
+
if (args.length < 1) {
|
|
42
|
+
console.log(chalk.red('Usage: app:create <name> [<remote_dir>] [--description=<description>] [--url=<url>]'));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
await createApp(args);
|
|
46
|
+
},
|
|
47
|
+
'app:update': async (args) => {
|
|
48
|
+
if (args.length < 1) {
|
|
49
|
+
console.log(chalk.red('Usage: app:update <name> <remote_dir>'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
await updateApp(args);
|
|
53
|
+
},
|
|
54
|
+
'app:delete': async (args) => {
|
|
55
|
+
if (args.length < 1) {
|
|
56
|
+
console.log(chalk.red('Usage: app:delete <name>'));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const name = args.find(arg => arg !=='-f')
|
|
60
|
+
const force = args.some(arg => arg =='-f')? true: false;
|
|
61
|
+
|
|
62
|
+
if (!force){
|
|
63
|
+
const { confirm } = await inquirer.prompt([
|
|
64
|
+
{
|
|
65
|
+
type: 'confirm',
|
|
66
|
+
name: 'confirm',
|
|
67
|
+
message: chalk.yellow(`Are you sure you want to delete "${name}"?`),
|
|
68
|
+
default: false
|
|
69
|
+
}
|
|
70
|
+
]);
|
|
71
|
+
if (!confirm) {
|
|
72
|
+
console.log(chalk.yellow('Operation cancelled.'));
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
await deleteApp(name);
|
|
77
|
+
},
|
|
78
|
+
ls: listFiles,
|
|
79
|
+
cd: async (args) => {
|
|
80
|
+
await changeDirectory(args);
|
|
81
|
+
},
|
|
82
|
+
pwd: showCwd,
|
|
83
|
+
mkdir: makeDirectory,
|
|
84
|
+
mv: renameFileOrDirectory,
|
|
85
|
+
rm: removeFileOrDirectory,
|
|
86
|
+
// rmdir: deleteFolder, // Not implemented in Puter API
|
|
87
|
+
clean: emptyTrash,
|
|
88
|
+
df: getDiskUsage,
|
|
89
|
+
usage: getUsageInfo,
|
|
90
|
+
cp: copyFile,
|
|
91
|
+
touch: createFile,
|
|
92
|
+
cat: readFile,
|
|
93
|
+
push: uploadFile,
|
|
94
|
+
pull: downloadFile,
|
|
95
|
+
update: syncDirectory,
|
|
96
|
+
sites: listSites,
|
|
97
|
+
site: infoSite,
|
|
98
|
+
'site:delete': deleteSite,
|
|
99
|
+
'site:create': createSite
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Execute a command
|
|
104
|
+
* @param {string} input The command line input
|
|
105
|
+
*/
|
|
106
|
+
export async function execCommand(input) {
|
|
107
|
+
const [cmd, ...args] = input.split(' ');
|
|
108
|
+
|
|
109
|
+
if (cmd === 'help') {
|
|
110
|
+
// Handle help command
|
|
111
|
+
const command = args[0];
|
|
112
|
+
showHelp(command);
|
|
113
|
+
} else if (cmd.startsWith('!')) {
|
|
114
|
+
// Execute the command on the host machine
|
|
115
|
+
const hostCommand = input.slice(1); // Remove the "!"
|
|
116
|
+
exec(hostCommand, (error, stdout, stderr) => {
|
|
117
|
+
if (error) {
|
|
118
|
+
console.error(chalk.red(`Host Error: ${error.message}`));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (stderr) {
|
|
122
|
+
console.error(chalk.red(stderr));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
console.log(stdout);
|
|
126
|
+
console.log(chalk.green(`Press <Enter> to return.`));
|
|
127
|
+
});
|
|
128
|
+
} else if (commands[cmd]) {
|
|
129
|
+
try {
|
|
130
|
+
await commands[cmd](args);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error(chalk.red(`Error executing command: ${error.message}`));
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
if (!['Y', 'N'].includes(cmd.toUpperCase()[0])) {
|
|
136
|
+
console.log(chalk.red(`Unknown command: ${cmd}`));
|
|
137
|
+
showHelp();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Display help for a specific command or general help if no command is provided.
|
|
144
|
+
* @param {string} [command] - The command to display help for.
|
|
145
|
+
*/
|
|
146
|
+
function showHelp(command) {
|
|
147
|
+
const commandHelp = {
|
|
148
|
+
help: `
|
|
149
|
+
${chalk.cyan('help [command]')}
|
|
150
|
+
Display help for a specific command or show general help.
|
|
151
|
+
Example: help ls
|
|
152
|
+
`,
|
|
153
|
+
exit: `
|
|
154
|
+
${chalk.cyan('exit')}
|
|
155
|
+
Exit the shell.
|
|
156
|
+
`,
|
|
157
|
+
logout: `
|
|
158
|
+
${chalk.cyan('logout')}
|
|
159
|
+
Logout from Puter account.
|
|
160
|
+
`,
|
|
161
|
+
whoami: `
|
|
162
|
+
${chalk.cyan('whoami')}
|
|
163
|
+
Show user information.
|
|
164
|
+
`,
|
|
165
|
+
stat: `
|
|
166
|
+
${chalk.cyan('stat <path>')}
|
|
167
|
+
Show file or directory information.
|
|
168
|
+
Example: stat /path/to/file
|
|
169
|
+
`,
|
|
170
|
+
df: `
|
|
171
|
+
${chalk.cyan('df')}
|
|
172
|
+
Show disk usage information.
|
|
173
|
+
`,
|
|
174
|
+
usage: `
|
|
175
|
+
${chalk.cyan('usage')}
|
|
176
|
+
Show usage information.
|
|
177
|
+
`,
|
|
178
|
+
apps: `
|
|
179
|
+
${chalk.cyan('apps [period]')}
|
|
180
|
+
List all your apps.
|
|
181
|
+
period: today, yesterday, 7d, 30d, this_month, last_month
|
|
182
|
+
Example: apps today
|
|
183
|
+
`,
|
|
184
|
+
app: `
|
|
185
|
+
${chalk.cyan('app <app_name>')}
|
|
186
|
+
Get application information.
|
|
187
|
+
Example: app myapp
|
|
188
|
+
`,
|
|
189
|
+
'app:create': `
|
|
190
|
+
${chalk.cyan('app:create <name> [url]')}
|
|
191
|
+
Create a new app.
|
|
192
|
+
Example: app:create myapp https://example.com
|
|
193
|
+
`,
|
|
194
|
+
'app:update': `
|
|
195
|
+
${chalk.cyan('app:update <name> [dir]')}
|
|
196
|
+
Update an app.
|
|
197
|
+
Example: app:update myapp .
|
|
198
|
+
`,
|
|
199
|
+
'app:delete': `
|
|
200
|
+
${chalk.cyan('app:delete <name>')}
|
|
201
|
+
Delete an app.
|
|
202
|
+
Example: app:delete myapp
|
|
203
|
+
`,
|
|
204
|
+
ls: `
|
|
205
|
+
${chalk.cyan('ls [dir]')}
|
|
206
|
+
List files and directories.
|
|
207
|
+
Example: ls /path/to/dir
|
|
208
|
+
`,
|
|
209
|
+
cd: `
|
|
210
|
+
${chalk.cyan('cd [dir]')}
|
|
211
|
+
Change the current working directory.
|
|
212
|
+
Example: cd /path/to/dir
|
|
213
|
+
`,
|
|
214
|
+
pwd: `
|
|
215
|
+
${chalk.cyan('pwd')}
|
|
216
|
+
Print the current working directory.
|
|
217
|
+
`,
|
|
218
|
+
mkdir: `
|
|
219
|
+
${chalk.cyan('mkdir <dir>')}
|
|
220
|
+
Create a new directory.
|
|
221
|
+
Example: mkdir /path/to/newdir
|
|
222
|
+
`,
|
|
223
|
+
mv: `
|
|
224
|
+
${chalk.cyan('mv <src> <dest>')}
|
|
225
|
+
Move or rename a file or directory.
|
|
226
|
+
Example: mv /path/to/src /path/to/dest
|
|
227
|
+
`,
|
|
228
|
+
rm: `
|
|
229
|
+
${chalk.cyan('rm <file>')}
|
|
230
|
+
Move a file or directory to the system's Trash.
|
|
231
|
+
Example: rm /path/to/file
|
|
232
|
+
`,
|
|
233
|
+
clean: `
|
|
234
|
+
${chalk.cyan('clean')}
|
|
235
|
+
Empty the system's Trash.
|
|
236
|
+
`,
|
|
237
|
+
cp: `
|
|
238
|
+
${chalk.cyan('cp <src> <dest>')}
|
|
239
|
+
Copy files or directories.
|
|
240
|
+
Example: cp /path/to/src /path/to/dest
|
|
241
|
+
`,
|
|
242
|
+
touch: `
|
|
243
|
+
${chalk.cyan('touch <file>')}
|
|
244
|
+
Create a new empty file.
|
|
245
|
+
Example: touch /path/to/file
|
|
246
|
+
`,
|
|
247
|
+
cat: `
|
|
248
|
+
${chalk.cyan('cat <file>')}
|
|
249
|
+
Output file content to the console.
|
|
250
|
+
Example: cat /path/to/file
|
|
251
|
+
`,
|
|
252
|
+
push: `
|
|
253
|
+
${chalk.cyan('push <file>')}
|
|
254
|
+
Upload file to Puter cloud.
|
|
255
|
+
Example: push /path/to/file
|
|
256
|
+
`,
|
|
257
|
+
pull: `
|
|
258
|
+
${chalk.cyan('pull <file>')}
|
|
259
|
+
Download file from Puter cloud.
|
|
260
|
+
Example: pull /path/to/file
|
|
261
|
+
`,
|
|
262
|
+
update: `
|
|
263
|
+
${chalk.cyan('update <src> <dest>')}
|
|
264
|
+
Sync local directory with remote cloud.
|
|
265
|
+
Example: update /local/path /remote/path
|
|
266
|
+
`,
|
|
267
|
+
sites: `
|
|
268
|
+
${chalk.cyan('sites')}
|
|
269
|
+
List sites and subdomains.
|
|
270
|
+
`,
|
|
271
|
+
site: `
|
|
272
|
+
${chalk.cyan('site <site_uid>')}
|
|
273
|
+
Get site information by UID.
|
|
274
|
+
Example: site sd-123456
|
|
275
|
+
`,
|
|
276
|
+
'site:delete': `
|
|
277
|
+
${chalk.cyan('site:delete <uid>')}
|
|
278
|
+
Delete a site by UID.
|
|
279
|
+
Example: site:delete sd-123456
|
|
280
|
+
`,
|
|
281
|
+
'site:create': `
|
|
282
|
+
${chalk.cyan('site:create <dir> [--subdomain=<name>]')}
|
|
283
|
+
Create a static website from directory.
|
|
284
|
+
Example: site:create /path/to/dir --subdomain=myapp
|
|
285
|
+
`,
|
|
286
|
+
'!': `
|
|
287
|
+
${chalk.cyan('!<command>')}
|
|
288
|
+
Execute a command on the host machine.
|
|
289
|
+
Example: !ls -la
|
|
290
|
+
`,
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
if (command && commandHelp[command]) {
|
|
294
|
+
console.log(chalk.yellow(`\nHelp for command: ${chalk.cyan(command)}`));
|
|
295
|
+
console.log(commandHelp[command]);
|
|
296
|
+
} else if (command) {
|
|
297
|
+
console.log(chalk.red(`Unknown command: ${command}`));
|
|
298
|
+
console.log(chalk.yellow('Use "help" to see a list of available commands.'));
|
|
299
|
+
} else {
|
|
300
|
+
console.log(chalk.yellow('\nAvailable commands:'));
|
|
301
|
+
for (const cmd in commandHelp) {
|
|
302
|
+
console.log(chalk.cyan(cmd.padEnd(20)) + '- ' + commandHelp[cmd].split('\n')[2].trim());
|
|
303
|
+
}
|
|
304
|
+
console.log(chalk.yellow('\nUse "help <command>" for detailed help on a specific command.'));
|
|
305
|
+
}
|
|
306
|
+
}
|