pinme 1.1.1 → 1.1.2

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +478 -81
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -116,7 +116,7 @@ pinme rm
116
116
  pinme rm bafybeifdwyoz66u5czbbjvmmais5fzrzrolxbyiydqsbrxessndt3s6zdi
117
117
  ```
118
118
 
119
- **Note:** This action permanently removes content from the IPFS network. Make sure you have the correct IPFS hash before proceeding.
119
+ **Note:** This action unpins the content from our IPFS node and deletes the ENS subdomain record. It does not ensure that the file is removed from the IPFS network.
120
120
 
121
121
  ### `list` / `ls`
122
122
 
package/dist/index.js CHANGED
@@ -1485,7 +1485,7 @@ var import_chalk6 = __toESM(require("chalk"));
1485
1485
  var import_figlet3 = __toESM(require("figlet"));
1486
1486
 
1487
1487
  // package.json
1488
- var version = "1.1.1";
1488
+ var version = "1.1.2";
1489
1489
 
1490
1490
  // bin/upload.ts
1491
1491
  var import_path5 = __toESM(require("path"));
@@ -4370,7 +4370,7 @@ var import_chalk2 = __toESM(require("chalk"));
4370
4370
  // bin/utils/uploadLimits.ts
4371
4371
  var import_fs = __toESM(require("fs"));
4372
4372
  var import_path = __toESM(require("path"));
4373
- var FILE_SIZE_LIMIT = parseInt("20", 10) * 1024 * 1024;
4373
+ var FILE_SIZE_LIMIT = parseInt("500", 10) * 1024 * 1024;
4374
4374
  var DIRECTORY_SIZE_LIMIT = parseInt("500", 10) * 1024 * 1024;
4375
4375
  function checkFileSizeLimit(filePath) {
4376
4376
  const stats = import_fs.default.statSync(filePath);
@@ -4521,16 +4521,105 @@ function getDeviceId() {
4521
4521
  }
4522
4522
 
4523
4523
  // bin/utils/uploadToIpfs.ts
4524
- var ipfsApiUrl = "https://pinme.dev/api/v2";
4524
+ var ipfsApiUrl = "https://pinme.dev/api/v3";
4525
+ var maxPollTime = parseInt(process.env.MAX_POLL_TIME_MINUTES || "5") * 60 * 1e3;
4526
+ var pollInterval = parseInt(process.env.POLL_INTERVAL_SECONDS || "2") * 1e3;
4527
+ var pollTimeout = parseInt(process.env.POLL_TIMEOUT_SECONDS || "10") * 1e3;
4528
+ async function pollUploadStatus(traceId, deviceId, smartProgress, startTime) {
4529
+ let consecutiveErrors = 0;
4530
+ let stopProgressUpdates = false;
4531
+ while (Date.now() - startTime < maxPollTime) {
4532
+ try {
4533
+ const response = await axios_default.get(
4534
+ `${ipfsApiUrl}/up_status?trace_id=${traceId}&uid=${deviceId}`,
4535
+ {
4536
+ timeout: pollTimeout,
4537
+ headers: {
4538
+ "User-Agent": "Pinme-CLI/1.0.0",
4539
+ Accept: "*/*",
4540
+ Host: new URL(ipfsApiUrl).host,
4541
+ Connection: "keep-alive"
4542
+ }
4543
+ }
4544
+ );
4545
+ const { code, msg, data } = response.data;
4546
+ if (code === 200) {
4547
+ consecutiveErrors = 0;
4548
+ stopProgressUpdates = false;
4549
+ if (data.is_ready) {
4550
+ smartProgress.complete(traceId);
4551
+ return data;
4552
+ } else {
4553
+ smartProgress.updateDisplay();
4554
+ }
4555
+ } else {
4556
+ console.log(import_chalk2.default.yellow(`Warning: ${msg}`));
4557
+ }
4558
+ } catch (error) {
4559
+ consecutiveErrors++;
4560
+ console.log(import_chalk2.default.yellow(`Polling error: ${error.message}`));
4561
+ if (stopProgressUpdates) {
4562
+ smartProgress.updateTimeOnly();
4563
+ }
4564
+ }
4565
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
4566
+ }
4567
+ const maxPollTimeMinutes = Math.floor(maxPollTime / (60 * 1e3));
4568
+ smartProgress.fail(
4569
+ `Upload timeout after ${maxPollTimeMinutes} minutes`,
4570
+ traceId
4571
+ );
4572
+ return null;
4573
+ }
4574
+ function diagnoseDirectoryUploadError(directoryName, resData, expectedName) {
4575
+ const issues = [];
4576
+ if (directoryName.length > 100) {
4577
+ issues.push(
4578
+ `Directory name too long (${directoryName.length} characters, recommended under 100 characters)`
4579
+ );
4580
+ }
4581
+ const availableName = resData.Name;
4582
+ issues.push(`Name returned by IPFS: ${availableName}`);
4583
+ issues.push(`Expected directory name: ${expectedName}`);
4584
+ const encodedName = encodeURIComponent(directoryName);
4585
+ if (encodedName !== directoryName) {
4586
+ issues.push(`Directory name after encoding: ${encodedName}`);
4587
+ }
4588
+ return issues.join("\n - ");
4589
+ }
4590
+ function handleMultipartError(error, context) {
4591
+ if (error.message && error.message.includes("multipart: NextPart: EOF")) {
4592
+ return `Multipart form data error: ${context}. This usually indicates:
4593
+ - Empty directory or no valid files
4594
+ - File access permissions issue
4595
+ - Network interruption during upload
4596
+ - Server-side multipart parsing error`;
4597
+ }
4598
+ if (error.message && error.message.includes("ENOENT")) {
4599
+ return `File not found error: ${context}. Please check:
4600
+ - File path is correct
4601
+ - File exists and is accessible
4602
+ - No permission issues`;
4603
+ }
4604
+ if (error.message && error.message.includes("EACCES")) {
4605
+ return `Permission denied error: ${context}. Please check:
4606
+ - File read permissions
4607
+ - Directory access permissions
4608
+ - User has sufficient privileges`;
4609
+ }
4610
+ return `Upload error: ${error.message}`;
4611
+ }
4525
4612
  var ERROR_CODES = {
4526
- "30001": `File too large, single file max size: ${"20"}MB,single folder max size: ${"500"}MB`,
4613
+ "30001": `File too large, single file max size: ${"500"}MB,single folder max size: ${"500"}MB`,
4527
4614
  "30002": `Max storage quorum ${Number("1000") / 1e3} GB reached`
4528
4615
  };
4529
- var dirPath = null;
4530
- function loadFilesToArrRecursively(directoryPath, dist) {
4616
+ function loadFilesToArrRecursively(directoryPath, dist, basePath) {
4531
4617
  const filesArr = [];
4532
4618
  const sep = import_path4.default.sep;
4533
- dirPath = dirPath || directoryPath.replace(dist, "");
4619
+ if (!basePath) {
4620
+ const parentDir = import_path4.default.dirname(directoryPath);
4621
+ basePath = parentDir.endsWith(sep) ? parentDir : parentDir + sep;
4622
+ }
4534
4623
  if (import_fs_extra3.default.statSync(directoryPath).isDirectory()) {
4535
4624
  const files = import_fs_extra3.default.readdirSync(directoryPath);
4536
4625
  files.forEach((file) => {
@@ -4544,14 +4633,18 @@ function loadFilesToArrRecursively(directoryPath, dist) {
4544
4633
  )} (size: ${formatSize(sizeCheck.size)})`
