@heyputer/shell 2.1.0 → 3.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/.github/workflows/npm-build.yml +0 -2
- package/.github/workflows/npm-publish.yml +0 -4
- package/bin/index.js +22 -202
- package/package.json +3 -5
- package/src/commands/auth.js +7 -12
- package/src/commands/files.js +16 -18
- package/src/commands/shell.js +5 -3
- package/src/commons.js +81 -256
- package/src/executor.js +12 -129
- package/src/modules/ErrorModule.js +62 -1
- package/src/modules/ProfileModule.js +154 -38
- package/src/utils.js +0 -91
- package/tests/ErrorModule.test.js +73 -1
- package/tests/ProfileModule.test.js +58 -44
- package/tests/commons.test.js +66 -124
- package/tests/executor.test.js +8 -0
- package/tests/files.test.js +23 -3
- package/tests/shell.test.js +2 -1
- package/tests/utils.test.js +1 -141
- package/src/commands/apps.js +0 -356
- package/src/commands/deploy.js +0 -54
- package/src/commands/init.js +0 -322
- package/src/commands/sites.js +0 -169
- package/src/commands/subdomains.js +0 -95
- package/tests/apps.test.js +0 -194
- package/tests/deploy.test.js +0 -84
- package/tests/sites.test.js +0 -67
- package/tests/subdomains.test.js +0 -90
|
@@ -15,8 +15,6 @@ jobs:
|
|
|
15
15
|
- uses: actions/checkout@v4
|
|
16
16
|
- name: Install pnpm
|
|
17
17
|
uses: pnpm/action-setup@v4
|
|
18
|
-
with:
|
|
19
|
-
version: 8
|
|
20
18
|
- name: Use Node.js 20
|
|
21
19
|
uses: actions/setup-node@v4
|
|
22
20
|
with:
|
|
@@ -34,8 +32,6 @@ jobs:
|
|
|
34
32
|
- uses: actions/checkout@v4
|
|
35
33
|
- name: Install pnpm
|
|
36
34
|
uses: pnpm/action-setup@v4
|
|
37
|
-
with:
|
|
38
|
-
version: 8
|
|
39
35
|
- name: Use Node.js 20
|
|
40
36
|
uses: actions/setup-node@v4
|
|
41
37
|
with:
|
package/bin/index.js
CHANGED
|
@@ -2,21 +2,29 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { login, logout } from '../src/commands/auth.js';
|
|
5
|
-
import { init } from '../src/commands/init.js';
|
|
6
5
|
import { startShell } from '../src/commands/shell.js';
|
|
7
6
|
import { PROJECT_NAME, getLatestVersion } from '../src/commons.js';
|
|
8
|
-
import {
|
|
9
|
-
import inquirer from 'inquirer';
|
|
10
|
-
import { initProfileModule, getProfileModule } from '../src/modules/ProfileModule.js';
|
|
7
|
+
import { initProfileModule } from '../src/modules/ProfileModule.js';
|
|
11
8
|
import { initPuterModule } from '../src/modules/PuterModule.js';
|
|
12
|
-
import {
|
|
9
|
+
import { formatError, isAuthError, report } from '../src/modules/ErrorModule.js';
|
|
10
|
+
|
|
11
|
+
// puter.js attaches an async 'load' listener to its XHRs, so a failed request
|
|
12
|
+
// rejects twice: once on the promise we await, and once on a promise nothing
|
|
13
|
+
// holds. Without this guard Node kills the CLI and prints the rejected plain
|
|
14
|
+
// object as "#<Object>". Auth failures are already surfaced with a re-login
|
|
15
|
+
// prompt by checkLogin(), so only report what is not handled elsewhere.
|
|
16
|
+
process.on('unhandledRejection', (error) => {
|
|
17
|
+
report(error);
|
|
18
|
+
if (!isAuthError(error)) {
|
|
19
|
+
console.error(chalk.red(`Error: ${formatError(error)}`));
|
|
20
|
+
console.error(chalk.dim('Run "last-error" in the shell for the full details.'));
|
|
21
|
+
}
|
|
22
|
+
});
|
|
13
23
|
|
|
14
24
|
async function main() {
|
|
15
25
|
initProfileModule();
|
|
16
26
|
initPuterModule();
|
|
17
27
|
|
|
18
|
-
const profileModule = getProfileModule();
|
|
19
|
-
|
|
20
28
|
const version = await getLatestVersion(PROJECT_NAME);
|
|
21
29
|
|
|
22
30
|
const program = new Command();
|
|
@@ -45,206 +53,12 @@ async function main() {
|
|
|
45
53
|
process.exit(0);
|
|
46
54
|
});
|
|
47
55
|
|
|
48
|
-
program
|
|
49
|
-
.command('init')
|
|
50
|
-
.description('Initialize a new Puter app')
|
|
51
|
-
.action(init);
|
|
52
|
-
|
|
53
56
|
program
|
|
54
57
|
.command('shell')
|
|
55
58
|
.description('Start interactive shell')
|
|
56
59
|
.action(() => startShell());
|
|
57
60
|
|
|
58
61
|
|
|
59
|
-
// App commands
|
|
60
|
-
program
|
|
61
|
-
.command('apps')
|
|
62
|
-
.description('List all your apps')
|
|
63
|
-
.argument('[period]', 'period: today, yesterday, 7d, 30d, this_month, last_month')
|
|
64
|
-
.action(async (period) => {
|
|
65
|
-
await profileModule.checkLogin();
|
|
66
|
-
await listApps({
|
|
67
|
-
statsPeriod: period || 'all'
|
|
68
|
-
});
|
|
69
|
-
process.exit(0);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const app = program
|
|
73
|
-
.command('app')
|
|
74
|
-
.description('App management commands');
|
|
75
|
-
|
|
76
|
-
app
|
|
77
|
-
.command('info')
|
|
78
|
-
.description('Get application information')
|
|
79
|
-
.argument('<app_name>', 'Name of the application')
|
|
80
|
-
.action(async (app_name) => {
|
|
81
|
-
await profileModule.checkLogin();
|
|
82
|
-
await appInfo([app_name]);
|
|
83
|
-
process.exit(0);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
app
|
|
87
|
-
.command('create')
|
|
88
|
-
.description('Create a new app')
|
|
89
|
-
.argument('<name>', 'Name of the application')
|
|
90
|
-
.argument('<remote_dir>', 'Remote directory URL')
|
|
91
|
-
.action(async (name, remote_dir) => {
|
|
92
|
-
try {
|
|
93
|
-
await profileModule.checkLogin();
|
|
94
|
-
await createApp({
|
|
95
|
-
name: name,
|
|
96
|
-
directory: remote_dir || '',
|
|
97
|
-
description: '',
|
|
98
|
-
url: 'https://dev-center.puter.com/coming-soon.html'
|
|
99
|
-
});
|
|
100
|
-
} catch (error) {
|
|
101
|
-
console.error(chalk.red(error.message));
|
|
102
|
-
}
|
|
103
|
-
process.exit(0);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
app
|
|
107
|
-
.command('update')
|
|
108
|
-
.description('Update an app')
|
|
109
|
-
.argument('<name>', 'Name of the application')
|
|
110
|
-
.argument('[dir]', 'Directory path', '.')
|
|
111
|
-
.action(async (name, dir) => {
|
|
112
|
-
await profileModule.checkLogin();
|
|
113
|
-
await updateApp([name, dir]);
|
|
114
|
-
process.exit(0);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
app
|
|
118
|
-
.command('delete')
|
|
119
|
-
.description('Delete an app')
|
|
120
|
-
.argument('<name>', 'Name of the application')
|
|
121
|
-
.option('-f, --force', 'Force deletion without confirmation')
|
|
122
|
-
.action(async (name, options) => {
|
|
123
|
-
await profileModule.checkLogin();
|
|
124
|
-
let shouldDelete = options.force;
|
|
125
|
-
|
|
126
|
-
if (!shouldDelete) {
|
|
127
|
-
const answer = await inquirer.prompt([
|
|
128
|
-
{
|
|
129
|
-
type: 'confirm',
|
|
130
|
-
name: 'confirm',
|
|
131
|
-
message: `Are you sure you want to delete the app "${name}"?`,
|
|
132
|
-
default: false
|
|
133
|
-
}
|
|
134
|
-
]);
|
|
135
|
-
shouldDelete = answer.confirm;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (shouldDelete) {
|
|
139
|
-
await deleteApp(name);
|
|
140
|
-
} else {
|
|
141
|
-
console.log(chalk.yellow('App deletion cancelled.'));
|
|
142
|
-
}
|
|
143
|
-
process.exit(0);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
program
|
|
147
|
-
.command('sites')
|
|
148
|
-
.description('List sites and subdomains')
|
|
149
|
-
.action(async () => {
|
|
150
|
-
await profileModule.checkLogin();
|
|
151
|
-
await listSites();
|
|
152
|
-
process.exit(0);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
const site = program
|
|
156
|
-
.command('site')
|
|
157
|
-
.description('Site management commands');
|
|
158
|
-
|
|
159
|
-
site
|
|
160
|
-
.command('info')
|
|
161
|
-
.description('Get site information by UID')
|
|
162
|
-
.argument('<site_uid>', 'Site UID')
|
|
163
|
-
.action(async (site_uid) => {
|
|
164
|
-
await profileModule.checkLogin();
|
|
165
|
-
await infoSite([site_uid]);
|
|
166
|
-
process.exit(0);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
site
|
|
170
|
-
.command('create')
|
|
171
|
-
.description('Create a static website from directory')
|
|
172
|
-
.argument('<app_name>', 'Application name')
|
|
173
|
-
.argument('[dir]', 'Directory path')
|
|
174
|
-
.option('--subdomain <name>', 'Subdomain name')
|
|
175
|
-
.action(async (app_name, dir, options) => {
|
|
176
|
-
await profileModule.checkLogin();
|
|
177
|
-
const args = [app_name];
|
|
178
|
-
if (dir) args.push(dir);
|
|
179
|
-
if (options.subdomain) args.push(`--subdomain=${options.subdomain}`)
|
|
180
|
-
|
|
181
|
-
await createSite(args)
|
|
182
|
-
process.exit(0);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
site
|
|
186
|
-
.command('deploy')
|
|
187
|
-
.description('Deploy a local web project to Puter')
|
|
188
|
-
.argument('[local_dir]', 'Local directory path')
|
|
189
|
-
.argument('[subdomain]', 'Deployment subdomain (<subdomain>.puter.site)')
|
|
190
|
-
.action(async (local_dir, subdomain) => {
|
|
191
|
-
await profileModule.checkLogin();
|
|
192
|
-
if (!local_dir) {
|
|
193
|
-
const answer = await inquirer.prompt([
|
|
194
|
-
{
|
|
195
|
-
type: 'input',
|
|
196
|
-
name: 'local_dir',
|
|
197
|
-
message: 'Local directory path:',
|
|
198
|
-
default: '.'
|
|
199
|
-
}
|
|
200
|
-
]);
|
|
201
|
-
local_dir = answer.local_dir;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (!subdomain) {
|
|
205
|
-
const answer = await inquirer.prompt([
|
|
206
|
-
{
|
|
207
|
-
type: 'input',
|
|
208
|
-
name: 'subdomain',
|
|
209
|
-
message: 'Deployment subdomain (leave empty for random):',
|
|
210
|
-
}
|
|
211
|
-
]);
|
|
212
|
-
subdomain = answer.subdomain;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
await startShell(`site:deploy ${local_dir}${subdomain ? ` --subdomain=${subdomain}` : ''}`)
|
|
216
|
-
process.exit(0);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
site
|
|
220
|
-
.command('delete')
|
|
221
|
-
.description('Delete a site by UID')
|
|
222
|
-
.argument('<uid>', 'Site UID')
|
|
223
|
-
.option('-f, --force', 'Force deletion without confirmation')
|
|
224
|
-
.action(async (uid, options) => {
|
|
225
|
-
await profileModule.checkLogin();
|
|
226
|
-
let shouldDelete = options.force;
|
|
227
|
-
|
|
228
|
-
if (!shouldDelete) {
|
|
229
|
-
const answer = await inquirer.prompt([
|
|
230
|
-
{
|
|
231
|
-
type: 'confirm',
|
|
232
|
-
name: 'confirm',
|
|
233
|
-
message: `Are you sure you want to delete the site with UID "${uid}"?`,
|
|
234
|
-
default: false
|
|
235
|
-
}
|
|
236
|
-
]);
|
|
237
|
-
shouldDelete = answer.confirm;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (shouldDelete) {
|
|
241
|
-
await deleteSite([uid]);
|
|
242
|
-
} else {
|
|
243
|
-
console.log(chalk.yellow('Site deletion cancelled.'));
|
|
244
|
-
}
|
|
245
|
-
process.exit(0);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
62
|
if (process.argv.length === 2) {
|
|
249
63
|
startShell();
|
|
250
64
|
} else {
|
|
@@ -253,6 +67,12 @@ async function main() {
|
|
|
253
67
|
}
|
|
254
68
|
|
|
255
69
|
main().catch((err) => {
|
|
256
|
-
|
|
70
|
+
report(err);
|
|
71
|
+
if (isAuthError(err)) {
|
|
72
|
+
console.error(chalk.red('Your session has expired or its token is no longer valid.'));
|
|
73
|
+
console.error(chalk.cyan('Run "puter login" to sign in again.'));
|
|
74
|
+
} else {
|
|
75
|
+
console.error(chalk.red(`Error: ${formatError(err)}`));
|
|
76
|
+
}
|
|
257
77
|
process.exit(1);
|
|
258
78
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyputer/shell",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "SSH-style shell access to your Puter files",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"engines": {
|
|
20
20
|
"node": ">=20.0.0"
|
|
21
21
|
},
|
|
22
|
+
"packageManager": "pnpm@9.9.0",
|
|
22
23
|
"keywords": [
|
|
23
24
|
"puter",
|
|
24
25
|
"shell",
|
|
@@ -29,18 +30,15 @@
|
|
|
29
30
|
"dependencies": {
|
|
30
31
|
"@heyputer/puter.js": "^2.2.8",
|
|
31
32
|
"chalk": "^5.3.0",
|
|
32
|
-
"cli-table3": "^0.6.5",
|
|
33
33
|
"commander": "^13.0.0",
|
|
34
34
|
"conf": "^12.0.0",
|
|
35
|
-
"cross-spawn": "^7.0.3",
|
|
36
35
|
"dotenv": "^16.4.7",
|
|
37
36
|
"glob": "^11.0.0",
|
|
38
37
|
"inquirer": "^9.2.12",
|
|
39
38
|
"minimatch": "^10.0.1",
|
|
40
39
|
"node-fetch": "^3.3.2",
|
|
41
40
|
"ora": "^8.0.1",
|
|
42
|
-
"uuid": "^11.0.5"
|
|
43
|
-
"yargs-parser": "^21.1.1"
|
|
41
|
+
"uuid": "^11.0.5"
|
|
44
42
|
},
|
|
45
43
|
"devDependencies": {
|
|
46
44
|
"@vitest/coverage-v8": "2.1.8",
|
package/src/commands/auth.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import Conf from 'conf';
|
|
3
3
|
import ora from 'ora';
|
|
4
|
-
import { PROJECT_NAME, } from '../commons.js'
|
|
4
|
+
import { HOME, PROJECT_NAME, expandHome } from '../commons.js'
|
|
5
5
|
import { getProfileModule } from '../modules/ProfileModule.js';
|
|
6
6
|
import { getPuter } from '../modules/PuterModule.js';
|
|
7
7
|
const config = new Conf({ projectName: PROJECT_NAME });
|
|
@@ -29,18 +29,10 @@ export async function logout() {
|
|
|
29
29
|
let spinner;
|
|
30
30
|
try {
|
|
31
31
|
spinner = ora('Logging out from Puter...').start();
|
|
32
|
-
const token = config.get('auth_token');
|
|
33
32
|
const selected_profile = config.get('selected_profile');
|
|
34
33
|
|
|
35
|
-
if (
|
|
36
|
-
// legacy auth
|
|
37
|
-
config.clear();
|
|
38
|
-
spinner.succeed(chalk.green('Successfully logged out from Puter!'));
|
|
39
|
-
} else if (selected_profile) {
|
|
40
|
-
// multi profile auth
|
|
34
|
+
if (selected_profile) {
|
|
41
35
|
config.delete('selected_profile');
|
|
42
|
-
config.delete('username');
|
|
43
|
-
config.delete('cwd');
|
|
44
36
|
|
|
45
37
|
const profiles = config.get('profiles');
|
|
46
38
|
config.set('profiles', profiles.filter(profile => profile.uuid != selected_profile));
|
|
@@ -86,7 +78,10 @@ export async function getUserInfo() {
|
|
|
86
78
|
}
|
|
87
79
|
}
|
|
88
80
|
export function isAuthenticated() {
|
|
89
|
-
|
|
81
|
+
const uuid = config.get('selected_profile');
|
|
82
|
+
if (!uuid) return false;
|
|
83
|
+
const profiles = config.get('profiles') ?? [];
|
|
84
|
+
return !!profiles.find(p => p.uuid === uuid)?.token;
|
|
90
85
|
}
|
|
91
86
|
|
|
92
87
|
export function getAuthToken() {
|
|
@@ -100,7 +95,7 @@ export function getCurrentUserName() {
|
|
|
100
95
|
}
|
|
101
96
|
|
|
102
97
|
export function getCurrentDirectory() {
|
|
103
|
-
return
|
|
98
|
+
return expandHome(getProfileModule().getCwd());
|
|
104
99
|
}
|
|
105
100
|
|
|
106
101
|
/**
|
package/src/commands/files.js
CHANGED
|
@@ -7,10 +7,10 @@ import { minimatch } from 'minimatch';
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import Conf from 'conf';
|
|
9
9
|
import fetch from 'node-fetch';
|
|
10
|
-
import { API_BASE, BASE_URL, PROJECT_NAME, getHeaders, showDiskSpaceUsage, resolvePath, resolveRemotePath } from '../commons.js';
|
|
10
|
+
import { API_BASE, BASE_URL, HOME, PROJECT_NAME, expandHome, getHeaders, isAbsolutePath, showDiskSpaceUsage, resolvePath, resolveRemotePath } from '../commons.js';
|
|
11
11
|
import { formatDateTime, formatSize, getSystemEditor } from '../utils.js';
|
|
12
12
|
import inquirer from 'inquirer';
|
|
13
|
-
import { getAuthToken, getCurrentDirectory
|
|
13
|
+
import { getAuthToken, getCurrentDirectory } from './auth.js';
|
|
14
14
|
import { updatePrompt } from './shell.js';
|
|
15
15
|
import crypto from '../crypto.js';
|
|
16
16
|
import { getPuter } from '../modules/PuterModule.js';
|
|
@@ -85,7 +85,7 @@ export async function makeDirectory(args = []) {
|
|
|
85
85
|
const puter = getPuter();
|
|
86
86
|
|
|
87
87
|
try {
|
|
88
|
-
const data = await puter.fs.mkdir(
|
|
88
|
+
const data = await puter.fs.mkdir(resolvePath(getCurrentDirectory(), directoryName), {
|
|
89
89
|
overwrite: false,
|
|
90
90
|
dedupeName: true,
|
|
91
91
|
createMissingParents: false
|
|
@@ -314,7 +314,7 @@ export async function removeFileOrDirectory(args = []) {
|
|
|
314
314
|
const uid = statData.uid;
|
|
315
315
|
|
|
316
316
|
// Step 4.2: Perform the move operation to Trash
|
|
317
|
-
const moveData = await puter.fs.move(uid,
|
|
317
|
+
const moveData = await puter.fs.move(uid, expandHome(`${HOME}/Trash`), {
|
|
318
318
|
overwrite: false,
|
|
319
319
|
newName: uid,
|
|
320
320
|
createMissingParents: false,
|
|
@@ -385,7 +385,7 @@ export async function deleteFolder(folderPath, skipConfirmation = false) {
|
|
|
385
385
|
* @param {boolean} skipConfirmation - Whether to skip the confirmation prompt.
|
|
386
386
|
*/
|
|
387
387
|
export async function emptyTrash(skipConfirmation = true) {
|
|
388
|
-
const trashPath =
|
|
388
|
+
const trashPath = expandHome(`${HOME}/Trash`);
|
|
389
389
|
await deleteFolder(trashPath, skipConfirmation);
|
|
390
390
|
}
|
|
391
391
|
|
|
@@ -398,7 +398,7 @@ export async function getInfo(args = []) {
|
|
|
398
398
|
const puter = getPuter();
|
|
399
399
|
for (let name of names)
|
|
400
400
|
try {
|
|
401
|
-
name =
|
|
401
|
+
name = resolvePath(getCurrentDirectory(), name);
|
|
402
402
|
console.log(chalk.green(`Getting stat info for: "${name}"...\n`));
|
|
403
403
|
const data = await puter.fs.stat(name);
|
|
404
404
|
if (data) {
|
|
@@ -425,7 +425,7 @@ export async function getInfo(args = []) {
|
|
|
425
425
|
* Show the current working directory
|
|
426
426
|
*/
|
|
427
427
|
export async function showCwd() {
|
|
428
|
-
console.log(chalk.green(`${
|
|
428
|
+
console.log(chalk.green(`${getCurrentDirectory()}`));
|
|
429
429
|
}
|
|
430
430
|
|
|
431
431
|
/**
|
|
@@ -434,7 +434,7 @@ export async function showCwd() {
|
|
|
434
434
|
* @returns void
|
|
435
435
|
*/
|
|
436
436
|
export async function changeDirectory(args) {
|
|
437
|
-
let currentPath =
|
|
437
|
+
let currentPath = getCurrentDirectory();
|
|
438
438
|
// If no arguments, print the current directory
|
|
439
439
|
if (!args.length) {
|
|
440
440
|
console.log(chalk.green(currentPath));
|
|
@@ -443,17 +443,15 @@ export async function changeDirectory(args) {
|
|
|
443
443
|
const puter = getPuter();
|
|
444
444
|
|
|
445
445
|
const path = args[0];
|
|
446
|
-
//
|
|
447
|
-
const newPath =
|
|
446
|
+
// resolvePath handles "/", "~", "~/...", "." and ".." in one place.
|
|
447
|
+
const newPath = resolvePath(currentPath, path);
|
|
448
448
|
try {
|
|
449
449
|
// Check if the new path is a valid directory
|
|
450
450
|
const data = await puter.fs.stat(newPath);
|
|
451
451
|
if (data && data.is_dir) {
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
arrayDirs.push(data.name);
|
|
456
|
-
updatePrompt(arrayDirs.join('/')); // Update the shell prompt
|
|
452
|
+
// Adopt the server's canonical path, which is already rooted at the
|
|
453
|
+
// account's current home directory.
|
|
454
|
+
updatePrompt(data.path || newPath); // Update the shell prompt
|
|
457
455
|
} else {
|
|
458
456
|
console.log(chalk.red(`"${newPath}" is not a directory`));
|
|
459
457
|
}
|
|
@@ -519,7 +517,7 @@ export async function createFile(args = []) {
|
|
|
519
517
|
const filePath = args[0]; // File path (e.g., "app/index.html")
|
|
520
518
|
const content = args.length > 1 ? args.slice(1).join(' ') : ''; // Optional content
|
|
521
519
|
let fullPath = filePath;
|
|
522
|
-
if (!filePath
|
|
520
|
+
if (!isAbsolutePath(filePath)) {
|
|
523
521
|
fullPath = resolvePath(getCurrentDirectory(), filePath); // Resolve the full path
|
|
524
522
|
}
|
|
525
523
|
const dirName = path.dirname(fullPath); // Extract the directory name
|
|
@@ -763,8 +761,8 @@ export async function copyFile(args = []) {
|
|
|
763
761
|
return;
|
|
764
762
|
}
|
|
765
763
|
|
|
766
|
-
const sourcePath =
|
|
767
|
-
const destinationPath =
|
|
764
|
+
const sourcePath = resolvePath(getCurrentDirectory(), args[0]); // Resolve the source path
|
|
765
|
+
const destinationPath = resolvePath(getCurrentDirectory(), args[1]); // Resolve the destination path
|
|
768
766
|
|
|
769
767
|
console.log(chalk.green(`Copy: "${chalk.dim(sourcePath)}" to: "${chalk.dim(destinationPath)}"...\n`));
|
|
770
768
|
const puter = getPuter();
|
package/src/commands/shell.js
CHANGED
|
@@ -4,6 +4,7 @@ import Conf from 'conf';
|
|
|
4
4
|
import { execCommand, getPrompt } from '../executor.js';
|
|
5
5
|
import { PROJECT_NAME } from '../commons.js';
|
|
6
6
|
import { getProfileModule } from '../modules/ProfileModule.js';
|
|
7
|
+
import { report, formatError } from '../modules/ErrorModule.js';
|
|
7
8
|
|
|
8
9
|
const config = new Conf({ projectName: PROJECT_NAME });
|
|
9
10
|
|
|
@@ -13,7 +14,7 @@ export let rl;
|
|
|
13
14
|
* Update the current shell prompt
|
|
14
15
|
*/
|
|
15
16
|
export function updatePrompt(currentPath) {
|
|
16
|
-
|
|
17
|
+
getProfileModule().setCwd(currentPath);
|
|
17
18
|
rl.setPrompt(getPrompt());
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -37,7 +38,7 @@ export async function startShell(command) {
|
|
|
37
38
|
})
|
|
38
39
|
|
|
39
40
|
try {
|
|
40
|
-
console.log(chalk.green('Welcome to Puter
|
|
41
|
+
console.log(chalk.green('Welcome to Puter Shell! Type "help" for available commands.'));
|
|
41
42
|
rl.setPrompt(getPrompt());
|
|
42
43
|
rl.prompt();
|
|
43
44
|
|
|
@@ -47,7 +48,8 @@ export async function startShell(command) {
|
|
|
47
48
|
try {
|
|
48
49
|
await execCommand(trimmedLine);
|
|
49
50
|
} catch (error) {
|
|
50
|
-
|
|
51
|
+
report(error);
|
|
52
|
+
console.error(chalk.red(formatError(error)));
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
rl.prompt();
|