puter-cli 1.8.6 → 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.
@@ -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 response = await fetch(`${API_BASE}/readdir`, {
28
- method: 'POST',
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
- try {
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
- const data = await response.json();
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 statResponse = await fetch(`${API_BASE}/stat`, {
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
- const destStatResponse = await fetch(`${API_BASE}/stat`, {
150
- method: 'POST',
151
- headers: getHeaders(),
152
- body: JSON.stringify({ path: destPath })
153
- });
154
-
155
- const destData = await destStatResponse.json();
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 moveResponse = await fetch(`${API_BASE}/move`, {
165
- method: 'POST',
166
- headers: getHeaders(),
167
- body: JSON.stringify({
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 renameResponse = await fetch(`${API_BASE}/rename`, {
186
- method: 'POST',
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 dirResponse = await fetch(`${API_BASE}/readdir`, {
228
- method: 'POST',
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 listResponse = await fetch(`${API_BASE}/readdir`, {
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 statResponse = await fetch(`${API_BASE}/stat`, {
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 moveResponse = await fetch(`${API_BASE}/move`, {
367
- method: 'POST',
368
- headers: getHeaders(),
369
- body: JSON.stringify({
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 deleteResponse = await fetch(`${API_BASE}/delete`, {
429
- method: 'POST',
430
- headers: getHeaders(),
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 response = await fetch(`${API_BASE}/stat`, {
470
- method: 'POST',
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 response = await fetch(`${API_BASE}/stat`, {
523
- method: 'POST',
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 response = await fetch(`${API_BASE}/df`, {
553
- method: 'POST',
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
- const statResponse = await fetch(`${API_BASE}/stat`, {
581
- method: 'POST',
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
- const statResponse = await fetch(`${API_BASE}/stat`, {
622
- method: 'POST',
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 dfResponse = await fetch(`${API_BASE}/df`, {
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
- const dirStatResponse = await fetch(`${API_BASE}/stat`, {
663
- method: 'POST',
664
- headers: getHeaders(),
665
- body: JSON.stringify({
666
- path: dirName
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
- dedupe_name: true,
680
- create_missing_parents: true
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 using /batch
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 formData = `--${boundary}\r\n` +
692
- `Content-Disposition: form-data; name="operation_id"\r\n\r\n${operationId}\r\n` +
693
- `--${boundary}\r\n` +
694
- `Content-Disposition: form-data; name="socket_id"\r\n\r\n${socketId}\r\n` +
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
- if (!createResponse.ok) {
726
- const errorText = await createResponse.text();
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 response = await fetch(`${API_BASE}/read?file=${encodeURIComponent(filePath)}`, {
764
- method: 'GET',
765
- headers: getHeaders()
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 dfResponse = await fetch(`${API_BASE}/df`, {
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, 'utf8');
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
- if (!uploadResponse.ok) {
881
- const errorText = await uploadResponse.text();
882
- console.error(chalk.red(`Failed to upload file "${fileName}". Server response: ${errorText}`));
883
- continue;
884
- }
885
-
886
- const uploadData = await uploadResponse.json();
887
- if (uploadData && uploadData.results && uploadData.results.length > 0) {
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 listResponse = await fetch(`${API_BASE}/readdir`, {
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
- // Fetch the anti-CSRF token
981
- const antiCsrfToken = await getCsrfToken();
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
- const statResponse = await fetch(`${API_BASE}/stat`, {
1033
- method: 'POST',
1034
- headers: getHeaders(),
1035
- body: JSON.stringify({
1036
- path: sourcePath
1037
- })
1038
- });
1039
-
1040
- if (!statResponse.ok) {
1041
- console.error(chalk.red(`Failed to check source path. Server response: ${await statResponse.text()}`));
1042
- return;
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 copyResponse = await fetch(`${API_BASE}/copy`, {
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 copyResponse = await fetch(`${API_BASE}/copy`, {
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 fetch(`${API_BASE}/mkdir`, {
1243
- method: 'POST',
1244
- headers: getHeaders(),
1245
- body: JSON.stringify({
1246
- parent: path.dirname(remotePath),
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 = await listRemoteFiles(remoteDir);
1299
- if (!Array.isArray(remoteFiles)) {
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 statResponse = await fetch(`${API_BASE}/stat`, {
1408
- method: 'POST',
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 downloadResponse = await fetch(`${API_BASE}/read?file=${encodeURIComponent(filePath)}`, {
1421
- method: 'GET',
1422
- headers: getHeaders()
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 dfResponse = await fetch(`${API_BASE}/df`, {
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
- // Prepare FormData
1482
- const formData = `--${boundary}\r\n` +
1483
- `Content-Disposition: form-data; name="operation_id"\r\n\r\n${operationId}\r\n` +
1484
- `--${boundary}\r\n` +
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