@automatify-au/cli 0.1.8 → 0.1.10

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/README.md CHANGED
@@ -142,6 +142,7 @@ All TestOps commands are invoked as `automatify testops <command>`:
142
142
  - `allure`
143
143
  - `automatify testops allure upload ABC-123 ./allure-report.zip --dry-run`
144
144
  - `automatify testops allure upload ABC-123 ./allure-report.zip`
145
+ - `automatify testops allure download ABC-123 --output-dir ./downloads`
145
146
  - `auto`
146
147
  - `automatify testops auto --dry-run`
147
148
  - `automatify testops auto`
@@ -150,6 +151,7 @@ All TestOps commands are invoked as `automatify testops <command>`:
150
151
 
151
152
  Purpose:
152
153
  - upload an already generated Allure HTML report ZIP to a Jira issue as evidence.
154
+ - download an uploaded Allure HTML report ZIP from a Jira issue for local inspection.
153
155
  - parse `widgets/summary.json` from the ZIP and add a concise Jira comment with result counts.
154
156
  - keep the flow Java-free and backend-free; this command does not generate Allure reports from raw `allure-results`.
155
157
 
@@ -183,6 +185,8 @@ Upload without a Jira comment:
183
185
  automatify testops allure upload ABC-123 ./allure-report.zip --no-comment
184
186
  ```
185
187
 
188
+ Normal output prints the Jira issue, attachment filename, attachment id, and attachment URL when Jira returns them.
189
+
186
190
  Machine-readable output:
187
191
  ```bash
188
192
  automatify testops allure upload ABC-123 ./allure-report.zip --dry-run --json
@@ -214,6 +218,22 @@ After a real upload, JSON output includes Jira attachment metadata when Jira ret
214
218
  }
215
219
  ```
216
220
 
221
+ Download the latest Allure report ZIP attachment from a Jira issue:
222
+ ```bash
223
+ automatify testops allure download ABC-123 --output-dir ./downloads
224
+ ```
225
+
226
+ Download selection rules:
227
+ - `--attachment-id <id>` downloads one exact Jira attachment.
228
+ - `--filename <name>` downloads the newest attachment with that filename.
229
+ - without either flag, the CLI chooses the newest `allure-report*.zip` attachment, then falls back to the newest `.zip` attachment.
230
+ - existing local files are not overwritten unless `--force` is passed.
231
+
232
+ Download dry run / JSON:
233
+ ```bash
234
+ automatify testops allure download ABC-123 --dry-run --json
235
+ ```
236
+
217
237
  ### Optional gated maintenance commands
218
238
 
219
239
  - `sync`
@@ -12448,6 +12448,7 @@ function createAutoHandler(deps = {}) {
12448
12448
 
12449
12449
  // src/allure.ts
12450
12450
  var import_node_path8 = __toESM(require("node:path"), 1);
12451
+ var import_node_fs7 = require("node:fs");
12451
12452
 
12452
12453
  // ../../packages/jira-client/src/adf.ts
12453
12454
  function paragraph(text = "") {
@@ -12533,6 +12534,62 @@ async function uploadIssueAttachment(params) {
12533
12534
  size: typeof first?.size === "number" ? first.size : fileStat.size
12534
12535
  };
12535
12536
  }
12537
+ function parseIssueAttachment(value) {
12538
+ if (!value || typeof value !== "object") {
12539
+ return void 0;
12540
+ }
12541
+ const item = value;
12542
+ if (typeof item.id !== "string" || typeof item.filename !== "string") {
12543
+ return void 0;
12544
+ }
12545
+ return {
12546
+ id: item.id,
12547
+ filename: item.filename,
12548
+ self: typeof item.self === "string" ? item.self : void 0,
12549
+ content: typeof item.content === "string" ? item.content : void 0,
12550
+ size: typeof item.size === "number" ? item.size : void 0,
12551
+ created: typeof item.created === "string" ? item.created : void 0
12552
+ };
12553
+ }
12554
+ async function listIssueAttachments(params) {
12555
+ const site = normalizeSite(params.site);
12556
+ const response = await fetch(`${site}/rest/api/3/issue/${encodeURIComponent(params.issueKey)}?fields=attachment`, {
12557
+ headers: {
12558
+ authorization: authHeader(params.email, params.apiToken),
12559
+ accept: "application/json"
12560
+ }
12561
+ });
12562
+ if (!response.ok) {
12563
+ throw new Error(`Jira attachment listing failed: ${await readError(response)}`);
12564
+ }
12565
+ const parsed = await response.json();
12566
+ const attachments = Array.isArray(parsed.fields?.attachment) ? parsed.fields.attachment : [];
12567
+ return attachments.map(parseIssueAttachment).filter((item) => Boolean(item));
12568
+ }
12569
+ async function downloadIssueAttachment(params) {
12570
+ const url = params.attachment.content;
12571
+ if (!url) {
12572
+ throw new Error(`Jira attachment ${params.attachment.id} does not include a downloadable URL.`);
12573
+ }
12574
+ const response = await fetch(url, {
12575
+ headers: {
12576
+ authorization: authHeader(params.email, params.apiToken)
12577
+ }
12578
+ });
12579
+ if (!response.ok) {
12580
+ throw new Error(`Jira attachment download failed: ${await readError(response)}`);
12581
+ }
12582
+ const bytes = new Uint8Array(await response.arrayBuffer());
12583
+ (0, import_node_fs5.writeFileSync)(params.outputPath, bytes);
12584
+ return {
12585
+ filename: params.attachment.filename,
12586
+ id: params.attachment.id,
12587
+ outputPath: params.outputPath,
12588
+ self: params.attachment.self,
12589
+ content: params.attachment.content,
12590
+ size: bytes.byteLength
12591
+ };
12592
+ }
12536
12593
  async function addIssueComment(params) {
12537
12594
  const site = normalizeSite(params.site);
12538
12595
  const response = await fetch(`${site}/rest/api/3/issue/${encodeURIComponent(params.issueKey)}/comment`, {
@@ -12643,9 +12700,12 @@ function parseArgs2(args) {
12643
12700
  "--site",
12644
12701
  "--email",
12645
12702
  "--api-token",
12703
+ "--attachment-id",
12704
+ "--filename",
12705
+ "--output-dir",
12646
12706
  ...CONFIG_FLAGS2
12647
12707
  ]);
12648
- const supportedBoolFlags = /* @__PURE__ */ new Set(["--no-comment", "--comment", "--json", "--dry-run", "--verbose"]);
12708
+ const supportedBoolFlags = /* @__PURE__ */ new Set(["--no-comment", "--comment", "--json", "--dry-run", "--verbose", "--force"]);
12649
12709
  for (let i = 0; i < args.length; i += 1) {
12650
12710
  const token = args[i];
12651
12711
  if (!token.startsWith("--")) {
@@ -12688,6 +12748,23 @@ function normalizeError(error) {
12688
12748
  function hasCredential(value) {
12689
12749
  return value.trim().length > 0;
12690
12750
  }
12751
+ function resolveJiraConfig(parsed, env, cwd) {
12752
+ const config = resolveCliConfig(pickConfigArgs2(parsed), env, cwd);
12753
+ const site = normalizeSite2(parsed.flags["--site"] ?? env.JIRA_SITE ?? config.values.baseUrl);
12754
+ const email = (parsed.flags["--email"] ?? config.values.jiraEmail).trim();
12755
+ const apiToken = (parsed.flags["--api-token"] ?? config.values.jiraApiToken).trim();
12756
+ const errors = [];
12757
+ if (!hasCredential(site)) {
12758
+ errors.push("Missing Jira site. Pass --site, set JIRA_SITE/JIRA_BASE_URL, or configure baseUrl.");
12759
+ }
12760
+ if (!hasCredential(email)) {
12761
+ errors.push("Missing Jira email. Pass --email or set JIRA_EMAIL.");
12762
+ }
12763
+ if (!hasCredential(apiToken)) {
12764
+ errors.push("Missing Jira API token. Pass --api-token or set JIRA_API_TOKEN.");
12765
+ }
12766
+ return errors.length > 0 ? { errors } : { config: { site, email, apiToken }, errors: [] };
12767
+ }
12691
12768
  function formatHumanSummary(summary) {
12692
12769
  const counts = summary.counts;
12693
12770
  return [
@@ -12700,6 +12777,76 @@ function formatHumanSummary(summary) {
12700
12777
  `Unknown: ${counts.unknown}`
12701
12778
  ];
12702
12779
  }
12780
+ function formatHumanAttachment(attachment) {
12781
+ const lines = [`Attachment: ${attachment.filename}`];
12782
+ if (attachment.id) {
12783
+ lines.push(`Attachment ID: ${attachment.id}`);
12784
+ }
12785
+ if (attachment.content) {
12786
+ lines.push(`Attachment URL: ${attachment.content}`);
12787
+ } else if (attachment.self) {
12788
+ lines.push(`Attachment API URL: ${attachment.self}`);
12789
+ }
12790
+ return lines;
12791
+ }
12792
+ function isAllureReportZip(attachment) {
12793
+ const filename = attachment.filename.toLowerCase();
12794
+ return filename.endsWith(".zip") && filename.includes("allure-report");
12795
+ }
12796
+ function isZip(attachment) {
12797
+ return attachment.filename.toLowerCase().endsWith(".zip");
12798
+ }
12799
+ function sortNewestFirst(attachments) {
12800
+ return [...attachments].sort((left, right) => {
12801
+ const leftTime = left.created ? Date.parse(left.created) : 0;
12802
+ const rightTime = right.created ? Date.parse(right.created) : 0;
12803
+ if (rightTime !== leftTime) {
12804
+ return rightTime - leftTime;
12805
+ }
12806
+ return right.id.localeCompare(left.id);
12807
+ });
12808
+ }
12809
+ function selectAllureAttachment(params) {
12810
+ const { attachments, attachmentId, filename } = params;
12811
+ if (attachmentId) {
12812
+ const match = attachments.find((attachment) => attachment.id === attachmentId);
12813
+ if (!match) {
12814
+ throw new Error(`Could not find Jira attachment with id ${attachmentId}.`);
12815
+ }
12816
+ return match;
12817
+ }
12818
+ if (filename) {
12819
+ const matches = sortNewestFirst(attachments.filter((attachment) => attachment.filename === filename));
12820
+ if (matches.length === 0) {
12821
+ throw new Error(`Could not find Jira attachment named ${filename}.`);
12822
+ }
12823
+ return matches[0];
12824
+ }
12825
+ const allureMatches = sortNewestFirst(attachments.filter(isAllureReportZip));
12826
+ if (allureMatches.length > 0) {
12827
+ return allureMatches[0];
12828
+ }
12829
+ const zipMatches = sortNewestFirst(attachments.filter(isZip));
12830
+ if (zipMatches.length > 0) {
12831
+ return zipMatches[0];
12832
+ }
12833
+ throw new Error("Could not find an Allure report ZIP attachment on this Jira issue.");
12834
+ }
12835
+ function buildDownloadJsonOutput(params) {
12836
+ return {
12837
+ issueKey: params.issueKey,
12838
+ dryRun: params.dryRun,
12839
+ downloaded: params.downloaded,
12840
+ attachment: {
12841
+ filename: params.attachment.filename,
12842
+ id: params.attachment.id,
12843
+ ...params.attachment.self ? { self: params.attachment.self } : {},
12844
+ ...params.attachment.content ? { content: params.attachment.content } : {},
12845
+ ...typeof params.attachment.size === "number" ? { size: params.attachment.size } : {}
12846
+ },
12847
+ outputPath: params.outputPath
12848
+ };
12849
+ }
12703
12850
  function buildJsonOutput(params) {
12704
12851
  const attachment = {
12705
12852
  filename: params.attachment.filename
@@ -12738,12 +12885,18 @@ function createAllureHandler(deps = {}) {
12738
12885
  const parseZip = deps.parseAllureReportZip ?? parseAllureReportZip;
12739
12886
  const uploadAttachment = deps.uploadIssueAttachment ?? uploadIssueAttachment;
12740
12887
  const createComment = deps.addIssueComment ?? addIssueComment;
12888
+ const listAttachments = deps.listIssueAttachments ?? listIssueAttachments;
12889
+ const downloadAttachment = deps.downloadIssueAttachment ?? downloadIssueAttachment;
12741
12890
  return async (request) => {
12742
12891
  const [subcommand, ...subArgs] = request.args;
12743
- if (subcommand !== "upload") {
12892
+ if (subcommand !== "upload" && subcommand !== "download") {
12744
12893
  return {
12745
12894
  exitCode: ExitCode.UsageError,
12746
- stderr: ["Usage: automatify testops allure upload <issueKey> <zipPath> [options]"]
12895
+ stderr: [
12896
+ "Usage:",
12897
+ " automatify testops allure upload <issueKey> <zipPath> [options]",
12898
+ " automatify testops allure download <issueKey> [options]"
12899
+ ]
12747
12900
  };
12748
12901
  }
12749
12902
  const parsed = parseArgs2(subArgs);
@@ -12751,12 +12904,102 @@ function createAllureHandler(deps = {}) {
12751
12904
  const dryRun = parsed.boolFlags.has("--dry-run");
12752
12905
  const verbose = parsed.boolFlags.has("--verbose");
12753
12906
  const addComment = !parsed.boolFlags.has("--no-comment");
12907
+ const force = parsed.boolFlags.has("--force");
12754
12908
  if (parsed.unknownFlags.length > 0) {
12755
12909
  return {
12756
12910
  exitCode: ExitCode.UsageError,
12757
12911
  stderr: [`ERROR: Unknown or invalid flags: ${parsed.unknownFlags.join(", ")}`]
12758
12912
  };
12759
12913
  }
12914
+ if (subcommand === "download") {
12915
+ const [issueKey2] = parsed.positionals;
12916
+ if (!issueKey2 || parsed.positionals.length > 1) {
12917
+ return {
12918
+ exitCode: ExitCode.UsageError,
12919
+ stderr: ["Usage: automatify testops allure download <issueKey> [options]"]
12920
+ };
12921
+ }
12922
+ const resolved2 = resolveJiraConfig(parsed, env, cwd);
12923
+ if (!resolved2.config) {
12924
+ return {
12925
+ exitCode: ExitCode.ValidationError,
12926
+ stderr: resolved2.errors.map((line) => `ERROR: ${line}`)
12927
+ };
12928
+ }
12929
+ try {
12930
+ const attachments = await listAttachments({
12931
+ ...resolved2.config,
12932
+ issueKey: issueKey2
12933
+ });
12934
+ const selected = selectAllureAttachment({
12935
+ attachments,
12936
+ attachmentId: parsed.flags["--attachment-id"],
12937
+ filename: parsed.flags["--filename"]
12938
+ });
12939
+ const outputDir = import_node_path8.default.resolve(cwd, parsed.flags["--output-dir"] ?? ".");
12940
+ const outputPath = import_node_path8.default.join(outputDir, selected.filename);
12941
+ if (dryRun) {
12942
+ if (useJson) {
12943
+ return {
12944
+ exitCode: ExitCode.Success,
12945
+ stdout: toJsonLine(buildDownloadJsonOutput({
12946
+ issueKey: issueKey2,
12947
+ dryRun: true,
12948
+ downloaded: false,
12949
+ attachment: selected,
12950
+ outputPath
12951
+ }))
12952
+ };
12953
+ }
12954
+ return {
12955
+ exitCode: ExitCode.Success,
12956
+ stdout: [
12957
+ `Selected Jira attachment ${selected.filename} (${selected.id}) from issue ${issueKey2}.`,
12958
+ ...verbose && selected.created ? [`Created: ${selected.created}`] : [],
12959
+ `Dry run: would download to ${outputPath}.`
12960
+ ]
12961
+ };
12962
+ }
12963
+ (0, import_node_fs7.mkdirSync)(outputDir, { recursive: true });
12964
+ if ((0, import_node_fs7.existsSync)(outputPath) && !force) {
12965
+ return {
12966
+ exitCode: ExitCode.ValidationError,
12967
+ stderr: [`ERROR: Output file already exists: ${outputPath}. Pass --force to overwrite.`]
12968
+ };
12969
+ }
12970
+ const downloaded = await downloadAttachment({
12971
+ ...resolved2.config,
12972
+ attachment: selected,
12973
+ outputPath
12974
+ });
12975
+ if (useJson) {
12976
+ return {
12977
+ exitCode: ExitCode.Success,
12978
+ stdout: toJsonLine(buildDownloadJsonOutput({
12979
+ issueKey: issueKey2,
12980
+ dryRun: false,
12981
+ downloaded: true,
12982
+ attachment: downloaded,
12983
+ outputPath
12984
+ }))
12985
+ };
12986
+ }
12987
+ return {
12988
+ exitCode: ExitCode.Success,
12989
+ stdout: [
12990
+ `Downloaded Allure report attachment from Jira issue ${issueKey2}.`,
12991
+ `Attachment: ${downloaded.filename}`,
12992
+ `Attachment ID: ${downloaded.id}`,
12993
+ `Saved to: ${downloaded.outputPath}`
12994
+ ]
12995
+ };
12996
+ } catch (error) {
12997
+ return {
12998
+ exitCode: ExitCode.RemoteError,
12999
+ stderr: [`ERROR: ${normalizeError(error)}`]
13000
+ };
13001
+ }
13002
+ }
12760
13003
  const [issueKey, zipPathInput] = parsed.positionals;
12761
13004
  if (!issueKey || !zipPathInput || parsed.positionals.length > 2) {
12762
13005
  return {
@@ -12764,26 +13007,13 @@ function createAllureHandler(deps = {}) {
12764
13007
  stderr: ["Usage: automatify testops allure upload <issueKey> <zipPath> [options]"]
12765
13008
  };
12766
13009
  }
12767
- const config = resolveCliConfig(pickConfigArgs2(parsed), env, cwd);
12768
- const site = normalizeSite2(parsed.flags["--site"] ?? env.JIRA_SITE ?? config.values.baseUrl);
12769
- const email = (parsed.flags["--email"] ?? config.values.jiraEmail).trim();
12770
- const apiToken = (parsed.flags["--api-token"] ?? config.values.jiraApiToken).trim();
13010
+ const resolved = resolveJiraConfig(parsed, env, cwd);
12771
13011
  const zipPath = import_node_path8.default.resolve(cwd, zipPathInput);
12772
13012
  const attachmentFilename = import_node_path8.default.basename(zipPath);
12773
- const credentialErrors = [];
12774
- if (!hasCredential(site)) {
12775
- credentialErrors.push("Missing Jira site. Pass --site, set JIRA_SITE/JIRA_BASE_URL, or configure baseUrl.");
12776
- }
12777
- if (!hasCredential(email)) {
12778
- credentialErrors.push("Missing Jira email. Pass --email or set JIRA_EMAIL.");
12779
- }
12780
- if (!hasCredential(apiToken)) {
12781
- credentialErrors.push("Missing Jira API token. Pass --api-token or set JIRA_API_TOKEN.");
12782
- }
12783
- if (credentialErrors.length > 0) {
13013
+ if (!resolved.config) {
12784
13014
  return {
12785
13015
  exitCode: ExitCode.ValidationError,
12786
- stderr: credentialErrors.map((line) => `ERROR: ${line}`)
13016
+ stderr: resolved.errors.map((line) => `ERROR: ${line}`)
12787
13017
  };
12788
13018
  }
12789
13019
  let summary;
@@ -12822,18 +13052,14 @@ function createAllureHandler(deps = {}) {
12822
13052
  }
12823
13053
  try {
12824
13054
  const attachment = await uploadAttachment({
12825
- site,
12826
- email,
12827
- apiToken,
13055
+ ...resolved.config,
12828
13056
  issueKey,
12829
13057
  filePath: zipPath
12830
13058
  });
12831
13059
  let commentAdded = false;
12832
13060
  if (addComment) {
12833
13061
  await createComment({
12834
- site,
12835
- email,
12836
- apiToken,
13062
+ ...resolved.config,
12837
13063
  issueKey,
12838
13064
  body: buildAllureEvidenceCommentAdf({
12839
13065
  counts: summary.counts,
@@ -12863,6 +13089,7 @@ function createAllureHandler(deps = {}) {
12863
13089
  ...verbose ? [`Summary path: ${summary.summaryPath}`] : [],
12864
13090
  "",
12865
13091
  `Uploaded to Jira issue ${issueKey}.`,
13092
+ ...formatHumanAttachment(attachment),
12866
13093
  commentAdded ? "Comment added." : "Comment skipped."
12867
13094
  ]
12868
13095
  };
@@ -12876,7 +13103,7 @@ function createAllureHandler(deps = {}) {
12876
13103
  }
12877
13104
 
12878
13105
  // src/bdd.ts
12879
- var import_node_fs7 = require("node:fs");
13106
+ var import_node_fs8 = require("node:fs");
12880
13107
  var import_node_path9 = __toESM(require("node:path"), 1);
12881
13108
  var import_yazl = __toESM(require_yazl(), 1);
12882
13109
 
@@ -13299,7 +13526,7 @@ function toJsonExportManifestItems(items) {
13299
13526
  async function defaultCreateZipArchive(archivePath, entries) {
13300
13527
  await new Promise((resolve, reject) => {
13301
13528
  const zip = new import_yazl.ZipFile();
13302
- const output = zip.outputStream.pipe((0, import_node_fs7.createWriteStream)(archivePath));
13529
+ const output = zip.outputStream.pipe((0, import_node_fs8.createWriteStream)(archivePath));
13303
13530
  output.on("close", () => resolve());
13304
13531
  output.on("error", reject);
13305
13532
  zip.outputStream.on("error", reject);
@@ -13329,10 +13556,10 @@ function missingProjectResponse() {
13329
13556
  function createBddHandler(deps = {}) {
13330
13557
  const cwd = deps.cwd ?? process.cwd();
13331
13558
  const env = deps.env ?? process.env;
13332
- const mkdir = deps.mkdir ?? ((targetPath, options) => (0, import_node_fs7.mkdirSync)(targetPath, options));
13333
- const readFile = deps.readFile ?? ((filePath) => (0, import_node_fs7.readFileSync)(filePath, "utf8"));
13334
- const readDir = deps.readDir ?? ((dirPath) => (0, import_node_fs7.readdirSync)(dirPath));
13335
- const writeFile = deps.writeFile ?? ((filePath, content) => (0, import_node_fs7.writeFileSync)(filePath, content, "utf8"));
13559
+ const mkdir = deps.mkdir ?? ((targetPath, options) => (0, import_node_fs8.mkdirSync)(targetPath, options));
13560
+ const readFile = deps.readFile ?? ((filePath) => (0, import_node_fs8.readFileSync)(filePath, "utf8"));
13561
+ const readDir = deps.readDir ?? ((dirPath) => (0, import_node_fs8.readdirSync)(dirPath));
13562
+ const writeFile = deps.writeFile ?? ((filePath, content) => (0, import_node_fs8.writeFileSync)(filePath, content, "utf8"));
13336
13563
  const createZipArchive = deps.createZipArchive ?? defaultCreateZipArchive;
13337
13564
  return async (request, context) => {
13338
13565
  const [subcommand, ...restArgs] = request.args;
@@ -14546,7 +14773,7 @@ function createDoctorHandler(deps = {}) {
14546
14773
  }
14547
14774
 
14548
14775
  // src/ingestFeature.ts
14549
- var import_node_fs8 = require("node:fs");
14776
+ var import_node_fs9 = require("node:fs");
14550
14777
  var import_node_path10 = __toESM(require("node:path"), 1);
14551
14778
  var CONFIG_FLAGS6 = /* @__PURE__ */ new Set([
14552
14779
  "--config",
@@ -14633,8 +14860,8 @@ function normalizeError4(error) {
14633
14860
  return "Unknown ingest error.";
14634
14861
  }
14635
14862
  function createIngestFeatureHandler(deps = {}) {
14636
- const readFile = deps.readFile ?? ((filePath) => (0, import_node_fs8.readFileSync)(filePath, "utf8"));
14637
- const readStdin = deps.readStdin ?? (() => (0, import_node_fs8.readFileSync)(0, "utf8"));
14863
+ const readFile = deps.readFile ?? ((filePath) => (0, import_node_fs9.readFileSync)(filePath, "utf8"));
14864
+ const readStdin = deps.readStdin ?? (() => (0, import_node_fs9.readFileSync)(0, "utf8"));
14638
14865
  const cwd = deps.cwd ?? process.cwd();
14639
14866
  const env = deps.env ?? process.env;
14640
14867
  return async (request, context) => {
@@ -14773,7 +15000,7 @@ function createIngestFeatureHandler(deps = {}) {
14773
15000
  }
14774
15001
 
14775
15002
  // src/runUpload.ts
14776
- var import_node_fs9 = require("node:fs");
15003
+ var import_node_fs10 = require("node:fs");
14777
15004
  var import_node_path11 = __toESM(require("node:path"), 1);
14778
15005
  var STEP_RESULTS = [
14779
15006
  StepResult.Passed,
@@ -14915,8 +15142,8 @@ function summarizeRunResult(result) {
14915
15142
  return lines;
14916
15143
  }
14917
15144
  function createRunUploadHandler(deps = {}) {
14918
- const readFile = deps.readFile ?? ((filePath) => (0, import_node_fs9.readFileSync)(filePath, "utf8"));
14919
- const readStdin = deps.readStdin ?? (() => (0, import_node_fs9.readFileSync)(0, "utf8"));
15145
+ const readFile = deps.readFile ?? ((filePath) => (0, import_node_fs10.readFileSync)(filePath, "utf8"));
15146
+ const readStdin = deps.readStdin ?? (() => (0, import_node_fs10.readFileSync)(0, "utf8"));
14920
15147
  const cwd = deps.cwd ?? process.cwd();
14921
15148
  const env = deps.env ?? process.env;
14922
15149
  return async (request, context) => {
@@ -15766,8 +15993,8 @@ function createSyncHandler(deps = {}) {
15766
15993
  var COMMAND_REGISTRY = [
15767
15994
  {
15768
15995
  name: "allure",
15769
- description: "Allure evidence upload commands for Jira issues",
15770
- subcommands: ["upload"],
15996
+ description: "Allure evidence upload/download commands for Jira issues",
15997
+ subcommands: ["upload", "download"],
15771
15998
  handler: createAllureHandler()
15772
15999
  },
15773
16000
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automatify-au/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Forge-first CLI for Automatify Jira TestOps",
5
5
  "type": "module",
6
6
  "bin": {