puter-cli 1.8.5 → 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/.github/workflows/npm-build.yml +4 -3
- package/CHANGELOG.md +38 -0
- package/README.md +18 -4
- package/bin/index.js +184 -31
- package/package.json +12 -12
- package/src/commands/apps.js +53 -139
- package/src/commands/auth.js +113 -115
- package/src/commands/deploy.js +29 -27
- package/src/commands/files.js +151 -512
- package/src/commands/shell.js +13 -25
- package/src/commands/sites.js +25 -83
- package/src/commands/subdomains.js +46 -55
- package/src/commons.js +2 -2
- package/src/executor.js +27 -28
- package/src/modules/ErrorModule.js +18 -31
- package/src/modules/ProfileModule.js +183 -123
- package/src/modules/PuterModule.js +30 -0
- package/tests/ErrorModule.test.js +42 -0
- package/tests/ProfileModule.test.js +274 -0
- package/tests/PuterModule.test.js +56 -0
- package/tests/apps.test.js +194 -0
- package/tests/commons.test.js +380 -0
- package/tests/deploy.test.js +84 -0
- package/tests/executor.test.js +52 -0
- package/tests/files.test.js +640 -0
- package/tests/login.test.js +69 -51
- package/tests/shell.test.js +184 -0
- package/tests/sites.test.js +67 -0
- package/tests/subdomains.test.js +90 -0
- package/src/modules/SetContextModule.js +0 -5
- package/src/temporary/context_helpers.js +0 -17
package/src/commands/files.js
CHANGED
|
@@ -13,6 +13,7 @@ import inquirer from 'inquirer';
|
|
|
13
13
|
import { getAuthToken, getCurrentDirectory, getCurrentUserName } from './auth.js';
|
|
14
14
|
import { updatePrompt } from './shell.js';
|
|
15
15
|
import crypto from '../crypto.js';
|
|
16
|
+
import { getPuter } from '../modules/PuterModule.js';
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
const config = new Conf({ projectName: PROJECT_NAME });
|
|
@@ -24,12 +25,8 @@ const config = new Conf({ projectName: PROJECT_NAME });
|
|
|
24
25
|
* @returns List of files found
|
|
25
26
|
*/
|
|
26
27
|
export async function listRemoteFiles(path) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
headers: getHeaders(),
|
|
30
|
-
body: JSON.stringify({ path })
|
|
31
|
-
});
|
|
32
|
-
return await response.json();
|
|
28
|
+
const puter = getPuter();
|
|
29
|
+
return await puter.fs.readdir(path);
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
/**
|
|
@@ -85,20 +82,14 @@ export async function makeDirectory(args = []) {
|
|
|
85
82
|
const directoryName = args[0];
|
|
86
83
|
console.log(chalk.green(`Creating directory "${directoryName}" in "${getCurrentDirectory()}"...\n`));
|
|
87
84
|
|
|
88
|
-
|
|
89
|
-
const response = await fetch(`${API_BASE}/mkdir`, {
|
|
90
|
-
method: 'POST',
|
|
91
|
-
headers: getHeaders(),
|
|
92
|
-
body: JSON.stringify({
|
|
93
|
-
parent: getCurrentDirectory(),
|
|
94
|
-
path: directoryName,
|
|
95
|
-
overwrite: false,
|
|
96
|
-
dedupe_name: true,
|
|
97
|
-
create_missing_parents: false
|
|
98
|
-
})
|
|
99
|
-
});
|
|
85
|
+
const puter = getPuter();
|
|
100
86
|
|
|
101
|
-
|
|
87
|
+
try {
|
|
88
|
+
const data = await puter.fs.mkdir(`${getCurrentDirectory()}/${directoryName}`, {
|
|
89
|
+
overwrite: false,
|
|
90
|
+
dedupeName: true,
|
|
91
|
+
createMissingParents: false
|
|
92
|
+
})
|
|
102
93
|
if (data && data.id) {
|
|
103
94
|
console.log(chalk.green(`Directory "${directoryName}" created successfully!`));
|
|
104
95
|
console.log(chalk.dim(`Path: ${data.path}`));
|
|
@@ -127,16 +118,11 @@ export async function renameFileOrDirectory(args = []) {
|
|
|
127
118
|
const destPath = args[1].startsWith('/') ? args[1] : resolvePath(getCurrentDirectory(), args[1]);
|
|
128
119
|
|
|
129
120
|
console.log(chalk.green(`Moving "${sourcePath}" to "${destPath}"...\n`));
|
|
130
|
-
|
|
121
|
+
|
|
122
|
+
const puter = getPuter();
|
|
131
123
|
try {
|
|
132
124
|
// Step 1: Get the source file/directory info
|
|
133
|
-
const
|
|
134
|
-
method: 'POST',
|
|
135
|
-
headers: getHeaders(),
|
|
136
|
-
body: JSON.stringify({ path: sourcePath })
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const statData = await statResponse.json();
|
|
125
|
+
const statData = await puter.fs.stat(sourcePath);
|
|
140
126
|
if (!statData || !statData.uid) {
|
|
141
127
|
console.log(chalk.red(`Could not find source "${sourcePath}".`));
|
|
142
128
|
return;
|
|
@@ -146,13 +132,16 @@ export async function renameFileOrDirectory(args = []) {
|
|
|
146
132
|
const sourceName = statData.name;
|
|
147
133
|
|
|
148
134
|
// Step 2: Check if destination is an existing directory
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
135
|
+
let destData = null;
|
|
136
|
+
try {
|
|
137
|
+
destData = await puter.fs.stat(destPath);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error.code == "subject_does_not_exist") {
|
|
140
|
+
// no-op
|
|
141
|
+
} else {
|
|
142
|
+
throw (error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
156
145
|
|
|
157
146
|
// Determine if this is a rename or move operation
|
|
158
147
|
const isMove = destData && destData.is_dir;
|
|
@@ -161,20 +150,11 @@ export async function renameFileOrDirectory(args = []) {
|
|
|
161
150
|
|
|
162
151
|
if (isMove) {
|
|
163
152
|
// Move operation: use /move endpoint
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
source: sourceUid,
|
|
169
|
-
destination: destination,
|
|
170
|
-
overwrite: false,
|
|
171
|
-
new_name: newName,
|
|
172
|
-
create_missing_parents: false,
|
|
173
|
-
new_metadata: {}
|
|
174
|
-
})
|
|
153
|
+
const moveData = await puter.fs.move(sourceUid, destination, {
|
|
154
|
+
overwrite: false,
|
|
155
|
+
newName: newName,
|
|
156
|
+
createMissingParents: false,
|
|
175
157
|
});
|
|
176
|
-
|
|
177
|
-
const moveData = await moveResponse.json();
|
|
178
158
|
if (moveData && moveData.moved) {
|
|
179
159
|
console.log(chalk.green(`Successfully moved "${sourcePath}" to "${moveData.moved.path}"!`));
|
|
180
160
|
} else {
|
|
@@ -182,17 +162,8 @@ export async function renameFileOrDirectory(args = []) {
|
|
|
182
162
|
}
|
|
183
163
|
} else {
|
|
184
164
|
// Rename operation: use /rename endpoint
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
headers: getHeaders(),
|
|
188
|
-
body: JSON.stringify({
|
|
189
|
-
uid: sourceUid,
|
|
190
|
-
new_name: newName
|
|
191
|
-
})
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
const renameData = await renameResponse.json();
|
|
195
|
-
if (renameData && renameData.uid) {
|
|
165
|
+
const renameData = await puter.fs.rename(sourceUid, newName);
|
|
166
|
+
if (renameData) {
|
|
196
167
|
console.log(chalk.green(`Successfully renamed "${sourcePath}" to "${renameData.path}"!`));
|
|
197
168
|
} else {
|
|
198
169
|
console.log(chalk.red('Failed to rename item. Please check your input.'));
|
|
@@ -201,6 +172,7 @@ export async function renameFileOrDirectory(args = []) {
|
|
|
201
172
|
} catch (error) {
|
|
202
173
|
console.log(chalk.red('Failed to move/rename item.'));
|
|
203
174
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
175
|
+
console.error(error);
|
|
204
176
|
}
|
|
205
177
|
}
|
|
206
178
|
|
|
@@ -213,6 +185,7 @@ export async function renameFileOrDirectory(args = []) {
|
|
|
213
185
|
*/
|
|
214
186
|
async function findMatchingFiles(files, pattern, basePath) {
|
|
215
187
|
const matchedPaths = [];
|
|
188
|
+
const puter = getPuter();
|
|
216
189
|
|
|
217
190
|
for (const file of files) {
|
|
218
191
|
const filePath = path.join(basePath, file.name);
|
|
@@ -224,14 +197,8 @@ async function findMatchingFiles(files, pattern, basePath) {
|
|
|
224
197
|
|
|
225
198
|
// If it's a directory, recursively search its contents
|
|
226
199
|
if (file.is_dir) {
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
headers: getHeaders(),
|
|
230
|
-
body: JSON.stringify({ path: filePath })
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
if (dirResponse.ok) {
|
|
234
|
-
const dirFiles = await dirResponse.json();
|
|
200
|
+
const dirFiles = await puter.fs.readdir(filePath);
|
|
201
|
+
if (dirFiles && dirFiles.length > 0) {
|
|
235
202
|
const dirMatches = await findMatchingFiles(dirFiles, pattern, filePath);
|
|
236
203
|
matchedPaths.push(...dirMatches);
|
|
237
204
|
}
|
|
@@ -283,20 +250,11 @@ export async function removeFileOrDirectory(args = []) {
|
|
|
283
250
|
const skipConfirmation = args.includes('-f'); // Check the flag if provided
|
|
284
251
|
const names = skipConfirmation ? args.filter(option => option !== '-f') : args;
|
|
285
252
|
|
|
253
|
+
const puter = getPuter();
|
|
254
|
+
|
|
286
255
|
try {
|
|
287
256
|
// Step 1: Fetch the list of files and directories from the server
|
|
288
|
-
const
|
|
289
|
-
method: 'POST',
|
|
290
|
-
headers: getHeaders(),
|
|
291
|
-
body: JSON.stringify({ path: getCurrentDirectory() })
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
if (!listResponse.ok) {
|
|
295
|
-
console.error(chalk.red('Failed to list files from the server.'));
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const files = await listResponse.json();
|
|
257
|
+
const files = await puter.fs.readdir(getCurrentDirectory());
|
|
300
258
|
if (!Array.isArray(files) || files.length == 0) {
|
|
301
259
|
console.error(chalk.red('No files or directories found on the server.'));
|
|
302
260
|
return;
|
|
@@ -347,40 +305,20 @@ export async function removeFileOrDirectory(args = []) {
|
|
|
347
305
|
console.log(chalk.green(`Preparing to remove "${path}"...`));
|
|
348
306
|
|
|
349
307
|
// Step 4.1: Get the UID of the file/directory
|
|
350
|
-
const
|
|
351
|
-
method: 'POST',
|
|
352
|
-
headers: getHeaders(),
|
|
353
|
-
body: JSON.stringify({ path })
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
const statData = await statResponse.json();
|
|
308
|
+
const statData = await puter.fs.stat(path);
|
|
357
309
|
if (!statData || !statData.uid) {
|
|
358
310
|
console.error(chalk.red(`Could not find file or directory with path "${path}".`));
|
|
359
311
|
continue;
|
|
360
312
|
}
|
|
361
313
|
|
|
362
314
|
const uid = statData.uid;
|
|
363
|
-
const originalPath = statData.path;
|
|
364
315
|
|
|
365
316
|
// Step 4.2: Perform the move operation to Trash
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
source: uid,
|
|
371
|
-
destination: `/${getCurrentUserName()}/Trash`,
|
|
372
|
-
overwrite: false,
|
|
373
|
-
new_name: uid, // Use the UID as the new name in Trash
|
|
374
|
-
create_missing_parents: false,
|
|
375
|
-
new_metadata: {
|
|
376
|
-
original_name: path.split('/').pop(),
|
|
377
|
-
original_path: originalPath,
|
|
378
|
-
trashed_ts: Math.floor(Date.now() / 1000) // Current timestamp
|
|
379
|
-
}
|
|
380
|
-
})
|
|
317
|
+
const moveData = await puter.fs.move(uid, `/${getCurrentUserName()}/Trash`, {
|
|
318
|
+
overwrite: false,
|
|
319
|
+
newName: uid,
|
|
320
|
+
createMissingParents: false,
|
|
381
321
|
});
|
|
382
|
-
|
|
383
|
-
const moveData = await moveResponse.json();
|
|
384
322
|
if (moveData && moveData.moved) {
|
|
385
323
|
console.log(chalk.green(`Successfully moved "${path}" to Trash!`));
|
|
386
324
|
console.log(chalk.dim(`Moved to: ${moveData.moved.path}`));
|
|
@@ -406,6 +344,7 @@ export async function removeFileOrDirectory(args = []) {
|
|
|
406
344
|
export async function deleteFolder(folderPath, skipConfirmation = false) {
|
|
407
345
|
console.log(chalk.green(`Preparing to delete "${folderPath}"...\n`));
|
|
408
346
|
|
|
347
|
+
const puter = getPuter();
|
|
409
348
|
try {
|
|
410
349
|
// Step 1: Prompt for confirmation (unless skipConfirmation is true)
|
|
411
350
|
if (!skipConfirmation) {
|
|
@@ -425,18 +364,11 @@ export async function deleteFolder(folderPath, skipConfirmation = false) {
|
|
|
425
364
|
}
|
|
426
365
|
|
|
427
366
|
// Step 2: Perform the delete operation
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
body: JSON.stringify({
|
|
432
|
-
paths: [folderPath],
|
|
433
|
-
descendants_only: true, // Delete only the contents, not the folder itself
|
|
434
|
-
recursive: true // Delete all subdirectories and files
|
|
435
|
-
})
|
|
367
|
+
const deleteData = await puter.fs.delete(folderPath, {
|
|
368
|
+
descendantsOnly: true,
|
|
369
|
+
recursive: true
|
|
436
370
|
});
|
|
437
|
-
|
|
438
|
-
const deleteData = await deleteResponse.json();
|
|
439
|
-
if (deleteResponse.ok && Object.keys(deleteData).length == 0) {
|
|
371
|
+
if (Object.keys(deleteData).length == 0) {
|
|
440
372
|
console.log(chalk.green(`Successfully deleted all contents from: ${chalk.cyan(folderPath)}`));
|
|
441
373
|
} else {
|
|
442
374
|
console.log(chalk.red('Failed to delete folder. Please check your input.'));
|
|
@@ -444,6 +376,7 @@ export async function deleteFolder(folderPath, skipConfirmation = false) {
|
|
|
444
376
|
} catch (error) {
|
|
445
377
|
console.log(chalk.red('Failed to delete folder.'));
|
|
446
378
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
379
|
+
console.error(error);
|
|
447
380
|
}
|
|
448
381
|
}
|
|
449
382
|
|
|
@@ -462,19 +395,13 @@ export async function emptyTrash(skipConfirmation = true) {
|
|
|
462
395
|
*/
|
|
463
396
|
export async function getInfo(args = []) {
|
|
464
397
|
const names = args.length > 0 ? args : ['.'];
|
|
398
|
+
const puter = getPuter();
|
|
465
399
|
for (let name of names)
|
|
466
400
|
try {
|
|
467
401
|
name = `${getCurrentDirectory()}/${name}`;
|
|
468
402
|
console.log(chalk.green(`Getting stat info for: "${name}"...\n`));
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
headers: getHeaders(),
|
|
472
|
-
body: JSON.stringify({
|
|
473
|
-
path: name
|
|
474
|
-
})
|
|
475
|
-
});
|
|
476
|
-
const data = await response.json();
|
|
477
|
-
if (response.ok && data) {
|
|
403
|
+
const data = await puter.fs.stat(name);
|
|
404
|
+
if (data) {
|
|
478
405
|
console.log(chalk.cyan('File/Directory Information:'));
|
|
479
406
|
console.log(chalk.dim('----------------------------------------'));
|
|
480
407
|
console.log(chalk.cyan(`Name: `) + chalk.white(data.name));
|
|
@@ -513,22 +440,15 @@ export async function changeDirectory(args) {
|
|
|
513
440
|
console.log(chalk.green(currentPath));
|
|
514
441
|
return;
|
|
515
442
|
}
|
|
443
|
+
const puter = getPuter();
|
|
516
444
|
|
|
517
445
|
const path = args[0];
|
|
518
446
|
// Handle "/","~",".." and deeper navigation
|
|
519
447
|
const newPath = path.startsWith('/')? path: (path === '~'? `/${getCurrentUserName()}` :resolvePath(currentPath, path));
|
|
520
448
|
try {
|
|
521
449
|
// Check if the new path is a valid directory
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
headers: getHeaders(),
|
|
525
|
-
body: JSON.stringify({
|
|
526
|
-
path: newPath
|
|
527
|
-
})
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
const data = await response.json();
|
|
531
|
-
if (response.ok && data && data.is_dir) {
|
|
450
|
+
const data = await puter.fs.stat(newPath);
|
|
451
|
+
if (data && data.is_dir) {
|
|
532
452
|
// Update the newPath to use the correct name from the response
|
|
533
453
|
const arrayDirs = newPath.split('/');
|
|
534
454
|
arrayDirs.pop();
|
|
@@ -548,15 +468,10 @@ export async function changeDirectory(args) {
|
|
|
548
468
|
*/
|
|
549
469
|
export async function getDiskUsage(body = null) {
|
|
550
470
|
console.log(chalk.green('Fetching disk usage information...\n'));
|
|
471
|
+
const puter = getPuter();
|
|
551
472
|
try {
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
headers: getHeaders(),
|
|
555
|
-
body: body ? JSON.stringify(body) : null
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
const data = await response.json();
|
|
559
|
-
if (response.ok && data) {
|
|
473
|
+
const data = await puter.fs.space();
|
|
474
|
+
if (data) {
|
|
560
475
|
showDiskSpaceUsage(data);
|
|
561
476
|
} else {
|
|
562
477
|
console.error(chalk.red('Unable to fetch disk usage information.'));
|
|
@@ -575,18 +490,15 @@ export async function pathExists(filePath) {
|
|
|
575
490
|
console.log(chalk.red('No path provided.'));
|
|
576
491
|
return false;
|
|
577
492
|
}
|
|
493
|
+
const puter = getPuter();
|
|
578
494
|
try {
|
|
579
495
|
// Step 1: Check if the file already exists
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
headers: getHeaders(),
|
|
583
|
-
body: JSON.stringify({
|
|
584
|
-
path: filePath
|
|
585
|
-
})
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
return statResponse.ok;
|
|
496
|
+
await puter.fs.stat(filePath);
|
|
497
|
+
return true;
|
|
589
498
|
} catch (error){
|
|
499
|
+
if (error.code == "subject_does_not_exist") {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
590
502
|
console.error(chalk.red('Failed to check if file exists.'));
|
|
591
503
|
console.error('ERROR', error);
|
|
592
504
|
return false;
|
|
@@ -615,21 +527,12 @@ export async function createFile(args = []) {
|
|
|
615
527
|
const dedupeName = false; // Default: false
|
|
616
528
|
const overwrite = true; // Default: true
|
|
617
529
|
|
|
530
|
+
const puter = getPuter();
|
|
618
531
|
console.log(chalk.green(`Creating file:\nFileName: "${chalk.dim(fileName)}"\nPath: "${chalk.dim(dirName)}"\nContent Length: ${chalk.dim(content.length)}`));
|
|
619
532
|
try {
|
|
620
533
|
// Step 1: Check if the file already exists
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
headers: getHeaders(),
|
|
624
|
-
body: JSON.stringify({
|
|
625
|
-
path: fullPath
|
|
626
|
-
})
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
if (!statResponse.ok) {
|
|
630
|
-
console.log(chalk.cyan('File does not exists. Il will be created.'));
|
|
631
|
-
} else {
|
|
632
|
-
const statData = await statResponse.json();
|
|
534
|
+
try {
|
|
535
|
+
const statData = await puter.fs.stat(fullPath);
|
|
633
536
|
if (statData && statData.id) {
|
|
634
537
|
if (!overwrite) {
|
|
635
538
|
console.error(chalk.red(`File "${filePath}" already exists. Use --overwrite=true to replace it.`));
|
|
@@ -637,21 +540,16 @@ export async function createFile(args = []) {
|
|
|
637
540
|
}
|
|
638
541
|
console.log(chalk.yellow(`File "${filePath}" already exists. It will be overwritten.`));
|
|
639
542
|
}
|
|
543
|
+
} catch (error) {
|
|
544
|
+
if (error.code == "subject_does_not_exist") {
|
|
545
|
+
console.log(chalk.cyan('File does not exists. It will be created.'));
|
|
546
|
+
} else {
|
|
547
|
+
throw error;
|
|
548
|
+
}
|
|
640
549
|
}
|
|
641
550
|
|
|
642
551
|
// Step 2: Check disk space
|
|
643
|
-
const
|
|
644
|
-
method: 'POST',
|
|
645
|
-
headers: getHeaders(),
|
|
646
|
-
body: null
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
if (!dfResponse.ok) {
|
|
650
|
-
console.error(chalk.red('Unable to check disk space.'));
|
|
651
|
-
return false;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
const dfData = await dfResponse.json();
|
|
552
|
+
const dfData = await puter.fs.space();
|
|
655
553
|
if (dfData.used >= dfData.capacity) {
|
|
656
554
|
console.error(chalk.red('Not enough disk space to create the file.'));
|
|
657
555
|
showDiskSpaceUsage(dfData); // Display disk usage info
|
|
@@ -659,85 +557,32 @@ export async function createFile(args = []) {
|
|
|
659
557
|
}
|
|
660
558
|
|
|
661
559
|
// Step 3: Create the nested directories if they don't exist
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
if (!dirStatResponse.ok || dirStatResponse.status === 404) {
|
|
671
|
-
// Create the directory if it doesn't exist
|
|
672
|
-
await fetch(`${API_BASE}/mkdir`, {
|
|
673
|
-
method: 'POST',
|
|
674
|
-
headers: getHeaders(),
|
|
675
|
-
body: JSON.stringify({
|
|
676
|
-
parent: path.dirname(dirName),
|
|
677
|
-
path: path.basename(dirName),
|
|
560
|
+
try {
|
|
561
|
+
await puter.fs.stat(dirName);
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if (error.code == "subject_does_not_exist") {
|
|
564
|
+
// Create the directory if it doesn't exist
|
|
565
|
+
await puter.fs.mkdir(dirName, {
|
|
678
566
|
overwrite: false,
|
|
679
|
-
|
|
680
|
-
|
|
567
|
+
dedupeName: true,
|
|
568
|
+
createMissingParents: true
|
|
681
569
|
})
|
|
682
|
-
}
|
|
570
|
+
} else {
|
|
571
|
+
throw error;
|
|
572
|
+
}
|
|
683
573
|
}
|
|
684
574
|
|
|
685
|
-
// Step 4: Create the file
|
|
686
|
-
const operationId = crypto.randomUUID(); // Generate a unique operation ID
|
|
687
|
-
const socketId = 'undefined'; // Placeholder socket ID
|
|
688
|
-
const boundary = `----WebKitFormBoundary${crypto.randomUUID().replace(/-/g, '')}`;
|
|
575
|
+
// Step 4: Create the file
|
|
689
576
|
const fileBlob = new Blob([content || ''], { type: 'text/plain' });
|
|
690
577
|
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
`--${boundary}\r\n` +
|
|
696
|
-
`Content-Disposition: form-data; name="original_client_socket_id"\r\n\r\n${socketId}\r\n` +
|
|
697
|
-
`--${boundary}\r\n` +
|
|
698
|
-
`Content-Disposition: form-data; name="fileinfo"\r\n\r\n${JSON.stringify({
|
|
699
|
-
name: fileName,
|
|
700
|
-
type: 'text/plain',
|
|
701
|
-
size: fileBlob.size
|
|
702
|
-
})}\r\n` +
|
|
703
|
-
`--${boundary}\r\n` +
|
|
704
|
-
`Content-Disposition: form-data; name="operation"\r\n\r\n${JSON.stringify({
|
|
705
|
-
op: 'write',
|
|
706
|
-
dedupe_name: dedupeName,
|
|
707
|
-
overwrite: overwrite,
|
|
708
|
-
operation_id: operationId,
|
|
709
|
-
path: dirName,
|
|
710
|
-
name: fileName,
|
|
711
|
-
item_upload_id: 0
|
|
712
|
-
})}\r\n` +
|
|
713
|
-
`--${boundary}\r\n` +
|
|
714
|
-
`Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n` +
|
|
715
|
-
`Content-Type: text/plain\r\n\r\n${content || ''}\r\n` +
|
|
716
|
-
`--${boundary}--\r\n`;
|
|
717
|
-
|
|
718
|
-
// Send the request
|
|
719
|
-
const createResponse = await fetch(`${API_BASE}/batch`, {
|
|
720
|
-
method: 'POST',
|
|
721
|
-
headers: getHeaders(`multipart/form-data; boundary=${boundary}`),
|
|
722
|
-
body: formData
|
|
578
|
+
const createData = await puter.fs.upload(fileBlob, dirName, {
|
|
579
|
+
overwrite: overwrite,
|
|
580
|
+
dedupeName: dedupeName,
|
|
581
|
+
name: fileName
|
|
723
582
|
});
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
console.error(chalk.red(`Failed to create file. Server response: ${errorText}. status: ${createResponse.status}`));
|
|
728
|
-
return false;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
const createData = await createResponse.json();
|
|
732
|
-
if (createData && createData.results && createData.results.length > 0) {
|
|
733
|
-
const file = createData.results[0];
|
|
734
|
-
console.log(chalk.green(`File "${filePath}" created successfully!`));
|
|
735
|
-
console.log(chalk.dim(`Path: ${file.path}`));
|
|
736
|
-
console.log(chalk.dim(`UID: ${file.uid}`));
|
|
737
|
-
} else {
|
|
738
|
-
console.error(chalk.red('Failed to create file. Invalid response from server.'));
|
|
739
|
-
return false;
|
|
740
|
-
}
|
|
583
|
+
console.log(chalk.green(`File "${createData.name}" created successfully!`));
|
|
584
|
+
console.log(chalk.dim(`Path: ${createData.path}`));
|
|
585
|
+
console.log(chalk.dim(`UID: ${createData.uid}`));
|
|
741
586
|
} catch (error) {
|
|
742
587
|
console.error(chalk.red(`Failed to create file.\nError: ${error.message}`));
|
|
743
588
|
return false;
|
|
@@ -754,23 +599,19 @@ export async function readFile(args = []) {
|
|
|
754
599
|
console.log(chalk.red('Usage: cat <file_path>'));
|
|
755
600
|
return;
|
|
756
601
|
}
|
|
602
|
+
const puter = getPuter();
|
|
757
603
|
|
|
758
604
|
const filePath = resolvePath(getCurrentDirectory(), args[0]);
|
|
759
605
|
console.log(chalk.green(`Reading file "${filePath}"...\n`));
|
|
760
606
|
|
|
761
607
|
try {
|
|
762
|
-
// Step 1: Fetch the file content
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
});
|
|
767
|
-
|
|
768
|
-
if (!response.ok) {
|
|
769
|
-
console.error(chalk.red(`Failed to read file. Server response: ${response.statusText}`));
|
|
608
|
+
// Step 1: Fetch the file content
|
|
609
|
+
const fileBlob = await puter.fs.read(filePath);
|
|
610
|
+
if (!fileBlob) {
|
|
611
|
+
console.error(chalk.red(`Failed to read file.`));
|
|
770
612
|
return;
|
|
771
613
|
}
|
|
772
|
-
|
|
773
|
-
const data = await response.text();
|
|
614
|
+
const data = await fileBlob.text();
|
|
774
615
|
|
|
775
616
|
// Step 2: Dispaly the content
|
|
776
617
|
if (data.length) {
|
|
@@ -804,6 +645,7 @@ export async function uploadFile(args = []) {
|
|
|
804
645
|
const overwrite = args.length > 3 ? args[3] === 'true' : false; // Default: false
|
|
805
646
|
|
|
806
647
|
console.log(chalk.green(`Uploading files from "${localPath}" to "${remotePath}"...\n`));
|
|
648
|
+
const puter = getPuter();
|
|
807
649
|
try {
|
|
808
650
|
// Step 1: Find all matching files (excluding hidden files)
|
|
809
651
|
const files = glob.sync(localPath, { nodir: true, dot: false });
|
|
@@ -814,18 +656,7 @@ export async function uploadFile(args = []) {
|
|
|
814
656
|
}
|
|
815
657
|
|
|
816
658
|
// Step 2: Check disk space
|
|
817
|
-
const
|
|
818
|
-
method: 'POST',
|
|
819
|
-
headers: getHeaders(),
|
|
820
|
-
body: null
|
|
821
|
-
});
|
|
822
|
-
|
|
823
|
-
if (!dfResponse.ok) {
|
|
824
|
-
console.error(chalk.red('Unable to check disk space.'));
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
const dfData = await dfResponse.json();
|
|
659
|
+
const dfData = await puter.fs.space();
|
|
829
660
|
if (dfData.used >= dfData.capacity) {
|
|
830
661
|
console.error(chalk.red('Not enough disk space to upload the files.'));
|
|
831
662
|
showDiskSpaceUsage(dfData); // Display disk usage info
|
|
@@ -835,93 +666,23 @@ export async function uploadFile(args = []) {
|
|
|
835
666
|
// Step 3: Upload each file
|
|
836
667
|
for (const filePath of files) {
|
|
837
668
|
const fileName = path.basename(filePath);
|
|
838
|
-
const fileContent = fs.readFileSync(filePath
|
|
839
|
-
|
|
840
|
-
// Prepare the upload request
|
|
841
|
-
const operationId = crypto.randomUUID(); // Generate a unique operation ID
|
|
842
|
-
const socketId = 'undefined'; // Placeholder socket ID
|
|
843
|
-
const boundary = `----WebKitFormBoundary${crypto.randomUUID().replace(/-/g, '')}`;
|
|
844
|
-
|
|
845
|
-
// Prepare FormData
|
|
846
|
-
const formData = `--${boundary}\r\n` +
|
|
847
|
-
`Content-Disposition: form-data; name="operation_id"\r\n\r\n${operationId}\r\n` +
|
|
848
|
-
`--${boundary}\r\n` +
|
|
849
|
-
`Content-Disposition: form-data; name="socket_id"\r\n\r\n${socketId}\r\n` +
|
|
850
|
-
`--${boundary}\r\n` +
|
|
851
|
-
`Content-Disposition: form-data; name="original_client_socket_id"\r\n\r\n${socketId}\r\n` +
|
|
852
|
-
`--${boundary}\r\n` +
|
|
853
|
-
`Content-Disposition: form-data; name="fileinfo"\r\n\r\n${JSON.stringify({
|
|
854
|
-
name: fileName,
|
|
855
|
-
type: 'text/plain',
|
|
856
|
-
size: Buffer.byteLength(fileContent, 'utf8')
|
|
857
|
-
})}\r\n` +
|
|
858
|
-
`--${boundary}\r\n` +
|
|
859
|
-
`Content-Disposition: form-data; name="operation"\r\n\r\n${JSON.stringify({
|
|
860
|
-
op: 'write',
|
|
861
|
-
dedupe_name: dedupeName,
|
|
862
|
-
overwrite: overwrite,
|
|
863
|
-
operation_id: operationId,
|
|
864
|
-
path: remotePath,
|
|
865
|
-
name: fileName,
|
|
866
|
-
item_upload_id: 0
|
|
867
|
-
})}\r\n` +
|
|
868
|
-
`--${boundary}\r\n` +
|
|
869
|
-
`Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n` +
|
|
870
|
-
`Content-Type: text/plain\r\n\r\n${fileContent}\r\n` +
|
|
871
|
-
`--${boundary}--\r\n`;
|
|
872
|
-
|
|
873
|
-
// Send the upload request
|
|
874
|
-
const uploadResponse = await fetch(`${API_BASE}/batch`, {
|
|
875
|
-
method: 'POST',
|
|
876
|
-
headers: getHeaders(`multipart/form-data; boundary=${boundary}`),
|
|
877
|
-
body: formData
|
|
878
|
-
});
|
|
669
|
+
const fileContent = fs.readFileSync(filePath);
|
|
670
|
+
const blob = new Blob([fileContent]);
|
|
879
671
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
const file = uploadData.results[0];
|
|
889
|
-
console.log(chalk.green(`File "${fileName}" uploaded successfully!`));
|
|
890
|
-
console.log(chalk.dim(`Path: ${file.path}`));
|
|
891
|
-
console.log(chalk.dim(`UID: ${file.uid}`));
|
|
892
|
-
} else {
|
|
893
|
-
console.error(chalk.red(`Failed to upload file "${fileName}". Invalid response from server.`));
|
|
894
|
-
}
|
|
672
|
+
const uploadData = await puter.fs.upload(blob, remotePath, {
|
|
673
|
+
overwrite: overwrite,
|
|
674
|
+
dedupeName: dedupeName,
|
|
675
|
+
name: fileName
|
|
676
|
+
});
|
|
677
|
+
console.log(chalk.green(`File "${uploadData.name}" uploaded successfully!`));
|
|
678
|
+
console.log(chalk.dim(`Path: ${uploadData.path}`));
|
|
679
|
+
console.log(chalk.dim(`UID: ${uploadData.uid}`));
|
|
895
680
|
}
|
|
896
681
|
} catch (error) {
|
|
897
682
|
console.error(chalk.red(`Failed to upload files.\nError: ${error.message}`));
|
|
898
683
|
}
|
|
899
684
|
}
|
|
900
685
|
|
|
901
|
-
/**
|
|
902
|
-
* Get a temporary CSRF Token
|
|
903
|
-
* @returns The CSRF token
|
|
904
|
-
*/
|
|
905
|
-
async function getCsrfToken() {
|
|
906
|
-
const csrfResponse = await fetch(`${BASE_URL}/get-anticsrf-token`, {
|
|
907
|
-
method: 'GET',
|
|
908
|
-
headers: getHeaders()
|
|
909
|
-
});
|
|
910
|
-
|
|
911
|
-
if (!csrfResponse.ok) {
|
|
912
|
-
console.error(chalk.red('Failed to fetch CSRF token.'));
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
const csrfData = await csrfResponse.json();
|
|
917
|
-
if (!csrfData || !csrfData.token) {
|
|
918
|
-
console.error(chalk.red('Failed to fetch anti-CSRF token.'));
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
return csrfData.token;
|
|
923
|
-
}
|
|
924
|
-
|
|
925
686
|
/**
|
|
926
687
|
* Download a file from the Puter server to the host machine
|
|
927
688
|
* @param {Array} args - The arguments passed to the command (remote file path, Optional: local path).
|
|
@@ -937,21 +698,10 @@ export async function downloadFile(args = []) {
|
|
|
937
698
|
const overwrite = args.length > 2 ? args[2] === 'true' : false; // Default: false
|
|
938
699
|
|
|
939
700
|
console.log(chalk.green(`Downloading files matching "${remotePathPattern}" to "${localBasePath}"...\n`));
|
|
940
|
-
|
|
701
|
+
const puter = getPuter();
|
|
941
702
|
try {
|
|
942
703
|
// Step 1: Fetch the list of files and directories from the server
|
|
943
|
-
const
|
|
944
|
-
method: 'POST',
|
|
945
|
-
headers: getHeaders(),
|
|
946
|
-
body: JSON.stringify({ path: getCurrentDirectory() })
|
|
947
|
-
});
|
|
948
|
-
|
|
949
|
-
if (!listResponse.ok) {
|
|
950
|
-
console.error(chalk.red('Failed to list files from the server.'));
|
|
951
|
-
return;
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
const files = await listResponse.json();
|
|
704
|
+
const files = await puter.fs.readdir(getCurrentDirectory());
|
|
955
705
|
if (!Array.isArray(files) || files.length === 0) {
|
|
956
706
|
console.error(chalk.red('No files or directories found on the server.'));
|
|
957
707
|
return;
|
|
@@ -977,18 +727,8 @@ export async function downloadFile(args = []) {
|
|
|
977
727
|
|
|
978
728
|
console.log(chalk.green(`Downloading file "${remoteFilePath}" to "${localFilePath}"...`));
|
|
979
729
|
|
|
980
|
-
|
|
981
|
-
const
|
|
982
|
-
|
|
983
|
-
const downloadResponse = await fetch(`${BASE_URL}/down?path=${remoteFilePath}`, {
|
|
984
|
-
method: 'POST',
|
|
985
|
-
headers: {
|
|
986
|
-
...getHeaders('application/x-www-form-urlencoded'),
|
|
987
|
-
"cookie": `puter_auth_token=${getAuthToken()};`
|
|
988
|
-
},
|
|
989
|
-
"referrerPolicy": "strict-origin-when-cross-origin",
|
|
990
|
-
body: `anti_csrf=${antiCsrfToken}`
|
|
991
|
-
});
|
|
730
|
+
const fileUrl = await puter.fs.getReadURL(remoteFilePath);
|
|
731
|
+
const downloadResponse = await fetch(fileUrl);
|
|
992
732
|
|
|
993
733
|
if (!downloadResponse.ok) {
|
|
994
734
|
console.error(chalk.red(`Failed to download file "${remoteFilePath}". Server response: ${downloadResponse.statusText}`));
|
|
@@ -1027,25 +767,20 @@ export async function copyFile(args = []) {
|
|
|
1027
767
|
const destinationPath = args[1].startsWith(`/${getCurrentUserName()}`) ? args[1] : resolvePath(getCurrentDirectory(), args[1]); // Resolve the destination path
|
|
1028
768
|
|
|
1029
769
|
console.log(chalk.green(`Copy: "${chalk.dim(sourcePath)}" to: "${chalk.dim(destinationPath)}"...\n`));
|
|
770
|
+
const puter = getPuter();
|
|
1030
771
|
try {
|
|
1031
772
|
// Step 1: Check if the source is a directory or a file
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
const statData = await statResponse.json();
|
|
1046
|
-
if (!statData || !statData.id) {
|
|
1047
|
-
console.error(chalk.red(`Source path "${sourcePath}" does not exist.`));
|
|
1048
|
-
return;
|
|
773
|
+
let statData;
|
|
774
|
+
try {
|
|
775
|
+
statData = await puter.fs.stat(sourcePath);
|
|
776
|
+
} catch (error) {
|
|
777
|
+
if (error == "subject_does_not_exist") {
|
|
778
|
+
console.error(chalk.red(`Source path "${sourcePath}" does not exist.`));
|
|
779
|
+
return;
|
|
780
|
+
} else {
|
|
781
|
+
console.error(chalk.red(`Failed to check source path. Server response: ${await statResponse.text()}`));
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
1049
784
|
}
|
|
1050
785
|
|
|
1051
786
|
if (statData.is_dir) {
|
|
@@ -1055,21 +790,7 @@ export async function copyFile(args = []) {
|
|
|
1055
790
|
const relativePath = file.path.replace(sourcePath, '');
|
|
1056
791
|
const destPath = path.join(destinationPath, relativePath);
|
|
1057
792
|
|
|
1058
|
-
const
|
|
1059
|
-
method: 'POST',
|
|
1060
|
-
headers: getHeaders(),
|
|
1061
|
-
body: JSON.stringify({
|
|
1062
|
-
source: file.path,
|
|
1063
|
-
destination: destPath
|
|
1064
|
-
})
|
|
1065
|
-
});
|
|
1066
|
-
|
|
1067
|
-
if (!copyResponse.ok) {
|
|
1068
|
-
console.error(chalk.red(`Failed to copy file "${file.path}". Server response: ${await copyResponse.text()}`));
|
|
1069
|
-
continue;
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
const copyData = await copyResponse.json();
|
|
793
|
+
const copyData = await puter.fs.copy(file.path, destPath);
|
|
1073
794
|
if (copyData && copyData.length > 0 && copyData[0].copied) {
|
|
1074
795
|
console.log(chalk.green(`File "${chalk.dim(file.path)}" copied successfully to "${chalk.dim(copyData[0].copied.path)}"!`));
|
|
1075
796
|
} else {
|
|
@@ -1078,21 +799,7 @@ export async function copyFile(args = []) {
|
|
|
1078
799
|
}
|
|
1079
800
|
} else {
|
|
1080
801
|
// Step 3: If source is a file, copy it directly
|
|
1081
|
-
const
|
|
1082
|
-
method: 'POST',
|
|
1083
|
-
headers: getHeaders(),
|
|
1084
|
-
body: JSON.stringify({
|
|
1085
|
-
source: sourcePath,
|
|
1086
|
-
destination: destinationPath
|
|
1087
|
-
})
|
|
1088
|
-
});
|
|
1089
|
-
|
|
1090
|
-
if (!copyResponse.ok) {
|
|
1091
|
-
console.error(chalk.red(`Failed to copy file. Server response: ${await copyResponse.text()}`));
|
|
1092
|
-
return;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
const copyData = await copyResponse.json();
|
|
802
|
+
const copyData = await puter.fs.copy(sourcePath, destinationPath);
|
|
1096
803
|
if (copyData && copyData.length > 0 && copyData[0].copied) {
|
|
1097
804
|
console.log(chalk.green(`File "${sourcePath}" copied successfully to "${copyData[0].copied.path}"!`));
|
|
1098
805
|
console.log(chalk.dim(`UID: ${copyData[0].copied.uid}`));
|
|
@@ -1235,21 +942,16 @@ async function resolveLocalDirectory(localPath) {
|
|
|
1235
942
|
* @param {string} remotePath - The remote directory path
|
|
1236
943
|
*/
|
|
1237
944
|
async function ensureRemoteDirectoryExists(remotePath) {
|
|
945
|
+
const puter = getPuter();
|
|
1238
946
|
try {
|
|
1239
947
|
const exists = await pathExists(remotePath);
|
|
1240
948
|
if (!exists) {
|
|
1241
949
|
// Create the directory and any missing parents
|
|
1242
|
-
await
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
path: path.basename(remotePath),
|
|
1248
|
-
overwrite: false,
|
|
1249
|
-
dedupe_name: true,
|
|
1250
|
-
create_missing_parents: true
|
|
1251
|
-
})
|
|
1252
|
-
});
|
|
950
|
+
await puter.fs.mkdir(remotePath, {
|
|
951
|
+
overwrite: false,
|
|
952
|
+
dedupeName: true,
|
|
953
|
+
createMissingParents: true
|
|
954
|
+
})
|
|
1253
955
|
}
|
|
1254
956
|
} catch (error) {
|
|
1255
957
|
console.error(chalk.red(`Failed to create remote directory: ${remotePath}`));
|
|
@@ -1295,10 +997,11 @@ export async function syncDirectory(args = []) {
|
|
|
1295
997
|
}
|
|
1296
998
|
|
|
1297
999
|
// Step 2: Fetch remote directory contents
|
|
1298
|
-
let remoteFiles =
|
|
1299
|
-
|
|
1000
|
+
let remoteFiles = [];
|
|
1001
|
+
try {
|
|
1002
|
+
remoteFiles = await listRemoteFiles(remoteDir);
|
|
1003
|
+
} catch (error) {
|
|
1300
1004
|
console.log(chalk.yellow('Remote directory is empty or does not exist. Continuing...'));
|
|
1301
|
-
remoteFiles = [];
|
|
1302
1005
|
}
|
|
1303
1006
|
|
|
1304
1007
|
// Step 3: List local files
|
|
@@ -1402,32 +1105,23 @@ export async function editFile(args = []) {
|
|
|
1402
1105
|
const filePath = args[0].startsWith('/') ? args[0] : resolvePath(getCurrentDirectory(), args[0]);
|
|
1403
1106
|
console.log(chalk.green(`Fetching file: ${filePath}`));
|
|
1404
1107
|
|
|
1108
|
+
const puter = getPuter();
|
|
1109
|
+
|
|
1405
1110
|
try {
|
|
1406
1111
|
// Step 1: Check if file exists
|
|
1407
|
-
const
|
|
1408
|
-
|
|
1409
|
-
headers: getHeaders(),
|
|
1410
|
-
body: JSON.stringify({ path: filePath })
|
|
1411
|
-
});
|
|
1412
|
-
|
|
1413
|
-
const statData = await statResponse.json();
|
|
1414
|
-
if (!statData || !statData.uid || statData.is_dir) {
|
|
1112
|
+
const statData = await puter.fs.stat(filePath);
|
|
1113
|
+
if (!statData || statData.is_dir) {
|
|
1415
1114
|
console.log(chalk.red(`File not found or is a directory: ${filePath}`));
|
|
1416
1115
|
return;
|
|
1417
1116
|
}
|
|
1418
1117
|
|
|
1419
1118
|
// Step 2: Download the file content
|
|
1420
|
-
const
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
});
|
|
1424
|
-
|
|
1425
|
-
if (!downloadResponse.ok) {
|
|
1119
|
+
const fileBlob = await puter.fs.read(filePath);
|
|
1120
|
+
const fileContent = await fileBlob.text();
|
|
1121
|
+
if (!fileContent) {
|
|
1426
1122
|
console.log(chalk.red(`Failed to download file: ${filePath}`));
|
|
1427
1123
|
return;
|
|
1428
1124
|
}
|
|
1429
|
-
|
|
1430
|
-
const fileContent = await downloadResponse.text();
|
|
1431
1125
|
console.log(chalk.green(`File fetched: ${filePath} (${formatSize(fileContent.length)} bytes)`));
|
|
1432
1126
|
|
|
1433
1127
|
// Step 3: Create a temporary file
|
|
@@ -1449,22 +1143,12 @@ export async function editFile(args = []) {
|
|
|
1449
1143
|
|
|
1450
1144
|
// Read the updated content after editor closes
|
|
1451
1145
|
const updatedContent = fs.readFileSync(tempFilePath, 'utf8');
|
|
1146
|
+
const blob = new Blob([updatedContent]);
|
|
1452
1147
|
console.log(chalk.cyan('Uploading changes...'));
|
|
1453
1148
|
|
|
1454
1149
|
// Step 7: Upload the updated file content
|
|
1455
1150
|
// Step 7.1: Check disk space
|
|
1456
|
-
const
|
|
1457
|
-
method: 'POST',
|
|
1458
|
-
headers: getHeaders(),
|
|
1459
|
-
body: null
|
|
1460
|
-
});
|
|
1461
|
-
|
|
1462
|
-
if (!dfResponse.ok) {
|
|
1463
|
-
console.log(chalk.red('Unable to check disk space.'));
|
|
1464
|
-
return;
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
const dfData = await dfResponse.json();
|
|
1151
|
+
const dfData = await puter.fs.space();
|
|
1468
1152
|
if (dfData.used >= dfData.capacity) {
|
|
1469
1153
|
console.log(chalk.red('Not enough disk space to upload the file.'));
|
|
1470
1154
|
showDiskSpaceUsage(dfData); // Display disk usage info
|
|
@@ -1472,60 +1156,15 @@ export async function editFile(args = []) {
|
|
|
1472
1156
|
}
|
|
1473
1157
|
|
|
1474
1158
|
// Step 7.2: Uploading the updated file
|
|
1475
|
-
const operationId = crypto.randomUUID(); // Generate a unique operation ID
|
|
1476
|
-
const socketId = 'undefined'; // Placeholder socket ID
|
|
1477
|
-
const boundary = `----WebKitFormBoundary${crypto.randomUUID().replace(/-/g, '')}`;
|
|
1478
1159
|
const fileName = path.basename(filePath);
|
|
1479
1160
|
const dirName = path.dirname(filePath);
|
|
1480
1161
|
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
`Content-Disposition: form-data; name="socket_id"\r\n\r\n${socketId}\r\n` +
|
|
1486
|
-
`--${boundary}\r\n` +
|
|
1487
|
-
`Content-Disposition: form-data; name="original_client_socket_id"\r\n\r\n${socketId}\r\n` +
|
|
1488
|
-
`--${boundary}\r\n` +
|
|
1489
|
-
`Content-Disposition: form-data; name="fileinfo"\r\n\r\n${JSON.stringify({
|
|
1490
|
-
name: fileName,
|
|
1491
|
-
type: 'text/plain',
|
|
1492
|
-
size: Buffer.byteLength(updatedContent, 'utf8')
|
|
1493
|
-
})}\r\n` +
|
|
1494
|
-
`--${boundary}\r\n` +
|
|
1495
|
-
`Content-Disposition: form-data; name="operation"\r\n\r\n${JSON.stringify({
|
|
1496
|
-
op: 'write',
|
|
1497
|
-
dedupe_name: false,
|
|
1498
|
-
overwrite: true,
|
|
1499
|
-
operation_id: operationId,
|
|
1500
|
-
path: dirName,
|
|
1501
|
-
name: fileName,
|
|
1502
|
-
item_upload_id: 0
|
|
1503
|
-
})}\r\n` +
|
|
1504
|
-
`--${boundary}\r\n` +
|
|
1505
|
-
`Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n` +
|
|
1506
|
-
`Content-Type: text/plain\r\n\r\n${updatedContent}\r\n` +
|
|
1507
|
-
`--${boundary}--\r\n`;
|
|
1508
|
-
|
|
1509
|
-
// Send the upload request
|
|
1510
|
-
const uploadResponse = await fetch(`${API_BASE}/batch`, {
|
|
1511
|
-
method: 'POST',
|
|
1512
|
-
headers: getHeaders(`multipart/form-data; boundary=${boundary}`),
|
|
1513
|
-
body: formData
|
|
1162
|
+
const uploadData = await puter.fs.upload(blob, dirName, {
|
|
1163
|
+
overwrite: true,
|
|
1164
|
+
dedupeName: false,
|
|
1165
|
+
name: fileName
|
|
1514
1166
|
});
|
|
1515
|
-
|
|
1516
|
-
if (!uploadResponse.ok) {
|
|
1517
|
-
const errorText = await uploadResponse.text();
|
|
1518
|
-
console.log(chalk.red(`Failed to save file. Server response: ${errorText}`));
|
|
1519
|
-
return;
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
const uploadData = await uploadResponse.json();
|
|
1523
|
-
if (uploadData && uploadData.results && uploadData.results.length > 0) {
|
|
1524
|
-
const file = uploadData.results[0];
|
|
1525
|
-
console.log(chalk.green(`File saved: ${file.path}`));
|
|
1526
|
-
} else {
|
|
1527
|
-
console.log(chalk.red('Failed to save file. Invalid response from server.'));
|
|
1528
|
-
}
|
|
1167
|
+
console.log(chalk.green(`File saved: ${uploadData.path}`));
|
|
1529
1168
|
} catch (error) {
|
|
1530
1169
|
if (error.status === 130) {
|
|
1531
1170
|
// This is a SIGINT (Ctrl+C), which is normal for some editors
|