pinme 1.2.0 → 1.2.1

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 +93 -6
  2. package/dist/index.js +276 -132
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -25,7 +25,6 @@ PinMe handles availability and persistence for you.
25
25
 
26
26
  Website: [https://pinme.eth.limo/](https://pinme.eth.limo/)
27
27
 
28
-
29
28
  ## Installation
30
29
 
31
30
  ### Using npm
@@ -92,6 +91,23 @@ pinme set-appkey
92
91
  pinme set-appkey <AppKey>
93
92
  ```
94
93
 
94
+ ### View AppKey information
95
+
96
+ ```bash
97
+ # Show current AppKey (masked for security)
98
+ pinme show-appkey
99
+
100
+ # Or use the shorthand command
101
+ pinme appkey
102
+ ```
103
+
104
+ ### Log out
105
+
106
+ ```bash
107
+ # Log out and clear authentication
108
+ pinme logout
109
+ ```
110
+
95
111
  ### View your domains
96
112
 
97
113
  ```bash
@@ -120,10 +136,12 @@ pinme upload [path] [--domain <name>]
120
136
  ```
121
137
 
122
138
  **Options:**
139
+
123
140
  - `path`: Path to the file or directory to upload (optional, if not provided, interactive mode will be entered)
124
141
  - `-d, --domain <name>`: Pinme subdomain to bind after upload (optional)
125
142
 
126
143
  **Examples:**
144
+
127
145
  ```bash
128
146
  # Interactive upload
129
147
  pinme upload
@@ -148,9 +166,11 @@ pinme rm [hash]
148
166
  ```
149
167
 
150
168
  **Options:**
169
+
151
170
  - `hash`: IPFS content hash to remove (optional, if not provided, interactive mode will be entered)
152
171
 
153
172
  **Examples:**
173
+
154
174
  ```bash
155
175
  # Interactive removal
156
176
  pinme rm
@@ -171,10 +191,12 @@ pinme ls [options]
171
191
  ```
172
192
 
173
193
  **Options:**
194
+
174
195
  - `-l, --limit <number>`: Limit the number of records displayed
175
196
  - `-c, --clear`: Clear all upload history
176
197
 
177
198
  **Examples:**
199
+
178
200
  ```bash
179
201
  # Show the last 10 records
180
202
  pinme list
@@ -195,9 +217,11 @@ pinme set-appkey [AppKey]
195
217
  ```
196
218
 
197
219
  **Options:**
220
+
198
221
  - `AppKey`: Your AppKey for authentication (optional, if not provided, interactive mode will be entered)
199
222
 
200
223
  **Examples:**
224
+
201
225
  ```bash
202
226
  # Interactive AppKey setup
203
227
  pinme set-appkey
@@ -208,6 +232,55 @@ pinme set-appkey your-app-key-here
208
232
 
209
233
  **Note:** After setting the AppKey, your anonymous upload history will be automatically merged to your account.
210
234
 
235
+ ### `show-appkey` / `appkey`
236
+
237
+ Display current AppKey information with masked sensitive data.
238
+
239
+ ```bash
240
+ pinme show-appkey
241
+ pinme appkey
242
+ ```
243
+
244
+ **Description:**
245
+
246
+ This command shows the current AppKey information including:
247
+ - Address (fully displayed)
248
+ - Token (masked for security)
249
+ - AppKey (masked for security)
250
+
251
+ **Examples:**
252
+
253
+ ```bash
254
+ # Show AppKey information
255
+ pinme show-appkey
256
+
257
+ # Shorthand command
258
+ pinme appkey
259
+ ```
260
+
261
+ **Note:** Sensitive information (token and AppKey) will be masked to protect your credentials. Only the address is fully displayed.
262
+
263
+ ### `logout`
264
+
265
+ Log out and clear authentication information from local storage.
266
+
267
+ ```bash
268
+ pinme logout
269
+ ```
270
+
271
+ **Description:**
272
+
273
+ This command logs out the current user and removes the authentication information from local storage. After logging out, you will need to set your AppKey again to use authenticated features.
274
+
275
+ **Examples:**
276
+
277
+ ```bash
278
+ # Log out
279
+ pinme logout
280
+ ```
281
+
282
+ **Note:** This action will remove your AppKey from local storage. You can set it again using `pinme set-appkey` command.
283
+
211
284
  ### `my-domains` / `domain`
212
285
 
213
286
  List all domains owned by the current account.
@@ -218,6 +291,7 @@ pinme domain
218
291
  ```
219
292
 
220
293
  **Examples:**
294
+
221
295
  ```bash
222
296
  # List all domains
223
297
  pinme my-domains
@@ -227,6 +301,7 @@ pinme domain
227
301
  ```
228
302
 
229
303
  This command displays information about each domain including:
304
+
230
305
  - Domain name
231
306
  - Domain type
232
307
  - Bind time
@@ -241,9 +316,11 @@ pinme help [command]
241
316
  ```
242
317
 
243
318
  **Options:**
319
+
244
320
  - `command`: The specific command to view help for (optional)
245
321
 
246
322
  **Examples:**
323
+
247
324
  ```bash
248
325
  # Display general help
249
326
  pinme help
@@ -251,8 +328,8 @@ pinme help
251
328
 
252
329
  ## Upload Limits
253
330
 
254
- - Single file size limit: 20MB
255
- - Total directory size limit: 500MB
331
+ - Single file size limit: 200MB (free plan)
332
+ - Total directory size limit: 1GB (free plan)
256
333
 
257
334
  ## File Storage
258
335
 
@@ -264,10 +341,10 @@ Uploaded files are stored on the IPFS network and accessible through the Glitter
264
341
  ### Log Locations
265
342
 
266
343
  Logs and configuration files are stored in:
344
+
267
345
  - Linux/macOS: `~/.pinme/`
268
346
  - Windows: `%USERPROFILE%\.pinme\`
269
347
 
270
-
271
348
  ## License
272
349
 
273
350
  MIT License - See the [LICENSE](LICENSE) file for details
@@ -283,9 +360,9 @@ When uploading projects built with Vite, please note:
283
360
  ```js
284
361
  // vite.config.js
285
362
  export default {
286
- base: "./",
363
+ base: './',
287
364
  // other configurations...
288
- }
365
+ };
289
366
  ```
290
367
 
291
368
  ## GitHub Actions Integration
@@ -295,10 +372,12 @@ PinMe can be integrated with GitHub Actions to automatically deploy your project
295
372
  ### Quick Setup
296
373
 
297
374
  1. **Add the workflow file** to your repository:
375
+
298
376
  - Copy `.github/workflows/deploy.yml` from the PinMe repository to your project
299
377
  - Or create `.github/workflows/deploy.yml` in your repository
300
378
 
301
379
  2. **Configure GitHub Secrets**:
380
+
302
381
  - Go to your repository → Settings → Secrets and variables → Actions
303
382
  - Add a new secret named `PINME_APPKEY` with your PinMe AppKey
304
383
  - (Optional) Add `PINME_DOMAIN` to specify a custom domain name
@@ -326,6 +405,7 @@ The GitHub Actions workflow automatically:
326
405
  You can configure the following secrets in your repository:
327
406
 
328
407
  - **`PINME_APPKEY`** (Required): Your PinMe AppKey for authentication
408
+
329
409
  - Format: `<address>-<jwt>`
330
410
  - Get your AppKey from [PinMe website](https://pinme.eth.limo/)
331
411
 
@@ -390,18 +470,25 @@ The workflow automatically detects and supports:
390
470
  ### Troubleshooting
391
471
 
392
472
  **Build directory not found:**
473
+
393
474
  - Ensure your build script outputs to a standard directory (`dist`, `build`, `public`, or `out`)
394
475
  - Or set `PINME_DOMAIN` secret and use manual workflow dispatch to specify custom directory
395
476
 
396
477
  **Authentication failed:**
478
+
397
479
  - Verify your `PINME_APPKEY` secret is correctly set
398
480
  - Ensure the AppKey format is correct: `<address>-<jwt>`
399
481
 
400
482
  **Domain binding failed:**
483
+
401
484
  - Check if the domain name is available
402
485
  - Ensure you have permission to bind the domain
403
486
  - Try a different domain name
404
487
 
488
+ ## Star History
489
+
490
+ [![Star History Chart](https://api.star-history.com/svg?repos=glitternetwork/pinme&type=Date)](https://star-history.com/#glitternetwork/pinme&Date)
491
+
405
492
  ## Contact Us
406
493
 
407
494
  If you have questions or suggestions, please contact us through:
package/dist/index.js CHANGED
@@ -1519,11 +1519,11 @@ function checkNodeVersion() {
1519
1519
 
1520
1520
  // bin/index.ts
1521
1521
  var import_commander = require("commander");
1522
- var import_chalk9 = __toESM(require("chalk"));
1522
+ var import_chalk11 = __toESM(require("chalk"));
1523
1523
  var import_figlet3 = __toESM(require("figlet"));
1524
1524
 
1525
1525
  // package.json
1526
- var version = "1.2.0";
1526
+ var version = "1.2.1";
1527
1527
 
1528
1528
  // bin/upload.ts
1529
1529
  var import_path6 = __toESM(require("path"));
@@ -4399,8 +4399,8 @@ var {
4399
4399
  } = axios_default;
4400
4400
 
4401
4401
  // bin/utils/uploadToIpfsSplit.ts
4402
- var import_fs_extra3 = __toESM(require("fs-extra"));
4403
- var import_path4 = __toESM(require("path"));
4402
+ var import_fs_extra4 = __toESM(require("fs-extra"));
4403
+ var import_path5 = __toESM(require("path"));
4404
4404
  var import_form_data2 = __toESM(require("form-data"));
4405
4405
  var import_ora = __toESM(require("ora"));
4406
4406
  var crypto = __toESM(require("crypto"));
@@ -4540,23 +4540,91 @@ var clearUploadHistory = () => {
4540
4540
  };
4541
4541
 
4542
4542
  // bin/utils/getDeviceId.ts
4543
+ var import_fs_extra3 = __toESM(require("fs-extra"));
4544
+ var import_path4 = __toESM(require("path"));
4545
+ var import_os3 = __toESM(require("os"));
4546
+ var import_uuid = require("uuid");
4547
+
4548
+ // bin/utils/auth.ts
4543
4549
  var import_fs_extra2 = __toESM(require("fs-extra"));
4544
- var import_path3 = __toESM(require("path"));
4545
4550
  var import_os2 = __toESM(require("os"));
4546
- var import_uuid = require("uuid");
4551
+ var import_path3 = __toESM(require("path"));
4552
+ var CONFIG_DIR = import_path3.default.join(import_os2.default.homedir(), ".pinme");
4553
+ var AUTH_FILE = import_path3.default.join(CONFIG_DIR, "auth.json");
4554
+ function ensureConfigDir() {
4555
+ if (!import_fs_extra2.default.existsSync(CONFIG_DIR)) {
4556
+ import_fs_extra2.default.mkdirSync(CONFIG_DIR, { recursive: true });
4557
+ }
4558
+ }
4559
+ function parseCombinedToken(combined) {
4560
+ const firstDash = combined.indexOf("-");
4561
+ if (firstDash <= 0 || firstDash === combined.length - 1) {
4562
+ throw new Error('Invalid token format. Expected "<address>-<jwt>".');
4563
+ }
4564
+ const address = combined.slice(0, firstDash).trim();
4565
+ const token = combined.slice(firstDash + 1).trim();
4566
+ if (!address || !token) {
4567
+ throw new Error("Invalid token content. Address or token is empty.");
4568
+ }
4569
+ return { address, token };
4570
+ }
4571
+ function setAuthToken(combined) {
4572
+ ensureConfigDir();
4573
+ const auth = parseCombinedToken(combined);
4574
+ import_fs_extra2.default.writeJsonSync(AUTH_FILE, auth, { spaces: 2 });
4575
+ return auth;
4576
+ }
4577
+ function clearAuthToken() {
4578
+ try {
4579
+ if (import_fs_extra2.default.existsSync(AUTH_FILE)) {
4580
+ import_fs_extra2.default.removeSync(AUTH_FILE);
4581
+ }
4582
+ } catch (error) {
4583
+ console.error(`Failed to clear auth token: ${error}`);
4584
+ }
4585
+ }
4586
+ function getAuthConfig() {
4587
+ try {
4588
+ if (!import_fs_extra2.default.existsSync(AUTH_FILE)) return null;
4589
+ const data = import_fs_extra2.default.readJsonSync(AUTH_FILE);
4590
+ if (!(data == null ? void 0 : data.address) || !(data == null ? void 0 : data.token)) return null;
4591
+ return data;
4592
+ } catch {
4593
+ return null;
4594
+ }
4595
+ }
4596
+ function getAuthHeaders() {
4597
+ const conf = getAuthConfig();
4598
+ if (!conf) {
4599
+ throw new Error("Auth not set. Run: pinme set-appkey <AppKey>");
4600
+ }
4601
+ return {
4602
+ "token-address": conf.address,
4603
+ "authentication-tokens": conf.token
4604
+ };
4605
+ }
4606
+
4607
+ // bin/utils/getDeviceId.ts
4547
4608
  function getDeviceId() {
4548
- const configDir = import_path3.default.join(import_os2.default.homedir(), ".pinme");
4549
- const configFile = import_path3.default.join(configDir, "device-id");
4550
- if (!import_fs_extra2.default.existsSync(configDir)) {
4551
- import_fs_extra2.default.mkdirSync(configDir, { recursive: true });
4609
+ const configDir = import_path4.default.join(import_os3.default.homedir(), ".pinme");
4610
+ const configFile = import_path4.default.join(configDir, "device-id");
4611
+ if (!import_fs_extra3.default.existsSync(configDir)) {
4612
+ import_fs_extra3.default.mkdirSync(configDir, { recursive: true });
4552
4613
  }
4553
- if (import_fs_extra2.default.existsSync(configFile)) {
4554
- return import_fs_extra2.default.readFileSync(configFile, "utf8").trim();
4614
+ if (import_fs_extra3.default.existsSync(configFile)) {
4615
+ return import_fs_extra3.default.readFileSync(configFile, "utf8").trim();
4555
4616
  }
4556
4617
  const deviceId = (0, import_uuid.v4)();
4557
- import_fs_extra2.default.writeFileSync(configFile, deviceId);
4618
+ import_fs_extra3.default.writeFileSync(configFile, deviceId);
4558
4619
  return deviceId;
4559
4620
  }
4621
+ function getUid() {
4622
+ const auth = getAuthConfig();
4623
+ if (auth == null ? void 0 : auth.address) {
4624
+ return auth.address;
4625
+ }
4626
+ return getDeviceId();
4627
+ }
4560
4628
 
4561
4629
  // bin/utils/uploadToIpfsSplit.ts
4562
4630
  var IPFS_API_URL = "https://pinme.dev/api/v3";
@@ -4609,7 +4677,9 @@ var StepProgressBar = class {
4609
4677
  this.stopProgress();
4610
4678
  const totalTime = Math.floor((Date.now() - this.startTime) / 1e3);
4611
4679
  const progressBar = this.createProgressBar(1);
4612
- this.spinner.succeed(`Upload completed ${progressBar} 100% (${totalTime}s)`);
4680
+ this.spinner.succeed(
4681
+ `Upload completed ${progressBar} 100% (${totalTime}s)`
4682
+ );
4613
4683
  }
4614
4684
  fail(error) {
4615
4685
  this.stopProgress();
@@ -4639,7 +4709,10 @@ var StepProgressBar = class {
4639
4709
  }
4640
4710
  }
4641
4711
  calculateProgress(elapsed) {
4642
- return Math.min(elapsed / EXPECTED_UPLOAD_TIME * MAX_PROGRESS, MAX_PROGRESS);
4712
+ return Math.min(
4713
+ elapsed / EXPECTED_UPLOAD_TIME * MAX_PROGRESS,
4714
+ MAX_PROGRESS
4715
+ );
4643
4716
  }
4644
4717
  createProgressBar(progress, width = 20) {
4645
4718
  const percentage = Math.min(progress, 1);
@@ -4665,7 +4738,7 @@ var StepProgressBar = class {
4665
4738
  async function calculateMD5(filePath) {
4666
4739
  return new Promise((resolve, reject) => {
4667
4740
  const hash = crypto.createHash("md5");
4668
- const stream4 = import_fs_extra3.default.createReadStream(filePath);
4741
+ const stream4 = import_fs_extra4.default.createReadStream(filePath);
4669
4742
  stream4.on("data", hash.update.bind(hash));
4670
4743
  stream4.on("end", () => resolve(hash.digest("hex")));
4671
4744
  stream4.on("error", reject);
@@ -4674,20 +4747,20 @@ async function calculateMD5(filePath) {
4674
4747
  async function compressDirectory(sourcePath) {
4675
4748
  return new Promise((resolve, reject) => {
4676
4749
  const tempDir = require("os").tmpdir();
4677
- if (!import_fs_extra3.default.existsSync(tempDir)) {
4678
- import_fs_extra3.default.mkdirSync(tempDir, { recursive: true });
4750
+ if (!import_fs_extra4.default.existsSync(tempDir)) {
4751
+ import_fs_extra4.default.mkdirSync(tempDir, { recursive: true });
4679
4752
  }
4680
- const outputPath = import_path4.default.join(
4753
+ const outputPath = import_path5.default.join(
4681
4754
  tempDir,
4682
- `pinme_${import_path4.default.basename(sourcePath)}_${Date.now()}.zip`
4755
+ `pinme_${import_path5.default.basename(sourcePath)}_${Date.now()}.zip`
4683
4756
  );
4684
- const output = import_fs_extra3.default.createWriteStream(outputPath);
4757
+ const output = import_fs_extra4.default.createWriteStream(outputPath);
4685
4758
  const zlib2 = require("zlib");
4686
4759
  const gzip = zlib2.createGzip({ level: 9 });
4687
4760
  output.on("close", () => resolve(outputPath));
4688
4761
  gzip.on("error", reject);
4689
4762
  gzip.pipe(output);
4690
- const stats = import_fs_extra3.default.statSync(sourcePath);
4763
+ const stats = import_fs_extra4.default.statSync(sourcePath);
4691
4764
  if (stats.isDirectory()) {
4692
4765
  const archive = require("archiver");
4693
4766
  const archiveStream = archive("zip", { zlib: { level: 9 } });
@@ -4696,14 +4769,14 @@ async function compressDirectory(sourcePath) {
4696
4769
  archiveStream.directory(sourcePath, false);
4697
4770
  archiveStream.finalize();
4698
4771
  } else {
4699
- const fileStream = import_fs_extra3.default.createReadStream(sourcePath);
4772
+ const fileStream = import_fs_extra4.default.createReadStream(sourcePath);
4700
4773
  fileStream.pipe(gzip);
4701
4774
  }
4702
4775
  });
4703
4776
  }
4704
4777
  async function initChunkSession(filePath, deviceId, isDirectory = false) {
4705
- const stats = import_fs_extra3.default.statSync(filePath);
4706
- const fileName = import_path4.default.basename(filePath);
4778
+ const stats = import_fs_extra4.default.statSync(filePath);
4779
+ const fileName = import_path5.default.basename(filePath);
4707
4780
  const fileSize = stats.size;
4708
4781
  const md5 = await calculateMD5(filePath);
4709
4782
  try {
@@ -4804,7 +4877,7 @@ async function delayWithAbortCheck(delay, signal) {
4804
4877
  });
4805
4878
  }
4806
4879
  async function uploadFileChunks(filePath, sessionId, totalChunks, chunkSize, deviceId, progressBar) {
4807
- const fileData = import_fs_extra3.default.readFileSync(filePath);
4880
+ const fileData = import_fs_extra4.default.readFileSync(filePath);
4808
4881
  const abortController = new AbortController();
4809
4882
  let completedCount = 0;
4810
4883
  let hasFatalError = false;
@@ -4839,10 +4912,14 @@ async function uploadFileChunks(filePath, sessionId, totalChunks, chunkSize, dev
4839
4912
  });
4840
4913
  try {
4841
4914
  const results = await Promise.allSettled(uploadTasks.map((task) => task()));
4842
- const failedResults = results.filter((result) => result.status === "rejected");
4915
+ const failedResults = results.filter(
4916
+ (result) => result.status === "rejected"
4917
+ );
4843
4918
  if (failedResults.length > 0) {
4844
4919
  const firstFailure = failedResults[0];
4845
- throw new Error(firstFailure.reason.message || "Error occurred during upload");
4920
+ throw new Error(
4921
+ firstFailure.reason.message || "Error occurred during upload"
4922
+ );
4846
4923
  }
4847
4924
  if (hasFatalError) {
4848
4925
  throw new Error(fatalError || "Unknown error occurred during upload");
@@ -4935,10 +5012,12 @@ async function uploadDirectoryInChunks(directoryPath, deviceId) {
4935
5012
  const sizeCheck = checkDirectorySizeLimit(directoryPath);
4936
5013
  if (sizeCheck.exceeds) {
4937
5014
  throw new Error(
4938
- `Directory ${directoryPath} exceeds size limit ${formatSize(sizeCheck.limit)} (size: ${formatSize(sizeCheck.size)})`
5015
+ `Directory ${directoryPath} exceeds size limit ${formatSize(
5016
+ sizeCheck.limit
5017
+ )} (size: ${formatSize(sizeCheck.size)})`
4939
5018
  );
4940
5019
  }
4941
- const progressBar = new StepProgressBar(import_path4.default.basename(directoryPath), true);
5020
+ const progressBar = new StepProgressBar(import_path5.default.basename(directoryPath), true);
4942
5021
  try {
4943
5022
  progressBar.startStep(0, "Preparing compression");
4944
5023
  const compressedPath = await compressDirectory(directoryPath);
@@ -4963,12 +5042,12 @@ async function uploadDirectoryInChunks(directoryPath, deviceId) {
4963
5042
  const result = await monitorChunkProgress(traceId, deviceId, progressBar);
4964
5043
  progressBar.completeStep();
4965
5044
  try {
4966
- import_fs_extra3.default.unlinkSync(compressedPath);
5045
+ import_fs_extra4.default.unlinkSync(compressedPath);
4967
5046
  } catch (error) {
4968
5047
  }
4969
5048
  const uploadData = {
4970
5049
  path: directoryPath,
4971
- filename: import_path4.default.basename(directoryPath),
5050
+ filename: import_path5.default.basename(directoryPath),
4972
5051
  contentHash: (result == null ? void 0 : result.hash) || "unknown",
4973
5052
  size: sizeCheck.size,
4974
5053
  fileCount: 0,
@@ -4990,10 +5069,12 @@ async function uploadFileInChunks(filePath, deviceId) {
4990
5069
  const sizeCheck = checkFileSizeLimit(filePath);
4991
5070
  if (sizeCheck.exceeds) {
4992
5071
  throw new Error(
4993
- `File ${filePath} exceeds size limit ${formatSize(sizeCheck.limit)} (size: ${formatSize(sizeCheck.size)})`
5072
+ `File ${filePath} exceeds size limit ${formatSize(
5073
+ sizeCheck.limit
5074
+ )} (size: ${formatSize(sizeCheck.size)})`
4994
5075
  );
4995
5076
  }
4996
- const fileName = import_path4.default.basename(filePath);
5077
+ const fileName = import_path5.default.basename(filePath);
4997
5078
  const progressBar = new StepProgressBar(fileName, false);
4998
5079
  try {
4999
5080
  progressBar.startStep(0, "Initializing session");
@@ -5037,12 +5118,12 @@ async function uploadFileInChunks(filePath, deviceId) {
5037
5118
  }
5038
5119
  }
5039
5120
  async function uploadToIpfsSplit_default(filePath) {
5040
- const deviceId = getDeviceId();
5121
+ const deviceId = getUid();
5041
5122
  if (!deviceId) {
5042
5123
  throw new Error("Device ID not found");
5043
5124
  }
5044
5125
  try {
5045
- const isDirectory = import_fs_extra3.default.statSync(filePath).isDirectory();
5126
+ const isDirectory = import_fs_extra4.default.statSync(filePath).isDirectory();
5046
5127
  const result = isDirectory ? await uploadDirectoryInChunks(filePath, deviceId) : await uploadFileInChunks(filePath, deviceId);
5047
5128
  if (result == null ? void 0 : result.hash) {
5048
5129
  return {
@@ -5063,58 +5144,6 @@ var import_crypto_js = __toESM(require("crypto-js"));
5063
5144
 
5064
5145
  // bin/utils/pinmeApi.ts
5065
5146
  var import_chalk3 = __toESM(require("chalk"));
5066
-
5067
- // bin/utils/auth.ts
5068
- var import_fs_extra4 = __toESM(require("fs-extra"));
5069
- var import_os3 = __toESM(require("os"));
5070
- var import_path5 = __toESM(require("path"));
5071
- var CONFIG_DIR = import_path5.default.join(import_os3.default.homedir(), ".pinme");
5072
- var AUTH_FILE = import_path5.default.join(CONFIG_DIR, "auth.json");
5073
- function ensureConfigDir() {
5074
- if (!import_fs_extra4.default.existsSync(CONFIG_DIR)) {
5075
- import_fs_extra4.default.mkdirSync(CONFIG_DIR, { recursive: true });
5076
- }
5077
- }
5078
- function parseCombinedToken(combined) {
5079
- const firstDash = combined.indexOf("-");
5080
- if (firstDash <= 0 || firstDash === combined.length - 1) {
5081
- throw new Error('Invalid token format. Expected "<address>-<jwt>".');
5082
- }
5083
- const address = combined.slice(0, firstDash).trim();
5084
- const token = combined.slice(firstDash + 1).trim();
5085
- if (!address || !token) {
5086
- throw new Error("Invalid token content. Address or token is empty.");
5087
- }
5088
- return { address, token };
5089
- }
5090
- function setAuthToken(combined) {
5091
- ensureConfigDir();
5092
- const auth = parseCombinedToken(combined);
5093
- import_fs_extra4.default.writeJsonSync(AUTH_FILE, auth, { spaces: 2 });
5094
- return auth;
5095
- }
5096
- function getAuthConfig() {
5097
- try {
5098
- if (!import_fs_extra4.default.existsSync(AUTH_FILE)) return null;
5099
- const data = import_fs_extra4.default.readJsonSync(AUTH_FILE);
5100
- if (!(data == null ? void 0 : data.address) || !(data == null ? void 0 : data.token)) return null;
5101
- return data;
5102
- } catch {
5103
- return null;
5104
- }
5105
- }
5106
- function getAuthHeaders() {
5107
- const conf = getAuthConfig();
5108
- if (!conf) {
5109
- throw new Error("Auth not set. Run: pinme set-appkey <AppKey>");
5110
- }
5111
- return {
5112
- "token-address": conf.address,
5113
- "authentication-tokens": conf.token
5114
- };
5115
- }
5116
-
5117
- // bin/utils/pinmeApi.ts
5118
5147
  var DEFAULT_BASE = process.env.PINME_API_BASE || "http://ipfs-proxy.opena.chat/api/v4";
5119
5148
  function createClient() {
5120
5149
  const headers = getAuthHeaders();
@@ -5222,13 +5251,6 @@ function getDomainFromArgs() {
5222
5251
  }
5223
5252
  return null;
5224
5253
  }
5225
- function getUid() {
5226
- const auth = getAuthConfig();
5227
- if (auth == null ? void 0 : auth.address) {
5228
- return auth.address;
5229
- }
5230
- return getDeviceId();
5231
- }
5232
5254
  var upload_default = async (options) => {
5233
5255
  try {
5234
5256
  console.log(
@@ -5251,7 +5273,11 @@ var upload_default = async (options) => {
5251
5273
  if (domainArg) {
5252
5274
  const check = await checkDomainAvailable(domainArg);
5253
5275
  if (!check.is_valid) {
5254
- console.log(import_chalk4.default.red(`Domain not available: ${check.error || "unknown reason"}`));
5276
+ console.log(
5277
+ import_chalk4.default.red(
5278
+ `Domain not available: ${check.error || "unknown reason"}`
5279
+ )
5280
+ );
5255
5281
  return;
5256
5282
  }
5257
5283
  console.log(import_chalk4.default.green(`Domain available: ${domainArg}`));
@@ -5270,11 +5296,19 @@ var upload_default = async (options) => {
5270
5296
  console.log(import_chalk4.default.cyan(`URL:`));
5271
5297
  console.log(import_chalk4.default.cyan(`${URL2}${encryptedCID}`));
5272
5298
  if (domainArg) {
5273
- console.log(import_chalk4.default.blue(`Binding domain: ${domainArg} with CID: ${result.contentHash}`));
5299
+ console.log(
5300
+ import_chalk4.default.blue(
5301
+ `Binding domain: ${domainArg} with CID: ${result.contentHash}`
5302
+ )
5303
+ );
5274
5304
  const ok = await bindPinmeDomain(domainArg, result.contentHash);
5275
5305
  if (ok) {
5276
5306
  console.log(import_chalk4.default.green(`Bind success: ${domainArg}`));
5277
- console.log(import_chalk4.default.white(`Visit (Pinme subdomain example): https://${domainArg}.pinit.eth.limo`));
5307
+ console.log(
5308
+ import_chalk4.default.white(
5309
+ `Visit (Pinme subdomain example): https://${domainArg}.pinit.eth.limo`
5310
+ )
5311
+ );
5278
5312
  } else {
5279
5313
  console.log(import_chalk4.default.red("Binding failed. Please try again later."));
5280
5314
  }
