pinme 2.0.8 → 2.0.9-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +212 -135
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5978,7 +5978,7 @@ var import_chalk26 = __toESM(require("chalk"));
5978
5978
  var import_figlet5 = __toESM(require("figlet"));
5979
5979
 
5980
5980
  // package.json
5981
- var version = "2.0.8";
5981
+ var version = "2.0.9-beta.2";
5982
5982
 
5983
5983
  // bin/upload.ts
5984
5984
  var import_path7 = __toESM(require("path"));
@@ -9067,16 +9067,10 @@ var import_os5 = __toESM(require("os"));
9067
9067
  var import_path11 = __toESM(require("path"));
9068
9068
  var import_chalk19 = __toESM(require("chalk"));
9069
9069
  var import_child_process3 = require("child_process");
9070
- var DependencyInstallError = class extends Error {
9071
- command;
9072
- constructor(command, cause) {
9073
- const reason = cause instanceof Error ? cause.message : String(cause);
9074
- super(`${command} failed: ${reason}`);
9075
- this.name = "DependencyInstallError";
9076
- this.command = command;
9077
- this.cause = cause;
9078
- }
9079
- };
9070
+ var INSTALL_TIMEOUT_MS = 10 * 60 * 1e3;
9071
+ var SLOW_INSTALL_NOTICE_MS = 60 * 1e3;
9072
+ var INSTALL_LOG_FILE = ".pinme-install.log";
9073
+ var INSTALL_EXITCODE_FILE = ".pinme-install.exitcode";
9080
9074
  function makeTempCacheDir() {
9081
9075
  return import_fs_extra5.default.mkdtempSync(import_path11.default.join(import_os5.default.tmpdir(), "pinme-npm-cache-"));
9082
9076
  }
