pinme 2.0.0-beta.2 → 2.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -96,8 +96,8 @@ var require_package = __commonJS({
96
96
  // node_modules/.pnpm/dotenv@16.5.0/node_modules/dotenv/lib/main.js
97
97
  var require_main = __commonJS({
98
98
  "node_modules/.pnpm/dotenv@16.5.0/node_modules/dotenv/lib/main.js"(exports2, module2) {
99
- var fs15 = require("fs");
100
- var path16 = require("path");
99
+ var fs16 = require("fs");
100
+ var path17 = require("path");
101
101
  var os5 = require("os");
102
102
  var crypto3 = require("crypto");
103
103
  var packageJson = require_package();
@@ -200,7 +200,7 @@ var require_main = __commonJS({
200
200
  if (options && options.path && options.path.length > 0) {
201
201
  if (Array.isArray(options.path)) {
202
202
  for (const filepath of options.path) {
203
- if (fs15.existsSync(filepath)) {
203
+ if (fs16.existsSync(filepath)) {
204
204
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
205
205
  }
206
206
  }
@@ -208,15 +208,15 @@ var require_main = __commonJS({
208
208
  possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
209
209
  }
210
210
  } else {
211
- possibleVaultPath = path16.resolve(process.cwd(), ".env.vault");
211
+ possibleVaultPath = path17.resolve(process.cwd(), ".env.vault");
212
212
  }
213
- if (fs15.existsSync(possibleVaultPath)) {
213
+ if (fs16.existsSync(possibleVaultPath)) {
214
214
  return possibleVaultPath;
215
215
  }
216
216
  return null;
217
217
  }
218
218
  function _resolveHome(envPath) {
219
- return envPath[0] === "~" ? path16.join(os5.homedir(), envPath.slice(1)) : envPath;
219
+ return envPath[0] === "~" ? path17.join(os5.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 = path16.resolve(process.cwd(), ".env");
235
+ const dotenvPath = path17.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 path17 of optionPaths) {
258
+ for (const path18 of optionPaths) {
259
259
  try {
260
- const parsed = DotenvModule.parse(fs15.readFileSync(path17, { encoding }));
260
+ const parsed = DotenvModule.parse(fs16.readFileSync(path18, { encoding }));
261
261
  DotenvModule.populate(parsedAll, parsed, options);
262
262
  } catch (e) {
263
263
  if (debug) {
264
- _debug(`Failed to load ${path17} ${e.message}`);
264
+ _debug(`Failed to load ${path18} ${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_chalk21 = __toESM(require("chalk"));
1522
+ var import_chalk23 = __toESM(require("chalk"));
1523
1523
  var import_figlet5 = __toESM(require("figlet"));
1524
1524
 
1525
1525
  // package.json
1526
- var version = "2.0.0-beta.2";
1526
+ var version = "2.0.0-beta.21";
1527
1527
 
1528
1528
  // bin/upload.ts
1529
1529
  var import_path7 = __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(path16, key, dots) {
1972
- if (!path16) return key;
1973
- return path16.concat(key).map(function each(token, i) {
1971
+ function renderKey(path17, key, dots) {
1972
+ if (!path17) return key;
1973
+ return path17.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, path16) {
2018
+ function defaultVisitor(value, key, path17) {
2019
2019
  let arr = value;
2020
- if (value && !path16 && typeof value === "object") {
2020
+ if (value && !path17 && 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(path16, key, dots), convertValue(value));
2039
+ formData.append(renderKey(path17, 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, path16) {
2048
+ function build(value, path17) {
2049
2049
  if (utils_default.isUndefined(value)) return;
2050
2050
  if (stack.indexOf(value) !== -1) {
2051
- throw Error("Circular reference detected in " + path16.join("."));
2051
+ throw Error("Circular reference detected in " + path17.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
- path16,
2059
+ path17,
2060
2060
  exposedHelpers
2061
2061
  );
2062
2062
  if (result === true) {
2063
- build(el, path16 ? path16.concat(key) : [key]);
2063
+ build(el, path17 ? path17.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, path16, helpers) {
2224
+ visitor: function(value, key, path17, 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(path16, value, target, index) {
2254
- let name = path16[index++];
2253
+ function buildPath(path17, value, target, index) {
2254
+ let name = path17[index++];
2255
2255
  const isNumericKey = Number.isFinite(+name);
2256
- const isLast = index >= path16.length;
2256
+ const isLast = index >= path17.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(path16, value, target[name], index);
2269
+ const result = buildPath(path17, 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 path16;
3331
+ let path17;
3332
3332
  try {
3333
- path16 = buildURL(
3333
+ path17 = 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: path16,
3351
+ path: path17,
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, path16, domain, secure) {
3570
+ write: function write(name, value, expires, path17, 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(path16)) {
3577
- cookie.push("path=" + path16);
3576
+ if (utils_default.isString(path17)) {
3577
+ cookie.push("path=" + path17);
3578
3578
  }
3579
3579
  if (utils_default.isString(domain)) {
3580
3580
  cookie.push("domain=" + domain);
@@ -4399,11 +4399,11 @@ var {
4399
4399
  } = axios_default;
4400
4400
 
4401
4401
  // bin/utils/uploadToIpfsSplit.ts
4402
- var import_fs_extra4 = __toESM(require("fs-extra"));
4403
- var import_path5 = __toESM(require("path"));
4402
+ var import_fs_extra5 = __toESM(require("fs-extra"));
4403
+ var import_path6 = __toESM(require("path"));
4404
4404
  var import_form_data2 = __toESM(require("form-data"));
4405
4405
  var import_ora = __toESM(require("ora"));
4406
- var crypto = __toESM(require("crypto"));
4406
+ var crypto2 = __toESM(require("crypto"));
4407
4407
 
4408
4408
  // bin/utils/uploadLimits.ts
4409
4409
  var import_fs = __toESM(require("fs"));
@@ -4584,119 +4584,615 @@ function getUid() {
4584
4584
  return getDeviceId();
4585
4585
  }
4586
4586
 
4587
- // bin/utils/uploadToIpfsSplit.ts
4588
- var IPFS_API_URL = "https://pinme.dev/api/v3";
4589
- var MAX_RETRIES = parseInt(process.env.MAX_RETRIES || "2");
4590
- var RETRY_DELAY = parseInt(process.env.RETRY_DELAY_MS || "1000");
4591
- var TIMEOUT = parseInt(process.env.TIMEOUT_MS || "600000");
4592
- var MAX_POLL_TIME = parseInt("5") * 60 * 1e3;
4593
- var POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL_SECONDS || "2") * 1e3;
4594
- var PROGRESS_UPDATE_INTERVAL = 200;
4595
- var EXPECTED_UPLOAD_TIME = 6e4;
4596
- var MAX_PROGRESS = 0.9;
4597
- var StepProgressBar = class {
4598
- spinner;
4599
- fileName;
4600
- startTime;
4601
- currentStep = 0;
4602
- stepStartTime = 0;
4603
- progressInterval = null;
4604
- isSimulatingProgress = false;
4605
- simulationStartTime = 0;
4606
- constructor(fileName, isDirectory = false) {
4607
- this.fileName = fileName;
4608
- this.spinner = (0, import_ora.default)(`Preparing to upload ${fileName}...`).start();
4609
- this.startTime = Date.now();
4610
- this.stepStartTime = Date.now();
4611
- this.startProgress();
4612
- }
4613
- startStep(stepIndex, stepName) {
4614
- this.currentStep = stepIndex;
4615
- this.stepStartTime = Date.now();
4616
- }
4617
- updateProgress(progress, total) {
4618
- }
4619
- completeStep() {
4587
+ // bin/utils/webLogin.ts
4588
+ var import_crypto = __toESM(require("crypto"));
4589
+ var import_http3 = __toESM(require("http"));
4590
+ var import_url2 = require("url");
4591
+ var import_chalk3 = __toESM(require("chalk"));
4592
+ var import_child_process = require("child_process");
4593
+ var import_fs_extra4 = __toESM(require("fs-extra"));
4594
+ var import_os4 = __toESM(require("os"));
4595
+ var import_path5 = __toESM(require("path"));
4596
+ function openBrowser(url2) {
4597
+ const platform = process.platform;
4598
+ let command;
4599
+ if (platform === "darwin") {
4600
+ command = `open "${url2}"`;
4601
+ } else if (platform === "win32") {
4602
+ command = `start "" "${url2}"`;
4603
+ } else {
4604
+ command = `xdg-open "${url2}"`;
4620
4605
  }
4621
- // Start simulating progress to continue display after 90%
4622
- startSimulatingProgress() {
4623
- this.isSimulatingProgress = true;
4624
- this.simulationStartTime = Date.now();
4606
+ (0, import_child_process.exec)(command, (err) => {
4607
+ if (err) {
4608
+ console.log(import_chalk3.default.yellow(`Unable to open browser automatically. Please visit manually: ${url2}`));
4609
+ }
4610
+ });
4611
+ }
4612
+ var CONFIG_DIR2 = import_path5.default.join(import_os4.default.homedir(), ".pinme");
4613
+ var AUTH_FILE2 = import_path5.default.join(CONFIG_DIR2, "auth.json");
4614
+ var DEFAULT_OPTIONS = {
4615
+ apiBaseUrl: "https://pinme.dev/api/v4",
4616
+ webBaseUrl: process.env.PINME_WEB_URL || "http://localhost:5173",
4617
+ callbackPort: 34567,
4618
+ callbackPath: "/cli/callback"
4619
+ };
4620
+ var WebLoginManager = class {
4621
+ config;
4622
+ server = null;
4623
+ resolvePromise = null;
4624
+ rejectPromise = null;
4625
+ loginToken = "";
4626
+ constructor(options = {}) {
4627
+ this.config = { ...DEFAULT_OPTIONS, ...options };
4625
4628
  }
4626
- // Stop simulating progress
4627
- stopSimulatingProgress() {
4628
- this.isSimulatingProgress = false;
4629
+ async login() {
4630
+ console.log(import_chalk3.default.blue("Starting login flow...\n"));
4631
+ this.loginToken = this.generateLoginToken();
4632
+ console.log(import_chalk3.default.blue("Starting local callback server..."));
4633
+ await this.startCallbackServer();
4634
+ try {
4635
+ const loginUrl = this.buildLoginUrl();
4636
+ console.log(import_chalk3.default.blue("Opening browser..."));
4637
+ console.log(import_chalk3.default.white("If browser does not open automatically, please visit manually:"));
4638
+ console.log(import_chalk3.default.cyan(` ${loginUrl}
4639
+ `));
4640
+ openBrowser(loginUrl);
4641
+ console.log(import_chalk3.default.yellow("Please complete login in browser..."));
4642
+ console.log(import_chalk3.default.gray("Browser will close automatically after successful login.\n"));
4643
+ const authToken = await this.waitForCallback();
4644
+ const authConfig = this.parseAuthToken(authToken);
4645
+ this.saveAuthConfig(authConfig);
4646
+ console.log(import_chalk3.default.green("\nLogin successful!"));
4647
+ if (authConfig.email) {
4648
+ console.log(import_chalk3.default.green(`Welcome, ${authConfig.email}`));
4649
+ }
4650
+ console.log(import_chalk3.default.gray(`Address: ${authConfig.address}`));
4651
+ return authConfig;
4652
+ } catch (error) {
4653
+ console.error(import_chalk3.default.red(`
4654
+ Login failed: ${error.message}`));
4655
+ throw error;
4656
+ } finally {
4657
+ this.closeServer();
4658
+ }
4629
4659
  }
4630
- failStep(error) {
4631
- this.stopProgress();
4632
- this.spinner.fail(`Upload failed: ${error}`);
4660
+ generateLoginToken() {
4661
+ return import_crypto.default.randomBytes(32).toString("hex");
4633
4662
  }
4634
- complete() {
4635
- this.stopProgress();
4636
- const totalTime = Math.floor((Date.now() - this.startTime) / 1e3);
4637
- const progressBar = this.createProgressBar(1);
4638
- this.spinner.succeed(
4639
- `Upload completed ${progressBar} 100% (${totalTime}s)`
4640
- );
4663
+ async startCallbackServer() {
4664
+ return new Promise((resolve, reject) => {
4665
+ this.server = import_http3.default.createServer(async (req, res) => {
4666
+ try {
4667
+ const url2 = new import_url2.URL(req.url || "", `http://localhost:${this.config.callbackPort}`);
4668
+ if (url2.pathname === this.config.callbackPath) {
4669
+ const authToken = url2.searchParams.get("token");
4670
+ const error = url2.searchParams.get("error");
4671
+ const loginToken = url2.searchParams.get("login_token");
4672
+ if (error) {
4673
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
4674
+ res.end(this.getErrorHtml(error));
4675
+ if (this.rejectPromise) {
4676
+ this.rejectPromise(new Error(error));
4677
+ }
4678
+ return;
4679
+ }
4680
+ if (!authToken) {
4681
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
4682
+ res.end(this.getErrorHtml("Auth token not received"));
4683
+ if (this.rejectPromise) {
4684
+ this.rejectPromise(new Error("Auth token not received"));
4685
+ }
4686
+ return;
4687
+ }
4688
+ if (loginToken !== this.loginToken) {
4689
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
4690
+ res.end(this.getErrorHtml("Login token invalid or expired"));
4691
+ if (this.rejectPromise) {
4692
+ this.rejectPromise(new Error("Login token invalid or expired"));
4693
+ }
4694
+ return;
4695
+ }
4696
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
4697
+ res.end(this.getSuccessHtml());
4698
+ if (this.resolvePromise) {
4699
+ this.resolvePromise(authToken);
4700
+ }
4701
+ } else {
4702
+ res.writeHead(404, { "Content-Type": "text/plain" });
4703
+ res.end("Not Found");
4704
+ }
4705
+ } catch (err) {
4706
+ console.error("Callback error:", err);
4707
+ res.writeHead(500, { "Content-Type": "text/plain" });
4708
+ res.end("Internal Server Error");
4709
+ }
4710
+ });
4711
+ this.server.on("error", (err) => {
4712
+ reject(err);
4713
+ });
4714
+ this.server.listen(this.config.callbackPort, "127.0.0.1", () => {
4715
+ console.log(import_chalk3.default.gray(`Local server: http://localhost:${this.config.callbackPort}`));
4716
+ resolve();
4717
+ });
4718
+ setTimeout(() => {
4719
+ if (this.rejectPromise) {
4720
+ this.rejectPromise(new Error("Login timeout, please try again"));
4721
+ }
4722
+ this.closeServer();
4723
+ }, 10 * 60 * 1e3);
4724
+ });
4641
4725
  }
4642
- fail(error) {
4643
- this.stopProgress();
4644
- const totalTime = Math.floor((Date.now() - this.startTime) / 1e3);
4645
- this.spinner.fail(`Upload failed: ${error} (${totalTime}s)`);
4726
+ buildLoginUrl() {
4727
+ const callbackUrl = `http://localhost:${this.config.callbackPort}${this.config.callbackPath}`;
4728
+ const url2 = `${this.config.webBaseUrl}/#/cli-login?login_token=${this.loginToken}&callback_url=${encodeURIComponent(callbackUrl)}`;
4729
+ return url2;
4646
4730
  }
4647
- startProgress() {
4648
- this.progressInterval = setInterval(() => {
4649
- const elapsed = Date.now() - this.startTime;
4650
- let progress;
4651
- if (this.isSimulatingProgress) {
4652
- const simulationElapsed = Date.now() - this.simulationStartTime;
4653
- const simulationProgress = Math.min(simulationElapsed / 6e4, 1);
4654
- progress = 0.9 + simulationProgress * 0.09;
4655
- } else {
4656
- progress = this.calculateProgress(elapsed);
4657
- }
4658
- const duration = this.formatDuration(Math.floor(elapsed / 1e3));
4659
- const progressBar = this.createProgressBar(progress);
4660
- this.spinner.text = `Uploading ${this.fileName}... ${progressBar} ${Math.round(progress * 100)}% (${duration})`;
4661
- }, PROGRESS_UPDATE_INTERVAL);
4731
+ waitForCallback() {
4732
+ return new Promise((resolve, reject) => {
4733
+ this.resolvePromise = resolve;
4734
+ this.rejectPromise = reject;
4735
+ });
4662
4736
  }
4663
- stopProgress() {
4664
- if (this.progressInterval) {
4665
- clearInterval(this.progressInterval);
4666
- this.progressInterval = null;
4737
+ parseAuthToken(authToken) {
4738
+ const firstDash = authToken.indexOf("-");
4739
+ if (firstDash <= 0 || firstDash === authToken.length - 1) {
4740
+ throw new Error("Invalid token format");
4667
4741
  }
4742
+ const address = authToken.slice(0, firstDash).trim();
4743
+ const token = authToken.slice(firstDash + 1).trim();
4744
+ if (!address || !token) {
4745
+ throw new Error("Token parsing failed: address or token is empty");
4746
+ }
4747
+ return { address, token };
4668
4748
  }
4669
- calculateProgress(elapsed) {
4670
- return Math.min(
4671
- elapsed / EXPECTED_UPLOAD_TIME * MAX_PROGRESS,
4672
- MAX_PROGRESS
4673
- );
4674
- }
4675
- createProgressBar(progress, width = 20) {
4676
- const percentage = Math.min(progress, 1);
4677
- const filledWidth = Math.round(width * percentage);
4678
- const emptyWidth = width - filledWidth;
4679
- return `[${"\u2588".repeat(filledWidth)}${"\u2591".repeat(emptyWidth)}]`;
4749
+ saveAuthConfig(config) {
4750
+ import_fs_extra4.default.ensureDirSync(CONFIG_DIR2);
4751
+ import_fs_extra4.default.writeJsonSync(AUTH_FILE2, config, { spaces: 2 });
4752
+ import_fs_extra4.default.chmodSync(AUTH_FILE2, 384);
4680
4753
  }
4681
- formatDuration(seconds) {
4682
- if (seconds < 60) {
4683
- return `${seconds}s`;
4684
- } else if (seconds < 3600) {
4685
- const minutes = Math.floor(seconds / 60);
4686
- const remainingSeconds = seconds % 60;
4687
- return `${minutes}m ${remainingSeconds}s`;
4688
- } else {
4689
- const hours = Math.floor(seconds / 3600);
4690
- const minutes = Math.floor(seconds % 3600 / 60);
4691
- const remainingSeconds = seconds % 60;
4692
- return `${hours}h ${minutes}m ${remainingSeconds}s`;
4754
+ closeServer() {
4755
+ if (this.server) {
4756
+ this.server.close();
4757
+ this.server = null;
4758
+ }
4759
+ }
4760
+ // HTML templates
4761
+ getSuccessHtml() {
4762
+ return `
4763
+ <!DOCTYPE html>
4764
+ <html>
4765
+ <head>
4766
+ <title>Login Success - PinMe</title>
4767
+ <style>
4768
+ * { margin: 0; padding: 0; box-sizing: border-box; }
4769
+ body {
4770
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
4771
+ display: flex;
4772
+ justify-content: center;
4773
+ align-items: center;
4774
+ min-height: 100vh;
4775
+ background: #000;
4776
+ overflow: hidden;
4777
+ }
4778
+ .bg {
4779
+ position: fixed;
4780
+ top: 0;
4781
+ left: 0;
4782
+ width: 100%;
4783
+ height: 100%;
4784
+ background:
4785
+ radial-gradient(ellipse at 20% 80%, rgba(120, 0, 255, 0.3) 0%, transparent 50%),
4786
+ radial-gradient(ellipse at 80% 20%, rgba(0, 200, 255, 0.3) 0%, transparent 50%),
4787
+ radial-gradient(ellipse at 50% 50%, rgba(255, 0, 150, 0.15) 0%, transparent 60%);
4788
+ animation: bgPulse 6s ease-in-out infinite;
4789
+ }
4790
+ @keyframes bgPulse {
4791
+ 0%, 100% { opacity: 1; transform: scale(1); }
4792
+ 50% { opacity: 0.8; transform: scale(1.05); }
4793
+ }
4794
+ .grid {
4795
+ position: fixed;
4796
+ top: 0;
4797
+ left: 0;
4798
+ width: 200%;
4799
+ height: 200%;
4800
+ background-image:
4801
+ linear-gradient(rgba(0, 200, 255, 0.03) 1px, transparent 1px),
4802
+ linear-gradient(90deg, rgba(0, 200, 255, 0.03) 1px, transparent 1px);
4803
+ background-size: 50px 50px;
4804
+ transform: perspective(500px) rotateX(60deg) translateY(-50%) translateZ(-200px);
4805
+ animation: gridMove 20s linear infinite;
4806
+ }
4807
+ @keyframes gridMove {
4808
+ 0% { transform: perspective(500px) rotateX(60deg) translateY(0) translateZ(-200px); }
4809
+ 100% { transform: perspective(500px) rotateX(60deg) translateY(50px) translateZ(-200px); }
4810
+ }
4811
+ .container {
4812
+ position: relative;
4813
+ z-index: 10;
4814
+ background: linear-gradient(135deg, rgba(20, 20, 40, 0.9) 0%, rgba(10, 10, 30, 0.95) 100%);
4815
+ padding: 3.5rem 4rem;
4816
+ border-radius: 32px;
4817
+ box-shadow:
4818
+ 0 0 60px rgba(0, 200, 255, 0.15),
4819
+ 0 25px 50px rgba(0, 0, 0, 0.5),
4820
+ inset 0 1px 0 rgba(255, 255, 255, 0.1),
4821
+ inset 0 -1px 0 rgba(0, 200, 255, 0.1);
4822
+ text-align: center;
4823
+ border: 1px solid rgba(0, 200, 255, 0.2);
4824
+ max-width: 440px;
4825
+ backdrop-filter: blur(30px);
4826
+ }
4827
+ .container::before {
4828
+ content: '';
4829
+ position: absolute;
4830
+ top: -1px;
4831
+ left: -1px;
4832
+ right: -1px;
4833
+ bottom: -1px;
4834
+ border-radius: 32px;
4835
+ background: linear-gradient(135deg, rgba(0, 200, 255, 0.5), rgba(255, 0, 150, 0.5), rgba(120, 0, 255, 0.5));
4836
+ z-index: -1;
4837
+ opacity: 0.5;
4838
+ animation: borderGlow 3s ease-in-out infinite;
4839
+ }
4840
+ @keyframes borderGlow {
4841
+ 0%, 100% { opacity: 0.3; }
4842
+ 50% { opacity: 0.7; }
4843
+ }
4844
+ .success-icon {
4845
+ font-size: 5rem;
4846
+ margin-bottom: 1.5rem;
4847
+ animation: bounceIn 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
4848
+ filter: drop-shadow(0 0 20px rgba(0, 200, 255, 0.5));
4849
+ }
4850
+ @keyframes bounceIn {
4851
+ 0% { transform: scale(0); opacity: 0; }
4852
+ 50% { transform: scale(1.2); }
4853
+ 100% { transform: scale(1); opacity: 1; }
4854
+ }
4855
+ h1 {
4856
+ color: #fff;
4857
+ font-size: 2.2rem;
4858
+ font-weight: 700;
4859
+ margin: 0 0 0.75rem 0;
4860
+ background: linear-gradient(90deg, #fff, #00d4ff);
4861
+ -webkit-background-clip: text;
4862
+ -webkit-text-fill-color: transparent;
4863
+ background-clip: text;
4864
+ }
4865
+ p {
4866
+ color: rgba(255, 255, 255, 0.6);
4867
+ font-size: 1.1rem;
4868
+ margin: 0 0 2rem 0;
4869
+ line-height: 1.6;
4870
+ }
4871
+ .highlight { color: #00d4ff; font-weight: 600; }
4872
+ .sparkle {
4873
+ position: absolute;
4874
+ width: 4px;
4875
+ height: 4px;
4876
+ background: #00d4ff;
4877
+ border-radius: 50%;
4878
+ animation: sparkle 2s ease-in-out infinite;
4879
+ }
4880
+ .sparkle:nth-child(1) { top: 20%; left: 10%; animation-delay: 0s; }
4881
+ .sparkle:nth-child(2) { top: 30%; right: 15%; animation-delay: 0.5s; }
4882
+ .sparkle:nth-child(3) { bottom: 25%; left: 20%; animation-delay: 1s; }
4883
+ .sparkle:nth-child(4) { bottom: 35%; right: 10%; animation-delay: 1.5s; }
4884
+ @keyframes sparkle {
4885
+ 0%, 100% { opacity: 0; transform: scale(0); }
4886
+ 50% { opacity: 1; transform: scale(1); }
4887
+ }
4888
+ </style>
4889
+ </head>
4890
+ <body>
4891
+ <div class="bg"></div>
4892
+ <div class="grid"></div>
4893
+ <div class="container">
4894
+ <div class="sparkle"></div>
4895
+ <div class="sparkle"></div>
4896
+ <div class="sparkle"></div>
4897
+ <div class="sparkle"></div>
4898
+ <div class="success-icon">\u{1F389}</div>
4899
+ <h1>Welcome to PinMe</h1>
4900
+ <p>You are now logged in! <span class="highlight">\u{1F680}</span><br>Return to your terminal to continue.</p>
4901
+ </div>
4902
+ </body>
4903
+ </html>`;
4904
+ }
4905
+ getErrorHtml(error) {
4906
+ const encodedError = encodeURIComponent(error);
4907
+ return `
4908
+ <!DOCTYPE html>
4909
+ <html>
4910
+ <head>
4911
+ <title>Login Failed - PinMe</title>
4912
+ <style>
4913
+ * { margin: 0; padding: 0; box-sizing: border-box; }
4914
+ body {
4915
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
4916
+ display: flex;
4917
+ justify-content: center;
4918
+ align-items: center;
4919
+ min-height: 100vh;
4920
+ background: #000;
4921
+ overflow: hidden;
4922
+ }
4923
+ .bg {
4924
+ position: fixed;
4925
+ top: 0;
4926
+ left: 0;
4927
+ width: 100%;
4928
+ height: 100%;
4929
+ background:
4930
+ radial-gradient(ellipse at 20% 80%, rgba(255, 50, 50, 0.2) 0%, transparent 50%),
4931
+ radial-gradient(ellipse at 80% 20%, rgba(255, 100, 50, 0.2) 0%, transparent 50%),
4932
+ radial-gradient(ellipse at 50% 50%, rgba(100, 0, 50, 0.15) 0%, transparent 60%);
4933
+ animation: bgPulse 6s ease-in-out infinite;
4934
+ }
4935
+ @keyframes bgPulse {
4936
+ 0%, 100% { opacity: 1; transform: scale(1); }
4937
+ 50% { opacity: 0.8; transform: scale(1.05); }
4938
+ }
4939
+ .grid {
4940
+ position: fixed;
4941
+ top: 0;
4942
+ left: 0;
4943
+ width: 200%;
4944
+ height: 200%;
4945
+ background-image:
4946
+ linear-gradient(rgba(255, 80, 80, 0.03) 1px, transparent 1px),
4947
+ linear-gradient(90deg, rgba(255, 80, 80, 0.03) 1px, transparent 1px);
4948
+ background-size: 50px 50px;
4949
+ transform: perspective(500px) rotateX(60deg) translateY(-50%) translateZ(-200px);
4950
+ animation: gridMove 20s linear infinite;
4951
+ }
4952
+ @keyframes gridMove {
4953
+ 0% { transform: perspective(500px) rotateX(60deg) translateY(0) translateZ(-200px); }
4954
+ 100% { transform: perspective(500px) rotateX(60deg) translateY(50px) translateZ(-200px); }
4955
+ }
4956
+ .container {
4957
+ position: relative;
4958
+ z-index: 10;
4959
+ background: linear-gradient(135deg, rgba(40, 20, 20, 0.9) 0%, rgba(30, 10, 10, 0.95) 100%);
4960
+ padding: 3.5rem 4rem;
4961
+ border-radius: 32px;
4962
+ box-shadow:
4963
+ 0 0 60px rgba(255, 50, 50, 0.15),
4964
+ 0 25px 50px rgba(0, 0, 0, 0.5),
4965
+ inset 0 1px 0 rgba(255, 255, 255, 0.1),
4966
+ inset 0 -1px 0 rgba(255, 50, 50, 0.1);
4967
+ text-align: center;
4968
+ border: 1px solid rgba(255, 50, 50, 0.2);
4969
+ max-width: 440px;
4970
+ backdrop-filter: blur(30px);
4971
+ }
4972
+ .container::before {
4973
+ content: '';
4974
+ position: absolute;
4975
+ top: -1px;
4976
+ left: -1px;
4977
+ right: -1px;
4978
+ bottom: -1px;
4979
+ border-radius: 32px;
4980
+ background: linear-gradient(135deg, rgba(255, 50, 50, 0.5), rgba(255, 150, 50, 0.5), rgba(150, 0, 50, 0.5));
4981
+ z-index: -1;
4982
+ opacity: 0.5;
4983
+ animation: borderGlow 3s ease-in-out infinite;
4984
+ }
4985
+ @keyframes borderGlow {
4986
+ 0%, 100% { opacity: 0.3; }
4987
+ 50% { opacity: 0.7; }
4988
+ }
4989
+ .error-icon {
4990
+ font-size: 5rem;
4991
+ margin-bottom: 1.5rem;
4992
+ animation: shake 0.5s ease-in-out;
4993
+ filter: drop-shadow(0 0 20px rgba(255, 50, 50, 0.5));
4994
+ }
4995
+ @keyframes shake {
4996
+ 0%, 100% { transform: translateX(0); }
4997
+ 20% { transform: translateX(-10px) rotate(-5deg); }
4998
+ 40% { transform: translateX(10px) rotate(5deg); }
4999
+ 60% { transform: translateX(-10px) rotate(-5deg); }
5000
+ 80% { transform: translateX(10px) rotate(5deg); }
5001
+ }
5002
+ h1 {
5003
+ color: #fff;
5004
+ font-size: 2.2rem;
5005
+ font-weight: 700;
5006
+ margin: 0 0 0.75rem 0;
5007
+ background: linear-gradient(90deg, #fff, #ff5050);
5008
+ -webkit-background-clip: text;
5009
+ -webkit-text-fill-color: transparent;
5010
+ background-clip: text;
5011
+ }
5012
+ .error {
5013
+ color: #ff6b6b;
5014
+ font-size: 1rem;
5015
+ margin: 0 0 2rem 0;
5016
+ padding: 1.25rem;
5017
+ background: rgba(255, 50, 50, 0.1);
5018
+ border-radius: 16px;
5019
+ border: 1px solid rgba(255, 50, 50, 0.2);
5020
+ font-weight: 500;
5021
+ box-shadow: 0 0 20px rgba(255, 50, 50, 0.1);
5022
+ }
5023
+ </style>
5024
+ </head>
5025
+ <body>
5026
+ <div class="bg"></div>
5027
+ <div class="grid"></div>
5028
+ <div class="container">
5029
+ <div class="error-icon">\u{1F635}</div>
5030
+ <h1>Oops!</h1>
5031
+ <div class="error">${error}</div>
5032
+ </div>
5033
+ </body>
5034
+ </html>`;
5035
+ }
5036
+ };
5037
+ var webLoginManager = new WebLoginManager();
5038
+ function setAuthToken(combined) {
5039
+ const firstDash = combined.indexOf("-");
5040
+ if (firstDash <= 0 || firstDash === combined.length - 1) {
5041
+ throw new Error('Invalid token format. Expected "<address>-<jwt>".');
5042
+ }
5043
+ const address = combined.slice(0, firstDash).trim();
5044
+ const token = combined.slice(firstDash + 1).trim();
5045
+ if (!address || !token) {
5046
+ throw new Error("Invalid token content. Address or token is empty.");
5047
+ }
5048
+ const config = { address, token };
5049
+ import_fs_extra4.default.ensureDirSync(CONFIG_DIR2);
5050
+ import_fs_extra4.default.writeJsonSync(AUTH_FILE2, config, { spaces: 2 });
5051
+ return config;
5052
+ }
5053
+ function getAuthConfig2() {
5054
+ try {
5055
+ if (!import_fs_extra4.default.existsSync(AUTH_FILE2)) return null;
5056
+ const data = import_fs_extra4.default.readJsonSync(AUTH_FILE2);
5057
+ if (!(data == null ? void 0 : data.address) || !(data == null ? void 0 : data.token)) return null;
5058
+ return data;
5059
+ } catch {
5060
+ return null;
5061
+ }
5062
+ }
5063
+ function clearAuthToken() {
5064
+ try {
5065
+ if (import_fs_extra4.default.existsSync(AUTH_FILE2)) {
5066
+ import_fs_extra4.default.removeSync(AUTH_FILE2);
5067
+ }
5068
+ } catch (error) {
5069
+ console.error(`Failed to clear auth token: ${error}`);
5070
+ }
5071
+ }
5072
+ function getAuthHeaders() {
5073
+ const conf = getAuthConfig2();
5074
+ if (!conf) {
5075
+ throw new Error("Auth not set. Run: pinme login");
5076
+ }
5077
+ return {
5078
+ "token-address": conf.address,
5079
+ "authentication-tokens": conf.token
5080
+ };
5081
+ }
5082
+
5083
+ // bin/utils/uploadToIpfsSplit.ts
5084
+ var IPFS_API_URL = "https://pinme.dev/api/v3";
5085
+ var MAX_RETRIES = parseInt(process.env.MAX_RETRIES || "2");
5086
+ var RETRY_DELAY = parseInt(process.env.RETRY_DELAY_MS || "1000");
5087
+ var TIMEOUT = parseInt(process.env.TIMEOUT_MS || "600000");
5088
+ var MAX_POLL_TIME = parseInt("5") * 60 * 1e3;
5089
+ var POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL_SECONDS || "2") * 1e3;
5090
+ var PROGRESS_UPDATE_INTERVAL = 200;
5091
+ var EXPECTED_UPLOAD_TIME = 6e4;
5092
+ var MAX_PROGRESS = 0.9;
5093
+ var StepProgressBar = class {
5094
+ spinner;
5095
+ fileName;
5096
+ startTime;
5097
+ currentStep = 0;
5098
+ stepStartTime = 0;
5099
+ progressInterval = null;
5100
+ isSimulatingProgress = false;
5101
+ simulationStartTime = 0;
5102
+ constructor(fileName, isDirectory = false) {
5103
+ this.fileName = fileName;
5104
+ this.spinner = (0, import_ora.default)(`Preparing to upload ${fileName}...`).start();
5105
+ this.startTime = Date.now();
5106
+ this.stepStartTime = Date.now();
5107
+ this.startProgress();
5108
+ }
5109
+ startStep(stepIndex, stepName) {
5110
+ this.currentStep = stepIndex;
5111
+ this.stepStartTime = Date.now();
5112
+ }
5113
+ updateProgress(progress, total) {
5114
+ }
5115
+ completeStep() {
5116
+ }
5117
+ // Start simulating progress to continue display after 90%
5118
+ startSimulatingProgress() {
5119
+ this.isSimulatingProgress = true;
5120
+ this.simulationStartTime = Date.now();
5121
+ }
5122
+ // Stop simulating progress
5123
+ stopSimulatingProgress() {
5124
+ this.isSimulatingProgress = false;
5125
+ }
5126
+ failStep(error) {
5127
+ this.stopProgress();
5128
+ this.spinner.fail(`Upload failed: ${error}`);
5129
+ }
5130
+ complete() {
5131
+ this.stopProgress();
5132
+ const totalTime = Math.floor((Date.now() - this.startTime) / 1e3);
5133
+ const progressBar = this.createProgressBar(1);
5134
+ this.spinner.succeed(
5135
+ `Upload completed ${progressBar} 100% (${totalTime}s)`
5136
+ );
5137
+ }
5138
+ fail(error) {
5139
+ this.stopProgress();
5140
+ const totalTime = Math.floor((Date.now() - this.startTime) / 1e3);
5141
+ this.spinner.fail(`Upload failed: ${error} (${totalTime}s)`);
5142
+ }
5143
+ startProgress() {
5144
+ this.progressInterval = setInterval(() => {
5145
+ const elapsed = Date.now() - this.startTime;
5146
+ let progress;
5147
+ if (this.isSimulatingProgress) {
5148
+ const simulationElapsed = Date.now() - this.simulationStartTime;
5149
+ const simulationProgress = Math.min(simulationElapsed / 6e4, 1);
5150
+ progress = 0.9 + simulationProgress * 0.09;
5151
+ } else {
5152
+ progress = this.calculateProgress(elapsed);
5153
+ }
5154
+ const duration = this.formatDuration(Math.floor(elapsed / 1e3));
5155
+ const progressBar = this.createProgressBar(progress);
5156
+ this.spinner.text = `Uploading ${this.fileName}... ${progressBar} ${Math.round(progress * 100)}% (${duration})`;
5157
+ }, PROGRESS_UPDATE_INTERVAL);
5158
+ }
5159
+ stopProgress() {
5160
+ if (this.progressInterval) {
5161
+ clearInterval(this.progressInterval);
5162
+ this.progressInterval = null;
5163
+ }
5164
+ }
5165
+ calculateProgress(elapsed) {
5166
+ return Math.min(
5167
+ elapsed / EXPECTED_UPLOAD_TIME * MAX_PROGRESS,
5168
+ MAX_PROGRESS
5169
+ );
5170
+ }
5171
+ createProgressBar(progress, width = 20) {
5172
+ const percentage = Math.min(progress, 1);
5173
+ const filledWidth = Math.round(width * percentage);
5174
+ const emptyWidth = width - filledWidth;
5175
+ return `[${"\u2588".repeat(filledWidth)}${"\u2591".repeat(emptyWidth)}]`;
5176
+ }
5177
+ formatDuration(seconds) {
5178
+ if (seconds < 60) {
5179
+ return `${seconds}s`;
5180
+ } else if (seconds < 3600) {
5181
+ const minutes = Math.floor(seconds / 60);
5182
+ const remainingSeconds = seconds % 60;
5183
+ return `${minutes}m ${remainingSeconds}s`;
5184
+ } else {
5185
+ const hours = Math.floor(seconds / 3600);
5186
+ const minutes = Math.floor(seconds % 3600 / 60);
5187
+ const remainingSeconds = seconds % 60;
5188
+ return `${hours}h ${minutes}m ${remainingSeconds}s`;
4693
5189
  }
4694
5190
  }
4695
5191
  };
4696
5192
  async function calculateMD5(filePath) {
4697
5193
  return new Promise((resolve, reject) => {
4698
- const hash = crypto.createHash("md5");
4699
- const stream4 = import_fs_extra4.default.createReadStream(filePath);
5194
+ const hash = crypto2.createHash("md5");
5195
+ const stream4 = import_fs_extra5.default.createReadStream(filePath);
4700
5196
  stream4.on("data", hash.update.bind(hash));
4701
5197
  stream4.on("end", () => resolve(hash.digest("hex")));
4702
5198
  stream4.on("error", reject);
@@ -4705,20 +5201,20 @@ async function calculateMD5(filePath) {
4705
5201
  async function compressDirectory(sourcePath) {
4706
5202
  return new Promise((resolve, reject) => {
4707
5203
  const tempDir = require("os").tmpdir();
4708
- if (!import_fs_extra4.default.existsSync(tempDir)) {
4709
- import_fs_extra4.default.mkdirSync(tempDir, { recursive: true });
5204
+ if (!import_fs_extra5.default.existsSync(tempDir)) {
5205
+ import_fs_extra5.default.mkdirSync(tempDir, { recursive: true });
4710
5206
  }
4711
- const outputPath = import_path5.default.join(
5207
+ const outputPath = import_path6.default.join(
4712
5208
  tempDir,
4713
- `pinme_${import_path5.default.basename(sourcePath)}_${Date.now()}.zip`
5209
+ `pinme_${import_path6.default.basename(sourcePath)}_${Date.now()}.zip`
4714
5210
  );
4715
- const output = import_fs_extra4.default.createWriteStream(outputPath);
5211
+ const output = import_fs_extra5.default.createWriteStream(outputPath);
4716
5212
  const zlib2 = require("zlib");
4717
5213
  const gzip = zlib2.createGzip({ level: 9 });
4718
5214
  output.on("close", () => resolve(outputPath));
4719
5215
  gzip.on("error", reject);
4720
5216
  gzip.pipe(output);
4721
- const stats = import_fs_extra4.default.statSync(sourcePath);
5217
+ const stats = import_fs_extra5.default.statSync(sourcePath);
4722
5218
  if (stats.isDirectory()) {
4723
5219
  const archive = require("archiver");
4724
5220
  const archiveStream = archive("zip", { zlib: { level: 9 } });
@@ -4727,14 +5223,14 @@ async function compressDirectory(sourcePath) {
4727
5223
  archiveStream.directory(sourcePath, false);
4728
5224
  archiveStream.finalize();
4729
5225
  } else {
4730
- const fileStream = import_fs_extra4.default.createReadStream(sourcePath);
5226
+ const fileStream = import_fs_extra5.default.createReadStream(sourcePath);
4731
5227
  fileStream.pipe(gzip);
4732
5228
  }
4733
5229
  });
4734
5230
  }
4735
5231
  async function initChunkSession(filePath, deviceId, isDirectory = false) {
4736
- const stats = import_fs_extra4.default.statSync(filePath);
4737
- const fileName = import_path5.default.basename(filePath);
5232
+ const stats = import_fs_extra5.default.statSync(filePath);
5233
+ const fileName = import_path6.default.basename(filePath);
4738
5234
  const fileSize = stats.size;
4739
5235
  const md5 = await calculateMD5(filePath);
4740
5236
  try {
@@ -4835,7 +5331,7 @@ async function delayWithAbortCheck(delay, signal) {
4835
5331
  });
4836
5332
  }
4837
5333
  async function uploadFileChunks(filePath, sessionId, totalChunks, chunkSize, deviceId, progressBar) {
4838
- const fileData = import_fs_extra4.default.readFileSync(filePath);
5334
+ const fileData = import_fs_extra5.default.readFileSync(filePath);
4839
5335
  const abortController = new AbortController();
4840
5336
  let completedCount = 0;
4841
5337
  let hasFatalError = false;
@@ -4887,17 +5383,27 @@ async function uploadFileChunks(filePath, sessionId, totalChunks, chunkSize, dev
4887
5383
  }
4888
5384
  }
4889
5385
  async function completeChunkUpload(sessionId, deviceId, importAsCar = false) {
5386
+ var _a;
4890
5387
  try {
4891
5388
  const requestBody = { session_id: sessionId, uid: deviceId };
5389
+ const projectName = (_a = process.env.PINME_PROJECT_NAME) == null ? void 0 : _a.trim();
5390
+ let authHeaders = {};
4892
5391
  if (importAsCar) {
4893
5392
  requestBody.import_as_car = true;
4894
5393
  }
5394
+ if (projectName) {
5395
+ requestBody.project_name = projectName;
5396
+ authHeaders = getAuthHeaders();
5397
+ }
4895
5398
  const response = await axios_default.post(
4896
5399
  `${IPFS_API_URL}/chunk/complete`,
4897
5400
  requestBody,
4898
5401
  {
4899
5402
  timeout: TIMEOUT,
4900
- headers: { "Content-Type": "application/json" }
5403
+ headers: {
5404
+ "Content-Type": "application/json",
5405
+ ...authHeaders
5406
+ }
4901
5407
  }
4902
5408
  );
4903
5409
  const { code, msg, data } = response.data;
@@ -4909,525 +5415,214 @@ async function completeChunkUpload(sessionId, deviceId, importAsCar = false) {
4909
5415
  if (axios_default.isAxiosError(error)) {
4910
5416
  throw new Error(`Network error: ${error.message}`);
4911
5417
  }
4912
- throw error;
4913
- }
4914
- }
4915
- async function getChunkStatus(sessionId, deviceId) {
4916
- try {
4917
- const response = await axios_default.get(
4918
- `${IPFS_API_URL}/up_status`,
4919
- {
4920
- params: { trace_id: sessionId, uid: deviceId },
4921
- timeout: TIMEOUT,
4922
- headers: { "Content-Type": "application/json" }
4923
- }
4924
- );
4925
- const { code, msg, data } = response.data;
4926
- if (code === 200) {
4927
- return data;
4928
- }
4929
- throw new Error(`Server returned error: ${msg} (code: ${code})`);
4930
- } catch (error) {
4931
- if (axios_default.isAxiosError(error)) {
4932
- throw new Error(`Network error: ${error.message}`);
4933
- }
4934
- throw error;
4935
- }
4936
- }
4937
- async function monitorChunkProgress(traceId, deviceId, progressBar) {
4938
- let consecutiveErrors = 0;
4939
- const startTime = Date.now();
4940
- if (progressBar) {
4941
- progressBar.startSimulatingProgress();
4942
- }
4943
- try {
4944
- while (Date.now() - startTime < MAX_POLL_TIME) {
4945
- try {
4946
- const status = await getChunkStatus(traceId, deviceId);
4947
- consecutiveErrors = 0;
4948
- if (status.is_ready && status.upload_rst.Hash) {
4949
- if (progressBar) {
4950
- progressBar.stopSimulatingProgress();
4951
- }
4952
- return {
4953
- hash: status.upload_rst.Hash,
4954
- shortUrl: status.upload_rst.ShortUrl
4955
- };
4956
- }
4957
- } catch (error) {
4958
- consecutiveErrors++;
4959
- if (consecutiveErrors > 10) {
4960
- throw new Error(`Polling failed: ${error.message}`);
4961
- }
4962
- }
4963
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
4964
- }
4965
- const maxPollTimeMinutes = Math.floor(MAX_POLL_TIME / (60 * 1e3));
4966
- throw new Error(`Polling timeout after ${maxPollTimeMinutes} minutes`);
4967
- } finally {
4968
- if (progressBar) {
4969
- progressBar.stopSimulatingProgress();
4970
- }
4971
- }
4972
- }
4973
- async function uploadDirectoryInChunks(directoryPath, deviceId, importAsCar = false) {
4974
- const sizeCheck = checkDirectorySizeLimit(directoryPath);
4975
- if (sizeCheck.exceeds) {
4976
- throw new Error(
4977
- `Directory ${directoryPath} exceeds size limit ${formatSize(
4978
- sizeCheck.limit
4979
- )} (size: ${formatSize(sizeCheck.size)})`
4980
- );
4981
- }
4982
- const progressBar = new StepProgressBar(import_path5.default.basename(directoryPath), true);
4983
- try {
4984
- progressBar.startStep(0, "Preparing compression");
4985
- const compressedPath = await compressDirectory(directoryPath);
4986
- progressBar.completeStep();
4987
- progressBar.startStep(1, "Initializing session");
4988
- const sessionInfo = await initChunkSession(compressedPath, deviceId, true);
4989
- progressBar.completeStep();
4990
- progressBar.startStep(2, "Chunk upload");
4991
- await uploadFileChunks(
4992
- compressedPath,
4993
- sessionInfo.session_id,
4994
- sessionInfo.total_chunks,
4995
- sessionInfo.chunk_size,
4996
- deviceId,
4997
- progressBar
4998
- );
4999
- progressBar.completeStep();
5000
- progressBar.startStep(3, "Completing upload");
5001
- const traceId = await completeChunkUpload(sessionInfo.session_id, deviceId, importAsCar);
5002
- progressBar.completeStep();
5003
- progressBar.startStep(4, "Waiting for processing");
5004
- const result = await monitorChunkProgress(traceId, deviceId, progressBar);
5005
- progressBar.completeStep();
5006
- try {
5007
- import_fs_extra4.default.unlinkSync(compressedPath);
5008
- } catch (error) {
5009
- }
5010
- const uploadData = {
5011
- path: directoryPath,
5012
- filename: import_path5.default.basename(directoryPath),
5013
- contentHash: (result == null ? void 0 : result.hash) || "unknown",
5014
- size: sizeCheck.size,
5015
- fileCount: 0,
5016
- isDirectory: true,
5017
- shortUrl: (result == null ? void 0 : result.shortUrl) || null
5018
- };
5019
- saveUploadHistory(uploadData);
5020
- if (!(result == null ? void 0 : result.hash)) {
5021
- throw new Error("Server did not return valid hash value");
5022
- }
5023
- progressBar.complete();
5024
- return result;
5025
- } catch (error) {
5026
- progressBar.fail(error.message);
5027
- throw error;
5028
- }
5029
- }
5030
- async function uploadFileInChunks(filePath, deviceId, importAsCar = false) {
5031
- const sizeCheck = checkFileSizeLimit(filePath);
5032
- if (sizeCheck.exceeds) {
5033
- throw new Error(
5034
- `File ${filePath} exceeds size limit ${formatSize(
5035
- sizeCheck.limit
5036
- )} (size: ${formatSize(sizeCheck.size)})`
5037
- );
5038
- }
5039
- const fileName = import_path5.default.basename(filePath);
5040
- const progressBar = new StepProgressBar(fileName, false);
5041
- try {
5042
- progressBar.startStep(0, "Initializing session");
5043
- const sessionInfo = await initChunkSession(filePath, deviceId, false);
5044
- progressBar.completeStep();
5045
- progressBar.startStep(1, "Chunk upload");
5046
- await uploadFileChunks(
5047
- filePath,
5048
- sessionInfo.session_id,
5049
- sessionInfo.total_chunks,
5050
- sessionInfo.chunk_size,
5051
- deviceId,
5052
- progressBar
5053
- );
5054
- progressBar.completeStep();
5055
- progressBar.startStep(2, "Completing upload");
5056
- const traceId = await completeChunkUpload(sessionInfo.session_id, deviceId, importAsCar);
5057
- progressBar.completeStep();
5058
- progressBar.startStep(3, "Waiting for processing");
5059
- const result = await monitorChunkProgress(traceId, deviceId, progressBar);
5060
- progressBar.completeStep();
5061
- const uploadData = {
5062
- path: filePath,
5063
- filename: fileName,
5064
- contentHash: (result == null ? void 0 : result.hash) || "unknown",
5065
- previewHash: null,
5066
- size: sizeCheck.size,
5067
- fileCount: 1,
5068
- isDirectory: false,
5069
- shortUrl: (result == null ? void 0 : result.shortUrl) || null
5070
- };
5071
- saveUploadHistory(uploadData);
5072
- if (!(result == null ? void 0 : result.hash)) {
5073
- throw new Error("Server did not return valid hash value");
5074
- }
5075
- progressBar.complete();
5076
- return result;
5077
- } catch (error) {
5078
- progressBar.fail(error.message);
5079
- throw error;
5080
- }
5081
- }
5082
- async function uploadToIpfsSplit_default(filePath, importAsCar = false) {
5083
- const deviceId = getUid();
5084
- if (!deviceId) {
5085
- throw new Error("Device ID not found");
5086
- }
5087
- try {
5088
- const isDirectory = import_fs_extra4.default.statSync(filePath).isDirectory();
5089
- const result = isDirectory ? await uploadDirectoryInChunks(filePath, deviceId, importAsCar) : await uploadFileInChunks(filePath, deviceId, importAsCar);
5090
- if (result == null ? void 0 : result.hash) {
5091
- return {
5092
- contentHash: result.hash,
5093
- previewHash: null,
5094
- shortUrl: result.shortUrl
5095
- };
5096
- }
5097
- return null;
5098
- } catch (error) {
5099
- return null;
5100
- }
5101
- }
5102
-
5103
- // bin/upload.ts
5104
- var import_fs2 = __toESM(require("fs"));
5105
- var import_crypto_js = __toESM(require("crypto-js"));
5106
-
5107
- // bin/utils/pinmeApi.ts
5108
- var import_chalk4 = __toESM(require("chalk"));
5109
-
5110
- // bin/utils/webLogin.ts
5111
- var import_crypto = __toESM(require("crypto"));
5112
- var import_http3 = __toESM(require("http"));
5113
- var import_url2 = require("url");
5114
- var import_chalk3 = __toESM(require("chalk"));
5115
- var import_child_process = require("child_process");
5116
- var import_fs_extra5 = __toESM(require("fs-extra"));
5117
- var import_os4 = __toESM(require("os"));
5118
- var import_path6 = __toESM(require("path"));
5119
- function openBrowser(url2) {
5120
- const platform = process.platform;
5121
- let command;
5122
- if (platform === "darwin") {
5123
- command = `open "${url2}"`;
5124
- } else if (platform === "win32") {
5125
- command = `start "" "${url2}"`;
5126
- } else {
5127
- command = `xdg-open "${url2}"`;
5128
- }
5129
- (0, import_child_process.exec)(command, (err) => {
5130
- if (err) {
5131
- console.log(import_chalk3.default.yellow(`Unable to open browser automatically. Please visit manually: ${url2}`));
5132
- }
5133
- });
5134
- }
5135
- var CONFIG_DIR2 = import_path6.default.join(import_os4.default.homedir(), ".pinme");
5136
- var AUTH_FILE2 = import_path6.default.join(CONFIG_DIR2, "auth.json");
5137
- var DEFAULT_OPTIONS = {
5138
- apiBaseUrl: "https://pinme.dev/api/v4",
5139
- webBaseUrl: process.env.PINME_WEB_URL || "http://localhost:5173",
5140
- callbackPort: 3e3,
5141
- callbackPath: "/cli/callback"
5142
- };
5143
- var WebLoginManager = class {
5144
- config;
5145
- server = null;
5146
- resolvePromise = null;
5147
- rejectPromise = null;
5148
- loginToken = "";
5149
- constructor(options = {}) {
5150
- this.config = { ...DEFAULT_OPTIONS, ...options };
5151
- }
5152
- async login() {
5153
- console.log(import_chalk3.default.blue("Starting login flow...\n"));
5154
- this.loginToken = this.generateLoginToken();
5155
- console.log(import_chalk3.default.blue("Starting local callback server..."));
5156
- await this.startCallbackServer();
5157
- try {
5158
- const loginUrl = this.buildLoginUrl();
5159
- console.log(import_chalk3.default.blue("Opening browser..."));
5160
- console.log(import_chalk3.default.white("If browser does not open automatically, please visit manually:"));
5161
- console.log(import_chalk3.default.cyan(` ${loginUrl}
5162
- `));
5163
- openBrowser(loginUrl);
5164
- console.log(import_chalk3.default.yellow("Please complete login in browser..."));
5165
- console.log(import_chalk3.default.gray("Browser will close automatically after successful login.\n"));
5166
- const authToken = await this.waitForCallback();
5167
- const authConfig = this.parseAuthToken(authToken);
5168
- this.saveAuthConfig(authConfig);
5169
- console.log(import_chalk3.default.green("\nLogin successful!"));
5170
- if (authConfig.email) {
5171
- console.log(import_chalk3.default.green(`Welcome, ${authConfig.email}`));
5172
- }
5173
- console.log(import_chalk3.default.gray(`Address: ${authConfig.address}`));
5174
- return authConfig;
5175
- } catch (error) {
5176
- console.error(import_chalk3.default.red(`
5177
- Login failed: ${error.message}`));
5178
- throw error;
5179
- } finally {
5180
- this.closeServer();
5181
- }
5182
- }
5183
- generateLoginToken() {
5184
- return import_crypto.default.randomBytes(32).toString("hex");
5185
- }
5186
- async startCallbackServer() {
5187
- return new Promise((resolve, reject) => {
5188
- this.server = import_http3.default.createServer(async (req, res) => {
5189
- try {
5190
- const url2 = new import_url2.URL(req.url || "", `http://localhost:${this.config.callbackPort}`);
5191
- if (url2.pathname === this.config.callbackPath) {
5192
- const authToken = url2.searchParams.get("token");
5193
- const error = url2.searchParams.get("error");
5194
- const loginToken = url2.searchParams.get("login_token");
5195
- if (error) {
5196
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
5197
- res.end(this.getErrorHtml(error));
5198
- if (this.rejectPromise) {
5199
- this.rejectPromise(new Error(error));
5200
- }
5201
- return;
5202
- }
5203
- if (!authToken) {
5204
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
5205
- res.end(this.getErrorHtml("Auth token not received"));
5206
- if (this.rejectPromise) {
5207
- this.rejectPromise(new Error("Auth token not received"));
5208
- }
5209
- return;
5210
- }
5211
- if (loginToken !== this.loginToken) {
5212
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
5213
- res.end(this.getErrorHtml("Login token invalid or expired"));
5214
- if (this.rejectPromise) {
5215
- this.rejectPromise(new Error("Login token invalid or expired"));
5216
- }
5217
- return;
5218
- }
5219
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
5220
- res.end(this.getSuccessHtml());
5221
- if (this.resolvePromise) {
5222
- this.resolvePromise(authToken);
5223
- }
5224
- } else {
5225
- res.writeHead(404, { "Content-Type": "text/plain" });
5226
- res.end("Not Found");
5227
- }
5228
- } catch (err) {
5229
- console.error("Callback error:", err);
5230
- res.writeHead(500, { "Content-Type": "text/plain" });
5231
- res.end("Internal Server Error");
5232
- }
5233
- });
5234
- this.server.on("error", (err) => {
5235
- reject(err);
5236
- });
5237
- this.server.listen(this.config.callbackPort, "127.0.0.1", () => {
5238
- console.log(import_chalk3.default.gray(`Local server: http://localhost:${this.config.callbackPort}`));
5239
- resolve();
5240
- });
5241
- setTimeout(() => {
5242
- if (this.rejectPromise) {
5243
- this.rejectPromise(new Error("Login timeout, please try again"));
5244
- }
5245
- this.closeServer();
5246
- }, 10 * 60 * 1e3);
5247
- });
5248
- }
5249
- buildLoginUrl() {
5250
- const callbackUrl = `http://localhost:${this.config.callbackPort}${this.config.callbackPath}`;
5251
- const url2 = `${this.config.webBaseUrl}/#/cli-login?login_token=${this.loginToken}&callback_url=${encodeURIComponent(callbackUrl)}`;
5252
- return url2;
5253
- }
5254
- waitForCallback() {
5255
- return new Promise((resolve, reject) => {
5256
- this.resolvePromise = resolve;
5257
- this.rejectPromise = reject;
5258
- });
5259
- }
5260
- parseAuthToken(authToken) {
5261
- const firstDash = authToken.indexOf("-");
5262
- if (firstDash <= 0 || firstDash === authToken.length - 1) {
5263
- throw new Error("Invalid token format");
5264
- }
5265
- const address = authToken.slice(0, firstDash).trim();
5266
- const token = authToken.slice(firstDash + 1).trim();
5267
- if (!address || !token) {
5268
- throw new Error("Token parsing failed: address or token is empty");
5269
- }
5270
- return { address, token };
5271
- }
5272
- saveAuthConfig(config) {
5273
- import_fs_extra5.default.ensureDirSync(CONFIG_DIR2);
5274
- import_fs_extra5.default.writeJsonSync(AUTH_FILE2, config, { spaces: 2 });
5275
- import_fs_extra5.default.chmodSync(AUTH_FILE2, 384);
5276
- }
5277
- closeServer() {
5278
- if (this.server) {
5279
- this.server.close();
5280
- this.server = null;
5281
- }
5282
- }
5283
- // HTML templates
5284
- getSuccessHtml() {
5285
- return `
5286
- <!DOCTYPE html>
5287
- <html>
5288
- <head>
5289
- <title>Login Success - PinMe</title>
5290
- <style>
5291
- body {
5292
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
5293
- display: flex;
5294
- justify-content: center;
5295
- align-items: center;
5296
- height: 100vh;
5297
- margin: 0;
5298
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
5299
- }
5300
- .container {
5301
- background: white;
5302
- padding: 3rem;
5303
- border-radius: 12px;
5304
- box-shadow: 0 20px 60px rgba(0,0,0,0.3);
5305
- text-align: center;
5306
- max-width: 400px;
5307
- }
5308
- .success-icon {
5309
- font-size: 4rem;
5310
- margin-bottom: 1rem;
5311
- }
5312
- h1 {
5313
- color: #1f2937;
5314
- margin: 0 0 1rem 0;
5315
- }
5316
- p {
5317
- color: #6b7280;
5318
- margin: 0 0 2rem 0;
5418
+ throw error;
5419
+ }
5420
+ }
5421
+ async function getChunkStatus(sessionId, deviceId) {
5422
+ var _a;
5423
+ try {
5424
+ const projectName = (_a = process.env.PINME_PROJECT_NAME) == null ? void 0 : _a.trim();
5425
+ const queryParams = new URLSearchParams({
5426
+ trace_id: sessionId,
5427
+ uid: deviceId
5428
+ });
5429
+ if (projectName) {
5430
+ queryParams.append("project_name", projectName);
5319
5431
  }
5320
- .close-btn {
5321
- background: #3b82f6;
5322
- color: white;
5323
- border: none;
5324
- padding: 0.75rem 2rem;
5325
- border-radius: 6px;
5326
- font-size: 1rem;
5327
- cursor: pointer;
5328
- transition: background 0.2s;
5432
+ const response = await axios_default.get(
5433
+ `${IPFS_API_URL}/up_status?${queryParams.toString()}`,
5434
+ {
5435
+ timeout: TIMEOUT,
5436
+ headers: { "Content-Type": "application/json" }
5437
+ }
5438
+ );
5439
+ const { code, msg, data } = response.data;
5440
+ if (code === 200) {
5441
+ return data;
5329
5442
  }
5330
- .close-btn:hover {
5331
- background: #2563eb;
5443
+ throw new Error(`Server returned error: ${msg} (code: ${code})`);
5444
+ } catch (error) {
5445
+ if (axios_default.isAxiosError(error)) {
5446
+ throw new Error(`Network error: ${error.message}`);
5332
5447
  }
5333
- </style>
5334
- </head>
5335
- <body>
5336
- <div class="container">
5337
- <div class="success-icon">\u2705</div>
5338
- <h1>Login Successful!</h1>
5339
- <p>You have successfully logged in to PinMe CLI.</p>
5340
- <p>You can close this window and return to the command line.</p>
5341
- <button id="closeBtn" class="close-btn">Close Window</button>
5342
- <p id="message" style="color: #6b7280; margin-top: 1rem;"></p>
5343
- </div>
5344
- <script>
5345
- // For redirected pages, show message instead of auto-close
5346
- document.addEventListener('DOMContentLoaded', function() {
5347
- document.getElementById('closeBtn').addEventListener('click', function() {
5348
- // Try to close, fallback to showing message
5349
- try {
5350
- window.close();
5351
- } catch (e) {
5352
- document.getElementById('message').textContent = 'You can close this window manually.';
5353
- }
5354
- });
5355
- });
5356
- </script>
5357
- </body>
5358
- </html>`;
5359
- }
5360
- getErrorHtml(error) {
5361
- const encodedError = encodeURIComponent(error);
5362
- return `
5363
- <!DOCTYPE html>
5364
- <html>
5365
- <head>
5366
- <title>Login Failed - PinMe</title>
5367
- <style>
5368
- body { font-family: sans-serif; padding: 2rem; text-align: center; background: #f3f4f6; }
5369
- .container { background: white; padding: 2rem; border-radius: 12px; max-width: 400px; margin: 0 auto; }
5370
- .error { color: #dc2626; font-size: 1.25rem; margin: 1rem 0; }
5371
- button { padding: 0.5rem 1rem; cursor: pointer; background: #3b82f6; color: white; border: none; border-radius: 6px; }
5372
- </style>
5373
- </head>
5374
- <body>
5375
- <div class="container">
5376
- <h2>Login Failed</h2>
5377
- <div class="error">${error}</div>
5378
- <button onclick="window.close()">Close</button>
5379
- </div>
5380
- </body>
5381
- </html>`;
5448
+ throw error;
5382
5449
  }
5383
- };
5384
- var webLoginManager = new WebLoginManager();
5385
- function setAuthToken(combined) {
5386
- const firstDash = combined.indexOf("-");
5387
- if (firstDash <= 0 || firstDash === combined.length - 1) {
5388
- throw new Error('Invalid token format. Expected "<address>-<jwt>".');
5450
+ }
5451
+ async function monitorChunkProgress(traceId, deviceId, progressBar) {
5452
+ let consecutiveErrors = 0;
5453
+ const startTime = Date.now();
5454
+ if (progressBar) {
5455
+ progressBar.startSimulatingProgress();
5389
5456
  }
5390
- const address = combined.slice(0, firstDash).trim();
5391
- const token = combined.slice(firstDash + 1).trim();
5392
- if (!address || !token) {
5393
- throw new Error("Invalid token content. Address or token is empty.");
5457
+ try {
5458
+ while (Date.now() - startTime < MAX_POLL_TIME) {
5459
+ try {
5460
+ const status = await getChunkStatus(traceId, deviceId);
5461
+ consecutiveErrors = 0;
5462
+ if (status.is_ready && status.upload_rst.Hash) {
5463
+ if (progressBar) {
5464
+ progressBar.stopSimulatingProgress();
5465
+ }
5466
+ const shortUrl = status.upload_rst.ShortUrl;
5467
+ const domain = status.domain;
5468
+ const fullShortUrl = shortUrl && domain ? `${shortUrl}.${domain}` : shortUrl;
5469
+ return {
5470
+ hash: status.upload_rst.Hash,
5471
+ shortUrl: fullShortUrl
5472
+ };
5473
+ }
5474
+ } catch (error) {
5475
+ consecutiveErrors++;
5476
+ if (consecutiveErrors > 10) {
5477
+ throw new Error(`Polling failed: ${error.message}`);
5478
+ }
5479
+ }
5480
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
5481
+ }
5482
+ const maxPollTimeMinutes = Math.floor(MAX_POLL_TIME / (60 * 1e3));
5483
+ throw new Error(`Polling timeout after ${maxPollTimeMinutes} minutes`);
5484
+ } finally {
5485
+ if (progressBar) {
5486
+ progressBar.stopSimulatingProgress();
5487
+ }
5394
5488
  }
5395
- const config = { address, token };
5396
- import_fs_extra5.default.ensureDirSync(CONFIG_DIR2);
5397
- import_fs_extra5.default.writeJsonSync(AUTH_FILE2, config, { spaces: 2 });
5398
- return config;
5399
5489
  }
5400
- function getAuthConfig2() {
5490
+ async function uploadDirectoryInChunks(directoryPath, deviceId, importAsCar = false) {
5491
+ const sizeCheck = checkDirectorySizeLimit(directoryPath);
5492
+ if (sizeCheck.exceeds) {
5493
+ throw new Error(
5494
+ `Directory ${directoryPath} exceeds size limit ${formatSize(
5495
+ sizeCheck.limit
5496
+ )} (size: ${formatSize(sizeCheck.size)})`
5497
+ );
5498
+ }
5499
+ const progressBar = new StepProgressBar(import_path6.default.basename(directoryPath), true);
5401
5500
  try {
5402
- if (!import_fs_extra5.default.existsSync(AUTH_FILE2)) return null;
5403
- const data = import_fs_extra5.default.readJsonSync(AUTH_FILE2);
5404
- if (!(data == null ? void 0 : data.address) || !(data == null ? void 0 : data.token)) return null;
5405
- return data;
5406
- } catch {
5407
- return null;
5501
+ progressBar.startStep(0, "Preparing compression");
5502
+ const compressedPath = await compressDirectory(directoryPath);
5503
+ progressBar.completeStep();
5504
+ progressBar.startStep(1, "Initializing session");
5505
+ const sessionInfo = await initChunkSession(compressedPath, deviceId, true);
5506
+ progressBar.completeStep();
5507
+ progressBar.startStep(2, "Chunk upload");
5508
+ await uploadFileChunks(
5509
+ compressedPath,
5510
+ sessionInfo.session_id,
5511
+ sessionInfo.total_chunks,
5512
+ sessionInfo.chunk_size,
5513
+ deviceId,
5514
+ progressBar
5515
+ );
5516
+ progressBar.completeStep();
5517
+ progressBar.startStep(3, "Completing upload");
5518
+ const traceId = await completeChunkUpload(sessionInfo.session_id, deviceId, importAsCar);
5519
+ progressBar.completeStep();
5520
+ progressBar.startStep(4, "Waiting for processing");
5521
+ const result = await monitorChunkProgress(traceId, deviceId, progressBar);
5522
+ progressBar.completeStep();
5523
+ try {
5524
+ import_fs_extra5.default.unlinkSync(compressedPath);
5525
+ } catch (error) {
5526
+ }
5527
+ const uploadData = {
5528
+ path: directoryPath,
5529
+ filename: import_path6.default.basename(directoryPath),
5530
+ contentHash: (result == null ? void 0 : result.hash) || "unknown",
5531
+ size: sizeCheck.size,
5532
+ fileCount: 0,
5533
+ isDirectory: true,
5534
+ shortUrl: (result == null ? void 0 : result.shortUrl) || null
5535
+ };
5536
+ saveUploadHistory(uploadData);
5537
+ if (!(result == null ? void 0 : result.hash)) {
5538
+ throw new Error("Server did not return valid hash value");
5539
+ }
5540
+ progressBar.complete();
5541
+ return result;
5542
+ } catch (error) {
5543
+ progressBar.fail(error.message);
5544
+ throw error;
5408
5545
  }
5409
5546
  }
5410
- function clearAuthToken() {
5547
+ async function uploadFileInChunks(filePath, deviceId, importAsCar = false) {
5548
+ const sizeCheck = checkFileSizeLimit(filePath);
5549
+ if (sizeCheck.exceeds) {
5550
+ throw new Error(
5551
+ `File ${filePath} exceeds size limit ${formatSize(
5552
+ sizeCheck.limit
5553
+ )} (size: ${formatSize(sizeCheck.size)})`
5554
+ );
5555
+ }
5556
+ const fileName = import_path6.default.basename(filePath);
5557
+ const progressBar = new StepProgressBar(fileName, false);
5411
5558
  try {
5412
- if (import_fs_extra5.default.existsSync(AUTH_FILE2)) {
5413
- import_fs_extra5.default.removeSync(AUTH_FILE2);
5559
+ progressBar.startStep(0, "Initializing session");
5560
+ const sessionInfo = await initChunkSession(filePath, deviceId, false);
5561
+ progressBar.completeStep();
5562
+ progressBar.startStep(1, "Chunk upload");
5563
+ await uploadFileChunks(
5564
+ filePath,
5565
+ sessionInfo.session_id,
5566
+ sessionInfo.total_chunks,
5567
+ sessionInfo.chunk_size,
5568
+ deviceId,
5569
+ progressBar
5570
+ );
5571
+ progressBar.completeStep();
5572
+ progressBar.startStep(2, "Completing upload");
5573
+ const traceId = await completeChunkUpload(sessionInfo.session_id, deviceId, importAsCar);
5574
+ progressBar.completeStep();
5575
+ progressBar.startStep(3, "Waiting for processing");
5576
+ const result = await monitorChunkProgress(traceId, deviceId, progressBar);
5577
+ progressBar.completeStep();
5578
+ const uploadData = {
5579
+ path: filePath,
5580
+ filename: fileName,
5581
+ contentHash: (result == null ? void 0 : result.hash) || "unknown",
5582
+ previewHash: null,
5583
+ size: sizeCheck.size,
5584
+ fileCount: 1,
5585
+ isDirectory: false,
5586
+ shortUrl: (result == null ? void 0 : result.shortUrl) || null
5587
+ };
5588
+ saveUploadHistory(uploadData);
5589
+ if (!(result == null ? void 0 : result.hash)) {
5590
+ throw new Error("Server did not return valid hash value");
5414
5591
  }
5592
+ progressBar.complete();
5593
+ return result;
5415
5594
  } catch (error) {
5416
- console.error(`Failed to clear auth token: ${error}`);
5595
+ progressBar.fail(error.message);
5596
+ throw error;
5417
5597
  }
5418
5598
  }
5419
- function getAuthHeaders() {
5420
- const conf = getAuthConfig2();
5421
- if (!conf) {
5422
- throw new Error("Auth not set. Run: pinme login");
5599
+ async function uploadToIpfsSplit_default(filePath, importAsCar = false) {
5600
+ const deviceId = getUid();
5601
+ if (!deviceId) {
5602
+ throw new Error("Device ID not found");
5603
+ }
5604
+ try {
5605
+ const isDirectory = import_fs_extra5.default.statSync(filePath).isDirectory();
5606
+ const result = isDirectory ? await uploadDirectoryInChunks(filePath, deviceId, importAsCar) : await uploadFileInChunks(filePath, deviceId, importAsCar);
5607
+ if (result == null ? void 0 : result.hash) {
5608
+ return {
5609
+ contentHash: result.hash,
5610
+ previewHash: null,
5611
+ shortUrl: result.shortUrl
5612
+ };
5613
+ }
5614
+ throw new Error("Upload failed: no hash returned");
5615
+ } catch (error) {
5616
+ throw error;
5423
5617
  }
5424
- return {
5425
- "token-address": conf.address,
5426
- "authentication-tokens": conf.token
5427
- };
5428
5618
  }
5429
5619
 
5620
+ // bin/upload.ts
5621
+ var import_fs2 = __toESM(require("fs"));
5622
+ var import_crypto_js = __toESM(require("crypto-js"));
5623
+
5430
5624
  // bin/utils/pinmeApi.ts
5625
+ var import_chalk4 = __toESM(require("chalk"));
5431
5626
  var DEFAULT_BASE = "https://pinme.dev/api/v4";
5432
5627
  var TOKEN_EXPIRED_CODES = [
5433
5628
  401,
@@ -5609,7 +5804,7 @@ async function isVip(tokenAddress, authToken) {
5609
5804
  throw e;
5610
5805
  }
5611
5806
  }
5612
- var CAR_API_BASE = process.env.CAR_API_BASE || "https://pinme.dev/api/v4";
5807
+ var CAR_API_BASE = process.env.CAR_API_BASE || "https://pinme.dev/api/v3";
5613
5808
  function createCarClient() {
5614
5809
  let headers = {};
5615
5810
  try {
@@ -5740,6 +5935,31 @@ function checkPathSync(inputPath) {
5740
5935
  return null;
5741
5936
  }
5742
5937
  }
5938
+ function formatEnsUrl(shortUrl) {
5939
+ if (!shortUrl) return "";
5940
+ const normalized = shortUrl.trim();
5941
+ if (!normalized) return "";
5942
+ if (/^https?:\/\//.test(normalized)) return normalized;
5943
+ if (normalized.includes(".")) return `https://${normalized}`;
5944
+ return `https://${normalized}.pinit.eth.limo`;
5945
+ }
5946
+ function printUploadUrls(contentHash, shortUrl) {
5947
+ var _a;
5948
+ const uid = getUid();
5949
+ const encryptedCID = encryptHash(contentHash, secretKey, uid);
5950
+ const previewUrl = `${URL3}${encryptedCID}`;
5951
+ const projectName = (_a = process.env.PINME_PROJECT_NAME) == null ? void 0 : _a.trim();
5952
+ if (projectName) {
5953
+ const ensUrl = formatEnsUrl(shortUrl);
5954
+ console.log(import_chalk5.default.cyan(`URL:`));
5955
+ console.log(import_chalk5.default.cyan(ensUrl || previewUrl));
5956
+ console.log(import_chalk5.default.cyan(`Management page:`));
5957
+ console.log(import_chalk5.default.cyan(previewUrl));
5958
+ return;
5959
+ }
5960
+ console.log(import_chalk5.default.cyan(`URL:`));
5961
+ console.log(import_chalk5.default.cyan(previewUrl));
5962
+ }
5743
5963
  function getDomainFromArgs() {
5744
5964
  const args = process.argv.slice(2);
5745
5965
  const dIdx = args.findIndex((a) => a === "--domain" || a === "-d");
@@ -5865,40 +6085,40 @@ var upload_default = async (options) => {
5865
6085
  throw e;
5866
6086
  }
5867
6087
  }
5868
- console.log(import_chalk5.default.blue(`uploading ${absolutePath} to ipfs...`));
5869
- try {
5870
- const result = await uploadToIpfsSplit_default(absolutePath);
5871
- if (result) {
5872
- const uid = getUid();
5873
- const encryptedCID = encryptHash(result.contentHash, secretKey, uid);
5874
- console.log(
5875
- import_chalk5.default.cyan(
5876
- import_figlet.default.textSync("Successful", { horizontalLayout: "full" })
5877
- )
5878
- );
5879
- console.log(import_chalk5.default.cyan(`URL:`));
5880
- console.log(import_chalk5.default.cyan(`${URL3}${encryptedCID}`));
5881
- if (domainArg) {
5882
- console.log(
5883
- import_chalk5.default.blue(
5884
- `Binding domain: ${displayDomain} with CID: ${result.contentHash}`
5885
- )
5886
- );
5887
- try {
5888
- await bindDomain(domainArg, result.contentHash, isDns, authConfig);
5889
- } catch (e) {
5890
- if (e.message === "Token expired") {
5891
- process.exit(1);
5892
- }
5893
- throw e;
5894
- }
5895
- }
5896
- console.log(import_chalk5.default.green("\n\u{1F389} upload successful, program exit"));
5897
- }
5898
- } catch (error) {
5899
- console.error(import_chalk5.default.red(`Error: ${error.message}`));
5900
- process.exit(1);
5901
- }
6088
+ console.log(import_chalk5.default.blue(`uploading ${absolutePath} to ipfs...`));
6089
+ let result;
6090
+ try {
6091
+ result = await uploadToIpfsSplit_default(absolutePath);
6092
+ } catch (error) {
6093
+ console.error(import_chalk5.default.red(`Upload error: ${error.message}`));
6094
+ process.exit(1);
6095
+ }
6096
+ if (!result) {
6097
+ console.error(import_chalk5.default.red("Upload failed: no result returned"));
6098
+ process.exit(1);
6099
+ }
6100
+ console.log(
6101
+ import_chalk5.default.cyan(
6102
+ import_figlet.default.textSync("Successful", { horizontalLayout: "full" })
6103
+ )
6104
+ );
6105
+ printUploadUrls(result.contentHash, result.shortUrl);
6106
+ if (domainArg) {
6107
+ console.log(
6108
+ import_chalk5.default.blue(
6109
+ `Binding domain: ${displayDomain} with CID: ${result.contentHash}`
6110
+ )
6111
+ );
6112
+ try {
6113
+ await bindDomain(domainArg, result.contentHash, isDns, authConfig);
6114
+ } catch (e) {
6115
+ if (e.message === "Token expired") {
6116
+ process.exit(1);
6117
+ }
6118
+ throw e;
6119
+ }
6120
+ }
6121
+ console.log(import_chalk5.default.green("\n\u{1F389} upload successful, program exit"));
5902
6122
  process.exit(0);
5903
6123
  }
5904
6124
  const answer = await import_inquirer.default.prompt([
@@ -5957,39 +6177,39 @@ var upload_default = async (options) => {
5957
6177
  }
5958
6178
  }
5959
6179
  console.log(import_chalk5.default.blue(`uploading ${absolutePath} to ipfs...`));
6180
+ let result;
5960
6181
  try {
5961
- const result = await uploadToIpfsSplit_default(absolutePath);
5962
- if (result) {
5963
- const uid = getUid();
5964
- const encryptedCID = encryptHash(result.contentHash, secretKey, uid);
5965
- console.log(
5966
- import_chalk5.default.cyan(
5967
- import_figlet.default.textSync("Successful", { horizontalLayout: "full" })
5968
- )
5969
- );
5970
- console.log(import_chalk5.default.cyan(`URL:`));
5971
- console.log(import_chalk5.default.cyan(`${URL3}${encryptedCID}`));
5972
- if (domainArg) {
5973
- console.log(
5974
- import_chalk5.default.blue(
5975
- `Binding domain: ${displayDomain} with CID: ${result.contentHash}`
5976
- )
5977
- );
5978
- try {
5979
- await bindDomain(domainArg, result.contentHash, isDns, authConfig);
5980
- } catch (e) {
5981
- if (e.message === "Token expired") {
5982
- process.exit(1);
5983
- }
5984
- throw e;
5985
- }
5986
- }
5987
- console.log(import_chalk5.default.green("\n\u{1F389} upload successful, program exit"));
5988
- }
6182
+ result = await uploadToIpfsSplit_default(absolutePath);
5989
6183
  } catch (error) {
5990
- console.error(import_chalk5.default.red(`Error: ${error.message}`));
6184
+ console.error(import_chalk5.default.red(`Upload error: ${error.message}`));
6185
+ process.exit(1);
6186
+ }
6187
+ if (!result) {
6188
+ console.error(import_chalk5.default.red("Upload failed: no result returned"));
5991
6189
  process.exit(1);
5992
6190
  }
6191
+ console.log(
6192
+ import_chalk5.default.cyan(
6193
+ import_figlet.default.textSync("Successful", { horizontalLayout: "full" })
6194
+ )
6195
+ );
6196
+ printUploadUrls(result.contentHash, result.shortUrl);
6197
+ if (domainArg) {
6198
+ console.log(
6199
+ import_chalk5.default.blue(
6200
+ `Binding domain: ${displayDomain} with CID: ${result.contentHash}`
6201
+ )
6202
+ );
6203
+ try {
6204
+ await bindDomain(domainArg, result.contentHash, isDns, authConfig);
6205
+ } catch (e) {
6206
+ if (e.message === "Token expired") {
6207
+ process.exit(1);
6208
+ }
6209
+ throw e;
6210
+ }
6211
+ }
6212
+ console.log(import_chalk5.default.green("\n\u{1F389} upload successful, program exit"));
5993
6213
  process.exit(0);
5994
6214
  }
5995
6215
  } catch (error) {
@@ -6811,7 +7031,6 @@ async function bindCmd() {
6811
7031
  return;
6812
7032
  }
6813
7033
  const isDns = dns || isDnsDomain2(domain);
6814
- console.log(isDns, "isDns");
6815
7034
  const displayDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
6816
7035
  if (isDns) {
6817
7036
  const validation = validateDnsDomain2(domain);
@@ -6894,14 +7113,6 @@ var ENV_URLS = {
6894
7113
  };
6895
7114
  async function loginCmd(options = {}) {
6896
7115
  try {
6897
- const existingAuth = getAuthConfig2();
6898
- if (existingAuth) {
6899
- console.log(import_chalk15.default.yellow("Already logged in"));
6900
- console.log(import_chalk15.default.gray(` Address: ${existingAuth.address}`));
6901
- console.log(import_chalk15.default.gray(" To re-login, please run: pinme logout\n"));
6902
- process.exit(0);
6903
- return;
6904
- }
6905
7116
  let webBaseUrl;
6906
7117
  const env = (options.env || "prod").toLowerCase();
6907
7118
  if (ENV_URLS[env]) {
@@ -6929,23 +7140,174 @@ Login failed: ${(e == null ? void 0 : e.message) || e}`));
6929
7140
  }
6930
7141
 
6931
7142
  // bin/create.ts
6932
- var import_chalk16 = __toESM(require("chalk"));
7143
+ var import_chalk17 = __toESM(require("chalk"));
6933
7144
  var import_fs_extra6 = __toESM(require("fs-extra"));
6934
7145
  var import_path11 = __toESM(require("path"));
6935
7146
  var import_inquirer8 = __toESM(require("inquirer"));
6936
- var TEMPLATE_DIR = import_path11.default.join(__dirname, "../template");
7147
+ var import_child_process2 = require("child_process");
7148
+
7149
+ // bin/utils/cliError.ts
7150
+ var import_chalk16 = __toESM(require("chalk"));
7151
+ var CliError = class extends Error {
7152
+ stage;
7153
+ details;
7154
+ suggestions;
7155
+ cause;
7156
+ constructor(options) {
7157
+ super(options.summary);
7158
+ this.name = "CliError";
7159
+ this.stage = options.stage;
7160
+ this.details = options.details || [];
7161
+ this.suggestions = options.suggestions || [];
7162
+ this.cause = options.cause;
7163
+ }
7164
+ };
7165
+ function stringifyValue(value) {
7166
+ if (value === void 0 || value === null) {
7167
+ return "";
7168
+ }
7169
+ if (typeof value === "string") {
7170
+ return value;
7171
+ }
7172
+ try {
7173
+ return JSON.stringify(value);
7174
+ } catch (error) {
7175
+ return String(value);
7176
+ }
7177
+ }
7178
+ function getApiMessage(data) {
7179
+ var _a, _b, _c;
7180
+ return ((_a = data == null ? void 0 : data.data) == null ? void 0 : _a.error) || ((_c = (_b = data == null ? void 0 : data.errors) == null ? void 0 : _b[0]) == null ? void 0 : _c.message) || (data == null ? void 0 : data.message) || (data == null ? void 0 : data.msg) || (data == null ? void 0 : data.error);
7181
+ }
7182
+ function getBusinessCode(data) {
7183
+ if ((data == null ? void 0 : data.code) === void 0 || (data == null ? void 0 : data.code) === null) {
7184
+ return void 0;
7185
+ }
7186
+ return String(data.code);
7187
+ }
7188
+ function getBusinessMessage(data) {
7189
+ if (!(data == null ? void 0 : data.msg)) {
7190
+ return void 0;
7191
+ }
7192
+ return String(data.msg);
7193
+ }
7194
+ function dedupeSuggestions(suggestions) {
7195
+ return Array.from(new Set(suggestions.filter(Boolean)));
7196
+ }
7197
+ function createConfigError(summary, suggestions = []) {
7198
+ return new CliError({
7199
+ summary,
7200
+ stage: "configuration",
7201
+ suggestions
7202
+ });
7203
+ }
7204
+ function createCommandError(stage, command, error, suggestions = []) {
7205
+ const exitCode = (error == null ? void 0 : error.status) ?? (error == null ? void 0 : error.code);
7206
+ const signal = error == null ? void 0 : error.signal;
7207
+ const detailLines = [`Command: ${command}`];
7208
+ if (exitCode !== void 0) {
7209
+ detailLines.push(`Exit code: ${exitCode}`);
7210
+ }
7211
+ if (signal) {
7212
+ detailLines.push(`Signal: ${signal}`);
7213
+ }
7214
+ if (error == null ? void 0 : error.message) {
7215
+ detailLines.push(`Reason: ${error.message}`);
7216
+ }
7217
+ return new CliError({
7218
+ summary: `${stage} failed.`,
7219
+ stage,
7220
+ details: detailLines,
7221
+ suggestions,
7222
+ cause: error
7223
+ });
7224
+ }
7225
+ function createApiError(stage, error, context = [], suggestions = []) {
7226
+ var _a, _b;
7227
+ const status = (_a = error == null ? void 0 : error.response) == null ? void 0 : _a.status;
7228
+ const responseData = (_b = error == null ? void 0 : error.response) == null ? void 0 : _b.data;
7229
+ const errorCode = error == null ? void 0 : error.code;
7230
+ const apiMessage = getApiMessage(responseData);
7231
+ const businessCode = getBusinessCode(responseData);
7232
+ const businessMessage = getBusinessMessage(responseData);
7233
+ const summary = apiMessage || (error == null ? void 0 : error.message) || `${stage} failed.`;
7234
+ const detailLines = [...context];
7235
+ if (status) {
7236
+ detailLines.push(`HTTP status: ${status}`);
7237
+ }
7238
+ if (businessCode) {
7239
+ detailLines.push(`Business code: ${businessCode}`);
7240
+ }
7241
+ if (businessMessage && businessMessage !== apiMessage) {
7242
+ detailLines.push(`Business message: ${businessMessage}`);
7243
+ }
7244
+ if (apiMessage && apiMessage !== summary) {
7245
+ detailLines.push(`Error message: ${apiMessage}`);
7246
+ }
7247
+ if (errorCode && errorCode !== "ERR_BAD_REQUEST" && !responseData) {
7248
+ detailLines.push(`Error code: ${errorCode}`);
7249
+ }
7250
+ if (!responseData && (error == null ? void 0 : error.message) && error.message !== apiMessage) {
7251
+ detailLines.push(`Reason: ${error.message}`);
7252
+ }
7253
+ return new CliError({
7254
+ summary,
7255
+ stage,
7256
+ details: detailLines,
7257
+ suggestions: dedupeSuggestions(suggestions),
7258
+ cause: error
7259
+ });
7260
+ }
7261
+ function normalizeCliError(error, fallbackSummary, suggestions = []) {
7262
+ if (error instanceof CliError) {
7263
+ return error;
7264
+ }
7265
+ if (error instanceof Error) {
7266
+ return new CliError({
7267
+ summary: error.message || fallbackSummary,
7268
+ suggestions: dedupeSuggestions(suggestions),
7269
+ cause: error
7270
+ });
7271
+ }
7272
+ return new CliError({
7273
+ summary: fallbackSummary,
7274
+ details: [`Raw error: ${stringifyValue(error)}`],
7275
+ suggestions: dedupeSuggestions(suggestions),
7276
+ cause: error
7277
+ });
7278
+ }
7279
+ function printCliError(error, fallbackSummary) {
7280
+ const cliError = normalizeCliError(error, fallbackSummary);
7281
+ console.error(import_chalk16.default.red(`
7282
+ Error: ${cliError.message}`));
7283
+ if (cliError.stage) {
7284
+ console.error(import_chalk16.default.gray(`Stage: ${cliError.stage}`));
7285
+ }
7286
+ for (const detail of cliError.details) {
7287
+ console.error(import_chalk16.default.gray(detail));
7288
+ }
7289
+ if (cliError.suggestions.length > 0) {
7290
+ console.error(import_chalk16.default.yellow("\nNext steps:"));
7291
+ for (const suggestion of cliError.suggestions) {
7292
+ console.error(import_chalk16.default.yellow(`- ${suggestion}`));
7293
+ }
7294
+ }
7295
+ }
7296
+
7297
+ // bin/create.ts
6937
7298
  var PROJECT_DIR = process.cwd();
6938
- var API_BASE = "https://pinme.benny1996.win/api/v4";
7299
+ var API_BASE = "https://pinme.dev/api/v4";
7300
+ var TEMPLATE_REPO = "glitternetwork/pinme-worker-template";
7301
+ var TEMPLATE_ZIP_URL = `https://github.com/${TEMPLATE_REPO}/archive/refs/heads/main.zip`;
6939
7302
  async function createCmd(options) {
6940
- var _a, _b, _c, _d, _e, _f;
6941
7303
  try {
6942
7304
  const headers = getAuthHeaders();
6943
7305
  if (!headers["authentication-tokens"] || !headers["token-address"]) {
6944
- console.log(import_chalk16.default.yellow("\n\u26A0\uFE0F You are not logged in."));
6945
- console.log(import_chalk16.default.gray("Please run: pinme login"));
6946
- process.exit(1);
7306
+ throw createConfigError("No valid local login session was found.", [
7307
+ "Run `pinme login` and retry."
7308
+ ]);
6947
7309
  }
6948
- console.log(import_chalk16.default.blue("Creating new project from template...\n"));
7310
+ console.log(import_chalk17.default.blue("Creating new project from template...\n"));
6949
7311
  let projectName = options.name;
6950
7312
  if (!projectName) {
6951
7313
  const answers = await import_inquirer8.default.prompt([
@@ -6966,7 +7328,7 @@ async function createCmd(options) {
6966
7328
  }
6967
7329
  const targetDir = import_path11.default.join(PROJECT_DIR, projectName);
6968
7330
  if (import_fs_extra6.default.existsSync(targetDir) && !options.force) {
6969
- console.log(import_chalk16.default.yellow(`
7331
+ console.log(import_chalk17.default.yellow(`
6970
7332
  Directory "${projectName}" already exists.`));
6971
7333
  const answers = await import_inquirer8.default.prompt([
6972
7334
  {
@@ -6977,16 +7339,16 @@ Directory "${projectName}" already exists.`));
6977
7339
  }
6978
7340
  ]);
6979
7341
  if (!answers.overwrite) {
6980
- console.log(import_chalk16.default.gray("Cancelled."));
7342
+ console.log(import_chalk17.default.gray("Cancelled."));
6981
7343
  process.exit(0);
6982
7344
  }
6983
7345
  import_fs_extra6.default.removeSync(targetDir);
6984
7346
  }
6985
- console.log(import_chalk16.default.blue("\n1. Creating worker and database..."));
7347
+ console.log(import_chalk17.default.blue("\n1. Creating worker and database..."));
6986
7348
  const apiUrl = `${API_BASE}/create_worker`;
6987
- console.log(import_chalk16.default.gray(`API URL: ${apiUrl}`));
7349
+ console.log(import_chalk17.default.gray(`API URL: ${apiUrl}`));
6988
7350
  const normalizedProjectName = projectName.toLowerCase();
6989
- console.log(import_chalk16.default.gray(`Project name: ${normalizedProjectName}`));
7351
+ console.log(import_chalk17.default.gray(`Project name: ${normalizedProjectName}`));
6990
7352
  let workerData;
6991
7353
  try {
6992
7354
  const response = await axios_default.post(apiUrl, {
@@ -6999,40 +7361,72 @@ Directory "${projectName}" already exists.`));
6999
7361
  });
7000
7362
  const data = response.data;
7001
7363
  if (data.code !== 200) {
7002
- const errorMsg = ((_a = data == null ? void 0 : data.data) == null ? void 0 : _a.error) || (data == null ? void 0 : data.msg) || "Failed to create worker";
7003
- throw new Error(errorMsg);
7364
+ throw createApiError("project creation", { response: { status: response.status, data } }, [
7365
+ `Project name: ${normalizedProjectName}`,
7366
+ `Endpoint: ${apiUrl}`
7367
+ ]);
7004
7368
  }
7005
7369
  workerData = data.data;
7006
- console.log(import_chalk16.default.gray(` API Response: ${JSON.stringify(workerData)}`));
7007
- console.log(import_chalk16.default.green(` API Domain: ${workerData.api_domain}`));
7008
- console.log(import_chalk16.default.green(` Project Name: ${workerData.project_name}`));
7009
- console.log(import_chalk16.default.green(` D1 UUID: ${workerData.uuid}`));
7370
+ console.log(import_chalk17.default.gray(` API Response: ${JSON.stringify(workerData)}`));
7371
+ console.log(import_chalk17.default.green(` API Domain: ${workerData.api_domain}`));
7372
+ console.log(import_chalk17.default.green(` Project Name: ${workerData.project_name}`));
7373
+ console.log(import_chalk17.default.green(` D1 UUID: ${workerData.uuid}`));
7010
7374
  } catch (error) {
7011
- const errorMsg = ((_d = (_c = (_b = error.response) == null ? void 0 : _b.data) == null ? void 0 : _c.data) == null ? void 0 : _d.error) || ((_f = (_e = error.response) == null ? void 0 : _e.data) == null ? void 0 : _f.msg) || error.message || "Failed to create worker";
7012
- throw new Error(errorMsg);
7375
+ throw createApiError("project creation", error, [
7376
+ `Project name: ${normalizedProjectName}`,
7377
+ `Endpoint: ${apiUrl}`
7378
+ ]);
7379
+ }
7380
+ console.log(import_chalk17.default.blue("\n2. Downloading template from repository..."));
7381
+ const zipPath = import_path11.default.join(PROJECT_DIR, "template.zip");
7382
+ let downloadSuccess = false;
7383
+ for (let attempt = 1; attempt <= 3 && !downloadSuccess; attempt++) {
7384
+ try {
7385
+ console.log(import_chalk17.default.gray(` Download attempt ${attempt}/3...`));
7386
+ (0, import_child_process2.execSync)(`curl -L --retry 3 --retry-delay 2 -o "${zipPath}" "${TEMPLATE_ZIP_URL}"`, {
7387
+ stdio: "inherit"
7388
+ });
7389
+ if (!import_fs_extra6.default.existsSync(zipPath) || import_fs_extra6.default.statSync(zipPath).size < 100) {
7390
+ throw new Error("Downloaded file is too small or empty");
7391
+ }
7392
+ downloadSuccess = true;
7393
+ } catch (downloadError) {
7394
+ console.log(import_chalk17.default.yellow(` Attempt ${attempt} failed: ${downloadError.message}`));
7395
+ if (import_fs_extra6.default.existsSync(zipPath)) {
7396
+ import_fs_extra6.default.removeSync(zipPath);
7397
+ }
7398
+ if (attempt === 3) {
7399
+ throw new Error(`Failed to download template after 3 attempts: ${downloadError.message}`);
7400
+ }
7401
+ }
7402
+ }
7403
+ try {
7404
+ (0, import_child_process2.execSync)(`unzip -o "${zipPath}" -d "${PROJECT_DIR}"`, {
7405
+ stdio: "inherit"
7406
+ });
7407
+ const subDir = import_path11.default.join(PROJECT_DIR, "pinme-worker-template-main");
7408
+ if (import_fs_extra6.default.existsSync(subDir)) {
7409
+ import_fs_extra6.default.copySync(subDir, targetDir);
7410
+ import_fs_extra6.default.removeSync(subDir);
7411
+ }
7412
+ import_fs_extra6.default.removeSync(zipPath);
7413
+ console.log(import_chalk17.default.green(` Template downloaded to: ${targetDir}`));
7414
+ } catch (error) {
7415
+ throw createCommandError("template extraction", `unzip -o "${zipPath}" -d "${PROJECT_DIR}"`, error, [
7416
+ "Check whether `unzip` is available and the downloaded template archive is valid."
7417
+ ]);
7013
7418
  }
7014
- console.log(import_chalk16.default.blue("\n2. Copying template files..."));
7015
- import_fs_extra6.default.copySync(TEMPLATE_DIR, targetDir);
7016
- console.log(import_chalk16.default.green(` Template copied to: ${targetDir}`));
7017
- console.log(import_chalk16.default.blue("\n3. Updating configuration..."));
7419
+ console.log(import_chalk17.default.blue("\n3. Updating configuration..."));
7018
7420
  const configPath = import_path11.default.join(targetDir, "pinme.toml");
7019
7421
  const config = import_fs_extra6.default.readFileSync(configPath, "utf-8");
7020
7422
  let updatedConfig = config.replace(
7021
7423
  /project_name = ".*"/,
7022
7424
  `project_name = "${workerData.project_name}"`
7023
7425
  );
7024
- updatedConfig = updatedConfig.replace(
7025
- /^VITE_WORKER_URL = ".*"$/m,
7026
- `VITE_WORKER_URL = "${workerData.api_domain}"`
7027
- );
7028
- updatedConfig = updatedConfig.replace(
7029
- /# database_id = ".*"/,
7030
- `database_id = "${workerData.uuid}"`
7031
- );
7032
7426
  import_fs_extra6.default.writeFileSync(configPath, updatedConfig);
7033
- console.log(import_chalk16.default.green(` Updated pinme.toml`));
7034
- console.log(import_chalk16.default.gray(` metadata: ${workerData.metadata}`));
7035
- console.log(import_chalk16.default.gray(` VITE_API_URL: ${workerData.api_domain}`));
7427
+ console.log(import_chalk17.default.green(` Updated pinme.toml`));
7428
+ console.log(import_chalk17.default.gray(` metadata: ${workerData.metadata}`));
7429
+ console.log(import_chalk17.default.gray(` VITE_API_URL: ${workerData.api_domain}`));
7036
7430
  const backendDir = import_path11.default.join(targetDir, "backend");
7037
7431
  if (import_fs_extra6.default.existsSync(backendDir) && workerData.metadata) {
7038
7432
  const metadataContent = typeof workerData.metadata === "string" ? workerData.metadata : JSON.stringify(workerData.metadata, null, 2);
@@ -7040,52 +7434,101 @@ Directory "${projectName}" already exists.`));
7040
7434
  import_path11.default.join(backendDir, "metadata.json"),
7041
7435
  metadataContent
7042
7436
  );
7043
- console.log(import_chalk16.default.green(` Saved metadata.json`));
7437
+ console.log(import_chalk17.default.green(` Saved metadata.json`));
7438
+ }
7439
+ const wranglerPath = import_path11.default.join(backendDir, "wrangler.toml");
7440
+ if (import_fs_extra6.default.existsSync(wranglerPath) && workerData.api_key) {
7441
+ let wranglerContent = import_fs_extra6.default.readFileSync(wranglerPath, "utf-8");
7442
+ wranglerContent = wranglerContent.replace(
7443
+ /^name = ".*"$/m,
7444
+ `name = "${workerData.project_name}"`
7445
+ );
7446
+ import_fs_extra6.default.writeFileSync(wranglerPath, wranglerContent);
7447
+ console.log(import_chalk17.default.green(` Updated backend/wrangler.toml API_KEY`));
7044
7448
  }
7045
7449
  const envExamplePath = import_path11.default.join(targetDir, "frontend", ".env.example");
7046
7450
  const envPath = import_path11.default.join(targetDir, "frontend", ".env");
7047
7451
  if (import_fs_extra6.default.existsSync(envExamplePath)) {
7048
7452
  let envContent = import_fs_extra6.default.readFileSync(envExamplePath, "utf-8");
7049
7453
  envContent = envContent.replace(/your-project/g, workerData.project_name);
7050
- envContent = envContent.replace(
7051
- /^VITE_WORKER_URL=.*$/m,
7052
- `VITE_WORKER_URL=${workerData.api_domain}`
7053
- );
7054
7454
  envContent = envContent.replace(
7055
7455
  /^VITE_API_URL=.*$/m,
7056
7456
  `VITE_API_URL=${workerData.api_domain}`
7057
7457
  );
7058
7458
  import_fs_extra6.default.writeFileSync(envPath, envContent);
7059
- console.log(import_chalk16.default.green(` Created frontend/.env file`));
7459
+ console.log(import_chalk17.default.green(` Created frontend/.env file`));
7460
+ }
7461
+ console.log(import_chalk17.default.blue("\n4. Building frontend..."));
7462
+ try {
7463
+ (0, import_child_process2.execSync)("npm install", {
7464
+ cwd: targetDir,
7465
+ stdio: "inherit"
7466
+ });
7467
+ console.log(import_chalk17.default.green(" Project dependencies installed"));
7468
+ } catch (error) {
7469
+ throw createCommandError("project dependency install", "npm install", error, [
7470
+ "Check network connectivity and npm registry availability.",
7471
+ "Inspect the generated workspace `package.json` files for dependency conflicts."
7472
+ ]);
7473
+ }
7474
+ const frontendDir = import_path11.default.join(targetDir, "frontend");
7475
+ if (import_fs_extra6.default.existsSync(frontendDir)) {
7476
+ try {
7477
+ (0, import_child_process2.execSync)("npm run build:frontend", {
7478
+ cwd: targetDir,
7479
+ stdio: "inherit"
7480
+ });
7481
+ console.log(import_chalk17.default.green(" Frontend built"));
7482
+ } catch (error) {
7483
+ throw createCommandError("frontend build", "npm run build:frontend", error, [
7484
+ "Fix the frontend build error shown above, then rerun `pinme create`."
7485
+ ]);
7486
+ }
7487
+ console.log(import_chalk17.default.blue(" Uploading to IPFS..."));
7488
+ try {
7489
+ (0, import_child_process2.execSync)("pinme upload ./dist", {
7490
+ cwd: frontendDir,
7491
+ stdio: "inherit",
7492
+ env: {
7493
+ ...process.env,
7494
+ PINME_PROJECT_NAME: workerData.project_name
7495
+ }
7496
+ });
7497
+ console.log(import_chalk17.default.green(" Frontend uploaded to IPFS"));
7498
+ } catch (error) {
7499
+ console.log(import_chalk17.default.yellow(" Warning: IPFS upload failed, you can upload manually later"));
7500
+ }
7060
7501
  }
7061
- console.log(import_chalk16.default.green("\n\u2705 Project created successfully!"));
7062
- console.log(import_chalk16.default.gray(`
7502
+ console.log(import_chalk17.default.green("\nProject created successfully."));
7503
+ console.log(import_chalk17.default.gray(`
7063
7504
  Project Details:`));
7064
- console.log(import_chalk16.default.gray(` API Domain: ${workerData.api_domain}`));
7065
- console.log(import_chalk16.default.gray(` Project Name: ${workerData.project_name}`));
7066
- console.log(import_chalk16.default.gray(`
7505
+ console.log(import_chalk17.default.gray(` API Domain: ${workerData.api_domain}`));
7506
+ console.log(import_chalk17.default.gray(` Project Name: ${workerData.project_name}`));
7507
+ console.log(import_chalk17.default.gray(`
7067
7508
  Next steps:`));
7068
- console.log(import_chalk16.default.gray(` cd ${projectName}`));
7069
- console.log(import_chalk16.default.gray(` npm install`));
7509
+ console.log(import_chalk17.default.gray(` cd ${projectName}`));
7510
+ console.log(import_chalk17.default.gray(` pinme save`));
7070
7511
  process.exit(0);
7071
7512
  } catch (error) {
7072
- console.error(import_chalk16.default.red(`
7073
- Error: ${error.message || error}`));
7513
+ printCliError(error, "Project creation failed.");
7074
7514
  process.exit(1);
7075
7515
  }
7076
7516
  }
7077
7517
 
7078
7518
  // bin/save.ts
7079
- var import_chalk17 = __toESM(require("chalk"));
7519
+ var import_chalk18 = __toESM(require("chalk"));
7080
7520
  var import_fs_extra7 = __toESM(require("fs-extra"));
7081
7521
  var import_path12 = __toESM(require("path"));
7082
- var import_child_process2 = require("child_process");
7522
+ var import_child_process3 = require("child_process");
7083
7523
  var PROJECT_DIR2 = process.cwd();
7084
- var API_BASE2 = "https://pinme.benny1996.win/api/v4";
7524
+ var API_BASE2 = "https://pinme.dev/api/v4";
7085
7525
  function loadConfig() {
7086
7526
  const configPath = import_path12.default.join(PROJECT_DIR2, "pinme.toml");
7087
7527
  if (!import_fs_extra7.default.existsSync(configPath)) {
7088
- throw new Error("pinme.toml not found");
7528
+ throw createConfigError("`pinme.toml` not found in the current directory.", [
7529
+ "Run this command from the Pinme project root.",
7530
+ "If the project has not been initialized yet, create or restore `pinme.toml` first."
7531
+ ]);
7089
7532
  }
7090
7533
  const configContent = import_fs_extra7.default.readFileSync(configPath, "utf-8");
7091
7534
  const projectNameMatch = configContent.match(/project_name\s*=\s*"([^"]+)"/);
@@ -7096,67 +7539,52 @@ function loadConfig() {
7096
7539
  function getMetadata() {
7097
7540
  const metadataPath = import_path12.default.join(PROJECT_DIR2, "backend", "metadata.json");
7098
7541
  if (!import_fs_extra7.default.existsSync(metadataPath)) {
7099
- console.log(import_chalk17.default.yellow(" Warning: metadata.json not found, using empty metadata"));
7542
+ console.log(import_chalk18.default.yellow(" Warning: metadata.json not found, using empty metadata"));
7100
7543
  return {};
7101
7544
  }
7102
7545
  return import_fs_extra7.default.readJsonSync(metadataPath);
7103
7546
  }
7104
7547
  function buildWorker() {
7105
- console.log(import_chalk17.default.blue("Building worker..."));
7548
+ console.log(import_chalk18.default.blue("Building worker..."));
7106
7549
  try {
7107
- (0, import_child_process2.execSync)("npm run build:worker", {
7550
+ (0, import_child_process3.execSync)("npm run build:worker", {
7108
7551
  cwd: PROJECT_DIR2,
7109
7552
  stdio: "inherit"
7110
7553
  });
7111
- console.log(import_chalk17.default.green("Worker built"));
7554
+ console.log(import_chalk18.default.green("Worker built"));
7112
7555
  } catch (error) {
7113
- throw new Error(`Worker build failed: ${error.message}`);
7556
+ throw createCommandError("worker build", "npm run build:worker", error, [
7557
+ "Fix the build error shown above, then rerun `pinme save`."
7558
+ ]);
7114
7559
  }
7115
7560
  }
7116
7561
  function installDependencies() {
7117
- console.log(import_chalk17.default.blue("Installing dependencies..."));
7562
+ console.log(import_chalk18.default.blue("Installing dependencies..."));
7118
7563
  try {
7119
- (0, import_child_process2.execSync)("npm install", {
7564
+ (0, import_child_process3.execSync)("npm install", {
7120
7565
  cwd: PROJECT_DIR2,
7121
7566
  stdio: "inherit"
7122
7567
  });
7123
- console.log(import_chalk17.default.green("Root dependencies installed"));
7568
+ console.log(import_chalk18.default.green("Project dependencies installed"));
7124
7569
  } catch (error) {
7125
- throw new Error(`Root dependencies install failed: ${error.message}`);
7126
- }
7127
- const backendDir = import_path12.default.join(PROJECT_DIR2, "backend");
7128
- if (import_fs_extra7.default.existsSync(import_path12.default.join(backendDir, "package.json"))) {
7129
- try {
7130
- (0, import_child_process2.execSync)("npm install", {
7131
- cwd: backendDir,
7132
- stdio: "inherit"
7133
- });
7134
- console.log(import_chalk17.default.green("Backend dependencies installed"));
7135
- } catch (error) {
7136
- throw new Error(`Backend dependencies install failed: ${error.message}`);
7137
- }
7138
- }
7139
- const frontendDir = import_path12.default.join(PROJECT_DIR2, "frontend");
7140
- if (import_fs_extra7.default.existsSync(import_path12.default.join(frontendDir, "package.json"))) {
7141
- try {
7142
- (0, import_child_process2.execSync)("npm install", {
7143
- cwd: frontendDir,
7144
- stdio: "inherit"
7145
- });
7146
- console.log(import_chalk17.default.green("Frontend dependencies installed"));
7147
- } catch (error) {
7148
- throw new Error(`Frontend dependencies install failed: ${error.message}`);
7149
- }
7570
+ throw createCommandError("project dependency install", "npm install", error, [
7571
+ "Check network connectivity and npm registry availability.",
7572
+ "If `package-lock.json` is stale or conflicted, resolve that before retrying."
7573
+ ]);
7150
7574
  }
7151
7575
  }
7152
7576
  function getBuiltWorker() {
7153
7577
  const distWorkerDir = import_path12.default.join(PROJECT_DIR2, "dist-worker");
7154
7578
  if (!import_fs_extra7.default.existsSync(distWorkerDir)) {
7155
- throw new Error('Dist worker not found. Run "npm run build:worker" first.');
7579
+ throw createConfigError("Built worker output not found: `dist-worker/`.", [
7580
+ "Make sure `npm run build:worker` completed successfully."
7581
+ ]);
7156
7582
  }
7157
7583
  const workerJsPath = import_path12.default.join(distWorkerDir, "worker.js");
7158
7584
  if (!import_fs_extra7.default.existsSync(workerJsPath)) {
7159
- throw new Error("worker.js not found in dist-worker");
7585
+ throw createConfigError("Built worker entry file not found: `dist-worker/worker.js`.", [
7586
+ "Check the worker build output and bundler config."
7587
+ ]);
7160
7588
  }
7161
7589
  const modulePaths = [];
7162
7590
  const files = import_fs_extra7.default.readdirSync(distWorkerDir);
@@ -7168,7 +7596,7 @@ function getBuiltWorker() {
7168
7596
  return { workerJsPath, modulePaths };
7169
7597
  }
7170
7598
  function getSqlFiles() {
7171
- const sqlDir = import_path12.default.join(PROJECT_DIR2, "backend", "schema");
7599
+ const sqlDir = import_path12.default.join(PROJECT_DIR2, "db");
7172
7600
  if (!import_fs_extra7.default.existsSync(sqlDir)) {
7173
7601
  return [];
7174
7602
  }
@@ -7176,16 +7604,16 @@ function getSqlFiles() {
7176
7604
  return files.map((f) => import_path12.default.join(sqlDir, f));
7177
7605
  }
7178
7606
  async function saveWorker(workerJsPath, modulePaths, sqlFiles, metadata, projectName) {
7179
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
7180
- console.log(import_chalk17.default.blue("Saving worker to platform..."));
7181
- console.log(import_chalk17.default.gray(`Project: ${projectName}`));
7182
- console.log(import_chalk17.default.gray(`workerJsPath: ${workerJsPath}`));
7183
- console.log(import_chalk17.default.gray(`modulePaths: ${modulePaths}`));
7184
- console.log(import_chalk17.default.gray(`sqlFiles: ${sqlFiles}`));
7185
- console.log(import_chalk17.default.gray(`metadata: ${metadata}`));
7607
+ var _a, _b;
7608
+ console.log(import_chalk18.default.blue("Saving worker to platform..."));
7609
+ console.log(import_chalk18.default.gray(`Project: ${projectName}`));
7610
+ console.log(import_chalk18.default.gray(`workerJsPath: ${workerJsPath}`));
7611
+ console.log(import_chalk18.default.gray(`modulePaths: ${modulePaths}`));
7612
+ console.log(import_chalk18.default.gray(`sqlFiles: ${sqlFiles}`));
7613
+ console.log(import_chalk18.default.gray(`metadata: ${metadata}`));
7186
7614
  const apiUrl = `${API_BASE2}/save_worker?project_name=${encodeURIComponent(projectName)}`;
7187
7615
  const headers = getAuthHeaders();
7188
- console.log(import_chalk17.default.gray(`API URL: ${apiUrl}`));
7616
+ console.log(import_chalk18.default.gray(`API URL: ${apiUrl}`));
7189
7617
  try {
7190
7618
  const FormData4 = (await import("formdata-node")).FormData;
7191
7619
  const Blob2 = (await import("formdata-node")).Blob;
@@ -7210,61 +7638,76 @@ async function saveWorker(workerJsPath, modulePaths, sqlFiles, metadata, project
7210
7638
  formData.append("sql_file", new Blob2([content], {
7211
7639
  type: "application/sql"
7212
7640
  }), filename);
7213
- console.log(import_chalk17.default.gray(` Including SQL: ${filename}`));
7641
+ console.log(import_chalk18.default.gray(` Including SQL: ${filename}`));
7214
7642
  }
7215
7643
  const response = await axios_default.put(apiUrl, formData, {
7216
7644
  headers: { ...headers },
7217
7645
  timeout: 12e4
7218
7646
  });
7219
- console.log(import_chalk17.default.gray(` Response: ${JSON.stringify(response.data)}`));
7647
+ console.log(import_chalk18.default.gray(` Response: ${JSON.stringify(response.data)}`));
7220
7648
  if (response.data) {
7221
- console.log(import_chalk17.default.green("Worker saved"));
7649
+ console.log(import_chalk18.default.green("Worker saved"));
7222
7650
  if ((_b = (_a = response.data) == null ? void 0 : _a.data) == null ? void 0 : _b.sql_results) {
7223
7651
  for (const result of response.data.data.sql_results) {
7224
- console.log(import_chalk17.default.gray(` SQL ${result.filename}: ${result.status}`));
7652
+ console.log(import_chalk18.default.gray(` SQL ${result.filename}: ${result.status}`));
7225
7653
  }
7226
7654
  }
7227
7655
  } else {
7228
- throw new Error(((_e = (_d = (_c = response.data) == null ? void 0 : _c.errors) == null ? void 0 : _d[0]) == null ? void 0 : _e.message) || "Failed to save worker");
7656
+ throw createApiError("worker save", { response: { data: response.data } }, [
7657
+ `Project: ${projectName}`,
7658
+ `Endpoint: ${apiUrl}`
7659
+ ], [
7660
+ "Verify the project exists and your account has permission to update it."
7661
+ ]);
7229
7662
  }
7230
7663
  } catch (error) {
7231
- console.log(import_chalk17.default.red(` Response status: ${(_f = error.response) == null ? void 0 : _f.status}`));
7232
- console.log(import_chalk17.default.red(` Response data: ${JSON.stringify((_g = error.response) == null ? void 0 : _g.data)}`));
7233
- const errorMsg = ((_k = (_j = (_i = (_h = error.response) == null ? void 0 : _h.data) == null ? void 0 : _i.errors) == null ? void 0 : _j[0]) == null ? void 0 : _k.message) || ((_m = (_l = error.response) == null ? void 0 : _l.data) == null ? void 0 : _m.error) || error.message || "Failed to save worker";
7234
- throw new Error(errorMsg);
7664
+ throw createApiError("worker save", error, [
7665
+ `Project: ${projectName}`,
7666
+ `Endpoint: ${apiUrl}`
7667
+ ], [
7668
+ "Check whether backend metadata, SQL files, or worker bundle contains invalid content."
7669
+ ]);
7235
7670
  }
7236
7671
  }
7237
7672
  function buildFrontend() {
7238
- console.log(import_chalk17.default.blue("Building frontend..."));
7673
+ console.log(import_chalk18.default.blue("Building frontend..."));
7239
7674
  try {
7240
- (0, import_child_process2.execSync)("npm run build:frontend", {
7675
+ (0, import_child_process3.execSync)("npm run build:frontend", {
7241
7676
  cwd: PROJECT_DIR2,
7242
7677
  stdio: "inherit"
7243
7678
  });
7244
- console.log(import_chalk17.default.green("Frontend built"));
7679
+ console.log(import_chalk18.default.green("Frontend built"));
7245
7680
  } catch (error) {
7246
- throw new Error(`Frontend build failed: ${error.message}`);
7681
+ throw createCommandError("frontend build", "npm run build:frontend", error, [
7682
+ "Fix the frontend build error shown above, then rerun `pinme save`."
7683
+ ]);
7247
7684
  }
7248
7685
  }
7249
- function deployFrontend() {
7250
- console.log(import_chalk17.default.blue("Deploying frontend to IPFS..."));
7686
+ function deployFrontend(projectName) {
7687
+ console.log(import_chalk18.default.blue("Deploying frontend to IPFS..."));
7251
7688
  try {
7252
- (0, import_child_process2.execSync)("pinme upload ./frontend/dist", {
7689
+ (0, import_child_process3.execSync)("pinme upload ./frontend/dist", {
7253
7690
  cwd: PROJECT_DIR2,
7254
- stdio: "inherit"
7691
+ stdio: "inherit",
7692
+ env: {
7693
+ ...process.env,
7694
+ PINME_PROJECT_NAME: projectName
7695
+ }
7255
7696
  });
7256
- console.log(import_chalk17.default.green("Frontend deployed to IPFS"));
7697
+ console.log(import_chalk18.default.green("Frontend deployed to IPFS"));
7257
7698
  } catch (error) {
7258
- throw new Error(`Frontend deploy failed: ${error.message}`);
7699
+ throw createCommandError("frontend deploy", "pinme upload ./frontend/dist", error, [
7700
+ "Make sure `frontend/dist` exists and `pinme upload` works in this environment."
7701
+ ]);
7259
7702
  }
7260
7703
  }
7261
7704
  async function saveCmd(options) {
7262
7705
  try {
7263
7706
  const headers = getAuthHeaders();
7264
7707
  if (!headers["authentication-tokens"] || !headers["token-address"]) {
7265
- console.log(import_chalk17.default.yellow("\n\u26A0\uFE0F You are not logged in."));
7266
- console.log(import_chalk17.default.gray("Please run: pinme login"));
7267
- process.exit(1);
7708
+ throw createConfigError("No valid local login session was found.", [
7709
+ "Run `pinme login` and retry."
7710
+ ]);
7268
7711
  }
7269
7712
  const projectDir = options.projectName || options.name ? import_path12.default.join(PROJECT_DIR2, options.projectName || options.name) : PROJECT_DIR2;
7270
7713
  const tokenFileSrc = import_path12.default.join(PROJECT_DIR2, ".token.json");
@@ -7272,48 +7715,51 @@ async function saveCmd(options) {
7272
7715
  if (import_fs_extra7.default.existsSync(tokenFileSrc) && !import_fs_extra7.default.existsSync(tokenFileDst)) {
7273
7716
  import_fs_extra7.default.copySync(tokenFileSrc, tokenFileDst);
7274
7717
  }
7275
- console.log(import_chalk17.default.blue("\u{1F680} Deploying to platform...\n"));
7276
- console.log(import_chalk17.default.gray(`Project dir: ${PROJECT_DIR2}`));
7718
+ console.log(import_chalk18.default.blue("Deploying to platform...\n"));
7719
+ console.log(import_chalk18.default.gray(`Project dir: ${PROJECT_DIR2}`));
7277
7720
  const config = loadConfig();
7278
7721
  const projectName = config.project_name;
7279
7722
  if (!projectName) {
7280
- throw new Error("project_name not found in pinme.toml");
7723
+ throw createConfigError("`project_name` is missing in `pinme.toml`.", [
7724
+ 'Set `project_name = "your-project-name"` in `pinme.toml`.'
7725
+ ]);
7281
7726
  }
7282
- console.log(import_chalk17.default.gray(`Project: ${projectName}`));
7727
+ console.log(import_chalk18.default.gray(`Project: ${projectName}`));
7283
7728
  const apiUrl = `${API_BASE2}/save_worker?project_name=${encodeURIComponent(projectName)}`;
7284
- console.log(import_chalk17.default.gray(`API URL: ${apiUrl}`));
7285
- console.log(import_chalk17.default.blue("\n--- Backend ---"));
7729
+ console.log(import_chalk18.default.gray(`API URL: ${apiUrl}`));
7730
+ console.log(import_chalk18.default.blue("\n--- Backend ---"));
7286
7731
  installDependencies();
7287
7732
  buildWorker();
7288
7733
  const metadata = getMetadata();
7289
7734
  const { workerJsPath, modulePaths } = getBuiltWorker();
7290
- console.log(import_chalk17.default.gray(`Worker JS: ${workerJsPath}`));
7291
- console.log(import_chalk17.default.gray(`Module paths: ${JSON.stringify(modulePaths)}`));
7735
+ console.log(import_chalk18.default.gray(`Worker JS: ${workerJsPath}`));
7736
+ console.log(import_chalk18.default.gray(`Module paths: ${JSON.stringify(modulePaths)}`));
7292
7737
  const sqlFiles = getSqlFiles();
7293
- console.log(import_chalk17.default.gray(`SQL files: ${JSON.stringify(sqlFiles)}`));
7738
+ console.log(import_chalk18.default.gray(`SQL files: ${JSON.stringify(sqlFiles)}`));
7294
7739
  await saveWorker(workerJsPath, modulePaths, sqlFiles, metadata, projectName);
7295
- console.log(import_chalk17.default.blue("\n--- Frontend ---"));
7740
+ console.log(import_chalk18.default.blue("\n--- Frontend ---"));
7296
7741
  buildFrontend();
7297
- deployFrontend();
7298
- console.log(import_chalk17.default.green("\n\u2705 Deployment complete!"));
7742
+ deployFrontend(projectName);
7743
+ console.log(import_chalk18.default.green("\nDeployment complete."));
7299
7744
  process.exit(0);
7300
7745
  } catch (error) {
7301
- console.error(import_chalk17.default.red(`
7302
- \u274C Error: ${error.message}`));
7746
+ printCliError(error, "Save failed.");
7303
7747
  process.exit(1);
7304
7748
  }
7305
7749
  }
7306
7750
 
7307
7751
  // bin/updateDb.ts
7308
- var import_chalk18 = __toESM(require("chalk"));
7752
+ var import_chalk19 = __toESM(require("chalk"));
7309
7753
  var import_fs_extra8 = __toESM(require("fs-extra"));
7310
7754
  var import_path13 = __toESM(require("path"));
7311
7755
  var PROJECT_DIR3 = process.cwd();
7312
- var API_BASE3 = "https://pinme.benny1996.win/api/v4";
7756
+ var API_BASE3 = "https://pinme.dev/api/v4";
7313
7757
  function loadConfig2() {
7314
7758
  const configPath = import_path13.default.join(PROJECT_DIR3, "pinme.toml");
7315
7759
  if (!import_fs_extra8.default.existsSync(configPath)) {
7316
- throw new Error("pinme.toml not found");
7760
+ throw createConfigError("`pinme.toml` not found in the current directory.", [
7761
+ "Run this command from the Pinme project root."
7762
+ ]);
7317
7763
  }
7318
7764
  const configContent = import_fs_extra8.default.readFileSync(configPath, "utf-8");
7319
7765
  const projectNameMatch = configContent.match(/project_name\s*=\s*"([^"]+)"/);
@@ -7322,24 +7768,27 @@ function loadConfig2() {
7322
7768
  };
7323
7769
  }
7324
7770
  function getSqlFiles2() {
7325
- const sqlDir = import_path13.default.join(PROJECT_DIR3, "backend", "schema");
7771
+ const sqlDir = import_path13.default.join(PROJECT_DIR3, "db");
7326
7772
  if (!import_fs_extra8.default.existsSync(sqlDir)) {
7327
- throw new Error("SQL directory not found: backend/schema");
7773
+ throw createConfigError("SQL directory not found: `db/`.", [
7774
+ "Create a `db/` directory and add at least one `.sql` migration file."
7775
+ ]);
7328
7776
  }
7329
7777
  const files = import_fs_extra8.default.readdirSync(sqlDir).filter((f) => f.endsWith(".sql")).sort();
7330
7778
  if (files.length === 0) {
7331
- throw new Error("No SQL files found in backend/schema");
7779
+ throw createConfigError("No `.sql` files were found in `db/`.", [
7780
+ "Add one or more migration files before running `pinme update-db`."
7781
+ ]);
7332
7782
  }
7333
7783
  return files.map((f) => import_path13.default.join(sqlDir, f));
7334
7784
  }
7335
7785
  async function updateDb(sqlFiles, projectName) {
7336
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
7337
- console.log(import_chalk18.default.blue("Importing SQL files to database..."));
7338
- console.log(import_chalk18.default.gray(`Project: ${projectName}`));
7339
- console.log(import_chalk18.default.gray(`SQL files: ${sqlFiles.length}`));
7786
+ console.log(import_chalk19.default.blue("Importing SQL files to database..."));
7787
+ console.log(import_chalk19.default.gray(`Project: ${projectName}`));
7788
+ console.log(import_chalk19.default.gray(`SQL files: ${sqlFiles.length}`));
7340
7789
  const apiUrl = `${API_BASE3}/update_db?project_name=${encodeURIComponent(projectName)}`;
7341
7790
  const headers = getAuthHeaders();
7342
- console.log(import_chalk18.default.gray(`API URL: ${apiUrl}`));
7791
+ console.log(import_chalk19.default.gray(`API URL: ${apiUrl}`));
7343
7792
  try {
7344
7793
  const FormData4 = (await import("formdata-node")).FormData;
7345
7794
  const Blob2 = (await import("formdata-node")).Blob;
@@ -7350,49 +7799,57 @@ async function updateDb(sqlFiles, projectName) {
7350
7799
  const content = import_fs_extra8.default.readFileSync(sqlFile);
7351
7800
  totalSize += content.length;
7352
7801
  if (totalSize > 10 * 1024 * 1024) {
7353
- throw new Error("Total SQL files size exceeds 10MB limit");
7802
+ throw createConfigError("Total SQL payload exceeds the 10MB platform limit.", [
7803
+ "Split migrations into smaller batches, then rerun `pinme update-db`."
7804
+ ]);
7354
7805
  }
7355
7806
  formData.append("file", new Blob2([content], {
7356
7807
  type: "application/sql"
7357
7808
  }), filename);
7358
- console.log(import_chalk18.default.gray(` Including: ${filename} (${content.length} bytes)`));
7809
+ console.log(import_chalk19.default.gray(` Including: ${filename} (${content.length} bytes)`));
7359
7810
  }
7360
7811
  const response = await axios_default.post(apiUrl, formData, {
7361
7812
  headers: { ...headers },
7362
7813
  timeout: 12e4
7363
7814
  });
7364
- console.log(import_chalk18.default.gray(` Response: ${JSON.stringify(response.data)}`));
7815
+ console.log(import_chalk19.default.gray(` Response: ${JSON.stringify(response.data)}`));
7365
7816
  if (response.data.code === 200) {
7366
- console.log(import_chalk18.default.green("SQL files imported successfully!"));
7817
+ console.log(import_chalk19.default.green("SQL files imported successfully!"));
7367
7818
  const results = response.data.data.results;
7368
7819
  for (const result of results) {
7369
7820
  if (result.status === "complete") {
7370
- console.log(import_chalk18.default.green(` \u2713 ${result.filename}: ${result.num_queries} queries, ${result.duration}ms`));
7821
+ console.log(import_chalk19.default.green(` COMPLETE ${result.filename}: ${result.num_queries} queries, ${result.duration}ms`));
7371
7822
  if (result.changes !== void 0) {
7372
- console.log(import_chalk18.default.gray(` Changes: ${result.changes}, Read: ${result.rows_read}, Written: ${result.rows_written}`));
7823
+ console.log(import_chalk19.default.gray(` Changes: ${result.changes}, Read: ${result.rows_read}, Written: ${result.rows_written}`));
7373
7824
  }
7374
7825
  } else if (result.status === "error") {
7375
- console.log(import_chalk18.default.red(` \u2717 ${result.filename}: ${result.error}`));
7826
+ console.log(import_chalk19.default.red(` ERROR ${result.filename}: ${result.error}`));
7376
7827
  }
7377
7828
  }
7378
7829
  } else {
7379
- const errorMsg = response.data.msg || response.data.error || "Failed to import SQL files";
7380
- throw new Error(errorMsg);
7830
+ throw createApiError("database update", { response: { data: response.data } }, [
7831
+ `Project: ${projectName}`,
7832
+ `Endpoint: ${apiUrl}`
7833
+ ], [
7834
+ "Inspect the SQL result list above to identify the failing migration file."
7835
+ ]);
7381
7836
  }
7382
7837
  } catch (error) {
7383
- console.log(import_chalk18.default.red(` Response status: ${(_a = error.response) == null ? void 0 : _a.status}`));
7384
- console.log(import_chalk18.default.red(` Response data: ${JSON.stringify((_b = error.response) == null ? void 0 : _b.data)}`));
7385
- const errorMsg = ((_f = (_e = (_d = (_c = error.response) == null ? void 0 : _c.data) == null ? void 0 : _d.errors) == null ? void 0 : _e[0]) == null ? void 0 : _f.message) || ((_h = (_g = error.response) == null ? void 0 : _g.data) == null ? void 0 : _h.error) || ((_j = (_i = error.response) == null ? void 0 : _i.data) == null ? void 0 : _j.msg) || error.message || "Failed to import SQL files";
7386
- throw new Error(errorMsg);
7838
+ throw createApiError("database update", error, [
7839
+ `Project: ${projectName}`,
7840
+ `Endpoint: ${apiUrl}`
7841
+ ], [
7842
+ "Validate the SQL syntax and check whether any migration is re-applying an existing schema change."
7843
+ ]);
7387
7844
  }
7388
7845
  }
7389
7846
  async function updateDbCmd(options) {
7390
7847
  try {
7391
7848
  const headers = getAuthHeaders();
7392
7849
  if (!headers["authentication-tokens"] || !headers["token-address"]) {
7393
- console.log(import_chalk18.default.yellow("\n\u26A0\uFE0F You are not logged in."));
7394
- console.log(import_chalk18.default.gray("Please run: pinme login"));
7395
- process.exit(1);
7850
+ throw createConfigError("No valid local login session was found.", [
7851
+ "Run `pinme login` and retry."
7852
+ ]);
7396
7853
  }
7397
7854
  const projectDir = (options == null ? void 0 : options.projectName) || (options == null ? void 0 : options.name) ? import_path13.default.join(PROJECT_DIR3, options.projectName || options.name) : PROJECT_DIR3;
7398
7855
  const tokenFileSrc = import_path13.default.join(PROJECT_DIR3, ".token.json");
@@ -7400,37 +7857,40 @@ async function updateDbCmd(options) {
7400
7857
  if (import_fs_extra8.default.existsSync(tokenFileSrc) && !import_fs_extra8.default.existsSync(tokenFileDst)) {
7401
7858
  import_fs_extra8.default.copySync(tokenFileSrc, tokenFileDst);
7402
7859
  }
7403
- console.log(import_chalk18.default.blue("\u{1F680} Importing SQL to database...\n"));
7404
- console.log(import_chalk18.default.gray(`Project dir: ${PROJECT_DIR3}`));
7860
+ console.log(import_chalk19.default.blue("Importing SQL to database...\n"));
7861
+ console.log(import_chalk19.default.gray(`Project dir: ${PROJECT_DIR3}`));
7405
7862
  const config = loadConfig2();
7406
7863
  const projectName = config.project_name;
7407
7864
  if (!projectName) {
7408
- throw new Error("project_name not found in pinme.toml");
7865
+ throw createConfigError("`project_name` is missing in `pinme.toml`.", [
7866
+ 'Set `project_name = "your-project-name"` in `pinme.toml`.'
7867
+ ]);
7409
7868
  }
7410
- console.log(import_chalk18.default.gray(`Project: ${projectName}`));
7869
+ console.log(import_chalk19.default.gray(`Project: ${projectName}`));
7411
7870
  const sqlFiles = getSqlFiles2();
7412
- console.log(import_chalk18.default.gray(`Found ${sqlFiles.length} SQL file(s) in backend/schema`));
7871
+ console.log(import_chalk19.default.gray(`Found ${sqlFiles.length} SQL file(s) in db`));
7413
7872
  await updateDb(sqlFiles, projectName);
7414
- console.log(import_chalk18.default.green("\n\u2705 Database update complete!"));
7873
+ console.log(import_chalk19.default.green("\nDatabase update complete."));
7415
7874
  process.exit(0);
7416
7875
  } catch (error) {
7417
- console.error(import_chalk18.default.red(`
7418
- \u274C Error: ${error.message}`));
7876
+ printCliError(error, "Database update failed.");
7419
7877
  process.exit(1);
7420
7878
  }
7421
7879
  }
7422
7880
 
7423
7881
  // bin/updateWorker.ts
7424
- var import_chalk19 = __toESM(require("chalk"));
7882
+ var import_chalk20 = __toESM(require("chalk"));
7425
7883
  var import_fs_extra9 = __toESM(require("fs-extra"));
7426
7884
  var import_path14 = __toESM(require("path"));
7427
- var import_child_process3 = require("child_process");
7885
+ var import_child_process4 = require("child_process");
7428
7886
  var PROJECT_DIR4 = process.cwd();
7429
- var API_BASE4 = "https://pinme.benny1996.win/api/v4";
7887
+ var API_BASE4 = "https://pinme.dev/api/v4";
7430
7888
  function loadConfig3() {
7431
7889
  const configPath = import_path14.default.join(PROJECT_DIR4, "pinme.toml");
7432
7890
  if (!import_fs_extra9.default.existsSync(configPath)) {
7433
- throw new Error("pinme.toml not found");
7891
+ throw createConfigError("`pinme.toml` not found in the current directory.", [
7892
+ "Run this command from the Pinme project root."
7893
+ ]);
7434
7894
  }
7435
7895
  const configContent = import_fs_extra9.default.readFileSync(configPath, "utf-8");
7436
7896
  const projectNameMatch = configContent.match(/project_name\s*=\s*"([^"]+)"/);
@@ -7441,30 +7901,38 @@ function loadConfig3() {
7441
7901
  function getMetadata2() {
7442
7902
  const metadataPath = import_path14.default.join(PROJECT_DIR4, "backend", "metadata.json");
7443
7903
  if (!import_fs_extra9.default.existsSync(metadataPath)) {
7444
- throw new Error("metadata.json not found in backend directory");
7904
+ throw createConfigError("`backend/metadata.json` not found.", [
7905
+ "Create `backend/metadata.json` or restore it from the project template."
7906
+ ]);
7445
7907
  }
7446
7908
  return import_fs_extra9.default.readJsonSync(metadataPath);
7447
7909
  }
7448
7910
  function buildWorker2() {
7449
- console.log(import_chalk19.default.blue("Building worker..."));
7911
+ console.log(import_chalk20.default.blue("Building worker..."));
7450
7912
  try {
7451
- (0, import_child_process3.execSync)("npm run build:worker", {
7913
+ (0, import_child_process4.execSync)("npm run build:worker", {
7452
7914
  cwd: PROJECT_DIR4,
7453
7915
  stdio: "inherit"
7454
7916
  });
7455
- console.log(import_chalk19.default.green("Worker built"));
7917
+ console.log(import_chalk20.default.green("Worker built"));
7456
7918
  } catch (error) {
7457
- throw new Error(`Worker build failed: ${error.message}`);
7919
+ throw createCommandError("worker build", "npm run build:worker", error, [
7920
+ "Fix the build error shown above, then rerun `pinme update-worker`."
7921
+ ]);
7458
7922
  }
7459
7923
  }
7460
7924
  function getBuiltWorker2() {
7461
7925
  const distWorkerDir = import_path14.default.join(PROJECT_DIR4, "dist-worker");
7462
7926
  if (!import_fs_extra9.default.existsSync(distWorkerDir)) {
7463
- throw new Error('Dist worker not found. Run "npm run build:worker" first.');
7927
+ throw createConfigError("Built worker output not found: `dist-worker/`.", [
7928
+ "Make sure `npm run build:worker` completed successfully."
7929
+ ]);
7464
7930
  }
7465
7931
  const workerJsPath = import_path14.default.join(distWorkerDir, "worker.js");
7466
7932
  if (!import_fs_extra9.default.existsSync(workerJsPath)) {
7467
- throw new Error("worker.js not found in dist-worker");
7933
+ throw createConfigError("Built worker entry file not found: `dist-worker/worker.js`.", [
7934
+ "Check the worker build output and bundler config."
7935
+ ]);
7468
7936
  }
7469
7937
  const modulePaths = [];
7470
7938
  const files = import_fs_extra9.default.readdirSync(distWorkerDir);
@@ -7476,15 +7944,14 @@ function getBuiltWorker2() {
7476
7944
  return { workerJsPath, modulePaths };
7477
7945
  }
7478
7946
  async function updateWorker(workerJsPath, modulePaths, metadata, projectName) {
7479
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
7480
- console.log(import_chalk19.default.blue("Updating worker on platform..."));
7481
- console.log(import_chalk19.default.gray(`Project: ${projectName}`));
7482
- console.log(import_chalk19.default.gray(`workerJsPath: ${workerJsPath}`));
7483
- console.log(import_chalk19.default.gray(`modulePaths: ${modulePaths}`));
7484
- console.log(import_chalk19.default.gray(`metadata: ${metadata}`));
7947
+ console.log(import_chalk20.default.blue("Updating worker on platform..."));
7948
+ console.log(import_chalk20.default.gray(`Project: ${projectName}`));
7949
+ console.log(import_chalk20.default.gray(`workerJsPath: ${workerJsPath}`));
7950
+ console.log(import_chalk20.default.gray(`modulePaths: ${modulePaths}`));
7951
+ console.log(import_chalk20.default.gray(`metadata: ${metadata}`));
7485
7952
  const apiUrl = `${API_BASE4}/update_worker?project_name=${encodeURIComponent(projectName)}`;
7486
7953
  const headers = getAuthHeaders();
7487
- console.log(import_chalk19.default.gray(`API URL: ${apiUrl}`));
7954
+ console.log(import_chalk20.default.gray(`API URL: ${apiUrl}`));
7488
7955
  try {
7489
7956
  const FormData4 = (await import("formdata-node")).FormData;
7490
7957
  const Blob2 = (await import("formdata-node")).Blob;
@@ -7507,51 +7974,58 @@ async function updateWorker(workerJsPath, modulePaths, metadata, projectName) {
7507
7974
  headers: { ...headers },
7508
7975
  timeout: 12e4
7509
7976
  });
7510
- console.log(import_chalk19.default.gray(` Response: ${JSON.stringify(response.data)}`));
7977
+ console.log(import_chalk20.default.gray(` Response: ${JSON.stringify(response.data)}`));
7511
7978
  if (response.data) {
7512
- console.log(import_chalk19.default.green("Worker updated"));
7979
+ console.log(import_chalk20.default.green("Worker updated"));
7513
7980
  const data = response.data.data;
7514
7981
  if (data.worker_id) {
7515
- console.log(import_chalk19.default.gray(` Worker ID: ${data.worker_id}`));
7982
+ console.log(import_chalk20.default.gray(` Worker ID: ${data.worker_id}`));
7516
7983
  }
7517
7984
  if (data.deployment_id) {
7518
- console.log(import_chalk19.default.gray(` Deployment ID: ${data.deployment_id}`));
7985
+ console.log(import_chalk20.default.gray(` Deployment ID: ${data.deployment_id}`));
7519
7986
  }
7520
7987
  if (data.entry_point) {
7521
- console.log(import_chalk19.default.gray(` Entry Point: ${data.entry_point}`));
7988
+ console.log(import_chalk20.default.gray(` Entry Point: ${data.entry_point}`));
7522
7989
  }
7523
7990
  if (data.created_on) {
7524
- console.log(import_chalk19.default.gray(` Created: ${data.created_on}`));
7991
+ console.log(import_chalk20.default.gray(` Created: ${data.created_on}`));
7525
7992
  }
7526
7993
  if (data.modified_on) {
7527
- console.log(import_chalk19.default.gray(` Modified: ${data.modified_on}`));
7994
+ console.log(import_chalk20.default.gray(` Modified: ${data.modified_on}`));
7528
7995
  }
7529
7996
  if (data.startup_time_ms !== void 0) {
7530
- console.log(import_chalk19.default.gray(` Startup Time: ${data.startup_time_ms}ms`));
7997
+ console.log(import_chalk20.default.gray(` Startup Time: ${data.startup_time_ms}ms`));
7531
7998
  }
7532
7999
  if (data.has_modules !== void 0) {
7533
- console.log(import_chalk19.default.gray(` Has Modules: ${data.has_modules}`));
8000
+ console.log(import_chalk20.default.gray(` Has Modules: ${data.has_modules}`));
7534
8001
  }
7535
8002
  if (data.domain) {
7536
- console.log(import_chalk19.default.gray(` Domain: ${data.domain}`));
8003
+ console.log(import_chalk20.default.gray(` Domain: ${data.domain}`));
7537
8004
  }
7538
8005
  } else {
7539
- throw new Error(((_c = (_b = (_a = response.data) == null ? void 0 : _a.errors) == null ? void 0 : _b[0]) == null ? void 0 : _c.message) || "Failed to update worker");
8006
+ throw createApiError("worker update", { response: { data: response.data } }, [
8007
+ `Project: ${projectName}`,
8008
+ `Endpoint: ${apiUrl}`
8009
+ ], [
8010
+ "Verify the project exists and your account has permission to update it."
8011
+ ]);
7540
8012
  }
7541
8013
  } catch (error) {
7542
- console.log(import_chalk19.default.red(` Response status: ${(_d = error.response) == null ? void 0 : _d.status}`));
7543
- console.log(import_chalk19.default.red(` Response data: ${JSON.stringify((_e = error.response) == null ? void 0 : _e.data)}`));
7544
- const errorMsg = ((_i = (_h = (_g = (_f = error.response) == null ? void 0 : _f.data) == null ? void 0 : _g.errors) == null ? void 0 : _h[0]) == null ? void 0 : _i.message) || ((_k = (_j = error.response) == null ? void 0 : _j.data) == null ? void 0 : _k.error) || error.message || "Failed to update worker";
7545
- throw new Error(errorMsg);
8014
+ throw createApiError("worker update", error, [
8015
+ `Project: ${projectName}`,
8016
+ `Endpoint: ${apiUrl}`
8017
+ ], [
8018
+ "Check whether `backend/metadata.json` and the built worker bundle are valid."
8019
+ ]);
7546
8020
  }
7547
8021
  }
7548
8022
  async function updateWorkerCmd(options) {
7549
8023
  try {
7550
8024
  const headers = getAuthHeaders();
7551
8025
  if (!headers["authentication-tokens"] || !headers["token-address"]) {
7552
- console.log(import_chalk19.default.yellow("\n\u26A0\uFE0F You are not logged in."));
7553
- console.log(import_chalk19.default.gray("Please run: pinme login"));
7554
- process.exit(1);
8026
+ throw createConfigError("No valid local login session was found.", [
8027
+ "Run `pinme login` and retry."
8028
+ ]);
7555
8029
  }
7556
8030
  const projectDir = (options == null ? void 0 : options.projectName) || (options == null ? void 0 : options.name) ? import_path14.default.join(PROJECT_DIR4, options.projectName || options.name) : PROJECT_DIR4;
7557
8031
  const tokenFileSrc = import_path14.default.join(PROJECT_DIR4, ".token.json");
@@ -7559,41 +8033,44 @@ async function updateWorkerCmd(options) {
7559
8033
  if (import_fs_extra9.default.existsSync(tokenFileSrc) && !import_fs_extra9.default.existsSync(tokenFileDst)) {
7560
8034
  import_fs_extra9.default.copySync(tokenFileSrc, tokenFileDst);
7561
8035
  }
7562
- console.log(import_chalk19.default.blue("\u{1F680} Updating worker...\n"));
7563
- console.log(import_chalk19.default.gray(`Project dir: ${PROJECT_DIR4}`));
8036
+ console.log(import_chalk20.default.blue("Updating worker...\n"));
8037
+ console.log(import_chalk20.default.gray(`Project dir: ${PROJECT_DIR4}`));
7564
8038
  const config = loadConfig3();
7565
8039
  const projectName = config.project_name;
7566
8040
  if (!projectName) {
7567
- throw new Error("project_name not found in pinme.toml");
8041
+ throw createConfigError("`project_name` is missing in `pinme.toml`.", [
8042
+ 'Set `project_name = "your-project-name"` in `pinme.toml`.'
8043
+ ]);
7568
8044
  }
7569
- console.log(import_chalk19.default.gray(`Project: ${projectName}`));
7570
- console.log(import_chalk19.default.blue("\n--- Worker Update ---"));
8045
+ console.log(import_chalk20.default.gray(`Project: ${projectName}`));
8046
+ console.log(import_chalk20.default.blue("\n--- Worker Update ---"));
7571
8047
  buildWorker2();
7572
8048
  const metadata = getMetadata2();
7573
8049
  const { workerJsPath, modulePaths } = getBuiltWorker2();
7574
- console.log(import_chalk19.default.gray(`Worker JS: ${workerJsPath}`));
7575
- console.log(import_chalk19.default.gray(`Module paths: ${JSON.stringify(modulePaths)}`));
7576
- console.log(import_chalk19.default.gray(`SQL files: ignored (not processed for update_worker)`));
8050
+ console.log(import_chalk20.default.gray(`Worker JS: ${workerJsPath}`));
8051
+ console.log(import_chalk20.default.gray(`Module paths: ${JSON.stringify(modulePaths)}`));
8052
+ console.log(import_chalk20.default.gray(`SQL files: ignored (not processed for update_worker)`));
7577
8053
  await updateWorker(workerJsPath, modulePaths, metadata, projectName);
7578
- console.log(import_chalk19.default.green("\n\u2705 Worker update complete!"));
8054
+ console.log(import_chalk20.default.green("\nWorker update complete."));
7579
8055
  process.exit(0);
7580
8056
  } catch (error) {
7581
- console.error(import_chalk19.default.red(`
7582
- \u274C Error: ${error.message}`));
8057
+ printCliError(error, "Worker update failed.");
7583
8058
  process.exit(1);
7584
8059
  }
7585
8060
  }
7586
8061
 
7587
8062
  // bin/updateWeb.ts
7588
- var import_chalk20 = __toESM(require("chalk"));
8063
+ var import_chalk21 = __toESM(require("chalk"));
7589
8064
  var import_fs_extra10 = __toESM(require("fs-extra"));
7590
8065
  var import_path15 = __toESM(require("path"));
7591
- var import_child_process4 = require("child_process");
8066
+ var import_child_process5 = require("child_process");
7592
8067
  var PROJECT_DIR5 = process.cwd();
7593
8068
  function loadConfig4() {
7594
8069
  const configPath = import_path15.default.join(PROJECT_DIR5, "pinme.toml");
7595
8070
  if (!import_fs_extra10.default.existsSync(configPath)) {
7596
- throw new Error("pinme.toml not found");
8071
+ throw createConfigError("`pinme.toml` not found in the current directory.", [
8072
+ "Run this command from the Pinme project root."
8073
+ ]);
7597
8074
  }
7598
8075
  const configContent = import_fs_extra10.default.readFileSync(configPath, "utf-8");
7599
8076
  const projectNameMatch = configContent.match(/project_name\s*=\s*"([^"]+)"/);
@@ -7602,36 +8079,44 @@ function loadConfig4() {
7602
8079
  };
7603
8080
  }
7604
8081
  function buildFrontend2() {
7605
- console.log(import_chalk20.default.blue("Building frontend..."));
8082
+ console.log(import_chalk21.default.blue("Building frontend..."));
7606
8083
  try {
7607
- (0, import_child_process4.execSync)("npm run build:frontend", {
8084
+ (0, import_child_process5.execSync)("npm run build:frontend", {
7608
8085
  cwd: PROJECT_DIR5,
7609
8086
  stdio: "inherit"
7610
8087
  });
7611
- console.log(import_chalk20.default.green("Frontend built"));
8088
+ console.log(import_chalk21.default.green("Frontend built"));
7612
8089
  } catch (error) {
7613
- throw new Error(`Frontend build failed: ${error.message}`);
8090
+ throw createCommandError("frontend build", "npm run build:frontend", error, [
8091
+ "Fix the frontend build error shown above, then rerun `pinme update-web`."
8092
+ ]);
7614
8093
  }
7615
8094
  }
7616
- function deployFrontend2() {
7617
- console.log(import_chalk20.default.blue("Deploying frontend to IPFS..."));
8095
+ function deployFrontend2(projectName) {
8096
+ console.log(import_chalk21.default.blue("Deploying frontend to IPFS..."));
7618
8097
  try {
7619
- (0, import_child_process4.execSync)("pinme upload ./frontend/dist", {
8098
+ (0, import_child_process5.execSync)("pinme upload ./frontend/dist", {
7620
8099
  cwd: PROJECT_DIR5,
7621
- stdio: "inherit"
8100
+ stdio: "inherit",
8101
+ env: {
8102
+ ...process.env,
8103
+ PINME_PROJECT_NAME: projectName
8104
+ }
7622
8105
  });
7623
- console.log(import_chalk20.default.green("Frontend deployed to IPFS"));
8106
+ console.log(import_chalk21.default.green("Frontend deployed to IPFS"));
7624
8107
  } catch (error) {
7625
- throw new Error(`Frontend deploy failed: ${error.message}`);
8108
+ throw createCommandError("frontend deploy", "pinme upload ./frontend/dist", error, [
8109
+ "Make sure `frontend/dist` exists and `pinme upload` can run successfully."
8110
+ ]);
7626
8111
  }
7627
8112
  }
7628
8113
  async function updateWebCmd(options) {
7629
8114
  try {
7630
8115
  const headers = getAuthHeaders();
7631
8116
  if (!headers["authentication-tokens"] || !headers["token-address"]) {
7632
- console.log(import_chalk20.default.yellow("\n\u26A0\uFE0F You are not logged in."));
7633
- console.log(import_chalk20.default.gray("Please run: pinme login"));
7634
- process.exit(1);
8117
+ throw createConfigError("No valid local login session was found.", [
8118
+ "Run `pinme login` and retry."
8119
+ ]);
7635
8120
  }
7636
8121
  const projectDir = (options == null ? void 0 : options.projectName) || (options == null ? void 0 : options.name) ? import_path15.default.join(PROJECT_DIR5, options.projectName || options.name) : PROJECT_DIR5;
7637
8122
  const tokenFileSrc = import_path15.default.join(PROJECT_DIR5, ".token.json");
@@ -7639,22 +8124,118 @@ async function updateWebCmd(options) {
7639
8124
  if (import_fs_extra10.default.existsSync(tokenFileSrc) && !import_fs_extra10.default.existsSync(tokenFileDst)) {
7640
8125
  import_fs_extra10.default.copySync(tokenFileSrc, tokenFileDst);
7641
8126
  }
7642
- console.log(import_chalk20.default.blue("\u{1F680} Updating web (frontend)...\n"));
7643
- console.log(import_chalk20.default.gray(`Project dir: ${PROJECT_DIR5}`));
8127
+ console.log(import_chalk21.default.blue("Updating web (frontend)...\n"));
8128
+ console.log(import_chalk21.default.gray(`Project dir: ${PROJECT_DIR5}`));
7644
8129
  const config = loadConfig4();
7645
8130
  const projectName = config.project_name;
7646
8131
  if (!projectName) {
7647
- throw new Error("project_name not found in pinme.toml");
8132
+ throw createConfigError("`project_name` is missing in `pinme.toml`.", [
8133
+ 'Set `project_name = "your-project-name"` in `pinme.toml`.'
8134
+ ]);
7648
8135
  }
7649
- console.log(import_chalk20.default.gray(`Project: ${projectName}`));
7650
- console.log(import_chalk20.default.blue("\n--- Frontend Update ---"));
8136
+ console.log(import_chalk21.default.gray(`Project: ${projectName}`));
8137
+ console.log(import_chalk21.default.blue("\n--- Frontend Update ---"));
7651
8138
  buildFrontend2();
7652
- deployFrontend2();
7653
- console.log(import_chalk20.default.green("\n\u2705 Web update complete!"));
8139
+ deployFrontend2(projectName);
8140
+ console.log(import_chalk21.default.green("\nWeb update complete."));
8141
+ process.exit(0);
8142
+ } catch (error) {
8143
+ printCliError(error, "Web update failed.");
8144
+ process.exit(1);
8145
+ }
8146
+ }
8147
+
8148
+ // bin/delete.ts
8149
+ var import_chalk22 = __toESM(require("chalk"));
8150
+ var import_inquirer9 = __toESM(require("inquirer"));
8151
+ var import_fs_extra11 = __toESM(require("fs-extra"));
8152
+ var import_path16 = __toESM(require("path"));
8153
+ var API_BASE5 = "https://pinme.dev/api/v4";
8154
+ function getProjectName() {
8155
+ const configPath = import_path16.default.join(process.cwd(), "pinme.toml");
8156
+ if (!import_fs_extra11.default.existsSync(configPath)) {
8157
+ return null;
8158
+ }
8159
+ const config = import_fs_extra11.default.readFileSync(configPath, "utf-8");
8160
+ const match = config.match(/project_name\s*=\s*"([^"]+)"/);
8161
+ return (match == null ? void 0 : match[1]) || null;
8162
+ }
8163
+ async function deleteCmd(options) {
8164
+ var _a, _b;
8165
+ try {
8166
+ const headers = getAuthHeaders();
8167
+ if (!headers["authentication-tokens"] || !headers["token-address"]) {
8168
+ console.log(import_chalk22.default.yellow("\n\u26A0\uFE0F You are not logged in."));
8169
+ console.log(import_chalk22.default.gray("Please run: pinme login"));
8170
+ process.exit(1);
8171
+ }
8172
+ console.log(import_chalk22.default.blue("Deleting project...\n"));
8173
+ let projectName = options.name || getProjectName();
8174
+ if (!projectName) {
8175
+ console.log(import_chalk22.default.red("\n\u274C Error: Cannot find project name."));
8176
+ console.log(import_chalk22.default.yellow(" Please make sure you are in the project directory."));
8177
+ console.log(import_chalk22.default.gray(" The project directory should contain a pinme.toml file."));
8178
+ console.log(import_chalk22.default.gray("\n Or specify the project name:"));
8179
+ console.log(import_chalk22.default.gray(" cd /path/to/your-project"));
8180
+ console.log(import_chalk22.default.gray(" pinme delete"));
8181
+ process.exit(1);
8182
+ }
8183
+ console.log(import_chalk22.default.gray(`Project: ${projectName}`));
8184
+ console.log(import_chalk22.default.gray(`Directory: ${process.cwd()}`));
8185
+ if (!options.force) {
8186
+ const answers = await import_inquirer9.default.prompt([
8187
+ {
8188
+ type: "confirm",
8189
+ name: "confirm",
8190
+ message: `Are you sure you want to delete project "${projectName}"? This will remove Worker, domain binding, and D1 database.`,
8191
+ default: false
8192
+ }
8193
+ ]);
8194
+ if (!answers.confirm) {
8195
+ console.log(import_chalk22.default.gray("Cancelled."));
8196
+ process.exit(0);
8197
+ }
8198
+ }
8199
+ console.log(import_chalk22.default.blue("Deleting project on platform..."));
8200
+ const apiUrl = `${API_BASE5}/delete_project`;
8201
+ console.log(import_chalk22.default.gray(`API URL: ${apiUrl}`));
8202
+ console.log(import_chalk22.default.gray(`Project name: ${projectName}`));
8203
+ const response = await axios_default.post(apiUrl, {
8204
+ project_name: projectName
8205
+ }, {
8206
+ headers: {
8207
+ ...headers,
8208
+ "Content-Type": "application/json"
8209
+ }
8210
+ }).catch((error) => {
8211
+ var _a2, _b2;
8212
+ if (error.response) {
8213
+ console.log(import_chalk22.default.red(` Response status: ${(_a2 = error.response) == null ? void 0 : _a2.status}`));
8214
+ console.log(import_chalk22.default.red(` Response data: ${JSON.stringify((_b2 = error.response) == null ? void 0 : _b2.data)}`));
8215
+ } else {
8216
+ console.log(import_chalk22.default.red("No Response"));
8217
+ }
8218
+ throw error;
8219
+ });
8220
+ const data = response.data;
8221
+ if (data.code === 200) {
8222
+ console.log(import_chalk22.default.green("\n\u2705 Project deleted successfully!"));
8223
+ console.log(import_chalk22.default.gray(`
8224
+ Project: ${data.data.project_name}`));
8225
+ console.log(import_chalk22.default.gray(` Domain deleted: ${data.data.domain_deleted ? "\u2705" : "\u274C"}`));
8226
+ console.log(import_chalk22.default.gray(` Worker deleted: ${data.data.worker_deleted ? "\u2705" : "\u274C"}`));
8227
+ console.log(import_chalk22.default.gray(` Database deleted: ${data.data.database_deleted ? "\u2705" : "\u274C"}`));
8228
+ console.log(import_chalk22.default.gray("\nLocal files are kept unchanged."));
8229
+ } else {
8230
+ const errorMsg = (data == null ? void 0 : data.msg) || "Failed to delete project";
8231
+ throw new Error(errorMsg);
8232
+ }
7654
8233
  process.exit(0);
7655
8234
  } catch (error) {
7656
- console.error(import_chalk20.default.red(`
7657
- \u274C Error: ${error.message}`));
8235
+ console.log(import_chalk22.default.red(error));
8236
+ const errorMsg = ((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.msg) || error.message || "Failed to delete project";
8237
+ console.error(import_chalk22.default.red(`
8238
+ \u274C Error: ${errorMsg}`));
7658
8239
  process.exit(1);
7659
8240
  }
7660
8241
  }
@@ -7664,9 +8245,9 @@ import_dotenv.default.config();
7664
8245
  checkNodeVersion();
7665
8246
  function showBanner() {
7666
8247
  console.log(
7667
- import_chalk21.default.cyan(import_figlet5.default.textSync("Pinme", { horizontalLayout: "full" }))
8248
+ import_chalk23.default.cyan(import_figlet5.default.textSync("Pinme", { horizontalLayout: "full" }))
7668
8249
  );
7669
- console.log(import_chalk21.default.cyan("A command-line tool for uploading files to IPFS\n"));
8250
+ console.log(import_chalk23.default.cyan("A command-line tool for uploading files to IPFS\n"));
7670
8251
  }
7671
8252
  var program = new import_commander.Command();
7672
8253
  program.name("pinme").version(version).option("-v, --version", "output the current version");
@@ -7689,6 +8270,7 @@ program.command("save").description("Deploy the project (frontend + backend)").a
7689
8270
  program.command("update-db").description("Execute database migration").action(() => updateDbCmd());
7690
8271
  program.command("update-worker").description("Execute worker migration").action(() => updateWorkerCmd());
7691
8272
  program.command("update-web").description("Deploy frontend to IPFS").action((options) => updateWebCmd(options));
8273
+ program.command("delete").description("Delete a project (Worker, domain, D1 database)").argument("[name]", "Project name").option("-f, --force", "Skip confirmation").action((name, options) => deleteCmd({ name, force: options == null ? void 0 : options.force }));
7692
8274
  program.command("domain").description("Alias for 'my-domains' command").action(() => myDomainsCmd());
7693
8275
  program.command("list").description("show upload history").option(
7694
8276
  "-l, --limit <number>",