azdo-cli 0.19.0-feature-041-changelog-auth-token.729 → 0.20.0-develop.738

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 +172 -123
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1146,25 +1146,26 @@ async function promptForPat() {
1146
1146
  process.stdin.on("data", onData);
1147
1147
  });
1148
1148
  }
1149
+ function patFromEnvFile(envFile) {
1150
+ if (!existsSync2(envFile)) return null;
1151
+ const contents = readFileSync2(envFile, "utf8");
1152
+ for (const line of contents.split("\n")) {
1153
+ const match = line.match(/^AZDO_PAT\s*=([^\n\r]+)$/);
1154
+ if (match === null) continue;
1155
+ const value = match[1].trim().replace(/^["']|["']$/g, "");
1156
+ if (value.length > 0) return value;
1157
+ }
1158
+ return null;
1159
+ }
1149
1160
  function findDotEnvPat(startDir = process.cwd()) {
1150
1161
  let current = startDir;
1151
- while (true) {
1152
- const envFile = join2(current, ".env");
1153
- if (existsSync2(envFile)) {
1154
- const contents = readFileSync2(envFile, "utf8");
1155
- for (const line of contents.split("\n")) {
1156
- const match = line.match(/^AZDO_PAT\s*=([^\n\r]+)$/);
1157
- if (match) {
1158
- const value = match[1].trim().replace(/^["']|["']$/g, "");
1159
- if (value.length > 0) return value;
1160
- }
1161
- }
1162
- }
1162
+ for (; ; ) {
1163
+ const found = patFromEnvFile(join2(current, ".env"));
1164
+ if (found !== null) return found;
1163
1165
  const parent = dirname2(current);
1164
- if (parent === current) break;
1166
+ if (parent === current) return null;
1165
1167
  current = parent;
1166
1168
  }
1167
- return null;
1168
1169
  }
1169
1170
  async function resolveAuthCredential(org) {
1170
1171
  let cred;
@@ -2336,36 +2337,30 @@ Identifier: ${masked}
2336
2337
  ` : "")
2337
2338
  );
2338
2339
  }
2339
- async function handleLogout(options, orgFromGlobal) {
2340
- if (options.all && orgFromGlobal) {
2341
- process.stderr.write("--org and --all are mutually exclusive.\n");
2342
- process.exitCode = 1;
2343
- return;
2344
- }
2345
- if (options.all) {
2346
- try {
2347
- const result = await logout({ all: true });
2348
- if (result.removed.length === 0) {
2349
- process.stdout.write("No stored credentials to remove.\n");
2350
- return;
2351
- }
2352
- for (const r of result.removed) {
2353
- process.stdout.write(`Removed ${r.kind} credential for org ${r.org}.
2354
- `);
2355
- }
2356
- } catch (err) {
2357
- if (err instanceof CredentialStoreUnavailableError) {
2358
- process.stderr.write(`${err.message}
2340
+ async function logoutAllOrgs() {
2341
+ try {
2342
+ const result = await logout({ all: true });
2343
+ if (result.removed.length === 0) {
2344
+ process.stdout.write("No stored credentials to remove.\n");
2345
+ return;
2346
+ }
2347
+ for (const r of result.removed) {
2348
+ process.stdout.write(`Removed ${r.kind} credential for org ${r.org}.
2359
2349
  `);
2360
- process.exitCode = 4;
2361
- return;
2362
- }
2363
- process.stderr.write(`Failed to remove credentials: ${err.message}
2350
+ }
2351
+ } catch (err) {
2352
+ if (err instanceof CredentialStoreUnavailableError) {
2353
+ process.stderr.write(`${err.message}
2364
2354
  `);
2365
- process.exitCode = 1;
2355
+ process.exitCode = 4;
2356
+ return;
2366
2357
  }
2367
- return;
2358
+ process.stderr.write(`Failed to remove credentials: ${err.message}
2359
+ `);
2360
+ process.exitCode = 1;
2368
2361
  }
2362
+ }
2363
+ async function logoutSingleOrg(orgFromGlobal) {
2369
2364
  const resolved = resolveOrg({ org: orgFromGlobal });
2370
2365
  if (!resolved) {
2371
2366
  process.stderr.write(`${formatResolutionError()}
@@ -2392,6 +2387,18 @@ async function handleLogout(options, orgFromGlobal) {
2392
2387
  throw err;
2393
2388
  }
2394
2389
  }
2390
+ async function handleLogout(options, orgFromGlobal) {
2391
+ if (options.all && orgFromGlobal) {
2392
+ process.stderr.write("--org and --all are mutually exclusive.\n");
2393
+ process.exitCode = 1;
2394
+ return;
2395
+ }
2396
+ if (options.all) {
2397
+ await logoutAllOrgs();
2398
+ return;
2399
+ }
2400
+ await logoutSingleOrg(orgFromGlobal);
2401
+ }
2395
2402
  function describeCredentialSource(cred) {
2396
2403
  switch (cred.source) {
2397
2404
  case "env":
@@ -3440,7 +3447,9 @@ function createUpsertCommand() {
3440
3447
  import { Command as Command11 } from "commander";
3441
3448
  function stringifyValue(value) {
3442
3449
  if (value === null || value === void 0) return "";
3450
+ if (typeof value === "string") return value;
3443
3451
  if (typeof value === "object") return JSON.stringify(value);
3452
+ if (typeof value === "symbol" || typeof value === "function") return value.toString();
3444
3453
  return String(value);
3445
3454
  }
3446
3455
  function formatRichValue(raw) {
@@ -4893,79 +4902,136 @@ function createPrReactivateCommand() {
4893
4902
  });
4894
4903
  return command;
4895
4904
  }
4905
+ function parseCommentsOptions(options) {
4906
+ let explicitPrId = null;
4907
+ if (options.prNumber !== void 0) {
4908
+ explicitPrId = parsePositivePrNumber(options.prNumber);
4909
+ if (explicitPrId === null) {
4910
+ writeError(`Invalid --pr-number "${options.prNumber}"; expected a positive integer.`);
4911
+ return null;
4912
+ }
4913
+ }
4914
+ let maxChars = 0;
4915
+ if (options.maxChars !== void 0) {
4916
+ const parsed = parseNonNegativeInt(options.maxChars);
4917
+ if (parsed === null) {
4918
+ writeError(`Invalid --max-chars "${options.maxChars}"; expected a non-negative integer.`);
4919
+ return null;
4920
+ }
4921
+ maxChars = parsed;
4922
+ }
4923
+ let threadFilter = null;
4924
+ if (options.thread !== void 0) {
4925
+ threadFilter = parsePositivePrNumber(options.thread);
4926
+ if (threadFilter === null) {
4927
+ writeError(`Invalid --thread "${options.thread}"; expected a positive integer.`);
4928
+ return null;
4929
+ }
4930
+ }
4931
+ return { explicitPrId, maxChars, threadFilter };
4932
+ }
4933
+ async function resolveCommentsPullRequest(resolved, explicitPrId) {
4934
+ if (explicitPrId !== null) {
4935
+ let pullRequest;
4936
+ try {
4937
+ pullRequest = await getPullRequestById(resolved.context, resolved.repo, resolved.pat, explicitPrId);
4938
+ } catch (err) {
4939
+ if (err instanceof Error && err.message.startsWith("NOT_FOUND")) {
4940
+ writeError(`Pull request #${explicitPrId} not found in ${resolved.context.org}/${resolved.context.project}/${resolved.repo}.`, EXIT_NOT_FOUND);
4941
+ return null;
4942
+ }
4943
+ throw err;
4944
+ }
4945
+ return { pullRequest, branchLabel: resolved.branch ?? pullRequest.sourceRefName };
4946
+ }
4947
+ const pullRequests = await listPullRequests(resolved.context, resolved.repo, resolved.pat, resolved.branch, {
4948
+ status: "active"
4949
+ });
4950
+ if (pullRequests.length === 0) {
4951
+ writeContractError(autoDetectZeroMatch(resolved.branch));
4952
+ return null;
4953
+ }
4954
+ if (pullRequests.length > 1) {
4955
+ writeContractError(autoDetectMultiMatch(resolved.branch, pullRequests.map((pr) => pr.id)));
4956
+ return null;
4957
+ }
4958
+ return { pullRequest: pullRequests[0], branchLabel: resolved.branch };
4959
+ }
4960
+ function selectCommentThreads(fetchedThreads, threadFilter, pullRequestId) {
4961
+ if (threadFilter === null) {
4962
+ return fetchedThreads;
4963
+ }
4964
+ if (!fetchedThreads.some((thread) => thread.id === threadFilter)) {
4965
+ writeError(`Thread #${threadFilter} not found on pull request #${pullRequestId}.`, EXIT_NOT_FOUND);
4966
+ return null;
4967
+ }
4968
+ return fetchedThreads.filter((thread) => thread.id === threadFilter);
4969
+ }
4970
+ function shapeCommentThreads(allThreads, opts) {
4971
+ return allThreads.filter(
4972
+ (thread) => (!opts.hideResolved || !isThreadResolved(thread.status)) && (!opts.codeRelatedOnly || thread.threadContext !== null)
4973
+ ).map(
4974
+ (thread) => shapeThreadForOutput(thread, {
4975
+ excludeSystem: opts.excludeSystem,
4976
+ maxChars: opts.maxChars,
4977
+ contains: opts.contains
4978
+ })
4979
+ ).filter((thread) => thread !== null);
4980
+ }
4981
+ function describeEmptyThreads(pullRequestId, allThreadCount, active) {
4982
+ const filters = [];
4983
+ if (active.contains !== void 0) {
4984
+ filters.push("matching");
4985
+ }
4986
+ if (active.codeRelatedOnly) {
4987
+ filters.push("code-related");
4988
+ }
4989
+ if (active.hideResolved) {
4990
+ filters.push("unresolved");
4991
+ }
4992
+ if (active.excludeSystem) {
4993
+ filters.push("non-system");
4994
+ }
4995
+ if (allThreadCount > 0 && filters.length > 0) {
4996
+ return `Pull request #${pullRequestId} has no ${filters.join(" ")} comment threads (filtered from ${allThreadCount} thread${allThreadCount === 1 ? "" : "s"}).
4997
+ `;
4998
+ }
4999
+ return `Pull request #${pullRequestId} has no comment threads.
5000
+ `;
5001
+ }
4896
5002
  function createPrCommentsCommand() {
4897
5003
  const command = new Command12("comments");
4898
5004
  withCommonPrOptions(configureUnwrappedHelp(command)).description("List pull request comment threads for the current branch").option("--pr-number <N>", PR_NUMBER_HELP).option("--hide-resolved", "hide threads whose status is resolved / won't fix / closed / by design").option("--exclude-resolved", "alias of --hide-resolved: exclude resolved / won't fix / closed / by design threads").option("--code-related-only", "show only threads anchored to a file/line; omit general discussion threads").option("--exclude-system", "omit Azure DevOps system comments (branch updates, reviewer votes, build events)").option("--max-chars <N>", "truncate each comment body to N characters (0 = no limit, the default)").option("--thread <id>", "show only the thread with this numeric id; fails when the pull request has no such thread").option("--contains <text>", "show only threads holding a comment that contains this literal, case-sensitive substring (matched before --max-chars truncates)").option("--json", "output JSON").action(async (options) => {
4899
5005
  validateOrgProjectPair(options);
4900
- let context;
4901
- let explicitPrId = null;
4902
- if (options.prNumber !== void 0) {
4903
- explicitPrId = parsePositivePrNumber(options.prNumber);
4904
- if (explicitPrId === null) {
4905
- writeError(`Invalid --pr-number "${options.prNumber}"; expected a positive integer.`);
4906
- return;
4907
- }
4908
- }
4909
- let maxChars = 0;
4910
- if (options.maxChars !== void 0) {
4911
- const parsed = parseNonNegativeInt(options.maxChars);
4912
- if (parsed === null) {
4913
- writeError(`Invalid --max-chars "${options.maxChars}"; expected a non-negative integer.`);
4914
- return;
4915
- }
4916
- maxChars = parsed;
4917
- }
4918
- let threadFilter = null;
4919
- if (options.thread !== void 0) {
4920
- threadFilter = parsePositivePrNumber(options.thread);
4921
- if (threadFilter === null) {
4922
- writeError(`Invalid --thread "${options.thread}"; expected a positive integer.`);
4923
- return;
4924
- }
5006
+ const parsed = parseCommentsOptions(options);
5007
+ if (parsed === null) {
5008
+ return;
4925
5009
  }
5010
+ const { explicitPrId, maxChars, threadFilter } = parsed;
5011
+ let context;
4926
5012
  try {
4927
5013
  const resolved = await resolvePrCommandContext(options, { requireBranch: explicitPrId === null });
4928
5014
  context = resolved.context;
4929
- let pullRequest;
4930
- let branchLabel;
4931
- if (explicitPrId !== null) {
4932
- try {
4933
- pullRequest = await getPullRequestById(resolved.context, resolved.repo, resolved.pat, explicitPrId);
4934
- } catch (err) {
4935
- if (err instanceof Error && err.message.startsWith("NOT_FOUND")) {
4936
- writeError(`Pull request #${explicitPrId} not found in ${resolved.context.org}/${resolved.context.project}/${resolved.repo}.`, EXIT_NOT_FOUND);
4937
- return;
4938
- }
4939
- throw err;
4940
- }
4941
- branchLabel = resolved.branch ?? pullRequest.sourceRefName;
4942
- } else {
4943
- const pullRequests = await listPullRequests(resolved.context, resolved.repo, resolved.pat, resolved.branch, {
4944
- status: "active"
4945
- });
4946
- if (pullRequests.length === 0) {
4947
- writeContractError(autoDetectZeroMatch(resolved.branch));
4948
- return;
4949
- }
4950
- if (pullRequests.length > 1) {
4951
- writeContractError(autoDetectMultiMatch(resolved.branch, pullRequests.map((pr) => pr.id)));
4952
- return;
4953
- }
4954
- pullRequest = pullRequests[0];
4955
- branchLabel = resolved.branch;
5015
+ const target = await resolveCommentsPullRequest(resolved, explicitPrId);
5016
+ if (target === null) {
5017
+ return;
4956
5018
  }
5019
+ const { pullRequest, branchLabel } = target;
4957
5020
  const hideResolved = options.hideResolved === true || options.excludeResolved === true;
4958
5021
  const codeRelatedOnly = options.codeRelatedOnly === true;
4959
5022
  const excludeSystem = options.excludeSystem === true;
4960
5023
  const fetchedThreads = await getPullRequestThreads(resolved.context, resolved.repo, resolved.pat, pullRequest.id);
4961
- if (threadFilter !== null && !fetchedThreads.some((thread) => thread.id === threadFilter)) {
4962
- writeError(`Thread #${threadFilter} not found on pull request #${pullRequest.id}.`, EXIT_NOT_FOUND);
5024
+ const allThreads = selectCommentThreads(fetchedThreads, threadFilter, pullRequest.id);
5025
+ if (allThreads === null) {
4963
5026
  return;
4964
5027
  }
4965
- const allThreads = threadFilter === null ? fetchedThreads : fetchedThreads.filter((thread) => thread.id === threadFilter);
4966
- const threads = allThreads.filter(
4967
- (thread) => (!hideResolved || !isThreadResolved(thread.status)) && (!codeRelatedOnly || thread.threadContext !== null)
4968
- ).map((thread) => shapeThreadForOutput(thread, { excludeSystem, maxChars, contains: options.contains })).filter((thread) => thread !== null);
5028
+ const threads = shapeCommentThreads(allThreads, {
5029
+ hideResolved,
5030
+ codeRelatedOnly,
5031
+ excludeSystem,
5032
+ maxChars,
5033
+ contains: options.contains
5034
+ });
4969
5035
  const result = { branch: branchLabel, pullRequest, threads };
4970
5036
  if (options.json) {
4971
5037
  process.stdout.write(`${JSON.stringify(result, null, 2)}
@@ -4973,28 +5039,12 @@ function createPrCommentsCommand() {
4973
5039
  return;
4974
5040
  }
4975
5041
  if (threads.length === 0) {
4976
- if (allThreads.length > 0 && (hideResolved || codeRelatedOnly || excludeSystem || options.contains !== void 0)) {
4977
- const filters = [];
4978
- if (options.contains !== void 0) {
4979
- filters.push("matching");
4980
- }
4981
- if (codeRelatedOnly) {
4982
- filters.push("code-related");
4983
- }
4984
- if (hideResolved) {
4985
- filters.push("unresolved");
4986
- }
4987
- if (excludeSystem) {
4988
- filters.push("non-system");
4989
- }
4990
- process.stdout.write(
4991
- `Pull request #${pullRequest.id} has no ${filters.join(" ")} comment threads (filtered from ${allThreads.length} thread${allThreads.length === 1 ? "" : "s"}).
4992
- `
4993
- );
4994
- } else {
4995
- process.stdout.write(`Pull request #${pullRequest.id} has no comment threads.
4996
- `);
4997
- }
5042
+ process.stdout.write(describeEmptyThreads(pullRequest.id, allThreads.length, {
5043
+ hideResolved,
5044
+ codeRelatedOnly,
5045
+ excludeSystem,
5046
+ contains: options.contains
5047
+ }));
4998
5048
  return;
4999
5049
  }
5000
5050
  process.stdout.write(`${formatThreads(pullRequest.id, pullRequest.title, threads)}
@@ -5174,8 +5224,7 @@ async function runCommentReply(threadIdRaw, text, options) {
5174
5224
  return;
5175
5225
  }
5176
5226
  const threads = await getPullRequestThreads(target.context, target.repo, target.pat, target.pullRequest.id);
5177
- const thread = threads.find((t) => t.id === target.threadId);
5178
- if (!thread) {
5227
+ if (!threads.some((t) => t.id === target.threadId)) {
5179
5228
  writeError(`Thread #${target.threadId} not found on pull request #${target.pullRequest.id}.`, EXIT_NOT_FOUND);
5180
5229
  return;
5181
5230
  }
@@ -7105,7 +7154,7 @@ function handleRelationError(err, id1) {
7105
7154
  `
7106
7155
  );
7107
7156
  } else if (msg.startsWith("NOT_FOUND")) {
7108
- const target = id1 !== void 0 ? id1 : "unknown";
7157
+ const target = id1 ?? "unknown";
7109
7158
  process.stderr.write(`Error: work item #${target} not found.
7110
7159
  `);
7111
7160
  } else if (isSentinel(msg, "AUTH_FAILED")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azdo-cli",
3
- "version": "0.19.0-feature-041-changelog-auth-token.729",
3
+ "version": "0.20.0-develop.738",
4
4
  "description": "Azure DevOps CLI tool",
5
5
  "type": "module",
6
6
  "bin": {