azdo-cli 0.19.0-feature-037-api-error-surfacing.693 → 0.19.0-feature-037-api-error-surfacing.692

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 +57 -182
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -326,52 +326,6 @@ async function fetchRaw(url, init) {
326
326
  const body = await response.text();
327
327
  return { status: response.status, body };
328
328
  }
329
- var MAX_DETAIL_CHARS = 500;
330
- var MAX_RAW_BODY_CHARS = 200;
331
- var failureDetails = /* @__PURE__ */ new WeakMap();
332
- function describeFailureBody(body, contentType) {
333
- if (body === null) return null;
334
- const trimmed = body.trim();
335
- if (trimmed === "") return null;
336
- if (contentType.toLowerCase().startsWith("text/html") || trimmed.startsWith("<")) {
337
- return null;
338
- }
339
- const redacted = redactBody(trimmed) ?? trimmed;
340
- let parsed;
341
- try {
342
- parsed = JSON.parse(redacted);
343
- } catch {
344
- return truncateDetail(redacted.slice(0, MAX_RAW_BODY_CHARS));
345
- }
346
- if (typeof parsed !== "object" || parsed === null) {
347
- return truncateDetail(redacted.slice(0, MAX_RAW_BODY_CHARS));
348
- }
349
- const record = parsed;
350
- const parts = [];
351
- if (typeof record.message === "string" && record.message.trim() !== "") {
352
- parts.push(record.message.trim());
353
- }
354
- if (typeof record.typeKey === "string" && record.typeKey.trim() !== "") {
355
- parts.push(`[${record.typeKey.trim()}]`);
356
- } else if (typeof record.errorCode === "number" || typeof record.errorCode === "string") {
357
- parts.push(`[errorCode ${record.errorCode}]`);
358
- }
359
- if (parts.length === 0) {
360
- return truncateDetail(redacted.slice(0, MAX_RAW_BODY_CHARS));
361
- }
362
- return truncateDetail(parts.join(" "));
363
- }
364
- function truncateDetail(detail) {
365
- const collapsed = detail.replace(/\s+/g, " ").trim();
366
- if (collapsed === "") return null;
367
- return collapsed.length > MAX_DETAIL_CHARS ? `${collapsed.slice(0, MAX_DETAIL_CHARS)}\u2026(truncated)` : collapsed;
368
- }
369
- function withDetail(sentinel, detail) {
370
- return detail === null ? sentinel : `${sentinel}: ${detail}`;
371
- }
372
- function httpError(response) {
373
- return new Error(withDetail(`HTTP_${response.status}`, failureDetails.get(response) ?? null));
374
- }
375
329
  async function fetchWithErrors(url, init) {
376
330
  const writer = getActiveTraceWriter();
377
331
  let response;
@@ -380,7 +334,6 @@ async function fetchWithErrors(url, init) {
380
334
  } catch (err) {
381
335
  throw new Error("NETWORK_ERROR", { cause: err });
382
336
  }
383
- let tracedBody = null;
384
337
  if (writer) {
385
338
  const reqHeaders = redactHeaders(init.headers ?? {});
386
339
  const reqBody = typeof init.body === "string" ? redactBody(init.body) : null;
@@ -390,7 +343,6 @@ async function fetchWithErrors(url, init) {
390
343
  responseBody = await clone.text();
391
344
  } catch {
392
345
  }
393
- tracedBody = responseBody;
394
346
  const respHeaders = {};
395
347
  response.headers.forEach((v, k) => {
396
348
  respHeaders[k] = v;
@@ -407,33 +359,18 @@ async function fetchWithErrors(url, init) {
407
359
  };
408
360
  writer.append(entry);
409
361
  }
410
- const contentType = response.headers?.get("content-type") ?? "";
411
- let failureDetail = null;
412
- if (!response.ok) {
413
- let body = tracedBody;
414
- if (body === null) {
415
- try {
416
- body = await response.clone().text();
417
- } catch {
418
- body = null;
419
- }
420
- }
421
- failureDetail = describeFailureBody(body, contentType);
422
- if (failureDetail !== null) {
423
- failureDetails.set(response, failureDetail);
424
- }
425
- }
426
- if (response.status === 401) throw new Error(withDetail("AUTH_FAILED", failureDetail));
427
- if (response.status === 403) throw new Error(withDetail("PERMISSION_DENIED", failureDetail));
362
+ if (response.status === 401) throw new Error("AUTH_FAILED");
363
+ if (response.status === 403) throw new Error("PERMISSION_DENIED");
428
364
  if (response.status === 404) {
429
365
  let detail = "";
430
366
  try {
431
- const body = tracedBody ?? await response.text();
367
+ const body = await response.text();
432
368
  detail = ` | url=${url} | body=${body}`;
433
369
  } catch {
434
370
  }
435
371
  throw new Error(`NOT_FOUND${detail}`);
436
372
  }
373
+ const contentType = response.headers?.get("content-type") ?? "";
437
374
  if (contentType.toLowerCase().startsWith("text/html")) {
438
375
  throw new Error("AUTH_FAILED");
439
376
  }
@@ -527,7 +464,7 @@ async function readWriteResponse(response, errorCode) {
527
464
  throw new Error(`${errorCode}: ${serverMessage}`);
528
465
  }
529
466
  if (!response.ok) {
530
- throw httpError(response);
467
+ throw new Error(`HTTP_${response.status}`);
531
468
  }
532
469
  const data = await response.json();
533
470
  return {
@@ -550,7 +487,7 @@ async function getWorkItemFields(context, id, cred) {
550
487
  }
551
488
  }
552
489
  if (!response.ok) {
553
- throw httpError(response);
490
+ throw new Error(`HTTP_${response.status}`);
554
491
  }
555
492
  const data = await response.json();
556
493
  return data.fields;
@@ -602,7 +539,7 @@ async function fetchWorkItemResponse(context, id, cred, options = {}) {
602
539
  }
603
540
  }
604
541
  if (!response.ok) {
605
- throw httpError(response);
542
+ throw new Error(`HTTP_${response.status}`);
606
543
  }
607
544
  return await response.json();
608
545
  }
@@ -613,7 +550,7 @@ async function getOrgFieldNames(context, cred) {
613
550
  url.searchParams.set("api-version", "7.1");
614
551
  const response = await fetchWithErrors(url.toString(), { headers: authHeaders(cred) });
615
552
  if (!response.ok) {
616
- throw httpError(response);
553
+ throw new Error(`HTTP_${response.status}`);
617
554
  }
618
555
  const data = await response.json();
619
556
  return (data.value ?? []).map((f) => f.referenceName);
@@ -695,7 +632,7 @@ async function getWorkItemFieldValue(context, id, cred, fieldName) {
695
632
  }
696
633
  }
697
634
  if (!response.ok) {
698
- throw httpError(response);
635
+ throw new Error(`HTTP_${response.status}`);
699
636
  }
700
637
  const data = await response.json();
701
638
  const value = data.fields[fieldName];
@@ -713,7 +650,7 @@ async function listWorkItemComments(context, id, cred) {
713
650
  { headers: authHeaders(cred) }
714
651
  );
715
652
  if (!response.ok) {
716
- throw httpError(response);
653
+ throw new Error(`HTTP_${response.status}`);
717
654
  }
718
655
  const data = await response.json();
719
656
  comments.push(
@@ -743,7 +680,7 @@ async function addWorkItemComment(context, id, cred, text, format = "html") {
743
680
  throw new Error(`BAD_REQUEST: ${serverMessage}`);
744
681
  }
745
682
  if (!response.ok) {
746
- throw httpError(response);
683
+ throw new Error(`HTTP_${response.status}`);
747
684
  }
748
685
  const data = await response.json();
749
686
  return {
@@ -795,7 +732,7 @@ async function applyWorkItemPatch(context, id, cred, operations) {
795
732
  async function downloadAttachment(url, cred) {
796
733
  const response = await fetchWithErrors(url, { headers: authHeaders(cred) });
797
734
  if (!response.ok) {
798
- throw httpError(response);
735
+ throw new Error(`HTTP_${response.status}`);
799
736
  }
800
737
  return response.arrayBuffer();
801
738
  }
@@ -820,7 +757,7 @@ async function createAttachment(context, fileName, content, cred) {
820
757
  }
821
758
  }
822
759
  if (!response.ok) {
823
- throw httpError(response);
760
+ throw new Error(`HTTP_${response.status}`);
824
761
  }
825
762
  return await response.json();
826
763
  }
@@ -1610,23 +1547,6 @@ function toMarkdown(content) {
1610
1547
  }
1611
1548
 
1612
1549
  // src/services/command-helpers.ts
1613
- function isSentinel(message, sentinel) {
1614
- return message === sentinel || message.startsWith(`${sentinel}: `);
1615
- }
1616
- function sentinelDetail(message, sentinel) {
1617
- return message.startsWith(`${sentinel}: `) ? message.slice(sentinel.length + 2) : null;
1618
- }
1619
- function splitSentinel(message) {
1620
- const separator = message.indexOf(": ");
1621
- return separator === -1 ? { sentinel: message, detail: null } : { sentinel: message.slice(0, separator), detail: message.slice(separator + 2) };
1622
- }
1623
- function writeErrorDetail(message, sentinel) {
1624
- const detail = sentinelDetail(message, sentinel);
1625
- if (detail !== null) {
1626
- process.stderr.write(` ${detail}
1627
- `);
1628
- }
1629
- }
1630
1550
  async function promptYesNo(prompt) {
1631
1551
  if (!process.stdin.isTTY) return true;
1632
1552
  process.stderr.write(prompt);
@@ -1688,18 +1608,16 @@ function handleCommandError(err, id, context, scope = "write", exit = true) {
1688
1608
  const error = err instanceof Error ? err : new Error(String(err));
1689
1609
  const msg = error.message;
1690
1610
  const scopeLabel = scope === "read" ? "Work Items (read)" : "Work Items (Read & Write)";
1691
- if (isSentinel(msg, "AUTH_FAILED")) {
1611
+ if (msg === "AUTH_FAILED") {
1692
1612
  process.stderr.write(
1693
1613
  `Error: Authentication failed. Check that your PAT is valid and has the "${scopeLabel}" scope.
1694
1614
  `
1695
1615
  );
1696
- writeErrorDetail(msg, "AUTH_FAILED");
1697
- } else if (isSentinel(msg, "PERMISSION_DENIED")) {
1616
+ } else if (msg === "PERMISSION_DENIED") {
1698
1617
  process.stderr.write(
1699
1618
  `Error: Access denied. Your PAT may lack ${scope} permissions for project "${context?.project}".
1700
1619
  `
1701
1620
  );
1702
- writeErrorDetail(msg, "PERMISSION_DENIED");
1703
1621
  } else if (msg.startsWith("NOT_FOUND")) {
1704
1622
  process.stderr.write(
1705
1623
  `Error: Work item ${id} not found in ${context?.org}/${context?.project}.
@@ -3214,10 +3132,10 @@ function buildUpsertResult(action, writeResult, fields, fallbackWorkItemType) {
3214
3132
  };
3215
3133
  }
3216
3134
  function isUpdateWriteError(err) {
3217
- return isSentinel(err.message, "AUTH_FAILED") || isSentinel(err.message, "PERMISSION_DENIED") || err.message.startsWith("NOT_FOUND") || err.message === "NETWORK_ERROR" || err.message.startsWith("BAD_REQUEST:") || err.message.startsWith("UPDATE_REJECTED:");
3135
+ return err.message === "AUTH_FAILED" || err.message === "PERMISSION_DENIED" || err.message.startsWith("NOT_FOUND") || err.message === "NETWORK_ERROR" || err.message.startsWith("BAD_REQUEST:") || err.message.startsWith("UPDATE_REJECTED:");
3218
3136
  }
3219
3137
  function isCreateWriteError(err) {
3220
- return isSentinel(err.message, "AUTH_FAILED") || isSentinel(err.message, "PERMISSION_DENIED") || err.message === "NETWORK_ERROR" || err.message.startsWith("BAD_REQUEST:") || err.message.startsWith("HTTP_");
3138
+ return err.message === "AUTH_FAILED" || err.message === "PERMISSION_DENIED" || err.message === "NETWORK_ERROR" || err.message.startsWith("BAD_REQUEST:") || err.message.startsWith("HTTP_");
3221
3139
  }
3222
3140
  function handleUpsertError(err, id, context) {
3223
3141
  if (!(err instanceof Error)) {
@@ -3547,7 +3465,7 @@ function isThreadResolved(status2) {
3547
3465
  }
3548
3466
  async function readJsonResponse(response) {
3549
3467
  if (!response.ok) {
3550
- throw httpError(response);
3468
+ throw new Error(`HTTP_${response.status}`);
3551
3469
  }
3552
3470
  return response.json();
3553
3471
  }
@@ -3634,32 +3552,19 @@ async function getPullRequestBuilds(context, cred, prId) {
3634
3552
  isBlocking: null
3635
3553
  }));
3636
3554
  }
3637
- var MAX_PR_DESCRIPTION_CHARS = 4e3;
3638
- var DESCRIPTION_SEPARATOR = "\n\n";
3639
3555
  function composeDescription(description, template) {
3640
- if (description === void 0 && template === null) {
3641
- return null;
3556
+ if (description !== void 0 && template !== null) {
3557
+ return `${description}
3558
+
3559
+ ${template.content}`;
3642
3560
  }
3643
- const provided = description ?? "";
3644
- const templateContent = template?.content ?? "";
3645
- const separator = description !== void 0 && template !== null ? DESCRIPTION_SEPARATOR : "";
3646
- const text = `${provided}${separator}${templateContent}`;
3647
- return {
3648
- text,
3649
- providedChars: provided.length,
3650
- separatorChars: separator.length,
3651
- templateChars: templateContent.length,
3652
- templatePath: template?.path ?? null,
3653
- totalChars: text.length
3654
- };
3655
- }
3656
- function formatDescriptionOverflow(composed) {
3657
- const overflow = composed.totalChars - MAX_PR_DESCRIPTION_CHARS;
3658
- const breakdown = composed.templateChars > 0 && composed.providedChars > 0 ? ` (${composed.providedChars} provided + ${composed.separatorChars} separator + ${composed.templateChars} from the repository pull request template ${composed.templatePath})` : composed.templateChars > 0 ? ` (all of it from the repository pull request template ${composed.templatePath})` : "";
3659
- return `description is ${composed.totalChars} characters${breakdown}, exceeding the Azure DevOps limit of ${MAX_PR_DESCRIPTION_CHARS} characters. Shorten the description by at least ${overflow} characters.`;
3660
- }
3661
- function describeDescriptionBudget(composed) {
3662
- return `description: ${composed.providedChars} provided + ${composed.separatorChars} separator + ${composed.templateChars} template = ${composed.totalChars} characters (client limit ${MAX_PR_DESCRIPTION_CHARS})`;
3561
+ if (description !== void 0) {
3562
+ return description;
3563
+ }
3564
+ if (template !== null) {
3565
+ return template.content;
3566
+ }
3567
+ return null;
3663
3568
  }
3664
3569
  async function openPullRequest(context, repo, cred, sourceBranch, title, description) {
3665
3570
  const existing = await listPullRequests(context, repo, cred, sourceBranch, {
@@ -3680,40 +3585,29 @@ async function openPullRequest(context, repo, cred, sourceBranch, title, descrip
3680
3585
  const repository = await getRepository(context, repo, cred);
3681
3586
  const defaultBranch = repository.defaultBranch ? repository.defaultBranch.replace(/^refs\/heads\//, "") : "develop";
3682
3587
  const template = await resolvePullRequestTemplate(context, repo, cred, defaultBranch, "develop");
3683
- const composed = composeDescription(description, template);
3684
- if (composed === null) {
3588
+ const finalDescription = composeDescription(description, template);
3589
+ if (finalDescription === null) {
3685
3590
  throw new Error("DESCRIPTION_REQUIRED");
3686
3591
  }
3687
- if (composed.totalChars > MAX_PR_DESCRIPTION_CHARS) {
3688
- throw new Error(`DESCRIPTION_TOO_LONG: ${formatDescriptionOverflow(composed)}`);
3689
- }
3690
3592
  const payload = {
3691
3593
  sourceRefName: `refs/heads/${sourceBranch}`,
3692
3594
  targetRefName: "refs/heads/develop",
3693
3595
  title,
3694
- description: composed.text
3596
+ description: finalDescription
3695
3597
  };
3696
3598
  const url = new URL(
3697
3599
  `https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/git/repositories/${encodeURIComponent(repo)}/pullrequests`
3698
3600
  );
3699
3601
  url.searchParams.set("api-version", "7.1");
3700
- let data;
3701
- try {
3702
- const response = await fetchWithErrors(url.toString(), {
3703
- method: "POST",
3704
- headers: {
3705
- ...authHeaders(cred),
3706
- "Content-Type": "application/json"
3707
- },
3708
- body: JSON.stringify(payload)
3709
- });
3710
- data = await readJsonResponse(response);
3711
- } catch (err) {
3712
- if (err instanceof Error && err.message.startsWith("HTTP_400")) {
3713
- throw new Error(`${err.message} | ${describeDescriptionBudget(composed)}`, { cause: err });
3714
- }
3715
- throw err;
3716
- }
3602
+ const response = await fetchWithErrors(url.toString(), {
3603
+ method: "POST",
3604
+ headers: {
3605
+ ...authHeaders(cred),
3606
+ "Content-Type": "application/json"
3607
+ },
3608
+ body: JSON.stringify(payload)
3609
+ });
3610
+ const data = await readJsonResponse(response);
3717
3611
  return {
3718
3612
  branch: sourceBranch,
3719
3613
  targetBranch: "develop",
@@ -3851,7 +3745,7 @@ async function patchWorkItemRelations(context, cred, workItemId, operation) {
3851
3745
  body: JSON.stringify([operation])
3852
3746
  });
3853
3747
  if (!response.ok) {
3854
- throw httpError(response);
3748
+ throw new Error(`HTTP_${response.status}`);
3855
3749
  }
3856
3750
  }
3857
3751
  async function linkWorkItemToPullRequest(context, repo, cred, prId, workItemId) {
@@ -3900,7 +3794,7 @@ async function resolveReviewerIdentity(org, cred, input) {
3900
3794
  headers: authHeaders(cred)
3901
3795
  });
3902
3796
  } catch (err) {
3903
- if (err instanceof Error && isSentinel(err.message, "AUTH_FAILED")) {
3797
+ if (err instanceof Error && err.message === "AUTH_FAILED") {
3904
3798
  throw new Error("IDENTITY_SCOPE_MISSING", { cause: err });
3905
3799
  }
3906
3800
  throw err;
@@ -3959,7 +3853,7 @@ async function removePullRequestReviewer(context, repo, cred, prId, reviewerId)
3959
3853
  headers: authHeaders(cred)
3960
3854
  });
3961
3855
  if (!response.ok) {
3962
- throw httpError(response);
3856
+ throw new Error(`HTTP_${response.status}`);
3963
3857
  }
3964
3858
  return { reviewer: existing, noop: false };
3965
3859
  }
@@ -3998,7 +3892,7 @@ async function fetchRepositoryItemContent(context, repo, cred, path3, branch) {
3998
3892
  throw err;
3999
3893
  }
4000
3894
  if (!response.ok) {
4001
- throw httpError(response);
3895
+ throw new Error(`HTTP_${response.status}`);
4002
3896
  }
4003
3897
  return response.text();
4004
3898
  }
@@ -4133,7 +4027,7 @@ function writeError(message, exitCode = 1) {
4133
4027
  }
4134
4028
  function handlePrCommandError(err, context, mode = "read") {
4135
4029
  const error = err instanceof Error ? err : new Error(String(err));
4136
- if (isSentinel(error.message, "AUTH_FAILED")) {
4030
+ if (error.message === "AUTH_FAILED") {
4137
4031
  const scopeLabel = mode === "write" ? "Code (Read & Write)" : "Code (Read)";
4138
4032
  writeError(`Authentication failed. Check that your PAT is valid and has the "${scopeLabel}" scope.`, EXIT_NOT_PERMITTED);
4139
4033
  const credentialHint = describeResolvedCredential();
@@ -4141,7 +4035,6 @@ function handlePrCommandError(err, context, mode = "read") {
4141
4035
  process.stderr.write(` ${credentialHint}
4142
4036
  `);
4143
4037
  }
4144
- writeErrorDetail(error.message, "AUTH_FAILED");
4145
4038
  return;
4146
4039
  }
4147
4040
  if (error.message === "IDENTITY_SCOPE_MISSING") {
@@ -4151,9 +4044,8 @@ function handlePrCommandError(err, context, mode = "read") {
4151
4044
  );
4152
4045
  return;
4153
4046
  }
4154
- if (isSentinel(error.message, "PERMISSION_DENIED")) {
4047
+ if (error.message === "PERMISSION_DENIED") {
4155
4048
  writeError(`Access denied. Your PAT may lack ${mode} permissions for project "${context?.project}".`, EXIT_NOT_PERMITTED);
4156
- writeErrorDetail(error.message, "PERMISSION_DENIED");
4157
4049
  return;
4158
4050
  }
4159
4051
  if (error.message === "NETWORK_ERROR") {
@@ -4165,12 +4057,7 @@ function handlePrCommandError(err, context, mode = "read") {
4165
4057
  return;
4166
4058
  }
4167
4059
  if (error.message.startsWith("HTTP_")) {
4168
- const { sentinel, detail } = splitSentinel(error.message);
4169
- writeError(`Azure DevOps request failed with ${sentinel}.`);
4170
- if (detail !== null) {
4171
- process.stderr.write(` ${detail}
4172
- `);
4173
- }
4060
+ writeError(`Azure DevOps request failed with ${error.message}.`);
4174
4061
  return;
4175
4062
  }
4176
4063
  writeError(error.message);
@@ -4402,10 +4289,6 @@ ${result.pullRequest.url ?? "\u2014"}
4402
4289
  writeError("--description is required for pull request creation.");
4403
4290
  return;
4404
4291
  }
4405
- if (err instanceof Error && err.message.startsWith("DESCRIPTION_TOO_LONG: ")) {
4406
- writeError(err.message.slice("DESCRIPTION_TOO_LONG: ".length));
4407
- return;
4408
- }
4409
4292
  handlePrCommandError(err, context, "write");
4410
4293
  }
4411
4294
  });
@@ -5215,7 +5098,7 @@ function withApiVersion(url) {
5215
5098
  }
5216
5099
  async function readJsonResponse2(response) {
5217
5100
  if (!response.ok) {
5218
- throw httpError(response);
5101
+ throw new Error(`HTTP_${response.status}`);
5219
5102
  }
5220
5103
  return await response.json();
5221
5104
  }
@@ -5491,7 +5374,7 @@ async function getRunLog(context, cred, buildId, logId) {
5491
5374
  );
5492
5375
  const response = await fetchWithErrors(url.toString(), { headers: authHeaders(cred) });
5493
5376
  if (!response.ok) {
5494
- throw httpError(response);
5377
+ throw new Error(`HTTP_${response.status}`);
5495
5378
  }
5496
5379
  return response.text();
5497
5380
  }
@@ -5507,14 +5390,12 @@ function writeError2(message) {
5507
5390
  }
5508
5391
  function handlePipelineError(err, context) {
5509
5392
  const error = err instanceof Error ? err : new Error(String(err));
5510
- if (isSentinel(error.message, "AUTH_FAILED")) {
5393
+ if (error.message === "AUTH_FAILED") {
5511
5394
  writeError2('Authentication failed. Check that your credential is valid and has the "Build (Read)" scope.');
5512
- writeErrorDetail(error.message, "AUTH_FAILED");
5513
5395
  return;
5514
5396
  }
5515
- if (isSentinel(error.message, "PERMISSION_DENIED")) {
5397
+ if (error.message === "PERMISSION_DENIED") {
5516
5398
  writeError2(`Access denied. Your credential may lack pipeline permissions for project "${context?.project}".`);
5517
- writeErrorDetail(error.message, "PERMISSION_DENIED");
5518
5399
  return;
5519
5400
  }
5520
5401
  if (error.message === "NETWORK_ERROR") {
@@ -5526,12 +5407,7 @@ function handlePipelineError(err, context) {
5526
5407
  return;
5527
5408
  }
5528
5409
  if (error.message.startsWith("HTTP_")) {
5529
- const { sentinel, detail } = splitSentinel(error.message);
5530
- writeError2(`Azure DevOps request failed with ${sentinel}.`);
5531
- if (detail !== null) {
5532
- process.stderr.write(` ${detail}
5533
- `);
5534
- }
5410
+ writeError2(`Azure DevOps request failed with ${error.message}.`);
5535
5411
  return;
5536
5412
  }
5537
5413
  writeError2(error.message);
@@ -6423,7 +6299,7 @@ function mapRelationType(raw) {
6423
6299
  };
6424
6300
  }
6425
6301
  async function readJsonResponse3(response) {
6426
- if (!response.ok) throw httpError(response);
6302
+ if (!response.ok) throw new Error(`HTTP_${response.status}`);
6427
6303
  return await response.json();
6428
6304
  }
6429
6305
  function parseTargetId(url) {
@@ -6471,7 +6347,7 @@ async function addWorkItemRelation(context, cred, type, id1, id2) {
6471
6347
  { op: "add", path: "/relations/-", value: { rel: relType.referenceName, url: targetUrl } }
6472
6348
  ])
6473
6349
  });
6474
- if (!response.ok) throw httpError(response);
6350
+ if (!response.ok) throw new Error(`HTTP_${response.status}`);
6475
6351
  return { status: "added", type: relType.name, referenceName: relType.referenceName, id1, id2 };
6476
6352
  }
6477
6353
  async function removeWorkItemRelation(context, cred, type, id1, id2) {
@@ -6491,7 +6367,7 @@ async function removeWorkItemRelation(context, cred, type, id1, id2) {
6491
6367
  headers: { ...authHeaders(cred), "Content-Type": "application/json-patch+json" },
6492
6368
  body: JSON.stringify([{ op: "remove", path: `/relations/${index}` }])
6493
6369
  });
6494
- if (!response.ok) throw httpError(response);
6370
+ if (!response.ok) throw new Error(`HTTP_${response.status}`);
6495
6371
  return { status: "removed", type: relType.name, referenceName: relType.referenceName, id1, id2 };
6496
6372
  }
6497
6373
  async function listWorkItemRelations(context, cred, id) {
@@ -6579,12 +6455,11 @@ function handleRelationError(err, id1) {
6579
6455
  const target = id1 !== void 0 ? id1 : "unknown";
6580
6456
  process.stderr.write(`Error: work item #${target} not found.
6581
6457
  `);
6582
- } else if (isSentinel(msg, "AUTH_FAILED")) {
6458
+ } else if (msg === "AUTH_FAILED") {
6583
6459
  process.stderr.write(
6584
6460
  `Error: authentication failed. Check your PAT has Work Items \u2192 Read & Write scope.
6585
6461
  `
6586
6462
  );
6587
- writeErrorDetail(msg, "AUTH_FAILED");
6588
6463
  } else {
6589
6464
  process.stderr.write(`Error: ${msg}
6590
6465
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azdo-cli",
3
- "version": "0.19.0-feature-037-api-error-surfacing.693",
3
+ "version": "0.19.0-feature-037-api-error-surfacing.692",
4
4
  "description": "Azure DevOps CLI tool",
5
5
  "type": "module",
6
6
  "bin": {