pinme 1.2.4 → 1.2.5

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 +121 -6
  2. package/dist/index.js +521 -92
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -33,6 +33,9 @@ Website: [https://pinme.eth.limo/](https://pinme.eth.limo/)
33
33
  - [For AI](#for-ai)
34
34
  - [Installation](#installation)
35
35
  - [Usage](#usage)
36
+ - [Bind Domain](#bind-domain-requires-vip)
37
+ - [Command Details](#command-details)
38
+ - [VIP Membership](#vip-membership)
36
39
  - [Common Static File Directories](#common-static-file-directories)
37
40
  - [Error Handling](#error-handling)
38
41
  - [Upload Limits](#upload-limits)
@@ -216,10 +219,30 @@ pinme upload
216
219
 
217
220
  # Specify path directly
218
221
  pinme upload /path/to/file-or-directory
222
+ ```
223
+
224
+ ### Bind Domain (requires VIP)
219
225
 
220
- # Upload and bind to a domain
221
- pinme upload /path/to/file-or-directory --domain <name>
222
- pinme upload /path/to/file-or-directory -d <name>
226
+ ```bash
227
+ # Upload and bind to a domain (auto-detected: Pinme subdomain or DNS domain)
228
+ pinme bind <path> --domain <name>
229
+ pinme bind <path> -d <name>
230
+ ```
231
+
232
+ **Smart Auto-Detection:**
233
+ - Domains with a dot (e.g., `example.com`) → **DNS domain**
234
+ - Domains without a dot (e.g., `my-site`) → **Pinme subdomain**
235
+
236
+ **Examples:**
237
+ ```bash
238
+ # Bind to a Pinme subdomain (auto-detected)
239
+ pinme bind ./dist --domain my-site
240
+
241
+ # Bind to a DNS domain (auto-detected by the dot)
242
+ pinme bind ./dist --domain example.com
243
+
244
+ # Force DNS mode if needed
245
+ pinme bind ./dist --domain my-site --dns
223
246
  ```
224
247
 
225
248
  ### Import CAR files
@@ -337,6 +360,51 @@ The selected directory must meet:
337
360
 
338
361
  ## Command Details
339
362
 
363
+ ### `bind`
364
+
365
+ Upload files and bind them to a custom domain. **Domain binding requires VIP membership.**
366
+
367
+ ```bash
368
+ pinme bind <path> [options]
369
+ ```
370
+
371
+ **Options:**
372
+ - `path`: Path to the file or directory to upload (optional, interactive if not provided)
373
+ - `-d, --domain <name>`: Domain name to bind (required)
374
+ - `--dns`: Force DNS domain mode (optional, auto-detected from domain format)
375
+
376
+ **Examples:**
377
+ ```bash
378
+ # Interactive mode (will prompt for path and domain)
379
+ pinme bind
380
+
381
+ # Bind to a Pinme subdomain (auto-detected: no dot in domain)
382
+ pinme bind ./dist --domain my-site
383
+
384
+ # Bind to a DNS domain (auto-detected: contains dot)
385
+ pinme bind ./dist --domain example.com
386
+
387
+ # Force DNS mode with --dns flag
388
+ pinme bind ./dist --domain my-site --dns
389
+ ```
390
+
391
+ **Auto-Detection:**
392
+ - Domains with a dot (e.g., `example.com`) are automatically treated as **DNS domains**
393
+ - Domains without a dot (e.g., `my-site`) are automatically treated as **Pinme subdomains**
394
+ - Use `--dns` or `-D` flag to force DNS domain mode when needed
395
+
396
+ **Requirements:**
397
+ - VIP membership required for domain binding
398
+ - Valid AppKey must be set (run: `pinme set-appkey <AppKey>`)
399
+ - For DNS domains, you must own the domain
400
+
401
+ **URL Formats:**
402
+ - Pinme subdomain: `https://<name>.pinit.eth.limo`
403
+ - DNS domain: `https://<your-domain>`
404
+
405
+ **DNS Setup:**
406
+ After successful DNS domain binding, visit the [DNS Configuration Guide](https://pinme.eth.limo/#/docs?id=custom-domain) to complete DNS setup.
407
+
340
408
  ### `upload`
341
409
 
342
410
  Upload a file or directory to the IPFS network.
@@ -347,20 +415,22 @@ pinme upload [path] [--domain <name>]
347
415
 
348
416
  **Options:**
349
417
  - `path`: Path to the file or directory to upload (optional, interactive if not provided)
350
- - `-d, --domain <name>`: Pinme subdomain to bind after upload (optional)
418
+ - `-d, --domain <name>`: Pinme subdomain to bind after upload (optional, requires VIP)
351
419
 
352
420
  **Examples:**
353
421
  ```bash
354
422
  # Upload dist directory
355
423
  pinme upload dist
356
424
 
357
- # Upload and bind to a domain (requires Plus membership)
358
- pinme upload dist --domain my-site
425
+ # Upload only (no domain binding)
426
+ pinme upload dist
359
427
 
360
428
  # Upload a specific file
361
429
  pinme upload ./example.jpg
362
430
  ```
363
431
 
432
+ **Note:** Domain binding during upload requires VIP. Use the `bind` command for domain binding.
433
+
364
434
  ### `import`
365
435
 
366
436
  Import CAR (Content Addressable aRchive) files to the IPFS network. This command is specifically designed for importing CAR files while maintaining their original structure. Supports binding to a Pinme subdomain after import.
@@ -475,6 +545,51 @@ List all domains owned by the current account.
475
545
 
476
546
  ---
477
547
 
548
+ ## VIP Membership
549
+
550
+ ### Overview
551
+
552
+ VIP membership provides access to premium features including domain binding and custom DNS support.
553
+
554
+ ### VIP Features
555
+
556
+ | Feature | Free | VIP |
557
+ |---------|------|-----|
558
+ | Upload files to IPFS | ✅ | ✅ |
559
+ | Preview URL | ✅ | ✅ |
560
+ | Pinme subdomain binding | ❌ | ✅ |
561
+ | Custom DNS domain binding | ❌ | ✅ |
562
+ | Priority support | ❌ | ✅ |
563
+
564
+ ### Domain Binding Requirements
565
+
566
+ Domain binding (both Pinme subdomains and custom DNS domains) requires VIP membership.
567
+
568
+ **Before using domain binding:**
569
+
570
+ 1. **Upgrade to VIP**
571
+ - Visit [PinMe website](https://pinme.eth.limo/) to upgrade
572
+
573
+ 2. **Set AppKey**
574
+ ```bash
575
+ pinme set-appkey <AppKey>
576
+ ```
577
+
578
+ 3. **Bind your domain**
579
+ ```bash
580
+ # Bind to a Pinme subdomain
581
+ pinme bind ./dist --domain my-site
582
+
583
+ # Bind to a custom DNS domain
584
+ pinme bind ./dist --domain example.com --dns
585
+ ```
586
+
587
+ ### Checking VIP Status
588
+
589
+ If you attempt to bind a domain without VIP, you'll see an error message. You can check your VIP status on the [PinMe website](https://pinme.eth.limo/).
590
+
591
+ ---
592
+
478
593
  ## Error Handling
479
594
 
480
595
  ### Common Errors and Solutions
package/dist/index.js CHANGED
@@ -97,7 +97,7 @@ var require_package = __commonJS({
97
97
  var require_main = __commonJS({
98
98
  "node_modules/.pnpm/dotenv@16.5.0/node_modules/dotenv/lib/main.js"(exports2, module2) {
99
99
  var fs9 = require("fs");
100
- var path9 = require("path");
100
+ var path10 = require("path");
101
101
  var os4 = require("os");
102
102
  var crypto2 = require("crypto");
103
103
  var packageJson = require_package();
@@ -208,7 +208,7 @@ var require_main = __commonJS({
208
208
  possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
209
209
  }
210
210
  } else {
211
- possibleVaultPath = path9.resolve(process.cwd(), ".env.vault");
211
+ possibleVaultPath = path10.resolve(process.cwd(), ".env.vault");
212
212
  }
213
213
  if (fs9.existsSync(possibleVaultPath)) {
214
214
  return possibleVaultPath;
@@ -216,7 +216,7 @@ var require_main = __commonJS({
216
216
  return null;
217
217
  }
218
218
  function _resolveHome(envPath) {
219
- return envPath[0] === "~" ? path9.join(os4.homedir(), envPath.slice(1)) : envPath;
219
+ return envPath[0] === "~" ? path10.join(os4.homedir(), envPath.slice(1)) : envPath;
220
220
  }
221
221
  function _configVault(options) {
222
222
  const debug = Boolean(options && options.debug);
@@ -232,7 +232,7 @@ var require_main = __commonJS({
232
232
  return { parsed };
233
233
  }
234
234
  function configDotenv(options) {
235
- const dotenvPath = path9.resolve(process.cwd(), ".env");
235
+ const dotenvPath = path10.resolve(process.cwd(), ".env");
236
236
  let encoding = "utf8";
237
237
  const debug = Boolean(options && options.debug);
238
238
  if (options && options.encoding) {
@@ -255,13 +255,13 @@ var require_main = __commonJS({
255
255
  }
256
256
  let lastError;
257
257
  const parsedAll = {};
258
- for (const path10 of optionPaths) {
258
+ for (const path11 of optionPaths) {
259
259
  try {
260
- const parsed = DotenvModule.parse(fs9.readFileSync(path10, { encoding }));
260
+ const parsed = DotenvModule.parse(fs9.readFileSync(path11, { encoding }));
261
261
  DotenvModule.populate(parsedAll, parsed, options);
262
262
  } catch (e) {
263
263
  if (debug) {
264
- _debug(`Failed to load ${path10} ${e.message}`);
264
+ _debug(`Failed to load ${path11} ${e.message}`);
265
265
  }
266
266
  lastError = e;
267
267
  }
@@ -1519,11 +1519,11 @@ function checkNodeVersion() {
1519
1519
 
1520
1520
  // bin/index.ts
1521
1521
  var import_commander = require("commander");
1522
- var import_chalk13 = __toESM(require("chalk"));
1522
+ var import_chalk14 = __toESM(require("chalk"));
1523
1523
  var import_figlet5 = __toESM(require("figlet"));
1524
1524
 
1525
1525
  // package.json
1526
- var version = "1.2.4";
1526
+ var version = "1.2.5";
1527
1527
 
1528
1528
  // bin/upload.ts
1529
1529
  var import_path6 = __toESM(require("path"));
@@ -1968,9 +1968,9 @@ function isVisitable(thing) {
1968
1968
  function removeBrackets(key) {
1969
1969
  return utils_default.endsWith(key, "[]") ? key.slice(0, -2) : key;
1970
1970
  }
1971
- function renderKey(path9, key, dots) {
1972
- if (!path9) return key;
1973
- return path9.concat(key).map(function each(token, i) {
1971
+ function renderKey(path10, key, dots) {
1972
+ if (!path10) return key;
1973
+ return path10.concat(key).map(function each(token, i) {
1974
1974
  token = removeBrackets(token);
1975
1975
  return !dots && i ? "[" + token + "]" : token;
1976
1976
  }).join(dots ? "." : "");
@@ -2015,9 +2015,9 @@ function toFormData(obj, formData, options) {
2015
2015
  }
2016
2016
  return value;
2017
2017
  }
2018
- function defaultVisitor(value, key, path9) {
2018
+ function defaultVisitor(value, key, path10) {
2019
2019
  let arr = value;
2020
- if (value && !path9 && typeof value === "object") {
2020
+ if (value && !path10 && typeof value === "object") {
2021
2021
  if (utils_default.endsWith(key, "{}")) {
2022
2022
  key = metaTokens ? key : key.slice(0, -2);
2023
2023
  value = JSON.stringify(value);
@@ -2036,7 +2036,7 @@ function toFormData(obj, formData, options) {
2036
2036
  if (isVisitable(value)) {
2037
2037
  return true;
2038
2038
  }
2039
- formData.append(renderKey(path9, key, dots), convertValue(value));
2039
+ formData.append(renderKey(path10, key, dots), convertValue(value));
2040
2040
  return false;
2041
2041
  }
2042
2042
  const stack = [];
@@ -2045,10 +2045,10 @@ function toFormData(obj, formData, options) {
2045
2045
  convertValue,
2046
2046
  isVisitable
2047
2047
  });
2048
- function build(value, path9) {
2048
+ function build(value, path10) {
2049
2049
  if (utils_default.isUndefined(value)) return;
2050
2050
  if (stack.indexOf(value) !== -1) {
2051
- throw Error("Circular reference detected in " + path9.join("."));
2051
+ throw Error("Circular reference detected in " + path10.join("."));
2052
2052
  }
2053
2053
  stack.push(value);
2054
2054
  utils_default.forEach(value, function each(el, key) {
@@ -2056,11 +2056,11 @@ function toFormData(obj, formData, options) {
2056
2056
  formData,
2057
2057
  el,
2058
2058
  utils_default.isString(key) ? key.trim() : key,
2059
- path9,
2059
+ path10,
2060
2060
  exposedHelpers
2061
2061
  );
2062
2062
  if (result === true) {
2063
- build(el, path9 ? path9.concat(key) : [key]);
2063
+ build(el, path10 ? path10.concat(key) : [key]);
2064
2064
  }
2065
2065
  });
2066
2066
  stack.pop();
@@ -2221,7 +2221,7 @@ var node_default = {
2221
2221
  // node_modules/.pnpm/axios@1.3.2/node_modules/axios/lib/helpers/toURLEncodedForm.js
2222
2222
  function toURLEncodedForm(data, options) {
2223
2223
  return toFormData_default(data, new node_default.classes.URLSearchParams(), Object.assign({
2224
- visitor: function(value, key, path9, helpers) {
2224
+ visitor: function(value, key, path10, helpers) {
2225
2225
  if (node_default.isNode && utils_default.isBuffer(value)) {
2226
2226
  this.append(key, value.toString("base64"));
2227
2227
  return false;
@@ -2250,10 +2250,10 @@ function arrayToObject(arr) {
2250
2250
  return obj;
2251
2251
  }
2252
2252
  function formDataToJSON(formData) {
2253
- function buildPath(path9, value, target, index) {
2254
- let name = path9[index++];
2253
+ function buildPath(path10, value, target, index) {
2254
+ let name = path10[index++];
2255
2255
  const isNumericKey = Number.isFinite(+name);
2256
- const isLast = index >= path9.length;
2256
+ const isLast = index >= path10.length;
2257
2257
  name = !name && utils_default.isArray(target) ? target.length : name;
2258
2258
  if (isLast) {
2259
2259
  if (utils_default.hasOwnProp(target, name)) {
@@ -2266,7 +2266,7 @@ function formDataToJSON(formData) {
2266
2266
  if (!target[name] || !utils_default.isObject(target[name])) {
2267
2267
  target[name] = [];
2268
2268
  }
2269
- const result = buildPath(path9, value, target[name], index);
2269
+ const result = buildPath(path10, value, target[name], index);
2270
2270
  if (result && utils_default.isArray(target[name])) {
2271
2271
  target[name] = arrayToObject(target[name]);
2272
2272
  }
@@ -3328,9 +3328,9 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config) {
3328
3328
  auth = urlUsername + ":" + urlPassword;
3329
3329
  }
3330
3330
  auth && headers.delete("authorization");
3331
- let path9;
3331
+ let path10;
3332
3332
  try {
3333
- path9 = buildURL(
3333
+ path10 = buildURL(
3334
3334
  parsed.pathname + parsed.search,
3335
3335
  config.params,
3336
3336
  config.paramsSerializer
@@ -3348,7 +3348,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config) {
3348
3348
  false
3349
3349
  );
3350
3350
  const options = {
3351
- path: path9,
3351
+ path: path10,
3352
3352
  method,
3353
3353
  headers: headers.toJSON(),
3354
3354
  agents: { http: config.httpAgent, https: config.httpsAgent },
@@ -3567,14 +3567,14 @@ var cookies_default = node_default.isStandardBrowserEnv ? (
3567
3567
  // Standard browser envs support document.cookie
3568
3568
  /* @__PURE__ */ function standardBrowserEnv() {
3569
3569
  return {
3570
- write: function write(name, value, expires, path9, domain, secure) {
3570
+ write: function write(name, value, expires, path10, domain, secure) {
3571
3571
  const cookie = [];
3572
3572
  cookie.push(name + "=" + encodeURIComponent(value));
3573
3573
  if (utils_default.isNumber(expires)) {
3574
3574
  cookie.push("expires=" + new Date(expires).toGMTString());
3575
3575
  }
3576
- if (utils_default.isString(path9)) {
3577
- cookie.push("path=" + path9);
3576
+ if (utils_default.isString(path10)) {
3577
+ cookie.push("path=" + path10);
3578
3578
  }
3579
3579
  if (utils_default.isString(domain)) {
3580
3580
  cookie.push("domain=" + domain);
@@ -5149,6 +5149,46 @@ var import_crypto_js = __toESM(require("crypto-js"));
5149
5149
  // bin/utils/pinmeApi.ts
5150
5150
  var import_chalk3 = __toESM(require("chalk"));
5151
5151
  var DEFAULT_BASE = "https://pinme.dev/api/v4";
5152
+ var TOKEN_EXPIRED_CODES = [
5153
+ 401,
5154
+ 403,
5155
+ 10001,
5156
+ 10002,
5157
+ 20001,
5158
+ "TOKEN_EXPIRED",
5159
+ "AUTH_FAILED"
5160
+ ];
5161
+ var TOKEN_EXPIRED_MESSAGES = [
5162
+ "token expired",
5163
+ "invalid token",
5164
+ "authentication failed",
5165
+ "auth failed",
5166
+ "unauthorized",
5167
+ "\u767B\u5F55",
5168
+ "\u8FC7\u671F",
5169
+ "token",
5170
+ "\u9274\u6743"
5171
+ ];
5172
+ function isTokenExpired(error) {
5173
+ var _a, _b, _c, _d, _e, _f, _g;
5174
+ const status = (_a = error.response) == null ? void 0 : _a.status;
5175
+ const message = ((_c = (_b = error.response) == null ? void 0 : _b.data) == null ? void 0 : _c.msg) || ((_e = (_d = error.response) == null ? void 0 : _d.data) == null ? void 0 : _e.message) || error.message || "";
5176
+ const code = (_g = (_f = error.response) == null ? void 0 : _f.data) == null ? void 0 : _g.code;
5177
+ if (status && TOKEN_EXPIRED_CODES.includes(status)) {
5178
+ return true;
5179
+ }
5180
+ if (code && TOKEN_EXPIRED_CODES.includes(code)) {
5181
+ return true;
5182
+ }
5183
+ const lowerMessage = message.toLowerCase();
5184
+ return TOKEN_EXPIRED_MESSAGES.some(
5185
+ (m) => lowerMessage.includes(m.toLowerCase())
5186
+ );
5187
+ }
5188
+ function showTokenExpiredHint() {
5189
+ console.log(import_chalk3.default.red("\n\u26A0\uFE0F Token has expired or is invalid."));
5190
+ console.log(import_chalk3.default.yellow("Please re-run: pinme set-appkey <AppKey>\n"));
5191
+ }
5152
5192
  function createClient() {
5153
5193
  const headers = getAuthHeaders();
5154
5194
  return axios_default.create({
@@ -5171,7 +5211,13 @@ async function bindAnonymousDevice(anonymousUid) {
5171
5211
  });
5172
5212
  return (data == null ? void 0 : data.code) === 200;
5173
5213
  } catch (e) {
5174
- console.log(import_chalk3.default.yellow(`Failed to trigger anonymous binding: ${(e == null ? void 0 : e.message) || e}`));
5214
+ if (isTokenExpired(e)) {
5215
+ showTokenExpiredHint();
5216
+ return false;
5217
+ }
5218
+ console.log(
5219
+ import_chalk3.default.yellow(`Failed to trigger anonymous binding: ${(e == null ? void 0 : e.message) || e}`)
5220
+ );
5175
5221
  return false;
5176
5222
  }
5177
5223
  }
@@ -5190,31 +5236,98 @@ async function checkDomainAvailable(domainName) {
5190
5236
  return { is_valid: data.data.is_valid, error: (_a = data.data) == null ? void 0 : _a.error };
5191
5237
  }
5192
5238
  } catch (e) {
5239
+ if (isTokenExpired(e)) {
5240
+ showTokenExpiredHint();
5241
+ throw new Error("Token expired");
5242
+ }
5193
5243
  }
5194
5244
  }
5195
5245
  return { is_valid: true };
5196
5246
  }
5197
5247
  async function bindPinmeDomain(domainName, hash) {
5198
- const client = createClient();
5199
- const { data } = await client.post("/bind_pinme_domain", {
5200
- domain_name: domainName,
5201
- hash
5202
- });
5203
- return (data == null ? void 0 : data.code) === 200;
5248
+ try {
5249
+ const client = createClient();
5250
+ const { data } = await client.post("/bind_pinme_domain", {
5251
+ domain_name: domainName,
5252
+ hash
5253
+ });
5254
+ return (data == null ? void 0 : data.code) === 200;
5255
+ } catch (e) {
5256
+ if (isTokenExpired(e)) {
5257
+ showTokenExpiredHint();
5258
+ throw new Error("Token expired");
5259
+ }
5260
+ throw e;
5261
+ }
5204
5262
  }
5205
5263
  async function getMyDomains() {
5206
5264
  var _a;
5207
- const client = createClient();
5208
- const { data } = await client.get("/my_domains");
5209
- if ((data == null ? void 0 : data.code) === 200) {
5210
- if (Array.isArray(data == null ? void 0 : data.data)) {
5211
- return data.data;
5265
+ try {
5266
+ const client = createClient();
5267
+ const { data } = await client.get("/my_domains");
5268
+ if ((data == null ? void 0 : data.code) === 200) {
5269
+ if (Array.isArray(data == null ? void 0 : data.data)) {
5270
+ return data.data;
5271
+ }
5272
+ if (((_a = data == null ? void 0 : data.data) == null ? void 0 : _a.list) && Array.isArray(data.data.list)) {
5273
+ return data.data.list;
5274
+ }
5212
5275
  }
5213
- if (((_a = data == null ? void 0 : data.data) == null ? void 0 : _a.list) && Array.isArray(data.data.list)) {
5214
- return data.data.list;
5276
+ if ((data == null ? void 0 : data.code) === 401 || (data == null ? void 0 : data.code) === 403) {
5277
+ showTokenExpiredHint();
5278
+ throw new Error("Token expired");
5215
5279
  }
5280
+ return [];
5281
+ } catch (e) {
5282
+ if (isTokenExpired(e)) {
5283
+ showTokenExpiredHint();
5284
+ throw new Error("Token expired");
5285
+ }
5286
+ throw e;
5287
+ }
5288
+ }
5289
+ async function bindDnsDomainV4(domainName, hash, tokenAddress, authToken) {
5290
+ try {
5291
+ const client = createClient();
5292
+ const { data } = await client.post(
5293
+ "/bind_dns",
5294
+ {
5295
+ domain_name: domainName,
5296
+ hash
5297
+ },
5298
+ {
5299
+ headers: {
5300
+ "x-auth-token": authToken,
5301
+ "x-token-address": tokenAddress
5302
+ }
5303
+ }
5304
+ );
5305
+ return data;
5306
+ } catch (e) {
5307
+ if (isTokenExpired(e)) {
5308
+ showTokenExpiredHint();
5309
+ throw new Error("Token expired");
5310
+ }
5311
+ throw e;
5312
+ }
5313
+ }
5314
+ async function isVip(tokenAddress, authToken) {
5315
+ try {
5316
+ const client = createClient();
5317
+ const { data } = await client.get("/is_vip", {
5318
+ headers: {
5319
+ "x-auth-token": authToken,
5320
+ "x-token-address": tokenAddress
5321
+ }
5322
+ });
5323
+ return data;
5324
+ } catch (e) {
5325
+ if (isTokenExpired(e)) {
5326
+ showTokenExpiredHint();
5327
+ throw new Error("Token expired");
5328
+ }
5329
+ throw e;
5216
5330
  }
5217
- return [];
5218
5331
  }
5219
5332
  var CAR_API_BASE = process.env.CAR_API_BASE || "https://pinme.dev/api/v4";
5220
5333
  function createCarClient() {
@@ -5250,6 +5363,10 @@ async function requestCarExport(cid, uid) {
5250
5363
  }
5251
5364
  throw new Error((data == null ? void 0 : data.msg) || "Failed to request CAR export");
5252
5365
  } catch (e) {
5366
+ if (isTokenExpired(e)) {
5367
+ showTokenExpiredHint();
5368
+ throw new Error("Token expired");
5369
+ }
5253
5370
  if ((_b = (_a = e.response) == null ? void 0 : _a.data) == null ? void 0 : _b.msg) {
5254
5371
  throw new Error(e.response.data.msg);
5255
5372
  }
@@ -5260,16 +5377,23 @@ async function checkCarExportStatus(taskId) {
5260
5377
  var _a, _b;
5261
5378
  try {
5262
5379
  const client = createCarClient();
5263
- const { data } = await client.get("/car/export/status", {
5264
- params: {
5265
- task_id: taskId
5380
+ const { data } = await client.get(
5381
+ "/car/export/status",
5382
+ {
5383
+ params: {
5384
+ task_id: taskId
5385
+ }
5266
5386
  }
5267
- });
5387
+ );
5268
5388
  if ((data == null ? void 0 : data.code) === 200 && (data == null ? void 0 : data.data)) {
5269
5389
  return data.data;
5270
5390
  }
5271
5391
  throw new Error((data == null ? void 0 : data.msg) || "Failed to check export status");
5272
5392
  } catch (e) {
5393
+ if (isTokenExpired(e)) {
5394
+ showTokenExpiredHint();
5395
+ throw new Error("Token expired");
5396
+ }
5273
5397
  if ((_b = (_a = e.response) == null ? void 0 : _a.data) == null ? void 0 : _b.msg) {
5274
5398
  throw new Error(e.response.data.msg);
5275
5399
  }
@@ -5281,6 +5405,35 @@ async function checkCarExportStatus(taskId) {
5281
5405
  var URL2 = "https://pinme.eth.limo/#/preview/";
5282
5406
  var secretKey = "pinme-secret-key";
5283
5407
  checkNodeVersion();
5408
+ function isDnsDomain(domain) {
5409
+ return domain.includes(".");
5410
+ }
5411
+ function validateDnsDomain(domain) {
5412
+ const cleanDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
5413
+ const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]*(\.[a-zA-Z0-9][a-zA-Z0-9-]*)*\.[a-zA-Z]{2,}$/;
5414
+ const parts = cleanDomain.split(".");
5415
+ if (parts.length < 2) {
5416
+ return { valid: false, message: "Invalid domain format. Please enter a complete domain (e.g., example.com)" };
5417
+ }
5418
+ for (const part of parts) {
5419
+ if (part.length === 0) {
5420
+ return { valid: false, message: "Invalid domain format. Consecutive dots are not allowed" };
5421
+ }
5422
+ if (part.length > 63) {
5423
+ return { valid: false, message: "Invalid domain format. Each label must be 63 characters or less" };
5424
+ }
5425
+ if (!/^[a-zA-Z0-9-]+$/.test(part)) {
5426
+ return { valid: false, message: "Invalid domain format. Domains can only contain letters, numbers, and hyphens" };
5427
+ }
5428
+ if (/^-|-$/.test(part)) {
5429
+ return { valid: false, message: "Invalid domain format. Labels cannot start or end with hyphens" };
5430
+ }
5431
+ }
5432
+ if (!domainRegex.test(cleanDomain)) {
5433
+ return { valid: false, message: "Invalid domain format" };
5434
+ }
5435
+ return { valid: true };
5436
+ }
5284
5437
  function encryptHash(contentHash, key, uid) {
5285
5438
  try {
5286
5439
  if (!key) {
@@ -5315,6 +5468,52 @@ function getDomainFromArgs() {
5315
5468
  }
5316
5469
  return null;
5317
5470
  }
5471
+ function getDnsFromArgs() {
5472
+ const args = process.argv.slice(2);
5473
+ return args.includes("--dns") || args.includes("-D");
5474
+ }
5475
+ async function checkVipStatus(authConfig) {
5476
+ var _a;
5477
+ console.log(import_chalk4.default.blue("Checking VIP status..."));
5478
+ try {
5479
+ const vipResult = await isVip(authConfig.address, authConfig.token);
5480
+ if (!((_a = vipResult.data) == null ? void 0 : _a.is_vip)) {
5481
+ return false;
5482
+ }
5483
+ console.log(import_chalk4.default.green("VIP verified."));
5484
+ return true;
5485
+ } catch (e) {
5486
+ if (e.message === "Token expired") {
5487
+ throw e;
5488
+ }
5489
+ console.log(import_chalk4.default.yellow("Failed to check VIP status, continuing..."));
5490
+ return true;
5491
+ }
5492
+ }
5493
+ async function bindDomain(domain, contentHash, isDns, authConfig) {
5494
+ const displayDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
5495
+ if (isDns) {
5496
+ console.log(import_chalk4.default.blue("Binding DNS domain..."));
5497
+ const dnsResult = await bindDnsDomainV4(displayDomain, contentHash, authConfig.address, authConfig.token);
5498
+ if (dnsResult.code !== 200) {
5499
+ console.log(import_chalk4.default.red(`DNS binding failed: ${dnsResult.msg}`));
5500
+ return false;
5501
+ }
5502
+ console.log(import_chalk4.default.green(`DNS bind success: ${displayDomain}`));
5503
+ console.log(import_chalk4.default.white(`Visit: https://${displayDomain}`));
5504
+ console.log(import_chalk4.default.cyan("\n\u{1F4DA} DNS Setup Guide: https://pinme.eth.limo/#/docs?id=custom-domain"));
5505
+ } else {
5506
+ console.log(import_chalk4.default.blue("Binding Pinme subdomain..."));
5507
+ const ok = await bindPinmeDomain(displayDomain, contentHash);
5508
+ if (!ok) {
5509
+ console.log(import_chalk4.default.red("Binding failed. Please try again later."));
5510
+ return false;
5511
+ }
5512
+ console.log(import_chalk4.default.green(`Bind success: ${displayDomain}`));
5513
+ console.log(import_chalk4.default.white(`Visit: https://${displayDomain}.pinit.eth.limo`));
5514
+ }
5515
+ return true;
5516
+ }
5318
5517
  var upload_default = async (options) => {
5319
5518
  try {
5320
5519
  console.log(
@@ -5326,25 +5525,61 @@ var upload_default = async (options) => {
5326
5525
  whitespaceBreak: true
5327
5526
  })
5328
5527
  );
5528
+ const authConfig = getAuthConfig();
5529
+ if (!authConfig) {
5530
+ console.log(import_chalk4.default.red("Please login first. Run: pinme set-appkey <AppKey>"));
5531
+ return;
5532
+ }
5329
5533
  const argPath = process.argv[3];
5330
5534
  const domainArg = getDomainFromArgs();
5535
+ const dnsArg = getDnsFromArgs();
5331
5536
  if (argPath && !argPath.startsWith("-")) {
5332
5537
  const absolutePath = checkPathSync(argPath);
5333
5538
  if (!absolutePath) {
5334
5539
  console.log(import_chalk4.default.red(`path ${argPath} does not exist`));
5335
5540
  return;
5336
5541
  }
5337
- if (domainArg) {
5338
- const check = await checkDomainAvailable(domainArg);
5339
- if (!check.is_valid) {
5340
- console.log(
5341
- import_chalk4.default.red(
5342
- `Domain not available: ${check.error || "unknown reason"}`
5343
- )
5344
- );
5542
+ const isDns = dnsArg || (domainArg ? isDnsDomain(domainArg) : false);
5543
+ const displayDomain = domainArg == null ? void 0 : domainArg.replace(/^https?:\/\//, "").replace(/\/$/, "");
5544
+ if (isDns && domainArg) {
5545
+ const validation = validateDnsDomain(domainArg);
5546
+ if (!validation.valid) {
5547
+ console.log(import_chalk4.default.red(validation.message));
5345
5548
  return;
5346
5549
  }
5347
- console.log(import_chalk4.default.green(`Domain available: ${domainArg}`));
5550
+ }
5551
+ if (domainArg) {
5552
+ try {
5553
+ const isVipUser = await checkVipStatus(authConfig);
5554
+ if (!isVipUser) {
5555
+ console.log(import_chalk4.default.red("Domain binding requires VIP. Please upgrade to VIP first."));
5556
+ return;
5557
+ }
5558
+ } catch (e) {
5559
+ if (e.message === "Token expired") {
5560
+ return;
5561
+ }
5562
+ throw e;
5563
+ }
5564
+ }
5565
+ if (domainArg) {
5566
+ try {
5567
+ const check = await checkDomainAvailable(displayDomain);
5568
+ if (!check.is_valid) {
5569
+ console.log(
5570
+ import_chalk4.default.red(
5571
+ `Domain not available: ${check.error || "unknown reason"}`
5572
+ )
5573
+ );
5574
+ return;
5575
+ }
5576
+ console.log(import_chalk4.default.green(`Domain available: ${displayDomain}`));
5577
+ } catch (e) {
5578
+ if (e.message === "Token expired") {
5579
+ return;
5580
+ }
5581
+ throw e;
5582
+ }
5348
5583
  }
5349
5584
  console.log(import_chalk4.default.blue(`uploading ${absolutePath} to ipfs...`));
5350
5585
  try {
@@ -5362,19 +5597,16 @@ var upload_default = async (options) => {
5362
5597
  if (domainArg) {
5363
5598
  console.log(
5364
5599
  import_chalk4.default.blue(
5365
- `Binding domain: ${domainArg} with CID: ${result.contentHash}`
5600
+ `Binding domain: ${displayDomain} with CID: ${result.contentHash}`
5366
5601
  )
5367
5602
  );
5368
- const ok = await bindPinmeDomain(domainArg, result.contentHash);
5369
- if (ok) {
5370
- console.log(import_chalk4.default.green(`Bind success: ${domainArg}`));
5371
- console.log(
5372
- import_chalk4.default.white(
5373
- `Visit (Pinme subdomain example): https://${domainArg}.pinit.eth.limo`
5374
- )
5375
- );
5376
- } else {
5377
- console.log(import_chalk4.default.red("Binding failed. Please try again later."));
5603
+ try {
5604
+ await bindDomain(domainArg, result.contentHash, isDns, authConfig);
5605
+ } catch (e) {
5606
+ if (e.message === "Token expired") {
5607
+ return;
5608
+ }
5609
+ throw e;
5378
5610
  }
5379
5611
  }
5380
5612
  console.log(import_chalk4.default.green("\n\u{1F389} upload successful, program exit"));
@@ -5397,17 +5629,47 @@ var upload_default = async (options) => {
5397
5629
  console.log(import_chalk4.default.red(`path ${answer.path} does not exist`));
5398
5630
  return;
5399
5631
  }
5400
- if (domainArg) {
5401
- const check = await checkDomainAvailable(domainArg);
5402
- if (!check.is_valid) {
5403
- console.log(
5404
- import_chalk4.default.red(
5405
- `Domain not available: ${check.error || "unknown reason"}`
5406
- )
5407
- );
5632
+ const isDns = dnsArg || (domainArg ? isDnsDomain(domainArg) : false);
5633
+ const displayDomain = domainArg == null ? void 0 : domainArg.replace(/^https?:\/\//, "").replace(/\/$/, "");
5634
+ if (isDns && domainArg) {
5635
+ const validation = validateDnsDomain(domainArg);
5636
+ if (!validation.valid) {
5637
+ console.log(import_chalk4.default.red(validation.message));
5408
5638
  return;
5409
5639
  }
5410
- console.log(import_chalk4.default.green(`Domain available: ${domainArg}`));
5640
+ }
5641
+ if (domainArg) {
5642
+ try {
5643
+ const isVipUser = await checkVipStatus(authConfig);
5644
+ if (!isVipUser) {
5645
+ console.log(import_chalk4.default.red("Domain binding requires VIP. Please upgrade to VIP first."));
5646
+ return;
5647
+ }
5648
+ } catch (e) {
5649
+ if (e.message === "Token expired") {
5650
+ return;
5651
+ }
5652
+ throw e;
5653
+ }
5654
+ }
5655
+ if (domainArg) {
5656
+ try {
5657
+ const check = await checkDomainAvailable(displayDomain);
5658
+ if (!check.is_valid) {
5659
+ console.log(
5660
+ import_chalk4.default.red(
5661
+ `Domain not available: ${check.error || "unknown reason"}`
5662
+ )
5663
+ );
5664
+ return;
5665
+ }
5666
+ console.log(import_chalk4.default.green(`Domain available: ${displayDomain}`));
5667
+ } catch (e) {
5668
+ if (e.message === "Token expired") {
5669
+ return;
5670
+ }
5671
+ throw e;
5672
+ }
5411
5673
  }
5412
5674
  console.log(import_chalk4.default.blue(`uploading ${absolutePath} to ipfs...`));
5413
5675
  try {
@@ -5425,19 +5687,16 @@ var upload_default = async (options) => {
5425
5687
  if (domainArg) {
5426
5688
  console.log(
5427
5689
  import_chalk4.default.blue(
5428
- `Binding domain: ${domainArg} with CID: ${result.contentHash}`
5690
+ `Binding domain: ${displayDomain} with CID: ${result.contentHash}`
5429
5691
  )
5430
5692
  );
5431
- const ok = await bindPinmeDomain(domainArg, result.contentHash);
5432
- if (ok) {
5433
- console.log(import_chalk4.default.green(`Bind success: ${domainArg}`));
5434
- console.log(
5435
- import_chalk4.default.white(
5436
- `Visit (Pinme subdomain example): https://${domainArg}.pinit.eth.limo`
5437
- )
5438
- );
5439
- } else {
5440
- console.log(import_chalk4.default.red("Binding failed. Please try again later."));
5693
+ try {
5694
+ await bindDomain(domainArg, result.contentHash, isDns, authConfig);
5695
+ } catch (e) {
5696
+ if (e.message === "Token expired") {
5697
+ return;
5698
+ }
5699
+ throw e;
5441
5700
  }
5442
5701
  }
5443
5702
  console.log(import_chalk4.default.green("\n\u{1F389} upload successful, program exit"));
@@ -6171,20 +6430,189 @@ async function myDomainsCmd() {
6171
6430
  }
6172
6431
  }
6173
6432
 
6433
+ // bin/bind.ts
6434
+ var import_path9 = __toESM(require("path"));
6435
+ var import_chalk13 = __toESM(require("chalk"));
6436
+ var import_inquirer7 = __toESM(require("inquirer"));
6437
+ function isDnsDomain2(domain) {
6438
+ return domain.includes(".");
6439
+ }
6440
+ function validateDnsDomain2(domain) {
6441
+ const cleanDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
6442
+ const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]*(\.[a-zA-Z0-9][a-zA-Z0-9-]*)*\.[a-zA-Z]{2,}$/;
6443
+ const parts = cleanDomain.split(".");
6444
+ if (parts.length < 2) {
6445
+ return { valid: false, message: "Invalid domain format. Please enter a complete domain (e.g., example.com)" };
6446
+ }
6447
+ for (const part of parts) {
6448
+ if (part.length === 0) {
6449
+ return { valid: false, message: "Invalid domain format. Consecutive dots are not allowed" };
6450
+ }
6451
+ if (part.length > 63) {
6452
+ return { valid: false, message: "Invalid domain format. Each label must be 63 characters or less" };
6453
+ }
6454
+ if (!/^[a-zA-Z0-9-]+$/.test(part)) {
6455
+ return { valid: false, message: "Invalid domain format. Domains can only contain letters, numbers, and hyphens" };
6456
+ }
6457
+ if (/^-|-$/.test(part)) {
6458
+ return { valid: false, message: "Invalid domain format. Labels cannot start or end with hyphens" };
6459
+ }
6460
+ }
6461
+ if (!domainRegex.test(cleanDomain)) {
6462
+ return { valid: false, message: "Invalid domain format" };
6463
+ }
6464
+ return { valid: true };
6465
+ }
6466
+ function parseArgs() {
6467
+ const args = process.argv.slice(2);
6468
+ const res = {};
6469
+ const idx = args.indexOf("bind");
6470
+ if (idx >= 0) {
6471
+ const maybePath = args[idx + 1];
6472
+ if (maybePath && !maybePath.startsWith("-")) res.targetPath = maybePath;
6473
+ }
6474
+ const dIdx = args.findIndex((a) => a === "--domain" || a === "-d");
6475
+ if (dIdx >= 0 && args[dIdx + 1]) {
6476
+ res.domain = args[dIdx + 1];
6477
+ }
6478
+ const dnsIdx = args.findIndex((a) => a === "--dns" || a === "-D");
6479
+ if (dnsIdx >= 0) {
6480
+ res.dns = true;
6481
+ }
6482
+ return res;
6483
+ }
6484
+ async function checkVipStatus2(authConfig) {
6485
+ var _a;
6486
+ console.log(import_chalk13.default.blue("Checking VIP status..."));
6487
+ try {
6488
+ const vipResult = await isVip(authConfig.address, authConfig.token);
6489
+ if (!((_a = vipResult.data) == null ? void 0 : _a.is_vip)) {
6490
+ return false;
6491
+ }
6492
+ console.log(import_chalk13.default.green("VIP verified."));
6493
+ return true;
6494
+ } catch (e) {
6495
+ if (e.message === "Token expired") {
6496
+ throw e;
6497
+ }
6498
+ console.log(import_chalk13.default.yellow("Failed to check VIP status, continuing..."));
6499
+ return true;
6500
+ }
6501
+ }
6502
+ async function bindCmd() {
6503
+ var _a;
6504
+ try {
6505
+ let { domain, targetPath, dns } = parseArgs();
6506
+ const authConfig = getAuthConfig();
6507
+ if (!authConfig) {
6508
+ console.log(import_chalk13.default.red("Please login first. Run: pinme set-appkey <AppKey>"));
6509
+ return;
6510
+ }
6511
+ if (!targetPath) {
6512
+ const ans = await import_inquirer7.default.prompt([
6513
+ { type: "input", name: "path", message: "Enter the path to upload and bind: " }
6514
+ ]);
6515
+ targetPath = ans.path;
6516
+ }
6517
+ if (!domain) {
6518
+ const ans = await import_inquirer7.default.prompt([
6519
+ { type: "input", name: "domain", message: "Enter the domain to bind (e.g., my-site or example.com): " }
6520
+ ]);
6521
+ domain = (_a = ans.domain) == null ? void 0 : _a.trim();
6522
+ }
6523
+ if (!targetPath || !domain) {
6524
+ console.log(import_chalk13.default.red("Missing parameters. Path and domain are required."));
6525
+ return;
6526
+ }
6527
+ const isDns = dns || isDnsDomain2(domain);
6528
+ console.log(isDns, "isDns");
6529
+ const displayDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
6530
+ if (isDns) {
6531
+ const validation = validateDnsDomain2(domain);
6532
+ if (!validation.valid) {
6533
+ console.log(import_chalk13.default.red(validation.message));
6534
+ return;
6535
+ }
6536
+ }
6537
+ try {
6538
+ const isVipUser = await checkVipStatus2(authConfig);
6539
+ if (!isVipUser) {
6540
+ console.log(import_chalk13.default.red("Domain binding requires VIP. Please upgrade to VIP first."));
6541
+ return;
6542
+ }
6543
+ } catch (e) {
6544
+ if (e.message === "Token expired") {
6545
+ return;
6546
+ }
6547
+ throw e;
6548
+ }
6549
+ try {
6550
+ const check = await checkDomainAvailable(displayDomain);
6551
+ if (!check.is_valid) {
6552
+ console.log(import_chalk13.default.red(`Domain not available: ${check.error || "unknown reason"}`));
6553
+ return;
6554
+ }
6555
+ console.log(import_chalk13.default.green(`Domain available: ${displayDomain}`));
6556
+ } catch (e) {
6557
+ if (e.message === "Token expired") {
6558
+ return;
6559
+ }
6560
+ throw e;
6561
+ }
6562
+ const absolutePath = import_path9.default.resolve(targetPath);
6563
+ console.log(import_chalk13.default.blue(`Uploading: ${absolutePath}`));
6564
+ const up = await uploadToIpfsSplit_default(absolutePath);
6565
+ if (!(up == null ? void 0 : up.contentHash)) {
6566
+ console.log(import_chalk13.default.red("Upload failed, binding aborted."));
6567
+ return;
6568
+ }
6569
+ console.log(import_chalk13.default.green(`Upload success, CID: ${up.contentHash}`));
6570
+ try {
6571
+ if (isDns) {
6572
+ console.log(import_chalk13.default.blue("Binding DNS domain..."));
6573
+ const dnsResult = await bindDnsDomainV4(displayDomain, up.contentHash, authConfig.address, authConfig.token);
6574
+ if (dnsResult.code !== 200) {
6575
+ console.log(import_chalk13.default.red(`DNS binding failed: ${dnsResult.msg}`));
6576
+ return;
6577
+ }
6578
+ console.log(import_chalk13.default.green(`DNS bind success: ${displayDomain}`));
6579
+ console.log(import_chalk13.default.white(`Visit: https://${displayDomain}`));
6580
+ console.log(import_chalk13.default.cyan("\n\u{1F4DA} DNS Setup Guide: https://pinme.eth.limo/#/docs?id=custom-domain"));
6581
+ } else {
6582
+ console.log(import_chalk13.default.blue("Binding Pinme subdomain..."));
6583
+ const ok = await bindPinmeDomain(displayDomain, up.contentHash);
6584
+ if (!ok) {
6585
+ console.log(import_chalk13.default.red("Binding failed. Please try again later."));
6586
+ return;
6587
+ }
6588
+ console.log(import_chalk13.default.green(`Bind success: ${displayDomain}`));
6589
+ console.log(import_chalk13.default.white(`Visit: https://${displayDomain}.pinit.eth.limo`));
6590
+ }
6591
+ } catch (e) {
6592
+ if (e.message === "Token expired") {
6593
+ return;
6594
+ }
6595
+ throw e;
6596
+ }
6597
+ } catch (e) {
6598
+ console.log(import_chalk13.default.red(`Execution failed: ${(e == null ? void 0 : e.message) || e}`));
6599
+ }
6600
+ }
6601
+
6174
6602
  // bin/index.ts
6175
6603
  import_dotenv.default.config();
6176
6604
  checkNodeVersion();
6177
6605
  function showBanner() {
6178
6606
  console.log(
6179
- import_chalk13.default.cyan(import_figlet5.default.textSync("Pinme", { horizontalLayout: "full" }))
6607
+ import_chalk14.default.cyan(import_figlet5.default.textSync("Pinme", { horizontalLayout: "full" }))
6180
6608
  );
6181
- console.log(import_chalk13.default.cyan("A command-line tool for uploading files to IPFS\n"));
6609
+ console.log(import_chalk14.default.cyan("A command-line tool for uploading files to IPFS\n"));
6182
6610
  }
6183
6611
  var program = new import_commander.Command();
6184
6612
  program.name("pinme").version(version).option("-v, --version", "output the current version");
6185
6613
  program.command("upload").description(
6186
6614
  "upload a file or directory to IPFS. Supports --domain to bind after upload"
6187
- ).option("-d, --domain <name>", "Pinme subdomain").action(() => upload_default());
6615
+ ).option("-d, --domain <name>", "Domain name to bind").option("--dns", "Force DNS domain mode").action(() => upload_default());
6188
6616
  program.command("import").description("import a CAR file to IPFS. Supports --domain to bind after import").option("-d, --domain <name>", "Pinme subdomain").action(() => importCar_default());
6189
6617
  program.command("export").description("export IPFS content as CAR file").option("-o, --output <path>", "output file path for CAR file").action(() => exportCar_default());
6190
6618
  program.command("rm").description("remove a file from IPFS network").action(() => remove_default());
@@ -6194,6 +6622,7 @@ program.command("set-appkey").description(
6194
6622
  program.command("logout").description("log out and clear authentication").action(() => logoutCmd());
6195
6623
  program.command("show-appkey").alias("appkey").description("show current AppKey information (masked)").action(() => showAppKeyCmd());
6196
6624
  program.command("my-domains").alias("domain").description("List domains owned by current account").action(() => myDomainsCmd());
6625
+ program.command("bind").description("Upload and bind to a domain (requires VIP)").option("-d, --domain <name>", "Domain name to bind").option("--dns", "Force DNS domain mode").action(() => bindCmd());
6197
6626
  program.command("domain").description("Alias for 'my-domains' command").action(() => myDomainsCmd());
6198
6627
  program.command("list").description("show upload history").option(
6199
6628
  "-l, --limit <number>",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinme",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },