flakiness 0.158.0 → 0.160.0

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
@@ -65,7 +65,6 @@ flakiness upload ./report.json --endpoint https://custom.flakiness.io
65
65
  **Options:**
66
66
  - `-t, --access-token <token>` — Read-write access token (env: `FLAKINESS_ACCESS_TOKEN`)
67
67
  - `-e, --endpoint <url>` — Service endpoint (env: `FLAKINESS_ENDPOINT`)
68
- - `--attachments-dir <dir>` — Directory containing attachments (defaults to report directory)
69
68
 
70
69
  ### Download Reports
71
70
 
package/lib/cli/cli.js CHANGED
@@ -813,6 +813,11 @@ var TypedHTTP;
813
813
  ...TypedHTTP2.StatusCodes.ClientErrors,
814
814
  ...TypedHTTP2.StatusCodes.ServerErrors
815
815
  };
816
+ function assert3(value, code, message) {
817
+ if (!value)
818
+ throw HttpError.withCode(code, message);
819
+ }
820
+ TypedHTTP2.assert = assert3;
816
821
  class HttpError extends Error {
817
822
  constructor(status, message) {
818
823
  super(message);
@@ -901,8 +906,8 @@ var TypedHTTP;
901
906
  }
902
907
  TypedHTTP2.Router = Router;
903
908
  function createClient(base, fetchCallback) {
904
- function buildUrl(method, path8, input, options) {
905
- const url = new URL(path8.join("/"), base);
909
+ function buildUrl(method, path7, input, options) {
910
+ const url = new URL(path7.join("/"), base);
906
911
  const signal = options?.signal;
907
912
  let body = void 0;
908
913
  if (method === "GET" && input)
@@ -917,8 +922,8 @@ var TypedHTTP;
917
922
  signal
918
923
  };
919
924
  }
920
- function fetcher(method, path8, input, methodOptions) {
921
- const options = buildUrl(method, path8, input, methodOptions);
925
+ function fetcher(method, path7, input, methodOptions) {
926
+ const options = buildUrl(method, path7, input, methodOptions);
922
927
  return fetchCallback(options.url, {
923
928
  method: options.method,
924
929
  body: options.body,
@@ -941,17 +946,17 @@ var TypedHTTP;
941
946
  }
942
947
  });
943
948
  }
944
- function createProxy(path8 = []) {
949
+ function createProxy(path7 = []) {
945
950
  return new Proxy({}, {
946
951
  get(target, prop) {
947
952
  if (typeof prop === "symbol")
948
953
  return void 0;
949
954
  if (allHttpMethods.includes(prop)) {
950
- const f = fetcher.bind(null, prop, path8);
951
- f.prepare = buildUrl.bind(null, prop, path8);
955
+ const f = fetcher.bind(null, prop, path7);
956
+ f.prepare = buildUrl.bind(null, prop, path7);
952
957
  return f;
953
958
  }
954
- const newPath = [...path8, prop];
959
+ const newPath = [...path7, prop];
955
960
  return createProxy(newPath);
956
961
  }
957
962
  });
@@ -964,14 +969,15 @@ var TypedHTTP;
964
969
  // src/cli/cli.ts
965
970
  import assert2 from "assert";
966
971
  import { Command, Option } from "commander";
967
- import fs7 from "fs";
972
+ import debug2 from "debug";
973
+ import fs6 from "fs";
968
974
  import ora from "ora";
969
- import path7 from "path";
975
+ import path6 from "path";
970
976
 
971
977
  // ../package.json
972
978
  var package_default = {
973
979
  name: "flakiness",
974
- version: "0.158.0",
980
+ version: "0.160.0",
975
981
  type: "module",
976
982
  private: true,
977
983
  scripts: {
@@ -990,10 +996,10 @@ var package_default = {
990
996
  license: "Fair Source 100",
991
997
  devDependencies: {
992
998
  "@flakiness/playwright": "^0.154.0",
993
- "@playwright/test": "^1.57.0",
999
+ "@playwright/test": "catalog:",
994
1000
  "@types/node": "^22.19.3",
995
1001
  esbuild: "^0.27.2",
996
- glob: "^10.5.0",
1002
+ glob: "catalog:",
997
1003
  kubik: "^0.24.0",
998
1004
  tsx: "^4.21.0",
999
1005
  typescript: "^5.9.3"
@@ -1001,157 +1007,13 @@ var package_default = {
1001
1007
  };
1002
1008
 
1003
1009
  // src/flakinessSession.ts
1004
- import fs2 from "fs/promises";
1010
+ import fs from "fs/promises";
1005
1011
  import os from "os";
1006
- import path2 from "path";
1007
-
1008
- // src/utils.ts
1009
- import { ReportUtils } from "@flakiness/sdk";
1010
- import crypto from "crypto";
1011
- import fs from "fs";
1012
- import http from "http";
1013
- import https from "https";
1014
1012
  import path from "path";
1015
- function sha1File(filePath) {
1016
- return new Promise((resolve, reject) => {
1017
- const hash = crypto.createHash("sha1");
1018
- const stream = fs.createReadStream(filePath);
1019
- stream.on("data", (chunk) => {
1020
- hash.update(chunk);
1021
- });
1022
- stream.on("end", () => {
1023
- resolve(hash.digest("hex"));
1024
- });
1025
- stream.on("error", (err2) => {
1026
- reject(err2);
1027
- });
1028
- });
1029
- }
1030
- var FLAKINESS_DBG = !!process.env.FLAKINESS_DBG;
1031
- function errorText(error) {
1032
- return FLAKINESS_DBG ? error.stack : error.message;
1033
- }
1034
- function sha1Buffer(data) {
1035
- const hash = crypto.createHash("sha1");
1036
- hash.update(data);
1037
- return hash.digest("hex");
1038
- }
1039
- async function retryWithBackoff(job, backoff = []) {
1040
- for (const timeout of backoff) {
1041
- try {
1042
- return await job();
1043
- } catch (e) {
1044
- if (e instanceof AggregateError)
1045
- console.error(`[flakiness.io err]`, errorText(e.errors[0]));
1046
- else if (e instanceof Error)
1047
- console.error(`[flakiness.io err]`, errorText(e));
1048
- else
1049
- console.error(`[flakiness.io err]`, e);
1050
- await new Promise((x) => setTimeout(x, timeout));
1051
- }
1052
- }
1053
- return await job();
1054
- }
1055
- var httpUtils;
1056
- ((httpUtils2) => {
1057
- function createRequest({ url, method = "get", headers = {} }) {
1058
- let resolve;
1059
- let reject;
1060
- const responseDataPromise = new Promise((a, b) => {
1061
- resolve = a;
1062
- reject = b;
1063
- });
1064
- const protocol = url.startsWith("https") ? https : http;
1065
- headers = Object.fromEntries(Object.entries(headers).filter(([key, value]) => value !== void 0));
1066
- const request = protocol.request(url, { method, headers }, (res) => {
1067
- const chunks = [];
1068
- res.on("data", (chunk) => chunks.push(chunk));
1069
- res.on("end", () => {
1070
- if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300)
1071
- resolve(Buffer.concat(chunks));
1072
- else
1073
- reject(new Error(`Request to ${url} failed with ${res.statusCode}`));
1074
- });
1075
- res.on("error", (error) => reject(error));
1076
- });
1077
- request.on("error", reject);
1078
- return { request, responseDataPromise };
1079
- }
1080
- httpUtils2.createRequest = createRequest;
1081
- async function getBuffer(url, backoff) {
1082
- return await retryWithBackoff(async () => {
1083
- const { request, responseDataPromise } = createRequest({ url });
1084
- request.end();
1085
- return await responseDataPromise;
1086
- }, backoff);
1087
- }
1088
- httpUtils2.getBuffer = getBuffer;
1089
- async function getText(url, backoff) {
1090
- const buffer = await getBuffer(url, backoff);
1091
- return buffer.toString("utf-8");
1092
- }
1093
- httpUtils2.getText = getText;
1094
- async function getJSON(url) {
1095
- return JSON.parse(await getText(url));
1096
- }
1097
- httpUtils2.getJSON = getJSON;
1098
- async function postText(url, text, backoff) {
1099
- const headers = {
1100
- "Content-Type": "application/json",
1101
- "Content-Length": Buffer.byteLength(text) + ""
1102
- };
1103
- return await retryWithBackoff(async () => {
1104
- const { request, responseDataPromise } = createRequest({ url, headers, method: "post" });
1105
- request.write(text);
1106
- request.end();
1107
- return await responseDataPromise;
1108
- }, backoff);
1109
- }
1110
- httpUtils2.postText = postText;
1111
- async function postJSON(url, json, backoff) {
1112
- const buffer = await postText(url, JSON.stringify(json), backoff);
1113
- return JSON.parse(buffer.toString("utf-8"));
1114
- }
1115
- httpUtils2.postJSON = postJSON;
1116
- })(httpUtils || (httpUtils = {}));
1117
- var ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", "g");
1118
- async function resolveAttachmentPaths(report, attachmentsDir) {
1119
- const attachmentFiles = await listFilesRecursively(attachmentsDir);
1120
- const filenameToPath = new Map(attachmentFiles.map((file) => [path.basename(file), file]));
1121
- const attachmentIdToPath = /* @__PURE__ */ new Map();
1122
- const missingAttachments = /* @__PURE__ */ new Set();
1123
- ReportUtils.visitTests(report, (test) => {
1124
- for (const attempt of test.attempts) {
1125
- for (const attachment of attempt.attachments ?? []) {
1126
- const attachmentPath = filenameToPath.get(attachment.id);
1127
- if (!attachmentPath) {
1128
- missingAttachments.add(attachment.id);
1129
- } else {
1130
- attachmentIdToPath.set(attachment.id, {
1131
- contentType: attachment.contentType,
1132
- id: attachment.id,
1133
- path: attachmentPath,
1134
- type: "file"
1135
- });
1136
- }
1137
- }
1138
- }
1139
- });
1140
- return { attachmentIdToPath, missingAttachments: Array.from(missingAttachments) };
1141
- }
1142
- async function listFilesRecursively(dir, result = []) {
1143
- const entries = await fs.promises.readdir(dir, { withFileTypes: true });
1144
- for (const entry of entries) {
1145
- const fullPath = path.join(dir, entry.name);
1146
- if (entry.isDirectory())
1147
- await listFilesRecursively(fullPath, result);
1148
- else
1149
- result.push(fullPath);
1150
- }
1151
- return result;
1152
- }
1153
1013
 
1154
1014
  // src/serverapi.ts
1015
+ import debug from "debug";
1016
+ var log = debug("fk:server_api");
1155
1017
  function createServerAPI(endpoint, options) {
1156
1018
  endpoint += "/api/";
1157
1019
  const fetcher = options?.auth ? (url, init) => fetch(url, {
@@ -1165,13 +1027,29 @@ function createServerAPI(endpoint, options) {
1165
1027
  return TypedHTTP.createClient(endpoint, (url, init) => retryWithBackoff(() => fetcher(url, init), options.retries));
1166
1028
  return TypedHTTP.createClient(endpoint, fetcher);
1167
1029
  }
1030
+ async function retryWithBackoff(job, backoff = []) {
1031
+ for (const timeout of backoff) {
1032
+ try {
1033
+ return await job();
1034
+ } catch (e) {
1035
+ if (e instanceof AggregateError)
1036
+ console.error(`[flakiness.io err]`, log(e.errors[0]));
1037
+ else if (e instanceof Error)
1038
+ console.error(`[flakiness.io err]`, log(e));
1039
+ else
1040
+ console.error(`[flakiness.io err]`, e);
1041
+ await new Promise((x) => setTimeout(x, timeout));
1042
+ }
1043
+ }
1044
+ return await job();
1045
+ }
1168
1046
 
1169
1047
  // src/flakinessSession.ts
1170
1048
  var CONFIG_DIR = (() => {
1171
- const configDir = process.platform === "darwin" ? path2.join(os.homedir(), "Library", "Application Support", "flakiness") : process.platform === "win32" ? path2.join(os.homedir(), "AppData", "Roaming", "flakiness") : path2.join(os.homedir(), ".config", "flakiness");
1049
+ const configDir = process.platform === "darwin" ? path.join(os.homedir(), "Library", "Application Support", "flakiness") : process.platform === "win32" ? path.join(os.homedir(), "AppData", "Roaming", "flakiness") : path.join(os.homedir(), ".config", "flakiness");
1172
1050
  return configDir;
1173
1051
  })();
1174
- var CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
1052
+ var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
1175
1053
  var FlakinessSession = class _FlakinessSession {
1176
1054
  constructor(_config) {
1177
1055
  this._config = _config;
@@ -1184,14 +1062,14 @@ var FlakinessSession = class _FlakinessSession {
1184
1062
  return session2;
1185
1063
  }
1186
1064
  static async load() {
1187
- const data = await fs2.readFile(CONFIG_PATH, "utf-8").catch((e) => void 0);
1065
+ const data = await fs.readFile(CONFIG_PATH, "utf-8").catch((e) => void 0);
1188
1066
  if (!data)
1189
1067
  return void 0;
1190
1068
  const json = JSON.parse(data);
1191
1069
  return new _FlakinessSession(json);
1192
1070
  }
1193
1071
  static async remove() {
1194
- await fs2.unlink(CONFIG_PATH).catch((e) => void 0);
1072
+ await fs.unlink(CONFIG_PATH).catch((e) => void 0);
1195
1073
  }
1196
1074
  api;
1197
1075
  endpoint() {
@@ -1204,22 +1082,23 @@ var FlakinessSession = class _FlakinessSession {
1204
1082
  return this._config.token;
1205
1083
  }
1206
1084
  async save() {
1207
- await fs2.mkdir(CONFIG_DIR, { recursive: true });
1208
- await fs2.writeFile(CONFIG_PATH, JSON.stringify(this._config, null, 2));
1085
+ await fs.mkdir(CONFIG_DIR, { recursive: true });
1086
+ await fs.writeFile(CONFIG_PATH, JSON.stringify(this._config, null, 2));
1209
1087
  }
1210
1088
  };
1211
1089
 
1212
1090
  // src/cli/cmd-convert.ts
1213
1091
  import { GitWorktree, writeReport } from "@flakiness/sdk";
1214
- import fs4 from "fs/promises";
1215
- import path4 from "path";
1092
+ import fs3 from "fs/promises";
1093
+ import path3 from "path";
1216
1094
 
1217
1095
  // src/junit.ts
1218
- import { ReportUtils as ReportUtils2 } from "@flakiness/sdk";
1096
+ import { ReportUtils } from "@flakiness/sdk";
1219
1097
  import { parseXml, XmlElement, XmlText } from "@rgrove/parse-xml";
1220
1098
  import assert from "assert";
1221
- import fs3 from "fs";
1222
- import path3 from "path";
1099
+ import fs2 from "fs";
1100
+ import mime from "mime";
1101
+ import path2 from "path";
1223
1102
  function getProperties(element) {
1224
1103
  const propertiesNodes = element.children.filter((node) => node instanceof XmlElement).filter((node) => node.name === "properties");
1225
1104
  if (!propertiesNodes.length)
@@ -1261,22 +1140,10 @@ function extractStdout(testcase, stdio) {
1261
1140
  }));
1262
1141
  }
1263
1142
  async function parseAttachment(value) {
1264
- let absolutePath = path3.resolve(process.cwd(), value);
1265
- if (fs3.existsSync(absolutePath)) {
1266
- const id = await sha1File(absolutePath);
1267
- return {
1268
- contentType: "image/png",
1269
- path: absolutePath,
1270
- id,
1271
- type: "file"
1272
- };
1273
- }
1274
- return {
1275
- contentType: "text/plain",
1276
- id: sha1Buffer(value),
1277
- body: Buffer.from(value),
1278
- type: "buffer"
1279
- };
1143
+ let absolutePath = path2.resolve(process.cwd(), value);
1144
+ if (fs2.existsSync(absolutePath))
1145
+ return ReportUtils.createFileAttachment(mime.getType(absolutePath) ?? "image/png", absolutePath);
1146
+ return ReportUtils.createDataAttachment("text/plain", Buffer.from(value));
1280
1147
  }
1281
1148
  async function traverseJUnitReport(context, node) {
1282
1149
  const element = node;
@@ -1337,7 +1204,7 @@ async function traverseJUnitReport(context, node) {
1337
1204
  id: attachment.id,
1338
1205
  contentType: attachment.contentType,
1339
1206
  //TODO: better default names for attachments?
1340
- name: attachment.type === "file" ? path3.basename(attachment.path) : `attachment`
1207
+ name: attachment.type === "file" ? path2.basename(attachment.path) : `attachment`
1341
1208
  });
1342
1209
  } else {
1343
1210
  annotations.push({
@@ -1405,22 +1272,22 @@ async function parseJUnit(xmls, options) {
1405
1272
  await traverseJUnitReport(context, element);
1406
1273
  }
1407
1274
  return {
1408
- report: ReportUtils2.normalizeReport(report),
1275
+ report: ReportUtils.normalizeReport(report),
1409
1276
  attachments: Array.from(context.attachments.values())
1410
1277
  };
1411
1278
  }
1412
1279
 
1413
1280
  // src/cli/cmd-convert.ts
1414
1281
  async function cmdConvert(junitPath, options) {
1415
- const fullPath = path4.resolve(junitPath);
1416
- if (!await fs4.access(fullPath, fs4.constants.F_OK).then(() => true).catch(() => false)) {
1282
+ const fullPath = path3.resolve(junitPath);
1283
+ if (!await fs3.access(fullPath, fs3.constants.F_OK).then(() => true).catch(() => false)) {
1417
1284
  console.error(`Error: path ${fullPath} is not accessible`);
1418
1285
  process.exit(1);
1419
1286
  }
1420
- const stat = await fs4.stat(fullPath);
1287
+ const stat = await fs3.stat(fullPath);
1421
1288
  let xmlContents = [];
1422
1289
  if (stat.isFile()) {
1423
- const xmlContent = await fs4.readFile(fullPath, "utf-8");
1290
+ const xmlContent = await fs3.readFile(fullPath, "utf-8");
1424
1291
  xmlContents.push(xmlContent);
1425
1292
  } else if (stat.isDirectory()) {
1426
1293
  const xmlFiles = await findXmlFiles(fullPath);
@@ -1430,7 +1297,7 @@ async function cmdConvert(junitPath, options) {
1430
1297
  }
1431
1298
  console.log(`Found ${xmlFiles.length} XML files`);
1432
1299
  for (const xmlFile of xmlFiles) {
1433
- const xmlContent = await fs4.readFile(xmlFile, "utf-8");
1300
+ const xmlContent = await fs3.readFile(xmlFile, "utf-8");
1434
1301
  xmlContents.push(xmlContent);
1435
1302
  }
1436
1303
  } else {
@@ -1459,9 +1326,9 @@ async function cmdConvert(junitPath, options) {
1459
1326
  console.log(`\u2713 Saved to ${options.outputDir}`);
1460
1327
  }
1461
1328
  async function findXmlFiles(dir, result = []) {
1462
- const entries = await fs4.readdir(dir, { withFileTypes: true });
1329
+ const entries = await fs3.readdir(dir, { withFileTypes: true });
1463
1330
  for (const entry of entries) {
1464
- const fullPath = path4.join(dir, entry.name);
1331
+ const fullPath = path3.join(dir, entry.name);
1465
1332
  if (entry.isFile() && entry.name.toLowerCase().endsWith(".xml"))
1466
1333
  result.push(fullPath);
1467
1334
  else if (entry.isDirectory())
@@ -1471,10 +1338,10 @@ async function findXmlFiles(dir, result = []) {
1471
1338
  }
1472
1339
 
1473
1340
  // src/cli/cmd-download.ts
1474
- import fs5 from "fs";
1475
- import path5 from "path";
1341
+ import fs4 from "fs";
1342
+ import path4 from "path";
1476
1343
  async function cmdDownload(session2, project, runId, rootDir) {
1477
- if (fs5.existsSync(rootDir)) {
1344
+ if (fs4.existsSync(rootDir)) {
1478
1345
  console.log(`Directory ${rootDir} already exists!`);
1479
1346
  return;
1480
1347
  }
@@ -1483,15 +1350,15 @@ async function cmdDownload(session2, project, runId, rootDir) {
1483
1350
  projectSlug: project.projectSlug,
1484
1351
  runId
1485
1352
  });
1486
- const attachmentsDir = path5.join(rootDir, "attachments");
1487
- await fs5.promises.mkdir(rootDir, { recursive: true });
1353
+ const attachmentsDir = path4.join(rootDir, "attachments");
1354
+ await fs4.promises.mkdir(rootDir, { recursive: true });
1488
1355
  if (urls.attachmentURLs.length)
1489
- await fs5.promises.mkdir(attachmentsDir, { recursive: true });
1356
+ await fs4.promises.mkdir(attachmentsDir, { recursive: true });
1490
1357
  const response = await fetch(urls.reportURL);
1491
1358
  if (!response.ok)
1492
1359
  throw new Error(`HTTP error ${response.status} for report URL: ${urls.reportURL}`);
1493
1360
  const reportContent = await response.text();
1494
- await fs5.promises.writeFile(path5.join(rootDir, "report.json"), reportContent);
1361
+ await fs4.promises.writeFile(path4.join(rootDir, "report.json"), reportContent);
1495
1362
  const attachmentDownloader = async () => {
1496
1363
  while (urls.attachmentURLs.length) {
1497
1364
  const url = urls.attachmentURLs.pop();
@@ -1499,8 +1366,8 @@ async function cmdDownload(session2, project, runId, rootDir) {
1499
1366
  if (!response2.ok)
1500
1367
  throw new Error(`HTTP error ${response2.status} for attachment URL: ${url}`);
1501
1368
  const fileBuffer = Buffer.from(await response2.arrayBuffer());
1502
- const filename = path5.basename(new URL(url).pathname);
1503
- await fs5.promises.writeFile(path5.join(attachmentsDir, filename), fileBuffer);
1369
+ const filename = path4.basename(new URL(url).pathname);
1370
+ await fs4.promises.writeFile(path4.join(attachmentsDir, filename), fileBuffer);
1504
1371
  }
1505
1372
  };
1506
1373
  const workerPromises = [];
@@ -1621,27 +1488,23 @@ async function cmdUnlink() {
1621
1488
  }
1622
1489
 
1623
1490
  // src/cli/cmd-upload.ts
1624
- import { uploadReport } from "@flakiness/sdk";
1491
+ import { readReport, uploadReport } from "@flakiness/sdk";
1625
1492
  import chalk from "chalk";
1626
- import fs6 from "fs/promises";
1627
- import path6 from "path";
1493
+ import fs5 from "fs/promises";
1494
+ import path5 from "path";
1628
1495
  var warn = (txt) => console.warn(chalk.yellow(`[flakiness.io] WARN: ${txt}`));
1629
1496
  var err = (txt) => console.error(chalk.red(`[flakiness.io] Error: ${txt}`));
1630
1497
  async function cmdUpload(relativePaths, options) {
1631
1498
  for (const relativePath of relativePaths) {
1632
- const fullPath = path6.resolve(relativePath);
1633
- if (!await fs6.access(fullPath, fs6.constants.F_OK).then(() => true).catch(() => false)) {
1499
+ const fullPath = path5.resolve(relativePath);
1500
+ if (!await fs5.access(fullPath, fs5.constants.F_OK).then(() => true).catch(() => false)) {
1634
1501
  err(`Path ${fullPath} is not accessible!`);
1635
1502
  process.exit(1);
1636
1503
  }
1637
- const text = await fs6.readFile(fullPath, "utf-8");
1638
- const report = JSON.parse(text);
1639
- const attachmentsDir = options.attachmentsDir ?? path6.dirname(fullPath);
1640
- const { attachmentIdToPath, missingAttachments } = await resolveAttachmentPaths(report, attachmentsDir);
1641
- if (missingAttachments.length) {
1504
+ const { report, attachments, missingAttachments } = await readReport(path5.dirname(fullPath));
1505
+ if (missingAttachments.length)
1642
1506
  warn(`Missing ${missingAttachments.length} attachments`);
1643
- }
1644
- await uploadReport(report, Array.from(attachmentIdToPath.values()), {
1507
+ await uploadReport(report, attachments, {
1645
1508
  flakinessAccessToken: options.accessToken,
1646
1509
  flakinessEndpoint: options.endpoint
1647
1510
  });
@@ -1661,17 +1524,17 @@ async function cmdWhoami() {
1661
1524
  }
1662
1525
 
1663
1526
  // src/cli/cli.ts
1527
+ var log2 = debug2("fk:cli");
1664
1528
  var session = await FlakinessSession.load();
1665
1529
  var optAccessToken = new Option("-t, --access-token <token>", "A read-write flakiness.io access token").env("FLAKINESS_ACCESS_TOKEN");
1666
1530
  var optEndpoint = new Option("-e, --endpoint <url>", "An endpoint where the service is deployed").default(session?.endpoint() ?? DEFAULT_FLAKINESS_ENDPOINT).env("FLAKINESS_ENDPOINT");
1667
- var optAttachmentsDir = new Option("--attachments-dir <dir>", "Directory containing attachments to upload. Defaults to the report directory");
1668
1531
  async function runCommand(callback) {
1669
1532
  try {
1670
1533
  await callback();
1671
1534
  } catch (e) {
1672
1535
  if (!(e instanceof Error))
1673
1536
  throw e;
1674
- console.error(errorText(e));
1537
+ log2(e);
1675
1538
  process.exit(1);
1676
1539
  }
1677
1540
  }
@@ -1775,7 +1638,7 @@ program.command("download").description("Download run").addOption(optSince).addO
1775
1638
  });
1776
1639
  console.log(`Found ${Ranges.cardinality(runIds)} reports uploaded since ${options.since}`);
1777
1640
  }
1778
- const alreadyExisting = fs7.readdirSync(process.cwd());
1641
+ const alreadyExisting = fs6.readdirSync(process.cwd());
1779
1642
  const downloadedRuns = alreadyExisting.filter((entry) => entry.startsWith("fkrun-")).map((run) => parseInt(run.substring(`fkrun-`.length), 10)).filter((runId) => !isNaN(runId));
1780
1643
  console.log(`Found ${downloadedRuns.length} locally downloaded reports`);
1781
1644
  const toBeDownloaded = Ranges.subtract(runIds, Ranges.fromList(downloadedRuns));
@@ -1798,13 +1661,13 @@ program.command("download").description("Download run").addOption(optSince).addO
1798
1661
  await Promise.all(downloaders);
1799
1662
  spinner.stop();
1800
1663
  }));
1801
- program.command("upload").description("Upload Flakiness report to the flakiness.io service").argument("<relative-paths...>", "Paths to the Flakiness report files").addOption(optAccessToken).addOption(optEndpoint).addOption(optAttachmentsDir).action(async (relativePaths, options) => {
1664
+ program.command("upload").description("Upload Flakiness report to the flakiness.io service").argument("<relative-paths...>", "Paths to the Flakiness report files").addOption(optAccessToken).addOption(optEndpoint).action(async (relativePaths, options) => {
1802
1665
  await runCommand(async () => {
1803
1666
  await cmdUpload(relativePaths, await ensureAccessToken(options));
1804
1667
  });
1805
1668
  });
1806
1669
  program.command("show").description("Show flakiness report").argument("[relative-path]", "Path to the Flakiness report file or folder that contains `report.json`. (default: flakiness-report)").action(async (arg) => runCommand(async () => {
1807
- const dir = path7.resolve(arg ?? "flakiness-report");
1670
+ const dir = path6.resolve(arg ?? "flakiness-report");
1808
1671
  await showReport(dir);
1809
1672
  }));
1810
1673
  program.command("convert-junit").description("Convert JUnit XML report(s) to Flakiness report format").argument("<junit-root-dir-path>", "Path to JUnit XML file or directory containing XML files").option("--env-name <name>", "Environment name for the report", "junit").option("--commit-id <id>", "Git commit ID (auto-detected if not provided)").option("--output-dir <dir>", "Output directory for the report", "flakiness-report").action(async (junitPath, options) => {