4545
4634
  );
4546
4635
  }
4547
- const filePathWithNoEndSep = filePath.replace(dirPath, "");
4548
- const filePathEncodeSep = filePathWithNoEndSep.replaceAll(sep, "%2F");
4636
+ const relativePath = filePath.replace(basePath, "");
4637
+ const encodedPath = relativePath.replaceAll(sep, "%2F");
4549
4638
  filesArr.push({
4550
- name: filePathEncodeSep,
4639
+ name: encodedPath,
4551
4640
  path: filePath
4552
4641
  });
4553
4642
  } else if (import_fs_extra3.default.statSync(filePath).isDirectory()) {
4554
- const recursiveFiles = loadFilesToArrRecursively(filePath, dist);
4643
+ const recursiveFiles = loadFilesToArrRecursively(
4644
+ filePath,
4645
+ dist,
4646
+ basePath
4647
+ );
4555
4648
  filesArr.push(...recursiveFiles);
4556
4649
  }
4557
4650
  });
@@ -4589,18 +4682,47 @@ async function uploadDirectory(directoryPath, deviceId) {
4589
4682
  const dist = directoryPath.split(import_path4.default.sep).pop() || "";
4590
4683
  const files = loadFilesToArrRecursively(directoryPath, dist);
4591
4684
  const totalFiles = files.length;
4685
+ if (totalFiles === 0) {
4686
+ throw new Error(
4687
+ `Directory ${directoryPath} is empty or contains no valid files`
4688
+ );
4689
+ }
4592
4690
  files.forEach((file) => {
4691
+ if (!import_fs_extra3.default.existsSync(file.path)) {
4692
+ throw new Error(`File not found: ${file.path}`);
4693
+ }
4694
+ const fileStats = import_fs_extra3.default.statSync(file.path);
4695
+ if (!fileStats.isFile()) {
4696
+ throw new Error(`Path is not a file: ${file.path}`);
4697
+ }
4593
4698
  formData.append("file", import_fs_extra3.default.createReadStream(file.path), {
4594
4699
  filename: file.name
4595
4700
  });
4596
4701
  });
4597
4702
  const startTime = Date.now();
4598
- const spinner = (0, import_ora.default)(`Uploading ${dist} (${totalFiles} files)... 0s`).start();
4703
+ const spinner = (0, import_ora.default)(`Preparing upload...`).start();
4704
+ let totalSize = 0;
4705
+ files.forEach((file) => {
4706
+ try {
4707
+ const stats = import_fs_extra3.default.statSync(file.path);
4708
+ totalSize += stats.size;
4709
+ } catch (error) {
4710
+ }
4711
+ });
4712
+ const smartProgress = new SmartProgressBar(
4713
+ dist,
4714
+ totalFiles,
4715
+ totalSize,
4716
+ spinner
4717
+ );
4599
4718
  const timeInterval = setInterval(() => {
4600
- const elapsed = Math.floor((Date.now() - startTime) / 1e3);
4601
- spinner.text = `Uploading ${dist} (${totalFiles} files)... ${elapsed}s`;
4719
+ smartProgress.updateTime();
4602
4720
  }, 1e3);
4721
+ const progressInterval = setInterval(() => {
4722
+ smartProgress.updateProgress();
4723
+ }, 200);
4603
4724
  try {
4725
+ smartProgress.startUpload();
4604
4726
  const response = await axios_default.post(
4605
4727
  `${ipfsApiUrl}/add?uid=${deviceId}&cidV=1`,
4606
4728
  formData,
@@ -4612,52 +4734,97 @@ async function uploadDirectory(directoryPath, deviceId) {
4612
4734
  // 30 minutes timeout
4613
4735
  }
4614
4736
  );
4615
- clearInterval(timeInterval);
4616
- const resData = response.data.data;
4617
- if (Array.isArray(resData) && resData.length > 0) {
4618
- const directoryItem = resData.find((item) => item.Name === dist);
4619
- if (directoryItem) {
4620
- const elapsed = Math.floor((Date.now() - startTime) / 1e3);
4621
- spinner.succeed(
4622
- `Successfully uploaded ${dist} (${totalFiles} files) in ${elapsed}s`
4623
- );
4624
- const fileCount = countFilesInDirectory(directoryPath);
4625
- const uploadData = {
4626
- path: directoryPath,
4627
- filename: import_path4.default.basename(directoryPath),
4628
- contentHash: directoryItem.Hash,
4629
- previewHash: null,
4630
- size: sizeCheck.size,
4631
- fileCount,
4632
- isDirectory: true,
4633
- shortUrl: directoryItem.ShortUrl || null
4634
- };
4635
- saveUploadHistory(uploadData);
4636
- return {
4637
- hash: directoryItem.Hash,
4638
- shortUrl: directoryItem.ShortUrl
4639
- };
4640
- }
4641
- spinner.fail(`Directory hash not found in response`);
4642
- console.log(import_chalk2.default.red(`Directory hash not found in response`));
4643
- } else {
4644
- spinner.fail(`Invalid response format from IPFS`);
4645
- console.log(import_chalk2.default.red(`Invalid response format from IPFS`));
4737
+ clearInterval(progressInterval);
4738
+ smartProgress.startPolling();
4739
+ const { trace_id } = response.data.data;
4740
+ if (!trace_id) {
4741
+ smartProgress.fail("No request id received from server");
4742
+ clearInterval(timeInterval);
4743
+ return null;
4646
4744
  }
4745
+ smartProgress.updateDisplay();
4746
+ const uploadResult = await pollUploadStatus(
4747
+ trace_id,
4748
+ deviceId,
4749
+ smartProgress,
4750
+ startTime
4751
+ );
4752
+ if (!uploadResult) {
4753
+ clearInterval(timeInterval);
4754
+ return null;
4755
+ }
4756
+ const directoryItem = uploadResult.upload_rst;
4757
+ if (directoryItem) {
4758
+ const fileCount = countFilesInDirectory(directoryPath);
4759
+ const uploadData = {
4760
+ path: directoryPath,
4761
+ filename: import_path4.default.basename(directoryPath),
4762
+ contentHash: directoryItem.Hash,
4763
+ previewHash: null,
4764
+ size: sizeCheck.size,
4765
+ fileCount,
4766
+ isDirectory: true,
4767
+ shortUrl: directoryItem.ShortUrl || null
4768
+ };
4769
+ saveUploadHistory(uploadData);
4770
+ clearInterval(timeInterval);
4771
+ return {
4772
+ hash: directoryItem.Hash,
4773
+ shortUrl: directoryItem.ShortUrl
4774
+ };
4775
+ }
4776
+ const diagnosticInfo = diagnoseDirectoryUploadError(
4777
+ dist,
4778
+ uploadResult.upload_rst,
4779
+ dist
4780
+ );
4781
+ smartProgress.fail("Directory hash not found in response");
4782
+ console.log(
4783
+ import_chalk2.default.red(
4784
+ `
4785
+ \u274C Directory upload failed: Directory hash not found in response`
4786
+ )
4787
+ );
4788
+ console.log(import_chalk2.default.yellow(`
4789
+ \u{1F4CB} Error diagnosis information:`));
4790
+ console.log(import_chalk2.default.gray(` - ${diagnosticInfo}`));
4791
+ console.log(import_chalk2.default.blue(`
4792
+ \u{1F527} Solutions:`));
4793
+ console.log(
4794
+ import_chalk2.default.gray(` 1. Ensure directory is not empty and contains valid files`)
4795
+ );
4796
+ console.log(import_chalk2.default.gray(` 2. Check network connection stability`));
4797
+ console.log(
4798
+ import_chalk2.default.gray(` 3. Try uploading a smaller directory for testing`)
4799
+ );
4800
+ clearInterval(timeInterval);
4647
4801
  return null;
4648
4802
  } catch (error) {
4803
+ clearInterval(progressInterval);
4649
4804
  clearInterval(timeInterval);
4805
+ if (error.message && error.message.includes("multipart")) {
4806
+ const errorMessage = handleMultipartError(
4807
+ error,
4808
+ `Directory upload: ${dist}`
4809
+ );
4810
+ smartProgress.fail(errorMessage);
4811
+ console.log(import_chalk2.default.red(`
4812
+ \u274C ${errorMessage}`));
4813
+ return null;
4814
+ }
4650
4815
  if (error.response && error.response.data && error.response.data.code) {
4651
4816
  const errorCode = error.response.data.code.toString();
4652
4817
  if (ERROR_CODES[errorCode]) {
4653
- spinner.fail(`Error: ${ERROR_CODES[errorCode]} (Code: ${errorCode})`);
4818
+ smartProgress.fail(
4819
+ `Error: ${ERROR_CODES[errorCode]} (Code: ${errorCode})`
4820
+ );
4654
4821
  console.log(
4655
4822
  import_chalk2.default.red(`Error: ${ERROR_CODES[errorCode]} (Code: ${errorCode})`)
4656
4823
  );
4657
4824
  return null;
4658
4825
  }
4659
4826
  }
4660
- spinner.fail(`Error: ${error.message}`);
4827
+ smartProgress.fail(`Error: ${error.message}`);
4661
4828
  console.log(import_chalk2.default.red(`Error: ${error.message}`));
4662
4829
  return null;
4663
4830
  }
@@ -4672,13 +4839,37 @@ async function uploadFile(filePath, deviceId) {
4672
4839
  );
4673
4840
  }
4674
4841
  const fileName = filePath.split(import_path4.default.sep).pop() || "";
4842
+ if (!import_fs_extra3.default.existsSync(filePath)) {
4843
+ throw new Error(`File not found: ${filePath}`);
4844
+ }
4845
+ const fileStats = import_fs_extra3.default.statSync(filePath);
4846
+ if (!fileStats.isFile()) {
4847
+ throw new Error(`Path is not a file: ${filePath}`);
4848
+ }
4849
+ console.log(import_chalk2.default.blue("\n\u{1F4C4} File Upload Analysis:"));
4850
+ console.log(import_chalk2.default.gray(` File path: ${filePath}`));
4851
+ console.log(import_chalk2.default.gray(` File name: ${fileName}`));
4852
+ console.log(import_chalk2.default.gray(` File size: ${formatSize(fileStats.size)}`));
4853
+ console.log(import_chalk2.default.gray(` File exists: ${import_fs_extra3.default.existsSync(filePath)}`));
4854
+ console.log(import_chalk2.default.gray(` Is file: ${fileStats.isFile()}
4855
+ `));
4675
4856
  const startTime = Date.now();
4676
- const spinner = (0, import_ora.default)(`Uploading ${fileName}... 0s`).start();
4857
+ const spinner = (0, import_ora.default)(`Preparing upload...`).start();
4858
+ let totalSize = 0;
4859
+ try {
4860
+ const stats = import_fs_extra3.default.statSync(filePath);
4861
+ totalSize = stats.size;
4862
+ } catch (error) {
4863
+ }
4864
+ const smartProgress = new SmartProgressBar(fileName, 1, totalSize, spinner);
4677
4865
  const timeInterval = setInterval(() => {
4678
- const elapsed = Math.floor((Date.now() - startTime) / 1e3);
4679
- spinner.text = `Uploading ${fileName}... ${elapsed}s`;
4866
+ smartProgress.updateTime();
4680
4867
  }, 1e3);
4868
+ const progressInterval = setInterval(() => {
4869
+ smartProgress.updateProgress();
4870
+ }, 200);
4681
4871
  try {
4872
+ smartProgress.startUpload();
4682
4873
  const formData = new import_form_data2.default();
4683
4874
  const encodedFileName = encodeURIComponent(fileName);
4684
4875
  formData.append("file", import_fs_extra3.default.createReadStream(filePath), {
@@ -4695,53 +4886,259 @@ async function uploadFile(filePath, deviceId) {
4695
4886
  // 30 minutes timeout
4696
4887
  }
4697
4888
  );
4698
- clearInterval(timeInterval);
4699
- const resData = response.data.data;
4700
- if (Array.isArray(resData) && resData.length > 0) {
4701
- const fileItem = resData.find((item) => item.Name === fileName);
4702
- if (fileItem) {
4703
- const elapsed = Math.floor((Date.now() - startTime) / 1e3);
4704
- spinner.succeed(`Successfully uploaded ${fileName} in ${elapsed}s`);
4705
- const uploadData = {
4706
- path: filePath,
4707
- filename: fileName,
4708
- contentHash: fileItem.Hash,
4709
- previewHash: null,
4710
- size: sizeCheck.size,
4711
- fileCount: 1,
4712
- isDirectory: false,
4713
- shortUrl: fileItem.ShortUrl || null
4714
- };
4715
- saveUploadHistory(uploadData);
4716
- return {
4717
- hash: fileItem.Hash,
4718
- shortUrl: fileItem.ShortUrl
4719
- };
4720
- }
4721
- spinner.fail(`File hash not found in response`);
4722
- console.log(import_chalk2.default.red(`File hash not found in response`));
4723
- } else {
4724
- spinner.fail(`Invalid response format from IPFS`);
4725
- console.log(import_chalk2.default.red(`Invalid response format from IPFS`));
4889
+ clearInterval(progressInterval);
4890
+ smartProgress.startPolling();
4891
+ const trace_id = response.data.data.trace_id;
4892
+ if (!trace_id) {
4893
+ smartProgress.fail("No request id received from server");
4894
+ clearInterval(timeInterval);
4895
+ return null;
4726
4896
  }
4897
+ smartProgress.updateDisplay();
4898
+ const uploadResult = await pollUploadStatus(
4899
+ trace_id,
4900
+ deviceId,
4901
+ smartProgress,
4902
+ startTime
4903
+ );
4904
+ if (!uploadResult) {
4905
+ clearInterval(timeInterval);
4906
+ return null;
4907
+ }
4908
+ const fileItem = uploadResult.upload_rst;
4909
+ if (fileItem) {
4910
+ const uploadData = {
4911
+ path: filePath,
4912
+ filename: fileName,
4913
+ contentHash: fileItem.Hash,
4914
+ previewHash: null,
4915
+ size: sizeCheck.size,
4916
+ fileCount: 1,
4917
+ isDirectory: false,
4918
+ shortUrl: fileItem.ShortUrl || null
4919
+ };
4920
+ saveUploadHistory(uploadData);
4921
+ smartProgress.complete(trace_id);
4922
+ clearInterval(timeInterval);
4923
+ return {
4924
+ hash: fileItem.Hash,
4925
+ shortUrl: fileItem.ShortUrl
4926
+ };
4927
+ }
4928
+ smartProgress.fail("File hash not found in response");
4929
+ console.log(
4930
+ import_chalk2.default.red(`
4931
+ \u274C File upload failed: File hash not found in response`)
4932
+ );
4933
+ console.log(import_chalk2.default.yellow(`
4934
+ \u{1F4CB} Error diagnosis information:`));
4935
+ console.log(import_chalk2.default.gray(` - File name: ${fileName}`));
4936
+ console.log(
4937
+ import_chalk2.default.gray(` - Name returned by IPFS: ${uploadResult.upload_rst.Name}`)
4938
+ );
4939
+ console.log(import_chalk2.default.blue(`
4940
+ \u{1F527} Solutions:`));
4941
+ console.log(import_chalk2.default.gray(` 1. Check if file is corrupted or unreadable`));
4942
+ console.log(import_chalk2.default.gray(` 2. Check network connection stability`));
4943
+ console.log(import_chalk2.default.gray(` 3. Try uploading a smaller file for testing`));
4944
+ clearInterval(timeInterval);
4727
4945
  return null;
4728
4946
  } catch (error) {
4947
+ clearInterval(progressInterval);
4729
4948
  clearInterval(timeInterval);
4949
+ if (error.message && error.message.includes("multipart")) {
4950
+ const errorMessage = handleMultipartError(
4951
+ error,
4952
+ `File upload: ${fileName}`
4953
+ );
4954
+ smartProgress.fail(errorMessage);
4955
+ console.log(import_chalk2.default.red(`
4956
+ \u274C ${errorMessage}`));
4957
+ return null;
4958
+ }
4730
4959
  if (error.response && error.response.data && error.response.data.code) {
4731
4960
  const errorCode = error.response.data.code.toString();
4732
4961
  if (ERROR_CODES[errorCode]) {
4733
- spinner.fail(`Error: ${ERROR_CODES[errorCode]} (Code: ${errorCode})`);
4962
+ smartProgress.fail(
4963
+ `Error: ${ERROR_CODES[errorCode]} (Code: ${errorCode})`
4964
+ );
4734
4965
  console.log(
4735
4966
  import_chalk2.default.red(`Error: ${ERROR_CODES[errorCode]} (Code: ${errorCode})`)
4736
4967
  );
4737
4968
  return null;
4738
4969
  }
4739
4970
  }
4740
- spinner.fail(`Error: ${error.message}`);
4971
+ smartProgress.fail(`Error: ${error.message}`);
4741
4972
  console.log(import_chalk2.default.red(`Error: ${error.message}`));
4742
4973
  return null;
4743
4974
  }
4744
4975
  }
4976
+ var SmartProgressBar = class {
4977
+ timeConstant;
4978
+ startTime;
4979
+ uploadStartTime;
4980
+ isUploading;
4981
+ isPolling;
4982
+ fileCount;
4983
+ totalSize;
4984
+ spinner;
4985
+ fileName;
4986
+ isCompleted;
4987
+ // Add completion flag
4988
+ constructor(fileName, fileCount, totalSize, spinner) {
4989
+ this.fileName = fileName;
4990
+ this.fileCount = fileCount;
4991
+ this.totalSize = totalSize;
4992
+ this.spinner = spinner;
4993
+ this.timeConstant = this.calcTimeConstant(fileCount, totalSize);
4994
+ this.startTime = Date.now();
4995
+ this.uploadStartTime = 0;
4996
+ this.isUploading = false;
4997
+ this.isPolling = false;
4998
+ this.isCompleted = false;
4999
+ }
5000
+ // Calculate time constant based on file count and total size
5001
+ calcTimeConstant(fileCount, totalSize) {
5002
+ const base = 8e3;
5003
+ const countFactor = 0.3 * Math.log(1 + fileCount);
5004
+ const sizeFactor = 0.7 * Math.log(1 + totalSize / 1024 / 1024);
5005
+ const minTimeConstant = 15e3;
5006
+ const calculatedTimeConstant = base * (1 + countFactor + sizeFactor);
5007
+ return Math.max(calculatedTimeConstant, minTimeConstant);
5008
+ }
5009
+ // Calculate progress using exponential decay model with upper limit
5010
+ calculateProgress() {
5011
+ const elapsed = Date.now() - this.startTime;
5012
+ const rawProgress = 1 - Math.exp(-elapsed / this.timeConstant);
5013
+ const maxProgress = this.isPolling ? 0.95 : 0.9;
5014
+ return Math.min(rawProgress, maxProgress);
5015
+ }
5016
+ // Start upload phase
5017
+ startUpload() {
5018
+ this.isUploading = true;
5019
+ this.uploadStartTime = Date.now();
5020
+ }
5021
+ // Start polling phase
5022
+ startPolling() {
5023
+ this.isPolling = true;
5024
+ this.isUploading = false;
5025
+ }
5026
+ // Update progress display
5027
+ update() {
5028
+ if (this.isCompleted) {
5029
+ return;
5030
+ }
5031
+ const progress = this.calculateProgress();
5032
+ const elapsed = Math.floor((Date.now() - this.startTime) / 1e3);
5033
+ const progressBar = this.createProgressBar(progress);
5034
+ const duration = this.formatDuration(elapsed);
5035
+ let status = "";
5036
+ if (this.isUploading) {
5037
+ status = "uploading";
5038
+ } else if (this.isPolling) {
5039
+ status = "processing";
5040
+ } else {
5041
+ status = "preparing";
5042
+ }
5043
+ const fileInfo = this.fileCount > 1 ? `${this.fileName} (${this.fileCount} files)` : this.fileName;
5044
+ this.spinner.text = `Uploading ${fileInfo} ${progressBar} ${duration} (${status})`;
5045
+ }
5046
+ // Update time display only (called every second)
5047
+ updateTime() {
5048
+ if (this.isCompleted) {
5049
+ return;
5050
+ }
5051
+ const elapsed = Math.floor((Date.now() - this.startTime) / 1e3);
5052
+ const duration = this.formatDuration(elapsed);
5053
+ const progress = this.calculateProgress();
5054
+ const progressBar = this.createProgressBar(progress);
5055
+ let status = "";
5056
+ if (this.isUploading) {
5057
+ status = "uploading";
5058
+ } else if (this.isPolling) {
5059
+ status = "processing";
5060
+ } else {
5061
+ status = "preparing";
5062
+ }
5063
+ const fileInfo = this.fileCount > 1 ? `${this.fileName} (${this.fileCount} files)` : this.fileName;
5064
+ this.spinner.text = `Uploading ${fileInfo} ${progressBar} ${duration} (${status})`;
5065
+ }
5066
+ // Update progress bar only (called every 200ms)
5067
+ updateProgress() {
5068
+ if (this.isCompleted) {
5069
+ return;
5070
+ }
5071
+ const progress = this.calculateProgress();
5072
+ const elapsed = Math.floor((Date.now() - this.startTime) / 1e3);
5073
+ const progressBar = this.createProgressBar(progress);
5074
+ const duration = this.formatDuration(elapsed);
5075
+ let status = "";
5076
+ if (this.isUploading) {
5077
+ status = "uploading";
5078
+ } else if (this.isPolling) {
5079
+ status = "processing";
5080
+ } else {
5081
+ status = "preparing";
5082
+ }
5083
+ const fileInfo = this.fileCount > 1 ? `${this.fileName} (${this.fileCount} files)` : this.fileName;
5084
+ this.spinner.text = `Uploading ${fileInfo} ${progressBar} ${duration} (${status})`;
5085
+ }
5086
+ // Update display (for manual updates)
5087
+ updateDisplay() {
5088
+ this.updateTime();
5089
+ }
5090
+ // Update progress display only (no progress bar)
5091
+ updateTimeOnly() {
5092
+ const elapsed = Math.floor((Date.now() - this.startTime) / 1e3);
5093
+ const duration = this.formatDuration(elapsed);
5094
+ this.spinner.text = `Uploading ${this.fileName} ${duration}`;
5095
+ }
5096
+ // Complete progress
5097
+ complete(traceId) {
5098
+ if (this.isCompleted) {
5099
+ return;
5100
+ }
5101
+ const progressBar = this.createProgressBar(1);
5102
+ const elapsed = Math.floor((Date.now() - this.startTime) / 1e3);
5103
+ const duration = this.formatDuration(elapsed);
5104
+ this.spinner.succeed(`Upload completed ${progressBar} ${duration}`);
5105
+ this.isCompleted = true;
5106
+ }
5107
+ // Fail progress
5108
+ fail(message, traceId) {
5109
+ if (this.isCompleted) {
5110
+ return;
5111
+ }
5112
+ const elapsed = Math.floor((Date.now() - this.startTime) / 1e3);
5113
+ const duration = this.formatDuration(elapsed);
5114
+ this.spinner.fail(`${message} ${duration}`);
5115
+ this.isCompleted = true;
5116
+ }
5117
+ // Create visual progress bar
5118
+ createProgressBar(progress, width = 20) {
5119
+ const percentage = Math.min(progress, 1);
5120
+ const filledWidth = Math.round(width * percentage);
5121
+ const emptyWidth = width - filledWidth;
5122
+ const filled = "\u2588".repeat(filledWidth);
5123
+ const empty = "\u2591".repeat(emptyWidth);
5124
+ return `[${filled}${empty}] ${Math.round(percentage * 100)}%`;
5125
+ }
5126
+ // Format time duration
5127
+ formatDuration(seconds) {
5128
+ if (seconds < 60) {
5129
+ return `${seconds}s`;
5130
+ } else if (seconds < 3600) {
5131
+ const minutes = Math.floor(seconds / 60);
5132
+ const remainingSeconds = seconds % 60;
5133
+ return `${minutes}m ${remainingSeconds}s`;
5134
+ } else {
5135
+ const hours = Math.floor(seconds / 3600);
5136
+ const minutes = Math.floor(seconds % 3600 / 60);
5137
+ const remainingSeconds = seconds % 60;
5138
+ return `${hours}h ${minutes}m ${remainingSeconds}s`;
5139
+ }
5140
+ }
5141
+ };
4745
5142
  async function uploadToIpfs_default(filePath) {
4746
5143
  const deviceId = getDeviceId();
4747
5144
  if (!deviceId) {
@@ -4881,7 +5278,7 @@ var import_figlet2 = __toESM(require("figlet"));
4881
5278
 
4882
5279
  // bin/utils/removeFromIpfs.ts
4883
5280
  var import_chalk4 = __toESM(require("chalk"));
4884
- var ipfsApiUrl2 = "https://pinme.dev/api/v2";
5281
+ var ipfsApiUrl2 = "https://pinme.dev/api/v3";
4885
5282
  async function removeFromIpfs(value, type = "hash") {
4886
5283
  try {
4887
5284
  const uid = getDeviceId();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinme",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },