@qpfai/pf-gate-cli 1.0.14 → 1.0.16

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-02-16T22:28:02.794Z",
3
+ "generatedAt": "2026-02-16T22:39:32.770Z",
4
4
  "pythonPackage": "persons-field",
5
5
  "pythonPackageVersion": "1.0.0",
6
6
  "wheel": "persons_field-1.0.0-py3-none-any.whl",
7
- "sha256": "322c007a43e3ce7b6f42339637da32e039f3f06e7b1385175e8ab1a2cf6eac78"
7
+ "sha256": "10cbc5c716202bace88f1b20f3a671030cca7c3bbc45b470ba5f52e64e188833"
8
8
  }
package/lib/main.mjs CHANGED
@@ -16,6 +16,8 @@ const INSTALL_STATE_NAME = "install-state.json";
16
16
  const PYTHON_CANDIDATES = ["python3.13", "python3", "python"];
17
17
  const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
18
18
  const UPDATE_CHECK_TIMEOUT_MS = 6000;
19
+ const UPDATE_INSTALL_MAX_RETRIES = 10;
20
+ const UPDATE_INSTALL_RETRY_MS = 3000;
19
21
  const SPHERE_FRAMES = ["🌐", "🌍", "🌎", "🌏"];
20
22
 
21
23
  function isWindows() {
@@ -117,6 +119,10 @@ function runCapture(command, args) {
117
119
  };
118
120
  }
119
121
 
122
+ function sleep(ms) {
123
+ return new Promise((resolve) => setTimeout(resolve, ms));
124
+ }
125
+
120
126
  function startSphereSpinner(message) {
121
127
  if (!process.stdout.isTTY) {
122
128
  return (_doneMessage) => {};
@@ -327,11 +333,14 @@ function canPrompt() {
327
333
  return Boolean(process.stdin.isTTY && process.stdout.isTTY);
328
334
  }
329
335
 
330
- async function promptYesNo(question, defaultYes = true) {
336
+ async function promptYesNo(question, defaultYes = true, promptSuffix = null) {
331
337
  if (!canPrompt()) {
332
338
  return false;
333
339
  }
334
- const prompt = defaultYes ? `${question} [Y/n] ` : `${question} [y/N] `;
340
+ let prompt = defaultYes ? `${question} [Y/n] ` : `${question} [y/N] `;
341
+ if (typeof promptSuffix === "string" && promptSuffix.trim()) {
342
+ prompt = `${question} ${promptSuffix.trim()} `;
343
+ }
335
344
  const rl = readline.createInterface({
336
345
  input: process.stdin,
337
346
  output: process.stdout,
@@ -347,6 +356,17 @@ async function promptYesNo(question, defaultYes = true) {
347
356
  }
348
357
  }
349
358
 
359
+ async function promptCliUpdate(metadata, latestVersion) {
360
+ if (!canPrompt()) {
361
+ return false;
362
+ }
363
+ console.clear();
364
+ console.log(
365
+ `Update available: ${metadata.name} ${metadata.version} -> ${latestVersion}`
366
+ );
367
+ return promptYesNo("Install update now?", false, "[Y/N]");
368
+ }
369
+
350
370
  function npmGlobalBinDir() {
351
371
  const prefix = runCapture("npm", ["prefix", "-g"]);
352
372
  if (!prefix.ok) {
@@ -386,8 +406,26 @@ function maybeHealBinCollision(output) {
386
406
  return removed;
387
407
  }
388
408
 
389
- async function installLatestCliPackage(packageName, registry) {
390
- const args = ["i", "-g", `${packageName}@latest`, "--registry", registry];
409
+ function isEtargetPublishRace(output) {
410
+ const text = String(output || "").toLowerCase();
411
+ return text.includes("code etarget") || text.includes("notarget no matching version");
412
+ }
413
+
414
+ async function npmVersionExists(packageName, version, registry) {
415
+ const result = await spawnCapture(
416
+ "npm",
417
+ ["view", `${packageName}@${version}`, "version", "--json", "--registry", registry],
418
+ { encoding: "utf-8" }
419
+ );
420
+ if (result.error || (result.status ?? 1) !== 0) {
421
+ return false;
422
+ }
423
+ const parsed = parseNpmViewVersion(String(result.stdout || "").trim());
424
+ return parsed === String(version || "").trim();
425
+ }
426
+
427
+ async function installLatestCliPackage(packageName, targetVersion, registry) {
428
+ const args = ["i", "-g", `${packageName}@${targetVersion}`, "--registry", registry];
391
429
  const runOnce = async () => {
392
430
  const stop = startSphereSpinner("Installing PF Gate update...");
393
431
  const result = await spawnCapture("npm", args, { encoding: "utf-8" });
@@ -399,22 +437,55 @@ async function installLatestCliPackage(packageName, registry) {
399
437
  return result;
400
438
  };
401
439
 
402
- let result = await runOnce();
403
- if (!result.error && (result.status ?? 1) === 0) {
404
- return true;
405
- }
440
+ let attempts = 0;
441
+ let healedCollision = false;
442
+ let result = null;
443
+
444
+ while (attempts < UPDATE_INSTALL_MAX_RETRIES) {
445
+ attempts += 1;
446
+ const versionReady = await npmVersionExists(packageName, targetVersion, registry);
447
+ if (!versionReady) {
448
+ if (attempts < UPDATE_INSTALL_MAX_RETRIES) {
449
+ const delayMs = UPDATE_INSTALL_RETRY_MS * attempts;
450
+ const seconds = Math.ceil(delayMs / 1000);
451
+ const stopWait = startSphereSpinner(
452
+ `Waiting for ${packageName}@${targetVersion} to be available (${seconds}s)...`
453
+ );
454
+ await sleep(delayMs);
455
+ stopWait("✓ Retrying update install...");
456
+ continue;
457
+ }
458
+ break;
459
+ }
406
460
 
407
- const combined = `${String(result.stdout || "")}\n${String(result.stderr || "")}`;
408
- if (maybeHealBinCollision(combined)) {
409
461
  result = await runOnce();
410
462
  if (!result.error && (result.status ?? 1) === 0) {
411
463
  return true;
412
464
  }
465
+
466
+ const combined = `${String(result.stdout || "")}\n${String(result.stderr || "")}`;
467
+ if (!healedCollision && maybeHealBinCollision(combined)) {
468
+ healedCollision = true;
469
+ continue;
470
+ }
471
+
472
+ if (attempts < UPDATE_INSTALL_MAX_RETRIES && isEtargetPublishRace(combined)) {
473
+ const delayMs = UPDATE_INSTALL_RETRY_MS * attempts;
474
+ const seconds = Math.ceil(delayMs / 1000);
475
+ const stopWait = startSphereSpinner(
476
+ `Waiting for npm registry propagation (${seconds}s)...`
477
+ );
478
+ await sleep(delayMs);
479
+ stopWait("✓ Retrying update install...");
480
+ continue;
481
+ }
482
+ break;
413
483
  }
414
- if (result.stdout) {
484
+
485
+ if (result && result.stdout) {
415
486
  process.stdout.write(String(result.stdout));
416
487
  }
417
- if (result.stderr) {
488
+ if (result && result.stderr) {
418
489
  process.stderr.write(String(result.stderr));
419
490
  }
420
491
  return false;
@@ -438,16 +509,13 @@ async function maybeOfferCliUpdate(metadata) {
438
509
  debugUpdate(`up-to-date (latest=${latestVersion})`);
439
510
  return false;
440
511
  }
441
- console.log(
442
- `Update available: ${metadata.name} ${metadata.version} -> ${latestVersion}`
443
- );
444
- const wantsUpdate = await promptYesNo("Install update now?", true);
512
+ const wantsUpdate = await promptCliUpdate(metadata, latestVersion);
445
513
  if (!wantsUpdate) {
446
514
  console.log(`Continue with ${metadata.version}.`);
447
515
  return false;
448
516
  }
449
517
  console.log(`Updating ${metadata.name}...`);
450
- const installed = await installLatestCliPackage(metadata.name, registry);
518
+ const installed = await installLatestCliPackage(metadata.name, latestVersion, registry);
451
519
  if (!installed) {
452
520
  console.log("Update failed. Continuing with current version.");
453
521
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qpfai/pf-gate-cli",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "PF Gate terminal launcher with first-run runtime bootstrap.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",