@@ -9089,54 +9083,98 @@ function getNpmCommand() {
9089
9083
  function hasPackageLock(cwd) {
9090
9084
  return import_fs_extra5.default.existsSync(import_path11.default.join(cwd, "package-lock.json"));
9091
9085
  }
9092
- function getInstallScript(cwd) {
9086
+ function getInstallScript(cwd, mode) {
9087
+ if (mode !== "auto") {
9088
+ return mode;
9089
+ }
9093
9090
  return hasPackageLock(cwd) ? "ci" : "install";
9094
9091
  }
9095
- function formatInstallCommand(script) {
9096
- return `npm ${script} --cache <isolated npm cache> --no-audit --no-fund`;
9097
- }
9098
- function runInstall(cwd, cacheDir, script) {
9099
- const npm = getNpmCommand();
9100
- const result = (0, import_child_process3.spawnSync)(npm, [script, "--cache", cacheDir, "--no-audit", "--no-fund"], {
9092
+ function getInstallArgs(script, cacheDir) {
9093
+ return [
9094
+ script,
9095
+ "--cache",
9096
+ cacheDir,
9097
+ "--no-audit",
9098
+ "--no-fund",
9099
+ "--fetch-retries=3",
9100
+ "--fetch-retry-factor=2",
9101
+ "--fetch-retry-mintimeout=10000",
9102
+ "--fetch-retry-maxtimeout=60000",
9103
+ "--fetch-timeout=60000"
9104
+ ];
9105
+ }
9106
+ function quoteForShell(value) {
9107
+ if (process.platform === "win32") {
9108
+ return `"${value}"`;
9109
+ }
9110
+ return `'${value.replace(/'/g, `'\\''`)}'`;
9111
+ }
9112
+ function startBackgroundInstall(cwd) {
9113
+ const script = getInstallScript(cwd, "auto");
9114
+ const cacheDir = makeTempCacheDir();
9115
+ const args = getInstallArgs(script, cacheDir);
9116
+ const logPath = import_path11.default.join(cwd, INSTALL_LOG_FILE);
9117
+ const exitCodePath = import_path11.default.join(cwd, INSTALL_EXITCODE_FILE);
9118
+ import_fs_extra5.default.removeSync(exitCodePath);
9119
+ import_fs_extra5.default.writeFileSync(logPath, `[pinme] ${(/* @__PURE__ */ new Date()).toISOString()} starting "npm ${script}"
9120
+ `);
9121
+ const npmCmd = process.platform === "win32" ? "npm" : getNpmCommand();
9122
+ const installCmd = `${npmCmd} ${args.map(quoteForShell).join(" ")}`;
9123
+ const qLog = quoteForShell(logPath);
9124
+ const qExit = quoteForShell(exitCodePath);
9125
+ let shellBin;
9126
+ let shellArgs;
9127
+ if (process.platform === "win32") {
9128
+ const command = `${installCmd} >> ${qLog} 2>&1 & echo %errorlevel% > ${qExit}`;
9129
+ shellBin = process.env.ComSpec || "cmd.exe";
9130
+ shellArgs = ["/d", "/s", "/c", command];
9131
+ } else {
9132
+ const command = `${installCmd} >> ${qLog} 2>&1; printf '%s' "$?" > ${qExit}`;
9133
+ shellBin = "/bin/sh";
9134
+ shellArgs = ["-c", command];
9135
+ }
9136
+ const child = (0, import_child_process3.spawn)(shellBin, shellArgs, {
9101
9137
  cwd,
9102
- stdio: "inherit",
9103
- shell: true,
9138
+ detached: true,
9139
+ stdio: "ignore",
9104
9140
  env: {
9105
9141
  ...process.env,
9106
9142
  npm_config_cache: cacheDir,
9107
9143
  npm_config_audit: "false",
9108
- npm_config_fund: "false"
9144
+ npm_config_fund: "false",
9145
+ npm_config_fetch_retries: "3",
9146
+ npm_config_fetch_retry_factor: "2",
9147
+ npm_config_fetch_retry_mintimeout: "10000",
9148
+ npm_config_fetch_retry_maxtimeout: "60000",
9149
+ npm_config_fetch_timeout: "60000"
9109
9150
  }
9110
9151
  });
9111
- if (result.error) {
9112
- throw result.error;
9152
+ child.unref();
9153
+ return { logPath };
9154
+ }
9155
+ function readBackgroundInstallStatus(cwd) {
9156
+ const logPath = import_path11.default.join(cwd, INSTALL_LOG_FILE);
9157
+ const exitCodePath = import_path11.default.join(cwd, INSTALL_EXITCODE_FILE);
9158
+ if (import_fs_extra5.default.existsSync(exitCodePath)) {
9159
+ const raw = import_fs_extra5.default.readFileSync(exitCodePath, "utf-8").trim();
9160
+ const code = Number.parseInt(raw, 10);
9161
+ if (Number.isNaN(code)) {
9162
+ return { status: "running", exitCode: null, logPath };
9163
+ }
9164
+ return { status: code === 0 ? "success" : "failed", exitCode: code, logPath };
9113
9165
  }
9114
- if (result.status !== 0) {
9115
- throw new Error(`npm ${script} failed with exit code ${result.status}`);
9166
+ if (import_fs_extra5.default.existsSync(logPath)) {
9167
+ return { status: "running", exitCode: null, logPath };
9116
9168
  }
9169
+ return { status: "idle", exitCode: null, logPath };
9117
9170
  }
9118
- function installProjectDependencies(cwd) {
9119
- const script = getInstallScript(cwd);
9120
- const command = formatInstallCommand(script);
9121
- let lastError;
9122
- for (let attempt = 1; attempt <= 2; attempt += 1) {
9123
- const cacheDir = makeTempCacheDir();
9124
- try {
9125
- if (attempt > 1) {
9126
- console.log(import_chalk19.default.yellow(" Retrying dependency install with a fresh npm cache..."));
9127
- }
9128
- if (attempt === 1 && script === "ci") {
9129
- console.log(import_chalk19.default.gray(" package-lock.json found; using npm ci for a reproducible install."));
9130
- }
9131
- runInstall(cwd, cacheDir, script);
9132
- return;
9133
- } catch (error) {
9134
- lastError = error;
9135
- } finally {
9136
- import_fs_extra5.default.removeSync(cacheDir);
9137
- }
9171
+ function readBackgroundInstallLogTail(cwd, maxChars = 1500) {
9172
+ const logPath = import_path11.default.join(cwd, INSTALL_LOG_FILE);
9173
+ if (!import_fs_extra5.default.existsSync(logPath)) {
9174
+ return "";
9138
9175
  }
9139
- throw new DependencyInstallError(command, lastError);
9176
+ const content = import_fs_extra5.default.readFileSync(logPath, "utf-8");
9177
+ return content.length > maxChars ? content.slice(content.length - maxChars) : content;
9140
9178
  }
9141
9179
 
9142
9180
  // bin/create.ts
@@ -9312,23 +9350,14 @@ Directory "${projectName}" already exists.`));
9312
9350
  import_fs_extra6.default.removeSync(zipPath);
9313
9351
  import_fs_extra6.default.removeSync(extractDir);
9314
9352
  const nodeModulesPath = import_path12.default.join(targetDir, "node_modules");
9315
- const packageLockPath = import_path12.default.join(targetDir, "package-lock.json");
9316
9353
  if (import_fs_extra6.default.existsSync(nodeModulesPath)) {
9317
9354
  console.log(import_chalk20.default.gray(" Removing existing node_modules..."));
9318
9355
  import_fs_extra6.default.removeSync(nodeModulesPath);
9319
9356
  }
9320
- if (import_fs_extra6.default.existsSync(packageLockPath)) {
9321
- console.log(import_chalk20.default.gray(" Removing existing package-lock.json..."));
9322
- import_fs_extra6.default.removeSync(packageLockPath);
9323
- }
9324
9357
  const frontendNodeModules = import_path12.default.join(targetDir, "frontend", "node_modules");
9325
9358
  const backendNodeModules = import_path12.default.join(targetDir, "backend", "node_modules");
9326
- const frontendPackageLock = import_path12.default.join(targetDir, "frontend", "package-lock.json");
9327
- const backendPackageLock = import_path12.default.join(targetDir, "backend", "package-lock.json");
9328
9359
  if (import_fs_extra6.default.existsSync(frontendNodeModules)) import_fs_extra6.default.removeSync(frontendNodeModules);
9329
9360
  if (import_fs_extra6.default.existsSync(backendNodeModules)) import_fs_extra6.default.removeSync(backendNodeModules);
9330
- if (import_fs_extra6.default.existsSync(frontendPackageLock)) import_fs_extra6.default.removeSync(frontendPackageLock);
9331
- if (import_fs_extra6.default.existsSync(backendPackageLock)) import_fs_extra6.default.removeSync(backendPackageLock);
9332
9361
  console.log(import_chalk20.default.green(` Template downloaded to: ${targetDir}`));
9333
9362
  } catch (error) {
9334
9363
  throw createCommandError("template extraction", `extract "${zipPath}" to "${extractDir}"`, error, [
@@ -9406,53 +9435,23 @@ Directory "${projectName}" already exists.`));
9406
9435
  }
9407
9436
  import_fs_extra6.default.writeFileSync(configPath, pinmeConfig);
9408
9437
  console.log(import_chalk20.default.green(` Updated pinme.toml with api_url`));
9409
- console.log(import_chalk20.default.blue("\n4. Installing dependencies..."));
9438
+ console.log(import_chalk20.default.blue("\n4. Installing dependencies in the background..."));
9410
9439
  try {
9411
- installProjectDependencies(targetDir);
9412
- console.log(import_chalk20.default.green(" Project dependencies installed"));
9440
+ const { logPath } = startBackgroundInstall(targetDir);
9441
+ console.log(import_chalk20.default.gray(" Dependencies are installing in the background; create will continue."));
9442
+ console.log(import_chalk20.default.gray(` Install progress is logged to: ${logPath}`));
9443
+ console.log(import_chalk20.default.gray(" `pinme save` will automatically wait for this install to finish."));
9413
9444
  } catch (error) {
9414
- const errorMsg = error.message || "";
9415
- const installCommand = error instanceof DependencyInstallError ? error.command : "npm ci/npm install --cache <isolated npm cache> --no-audit --no-fund";
9416
- if (errorMsg.includes("EACCES") || errorMsg.includes("EPERM") || errorMsg.includes("permission denied")) {
9417
- throw createCommandError("project dependency install", installCommand, error, [
9418
- "Permission error detected. Pinme already retries with an isolated npm cache.",
9419
- "Check whether the project directory is writable:",
9420
- " ls -la " + targetDir,
9421
- "If npm still reports a root-owned cache, set `npm_config_cache` to a user-writable directory and retry."
9422
- ]);
9423
- }
9424
- if (errorMsg.includes("ENOTFOUND") || errorMsg.includes("ETIMEDOUT") || errorMsg.includes("network")) {
9425
- throw createCommandError("project dependency install", installCommand, error, [
9426
- "Network error detected. Please check:",
9427
- " 1. Internet connection is available",
9428
- " 2. npm registry is accessible (https://registry.npmjs.org)",
9429
- " 3. Try using a mirror: npm config set registry https://registry.npmmirror.com"
9430
- ]);
9431
- }
9432
- throw createCommandError("project dependency install", installCommand, error, [
9433
- "Dependency installation failed.",
9434
- "Check network connectivity and npm registry availability.",
9435
- "Inspect the generated workspace `package.json` files for dependency conflicts.",
9436
- "If `package-lock.json` is stale, update it intentionally with `npm install` before retrying."
9437
- ]);
9438
- }
9439
- console.log(import_chalk20.default.blue("\n5. Building backend worker..."));
9440
- try {
9441
- (0, import_child_process4.execSync)("npm run build:worker", {
9442
- cwd: targetDir,
9443
- stdio: "inherit"
9444
- });
9445
- console.log(import_chalk20.default.green(" Worker built"));
9446
- } catch (error) {
9447
- throw createCommandError("worker build", "npm run build:worker", error, [
9448
- "Fix the build error shown above, then rerun `pinme create`."
9449
- ]);
9445
+ console.log(import_chalk20.default.yellow(" Warning: could not start background dependency install."));
9446
+ console.log(import_chalk20.default.yellow(" Run `npm install` inside the project before `pinme save`."));
9450
9447
  }
9448
+ console.log(import_chalk20.default.blue("\n5. Preparing backend worker..."));
9451
9449
  const distWorkerDir = import_path12.default.join(targetDir, "dist-worker");
9452
9450
  const workerJsPath = import_path12.default.join(distWorkerDir, "worker.js");
9453
9451
  if (!import_fs_extra6.default.existsSync(distWorkerDir) || !import_fs_extra6.default.existsSync(workerJsPath)) {
9454
- throw createConfigError("Built worker output not found: `dist-worker/worker.js`.", [
9455
- "Make sure `npm run build:worker` completed successfully."
9452
+ throw createConfigError("Prebuilt worker output not found: `dist-worker/worker.js`.", [
9453
+ "The template should ship a prebuilt `dist-worker/`.",
9454
+ "Once dependencies finish installing, run `npm run build:worker` in the project, then `pinme save`."
9456
9455
  ]);
9457
9456
  }
9458
9457
  const modulePaths = [];
@@ -9527,23 +9526,19 @@ Directory "${projectName}" already exists.`));
9527
9526
  "Check whether backend metadata, SQL files, or worker bundle contains invalid content."
9528
9527
  ]);
9529
9528
  }
9530
- console.log(import_chalk20.default.blue("\n7. Building frontend..."));
9529
+ console.log(import_chalk20.default.blue("\n7. Preparing frontend..."));
9531
9530
  const frontendDir = import_path12.default.join(targetDir, "frontend");
9531
+ const frontendDistDir = import_path12.default.join(frontendDir, "dist");
9532
9532
  if (import_fs_extra6.default.existsSync(frontendDir)) {
9533
- try {
9534
- (0, import_child_process4.execSync)("npm run build:frontend", {
9535
- cwd: targetDir,
9536
- stdio: "inherit"
9537
- });
9538
- console.log(import_chalk20.default.green(" Frontend built"));
9539
- } catch (error) {
9540
- throw createCommandError("frontend build", "npm run build:frontend", error, [
9541
- "Fix the frontend build error shown above, then rerun `pinme create`."
9533
+ if (!import_fs_extra6.default.existsSync(frontendDistDir)) {
9534
+ throw createConfigError("Prebuilt frontend output not found: `frontend/dist/`.", [
9535
+ "The template should ship a prebuilt `frontend/dist/`.",
9536
+ "Once dependencies finish installing, run `npm run build:frontend` in the project, then `pinme save`."
9542
9537
  ]);
9543
9538
  }
9544
9539
  console.log(import_chalk20.default.blue(" Uploading to IPFS..."));
9545
9540
  try {
9546
- const uploadResult = await uploadPath(import_path12.default.join(frontendDir, "dist"), {
9541
+ const uploadResult = await uploadPath(frontendDistDir, {
9547
9542
  action: "project_create",
9548
9543
  projectName: workerData.project_name,
9549
9544
  uid: headers["token-address"]
@@ -9602,6 +9597,7 @@ Next steps:`));
9602
9597
 
9603
9598
  // bin/save.ts
9604
9599
  var import_chalk21 = __toESM(require("chalk"));
9600
+ var import_ora3 = __toESM(require("ora"));
9605
9601
  var import_fs_extra7 = __toESM(require("fs-extra"));
9606
9602
  var import_path13 = __toESM(require("path"));
9607
9603
  init_axios2();
@@ -9646,42 +9642,117 @@ function buildWorker() {
9646
9642
  });
9647
9643
  console.log(import_chalk21.default.green("Worker built"));
9648
9644
  } catch (error) {
9645
+ if (isMissingDependencyError(error)) {
9646
+ throw dependenciesMissingError(
9647
+ "Worker build failed because a required CLI (e.g. `wrangler`) was not found. Project dependencies are missing or incomplete."
9648
+ );
9649
+ }
9649
9650
  throw createCommandError("worker build", "npm run build:worker", error, [
9650
9651
  "Fix the build error shown above, then rerun `pinme save`."
9651
9652
  ]);
9652
9653
  }
9653
9654
  }
9654
- function installDependencies() {
9655
- console.log(import_chalk21.default.blue("Installing dependencies..."));
9656
- try {
9657
- installProjectDependencies(PROJECT_DIR2);
9658
- console.log(import_chalk21.default.green("Project dependencies installed"));
9659
- } catch (error) {
9660
- const errorMsg = error.message || "";
9661
- const installCommand = error instanceof DependencyInstallError ? error.command : "npm ci/npm install --cache <isolated npm cache> --no-audit --no-fund";
9662
- if (errorMsg.includes("EACCES") || errorMsg.includes("EPERM") || errorMsg.includes("permission denied")) {
9663
- throw createCommandError("project dependency install", installCommand, error, [
9664
- "Permission error detected. Pinme already retries with an isolated npm cache.",
9665
- "Check whether the project directory is writable:",
9666
- " ls -la " + PROJECT_DIR2,
9667
- "If npm still reports a root-owned cache, set `npm_config_cache` to a user-writable directory and retry."
9668
- ]);
9655
+ function dependenciesMissingError(summary, logTail) {
9656
+ const installLogPath = import_path13.default.join(PROJECT_DIR2, ".pinme-install.log");
9657
+ const suggestions = [
9658
+ "Run `npm install` in the project root, wait for it to finish, then rerun `pinme save`."
9659
+ ];
9660
+ if (import_fs_extra7.default.existsSync(installLogPath)) {
9661
+ suggestions.push(
9662
+ `Background install log: ${installLogPath}`
9663
+ );
9664
+ }
9665
+ const error = createConfigError(summary, suggestions);
9666
+ const trimmedTail = logTail == null ? void 0 : logTail.trim();
9667
+ if (trimmedTail) {
9668
+ error.details = [
9669
+ ...error.details,
9670
+ "Install log (tail):",
9671
+ ...trimmedTail.split("\n").slice(-20)
9672
+ ];
9673
+ }
9674
+ return error;
9675
+ }
9676
+ function isMissingDependencyError(error) {
9677
+ const exitCode = (error == null ? void 0 : error.status) ?? (error == null ? void 0 : error.code);
9678
+ if (exitCode === 127) {
9679
+ return true;
9680
+ }
9681
+ const haystack = [error == null ? void 0 : error.message, error == null ? void 0 : error.stderr, error == null ? void 0 : error.stdout].map((value) => String(value || "")).join(" ").toLowerCase();
9682
+ return /command not found|not found|is not recognized|cannot find module|cannot find package/.test(haystack);
9683
+ }
9684
+ function dependenciesPresent() {
9685
+ return hasLocalBinary("wrangler") && hasLocalBinary("vite");
9686
+ }
9687
+ function sleep(ms) {
9688
+ return new Promise((resolve) => setTimeout(resolve, ms));
9689
+ }
9690
+ var WAIT_FOR_INSTALL_TIMEOUT_MS = 12 * 60 * 1e3;
9691
+ var WAIT_POLL_INTERVAL_MS = 2e3;
9692
+ async function ensureDependenciesReady() {
9693
+ if (dependenciesPresent()) {
9694
+ console.log(import_chalk21.default.gray("Dependencies are installed."));
9695
+ return;
9696
+ }
9697
+ const initial = readBackgroundInstallStatus(PROJECT_DIR2);
9698
+ if (initial.status === "idle") {
9699
+ throw dependenciesMissingError(
9700
+ "Project dependencies are not installed and no install is running."
9701
+ );
9702
+ }
9703
+ if (initial.status === "failed") {
9704
+ throw dependenciesMissingError(
9705
+ `The background dependency install (started by \`pinme create\`) failed with exit code ${initial.exitCode}.`,
9706
+ readBackgroundInstallLogTail(PROJECT_DIR2)
9707
+ );
9708
+ }
9709
+ const spinner = (0, import_ora3.default)("Waiting for background dependency install to finish...").start();
9710
+ const startedAt = Date.now();
9711
+ while (true) {
9712
+ await sleep(WAIT_POLL_INTERVAL_MS);
9713
+ if (dependenciesPresent()) {
9714
+ spinner.succeed("Dependencies installed.");
9715
+ return;
9669
9716
  }
9670
- if (errorMsg.includes("ENOTFOUND") || errorMsg.includes("ETIMEDOUT") || errorMsg.includes("network")) {
9671
- throw createCommandError("project dependency install", installCommand, error, [
9672
- "Network error detected. Please check:",
9673
- " 1. Internet connection is available",
9674
- " 2. npm registry is accessible (https://registry.npmjs.org)",
9675
- " 3. Try using a mirror: npm config set registry https://registry.npmmirror.com"
9676
- ]);
9717
+ const state = readBackgroundInstallStatus(PROJECT_DIR2);
9718
+ if (state.status === "failed") {
9719
+ spinner.fail("Background dependency install failed.");
9720
+ throw dependenciesMissingError(
9721
+ `The background dependency install (started by \`pinme create\`) failed with exit code ${state.exitCode}.`,
9722
+ readBackgroundInstallLogTail(PROJECT_DIR2)
9723
+ );
9677
9724
  }
9678
- throw createCommandError("project dependency install", installCommand, error, [
9679
- "Dependency installation failed.",
9680
- "Check network connectivity and npm registry availability.",
9681
- "If `package-lock.json` is stale, update it intentionally with `npm install` before retrying."
9682
- ]);
9725
+ if (state.status === "success") {
9726
+ spinner.succeed("Dependencies installed.");
9727
+ return;
9728
+ }
9729
+ const elapsedMs = Date.now() - startedAt;
9730
+ if (elapsedMs > WAIT_FOR_INSTALL_TIMEOUT_MS) {
9731
+ spinner.fail("Timed out waiting for the dependency install.");
9732
+ throw dependenciesMissingError(
9733
+ "Timed out waiting for the background dependency install to finish.",
9734
+ readBackgroundInstallLogTail(PROJECT_DIR2)
9735
+ );
9736
+ }
9737
+ spinner.text = `Waiting for background dependency install to finish... (${Math.round(elapsedMs / 1e3)}s)`;
9738
+ }
9739
+ }
9740
+ function cleanupInstallMarkers() {
9741
+ try {
9742
+ import_fs_extra7.default.removeSync(import_path13.default.join(PROJECT_DIR2, INSTALL_LOG_FILE));
9743
+ import_fs_extra7.default.removeSync(import_path13.default.join(PROJECT_DIR2, INSTALL_EXITCODE_FILE));
9744
+ } catch {
9683
9745
  }
9684
9746
  }
9747
+ function hasLocalBinary(name) {
9748
+ const binDirs = [
9749
+ import_path13.default.join(PROJECT_DIR2, "node_modules", ".bin"),
9750
+ import_path13.default.join(PROJECT_DIR2, "backend", "node_modules", ".bin"),
9751
+ import_path13.default.join(PROJECT_DIR2, "frontend", "node_modules", ".bin")
9752
+ ];
9753
+ const candidates = process.platform === "win32" ? [name, `${name}.cmd`, `${name}.exe`, `${name}.ps1`] : [name];
9754
+ return binDirs.some((dir) => candidates.some((candidate) => import_fs_extra7.default.existsSync(import_path13.default.join(dir, candidate))));
9755
+ }
9685
9756
  function getBuiltWorker() {
9686
9757
  const distWorkerDir = import_path13.default.join(PROJECT_DIR2, "dist-worker");
9687
9758
  if (!import_fs_extra7.default.existsSync(distWorkerDir)) {
@@ -9787,6 +9858,11 @@ function buildFrontend() {
9787
9858
  });
9788
9859
  console.log(import_chalk21.default.green("Frontend built"));
9789
9860
  } catch (error) {
9861
+ if (isMissingDependencyError(error)) {
9862
+ throw dependenciesMissingError(
9863
+ "Frontend build failed because a required CLI (e.g. `vite`) was not found. Project dependencies are missing or incomplete."
9864
+ );
9865
+ }
9790
9866
  throw createCommandError("frontend build", "npm run build:frontend", error, [
9791
9867
  "Fix the frontend build error shown above, then rerun `pinme save`."
9792
9868
  ]);
@@ -9886,7 +9962,7 @@ async function saveCmd(options) {
9886
9962
  const apiUrl = `${getPinmeApiUrl("/save_worker")}?project_name=${encodeURIComponent(projectName)}`;
9887
9963
  console.log(import_chalk21.default.gray(`API URL: ${apiUrl}`));
9888
9964
  console.log(import_chalk21.default.blue("\n--- Backend ---"));
9889
- installDependencies();
9965
+ await ensureDependenciesReady();
9890
9966
  buildWorker();
9891
9967
  const metadata = getMetadata();
9892
9968
  const { workerJsPath, modulePaths } = getBuiltWorker();
@@ -9915,6 +9991,7 @@ async function saveCmd(options) {
9915
9991
  "management"
9916
9992
  );
9917
9993
  console.log(import_chalk21.default.green("\nDeployment complete."));
9994
+ cleanupInstallMarkers();
9918
9995
  void tracker_default.trackEvent(TRACK_EVENTS.projectSaveSuccess, TRACK_PAGES.deploy, {
9919
9996
  a: resolveTrackAction2(TRACK_EVENTS.projectSaveSuccess),
9920
9997
  project_name: projectName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinme",
3
- "version": "2.0.8",
3
+ "version": "2.0.9-beta.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },