@tempo-ai/visual-review 0.0.114-staging.1 → 0.0.115-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -6,7 +6,7 @@ async function main(argv = process.argv.slice(2)) {
6
6
  const [command, ...commandArgs] = argv;
7
7
  switch (command) {
8
8
  case "capture":
9
- return await (await import("./cli-BJVATWX4.js")).main(commandArgs);
9
+ return await (await import("./cli-HEA4IYKH.js")).main(commandArgs);
10
10
  case "claim":
11
11
  return await (await import("./claim-J654JAS6.js")).main(commandArgs);
12
12
  case "report":
@@ -1503,18 +1503,23 @@ function runAbortableShellCommand(options) {
1503
1503
  exitCode: null,
1504
1504
  stdout: "",
1505
1505
  stderr: "",
1506
+ output: "",
1506
1507
  aborted: true,
1508
+ timedOut: false,
1507
1509
  spawnError: null
1508
1510
  });
1509
1511
  }
1510
1512
  return new Promise((resolve2) => {
1511
1513
  let stdout = "";
1512
1514
  let stderr = "";
1515
+ let output = "";
1513
1516
  let aborted = false;
1517
+ let timedOut = false;
1514
1518
  let settled = false;
1515
1519
  let terminationComplete = true;
1516
1520
  let closeResult;
1517
1521
  let forceTimer = null;
1522
+ let idleTimer = null;
1518
1523
  let child;
1519
1524
  try {
1520
1525
  child = spawn2(options.command, {
@@ -1530,7 +1535,9 @@ function runAbortableShellCommand(options) {
1530
1535
  exitCode: null,
1531
1536
  stdout,
1532
1537
  stderr,
1538
+ output,
1533
1539
  aborted: false,
1540
+ timedOut: false,
1534
1541
  spawnError: error instanceof Error ? error : new Error(String(error))
1535
1542
  });
1536
1543
  return;
@@ -1540,18 +1547,23 @@ function runAbortableShellCommand(options) {
1540
1547
  if (settled || closeResult === void 0 || !terminationComplete) return;
1541
1548
  settled = true;
1542
1549
  if (forceTimer) clearTimeout(forceTimer);
1550
+ if (idleTimer) clearTimeout(idleTimer);
1543
1551
  options.signal?.removeEventListener("abort", onAbort);
1544
1552
  resolve2({
1545
1553
  exitCode: closeResult,
1546
1554
  stdout,
1547
1555
  stderr,
1556
+ output,
1548
1557
  aborted,
1558
+ timedOut,
1549
1559
  spawnError
1550
1560
  });
1551
1561
  };
1552
- const terminateTree = () => {
1553
- if (aborted) return;
1554
- aborted = true;
1562
+ const terminateTree = (reason) => {
1563
+ if (aborted || timedOut || closeResult !== void 0) return;
1564
+ aborted = reason === "abort";
1565
+ timedOut = reason === "timeout";
1566
+ if (idleTimer) clearTimeout(idleTimer);
1555
1567
  terminationComplete = false;
1556
1568
  const pid = child.pid;
1557
1569
  if (pid != null && process.platform !== "win32") {
@@ -1602,19 +1614,41 @@ function runAbortableShellCommand(options) {
1602
1614
  terminationComplete = true;
1603
1615
  }
1604
1616
  };
1605
- const onAbort = () => terminateTree();
1617
+ const onAbort = () => terminateTree("abort");
1606
1618
  options.signal?.addEventListener("abort", onAbort, { once: true });
1607
- if (options.signal?.aborted) terminateTree();
1619
+ if (options.signal?.aborted) onAbort();
1620
+ const resetIdleTimer = () => {
1621
+ if (idleTimer) clearTimeout(idleTimer);
1622
+ if (aborted || timedOut || closeResult !== void 0 || !options.idleTimeoutMs)
1623
+ return;
1624
+ idleTimer = setTimeout(
1625
+ () => terminateTree("timeout"),
1626
+ options.idleTimeoutMs
1627
+ );
1628
+ };
1629
+ const forwardOutput = (chunk) => {
1630
+ if (settled || String(chunk).length === 0) return;
1631
+ output = appendTail(output, chunk, outputLimit);
1632
+ resetIdleTimer();
1633
+ try {
1634
+ options.onOutput?.(String(chunk));
1635
+ } catch {
1636
+ }
1637
+ };
1638
+ resetIdleTimer();
1608
1639
  child.stdout?.on("data", (chunk) => {
1609
1640
  stdout = appendTail(stdout, chunk, outputLimit);
1641
+ forwardOutput(chunk);
1610
1642
  });
1611
1643
  child.stderr?.on("data", (chunk) => {
1612
1644
  stderr = appendTail(stderr, chunk, outputLimit);
1645
+ forwardOutput(chunk);
1613
1646
  });
1614
1647
  child.on("error", (error) => {
1615
1648
  spawnError = error;
1616
1649
  });
1617
1650
  child.on("close", (exitCode) => {
1651
+ if (idleTimer) clearTimeout(idleTimer);
1618
1652
  closeResult = exitCode;
1619
1653
  finishIfComplete();
1620
1654
  });
@@ -4876,6 +4910,34 @@ async function parcelEventToFileEvent(event, rootInfo) {
4876
4910
  };
4877
4911
  }
4878
4912
 
4913
+ // ../tempo-devserver/install-feedback.ts
4914
+ var INSTALL_IDLE_TIMEOUT_MS = 5 * 6e4;
4915
+ var CONNECTION_CODE = /\b(?:ETIMEDOUT|ESOCKETTIMEDOUT|ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ENETUNREACH|EHOSTUNREACH|ERR_SOCKET_TIMEOUT|UND_ERR_CONNECT_TIMEOUT|UND_ERR_SOCKET)\b/;
4916
+ var CERTIFICATE_CODE = /\b(?:CERT_HAS_EXPIRED|CERT_NOT_YET_VALID|SELF_SIGNED_CERT_IN_CHAIN|DEPTH_ZERO_SELF_SIGNED_CERT|UNABLE_TO_VERIFY_LEAF_SIGNATURE|UNABLE_TO_GET_ISSUER_CERT_LOCALLY|ERR_TLS_CERT_ALTNAME_INVALID)\b/;
4917
+ function describeInstallConnectivity(output) {
4918
+ const relevant = output.replace(/\u001b\[[0-9;]*m/g, "").split(/\r?\n/).filter((line) => !/\b(?:warn|warning)\s+deprecated\b/i.test(line)).join("\n");
4919
+ const terminal = [
4920
+ ...relevant.matchAll(/npm (?:error|ERR!) code\s+(\w+)|\bERR_PNPM_(\w+)/g)
4921
+ ].at(-1);
4922
+ const terminalCode = terminal?.[1] ?? terminal?.[2];
4923
+ const failureDetail = terminal?.[2] === "META_FETCH_FAIL" ? relevant.slice(terminal.index) : terminalCode ?? relevant;
4924
+ if (terminalCode && terminalCode !== "META_FETCH_FAIL" && !CONNECTION_CODE.test(terminalCode) && !CERTIFICATE_CODE.test(terminalCode))
4925
+ return null;
4926
+ const certificate = failureDetail.match(CERTIFICATE_CODE)?.[0];
4927
+ if (certificate)
4928
+ return {
4929
+ code: certificate,
4930
+ hint: "Check your proxy or registry certificate trust, then retry."
4931
+ };
4932
+ const connection = failureDetail.match(CONNECTION_CODE)?.[0];
4933
+ if (connection)
4934
+ return {
4935
+ code: connection,
4936
+ hint: "Check your internet connection, VPN, or proxy, then retry."
4937
+ };
4938
+ return null;
4939
+ }
4940
+
4879
4941
  // ../tempo-sdk/package.json
4880
4942
  var package_default = {
4881
4943
  name: "tempo-sdk",
@@ -5554,10 +5616,11 @@ import path8 from "path";
5554
5616
 
5555
5617
  // ../tempo-devserver/errors.ts
5556
5618
  var TempoDevServerError = class extends Error {
5557
- constructor(code, message) {
5619
+ constructor(code, message, recoveryHint) {
5558
5620
  super(message);
5559
5621
  this.code = code;
5560
5622
  this.name = "TempoDevServerError";
5623
+ if (recoveryHint) this.recoveryHint = recoveryHint;
5561
5624
  }
5562
5625
  };
5563
5626
  var ConfigMissingError = class extends TempoDevServerError {
@@ -5577,6 +5640,32 @@ ${output}` : "";
5577
5640
  this.name = "InstallFailedError";
5578
5641
  }
5579
5642
  };
5643
+ var InstallNetworkError = class extends TempoDevServerError {
5644
+ constructor(command, output, problem) {
5645
+ super(
5646
+ "install_network_error",
5647
+ `${problem.hint}
5648
+
5649
+ Package download failed (${problem.code}): ${command}
5650
+ ${output.trim()}`
5651
+ );
5652
+ this.name = "InstallNetworkError";
5653
+ this.recoveryHint = problem.hint;
5654
+ }
5655
+ };
5656
+ var InstallTimeoutError = class extends TempoDevServerError {
5657
+ constructor(command, output) {
5658
+ super(
5659
+ "install_timeout",
5660
+ `Dependency installation produced no output for 5 minutes and was stopped. Check the install logs, your connection, or a waiting install script before retrying.
5661
+
5662
+ Command: ${command}
5663
+ ${output.trim()}`
5664
+ );
5665
+ this.recoveryHint = "No install output for 5 minutes. Check the install logs and your connection, then retry.";
5666
+ this.name = "InstallTimeoutError";
5667
+ }
5668
+ };
5580
5669
  var ToolMissingError = class extends TempoDevServerError {
5581
5670
  constructor(toolName, cwd) {
5582
5671
  super(
@@ -7904,13 +7993,15 @@ function buildUnsatisfiedNodeHint(args) {
7904
7993
  // ../tempo-devserver/sdk-upgrade-plan.ts
7905
7994
  import { access as access3, readFile as readFile7 } from "fs/promises";
7906
7995
  import path11 from "path";
7907
- var LOCKFILES = [
7996
+ var INSTALL_MARKERS = [
7908
7997
  // Order matters, same rationale as pm-detect: a tree carrying both bun.lock
7909
7998
  // and package-lock.json is a bun tree with a stale npm lockfile.
7910
7999
  { file: "bun.lock", pm: "bun" },
7911
8000
  { file: "bun.lockb", pm: "bun" },
7912
8001
  { file: "pnpm-lock.yaml", pm: "pnpm" },
7913
8002
  { file: "yarn.lock", pm: "yarn" },
8003
+ // A pnpm workspace owns its install even before its first lockfile exists.
8004
+ { file: "pnpm-workspace.yaml", pm: "pnpm" },
7914
8005
  { file: "package-lock.json", pm: "npm" }
7915
8006
  ];
7916
8007
  async function fileExists2(filePath) {
@@ -7932,11 +8023,11 @@ function parsePackageManagerField2(value) {
7932
8023
  function isYarnBerryLockfile(contents) {
7933
8024
  return /^__metadata:/m.test(contents);
7934
8025
  }
7935
- function buildCommands(pm, yarnBerry, metadataCacheDir, scopedToTempoRoot) {
8026
+ function buildCommands(pm, yarnBerry, metadataCacheDir, ignorePnpmWorkspace) {
7936
8027
  switch (pm) {
7937
8028
  case "pnpm": {
7938
8029
  const cacheFlag = metadataCacheDir ? ` --config.cacheDir=${metadataCacheDir}` : "";
7939
- const workspaceFlag = scopedToTempoRoot ? " --ignore-workspace" : "";
8030
+ const workspaceFlag = ignorePnpmWorkspace ? " --ignore-workspace" : "";
7940
8031
  return {
7941
8032
  updateCommand: `pnpm update tempo-sdk --latest${workspaceFlag}${cacheFlag}`,
7942
8033
  remediationCommand: `pnpm update tempo-sdk --latest${workspaceFlag}${cacheFlag}`
@@ -7971,9 +8062,10 @@ async function resolveTempoSdkUpgradePlan(tempoRoot, options = {}) {
7971
8062
  const parsed = JSON.parse(raw);
7972
8063
  return parsePackageManagerField2(parsed.packageManager);
7973
8064
  }).catch(() => null);
7974
- const owner = await findLockfileOwner(resolvedRoot);
8065
+ const owner = await findInstallOwner(resolvedRoot);
7975
8066
  const resolvedPm = declared ?? owner?.pm ?? "npm";
7976
8067
  const governedByAncestorWorkspace = owner !== null && owner.dir !== resolvedRoot;
8068
+ const hasOwnPnpmWorkspace = resolvedPm === "pnpm" && await fileExists2(path11.join(resolvedRoot, "pnpm-workspace.yaml"));
7977
8069
  return {
7978
8070
  packageManager: resolvedPm,
7979
8071
  cwd: resolvedRoot,
@@ -7982,20 +8074,20 @@ async function resolveTempoSdkUpgradePlan(tempoRoot, options = {}) {
7982
8074
  resolvedPm,
7983
8075
  owner?.yarnLockContents !== void 0 && isYarnBerryLockfile(owner.yarnLockContents),
7984
8076
  options.metadataCacheDir ?? null,
7985
- !governedByAncestorWorkspace
8077
+ !governedByAncestorWorkspace && !hasOwnPnpmWorkspace
7986
8078
  )
7987
8079
  };
7988
8080
  }
7989
- async function findLockfileOwner(startDir) {
8081
+ async function findInstallOwner(startDir) {
7990
8082
  for (let dir = startDir; ; dir = path11.dirname(dir)) {
7991
- for (const candidate of LOCKFILES) {
7992
- const lockPath = path11.join(dir, candidate.file);
7993
- if (!await fileExists2(lockPath)) continue;
8083
+ for (const candidate of INSTALL_MARKERS) {
8084
+ const markerPath = path11.join(dir, candidate.file);
8085
+ if (!await fileExists2(markerPath)) continue;
7994
8086
  return {
7995
8087
  dir,
7996
8088
  pm: candidate.pm,
7997
8089
  ...candidate.pm === "yarn" ? {
7998
- yarnLockContents: await readFile7(lockPath, "utf-8").catch(
8090
+ yarnLockContents: await readFile7(markerPath, "utf-8").catch(
7999
8091
  () => ""
8000
8092
  )
8001
8093
  } : {}
@@ -9345,7 +9437,8 @@ var TempoDevServer = class _TempoDevServer {
9345
9437
  if (options?.forceInstall || !this.state.dependenciesInstalled) {
9346
9438
  await this.installDependenciesForStart(
9347
9439
  options?.forceInstall === true,
9348
- options?.signal
9440
+ options?.signal,
9441
+ options?.onInstallOutput
9349
9442
  );
9350
9443
  }
9351
9444
  throwIfLaunchAborted(options?.signal);
@@ -9379,7 +9472,12 @@ var TempoDevServer = class _TempoDevServer {
9379
9472
  }
9380
9473
  await invalidateInstallCompletionMarkers(root);
9381
9474
  }
9382
- await runShellCommand(command, cwd, signal);
9475
+ await runShellCommand(
9476
+ command,
9477
+ cwd,
9478
+ signal,
9479
+ options?.onInstallOutput
9480
+ );
9383
9481
  sdkCommandCompleted = true;
9384
9482
  }
9385
9483
  }
@@ -9586,7 +9684,7 @@ var TempoDevServer = class _TempoDevServer {
9586
9684
  }
9587
9685
  };
9588
9686
  }
9589
- async installDependenciesForStart(forceInstall, signal) {
9687
+ async installDependenciesForStart(forceInstall, signal, onOutput) {
9590
9688
  const installTimer = devTimer("supervisor:install", {
9591
9689
  tempoRoot: this.tempoRoot,
9592
9690
  commandCwd: this.commandCwd,
@@ -9604,7 +9702,7 @@ var TempoDevServer = class _TempoDevServer {
9604
9702
  try {
9605
9703
  for (const step of steps) {
9606
9704
  throwIfLaunchAborted(signal);
9607
- await runInstallStep(step, signal);
9705
+ await runInstallStep(step, signal, onOutput);
9608
9706
  }
9609
9707
  } catch (error) {
9610
9708
  throwIfLaunchAborted(signal);
@@ -9617,7 +9715,7 @@ var TempoDevServer = class _TempoDevServer {
9617
9715
  );
9618
9716
  for (const step of localSteps) {
9619
9717
  throwIfLaunchAborted(signal);
9620
- await runInstallStep(step, signal);
9718
+ await runInstallStep(step, signal, onOutput);
9621
9719
  }
9622
9720
  }
9623
9721
  };
@@ -11490,6 +11588,9 @@ function classifyInstallFailure(command, output) {
11490
11588
  if (INSTALL_LOCK_PATTERN.test(output)) {
11491
11589
  return new InstallPermissionDeniedError(command, output);
11492
11590
  }
11591
+ const connectivity = describeInstallConnectivity(output);
11592
+ if (connectivity)
11593
+ return new InstallNetworkError(command, output, connectivity);
11493
11594
  return null;
11494
11595
  }
11495
11596
  function isIgnoredBuildScriptsOnlyFailure(output) {
@@ -11503,6 +11604,7 @@ async function runInstallCommandWithLockRetry(command, cwd, runCommandFn = runSh
11503
11604
  await runCommandFn(command, cwd, signal);
11504
11605
  return;
11505
11606
  } catch (error) {
11607
+ if (error instanceof InstallTimeoutError) throw error;
11506
11608
  const output = error instanceof Error ? error.message : String(error);
11507
11609
  const delay = retryDelaysMs[attempt];
11508
11610
  if (delay === void 0 || !isTransientFsLockOutput(output)) throw error;
@@ -11511,7 +11613,7 @@ async function runInstallCommandWithLockRetry(command, cwd, runCommandFn = runSh
11511
11613
  }
11512
11614
  }
11513
11615
  }
11514
- async function runInstallStep(step, signal) {
11616
+ async function runInstallStep(step, signal, onOutput) {
11515
11617
  throwIfLaunchAborted(signal);
11516
11618
  await Promise.all(
11517
11619
  [
@@ -11538,12 +11640,13 @@ async function runInstallStep(step, signal) {
11538
11640
  await runInstallCommandWithLockRetry(
11539
11641
  step.command,
11540
11642
  step.cwd,
11541
- runShellCommand,
11643
+ (command, cwd, signal2) => runShellCommand(command, cwd, signal2, onOutput),
11542
11644
  INSTALL_LOCK_RETRY_DELAYS_MS,
11543
11645
  signal
11544
11646
  );
11545
11647
  } catch (error) {
11546
11648
  throwIfLaunchAborted(signal);
11649
+ if (error instanceof InstallTimeoutError) throw error;
11547
11650
  const output = error instanceof Error ? error.message : String(error);
11548
11651
  if (isIgnoredBuildScriptsOnlyFailure(output) && await nodeModulesHasMeaningfulEntries(step.cwd)) {
11549
11652
  console.warn(
@@ -12011,7 +12114,7 @@ async function runBoundedShellCommand(options) {
12011
12114
  );
12012
12115
  });
12013
12116
  }
12014
- async function runShellCommand(command, cwd, signal) {
12117
+ async function runShellCommand(command, cwd, signal, onOutput) {
12015
12118
  throwIfLaunchAborted(signal);
12016
12119
  const projectNode = toProjectNodeEnvOption(
12017
12120
  await resolveProjectNodeOverrideForDir(cwd)
@@ -12021,12 +12124,15 @@ async function runShellCommand(command, cwd, signal) {
12021
12124
  command: lifted.command,
12022
12125
  cwd,
12023
12126
  signal,
12024
- env: { ...buildManagedCommandEnv({ projectNode }), ...lifted.env }
12127
+ env: { ...buildManagedCommandEnv({ projectNode }), ...lifted.env },
12128
+ onOutput,
12129
+ idleTimeoutMs: INSTALL_IDLE_TIMEOUT_MS
12025
12130
  });
12026
12131
  if (result.aborted) throw launchAbortError();
12132
+ if (result.timedOut) throw new InstallTimeoutError(command, result.output);
12027
12133
  if (result.spawnError) throw result.spawnError;
12028
12134
  if (result.exitCode === 0) return;
12029
- const output = (result.stderr.trim() || result.stdout.trim()).slice(0, 4e3);
12135
+ const output = result.output.trim();
12030
12136
  throw new InstallFailedError(command, result.exitCode, output);
12031
12137
  }
12032
12138
  async function ensureTempoSdkLatestSpec(cwd, signal, options = {}) {
@@ -14067,4 +14173,4 @@ export {
14067
14173
  buildBaselineCaptureArgs,
14068
14174
  main
14069
14175
  };
14070
- //# sourceMappingURL=chunk-RY7ZWRV5.js.map
14176
+ //# sourceMappingURL=chunk-BXSN22AO.js.map