@@ -5302,7 +5336,11 @@ var upload_default = async (options) => {
5302
5336
  if (domainArg) {
5303
5337
  const check = await checkDomainAvailable(domainArg);
5304
5338
  if (!check.is_valid) {
5305
- console.log(import_chalk4.default.red(`Domain not available: ${check.error || "unknown reason"}`));
5339
+ console.log(
5340
+ import_chalk4.default.red(
5341
+ `Domain not available: ${check.error || "unknown reason"}`
5342
+ )
5343
+ );
5306
5344
  return;
5307
5345
  }
5308
5346
  console.log(import_chalk4.default.green(`Domain available: ${domainArg}`));
@@ -5321,11 +5359,19 @@ var upload_default = async (options) => {
5321
5359
  console.log(import_chalk4.default.cyan(`URL:`));
5322
5360
  console.log(import_chalk4.default.cyan(`${URL2}${encryptedCID}`));
5323
5361
  if (domainArg) {
5324
- console.log(import_chalk4.default.blue(`Binding domain: ${domainArg} with CID: ${result.contentHash}`));
5362
+ console.log(
5363
+ import_chalk4.default.blue(
5364
+ `Binding domain: ${domainArg} with CID: ${result.contentHash}`
5365
+ )
5366
+ );
5325
5367
  const ok = await bindPinmeDomain(domainArg, result.contentHash);
5326
5368
  if (ok) {
5327
5369
  console.log(import_chalk4.default.green(`Bind success: ${domainArg}`));
5328
- console.log(import_chalk4.default.white(`Visit (Pinme subdomain example): https://${domainArg}.pinit.eth.limo`));
5370
+ console.log(
5371
+ import_chalk4.default.white(
5372
+ `Visit (Pinme subdomain example): https://${domainArg}.pinit.eth.limo`
5373
+ )
5374
+ );
5329
5375
  } else {
5330
5376
  console.log(import_chalk4.default.red("Binding failed. Please try again later."));
5331
5377
  }
@@ -5353,7 +5399,7 @@ var import_chalk5 = __toESM(require("chalk"));
5353
5399
  var ipfsApiUrl = "https://pinme.dev/api/v3";
5354
5400
  async function removeFromIpfs(value, type = "hash") {
5355
5401
  try {
5356
- const uid = getDeviceId();
5402
+ const uid = getUid();
5357
5403
  console.log(import_chalk5.default.blue(`Removing content from IPFS: ${value}...`));
5358
5404
  const queryParams = new URLSearchParams({
5359
5405
  uid
@@ -5363,14 +5409,21 @@ async function removeFromIpfs(value, type = "hash") {
5363
5409
  } else {
5364
5410
  queryParams.append("arg", value);
5365
5411
  }
5366
- const response = await axios_default.post(`${ipfsApiUrl}/block/rm?${queryParams.toString()}`, {
5367
- timeout: 3e4
5368
- // 30 seconds timeout
5369
- });
5412
+ const response = await axios_default.post(
5413
+ `${ipfsApiUrl}/block/rm?${queryParams.toString()}`,
5414
+ {
5415
+ timeout: 3e4
5416
+ // 30 seconds timeout
5417
+ }
5418
+ );
5370
5419
  const { code, msg, data } = response.data;
5371
5420
  if (code === 200) {
5372
5421
  console.log(import_chalk5.default.green("\u2713 Removal successful!"));
5373
- console.log(import_chalk5.default.cyan(`Content ${type}: ${value} has been removed from IPFS network`));
5422
+ console.log(
5423
+ import_chalk5.default.cyan(
5424
+ `Content ${type}: ${value} has been removed from IPFS network`
5425
+ )
5426
+ );
5374
5427
  return true;
5375
5428
  } else {
5376
5429
  console.log(import_chalk5.default.red("\u2717 Removal failed"));
@@ -5381,17 +5434,31 @@ async function removeFromIpfs(value, type = "hash") {
5381
5434
  console.log(import_chalk5.default.red("\u2717 Removal failed", error));
5382
5435
  if (error.response) {
5383
5436
  const { status, data } = error.response;
5384
- console.log(import_chalk5.default.red(`HTTP Error ${status}: ${(data == null ? void 0 : data.msg) || "Server error"}`));
5437
+ console.log(
5438
+ import_chalk5.default.red(`HTTP Error ${status}: ${(data == null ? void 0 : data.msg) || "Server error"}`)
5439
+ );
5385
5440
  if (status === 404) {
5386
- console.log(import_chalk5.default.yellow("Content not found on the network or already removed"));
5441
+ console.log(
5442
+ import_chalk5.default.yellow("Content not found on the network or already removed")
5443
+ );
5387
5444
  } else if (status === 403) {
5388
- console.log(import_chalk5.default.yellow("Permission denied - you may not have access to remove this content"));
5445
+ console.log(
5446
+ import_chalk5.default.yellow(
5447
+ "Permission denied - you may not have access to remove this content"
5448
+ )
5449
+ );
5389
5450
  } else if (status === 500) {
5390
- console.log(import_chalk5.default.yellow("Server internal error - please try again later"));
5451
+ console.log(
5452
+ import_chalk5.default.yellow("Server internal error - please try again later")
5453
+ );
5391
5454
  }
5392
5455
  } else if (error.request) {
5393
- console.log(import_chalk5.default.red("Network error: Unable to connect to IPFS service"));
5394
- console.log(import_chalk5.default.yellow("Please check your internet connection and try again"));
5456
+ console.log(
5457
+ import_chalk5.default.red("Network error: Unable to connect to IPFS service")
5458
+ );
5459
+ console.log(
5460
+ import_chalk5.default.yellow("Please check your internet connection and try again")
5461
+ );
5395
5462
  } else {
5396
5463
  console.log(import_chalk5.default.red(`Error: ${error.message}`));
5397
5464
  }
@@ -5579,32 +5646,93 @@ async function setAppKeyCmd() {
5579
5646
  }
5580
5647
  }
5581
5648
 
5582
- // bin/my-domains.ts
5649
+ // bin/logout.ts
5583
5650
  var import_chalk8 = __toESM(require("chalk"));
5651
+ var import_inquirer4 = __toESM(require("inquirer"));
5652
+ async function logoutCmd() {
5653
+ try {
5654
+ const auth = getAuthConfig();
5655
+ if (!auth) {
5656
+ console.log(import_chalk8.default.yellow("No active session found. You are already logged out."));
5657
+ return;
5658
+ }
5659
+ const answer = await import_inquirer4.default.prompt([
5660
+ {
5661
+ type: "confirm",
5662
+ name: "confirm",
5663
+ message: `Are you sure you want to log out? (Current address: ${auth.address})`,
5664
+ default: false
5665
+ }
5666
+ ]);
5667
+ if (!answer.confirm) {
5668
+ console.log(import_chalk8.default.blue("Logout cancelled."));
5669
+ return;
5670
+ }
5671
+ clearAuthToken();
5672
+ console.log(import_chalk8.default.green("Successfully logged out."));
5673
+ console.log(import_chalk8.default.gray(`Address ${auth.address} has been removed from local storage.`));
5674
+ } catch (e) {
5675
+ console.log(import_chalk8.default.red(`Failed to logout: ${(e == null ? void 0 : e.message) || e}`));
5676
+ }
5677
+ }
5678
+
5679
+ // bin/show-appkey.ts
5680
+ var import_chalk9 = __toESM(require("chalk"));
5681
+ function showAppKeyCmd() {
5682
+ try {
5683
+ const auth = getAuthConfig();
5684
+ if (!auth) {
5685
+ console.log(import_chalk9.default.yellow("No AppKey found. Please set your AppKey first."));
5686
+ console.log(import_chalk9.default.gray("Run: pinme set-appkey <AppKey>"));
5687
+ return;
5688
+ }
5689
+ console.log(import_chalk9.default.green("Current AppKey Information:"));
5690
+ console.log(import_chalk9.default.cyan(` Address: ${auth.address}`));
5691
+ const token = auth.token;
5692
+ if (token.length > 12) {
5693
+ const maskedToken = `${token.substring(0, 8)}${"*".repeat(token.length - 12)}${token.substring(token.length - 4)}`;
5694
+ console.log(import_chalk9.default.cyan(` Token: ${maskedToken}`));
5695
+ } else {
5696
+ console.log(import_chalk9.default.cyan(` Token: ${"*".repeat(token.length)}`));
5697
+ }
5698
+ const combined = `${auth.address}-${auth.token}`;
5699
+ if (combined.length > 20) {
5700
+ const maskedAppKey = `${combined.substring(0, 12)}${"*".repeat(combined.length - 16)}${combined.substring(combined.length - 4)}`;
5701
+ console.log(import_chalk9.default.cyan(` AppKey: ${maskedAppKey}`));
5702
+ } else {
5703
+ console.log(import_chalk9.default.cyan(` AppKey: ${"*".repeat(combined.length)}`));
5704
+ }
5705
+ } catch (e) {
5706
+ console.log(import_chalk9.default.red(`Failed to show AppKey: ${(e == null ? void 0 : e.message) || e}`));
5707
+ }
5708
+ }
5709
+
5710
+ // bin/my-domains.ts
5711
+ var import_chalk10 = __toESM(require("chalk"));
5584
5712
  var import_dayjs2 = __toESM(require("dayjs"));
5585
5713
  async function myDomainsCmd() {
5586
5714
  try {
5587
5715
  const list = await getMyDomains();
5588
5716
  if (!list.length) {
5589
- console.log(import_chalk8.default.yellow("No bound domains found."));
5717
+ console.log(import_chalk10.default.yellow("No bound domains found."));
5590
5718
  return;
5591
5719
  }
5592
- console.log(import_chalk8.default.cyan("My domains:"));
5593
- console.log(import_chalk8.default.cyan("-".repeat(80)));
5720
+ console.log(import_chalk10.default.cyan("My domains:"));
5721
+ console.log(import_chalk10.default.cyan("-".repeat(80)));
5594
5722
  list.forEach((item, i) => {
5595
- console.log(import_chalk8.default.green(`${i + 1}. ${item.domain_name}`));
5596
- console.log(import_chalk8.default.white(` Type: ${item.domain_type}`));
5723
+ console.log(import_chalk10.default.green(`${i + 1}. ${item.domain_name}`));
5724
+ console.log(import_chalk10.default.white(` Type: ${item.domain_type}`));
5597
5725
  if (item.bind_time) {
5598
- console.log(import_chalk8.default.white(` Bind time: ${(0, import_dayjs2.default)(item.bind_time * 1e3).format("YYYY-MM-DD HH:mm:ss")}`));
5726
+ console.log(import_chalk10.default.white(` Bind time: ${(0, import_dayjs2.default)(item.bind_time * 1e3).format("YYYY-MM-DD HH:mm:ss")}`));
5599
5727
  }
5600
5728
  if (typeof item.expire_time === "number") {
5601
5729
  const label = item.expire_time === 0 ? "Never" : (0, import_dayjs2.default)(item.expire_time * 1e3).format("YYYY-MM-DD HH:mm:ss");
5602
- console.log(import_chalk8.default.white(` Expire time: ${label}`));
5730
+ console.log(import_chalk10.default.white(` Expire time: ${label}`));
5603
5731
  }
5604
- console.log(import_chalk8.default.cyan("-".repeat(80)));
5732
+ console.log(import_chalk10.default.cyan("-".repeat(80)));
5605
5733
  });
5606
5734
  } catch (e) {
5607
- console.log(import_chalk8.default.red(`Failed to fetch domains: ${(e == null ? void 0 : e.message) || e}`));
5735
+ console.log(import_chalk10.default.red(`Failed to fetch domains: ${(e == null ? void 0 : e.message) || e}`));
5608
5736
  }
5609
5737
  }
5610
5738
 
@@ -5613,27 +5741,39 @@ import_dotenv.default.config();
5613
5741
  checkNodeVersion();
5614
5742
  function showBanner() {
5615
5743
  console.log(
5616
- import_chalk9.default.cyan(
5617
- import_figlet3.default.textSync("Pinme", { horizontalLayout: "full" })
5618
- )
5744
+ import_chalk11.default.cyan(import_figlet3.default.textSync("Pinme", { horizontalLayout: "full" }))
5619
5745
  );
5620
- console.log(import_chalk9.default.cyan("A command-line tool for uploading files to IPFS\n"));
5746
+ console.log(import_chalk11.default.cyan("A command-line tool for uploading files to IPFS\n"));
5621
5747
  }
5622
5748
  var program = new import_commander.Command();
5623
5749
  program.name("pinme").version(version).option("-v, --version", "output the current version");
5624
- program.command("upload").description("upload a file or directory to IPFS. Supports --domain to bind after upload").option("-d, --domain <name>", "Pinme subdomain").action(() => upload_default());
5750
+ program.command("upload").description(
5751
+ "upload a file or directory to IPFS. Supports --domain to bind after upload"
5752
+ ).option("-d, --domain <name>", "Pinme subdomain").action(() => upload_default());
5625
5753
  program.command("rm").description("remove a file from IPFS network").action(() => remove_default());
5626
- program.command("set-appkey").description("Set AppKey for authentication, and auto-merge anonymous history").action(() => setAppKeyCmd());
5754
+ program.command("set-appkey").description(
5755
+ "Set AppKey for authentication, and auto-merge anonymous history"
5756
+ ).action(() => setAppKeyCmd());
5757
+ program.command("logout").description("log out and clear authentication").action(() => logoutCmd());
5758
+ program.command("show-appkey").alias("appkey").description("show current AppKey information (masked)").action(() => showAppKeyCmd());
5627
5759
  program.command("my-domains").alias("domain").description("List domains owned by current account").action(() => myDomainsCmd());
5628
5760
  program.command("domain").description("Alias for 'my-domains' command").action(() => myDomainsCmd());
5629
- program.command("list").description("show upload history").option("-l, --limit <number>", "limit the number of records to show", parseInt).option("-c, --clear", "clear all upload history").action((options) => {
5761
+ program.command("list").description("show upload history").option(
5762
+ "-l, --limit <number>",
5763
+ "limit the number of records to show",
5764
+ parseInt
5765
+ ).option("-c, --clear", "clear all upload history").action((options) => {
5630
5766
  if (options.clear) {
5631
5767
  clearUploadHistory();
5632
5768
  } else {
5633
5769
  displayUploadHistory(options.limit || 10);
5634
5770
  }
5635
5771
  });
5636
- program.command("ls").description("alias for 'list' command").option("-l, --limit <number>", "limit the number of records to show", parseInt).option("-c, --clear", "clear all upload history").action((options) => {
5772
+ program.command("ls").description("alias for 'list' command").option(
5773
+ "-l, --limit <number>",
5774
+ "limit the number of records to show",
5775
+ parseInt
5776
+ ).option("-c, --clear", "clear all upload history").action((options) => {
5637
5777
  if (options.clear) {
5638
5778
  clearUploadHistory();
5639
5779
  } else {
@@ -5651,13 +5791,17 @@ program.on("--help", () => {
5651
5791
  console.log(" $ pinme upload <path> --domain <name>");
5652
5792
  console.log(" $ pinme rm <hash>");
5653
5793
  console.log(" $ pinme set-appkey <AppKey>");
5794
+ console.log(" $ pinme show-appkey");
5795
+ console.log(" $ pinme logout");
5654
5796
  console.log(" $ pinme my-domains");
5655
5797
  console.log(" $ pinme domain");
5656
5798
  console.log(" $ pinme list -l 5");
5657
5799
  console.log(" $ pinme ls");
5658
5800
  console.log(" $ pinme help");
5659
5801
  console.log("");
5660
- console.log("For more information, visit: https://github.com/glitternetwork/pinme");
5802
+ console.log(
5803
+ "For more information, visit: https://github.com/glitternetwork/pinme"
5804
+ );
5661
5805
  });
5662
5806
  program.parse(process.argv);
5663
5807
  if (process.argv.length === 2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinme",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },