recappi 0.1.61 → 0.1.63

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
@@ -18588,7 +18588,12 @@ function isNodeErrorCode(error51, code) {
18588
18588
  }
18589
18589
  async function readDurationMs(filePath, contentType) {
18590
18590
  if (contentType === "audio/wav") {
18591
- const handle = await fs2.open(filePath, "r");
18591
+ let handle;
18592
+ try {
18593
+ handle = await fs2.open(filePath, "r");
18594
+ } catch (error51) {
18595
+ throwReadablePathError(filePath, error51);
18596
+ }
18592
18597
  try {
18593
18598
  const buffer = Buffer.alloc(4096);
18594
18599
  const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
@@ -18603,7 +18608,10 @@ async function readDurationMs(filePath, contentType) {
18603
18608
  if (typeof metadata.format.duration === "number" && Number.isFinite(metadata.format.duration)) {
18604
18609
  return Math.max(1, Math.round(metadata.format.duration * 1e3));
18605
18610
  }
18606
- } catch {
18611
+ } catch (error51) {
18612
+ if (isPermissionDenied(error51)) {
18613
+ throwPermissionDenied(filePath);
18614
+ }
18607
18615
  throw cliError(
18608
18616
  "input.duration_unavailable",
18609
18617
  `Could not read duration for non-WAV file: ${filePath}`,
@@ -18620,6 +18628,28 @@ async function readDurationMs(filePath, contentType) {
18620
18628
  }
18621
18629
  );
18622
18630
  }
18631
+ function throwReadablePathError(filePath, error51) {
18632
+ if (isPermissionDenied(error51)) {
18633
+ throwPermissionDenied(filePath);
18634
+ }
18635
+ throw error51;
18636
+ }
18637
+ function throwPermissionDenied(filePath) {
18638
+ throw cliError("input.permission_denied", `Permission denied reading path: ${filePath}`, {
18639
+ hint: "Grant this terminal/agent access to the file, or copy the audio to a readable location."
18640
+ });
18641
+ }
18642
+ function isPermissionDenied(error51) {
18643
+ if (isNodeErrorCode(error51, "EACCES") || isNodeErrorCode(error51, "EPERM")) return true;
18644
+ if (typeof error51 === "object" && error51 !== null && "cause" in error51) {
18645
+ const cause = error51.cause;
18646
+ if (cause && cause !== error51 && isPermissionDenied(cause)) return true;
18647
+ }
18648
+ if (error51 instanceof Error) {
18649
+ return /(?:EACCES|EPERM|permission denied|operation not permitted)/i.test(error51.message);
18650
+ }
18651
+ return false;
18652
+ }
18623
18653
 
18624
18654
  // src/store.ts
18625
18655
  import { mkdirSync } from "fs";
@@ -19478,14 +19508,27 @@ var RecappiApiClient = class {
19478
19508
  }
19479
19509
  async request(method, pathname, body, opts = {}) {
19480
19510
  const token = requireToken(this.auth);
19481
- const response = await this.fetchImpl(new URL(pathname, this.auth.origin), {
19482
- method,
19483
- headers: {
19484
- authorization: `Bearer ${token}`,
19485
- ...opts.headers
19486
- },
19487
- ...body ? { body } : {}
19488
- });
19511
+ let response;
19512
+ try {
19513
+ response = await this.fetchImpl(new URL(pathname, this.auth.origin), {
19514
+ method,
19515
+ headers: {
19516
+ authorization: `Bearer ${token}`,
19517
+ ...opts.headers
19518
+ },
19519
+ ...body ? { body } : {}
19520
+ });
19521
+ } catch (error51) {
19522
+ if (error51 instanceof RecappiCliError) throw error51;
19523
+ throw cliError(
19524
+ "cloud.http_error",
19525
+ `Recappi Cloud request failed: ${transportErrorMessage(error51)}`,
19526
+ {
19527
+ retryable: true,
19528
+ hint: `Check your network connection and Recappi Cloud origin (${this.auth.origin}), then retry.`
19529
+ }
19530
+ );
19531
+ }
19489
19532
  if (!response.ok && !(opts.allowAuthFailure && (response.status === 401 || response.status === 403))) {
19490
19533
  const message = await responseMessage(response);
19491
19534
  throw new RecappiCliError(describeHttpError(response.status, message));
@@ -19493,6 +19536,10 @@ var RecappiApiClient = class {
19493
19536
  return response;
19494
19537
  }
19495
19538
  };
19539
+ function transportErrorMessage(error51) {
19540
+ if (error51 instanceof Error && error51.message) return error51.message;
19541
+ return String(error51 || "network request failed");
19542
+ }
19496
19543
  async function parseJson(response) {
19497
19544
  try {
19498
19545
  return await response.json();
@@ -20236,8 +20283,11 @@ function renderFailure(command, error51, opts, data) {
20236
20283
  for (const item of data.failures) {
20237
20284
  const label = humanFileLabel(item.filePath) ?? item.filePath;
20238
20285
  opts.stderr(` ${label}: ${item.error.message} (${item.error.code})
20286
+ `);
20287
+ if (item.error.hint) opts.stderr(` ${item.error.hint}
20239
20288
  `);
20240
20289
  }
20290
+ return;
20241
20291
  }
20242
20292
  if (error51.hint) opts.stderr(`${error51.hint}
20243
20293
  `);