qualty 0.1.26 → 0.1.29

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.
@@ -1374,8 +1374,9 @@ async function runLocalSingleCombination({
1374
1374
  explicitAuthProfile: String(authProfile || "").trim(),
1375
1375
  noAuthState: Boolean(noAuthState),
1376
1376
  });
1377
+ const explicitAuthProfile = String(authProfile || "").trim();
1377
1378
  const claimProfile = localProfileLabel(claimSavedLogin);
1378
- const effectiveProfile = String(authProfile || "").trim() || setupFromClaim?.profile || claimProfile;
1379
+ const effectiveProfile = explicitAuthProfile || setupFromClaim?.profile || claimProfile;
1379
1380
  const resolvedStoragePath =
1380
1381
  !noAuthState && effectiveProfile
1381
1382
  ? localAuthStatePath({
@@ -1387,17 +1388,20 @@ async function runLocalSingleCombination({
1387
1388
  const finalStoragePath =
1388
1389
  !noAuthState && setupFromClaim?.path && existsSync(setupFromClaim.path)
1389
1390
  ? setupFromClaim.path
1390
- : !noAuthState && storageStatePath && existsSync(storageStatePath)
1391
- ? storageStatePath
1392
- : !noAuthState && resolvedStoragePath && existsSync(resolvedStoragePath)
1393
- ? resolvedStoragePath
1394
- : null;
1391
+ : !noAuthState &&
1392
+ explicitAuthProfile &&
1393
+ storageStatePath &&
1394
+ existsSync(storageStatePath)
1395
+ ? storageStatePath
1396
+ : !noAuthState && resolvedStoragePath && existsSync(resolvedStoragePath)
1397
+ ? resolvedStoragePath
1398
+ : null;
1395
1399
 
1396
1400
  let authSource = "none";
1397
1401
  if (!noAuthState && finalStoragePath) {
1398
1402
  if (setupFromClaim?.path && finalStoragePath === setupFromClaim.path) {
1399
1403
  authSource = "interactive setup during run";
1400
- } else if (storageStatePath && finalStoragePath === storageStatePath) {
1404
+ } else if (explicitAuthProfile && storageStatePath && finalStoragePath === storageStatePath) {
1401
1405
  authSource = "--auth-profile flag";
1402
1406
  } else if (resolvedStoragePath && finalStoragePath === resolvedStoragePath) {
1403
1407
  authSource = claimProfile ? "dashboard saved login profile" : "resolved profile path";
package/bin/qualty.js CHANGED
@@ -30,6 +30,7 @@ import {
30
30
  const DEFAULT_QUALTY_API_URL = "https://qualty-api-production.up.railway.app";
31
31
 
32
32
  const PROJECT_CONFIG_FILENAME = ".qualty.json";
33
+ const DOTENV_FILENAME = ".env";
33
34
  const QUALTY_SHARED_CREDS_DIR = join(homedir(), ".qualty");
34
35
  const QUALTY_SHARED_CREDS_PATH = join(QUALTY_SHARED_CREDS_DIR, "credentials");
35
36
 
@@ -48,6 +49,47 @@ function getProjectConfigWritePath() {
48
49
  return findProjectConfigPath() || join(process.cwd(), PROJECT_CONFIG_FILENAME);
49
50
  }
50
51
 
52
+ function findProjectDotenvPath() {
53
+ let current = process.cwd();
54
+ for (;;) {
55
+ const candidate = join(current, DOTENV_FILENAME);
56
+ if (existsSync(candidate)) return candidate;
57
+ const parent = dirname(current);
58
+ if (parent === current) return null;
59
+ current = parent;
60
+ }
61
+ }
62
+
63
+ function parseDotenvText(text) {
64
+ const out = {};
65
+ for (const raw of String(text || "").split("\n")) {
66
+ const line = raw.trim();
67
+ if (!line || line.startsWith("#") || !line.includes("=")) continue;
68
+ const eq = line.indexOf("=");
69
+ const key = line.slice(0, eq).trim();
70
+ let value = line.slice(eq + 1).trim();
71
+ if (
72
+ (value.startsWith('"') && value.endsWith('"'))
73
+ || (value.startsWith("'") && value.endsWith("'"))
74
+ ) {
75
+ value = value.slice(1, -1);
76
+ }
77
+ if (key) out[key] = value;
78
+ }
79
+ return out;
80
+ }
81
+
82
+ /** Project .env walked up from cwd (same resolution as Qualty MCP). */
83
+ function loadProjectDotenv() {
84
+ const path = findProjectDotenvPath();
85
+ if (!path) return {};
86
+ try {
87
+ return parseDotenvText(readFileSync(path, "utf8"));
88
+ } catch {
89
+ return {};
90
+ }
91
+ }
92
+
51
93
  function readSharedCredentials() {
52
94
  if (!existsSync(QUALTY_SHARED_CREDS_PATH)) return {};
53
95
  try {
@@ -82,6 +124,11 @@ function authProfileFromConfig(projectCfg, fallback = "") {
82
124
  return String(fallback || projectCfg.default_auth_profile || "").trim();
83
125
  }
84
126
 
127
+ /** Only --auth-profile on the CLI; config default is not a run-wide override. */
128
+ function explicitAuthProfileFromArgs(args) {
129
+ return String(args["auth-profile"] || "").trim();
130
+ }
131
+
85
132
  function readProjectConfig() {
86
133
  const configPath = findProjectConfigPath();
87
134
  if (!configPath) return {};
@@ -124,7 +171,14 @@ function normalizeApiUrl(raw, { label = "API URL" } = {}) {
124
171
  }
125
172
 
126
173
  function qualtyBaseUrlFromEnv() {
127
- return String(process.env.QUALTY_BASE_URL || process.env.QUALTY_API_URL || "").trim();
174
+ const dotenv = loadProjectDotenv();
175
+ return String(
176
+ dotenv.QUALTY_BASE_URL
177
+ || dotenv.QUALTY_API_URL
178
+ || process.env.QUALTY_BASE_URL
179
+ || process.env.QUALTY_API_URL
180
+ || ""
181
+ ).trim();
128
182
  }
129
183
 
130
184
  function resolveApiUrl(args = {}) {
@@ -142,8 +196,14 @@ function resolveApiUrl(args = {}) {
142
196
  }
143
197
 
144
198
  function resolveToken(args = {}) {
199
+ if (args.token) return String(args.token).trim();
200
+ const dotenv = loadProjectDotenv();
201
+ const fromDotenv = String(dotenv.QUALTY_API_TOKEN || "").trim();
202
+ if (fromDotenv) return fromDotenv;
203
+ const fromProcessEnv = String(process.env.QUALTY_API_TOKEN || "").trim();
204
+ if (fromProcessEnv) return fromProcessEnv;
145
205
  const creds = readSharedCredentials();
146
- return args.token || process.env.QUALTY_API_TOKEN || creds.api_token || "";
206
+ return String(creds.api_token || "").trim();
147
207
  }
148
208
 
149
209
  function resolveOrgId(args = {}) {
@@ -187,6 +247,10 @@ function usage() {
187
247
  "Usage:",
188
248
  " qualty setup [--token <bearer-token>] [--org <org-id>]",
189
249
  " qualty auth setup [--project <project-id>] [--profile <profile-name>] [--headed]",
250
+ " qualty auth export [--project <project-id>] [--profile <profile-name>] [--org <org-id>]",
251
+ " [--profiles <name1,name2>] [--repo <owner/repo>]",
252
+ " [--output <path>] [--no-file] [--copy] [--set-secret | --set-github]",
253
+ " Push login profile state to GitHub only (not API token, org, or project vars).",
190
254
  " qualty ci setup [--project <project-id>] [--profile <profile-name>] [--org <org-id>]",
191
255
  " [--all-profiles] [--profiles <name1,name2>]",
192
256
  " [--suite-id <suite-uuid>] [--ids <id1,id2>] bundle login profiles required by tests",
@@ -206,11 +270,11 @@ function usage() {
206
270
  " qualty resolve --project <project-id> [--suite-id <suite-id>] [--ids <id1,id2>] [--json] [--token <bearer-token>]",
207
271
  "",
208
272
  "Env vars:",
209
- " QUALTY_API_TOKEN Bearer token used for auth",
273
+ " QUALTY_API_TOKEN Project .env first (same as MCP), then process env, then ~/.qualty/credentials",
210
274
  " QUALTY_BASE_URL API base URL when --url is not set (default: production)",
211
275
  " QUALTY_LOCAL_CONCURRENCY Local parallel workers for --local runs (default: 4)",
212
276
  "",
213
- "Config (.qualty.json): default_org_id, default_project_id, default_auth_profile",
277
+ "Config (.qualty.json): default_org_id, default_project_id, default_auth_profile (ci/auth setup only; local runs use each test's saved login profile unless --auth-profile is set)",
214
278
  " CLI flags (--org, --project, --profile, --url) override config values.",
215
279
  ].join("\n")
216
280
  );
@@ -393,9 +457,7 @@ async function runSetup(args) {
393
457
 
394
458
  try {
395
459
  const currentOrgId = String(args.org || projectCfg.default_org_id || "");
396
- const currentToken = String(
397
- args.token || process.env.QUALTY_API_TOKEN || sharedCreds.api_token || ""
398
- );
460
+ const currentToken = resolveToken(args);
399
461
 
400
462
  let token = "";
401
463
  if (args.token) {
@@ -674,6 +736,37 @@ function syncGithubCiConfig({
674
736
  return results;
675
737
  }
676
738
 
739
+ /**
740
+ * Push only auth bundle secrets and auth-related repository variables (no API token or org/project).
741
+ */
742
+ function syncGithubAuthProfileOnly({ repo, profile, profileNames, authShards }) {
743
+ const results = [];
744
+ const record = (kind, name, outcome) => {
745
+ results.push({ kind, name, ...outcome });
746
+ };
747
+
748
+ for (const shard of authShards || []) {
749
+ record("secret", shard.secretName, setGithubSecret(shard.secretName, shard.payload, { repo }));
750
+ }
751
+
752
+ const names = (profileNames || []).filter(Boolean);
753
+ if (names.length > 0) {
754
+ record("variable", AUTH_PROFILES_VARIABLE, setGithubVariable(AUTH_PROFILES_VARIABLE, names.join(","), { repo }));
755
+ }
756
+ if (profile) {
757
+ record("variable", "QUALTY_AUTH_PROFILE", setGithubVariable("QUALTY_AUTH_PROFILE", profile, { repo }));
758
+ }
759
+ if ((authShards || []).length > 1) {
760
+ record(
761
+ "variable",
762
+ AUTH_SHARDS_VARIABLE,
763
+ setGithubVariable(AUTH_SHARDS_VARIABLE, String(authShards.length), { repo })
764
+ );
765
+ }
766
+
767
+ return results;
768
+ }
769
+
677
770
  function printGithubSyncResults(results, { repo }) {
678
771
  const target = repo ? ` (repo ${repo})` : "";
679
772
  const printGroup = (title, rows) => {
@@ -1033,7 +1126,7 @@ async function runAuthRestoreCi(args) {
1033
1126
  const payloads = collectAuthShardPayloadsFromEnv(process.env);
1034
1127
  if (payloads.length === 0) {
1035
1128
  throw new Error(
1036
- `Missing ${AUTH_SECRET_PRIMARY}. Run qualty ci setup --set-github from your app repo.`
1129
+ `Missing ${AUTH_SECRET_PRIMARY}. Run qualty ci setup --set-github (first time) or qualty auth export --set-github (profile refresh).`
1037
1130
  );
1038
1131
  }
1039
1132
 
@@ -1052,6 +1145,197 @@ async function runAuthRestoreCi(args) {
1052
1145
  console.log(`Restored ${restored.length} login profile(s).`);
1053
1146
  }
1054
1147
 
1148
+ async function runAuthExport(args) {
1149
+ if (
1150
+ parseBoolean(args["all-profiles"], false) ||
1151
+ String(args["suite-id"] || args.suite || "").trim() ||
1152
+ String(args.ids || "")
1153
+ .split(",")
1154
+ .map((part) => part.trim())
1155
+ .filter(Boolean).length > 0
1156
+ ) {
1157
+ throw new Error(
1158
+ "qualty auth export only updates login profile secrets. For full CI bootstrap use: qualty ci setup"
1159
+ );
1160
+ }
1161
+
1162
+ const profilesList = String(args.profiles || "")
1163
+ .split(",")
1164
+ .map((part) => part.trim())
1165
+ .filter(Boolean);
1166
+ const { orgId, projectId, profile, apiUrl, configPath } = resolveCiSetupConfig(args, {
1167
+ requireProfile: profilesList.length === 0,
1168
+ });
1169
+ const token = resolveToken(args);
1170
+
1171
+ const profileEntries = await resolveCiSetupProfileEntries({
1172
+ orgId,
1173
+ projectId,
1174
+ profile,
1175
+ allProfiles: false,
1176
+ profilesList,
1177
+ suiteId: "",
1178
+ explicitIds: [],
1179
+ apiUrl,
1180
+ token,
1181
+ });
1182
+ const profileNames = profileEntries.map((entry) => entry.name);
1183
+ const primaryProfile = profile || profileNames[0] || "";
1184
+
1185
+ const manifest = buildAuthBundleManifest({ orgId, projectId, profileEntries });
1186
+ const authShards = shardAuthBundleManifest(manifest);
1187
+ const primaryShard = authShards[0];
1188
+
1189
+ const setGithubFlag =
1190
+ parseBoolean(args["set-github"], false) || parseBoolean(args["set-secret"], false);
1191
+ const noFile = parseBoolean(args["no-file"], false);
1192
+ const outputPath = noFile
1193
+ ? String(args.output || "").trim()
1194
+ : String(args.output || "").trim() ||
1195
+ ciSetupAuthBundleOutputPath(
1196
+ localAuthStatePath({ orgId, projectId, profile: profileEntries[0].localProfileName })
1197
+ );
1198
+ const copyToClipboardFlag = parseBoolean(args.copy, false);
1199
+ const githubRepo = String(args.repo || "").trim();
1200
+
1201
+ if (outputPath) {
1202
+ writeFileSync(outputPath, primaryShard.payload, "utf8");
1203
+ try {
1204
+ chmodSync(outputPath, 0o600);
1205
+ } catch {
1206
+ // ignore platforms that do not support chmod
1207
+ }
1208
+ for (let index = 1; index < authShards.length; index += 1) {
1209
+ const shard = authShards[index];
1210
+ const shardPath = outputPath.replace(/(\.[^./]+)?$/, `_${index + 1}$1`);
1211
+ writeFileSync(shardPath, shard.payload, "utf8");
1212
+ try {
1213
+ chmodSync(shardPath, 0o600);
1214
+ } catch {
1215
+ // ignore
1216
+ }
1217
+ }
1218
+ }
1219
+
1220
+ const skipPayloadPrint = Boolean(outputPath || copyToClipboardFlag || setGithubFlag);
1221
+ const totalSecretBytes = authShards.reduce(
1222
+ (sum, shard) => sum + Buffer.byteLength(shard.payload, "utf8"),
1223
+ 0
1224
+ );
1225
+
1226
+ // eslint-disable-next-line no-console
1227
+ console.log("");
1228
+ // eslint-disable-next-line no-console
1229
+ console.log("=== Qualty → GitHub Actions (login profiles only) ===");
1230
+ // eslint-disable-next-line no-console
1231
+ console.log("");
1232
+ // eslint-disable-next-line no-console
1233
+ console.log(
1234
+ `Auth bundle: ${profileNames.length} profile(s), ${authShards.length} secret shard(s), ${totalSecretBytes} bytes total`
1235
+ );
1236
+ for (const shard of authShards) {
1237
+ // eslint-disable-next-line no-console
1238
+ console.log(
1239
+ ` - ${shard.secretName}: ${shard.profileNames.join(", ")} (${Buffer.byteLength(shard.payload, "utf8")} bytes)`
1240
+ );
1241
+ }
1242
+ // eslint-disable-next-line no-console
1243
+ console.log("");
1244
+ if (outputPath) {
1245
+ // eslint-disable-next-line no-console
1246
+ console.log(`Wrote ${AUTH_SECRET_PRIMARY} to ${outputPath}`);
1247
+ for (let index = 1; index < authShards.length; index += 1) {
1248
+ const shard = authShards[index];
1249
+ const shardPath = outputPath.replace(/(\.[^./]+)?$/, `_${index + 1}$1`);
1250
+ // eslint-disable-next-line no-console
1251
+ console.log(`Wrote ${shard.secretName} to ${shardPath}`);
1252
+ }
1253
+ // eslint-disable-next-line no-console
1254
+ console.log("");
1255
+ }
1256
+ if (setGithubFlag) {
1257
+ const syncResults = syncGithubAuthProfileOnly({
1258
+ repo: githubRepo,
1259
+ profile: primaryProfile,
1260
+ profileNames,
1261
+ authShards,
1262
+ });
1263
+ printGithubSyncResults(syncResults, { repo: githubRepo });
1264
+ const failedAuth = syncResults.filter((row) => row.kind === "secret" && !row.ok);
1265
+ if (failedAuth.length > 0 && outputPath) {
1266
+ // eslint-disable-next-line no-console
1267
+ console.log(" Manual auth secrets:");
1268
+ authShards.forEach((shard, index) => {
1269
+ const shardPath =
1270
+ index === 0 ? outputPath : outputPath.replace(/(\.[^./]+)?$/, `_${index + 1}$1`);
1271
+ // eslint-disable-next-line no-console
1272
+ console.log(` gh secret set ${shard.secretName} < ${shardPath}`);
1273
+ });
1274
+ // eslint-disable-next-line no-console
1275
+ console.log("");
1276
+ }
1277
+ } else {
1278
+ // eslint-disable-next-line no-console
1279
+ console.log("GitHub secrets (login state only — does not update QUALTY_API_TOKEN or org/project):");
1280
+ // eslint-disable-next-line no-console
1281
+ console.log("");
1282
+ for (const shard of authShards) {
1283
+ // eslint-disable-next-line no-console
1284
+ console.log("---");
1285
+ // eslint-disable-next-line no-console
1286
+ console.log(`Name: ${shard.secretName}`);
1287
+ if (skipPayloadPrint) {
1288
+ // eslint-disable-next-line no-console
1289
+ console.log(
1290
+ `Value: (see file above${copyToClipboardFlag && shard.secretName === AUTH_SECRET_PRIMARY ? " or clipboard" : ""})`
1291
+ );
1292
+ } else {
1293
+ // eslint-disable-next-line no-console
1294
+ console.log("Value:");
1295
+ // eslint-disable-next-line no-console
1296
+ console.log(shard.payload);
1297
+ }
1298
+ }
1299
+ // eslint-disable-next-line no-console
1300
+ console.log("---");
1301
+ // eslint-disable-next-line no-console
1302
+ console.log("");
1303
+ // eslint-disable-next-line no-console
1304
+ console.log(
1305
+ "Re-run with --set-github from this repo's git root to push via gh (after qualty ci setup for first-time CI)."
1306
+ );
1307
+ // eslint-disable-next-line no-console
1308
+ console.log("");
1309
+ }
1310
+ if (copyToClipboardFlag) {
1311
+ if (copyToClipboard(primaryShard.payload)) {
1312
+ // eslint-disable-next-line no-console
1313
+ console.log(`✓ Copied ${AUTH_SECRET_PRIMARY} to clipboard.`);
1314
+ } else {
1315
+ // eslint-disable-next-line no-console
1316
+ console.log("Could not copy to clipboard on this machine.");
1317
+ }
1318
+ // eslint-disable-next-line no-console
1319
+ console.log("");
1320
+ }
1321
+ for (const entry of profileEntries) {
1322
+ // eslint-disable-next-line no-console
1323
+ console.log(`Profile "${entry.name}" → ${entry.statePath}`);
1324
+ }
1325
+ if (configPath) {
1326
+ // eslint-disable-next-line no-console
1327
+ console.log(`Config: ${configPath}`);
1328
+ }
1329
+ // eslint-disable-next-line no-console
1330
+ console.log("");
1331
+ // eslint-disable-next-line no-console
1332
+ console.log(
1333
+ "When login expires, re-run qualty auth setup then qualty auth export --set-github (or qualty ci setup for a full refresh)."
1334
+ );
1335
+ // eslint-disable-next-line no-console
1336
+ console.log("");
1337
+ }
1338
+
1055
1339
  async function runCiSetup(args) {
1056
1340
  const {
1057
1341
  orgId,
@@ -1359,7 +1643,9 @@ async function runCiSetup(args) {
1359
1643
  // eslint-disable-next-line no-console
1360
1644
  console.log("");
1361
1645
  // eslint-disable-next-line no-console
1362
- console.log("When login expires, re-run qualty auth setup and qualty ci setup --set-github to refresh secrets.");
1646
+ console.log(
1647
+ "When login expires, re-run qualty auth setup and qualty auth export --set-github (or qualty ci setup --set-github for a full refresh)."
1648
+ );
1363
1649
  // eslint-disable-next-line no-console
1364
1650
  console.log("");
1365
1651
  }
@@ -2217,8 +2503,7 @@ async function maybePromptProjectIdForLocalRun({ args, apiUrl, token, orgId }) {
2217
2503
 
2218
2504
  function resolveLocalAuthStateForRun({ args, orgId, projectId }) {
2219
2505
  if (parseBoolean(args["no-auth-state"], false)) return null;
2220
- const projectCfg = readProjectConfig();
2221
- const profile = authProfileFromConfig(projectCfg, args["auth-profile"]);
2506
+ const profile = explicitAuthProfileFromArgs(args);
2222
2507
  if (!profile) return null;
2223
2508
  const path = localAuthStatePath({ orgId, projectId, profile });
2224
2509
  const summary = summarizeStorageStateFile(path);
@@ -2232,12 +2517,10 @@ function resolveLocalAuthStateForRun({ args, orgId, projectId }) {
2232
2517
 
2233
2518
  async function maybePromptAuthProfileForLocalRun({ args, orgId, projectId }) {
2234
2519
  const projectCfg = readProjectConfig();
2235
- const requestedProfile = authProfileFromConfig(projectCfg, args["auth-profile"]);
2520
+ const requestedProfile = explicitAuthProfileFromArgs(args);
2236
2521
  if (requestedProfile) return { authProfile: requestedProfile, noAuthState: false };
2237
2522
  if (parseBoolean(args["no-auth-state"], false)) return { authProfile: "", noAuthState: true };
2238
2523
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
2239
- const defaultProfile = authProfileFromConfig(projectCfg);
2240
- if (defaultProfile) return { authProfile: defaultProfile, noAuthState: false };
2241
2524
  return { authProfile: "", noAuthState: false };
2242
2525
  }
2243
2526
  if (!projectId) return { authProfile: "", noAuthState: false };
@@ -2472,8 +2755,8 @@ async function runAuthSetup(args) {
2472
2755
  console.log("");
2473
2756
  // eslint-disable-next-line no-console
2474
2757
  console.log(
2475
- `[qualty][auth] For GitHub Actions secrets, run:\n` +
2476
- ` qualty ci setup --project ${project.id} --profile "${profile}"`
2758
+ `[qualty][auth] Push this profile to GitHub Actions:\n` +
2759
+ ` qualty auth export --project ${project.id} --profile "${profile}" --set-github`
2477
2760
  );
2478
2761
  } finally {
2479
2762
  rl.close();
@@ -2509,11 +2792,15 @@ async function main() {
2509
2792
  await runAuthSetup(args);
2510
2793
  return;
2511
2794
  }
2795
+ if (sub === "export") {
2796
+ await runAuthExport(args);
2797
+ return;
2798
+ }
2512
2799
  if (sub === "restore-ci") {
2513
2800
  await runAuthRestoreCi(args);
2514
2801
  return;
2515
2802
  }
2516
- throw new Error("Unknown auth subcommand. Use: qualty auth setup | qualty auth restore-ci");
2803
+ throw new Error("Unknown auth subcommand. Use: qualty auth setup | qualty auth export | qualty auth restore-ci");
2517
2804
  }
2518
2805
  if (command === "run") {
2519
2806
  if (parseBoolean(args.local, false)) {
@@ -2546,7 +2833,7 @@ async function main() {
2546
2833
  projectId,
2547
2834
  })
2548
2835
  : {
2549
- authProfile: String(args["auth-profile"] || "").trim(),
2836
+ authProfile: explicitAuthProfileFromArgs(args),
2550
2837
  noAuthState: parseBoolean(args["no-auth-state"], false),
2551
2838
  };
2552
2839
  const localAuthArgs = {
@@ -2569,9 +2856,9 @@ async function main() {
2569
2856
  device: args.device ? String(args.device).trim() : undefined,
2570
2857
  headless: !parseBoolean(args.headed, false),
2571
2858
  localConcurrency: resolveLocalConcurrency(args),
2572
- authProfile: localAuth?.profile || authChoice.authProfile || "",
2859
+ authProfile: authChoice.authProfile || "",
2573
2860
  noAuthState: Boolean(authChoice.noAuthState || parseBoolean(args["no-auth-state"], false)),
2574
- storageStatePath: localAuth?.path,
2861
+ storageStatePath: authChoice.authProfile ? localAuth?.path : undefined,
2575
2862
  };
2576
2863
  if (suiteId && explicitIds.length === 0) {
2577
2864
  await runLocalSuiteWithSequences(localRunArgs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qualty",
3
- "version": "0.1.26",
3
+ "version": "0.1.29",
4
4
  "description": "Qualty CLI for localhost and CI test runs",
5
5
  "bin": {
6
6
  "qualty": "bin/qualty.js"