@heyputer/shell 2.1.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.
@@ -0,0 +1,386 @@
1
+ import chalk from 'chalk';
2
+ import Conf from 'conf';
3
+ import { listApps, appInfo, createApp, updateApp, deleteApp } from './commands/apps.js';
4
+ import { listSites, createSite, deleteSite, infoSite } from './commands/sites.js';
5
+ import {
6
+ listFiles, makeDirectory, renameFileOrDirectory,
7
+ removeFileOrDirectory, emptyTrash, changeDirectory, showCwd,
8
+ getInfo, getDiskUsage, createFile, readFile, uploadFile,
9
+ downloadFile, copyFile, syncDirectory, editFile
10
+ } from './commands/files.js';
11
+ import { getUserInfo, getUsageInfo, login } from './commands/auth.js';
12
+ import { deploy } from './commands/deploy.js';
13
+ import { PROJECT_NAME, API_BASE, getHeaders } from './commons.js';
14
+ import inquirer from 'inquirer';
15
+ import { exec } from 'node:child_process';
16
+ import { parseArgs, getSystemEditor } from './utils.js';
17
+ import { rl } from './commands/shell.js';
18
+ import { showLast } from './modules/ErrorModule.js'
19
+
20
+ const config = new Conf({ projectName: PROJECT_NAME });
21
+
22
+ // History of commands
23
+ const commandHistory = [];
24
+
25
+ /**
26
+ * Update the prompt function
27
+ * @returns The current prompt
28
+ */
29
+ export function getPrompt() {
30
+ return chalk.cyan(`puter@${config.get('cwd').slice(1)}> `);
31
+ }
32
+
33
+ const commands = {
34
+ help: showHelp,
35
+ exit: () => process.exit(0),
36
+ logout: async () => {
37
+ await import('./commands/auth.js').then(m => m.logout());
38
+ process.exit(0);
39
+ },
40
+ login: login,
41
+ whoami: getUserInfo,
42
+ stat: getInfo,
43
+ apps: async (args) => {
44
+ await listApps({
45
+ statsPeriod: args[0] || 'all'
46
+ });
47
+ },
48
+ app: appInfo,
49
+ history: async (args) => {
50
+ const lineNumber = parseInt(args[0]);
51
+
52
+ if (isNaN(lineNumber)) {
53
+ // Display full history
54
+ commandHistory.forEach((command, index) => {
55
+ console.log(chalk.cyan(`${index + 1}: ${command}`));
56
+ });
57
+ } else {
58
+ // Copy the command at the specified line number
59
+ if (lineNumber < 1 || lineNumber > commandHistory.length) {
60
+ console.error(chalk.red(`Invalid line number. History has ${commandHistory.length} entries.`));
61
+ return;
62
+ }
63
+
64
+ const commandToCopy = commandHistory[lineNumber - 1];
65
+ // Simulate typing the command in the shell
66
+ rl.write(commandToCopy);
67
+ }
68
+ },
69
+ 'last-error': showLast,
70
+ 'app:create': async (rawArgs) => {
71
+ try {
72
+ const args = parseArgs(rawArgs.join(' '));
73
+ // Consider using explicit argument definition if necessary
74
+ // const args = parseArgs(rawArgs.join(' '), {string: ['description', 'url'],
75
+ // alias: { d: 'description', u: 'url', },
76
+ // });
77
+
78
+ // NOTE: Keep the check for now at the function level, move the check here so in the future we'll use the function for non-interactive command mode.
79
+ await createApp({
80
+ name: args._[0],
81
+ directory: args._[1] || '',
82
+ description: args.description || '',
83
+ url: args.url || 'https://dev-center.puter.com/coming-soon.html'
84
+ });
85
+ } catch (error) {
86
+ console.error(chalk.red(error.message));
87
+ }
88
+ },
89
+ 'app:update': async (args) => {
90
+ if (args.length < 1) {
91
+ console.log(chalk.red('Usage: app:update <name> <remote_dir>'));
92
+ return;
93
+ }
94
+ await updateApp(args);
95
+ },
96
+ 'app:delete': async (rawArgs) => {
97
+ const args = parseArgs(rawArgs.join(' '), {
98
+ string: ['_'],
99
+ boolean: ['f'],
100
+ configuration: {
101
+ 'populate--': true
102
+ }
103
+ });
104
+ if (args._.length < 1) {
105
+ console.log(chalk.red('You must specify the app name:'));
106
+ console.log(chalk.yellow('Example: app:delete <name>'));
107
+ return;
108
+ }
109
+ const name = args._[0];
110
+ const force = !!args.f;
111
+
112
+ if (!force) {
113
+ const { confirm } = await inquirer.prompt([
114
+ {
115
+ type: 'confirm',
116
+ name: 'confirm',
117
+ message: chalk.yellow(`Are you sure you want to delete "${name}"?`),
118
+ default: false
119
+ }
120
+ ]);
121
+ if (!confirm) {
122
+ console.log(chalk.yellow('Operation cancelled.'));
123
+ return false;
124
+ }
125
+ }
126
+ await deleteApp(name);
127
+ },
128
+ ls: listFiles,
129
+ cd: async (args) => {
130
+ await changeDirectory(args);
131
+ },
132
+ pwd: showCwd,
133
+ mkdir: makeDirectory,
134
+ mv: renameFileOrDirectory,
135
+ rm: removeFileOrDirectory,
136
+ // rmdir: deleteFolder, // Not implemented in Puter API
137
+ clean: emptyTrash,
138
+ df: getDiskUsage,
139
+ usage: getUsageInfo,
140
+ cp: copyFile,
141
+ touch: createFile,
142
+ cat: readFile,
143
+ push: uploadFile,
144
+ pull: downloadFile,
145
+ update: syncDirectory,
146
+ edit: editFile,
147
+ sites: listSites,
148
+ site: infoSite,
149
+ 'site:delete': deleteSite,
150
+ 'site:create': createSite,
151
+ 'site:deploy': deploy,
152
+ };
153
+
154
+ /**
155
+ * Execute a command
156
+ * @param {string} input The command line input
157
+ */
158
+ export async function execCommand(input) {
159
+ const [cmd, ...args] = input ? input.split(' ') : [];
160
+
161
+ // Add the command to history (skip the "history" command itself)
162
+ if (cmd !== 'history') {
163
+ commandHistory.push(input);
164
+ }
165
+
166
+ if (cmd === 'help') {
167
+ // Handle help command
168
+ const command = args[0];
169
+ showHelp(command);
170
+ return;
171
+ }
172
+ if (cmd.startsWith('!')) {
173
+ // Execute the command on the host machine
174
+ const hostCommand = input.slice(1); // Remove the "!"
175
+ exec(hostCommand, (error, stdout, stderr) => {
176
+ if (error) {
177
+ console.error(chalk.red(`Host Error: ${error.message}`));
178
+ return;
179
+ }
180
+ if (stderr) {
181
+ console.error(chalk.red(stderr));
182
+ return;
183
+ }
184
+ console.log(stdout);
185
+ console.log(chalk.green(`Press <Enter> to return.`));
186
+ });
187
+ return;
188
+ }
189
+ if (commands[cmd]) {
190
+ try {
191
+ await commands[cmd](args);
192
+ } catch (error) {
193
+ console.error(chalk.red(`Error executing command: ${error.message}`));
194
+ }
195
+ return;
196
+ }
197
+
198
+ if (!['Y', 'N'].includes(cmd.toUpperCase()[0])) {
199
+ console.log(chalk.red(`Unknown command: ${cmd}`));
200
+ showHelp();
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Display help for a specific command or general help if no command is provided.
206
+ * @param {string} [command] - The command to display help for.
207
+ */
208
+ function showHelp(command) {
209
+ // Consider using `program.helpInformation()` function for global "help" command...
210
+ const commandHelp = {
211
+ help: `
212
+ ${chalk.cyan('help [command]')}
213
+ Display help for a specific command or show general help.
214
+ Example: help ls
215
+ `,
216
+ exit: `
217
+ ${chalk.cyan('exit')}
218
+ Exit the shell.
219
+ `,
220
+ logout: `
221
+ ${chalk.cyan('logout')}
222
+ Logout from Puter account.
223
+ `,
224
+ whoami: `
225
+ ${chalk.cyan('whoami')}
226
+ Show user information.
227
+ `,
228
+ stat: `
229
+ ${chalk.cyan('stat <path>')}
230
+ Show file or directory information.
231
+ Example: stat /path/to/file
232
+ `,
233
+ df: `
234
+ ${chalk.cyan('df')}
235
+ Show disk usage information.
236
+ `,
237
+ usage: `
238
+ ${chalk.cyan('usage')}
239
+ Show usage information.
240
+ `,
241
+ apps: `
242
+ ${chalk.cyan('apps [period]')}
243
+ List all your apps.
244
+ period: today, yesterday, 7d, 30d, this_month, last_month
245
+ Example: apps today
246
+ `,
247
+ app: `
248
+ ${chalk.cyan('app <app_name>')}
249
+ Get application information.
250
+ Example: app myapp
251
+ `,
252
+ 'app:create': `
253
+ ${chalk.cyan('app:create <name> <remote_dir>')}
254
+ Create a new app.
255
+ Example: app:create myapp https://myapp.puter.site
256
+ `,
257
+ 'app:update': `
258
+ ${chalk.cyan('app:update <name> [dir]')}
259
+ Update an app.
260
+ Example: app:update myapp .
261
+ `,
262
+ 'app:delete': `
263
+ ${chalk.cyan('app:delete <name>')}
264
+ Delete an app.
265
+ Example: app:delete myapp
266
+ `,
267
+ ls: `
268
+ ${chalk.cyan('ls [dir]')}
269
+ List files and directories.
270
+ Example: ls /path/to/dir
271
+ `,
272
+ cd: `
273
+ ${chalk.cyan('cd [dir]')}
274
+ Change the current working directory.
275
+ Example: cd /path/to/dir
276
+ `,
277
+ pwd: `
278
+ ${chalk.cyan('pwd')}
279
+ Print the current working directory.
280
+ `,
281
+ mkdir: `
282
+ ${chalk.cyan('mkdir <dir>')}
283
+ Create a new directory.
284
+ Example: mkdir /path/to/newdir
285
+ `,
286
+ mv: `
287
+ ${chalk.cyan('mv <src> <dest>')}
288
+ Move or rename a file or directory.
289
+ Example: mv /path/to/src /path/to/dest
290
+ `,
291
+ rm: `
292
+ ${chalk.cyan('rm <file>')}
293
+ Move a file or directory to the system's Trash.
294
+ Example: rm /path/to/file
295
+ `,
296
+ clean: `
297
+ ${chalk.cyan('clean')}
298
+ Empty the system's Trash.
299
+ `,
300
+ cp: `
301
+ ${chalk.cyan('cp <src> <dest>')}
302
+ Copy files or directories.
303
+ Example: cp /path/to/src /path/to/dest
304
+ `,
305
+ touch: `
306
+ ${chalk.cyan('touch <file>')}
307
+ Create a new empty file.
308
+ Example: touch /path/to/file
309
+ `,
310
+ cat: `
311
+ ${chalk.cyan('cat <file>')}
312
+ Output file content to the console.
313
+ Example: cat /path/to/file
314
+ `,
315
+ push: `
316
+ ${chalk.cyan('push <file>')}
317
+ Upload file to Puter cloud.
318
+ Example: push /path/to/file
319
+ `,
320
+ pull: `
321
+ ${chalk.cyan('pull <file>')}
322
+ Download file from Puter cloud.
323
+ Example: pull /path/to/file
324
+ `,
325
+ update: `
326
+ ${chalk.cyan('update <src> <dest> [--delete] [-r]')}
327
+ Sync local directory with remote cloud.
328
+ Example: update /local/path /remote/path
329
+ `,
330
+ edit: `
331
+ ${chalk.cyan('edit <file>')}
332
+ Edit a remote file using your local text editor.
333
+ Example: edit /path/to/file
334
+
335
+ System editor: ${chalk.green(getSystemEditor())}
336
+ `,
337
+ sites: `
338
+ ${chalk.cyan('sites')}
339
+ List sites and subdomains.
340
+ `,
341
+ site: `
342
+ ${chalk.cyan('site <site_uid>')}
343
+ Get site information by UID.
344
+ Example: site sd-123456
345
+ `,
346
+ 'site:delete': `
347
+ ${chalk.cyan('site:delete <uid>')}
348
+ Delete a site by UID.
349
+ Example: site:delete sd-123456
350
+ `,
351
+ 'site:create': `
352
+ ${chalk.cyan('site:create <app_name> [<dir>] [--subdomain=<name>]')}
353
+ Create a static website from directory.
354
+ Example: site:create mywebsite /path/to/dir --subdomain=mywebsite
355
+ `,
356
+ 'site:deploy': `
357
+ ${chalk.cyan('site:deploy [<remote_dir>] [--subdomain=<subdomain>]')}
358
+ Deploy a local web project to Puter.
359
+ Example: site:deploy ./my-app --subdomain my-app
360
+ `,
361
+ '!': `
362
+ ${chalk.cyan('!<command>')}
363
+ Execute a command on the host machine.
364
+ Example: !ls -la
365
+ `,
366
+ 'history [line]': `
367
+ ${chalk.cyan('history [line]')}
368
+ Display history of commands or copy command by line number
369
+ Example: history 2
370
+ `,
371
+ };
372
+
373
+ if (command && commandHelp[command]) {
374
+ console.log(chalk.yellow(`\nHelp for command: ${chalk.cyan(command)}`));
375
+ console.log(commandHelp[command]);
376
+ } else if (command) {
377
+ console.log(chalk.red(`Unknown command: ${command}`));
378
+ console.log(chalk.yellow('Use "help" to see a list of available commands.'));
379
+ } else {
380
+ console.log(chalk.yellow('\nAvailable commands:'));
381
+ for (const cmd in commandHelp) {
382
+ console.log(chalk.cyan(cmd.padEnd(20)) + '- ' + commandHelp[cmd].split('\n')[2].trim());
383
+ }
384
+ console.log(chalk.yellow('\nUse "help <command>" for detailed help on a specific command.'));
385
+ }
386
+ }
@@ -0,0 +1,20 @@
1
+ export const ERROR_BUFFER_LIMIT = 20;
2
+
3
+ export const errors = [];
4
+
5
+ export const report = (error) => {
6
+ errors.push(error);
7
+ if (errors.length > ERROR_BUFFER_LIMIT) {
8
+ errors.splice(0, errors.length - ERROR_BUFFER_LIMIT)
9
+ }
10
+ }
11
+ export const showLast = () => {
12
+ // Print the last error from the error history,
13
+ // and remove it from the history
14
+ const err = errors.pop();
15
+ if (err) {
16
+ console.error(err);
17
+ } else {
18
+ console.log('No errors to report');
19
+ }
20
+ }