@qawolf/cli 1.29.0 → 1.31.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/dist/cli.js CHANGED
@@ -8382,17 +8382,13 @@ function isTimeoutError(err) {
8382
8382
  }
8383
8383
  var missingPackagePattern = /Cannot find (?:package|module) '([^']+)'/;
8384
8384
  var pathLikeSpecifierPattern = /^(?:\.|\/|[A-Za-z]:[\\/])/;
8385
- function extractMissingPackage(text) {
8385
+ var sourceFileSpecifierPattern = /\.(?:[cm]?[jt]sx?)$/;
8386
+ function extractMissingSpecifier(text) {
8386
8387
  const specifier = missingPackagePattern.exec(text)?.[1];
8387
8388
  if (specifier === undefined || pathLikeSpecifierPattern.test(specifier)) {
8388
8389
  return;
8389
8390
  }
8390
- return specifier;
8391
- }
8392
-
8393
- // src/core/pluralize.ts
8394
- function pluralize(count, singular, plural = `${singular}s`) {
8395
- return `${String(count)} ${count === 1 ? singular : plural}`;
8391
+ return sourceFileSpecifierPattern.test(specifier) ? { kind: "path-alias", specifier } : { kind: "package", specifier };
8396
8392
  }
8397
8393
 
8398
8394
  // src/core/formatSeconds.ts
@@ -8400,6 +8396,33 @@ function formatSeconds(ms) {
8400
8396
  return `${Math.round(ms / 1000)}s`;
8401
8397
  }
8402
8398
 
8399
+ // src/core/messages/agent.ts
8400
+ var followAgain = (sessionId) => `qawolf agent get --follow --session ${sessionId}`;
8401
+ var sessionGivenTwice = (argument, flag) => `Two different sessions were named: "${argument}" as an argument and "${flag}" as --session. Pass one of them.`;
8402
+ var agentMessages = {
8403
+ answerCancelled: "No answer sent, so QA Wolf is still waiting on one. The session keeps its place: answer it whenever you like.",
8404
+ answerPrompt: "Your answer",
8405
+ answerSendFailed: (reason) => `The answer could not be sent: ${reason}. The session is still waiting, so try again.`,
8406
+ blocked: (sessionId) => `QA Wolf asked a question and cannot continue until it is answered. Answer it with qawolf agent send "<your answer>" --session ${sessionId}.`,
8407
+ detachHint: "Press Ctrl-C to stop following. The session keeps running in the background.",
8408
+ detached: (sessionId) => `Stopped following. The session is still running — pick it back up with ${followAgain(sessionId)}.`,
8409
+ followTimedOut: (sessionId, seconds) => `Stopped following after ${formatSeconds(seconds * 1000)}. The session may still be going: pick it back up with ${followAgain(sessionId)}, or pass --timeout to wait longer.`,
8410
+ noSession: "No session named. Pass --session, set QAWOLF_SESSION_ID, or start one with qawolf agent send.",
8411
+ sessionNotRemembered: (sessionId) => `The session id could not be saved to .qawolf/agent.json, so pass --session ${sessionId} to pick it up later.`,
8412
+ sessionCancelled: (url) => `The session was cancelled before it finished. Read how far it got at ${url}.`,
8413
+ sessionCompleted: "QA Wolf finished the work.",
8414
+ sessionFailed: (url) => `The session failed. Read what happened at ${url}.`,
8415
+ sessionAt: (url) => `Following ${url}`,
8416
+ standing: (status, url) => `${status} — ${url}`,
8417
+ started: (sessionId, url) => `Started session ${sessionId} — ${url}`,
8418
+ waiting: "Waiting for QA Wolf",
8419
+ working: "QA Wolf is working. It reports back here as it goes, which can take a few minutes."
8420
+ };
8421
+ // src/core/pluralize.ts
8422
+ function pluralize(count, singular, plural = `${singular}s`) {
8423
+ return `${String(count)} ${count === 1 ? singular : plural}`;
8424
+ }
8425
+
8403
8426
  // src/core/messages/authErrors.ts
8404
8427
  var authErrorMessages = {
8405
8428
  identity: {
@@ -8413,7 +8436,14 @@ var authErrorMessages = {
8413
8436
  rejected401: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 401). Check your API key.`,
8414
8437
  rejected402: (noun) => `QA Wolf API refused the${noun ? ` ${noun}` : ""} request (HTTP 402): billing prevented it.`,
8415
8438
  rejected403: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 403). Check that your API key has access to this environment.`,
8416
- notFound404: (noun) => `QA Wolf API could not find ${noun ? `${noun} for that environment` : "that environment"} (HTTP 404). Check the --env value.`,
8439
+ notFound404Environment: (noun) => `QA Wolf API could not find ${noun ? `${noun} for that environment` : "that environment"} (HTTP 404). Check the --env value.`,
8440
+ notFound404Runner: (runnerId) => runnerId === undefined ? "That runner is not running (HTTP 404)." : `Runner ${runnerId} is not running (HTTP 404).`,
8441
+ runnerIsGone: "It was never launched, or it has since been terminated or idled out.",
8442
+ launchTheRunner: (runnerId) => runnerId === undefined ? "Launch one with qawolf runner launch --id <id>, or name a running one with --runner." : `Launch it with qawolf runner launch --id ${runnerId}, or send this to a different runner with --runner.`,
8443
+ notFound404Run: (runId) => `QA Wolf has no run ${runId ?? "by that id"} on this team (HTTP 404).`,
8444
+ runIdMayBeRunnerLocal: (runId) => `A run id printed by qawolf runner run belongs to that runner rather than to the platform, so this command cannot resolve it. Read that run with qawolf runner events run-status --run ${runId ?? "<id>"}.`,
8445
+ notFound404Record: (noun, id) => `QA Wolf has no ${noun} ${id} (HTTP 404).`,
8446
+ notFound404: (noun) => noun === undefined ? "QA Wolf found nothing matching the request (HTTP 404)." : `QA Wolf found nothing matching the ${noun} request (HTTP 404).`,
8417
8447
  failedWithStatus: (status, noun) => `QA Wolf API${noun ? ` ${noun}` : ""} request failed (HTTP ${status}).`,
8418
8448
  networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and QAWOLF_HOST_URL.`,
8419
8449
  timedOut: (timeoutMs, noun) => `The QA Wolf API${noun ? ` ${noun}` : ""} request timed out after ${formatSeconds(timeoutMs)}. The work may still be finishing on the platform.`,
@@ -8669,7 +8699,7 @@ var flowsMessages = {
8669
8699
  extractingBundle: "Extracting bundle",
8670
8700
  downloadingTeamStorageAssets: "Downloading team-storage assets",
8671
8701
  downloadingTeamStorageAssetsProgress: (current, total) => `Downloading team-storage assets (${String(current)}/${String(total)})`,
8672
- teamStorageRequiresTeamKey: "Team storage requires a team API key; organization keys are not supported here.",
8702
+ teamStorageRequiresTeam: "Team storage needs a team. Pull an environment to name its team, choose a workspace with 'qawolf auth switch', or use a team API key.",
8673
8703
  summary: (result, assetsAbs) => {
8674
8704
  const flows = pluralize(result.flowCount, "flow");
8675
8705
  const envVars = result.envVarCount === 0 ? "" : ` and ${pluralize(result.envVarCount, "environment variable")}`;
@@ -8844,17 +8874,18 @@ var interactMessages = {
8844
8874
  };
8845
8875
 
8846
8876
  // src/core/messages/interactiveRunner/lifecycle.ts
8877
+ var pageAt = (url) => `Its runner page is at ${url}`;
8847
8878
  var noRunnerId = "No runner id. Pass --runner, set QAWOLF_RUNNER_ID, or run qawolf runner launch first.";
8848
8879
  var lifecycleMessages = {
8849
- alreadyRunning: (id) => `Runner ${id} was already running.`,
8880
+ alreadyRunning: (id, url) => `Runner ${id} was already running. ${pageAt(url)}`,
8850
8881
  defaultNotRemembered: (id) => `Runner ${id} could not be written to .qawolf as this directory's default, so later commands will not find it on their own. Pass --runner ${id}, or set QAWOLF_RUNNER_ID=${id}.`,
8851
8882
  defaultNotForgotten: (id) => `Runner ${id} was terminated, but it could not be removed from .qawolf as this directory's default, so later commands will still be sent to it. Pass --runner, or launch a new runner.`,
8852
8883
  envRunnerIdShadowsLaunch: (launchedId, envId) => `QAWOLF_RUNNER_ID=${envId} is still set, so commands that omit --runner will keep targeting it instead of the runner just launched. Pass --runner ${launchedId} to address this one.`,
8853
8884
  keptAlive: (id) => `Runner ${id} is alive, and its inactivity clock has been reset.`,
8854
8885
  launchFailed: (id, error) => `Launching runner ${id} failed. ${error}`,
8855
8886
  launchLost: (id, error) => `Launching runner ${id} failed. ${error}. The request may still have reached QA Wolf and started a runner, so relaunch the same id with qawolf runner launch --id ${id} to attach to it rather than start a second one, or terminate it with qawolf runner terminate --runner ${id}.`,
8856
- launched: (id) => `Launched runner ${id}.`,
8857
- launchedForCommand: (id) => `No runner was given, so launched ${id} for this command. Its browser is fresh: nothing has been run on it and nothing is signed in. It bills until it is terminated or idles out, so terminate it with qawolf runner terminate --runner ${id} when you are done.`,
8887
+ launched: (id, url) => `Launched runner ${id}. ${pageAt(url)}`,
8888
+ launchedForCommand: (id, url) => `No runner was given, so launched ${id} for this command. Its browser is fresh: nothing has been run on it and nothing is signed in. It bills until it is terminated or idles out, so terminate it with qawolf runner terminate --runner ${id} when you are done. ${pageAt(url)}`,
8858
8889
  noRunnerIdForImport: `${noRunnerId} An install also needs a live run to go into, which a runner gets from qawolf runner run.`,
8859
8890
  noRunnerIdForHighlight: `${noRunnerId} Highlighting also needs a live page to draw on, which a runner gets from its first run: run a flow on it with qawolf runner run.`,
8860
8891
  noRunnerIdForInspect: `${noRunnerId} Inspecting also needs a page, which a runner gets from its first run: run a flow on it with qawolf runner run.`,
@@ -8865,7 +8896,14 @@ var lifecycleMessages = {
8865
8896
  terminated: (id) => `Terminated runner ${id}.`,
8866
8897
  runStopped: (id) => `Stopped the run on runner ${id}. The runner is still up, on whatever page the run reached.`,
8867
8898
  nothingToStop: (id) => `Runner ${id} had nothing to stop. The run had already finished, or none had been submitted.`,
8868
- runnerUnreachable: "The runner could not be reached. It may still be starting, or it may have terminated after inactivity. Retry, or launch it again."
8899
+ runnerUnreachable: "The runner could not be reached. It may still be starting, or it may have terminated after inactivity. Retry, or launch it again.",
8900
+ runnerIdCameFrom: (id, source) => ({
8901
+ environment: `The id ${id} came from QAWOLF_RUNNER_ID.`,
8902
+ flag: `The id ${id} came from --runner.`,
8903
+ given: `The id ${id} is the one this call named.`,
8904
+ launched: `Runner ${id} was launched for this command.`,
8905
+ stored: `The id ${id} came from the runner this directory last launched, recorded in .qawolf.`
8906
+ })[source]
8869
8907
  };
8870
8908
 
8871
8909
  // src/core/messages/interactiveRunner/list.ts
@@ -8964,7 +9002,9 @@ ${dir}`,
8964
9002
  ].join(`
8965
9003
  `),
8966
9004
  outerHopCarriedOver: (names) => `Carried over ${pluralize(names.length, "undeclared package")} from the project node_modules: ${listNames(names)}. Declare them in package.json "dependencies".`,
8967
- moduleNotFoundHint: (pkg, projectDir) => projectDir === undefined ? `Hint: '${pkg}' could not be resolved. Run from within your flows project so its dependencies can be found.` : `Hint: '${pkg}' could not be resolved. Ensure it is declared in ${projectDir}/package.json "dependencies" and run npm install in that project.`
9005
+ moduleNotFoundHint: (pkg, projectDir) => projectDir === undefined ? `Hint: '${pkg}' could not be resolved. Run from within your flows project so its dependencies can be found.` : `Hint: '${pkg}' could not be resolved. Ensure it is declared in ${projectDir}/package.json "dependencies" and run npm install in that project.`,
9006
+ tsconfigUnparsedNotice: (projectDir) => `Not resolving path aliases: ${projectDir}/tsconfig.json is not valid JSON. Comments and trailing commas are not supported, here or on the platform. Remove them so "compilerOptions.paths" can be read.`,
9007
+ pathAliasNotFoundHint: (specifier, projectDir) => projectDir === undefined ? `Hint: '${specifier}' looks like a tsconfig path alias. Run from within your flows project so its tsconfig.json can be found.` : `Hint: '${specifier}' looks like a tsconfig path alias. Ensure "compilerOptions.paths" in ${projectDir}/tsconfig.json maps it to a file that exists.`
8968
9008
  };
8969
9009
  // src/core/messages/updateCheck.ts
8970
9010
  var updateCheckMessages = {
@@ -9088,7 +9128,8 @@ var exitCodes = {
9088
9128
  network: 4,
9089
9129
  config: 5,
9090
9130
  timeout: 6,
9091
- payment: 7
9131
+ payment: 7,
9132
+ notFound: 8
9092
9133
  };
9093
9134
  function exit(code, message, proc = process) {
9094
9135
  if (message !== undefined && message.length > 0) {
@@ -9237,8 +9278,10 @@ function resolveHostUrl(env) {
9237
9278
  return configuredHostUrl.replace(/\/+$/, "");
9238
9279
  }
9239
9280
 
9240
- // src/domains/auth/resolve.ts
9241
- import { Entry as Entry5 } from "@napi-rs/keyring";
9281
+ // src/shell/keyring.ts
9282
+ function loadEntryClass() {
9283
+ return import("@napi-rs/keyring").then((keyring) => keyring.Entry);
9284
+ }
9242
9285
 
9243
9286
  // src/core/deviceAuth/tokenExpiry.ts
9244
9287
  function readAccessTokenExpiry(accessToken) {
@@ -11881,6 +11924,56 @@ function handlePipeResult(left, next, ctx) {
11881
11924
  }
11882
11925
  return next._zod.run({ value: left.value, issues: left.issues, fallback: left.fallback }, ctx);
11883
11926
  }
11927
+ var $ZodCodec = /* @__PURE__ */ $constructor("$ZodCodec", (inst, def) => {
11928
+ $ZodType.init(inst, def);
11929
+ defineLazy(inst._zod, "values", () => def.in._zod.values);
11930
+ defineLazy(inst._zod, "optin", () => def.in._zod.optin);
11931
+ defineLazy(inst._zod, "optout", () => def.out._zod.optout);
11932
+ defineLazy(inst._zod, "propValues", () => def.in._zod.propValues);
11933
+ inst._zod.parse = (payload, ctx) => {
11934
+ const direction = ctx.direction || "forward";
11935
+ if (direction === "forward") {
11936
+ const left = def.in._zod.run(payload, ctx);
11937
+ if (left instanceof Promise) {
11938
+ return left.then((left) => handleCodecAResult(left, def, ctx));
11939
+ }
11940
+ return handleCodecAResult(left, def, ctx);
11941
+ } else {
11942
+ const right = def.out._zod.run(payload, ctx);
11943
+ if (right instanceof Promise) {
11944
+ return right.then((right) => handleCodecAResult(right, def, ctx));
11945
+ }
11946
+ return handleCodecAResult(right, def, ctx);
11947
+ }
11948
+ };
11949
+ });
11950
+ function handleCodecAResult(result, def, ctx) {
11951
+ if (result.issues.length) {
11952
+ result.aborted = true;
11953
+ return result;
11954
+ }
11955
+ const direction = ctx.direction || "forward";
11956
+ if (direction === "forward") {
11957
+ const transformed = def.transform(result.value, result);
11958
+ if (transformed instanceof Promise) {
11959
+ return transformed.then((value) => handleCodecTxResult(result, value, def.out, ctx));
11960
+ }
11961
+ return handleCodecTxResult(result, transformed, def.out, ctx);
11962
+ } else {
11963
+ const transformed = def.reverseTransform(result.value, result);
11964
+ if (transformed instanceof Promise) {
11965
+ return transformed.then((value) => handleCodecTxResult(result, value, def.in, ctx));
11966
+ }
11967
+ return handleCodecTxResult(result, transformed, def.in, ctx);
11968
+ }
11969
+ }
11970
+ function handleCodecTxResult(left, value, nextSchema, ctx) {
11971
+ if (left.issues.length) {
11972
+ left.aborted = true;
11973
+ return left;
11974
+ }
11975
+ return nextSchema._zod.run({ value, issues: left.issues }, ctx);
11976
+ }
11884
11977
  var $ZodReadonly = /* @__PURE__ */ $constructor("$ZodReadonly", (inst, def) => {
11885
11978
  $ZodType.init(inst, def);
11886
11979
  defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues);
@@ -14426,6 +14519,19 @@ function pipe(in_, out) {
14426
14519
  out
14427
14520
  });
14428
14521
  }
14522
+ var ZodCodec = /* @__PURE__ */ $constructor("ZodCodec", (inst, def) => {
14523
+ ZodPipe.init(inst, def);
14524
+ $ZodCodec.init(inst, def);
14525
+ });
14526
+ function codec(in_, out, params) {
14527
+ return new ZodCodec({
14528
+ type: "pipe",
14529
+ in: in_,
14530
+ out,
14531
+ transform: params.decode,
14532
+ reverseTransform: params.encode
14533
+ });
14534
+ }
14429
14535
  var ZodReadonly = /* @__PURE__ */ $constructor("ZodReadonly", (inst, def) => {
14430
14536
  $ZodReadonly.init(inst, def);
14431
14537
  ZodType.init(inst, def);
@@ -14635,7 +14741,6 @@ function resolveWorkosConfig(publishedClientId) {
14635
14741
 
14636
14742
  // src/domains/auth/store/delete.ts
14637
14743
  import { join as join2 } from "node:path";
14638
- import { Entry } from "@napi-rs/keyring";
14639
14744
 
14640
14745
  // src/domains/auth/store/constants.ts
14641
14746
  var service = "qawolf-cli";
@@ -14645,8 +14750,9 @@ var tokensAccount = "oauth-tokens";
14645
14750
  var tokensFile = "tokens.json";
14646
14751
 
14647
14752
  // src/domains/auth/store/delete.ts
14648
- function deleteFromKeychain() {
14753
+ async function deleteFromKeychain() {
14649
14754
  try {
14755
+ const Entry = await loadEntryClass();
14650
14756
  new Entry(service, account).deletePassword();
14651
14757
  return "deleted";
14652
14758
  } catch {
@@ -14665,14 +14771,13 @@ async function deleteFromFile(configDir, fs) {
14665
14771
  }
14666
14772
  async function deleteApiKey(configDir, fs) {
14667
14773
  const [keychain, file] = await Promise.all([
14668
- Promise.resolve(deleteFromKeychain()),
14774
+ deleteFromKeychain(),
14669
14775
  deleteFromFile(configDir, fs)
14670
14776
  ]);
14671
14777
  return { keychain, file };
14672
14778
  }
14673
14779
  // src/domains/auth/store/hasStoredCredentials.ts
14674
14780
  import { join as join3 } from "node:path";
14675
- import { Entry as Entry2 } from "@napi-rs/keyring";
14676
14781
  async function fileExists(path, fs) {
14677
14782
  try {
14678
14783
  await fs.readFile(path);
@@ -14681,9 +14786,10 @@ async function fileExists(path, fs) {
14681
14786
  return !isNoEntError(err);
14682
14787
  }
14683
14788
  }
14684
- function keychainHolds(entryAccount) {
14789
+ async function keychainHolds(entryAccount) {
14685
14790
  try {
14686
- return Boolean(new Entry2(service, entryAccount).getPassword());
14791
+ const Entry = await loadEntryClass();
14792
+ return Boolean(new Entry(service, entryAccount).getPassword());
14687
14793
  } catch {
14688
14794
  return false;
14689
14795
  }
@@ -14693,7 +14799,9 @@ async function hasStoredCredentials(configDir, fs) {
14693
14799
  fileExists(join3(configDir, credentialsFile), fs),
14694
14800
  fileExists(join3(configDir, tokensFile), fs)
14695
14801
  ]);
14696
- return apiKeyFile || tokenFile || keychainHolds(account) || keychainHolds(tokensAccount);
14802
+ if (apiKeyFile || tokenFile)
14803
+ return true;
14804
+ return await keychainHolds(account) || await keychainHolds(tokensAccount);
14697
14805
  }
14698
14806
  // src/domains/auth/store/load.ts
14699
14807
  import { join as join4 } from "node:path";
@@ -14716,7 +14824,8 @@ var oauthTokensSchema = object({
14716
14824
  async function loadApiKey(configDir, deps) {
14717
14825
  const errors = {};
14718
14826
  try {
14719
- const entry = new deps.EntryClass(service, account);
14827
+ const Entry = await deps.loadEntryClass();
14828
+ const entry = new Entry(service, account);
14720
14829
  const key = entry.getPassword();
14721
14830
  if (key)
14722
14831
  return { found: true, key, source: "keychain" };
@@ -14737,7 +14846,6 @@ async function loadApiKey(configDir, deps) {
14737
14846
  }
14738
14847
  // src/domains/auth/store/save.ts
14739
14848
  import { join as join5 } from "node:path";
14740
- import { Entry as Entry3 } from "@napi-rs/keyring";
14741
14849
  async function saveToFile(configDir, key, fs) {
14742
14850
  const payload = { apiKey: key };
14743
14851
  await fs.mkdir(configDir, { recursive: true, mode: 448 });
@@ -14745,7 +14853,8 @@ async function saveToFile(configDir, key, fs) {
14745
14853
  }
14746
14854
  async function saveApiKey(configDir, key, fs) {
14747
14855
  try {
14748
- const entry = new Entry3(service, account);
14856
+ const Entry = await loadEntryClass();
14857
+ const entry = new Entry(service, account);
14749
14858
  entry.setPassword(key);
14750
14859
  return { stored: "keychain" };
14751
14860
  } catch (err) {
@@ -14773,7 +14882,8 @@ function parseTokens(raw) {
14773
14882
  async function loadTokens(configDir, deps) {
14774
14883
  const errors = {};
14775
14884
  try {
14776
- const raw = new deps.EntryClass(service, tokensAccount).getPassword();
14885
+ const Entry = await deps.loadEntryClass();
14886
+ const raw = new Entry(service, tokensAccount).getPassword();
14777
14887
  if (raw) {
14778
14888
  const tokens = parseTokens(raw);
14779
14889
  if (tokens)
@@ -14798,7 +14908,6 @@ async function loadTokens(configDir, deps) {
14798
14908
  // src/domains/auth/store/saveTokens.ts
14799
14909
  import { randomUUID } from "node:crypto";
14800
14910
  import { join as join7 } from "node:path";
14801
- import { Entry as Entry4 } from "@napi-rs/keyring";
14802
14911
  async function saveToFile2(configDir, tokens, fs) {
14803
14912
  await fs.mkdir(configDir, { recursive: true, mode: 448 });
14804
14913
  const target = join7(configDir, tokensFile);
@@ -14815,11 +14924,13 @@ async function saveToFile2(configDir, tokens, fs) {
14815
14924
  }
14816
14925
  async function saveTokens(configDir, tokens, fs) {
14817
14926
  try {
14818
- new Entry4(service, tokensAccount).setPassword(JSON.stringify(tokens));
14927
+ const Entry = await loadEntryClass();
14928
+ new Entry(service, tokensAccount).setPassword(JSON.stringify(tokens));
14819
14929
  return { stored: "keychain" };
14820
14930
  } catch (err) {
14821
14931
  try {
14822
- new Entry4(service, tokensAccount).deletePassword();
14932
+ const Entry = await loadEntryClass();
14933
+ new Entry(service, tokensAccount).deletePassword();
14823
14934
  } catch {}
14824
14935
  await saveToFile2(configDir, tokens, fs);
14825
14936
  return { stored: "file", keychainError: errorMessage(err) };
@@ -14882,7 +14993,7 @@ async function resolveOauthToken(configDir, deps) {
14882
14993
  // src/domains/auth/resolve.ts
14883
14994
  function makeOauthDeps(fs) {
14884
14995
  return {
14885
- loadTokens: (configDir) => loadTokens(configDir, { EntryClass: Entry5, fs }),
14996
+ loadTokens: (configDir) => loadTokens(configDir, { loadEntryClass, fs }),
14886
14997
  refreshTokens: async ({ refreshToken, organizationId, clientId }) => {
14887
14998
  const config = resolveWorkosConfig(clientId);
14888
14999
  if (!config.configured) {
@@ -14904,7 +15015,7 @@ function makeOauthDeps(fs) {
14904
15015
  }
14905
15016
  function makeDefaultDeps(fs) {
14906
15017
  return {
14907
- loadApiKey: (configDir) => loadApiKey(configDir, { EntryClass: Entry5, fs }),
15018
+ loadApiKey: (configDir) => loadApiKey(configDir, { loadEntryClass, fs }),
14908
15019
  resolveOauth: (configDir) => resolveOauthToken(configDir, makeOauthDeps(fs)),
14909
15020
  env: process.env
14910
15021
  };
@@ -15017,6 +15128,47 @@ function createLoggingSystem(opts) {
15017
15128
  };
15018
15129
  }
15019
15130
 
15131
+ // src/core/publicApi/notFoundSubject.ts
15132
+ var runnerRoutesThatNameNoRunner = new Set([
15133
+ "runner.launch",
15134
+ "runner.list"
15135
+ ]);
15136
+ var runRoutesThatResolveOneRun = new Set([
15137
+ "run.diagnose",
15138
+ "run.get",
15139
+ "run.reattempt",
15140
+ "run.stop"
15141
+ ]);
15142
+ var recordFieldNouns = [
15143
+ ["sessionId", "session"],
15144
+ ["triggerId", "trigger"],
15145
+ ["issueId", "issue"],
15146
+ ["flowId", "flow"],
15147
+ ["filePath", "file"]
15148
+ ];
15149
+ function field(input, name) {
15150
+ if (typeof input !== "object" || input === null)
15151
+ return;
15152
+ const value = input[name];
15153
+ return typeof value === "string" ? value : undefined;
15154
+ }
15155
+ function notFoundSubject(contractName, input) {
15156
+ if (contractName.startsWith("runner.") && !runnerRoutesThatNameNoRunner.has(contractName)) {
15157
+ return { kind: "runner", runnerId: field(input, "id") };
15158
+ }
15159
+ if (runRoutesThatResolveOneRun.has(contractName)) {
15160
+ return { kind: "run", runId: field(input, "runId") };
15161
+ }
15162
+ for (const [name, noun] of recordFieldNouns) {
15163
+ const id = field(input, name);
15164
+ if (id !== undefined)
15165
+ return { id, kind: "record", noun };
15166
+ }
15167
+ if (field(input, "environmentId") !== undefined)
15168
+ return { kind: "environment" };
15169
+ return { kind: "other" };
15170
+ }
15171
+
15020
15172
  // src/core/publicApi/flagKind.ts
15021
15173
  function flagKind(schema) {
15022
15174
  if (schema.type === "string")
@@ -15187,6 +15339,42 @@ function applyWorkspaceId(inputSchema, input, workspaceId) {
15187
15339
  return { ...input, workspaceId };
15188
15340
  }
15189
15341
 
15342
+ // src/shell/platform/describeNotFound.ts
15343
+ var m = authMessages.errors.request;
15344
+ var saysNothingMore = (reason) => /^(\w+ )*not ?found\.?$/i.test(reason.trim());
15345
+ function describeNotFound(subject, noun, reason) {
15346
+ const explained = reason && !saysNothingMore(reason) ? reason : undefined;
15347
+ const say = (own) => {
15348
+ const body = [explained ? undefined : own.guess, own.guidance].filter((line) => line);
15349
+ return {
15350
+ error: explained ? `${explained} (HTTP 404)` : own.statement,
15351
+ exitCode: exitCodes.notFound,
15352
+ ...body.length > 0 ? { errorBody: body.join(`
15353
+ `) } : {}
15354
+ };
15355
+ };
15356
+ switch (subject?.kind) {
15357
+ case "runner":
15358
+ return say({
15359
+ guess: m.runnerIsGone,
15360
+ guidance: m.launchTheRunner(subject.runnerId),
15361
+ statement: m.notFound404Runner(subject.runnerId)
15362
+ });
15363
+ case "run":
15364
+ return say({
15365
+ guess: m.runIdMayBeRunnerLocal(subject.runId),
15366
+ statement: m.notFound404Run(subject.runId)
15367
+ });
15368
+ case "record":
15369
+ return say({ statement: m.notFound404Record(subject.noun, subject.id) });
15370
+ case "other":
15371
+ return say({ statement: m.notFound404(noun) });
15372
+ case "environment":
15373
+ case undefined:
15374
+ return say({ statement: m.notFound404Environment(noun) });
15375
+ }
15376
+ }
15377
+
15190
15378
  // src/shell/platform/parseErrorBody.ts
15191
15379
  var defaultMaxLength = 1024;
15192
15380
  function asRecord(value) {
@@ -15229,69 +15417,69 @@ function clip(message, maxLength) {
15229
15417
  }
15230
15418
 
15231
15419
  // src/shell/platform/describeErrors.ts
15232
- var m = authMessages.errors;
15420
+ var m2 = authMessages.errors;
15233
15421
  var inlineReasonMaxLength = 200;
15234
15422
  function describeIdentityError(err) {
15235
15423
  if (err.kind === "http") {
15236
15424
  if (err.status === 401 || err.status === 403) {
15237
15425
  return {
15238
- error: m.identity.invalidOrUnauthorized,
15426
+ error: m2.identity.invalidOrUnauthorized,
15239
15427
  ...err.status === 401 ? { exitCode: exitCodes.auth } : {}
15240
15428
  };
15241
15429
  }
15242
15430
  return {
15243
- error: m.identity.couldNotVerify(parseErrorBody(err.body, inlineReasonMaxLength), err.status)
15431
+ error: m2.identity.couldNotVerify(parseErrorBody(err.body, inlineReasonMaxLength), err.status)
15244
15432
  };
15245
15433
  }
15246
15434
  if (err.kind === "network") {
15247
- return { error: m.identity.couldNotVerifyNetwork(err.cause.message) };
15435
+ return { error: m2.identity.couldNotVerifyNetwork(err.cause.message) };
15248
15436
  }
15249
15437
  if (err.kind === "timeout") {
15250
- return { error: m.identity.timedOut(err.timeoutMs) };
15438
+ return { error: m2.identity.timedOut(err.timeoutMs) };
15251
15439
  }
15252
- return { error: m.identity.unexpectedFormat };
15440
+ return { error: m2.identity.unexpectedFormat };
15253
15441
  }
15254
- function describeRequestError(err, baseUrl, noun) {
15442
+ function describeRequestError(err, baseUrl, noun, notFound) {
15255
15443
  if (err.kind === "http") {
15256
15444
  const reason = parseErrorBody(err.body);
15257
15445
  const body = reason ? { errorBody: reason } : {};
15258
15446
  if (err.status === 401)
15259
15447
  return {
15260
- error: m.request.rejected401(noun),
15448
+ error: m2.request.rejected401(noun),
15261
15449
  exitCode: exitCodes.auth,
15262
15450
  ...body
15263
15451
  };
15264
15452
  if (err.status === 402)
15265
15453
  return {
15266
- error: m.request.rejected402(noun),
15454
+ error: m2.request.rejected402(noun),
15267
15455
  exitCode: exitCodes.payment,
15268
15456
  ...body
15269
15457
  };
15270
15458
  if (err.status === 403)
15271
- return { error: m.request.rejected403(noun), ...body };
15459
+ return { error: m2.request.rejected403(noun), ...body };
15272
15460
  if (err.status === 404)
15273
- return { error: m.request.notFound404(noun), ...body };
15274
- return { error: m.request.failedWithStatus(err.status, noun), ...body };
15461
+ return describeNotFound(notFound, noun, reason);
15462
+ return { error: m2.request.failedWithStatus(err.status, noun), ...body };
15275
15463
  }
15276
15464
  if (err.kind === "network") {
15277
- return { error: m.request.networkUnreachable(baseUrl, noun) };
15465
+ return { error: m2.request.networkUnreachable(baseUrl, noun) };
15278
15466
  }
15279
15467
  if (err.kind === "timeout") {
15280
- return { error: m.request.timedOut(err.timeoutMs, noun) };
15468
+ return { error: m2.request.timedOut(err.timeoutMs, noun) };
15281
15469
  }
15282
- return { error: m.request.unexpectedResponse(noun) };
15470
+ return { error: m2.request.unexpectedResponse(noun) };
15283
15471
  }
15284
15472
  function describeBundleDownloadError(err) {
15285
15473
  if (err.kind === "http") {
15286
15474
  if (err.status === 401 || err.status === 403)
15287
- return m.bundle.linkExpired;
15288
- return m.bundle.failedWithStatus(err.status);
15475
+ return m2.bundle.linkExpired;
15476
+ return m2.bundle.failedWithStatus(err.status);
15289
15477
  }
15290
15478
  if (err.kind === "network")
15291
- return m.bundle.networkUnreachable;
15479
+ return m2.bundle.networkUnreachable;
15292
15480
  if (err.kind === "timeout")
15293
- return m.bundle.timedOut(err.timeoutMs);
15294
- return m.bundle.malformed;
15481
+ return m2.bundle.timedOut(err.timeoutMs);
15482
+ return m2.bundle.malformed;
15295
15483
  }
15296
15484
  function describeTeamStorageDownloadError(path, err) {
15297
15485
  if (err.kind === "http") {
@@ -15352,12 +15540,16 @@ function callPublicApi(trpc, contract, input, options) {
15352
15540
  return trpc.mutation(path, input, contract.output, options);
15353
15541
  }
15354
15542
  function makeCallPublicApiMethod(trpc, deps, readBackoffMs) {
15355
- return async (contract, input, options) => requestWithRetry({
15356
- call: () => callPublicApi(trpc, contract, applyWorkspaceId(contract.input, input, deps.workspaceId), options),
15357
- backoffMs: contract.kind === "read" ? readBackoffMs : [],
15358
- describe: (err) => describeRequestError(err, deps.baseUrl, contract.name),
15359
- sleep: deps.sleep
15360
- });
15543
+ return async (contract, input, options) => {
15544
+ const sent = applyWorkspaceId(contract.input, input, deps.workspaceId);
15545
+ const subject = notFoundSubject(contract.name, sent);
15546
+ return requestWithRetry({
15547
+ call: () => callPublicApi(trpc, contract, sent, options),
15548
+ backoffMs: contract.kind === "read" ? readBackoffMs : [],
15549
+ describe: (err) => describeRequestError(err, deps.baseUrl, contract.name, subject),
15550
+ sleep: deps.sleep
15551
+ });
15552
+ };
15361
15553
  }
15362
15554
 
15363
15555
  // node_modules/superjson/dist/double-indexed-kv.js
@@ -18988,7 +19180,8 @@ var environmentListItemSchema = object({
18988
19180
  slug: string2()
18989
19181
  });
18990
19182
  var environmentWithVariablesResponseSchema = object({
18991
- environmentVariables: record(string2(), string2())
19183
+ environmentVariables: record(string2(), string2()),
19184
+ teamId: string2()
18992
19185
  });
18993
19186
  var flowListItemSchema = object({
18994
19187
  flowId: string2(),
@@ -19265,7 +19458,10 @@ async function writeTeamStorageAssets(args, deps) {
19265
19458
 
19266
19459
  // src/shell/platform/teamStorageMethods.ts
19267
19460
  function createTeamStorageMethods(trpc, deps, fs, getIdentity) {
19268
- async function list() {
19461
+ async function list(opts) {
19462
+ if (opts?.teamId !== undefined) {
19463
+ return listTeamStorageFiles(trpc, { teamId: opts.teamId }, deps);
19464
+ }
19269
19465
  if (deps.workspaceId !== undefined) {
19270
19466
  return listTeamStorageFiles(trpc, { teamId: deps.workspaceId }, deps);
19271
19467
  }
@@ -19275,7 +19471,7 @@ function createTeamStorageMethods(trpc, deps, fs, getIdentity) {
19275
19471
  if (!("team" in identity.value)) {
19276
19472
  return {
19277
19473
  ok: false,
19278
- error: flowsMessages.pull.teamStorageRequiresTeamKey
19474
+ error: flowsMessages.pull.teamStorageRequiresTeam
19279
19475
  };
19280
19476
  }
19281
19477
  return listTeamStorageFiles(trpc, { teamId: identity.value.team.id }, deps);
@@ -19283,7 +19479,7 @@ function createTeamStorageMethods(trpc, deps, fs, getIdentity) {
19283
19479
  return {
19284
19480
  listTeamStorageFiles: list,
19285
19481
  async syncTeamStorageAssets(assetsAbs, opts) {
19286
- const files = await list();
19482
+ const files = await list({ teamId: opts?.teamId });
19287
19483
  if (!files.ok)
19288
19484
  return files;
19289
19485
  return downloadTeamStorageAssets({ assetsAbs, files: files.value }, { fetch: deps.fetch, fs, onProgress: opts?.onProgress });
@@ -19313,16 +19509,14 @@ function createPlatformClient(apiKey, deps) {
19313
19509
  ...createTeamStorageMethods(trpc, deps, fs, identityMethods.getIdentity),
19314
19510
  getFlowsBundleUrl: getFlowsBundleUrlImpl,
19315
19511
  callPublicApi: makeCallPublicApiMethod(trpc, deps, requestBackoffMs2),
19316
- async getEnvVars(envId) {
19512
+ async getEnvironmentWithVariables(envId) {
19317
19513
  const result = await requestWithRetry({
19318
19514
  call: () => trpc.query("environment.getEnvironmentWithVariables", { id: envId }, environmentWithVariablesResponseSchema),
19319
19515
  backoffMs: requestBackoffMs2,
19320
19516
  describe: (err) => describeRequestError(err, deps.baseUrl, "env-vars"),
19321
19517
  sleep: deps.sleep
19322
19518
  });
19323
- if (!result.ok)
19324
- return result;
19325
- return { ok: true, value: result.value.environmentVariables };
19519
+ return result;
19326
19520
  },
19327
19521
  async downloadBundle(envId) {
19328
19522
  const urlResult = await getFlowsBundleUrlImpl(envId);
@@ -20000,7 +20194,7 @@ function p$1(l, e) {
20000
20194
  function g(l, e) {
20001
20195
  return (e.label ?? String(e.value)).toLowerCase().includes(l.toLowerCase());
20002
20196
  }
20003
- function m2(l, e) {
20197
+ function m3(l, e) {
20004
20198
  if (e)
20005
20199
  return l ? e : e[0];
20006
20200
  }
@@ -20050,7 +20244,7 @@ var T$1 = class T extends V {
20050
20244
  this.userInput === "\t" && this._clearUserInput(), this._setUserInput(u, true), this.isNavigating = false;
20051
20245
  return;
20052
20246
  }
20053
- i || s ? (this.#e = findCursor(this.#e, i ? -1 : 1, this.filteredOptions), this.focusedValue = this.filteredOptions[this.#e]?.value, this.multiple || (this.selectedValues = [this.focusedValue]), this.isNavigating = true) : n ? this.value = m2(this.multiple, this.selectedValues) : this.multiple ? this.focusedValue !== undefined && (t.name === "tab" || this.isNavigating && t.name === "space") ? this.toggleSelected(this.focusedValue) : this.isNavigating = false : (this.focusedValue && (this.selectedValues = [this.focusedValue]), this.isNavigating = false);
20247
+ i || s ? (this.#e = findCursor(this.#e, i ? -1 : 1, this.filteredOptions), this.focusedValue = this.filteredOptions[this.#e]?.value, this.multiple || (this.selectedValues = [this.focusedValue]), this.isNavigating = true) : n ? this.value = m3(this.multiple, this.selectedValues) : this.multiple ? this.focusedValue !== undefined && (t.name === "tab" || this.isNavigating && t.name === "space") ? this.toggleSelected(this.focusedValue) : this.isNavigating = false : (this.focusedValue && (this.selectedValues = [this.focusedValue]), this.isNavigating = false);
20054
20248
  }
20055
20249
  deselectAll() {
20056
20250
  this.selectedValues = [];
@@ -20142,6 +20336,30 @@ class a extends V {
20142
20336
  });
20143
20337
  }
20144
20338
  }
20339
+ class n extends V {
20340
+ get userInputWithCursor() {
20341
+ if (this.state === "submit")
20342
+ return this.userInput;
20343
+ const t = this.userInput;
20344
+ if (this.cursor >= t.length)
20345
+ return `${this.userInput}█`;
20346
+ const e = t.slice(0, this.cursor), [s, ...r] = t.slice(this.cursor);
20347
+ return `${e}${styleText("inverse", s)}${r.join("")}`;
20348
+ }
20349
+ get cursor() {
20350
+ return this._cursor;
20351
+ }
20352
+ constructor(t) {
20353
+ super({
20354
+ ...t,
20355
+ initialUserInput: t.initialUserInput ?? t.initialValue
20356
+ }), this.on("userInput", (e) => {
20357
+ this._setValue(e);
20358
+ }), this.on("finalize", () => {
20359
+ this.value || (this.value = t.defaultValue), this.value === undefined && (this.value = "");
20360
+ });
20361
+ }
20362
+ }
20145
20363
 
20146
20364
  // node_modules/@clack/prompts/dist/index.mjs
20147
20365
  import { styleText as styleText2, stripVTControlCharacters } from "node:util";
@@ -20752,14 +20970,56 @@ ${e.value}` : e.value;
20752
20970
  }
20753
20971
  };
20754
20972
  };
20973
+ var text = (t) => new n({
20974
+ validate: t.validate,
20975
+ placeholder: t.placeholder,
20976
+ defaultValue: t.defaultValue,
20977
+ initialValue: t.initialValue,
20978
+ output: t.output,
20979
+ signal: t.signal,
20980
+ input: t.input,
20981
+ render() {
20982
+ const i = t?.withGuide ?? settings.withGuide, s = `${`${i ? `${styleText2("gray", S_BAR)}
20983
+ ` : ""}${symbol(this.state)} `}${t.message}
20984
+ `, c = t.placeholder ? styleText2("inverse", t.placeholder[0]) + styleText2("dim", t.placeholder.slice(1)) : styleText2(["inverse", "hidden"], "_"), o = this.userInput ? this.userInputWithCursor : c, a = this.value ?? "";
20985
+ switch (this.state) {
20986
+ case "error": {
20987
+ const n = this.error ? ` ${styleText2("yellow", this.error)}` : "", r = i ? `${styleText2("yellow", S_BAR)} ` : "", d = i ? styleText2("yellow", S_BAR_END) : "";
20988
+ return `${s.trim()}
20989
+ ${r}${o}
20990
+ ${d}${n}
20991
+ `;
20992
+ }
20993
+ case "submit": {
20994
+ const n = a ? ` ${styleText2("dim", a)}` : "", r = i ? styleText2("gray", S_BAR) : "";
20995
+ return `${s}${r}${n}`;
20996
+ }
20997
+ case "cancel": {
20998
+ const n = a ? ` ${styleText2(["strikethrough", "dim"], a)}` : "", r = i ? styleText2("gray", S_BAR) : "";
20999
+ return `${s}${r}${n}${a.trim() ? `
21000
+ ${r}` : ""}`;
21001
+ }
21002
+ default: {
21003
+ const n = i ? `${styleText2("cyan", S_BAR)} ` : "", r = i ? styleText2("cyan", S_BAR_END) : "";
21004
+ return `${s}${n}${o}
21005
+ ${r}
21006
+ `;
21007
+ }
21008
+ }
21009
+ }
21010
+ }).prompt();
20755
21011
 
20756
21012
  // src/shell/ui/clack/theme.ts
21013
+ import { styleText as styleText3 } from "node:util";
20757
21014
  var qaWolfBlueBg = "\x1B[48;2;59;59;239m";
20758
21015
  var whiteBold = "\x1B[97;1m";
20759
21016
  var reset = "\x1B[0m";
20760
21017
  function styledTitle(title) {
20761
21018
  return `${qaWolfBlueBg}${whiteBold} ${title} ${reset}`;
20762
21019
  }
21020
+ function replySymbol() {
21021
+ return styleText3("cyan", "◇");
21022
+ }
20763
21023
 
20764
21024
  // src/shell/ui/clack/styledClack.ts
20765
21025
  function createStyledClack() {
@@ -20770,6 +21030,7 @@ function createStyledClack() {
20770
21030
  cancel,
20771
21031
  confirm,
20772
21032
  password,
21033
+ text,
20773
21034
  autocomplete,
20774
21035
  select,
20775
21036
  isCancel,
@@ -20873,6 +21134,12 @@ function createAgentRenderers() {
20873
21134
  gap: () => writeStderrLine(""),
20874
21135
  stream: (_data, line) => writeStdoutRaw(`${line}
20875
21136
  `),
21137
+ transcript: ({ body, headline }) => writeStdoutRaw(`**${headline}**
21138
+
21139
+ ${body}
21140
+
21141
+ `),
21142
+ wait: () => ({ stop: () => {} }),
20876
21143
  write: (text) => writeStderrRaw(text),
20877
21144
  withProgress: async (steps, done) => {
20878
21145
  const results = [];
@@ -20889,7 +21156,69 @@ function createAgentRenderers() {
20889
21156
  };
20890
21157
  }
20891
21158
 
21159
+ // src/core/wrapText.ts
21160
+ function wrapLine(rawLine, width) {
21161
+ const line = rawLine.trimEnd();
21162
+ if (line.length <= width)
21163
+ return [line];
21164
+ const indent = /^\s*/.exec(line)?.[0] ?? "";
21165
+ const lines = [];
21166
+ let current = "";
21167
+ for (const word of line.trimStart().split(" ")) {
21168
+ if (current === "") {
21169
+ current = word;
21170
+ } else if (indent.length + current.length + 1 + word.length <= width) {
21171
+ current = `${current} ${word}`;
21172
+ } else {
21173
+ lines.push(indent + current);
21174
+ current = word;
21175
+ }
21176
+ }
21177
+ lines.push(indent + current);
21178
+ return lines;
21179
+ }
21180
+ function wrapText(text, width) {
21181
+ return text.split(`
21182
+ `).flatMap((line) => wrapLine(line, width)).join(`
21183
+ `);
21184
+ }
21185
+
21186
+ // src/shell/ui/renderers/waitLine.ts
21187
+ import { styleText as styleText4 } from "node:util";
21188
+ var frames = ["◒", "◐", "◓", "◑"];
21189
+ var frameMs = 120;
21190
+ function elapsed(sinceMs) {
21191
+ const seconds = Math.floor((Date.now() - sinceMs) / 1000);
21192
+ const minutes = Math.floor(seconds / 60);
21193
+ return minutes > 0 ? `[${minutes}m ${seconds % 60}s]` : `[${seconds}s]`;
21194
+ }
21195
+ function startWaitLine(message, output = process.stdout) {
21196
+ const startedAt = Date.now();
21197
+ let frame = 0;
21198
+ const draw = () => {
21199
+ const mark = styleText4("magenta", frames[frame] ?? "◒");
21200
+ output.write(`\r\x1B[2K${mark} ${message} ${elapsed(startedAt)}`);
21201
+ frame = (frame + 1) % frames.length;
21202
+ };
21203
+ draw();
21204
+ const timer = setInterval(draw, frameMs);
21205
+ let stopped = false;
21206
+ return {
21207
+ stop: () => {
21208
+ if (stopped)
21209
+ return;
21210
+ stopped = true;
21211
+ clearInterval(timer);
21212
+ output.write("\r\x1B[2K");
21213
+ }
21214
+ };
21215
+ }
21216
+
20892
21217
  // src/shell/ui/renderers/modes/human.ts
21218
+ function transcriptWidth() {
21219
+ const columns = process.stdout.columns || 80;
21220
+ return Math.min(Math.max(columns - 4, 20), 100);
21221
+ }
20893
21222
  function createHumanRenderers(clack, verboseTarget) {
20894
21223
  return {
20895
21224
  intro: (title) => clack.intro(title),
@@ -20911,6 +21240,12 @@ ${body}` : ""));
20911
21240
  `),
20912
21241
  stream: (_data, line) => writeStdoutRaw(`${line}
20913
21242
  `),
21243
+ transcript: ({ body, headline }) => {
21244
+ const lines = wrapText(body, transcriptWidth()).split(`
21245
+ `);
21246
+ clack.log.message([headline, ...lines], { symbol: replySymbol() });
21247
+ },
21248
+ wait: (message) => startWaitLine(message),
20914
21249
  write: (text) => writeStdoutRaw(text),
20915
21250
  withProgress: async (steps, done) => {
20916
21251
  const results = [];
@@ -20984,6 +21319,8 @@ function createJsonRenderers() {
20984
21319
  output: (data, _humanMessage) => writeJsonLine(data),
20985
21320
  gap: () => {},
20986
21321
  stream: (data, _line) => writeJsonLine(data),
21322
+ transcript: ({ data }) => writeJsonLine(data),
21323
+ wait: () => ({ stop: () => {} }),
20987
21324
  write: () => {},
20988
21325
  withProgress: async (steps, done) => {
20989
21326
  const results = [];
@@ -21080,6 +21417,20 @@ function createSelect({ mode, clack }) {
21080
21417
  };
21081
21418
  }
21082
21419
 
21420
+ // src/shell/ui/renderers/text.ts
21421
+ function createText({ mode, clack }) {
21422
+ return async (message) => {
21423
+ assertHumanMode(mode, "text");
21424
+ const value = await clack.text({ message });
21425
+ if (clack.isCancel(value))
21426
+ return { ok: false };
21427
+ const answer = value.trim();
21428
+ if (answer.length === 0)
21429
+ return { ok: false };
21430
+ return { ok: true, value: answer };
21431
+ };
21432
+ }
21433
+
21083
21434
  // src/shell/ui/createUi.ts
21084
21435
  function createUI(mode, opts = {}) {
21085
21436
  const clack = opts.clack ?? createStyledClack();
@@ -21089,6 +21440,7 @@ function createUI(mode, opts = {}) {
21089
21440
  confirm: createConfirm({ mode, clack }),
21090
21441
  password: createPassword({ mode, clack }),
21091
21442
  select: createSelect({ mode, clack }),
21443
+ text: createText({ mode, clack }),
21092
21444
  json: createJson()
21093
21445
  };
21094
21446
  }
@@ -21213,7 +21565,7 @@ function startUpdateCheck(deps) {
21213
21565
  // package.json
21214
21566
  var package_default = {
21215
21567
  name: "@qawolf/cli",
21216
- version: "1.29.0",
21568
+ version: "1.31.0",
21217
21569
  description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
21218
21570
  keywords: [
21219
21571
  "automation",
@@ -21288,7 +21640,7 @@ var package_default = {
21288
21640
  "@qawolf/emails": "1.1.1",
21289
21641
  "@qawolf/flow-targets": "1.0.0",
21290
21642
  "@qawolf/flows": "0.1.4",
21291
- "@qawolf/testkit": "1.1.1",
21643
+ "@qawolf/testkit": "1.2.1",
21292
21644
  commander: "14.0.3",
21293
21645
  "env-paths": "4.0.0",
21294
21646
  picomatch: "4.0.4",
@@ -21429,97 +21781,565 @@ function withAuthContext(signals, fn, deps = {}) {
21429
21781
  };
21430
21782
  }
21431
21783
 
21432
- // src/commands/auth/loginApiKey.ts
21433
- async function loginWithApiKey(ctx) {
21434
- const result = await ctx.ui.password(authMessages.promptApiKey, "Set QAWOLF_API_KEY to authenticate in non-interactive environments.");
21435
- if (!result.ok) {
21436
- ctx.ui.cancel(authMessages.cancelled);
21784
+ // src/core/agent/session.ts
21785
+ var defaultAgentFollowTimeoutSeconds = 1800;
21786
+ var settledStatuses = [
21787
+ "cancelled",
21788
+ "completed",
21789
+ "failed"
21790
+ ];
21791
+ function isSettled(status) {
21792
+ return settledStatuses.includes(status);
21793
+ }
21794
+ function pendingQuestion(status, replies) {
21795
+ if (status !== "waiting-for-you")
21437
21796
  return;
21438
- }
21439
- const apiKey = result.value.trim();
21440
- if (!apiKey) {
21441
- ctx.ui.cancel(authMessages.cancelled);
21797
+ return replies[replies.length - 1];
21798
+ }
21799
+
21800
+ // src/core/agent/formatReply.ts
21801
+ var speaker = "QA Wolf";
21802
+ function replyTime(askedAt) {
21803
+ const at = new Date(askedAt);
21804
+ if (Number.isNaN(at.getTime()))
21805
+ return;
21806
+ return at.toTimeString().slice(0, 8);
21807
+ }
21808
+ function replyParts(reply, options) {
21809
+ const time = replyTime(reply.askedAt);
21810
+ const choices = options.listChoices && reply.choices !== undefined ? ["", ...reply.choices.map((choice) => `- ${choice}`)] : [];
21811
+ return {
21812
+ body: [reply.text.trim(), ...choices].join(`
21813
+ `),
21814
+ headline: time === undefined ? speaker : `${speaker} ${time}`
21815
+ };
21816
+ }
21817
+
21818
+ // src/core/followTimeout.ts
21819
+ var followTimeoutSchema = exports_coerce.number().int().positive();
21820
+ function parseFollowTimeout(seconds, defaultSeconds) {
21821
+ if (seconds === undefined)
21822
+ return { ok: true, seconds: defaultSeconds };
21823
+ const parsed = followTimeoutSchema.safeParse(seconds);
21824
+ if (!parsed.success)
21825
+ return { error: prettifyError(parsed.error), ok: false };
21826
+ return { ok: true, seconds: parsed.data };
21827
+ }
21828
+
21829
+ // src/domains/agent/settlement.ts
21830
+ function endWithSession(ctx, session) {
21831
+ if (ctx.ui.mode !== "human")
21832
+ ctx.ui.json(session);
21833
+ }
21834
+ function reportSettlement(ctx, session) {
21835
+ endWithSession(ctx, session);
21836
+ if (session.status === "completed") {
21837
+ ctx.ui.success(agentMessages.sessionCompleted);
21442
21838
  return;
21443
21839
  }
21444
- await ctx.ui.withProgress([
21445
- {
21446
- message: authMessages.verifying,
21447
- task: async () => {
21448
- const v = await validateApiKey({
21449
- platformClient: createPlatformClient(apiKey, {
21450
- baseUrl: ctx.apiBaseUrl,
21451
- fetch: globalThis.fetch
21452
- })
21453
- });
21454
- if (!v.valid)
21455
- throw Error(v.error);
21456
- }
21457
- },
21458
- {
21459
- message: authMessages.storing,
21460
- task: async () => saveApiKey(ctx.configDir, apiKey, ctx.fs)
21461
- }
21462
- ], ([, saveResult]) => {
21463
- return saveResult.stored === "file" ? authMessages.storedFile : authMessages.storedKeychain;
21464
- });
21465
- ctx.ui.outro(authMessages.outroSuccess);
21840
+ return {
21841
+ error: session.status === "cancelled" ? agentMessages.sessionCancelled(session.url) : agentMessages.sessionFailed(session.url),
21842
+ exitCode: exitCodes.testFailure
21843
+ };
21466
21844
  }
21467
21845
 
21468
- // src/core/deviceAuth/pollState.ts
21469
- var slowDownIncrementMs = 5000;
21470
- var unreachableBackoffFactor = 2;
21471
- function capToLifetime(delayMs, state, nowMs) {
21472
- return Math.min(delayMs, Math.max(1, state.deadlineMs - nowMs));
21846
+ // src/domains/agent/answerQuestion.ts
21847
+ async function ask(ctx, question) {
21848
+ const choices = question.choices ?? [];
21849
+ const answer = choices.length > 0 ? await ctx.ui.select(agentMessages.answerPrompt, choices.map((choice) => ({ label: choice, value: choice }))) : await ctx.ui.text(agentMessages.answerPrompt);
21850
+ return answer.ok ? answer.value : undefined;
21473
21851
  }
21474
- function nextPollStep(state, response, nowMs) {
21475
- if (response.kind === "tokens") {
21476
- return { action: "done", tokens: response.tokens };
21852
+ async function answerQuestion(ctx, options) {
21853
+ const answer = await ask(ctx, options.question);
21854
+ if (answer === undefined) {
21855
+ ctx.ui.warn(agentMessages.answerCancelled);
21856
+ return { type: "declined" };
21477
21857
  }
21478
- if (nowMs > state.deadlineMs) {
21479
- return { action: "fail", reason: "timeout", detail: undefined };
21858
+ const sent = await ctx.platformClient.callPublicApi(publicContractsV1.agent.send, {
21859
+ message: answer,
21860
+ sessionId: options.sessionId,
21861
+ ...options.workspaceId === undefined ? {} : { workspaceId: options.workspaceId }
21862
+ });
21863
+ if (!sent.ok) {
21864
+ const failure = failureFields(sent);
21865
+ return {
21866
+ result: {
21867
+ exitCode: exitCodes.network,
21868
+ ...failure,
21869
+ error: agentMessages.answerSendFailed(failure.error)
21870
+ },
21871
+ type: "failed"
21872
+ };
21480
21873
  }
21481
- switch (response.kind) {
21482
- case "pending":
21483
- return {
21484
- action: "poll",
21485
- delayMs: capToLifetime(state.intervalMs, state, nowMs),
21486
- state
21487
- };
21488
- case "slow-down": {
21489
- const slowed = {
21490
- intervalMs: state.intervalMs + slowDownIncrementMs,
21491
- deadlineMs: state.deadlineMs
21492
- };
21493
- return {
21494
- action: "poll",
21495
- delayMs: capToLifetime(slowed.intervalMs, slowed, nowMs),
21496
- state: slowed
21497
- };
21498
- }
21499
- case "denied":
21500
- return { action: "fail", reason: "access-denied", detail: undefined };
21501
- case "expired":
21502
- return { action: "fail", reason: "expired", detail: undefined };
21503
- case "unreachable": {
21504
- const backedOff = {
21505
- intervalMs: state.intervalMs * unreachableBackoffFactor,
21506
- deadlineMs: state.deadlineMs
21507
- };
21508
- return {
21509
- action: "poll",
21510
- delayMs: capToLifetime(backedOff.intervalMs, backedOff, nowMs),
21511
- state: backedOff
21512
- };
21874
+ return { repliesAtAnswer: options.repliesSoFar, type: "answered" };
21875
+ }
21876
+ async function handleQuestion(ctx, options) {
21877
+ const { session, sessionId } = options;
21878
+ if (!options.canPrompt) {
21879
+ ctx.ui.info(agentMessages.blocked(sessionId));
21880
+ endWithSession(ctx, session);
21881
+ return { result: undefined, type: "ended" };
21882
+ }
21883
+ const outcome = await answerQuestion(ctx, {
21884
+ question: options.question,
21885
+ repliesSoFar: session.replies.length,
21886
+ sessionId,
21887
+ workspaceId: options.workspaceId
21888
+ });
21889
+ if (outcome.type === "failed")
21890
+ return { result: outcome.result, type: "ended" };
21891
+ if (outcome.type === "declined")
21892
+ return { result: undefined, type: "ended" };
21893
+ return outcome;
21894
+ }
21895
+
21896
+ // src/domains/agent/followSession.ts
21897
+ var pollIntervalMs = 5000;
21898
+ async function followSession(ctx, options, deps) {
21899
+ const { sessionId, workspaceId } = options;
21900
+ const canPrompt = ctx.ui.mode === "human" && ctx.isInteractive;
21901
+ const startedAt = deps.now();
21902
+ let promptedMs = 0;
21903
+ const timedOut = () => deps.now() - startedAt - promptedMs >= options.timeoutSeconds * 1000;
21904
+ let pending = options.initial;
21905
+ let printed = 0;
21906
+ let { repliesAtAnswer } = options;
21907
+ let saidDetachHint = false;
21908
+ let waiting;
21909
+ const stopWaiting = () => {
21910
+ waiting?.stop();
21911
+ waiting = undefined;
21912
+ };
21913
+ const detach = ctx.signals.register(() => {
21914
+ stopWaiting();
21915
+ ctx.ui.info(agentMessages.detached(sessionId));
21916
+ });
21917
+ try {
21918
+ for (let poll = 1;; poll++) {
21919
+ let session;
21920
+ if (pending !== undefined) {
21921
+ session = pending;
21922
+ pending = undefined;
21923
+ } else {
21924
+ const read = await ctx.platformClient.callPublicApi(publicContractsV1.agent.get, { sessionId });
21925
+ if (!read.ok) {
21926
+ return { exitCode: exitCodes.network, ...failureFields(read) };
21927
+ }
21928
+ session = read.value;
21929
+ }
21930
+ const { replies, status } = session;
21931
+ const fresh = replies.slice(printed);
21932
+ printed = replies.length;
21933
+ if (fresh.length > 0)
21934
+ stopWaiting();
21935
+ for (const reply of fresh) {
21936
+ ctx.ui.transcript({
21937
+ ...replyParts(reply, { listChoices: !canPrompt }),
21938
+ data: reply
21939
+ });
21940
+ }
21941
+ if (poll === 1 && replies.length === 0 && !isSettled(status)) {
21942
+ ctx.ui.info(agentMessages.working);
21943
+ }
21944
+ if (isSettled(status)) {
21945
+ stopWaiting();
21946
+ return reportSettlement(ctx, session);
21947
+ }
21948
+ const question = pendingQuestion(status, replies);
21949
+ const awaitingOurAnswer = repliesAtAnswer !== undefined && replies.length <= repliesAtAnswer;
21950
+ if (question !== undefined && !awaitingOurAnswer) {
21951
+ stopWaiting();
21952
+ const promptStartedAt = deps.now();
21953
+ const step = await handleQuestion(ctx, {
21954
+ canPrompt,
21955
+ question,
21956
+ session,
21957
+ sessionId,
21958
+ workspaceId
21959
+ });
21960
+ promptedMs += deps.now() - promptStartedAt;
21961
+ if (step.type === "ended")
21962
+ return step.result;
21963
+ repliesAtAnswer = step.repliesAtAnswer;
21964
+ }
21965
+ if (!saidDetachHint && ctx.ui.mode === "human") {
21966
+ saidDetachHint = true;
21967
+ ctx.ui.info(agentMessages.detachHint);
21968
+ }
21969
+ waiting ??= ctx.ui.wait(agentMessages.waiting);
21970
+ await deps.sleep(pollIntervalMs);
21971
+ if (timedOut()) {
21972
+ return {
21973
+ error: agentMessages.followTimedOut(sessionId, options.timeoutSeconds),
21974
+ exitCode: exitCodes.timeout
21975
+ };
21976
+ }
21513
21977
  }
21514
- case "error":
21515
- return { action: "fail", reason: "network", detail: response.detail };
21978
+ } finally {
21979
+ stopWaiting();
21980
+ detach();
21516
21981
  }
21517
21982
  }
21518
21983
 
21519
- // src/domains/auth/deviceLogin.ts
21520
- var cancelled = {
21521
- ok: false,
21522
- reason: "cancelled",
21984
+ // src/core/resolveId.ts
21985
+ async function resolveIdFrom(options) {
21986
+ if (options.given !== undefined) {
21987
+ return { id: options.given, source: "flag" };
21988
+ }
21989
+ const fromEnvironment = options.env[options.environmentVariable]?.trim();
21990
+ if (fromEnvironment) {
21991
+ return { id: fromEnvironment, source: "environment" };
21992
+ }
21993
+ const stored = await options.readStored();
21994
+ return stored === undefined ? undefined : { id: stored, source: "stored" };
21995
+ }
21996
+
21997
+ // src/domains/agent/resolveSessionId.ts
21998
+ var sessionIdEnvironmentVariable = "QAWOLF_SESSION_ID";
21999
+ function chooseGivenSessionId(given) {
22000
+ const { argument, flag } = given;
22001
+ if (argument !== undefined && flag !== undefined && argument !== flag) {
22002
+ return { error: sessionGivenTwice(argument, flag), ok: false };
22003
+ }
22004
+ return { ok: true, sessionId: argument ?? flag };
22005
+ }
22006
+ var resolveSessionId = async (session, deps) => (await resolveIdFrom({
22007
+ env: deps.env,
22008
+ environmentVariable: sessionIdEnvironmentVariable,
22009
+ given: session,
22010
+ readStored: deps.store.readLastSessionId
22011
+ }))?.id;
22012
+
22013
+ // src/domains/agent/get.ts
22014
+ function renderSession(ctx, session) {
22015
+ if (ctx.ui.mode !== "json") {
22016
+ for (const reply of session.replies) {
22017
+ ctx.ui.transcript({
22018
+ ...replyParts(reply, { listChoices: true }),
22019
+ data: reply
22020
+ });
22021
+ }
22022
+ }
22023
+ ctx.ui.output(session, agentMessages.standing(session.status, session.url));
22024
+ }
22025
+ async function handleAgentGet(ctx, options, deps) {
22026
+ const timeout = parseFollowTimeout(options.timeout, defaultAgentFollowTimeoutSeconds);
22027
+ if (!timeout.ok) {
22028
+ return { error: timeout.error, exitCode: exitCodes.invalidArgs };
22029
+ }
22030
+ const given = chooseGivenSessionId({
22031
+ argument: options.sessionArgument,
22032
+ flag: options.session
22033
+ });
22034
+ if (!given.ok) {
22035
+ return { error: given.error, exitCode: exitCodes.invalidArgs };
22036
+ }
22037
+ const sessionId = await resolveSessionId(given.sessionId, deps);
22038
+ if (sessionId === undefined) {
22039
+ return { error: agentMessages.noSession, exitCode: exitCodes.invalidArgs };
22040
+ }
22041
+ const read = await ctx.platformClient.callPublicApi(publicContractsV1.agent.get, { sessionId });
22042
+ if (!read.ok)
22043
+ return { exitCode: exitCodes.network, ...failureFields(read) };
22044
+ if (!options.follow) {
22045
+ renderSession(ctx, read.value);
22046
+ return;
22047
+ }
22048
+ ctx.ui.info(agentMessages.sessionAt(read.value.url));
22049
+ return followSession(ctx, {
22050
+ initial: read.value,
22051
+ repliesAtAnswer: undefined,
22052
+ sessionId,
22053
+ timeoutSeconds: timeout.seconds,
22054
+ workspaceId: options.workspaceId
22055
+ }, deps);
22056
+ }
22057
+
22058
+ // src/shell/agent/sessionStore.ts
22059
+ import { join as join14 } from "node:path";
22060
+
22061
+ // src/core/parseJson.ts
22062
+ function parseJson(text) {
22063
+ try {
22064
+ return JSON.parse(text);
22065
+ } catch {
22066
+ return;
22067
+ }
22068
+ }
22069
+
22070
+ // src/shell/jsonFile.ts
22071
+ var pendingWrites = 0;
22072
+ async function readJsonFile(fs, path, schema) {
22073
+ const contents = await fs.readFile(path).catch(() => {
22074
+ return;
22075
+ });
22076
+ if (contents === undefined)
22077
+ return;
22078
+ const parsed = schema.safeParse(parseJson(contents));
22079
+ return parsed.success ? parsed.data : undefined;
22080
+ }
22081
+ async function writeJsonFileAtomically(fs, path, value) {
22082
+ const pendingPath = `${path}.${String(process.pid)}.${String(++pendingWrites)}.tmp`;
22083
+ try {
22084
+ await fs.writeFile(pendingPath, `${JSON.stringify(value, undefined, 2)}
22085
+ `);
22086
+ await fs.rename(pendingPath, path);
22087
+ } finally {
22088
+ await fs.rm(pendingPath, { force: true }).catch(() => {});
22089
+ }
22090
+ }
22091
+
22092
+ // src/shell/agent/sessionStore.ts
22093
+ var storeFileName = "agent.json";
22094
+ var storeSchema = object({ lastSessionId: string2().optional() });
22095
+ function makeAgentSessionStore(options) {
22096
+ const directory = join14(options.cwd, qawolfDir);
22097
+ const path = join14(directory, storeFileName);
22098
+ return {
22099
+ async readLastSessionId() {
22100
+ const stored = await readJsonFile(options.fs, path, storeSchema);
22101
+ return stored?.lastSessionId;
22102
+ },
22103
+ async rememberSession(sessionId) {
22104
+ await options.fs.mkdir(directory, { recursive: true });
22105
+ await writeJsonFileAtomically(options.fs, path, {
22106
+ lastSessionId: sessionId
22107
+ });
22108
+ }
22109
+ };
22110
+ }
22111
+
22112
+ // src/domains/agent/deps.ts
22113
+ function makeAgentDeps(options) {
22114
+ return {
22115
+ env: options.env,
22116
+ now: Date.now,
22117
+ sleep,
22118
+ store: makeAgentSessionStore({ cwd: options.cwd, fs: options.fs })
22119
+ };
22120
+ }
22121
+
22122
+ // src/commands/agent/context.ts
22123
+ var sessionFlagDescription = `Session to target. Defaults to ${sessionIdEnvironmentVariable}, then the session this directory last started`;
22124
+ var timeoutFlagDescription = "Give up following after this long. A session that never settles would otherwise hold a CI job open until the build is killed";
22125
+ function agentDeps(ctx) {
22126
+ return makeAgentDeps({
22127
+ cwd: process.cwd(),
22128
+ env: process.env,
22129
+ fs: ctx.fs
22130
+ });
22131
+ }
22132
+
22133
+ // src/commands/agent/get.register.ts
22134
+ var getExamples = `
22135
+ Examples:
22136
+ $ qawolf agent get
22137
+ $ qawolf agent get --follow
22138
+ $ qawolf agent get <sessionId> --follow
22139
+ $ qawolf agent get --session <sessionId>`;
22140
+ function registerAgentGetCommand(agent, signals) {
22141
+ declareCommandKind(agent.command("get [session]"), "read").description("Read what the QA Wolf AI has said and whether it is still working").option("--follow", "Stay and report what QA Wolf says until the session settles, answering its questions as they come", false).option("--session <id>", sessionFlagDescription).option("--workspace-id <id>", "The workspace an answer given during --follow is sent to. Required when authenticating with an organization or user API key").option("--timeout <seconds>", timeoutFlagDescription, String(defaultAgentFollowTimeoutSeconds)).addHelpText("after", getExamples).action((session, opts, command) => withAuthContext(signals, (ctx) => handleAgentGet(ctx, {
22142
+ follow: opts.follow,
22143
+ session: opts.session,
22144
+ sessionArgument: session,
22145
+ timeout: opts.timeout,
22146
+ workspaceId: opts.workspaceId
22147
+ }, agentDeps(ctx)))(opts, command));
22148
+ }
22149
+
22150
+ // src/commands/publicApi/environmentOptions.ts
22151
+ var environmentIdEnvironmentVariable = "QAWOLF_ENVIRONMENT";
22152
+ var optionEnvironmentVariables = new Map([
22153
+ ["environmentId", environmentIdEnvironmentVariable],
22154
+ ["public.run.create.aiTaskId", "QAWOLF_AI_TASK_ID"],
22155
+ ["public.run.create.chatSessionId", "QAWOLF_CHAT_SESSION_ID"]
22156
+ ]);
22157
+ function optionEnvironmentVariable(spec, path) {
22158
+ const dotted = path.join(".");
22159
+ return optionEnvironmentVariables.get(`${spec.trpcPath}.${dotted}`) ?? optionEnvironmentVariables.get(dotted);
22160
+ }
22161
+ var exclusiveOptions = new Map([
22162
+ ["public.run.create", { keep: "aiTaskId", drop: "chatSessionId" }]
22163
+ ]);
22164
+ function withoutExclusiveConflict(spec, command, options) {
22165
+ const pair = exclusiveOptions.get(spec.trpcPath);
22166
+ if (!pair)
22167
+ return options;
22168
+ if (options[pair.keep] === undefined || options[pair.drop] === undefined) {
22169
+ return options;
22170
+ }
22171
+ const fromEnvironment = (key) => command.getOptionValueSource(key) === "env";
22172
+ if (!fromEnvironment(pair.keep) && !fromEnvironment(pair.drop)) {
22173
+ return options;
22174
+ }
22175
+ const dropped = fromEnvironment(pair.drop) ? pair.drop : pair.keep;
22176
+ return Object.fromEntries(Object.entries(options).filter(([key]) => key !== dropped));
22177
+ }
22178
+
22179
+ // src/domains/agent/send.ts
22180
+ function parseInput(options) {
22181
+ return publicContractsV1.agent.send.input.safeParse({
22182
+ message: options.message,
22183
+ ...options.environmentId === undefined ? {} : { environmentId: options.environmentId },
22184
+ ...options.filePaths === undefined ? {} : { filePaths: options.filePaths },
22185
+ ...options.session === undefined ? {} : { sessionId: options.session },
22186
+ ...options.workspaceId === undefined ? {} : { workspaceId: options.workspaceId }
22187
+ });
22188
+ }
22189
+ async function handleAgentSend(ctx, options, deps) {
22190
+ const timeout = parseFollowTimeout(options.timeout, defaultAgentFollowTimeoutSeconds);
22191
+ if (!timeout.ok) {
22192
+ return { error: timeout.error, exitCode: exitCodes.invalidArgs };
22193
+ }
22194
+ const input = parseInput(options);
22195
+ if (!input.success) {
22196
+ return {
22197
+ error: prettifyError(input.error),
22198
+ exitCode: exitCodes.invalidArgs
22199
+ };
22200
+ }
22201
+ let repliesAtAnswer;
22202
+ if (options.follow && options.session !== undefined) {
22203
+ const before = await ctx.platformClient.callPublicApi(publicContractsV1.agent.get, { sessionId: options.session });
22204
+ if (before.ok)
22205
+ repliesAtAnswer = before.value.replies.length;
22206
+ }
22207
+ const sent = await ctx.platformClient.callPublicApi(publicContractsV1.agent.send, input.data);
22208
+ if (!sent.ok)
22209
+ return { exitCode: exitCodes.network, ...failureFields(sent) };
22210
+ ctx.ui.output(sent.value, agentMessages.started(sent.value.sessionId, sent.value.url));
22211
+ await deps.store.rememberSession(sent.value.sessionId).catch(() => {
22212
+ ctx.ui.warn(agentMessages.sessionNotRemembered(sent.value.sessionId));
22213
+ });
22214
+ if (!options.follow)
22215
+ return;
22216
+ return followSession(ctx, {
22217
+ initial: undefined,
22218
+ repliesAtAnswer,
22219
+ sessionId: sent.value.sessionId,
22220
+ timeoutSeconds: timeout.seconds,
22221
+ workspaceId: options.workspaceId
22222
+ }, deps);
22223
+ }
22224
+
22225
+ // src/commands/agent/send.register.ts
22226
+ var sendExamples = `
22227
+ Examples:
22228
+ $ qawolf agent send "cover the checkout journey"
22229
+ $ qawolf agent send "cover the checkout journey" --follow
22230
+ $ qawolf agent send "use the staging login test@example.com" --session <id>
22231
+ $ qawolf agent send "cover signup" --environment-id staging --follow
22232
+ $ qawolf agent send "cover every journey in the sheet" --file-paths uploads/journeys.csv --follow`;
22233
+ function registerAgentSendCommand(agent, signals) {
22234
+ declareCommandKind(agent.command("send <message>"), "write").description("Ask the QA Wolf AI to do a piece of work, such as covering a journey or fixing a broken flow").option("--follow", "Stay and report what QA Wolf says until the session settles, answering its questions as they come", false).option("--session <id>", "Continue this session instead of opening a new one. Send an answer, or add context to work already running").addOption(new Option2("--environment-id <env>", "QA Wolf environment to work in, by id or alias. Ignored with --session, because a session keeps the environment it opened in").env(environmentIdEnvironmentVariable)).option("--workspace-id <id>", "The workspace to work in. Required when authenticating with an organization or user API key").option("--file-paths <path...>", "Team-storage paths of files this request is about, from qawolf file requestUpload. The AI reads them from storage rather than the message, so a plan of hundreds of journeys costs nothing to send. Up to 20").option("--timeout <seconds>", timeoutFlagDescription, String(defaultAgentFollowTimeoutSeconds)).addHelpText("after", sendExamples).action((message, opts, command) => withAuthContext(signals, (ctx) => handleAgentSend(ctx, {
22235
+ environmentId: opts.environmentId,
22236
+ filePaths: opts.filePaths,
22237
+ follow: opts.follow,
22238
+ message,
22239
+ session: opts.session,
22240
+ timeout: opts.timeout,
22241
+ workspaceId: opts.workspaceId
22242
+ }, agentDeps(ctx)))(opts, command));
22243
+ }
22244
+
22245
+ // src/commands/agent/index.ts
22246
+ function registerAgentCommand(program, signals) {
22247
+ const agent = program.command("agent").description("Ask the QA Wolf AI for work and follow what it does");
22248
+ registerAgentSendCommand(agent, signals);
22249
+ registerAgentGetCommand(agent, signals);
22250
+ }
22251
+
22252
+ // src/commands/auth/loginApiKey.ts
22253
+ async function loginWithApiKey(ctx) {
22254
+ const result = await ctx.ui.password(authMessages.promptApiKey, "Set QAWOLF_API_KEY to authenticate in non-interactive environments.");
22255
+ if (!result.ok) {
22256
+ ctx.ui.cancel(authMessages.cancelled);
22257
+ return;
22258
+ }
22259
+ const apiKey = result.value.trim();
22260
+ if (!apiKey) {
22261
+ ctx.ui.cancel(authMessages.cancelled);
22262
+ return;
22263
+ }
22264
+ await ctx.ui.withProgress([
22265
+ {
22266
+ message: authMessages.verifying,
22267
+ task: async () => {
22268
+ const v = await validateApiKey({
22269
+ platformClient: createPlatformClient(apiKey, {
22270
+ baseUrl: ctx.apiBaseUrl,
22271
+ fetch: globalThis.fetch
22272
+ })
22273
+ });
22274
+ if (!v.valid)
22275
+ throw Error(v.error);
22276
+ }
22277
+ },
22278
+ {
22279
+ message: authMessages.storing,
22280
+ task: async () => saveApiKey(ctx.configDir, apiKey, ctx.fs)
22281
+ }
22282
+ ], ([, saveResult]) => {
22283
+ return saveResult.stored === "file" ? authMessages.storedFile : authMessages.storedKeychain;
22284
+ });
22285
+ ctx.ui.outro(authMessages.outroSuccess);
22286
+ }
22287
+
22288
+ // src/core/deviceAuth/pollState.ts
22289
+ var slowDownIncrementMs = 5000;
22290
+ var unreachableBackoffFactor = 2;
22291
+ function capToLifetime(delayMs, state, nowMs) {
22292
+ return Math.min(delayMs, Math.max(1, state.deadlineMs - nowMs));
22293
+ }
22294
+ function nextPollStep(state, response, nowMs) {
22295
+ if (response.kind === "tokens") {
22296
+ return { action: "done", tokens: response.tokens };
22297
+ }
22298
+ if (nowMs > state.deadlineMs) {
22299
+ return { action: "fail", reason: "timeout", detail: undefined };
22300
+ }
22301
+ switch (response.kind) {
22302
+ case "pending":
22303
+ return {
22304
+ action: "poll",
22305
+ delayMs: capToLifetime(state.intervalMs, state, nowMs),
22306
+ state
22307
+ };
22308
+ case "slow-down": {
22309
+ const slowed = {
22310
+ intervalMs: state.intervalMs + slowDownIncrementMs,
22311
+ deadlineMs: state.deadlineMs
22312
+ };
22313
+ return {
22314
+ action: "poll",
22315
+ delayMs: capToLifetime(slowed.intervalMs, slowed, nowMs),
22316
+ state: slowed
22317
+ };
22318
+ }
22319
+ case "denied":
22320
+ return { action: "fail", reason: "access-denied", detail: undefined };
22321
+ case "expired":
22322
+ return { action: "fail", reason: "expired", detail: undefined };
22323
+ case "unreachable": {
22324
+ const backedOff = {
22325
+ intervalMs: state.intervalMs * unreachableBackoffFactor,
22326
+ deadlineMs: state.deadlineMs
22327
+ };
22328
+ return {
22329
+ action: "poll",
22330
+ delayMs: capToLifetime(backedOff.intervalMs, backedOff, nowMs),
22331
+ state: backedOff
22332
+ };
22333
+ }
22334
+ case "error":
22335
+ return { action: "fail", reason: "network", detail: response.detail };
22336
+ }
22337
+ }
22338
+
22339
+ // src/domains/auth/deviceLogin.ts
22340
+ var cancelled = {
22341
+ ok: false,
22342
+ reason: "cancelled",
21523
22343
  detail: undefined
21524
22344
  };
21525
22345
  async function deviceLogin(deps) {
@@ -21841,10 +22661,9 @@ async function selectWorkspace(deps) {
21841
22661
  }
21842
22662
 
21843
22663
  // src/domains/auth/store/saveWorkspaceId.ts
21844
- import { Entry as Entry6 } from "@napi-rs/keyring";
21845
22664
  function makeSaveWorkspaceIdDeps(configDir, fs) {
21846
22665
  return {
21847
- loadTokens: () => loadTokens(configDir, { EntryClass: Entry6, fs }),
22666
+ loadTokens: () => loadTokens(configDir, { loadEntryClass, fs }),
21848
22667
  saveTokens: (session) => saveTokens(configDir, session, fs)
21849
22668
  };
21850
22669
  }
@@ -22050,11 +22869,11 @@ async function handleLogin(ctx, deps = {}) {
22050
22869
  }
22051
22870
 
22052
22871
  // src/domains/auth/store/deleteTokens.ts
22053
- import { join as join14 } from "node:path";
22054
- import { Entry as Entry7 } from "@napi-rs/keyring";
22055
- function deleteFromKeychain2() {
22872
+ import { join as join15 } from "node:path";
22873
+ async function deleteFromKeychain2() {
22056
22874
  try {
22057
- new Entry7(service, tokensAccount).deletePassword();
22875
+ const Entry = await loadEntryClass();
22876
+ new Entry(service, tokensAccount).deletePassword();
22058
22877
  return "deleted";
22059
22878
  } catch {
22060
22879
  return "unavailable";
@@ -22062,7 +22881,7 @@ function deleteFromKeychain2() {
22062
22881
  }
22063
22882
  async function deleteFromFile2(configDir, fs) {
22064
22883
  try {
22065
- await fs.unlink(join14(configDir, tokensFile));
22884
+ await fs.unlink(join15(configDir, tokensFile));
22066
22885
  return "deleted";
22067
22886
  } catch (err) {
22068
22887
  if (isNoEntError(err))
@@ -22072,7 +22891,7 @@ async function deleteFromFile2(configDir, fs) {
22072
22891
  }
22073
22892
  async function deleteTokens(configDir, fs) {
22074
22893
  const [keychain, file] = await Promise.all([
22075
- Promise.resolve(deleteFromKeychain2()),
22894
+ deleteFromKeychain2(),
22076
22895
  deleteFromFile2(configDir, fs)
22077
22896
  ]);
22078
22897
  return { keychain, file };
@@ -22121,10 +22940,9 @@ async function handleLogout(ctx, deps = {}) {
22121
22940
  }
22122
22941
 
22123
22942
  // src/domains/auth/refreshStoredSession.ts
22124
- import { Entry as Entry8 } from "@napi-rs/keyring";
22125
22943
  function makeDefaultDeps2(fs) {
22126
22944
  return {
22127
- loadTokens: (configDir) => loadTokens(configDir, { EntryClass: Entry8, fs }),
22945
+ loadTokens: (configDir) => loadTokens(configDir, { loadEntryClass, fs }),
22128
22946
  resolveOauth: (configDir) => resolveOauthToken(configDir, makeOauthDeps(fs))
22129
22947
  };
22130
22948
  }
@@ -22287,8 +23105,863 @@ function registerAuthCommand(program, signals) {
22287
23105
  declareCommandKind(auth.command("whoami"), "read").description("Show authentication status").action(withContext(signals, handleWhoami));
22288
23106
  }
22289
23107
 
23108
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel2Android14.js
23109
+ var androidPixel2Android14Target = {
23110
+ meta: {
23111
+ androidVersion: "34",
23112
+ deviceModel: "Pixel 2"
23113
+ },
23114
+ platform: "android",
23115
+ schemaVersion: 1
23116
+ };
23117
+
23118
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel9.js
23119
+ var androidPixel9Target = {
23120
+ meta: {
23121
+ androidVersion: "34",
23122
+ deviceModel: "Pixel 9"
23123
+ },
23124
+ platform: "android",
23125
+ schemaVersion: 1
23126
+ };
23127
+
23128
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel9Android14.js
23129
+ var androidPixel9Android14Target = {
23130
+ meta: {
23131
+ androidVersion: "34",
23132
+ deviceModel: "Pixel 9"
23133
+ },
23134
+ platform: "android",
23135
+ schemaVersion: 1
23136
+ };
23137
+
23138
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel9Android15.js
23139
+ var androidPixel9Android15Target = {
23140
+ meta: {
23141
+ androidVersion: "35",
23142
+ deviceModel: "Pixel 9"
23143
+ },
23144
+ platform: "android",
23145
+ schemaVersion: 1
23146
+ };
23147
+
23148
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel9Android16.js
23149
+ var androidPixel9Android16Target = {
23150
+ meta: {
23151
+ androidVersion: "36",
23152
+ deviceModel: "Pixel 9"
23153
+ },
23154
+ platform: "android",
23155
+ schemaVersion: 1
23156
+ };
23157
+
23158
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel.js
23159
+ var androidPixelTarget = {
23160
+ meta: {
23161
+ androidVersion: "34",
23162
+ deviceModel: "Pixel 2"
23163
+ },
23164
+ platform: "android",
23165
+ schemaVersion: 1
23166
+ };
23167
+
23168
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixelTabletAndroid14.js
23169
+ var androidPixelTabletAndroid14Target = {
23170
+ meta: {
23171
+ androidVersion: "34",
23172
+ deviceModel: "Pixel Tablet"
23173
+ },
23174
+ platform: "android",
23175
+ schemaVersion: 1
23176
+ };
23177
+
23178
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidTablet.js
23179
+ var androidTabletTarget = {
23180
+ meta: {
23181
+ androidVersion: "34",
23182
+ deviceModel: "Pixel Tablet"
23183
+ },
23184
+ platform: "android",
23185
+ schemaVersion: 1
23186
+ };
23187
+
23188
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/allowlistedIphone.js
23189
+ var allowlistedIphoneTarget = {
23190
+ meta: {
23191
+ deviceModel: "iPhone 15",
23192
+ iosVersion: "26",
23193
+ targetDevices: "Allowlisted Device"
23194
+ },
23195
+ platform: "ios",
23196
+ schemaVersion: 1
23197
+ };
23198
+
23199
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/appleIdEnabledIpad.js
23200
+ var appleIdEnabledIpadTarget = {
23201
+ meta: {
23202
+ appleId: true,
23203
+ deviceModel: "iPad 11",
23204
+ iosVersion: "18"
23205
+ },
23206
+ platform: "ios",
23207
+ schemaVersion: 1
23208
+ };
23209
+
23210
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/appleIdEnabledIphone.js
23211
+ var appleIdEnabledIphoneTarget = {
23212
+ meta: {
23213
+ appleId: true,
23214
+ deviceModel: "iPhone 15",
23215
+ iosVersion: "26"
23216
+ },
23217
+ platform: "ios",
23218
+ schemaVersion: 1
23219
+ };
23220
+
23221
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/ipad11ios18.js
23222
+ var ipad11ios18Target = {
23223
+ meta: {
23224
+ deviceModel: "iPad 11",
23225
+ iosVersion: "18"
23226
+ },
23227
+ platform: "ios",
23228
+ schemaVersion: 1
23229
+ };
23230
+
23231
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/ipad11ios18AppleId.js
23232
+ var ipad11ios18AppleIdTarget = {
23233
+ meta: {
23234
+ appleId: true,
23235
+ deviceModel: "iPad 11",
23236
+ iosVersion: "18"
23237
+ },
23238
+ platform: "ios",
23239
+ schemaVersion: 1
23240
+ };
23241
+
23242
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/ipad11ios26.js
23243
+ var ipad11ios26Target = {
23244
+ meta: {
23245
+ deviceModel: "iPad 11",
23246
+ iosVersion: "26"
23247
+ },
23248
+ platform: "ios",
23249
+ schemaVersion: 1
23250
+ };
23251
+
23252
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/ipad.js
23253
+ var ipadTarget = {
23254
+ meta: {
23255
+ deviceModel: "iPad 11",
23256
+ iosVersion: "18"
23257
+ },
23258
+ platform: "ios",
23259
+ schemaVersion: 1
23260
+ };
23261
+
23262
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios17.js
23263
+ var iphone15ios17Target = {
23264
+ meta: {
23265
+ deviceModel: "iPhone 15",
23266
+ iosVersion: "17"
23267
+ },
23268
+ platform: "ios",
23269
+ schemaVersion: 1
23270
+ };
23271
+
23272
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios18.js
23273
+ var iphone15ios18Target = {
23274
+ meta: {
23275
+ deviceModel: "iPhone 15",
23276
+ iosVersion: "18"
23277
+ },
23278
+ platform: "ios",
23279
+ schemaVersion: 1
23280
+ };
23281
+
23282
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios26.js
23283
+ var iphone15ios26Target = {
23284
+ meta: {
23285
+ deviceModel: "iPhone 15",
23286
+ iosVersion: "26"
23287
+ },
23288
+ platform: "ios",
23289
+ schemaVersion: 1
23290
+ };
23291
+
23292
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios26Allowlisted.js
23293
+ var iphone15ios26AllowlistedTarget = {
23294
+ meta: {
23295
+ deviceModel: "iPhone 15",
23296
+ iosVersion: "26",
23297
+ targetDevices: "Allowlisted Device"
23298
+ },
23299
+ platform: "ios",
23300
+ schemaVersion: 1
23301
+ };
23302
+
23303
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios26AppleId.js
23304
+ var iphone15ios26AppleIdTarget = {
23305
+ meta: {
23306
+ appleId: true,
23307
+ deviceModel: "iPhone 15",
23308
+ iosVersion: "26"
23309
+ },
23310
+ platform: "ios",
23311
+ schemaVersion: 1
23312
+ };
23313
+
23314
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios26Private.js
23315
+ var iphone15ios26PrivateTarget = {
23316
+ meta: {
23317
+ deviceModel: "iPhone 15",
23318
+ iosVersion: "26",
23319
+ targetDevices: "Team Dedicated Device"
23320
+ },
23321
+ platform: "ios",
23322
+ schemaVersion: 1
23323
+ };
23324
+
23325
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone17ios26.js
23326
+ var iphone17ios26Target = {
23327
+ meta: {
23328
+ deviceModel: "iPhone 17",
23329
+ iosVersion: "26"
23330
+ },
23331
+ platform: "ios",
23332
+ schemaVersion: 1
23333
+ };
23334
+
23335
+ // node_modules/@qawolf/flow-targets/dist/presets/web/basic.js
23336
+ var webBasicTarget = {
23337
+ meta: "legacy",
23338
+ platform: "web",
23339
+ schemaVersion: 1
23340
+ };
23341
+
23342
+ // node_modules/@qawolf/flow-targets/dist/presets/web/chrome.js
23343
+ var webChromeTarget = {
23344
+ meta: {
23345
+ defaultBrowser: {
23346
+ name: "chrome"
23347
+ },
23348
+ gpuEnabled: false
23349
+ },
23350
+ platform: "web",
23351
+ schemaVersion: 1
23352
+ };
23353
+
23354
+ // node_modules/@qawolf/flow-targets/dist/presets/web/chromeWithGpu.js
23355
+ var webChromeWithGpuTarget = {
23356
+ meta: {
23357
+ defaultBrowser: {
23358
+ name: "chrome"
23359
+ },
23360
+ gpuEnabled: true
23361
+ },
23362
+ platform: "web",
23363
+ schemaVersion: 1
23364
+ };
23365
+
23366
+ // node_modules/@qawolf/flow-targets/dist/presets/web/electron.js
23367
+ var webElectronTarget = {
23368
+ meta: {
23369
+ gpuEnabled: false,
23370
+ kind: "electron"
23371
+ },
23372
+ platform: "web",
23373
+ schemaVersion: 1
23374
+ };
23375
+
23376
+ // node_modules/@qawolf/flow-targets/dist/presets/web/firefox.js
23377
+ var webFirefoxTarget = {
23378
+ meta: {
23379
+ defaultBrowser: {
23380
+ name: "firefox"
23381
+ },
23382
+ gpuEnabled: false
23383
+ },
23384
+ platform: "web",
23385
+ schemaVersion: 1
23386
+ };
23387
+
23388
+ // node_modules/@qawolf/flow-targets/dist/presets/web/firefoxWithGpu.js
23389
+ var webFirefoxWithGpuTarget = {
23390
+ meta: {
23391
+ defaultBrowser: {
23392
+ name: "firefox"
23393
+ },
23394
+ gpuEnabled: true
23395
+ },
23396
+ platform: "web",
23397
+ schemaVersion: 1
23398
+ };
23399
+
23400
+ // node_modules/@qawolf/flow-targets/dist/presets/web/safari.js
23401
+ var webSafariTarget = {
23402
+ meta: {
23403
+ defaultBrowser: {
23404
+ name: "safari"
23405
+ },
23406
+ gpuEnabled: false
23407
+ },
23408
+ platform: "web",
23409
+ schemaVersion: 1
23410
+ };
23411
+
23412
+ // node_modules/@qawolf/flow-targets/dist/presets/web/safariWithGpu.js
23413
+ var webSafariWithGpuTarget = {
23414
+ meta: {
23415
+ defaultBrowser: {
23416
+ name: "safari"
23417
+ },
23418
+ gpuEnabled: true
23419
+ },
23420
+ platform: "web",
23421
+ schemaVersion: 1
23422
+ };
23423
+
23424
+ // node_modules/@qawolf/flow-targets/dist/presets/index.js
23425
+ var androidPresetLiteralRaw = [
23426
+ "Android - Pixel",
23427
+ "Android - Pixel 2 (Android 14)",
23428
+ "Android - Pixel 9",
23429
+ "Android - Pixel 9 (Android 14)",
23430
+ "Android - Pixel 9 (Android 15)",
23431
+ "Android - Pixel 9 (Android 16)",
23432
+ "Android - Pixel Tablet (Android 14)",
23433
+ "Android - Tablet"
23434
+ ];
23435
+ var androidPresetLiteralSchema = _enum(androidPresetLiteralRaw);
23436
+ var iosPresetLiteralRaw = [
23437
+ "iOS - Allowlisted iPhone",
23438
+ "iOS - Apple ID Enabled iPad",
23439
+ "iOS - Apple ID Enabled iPhone",
23440
+ "iOS - iPad",
23441
+ "iOS - iPad 11 (iOS 18)",
23442
+ "iOS - iPad 11 (iOS 18) (Apple ID)",
23443
+ "iOS - iPad 11 (iOS 26)",
23444
+ "iOS - iPhone 15 (iOS 17)",
23445
+ "iOS - iPhone 15 (iOS 18)",
23446
+ "iOS - iPhone 15 (iOS 26)",
23447
+ "iOS - iPhone 15 (iOS 26) (Apple ID)",
23448
+ "iOS - iPhone 15 (iOS 26) (allowlisted)",
23449
+ "iOS - iPhone 15 (iOS 26) (private)",
23450
+ "iOS - iPhone 17 (iOS 26)"
23451
+ ];
23452
+ var iosPresetLiteralSchema = _enum(iosPresetLiteralRaw);
23453
+ var webPresetLiteralRaw = [
23454
+ "Basic",
23455
+ "Electron",
23456
+ "Web - Chrome",
23457
+ "Web - Chrome (GPU)",
23458
+ "Web - Firefox",
23459
+ "Web - Firefox (GPU)",
23460
+ "Web - Safari",
23461
+ "Web - Safari (GPU)"
23462
+ ];
23463
+ var webPresetLiteralSchema = _enum(webPresetLiteralRaw);
23464
+ var presetLiteralsSchema = union([
23465
+ androidPresetLiteralSchema,
23466
+ iosPresetLiteralSchema,
23467
+ webPresetLiteralSchema
23468
+ ]);
23469
+ var presetTargetMap = {
23470
+ "Android - Pixel": androidPixelTarget,
23471
+ "Android - Pixel 2 (Android 14)": androidPixel2Android14Target,
23472
+ "Android - Pixel 9": androidPixel9Target,
23473
+ "Android - Pixel 9 (Android 14)": androidPixel9Android14Target,
23474
+ "Android - Pixel 9 (Android 15)": androidPixel9Android15Target,
23475
+ "Android - Pixel 9 (Android 16)": androidPixel9Android16Target,
23476
+ "Android - Pixel Tablet (Android 14)": androidPixelTabletAndroid14Target,
23477
+ "Android - Tablet": androidTabletTarget,
23478
+ Basic: webBasicTarget,
23479
+ Electron: webElectronTarget,
23480
+ "iOS - Allowlisted iPhone": allowlistedIphoneTarget,
23481
+ "iOS - Apple ID Enabled iPad": appleIdEnabledIpadTarget,
23482
+ "iOS - Apple ID Enabled iPhone": appleIdEnabledIphoneTarget,
23483
+ "iOS - iPad": ipadTarget,
23484
+ "iOS - iPad 11 (iOS 18)": ipad11ios18Target,
23485
+ "iOS - iPad 11 (iOS 18) (Apple ID)": ipad11ios18AppleIdTarget,
23486
+ "iOS - iPad 11 (iOS 26)": ipad11ios26Target,
23487
+ "iOS - iPhone 15 (iOS 17)": iphone15ios17Target,
23488
+ "iOS - iPhone 15 (iOS 18)": iphone15ios18Target,
23489
+ "iOS - iPhone 15 (iOS 26)": iphone15ios26Target,
23490
+ "iOS - iPhone 15 (iOS 26) (allowlisted)": iphone15ios26AllowlistedTarget,
23491
+ "iOS - iPhone 15 (iOS 26) (Apple ID)": iphone15ios26AppleIdTarget,
23492
+ "iOS - iPhone 15 (iOS 26) (private)": iphone15ios26PrivateTarget,
23493
+ "iOS - iPhone 17 (iOS 26)": iphone17ios26Target,
23494
+ "Web - Chrome": webChromeTarget,
23495
+ "Web - Chrome (GPU)": webChromeWithGpuTarget,
23496
+ "Web - Firefox": webFirefoxTarget,
23497
+ "Web - Firefox (GPU)": webFirefoxWithGpuTarget,
23498
+ "Web - Safari": webSafariTarget,
23499
+ "Web - Safari (GPU)": webSafariWithGpuTarget
23500
+ };
23501
+
23502
+ // node_modules/@qawolf/flow-targets/dist/schemas/shared/screenDimensions.js
23503
+ var screenDimensions = {
23504
+ "iPad 11": { screenHeight: 2360, screenWidth: 1640 },
23505
+ "iPhone 15": { screenHeight: 2556, screenWidth: 1179 },
23506
+ "iPhone 15 Plus": { screenHeight: 2796, screenWidth: 1290 },
23507
+ "iPhone 15 Pro Max": { screenHeight: 2868, screenWidth: 1320 },
23508
+ "iPhone 16 Plus": { screenHeight: 2796, screenWidth: 1290 },
23509
+ "iPhone 17": { screenHeight: 2622, screenWidth: 1206 },
23510
+ "Pixel 2": { screenHeight: 1920, screenWidth: 1080 },
23511
+ "Pixel 9": { screenHeight: 2424, screenWidth: 1080 },
23512
+ "Pixel Tablet": { screenHeight: 1600, screenWidth: 2560 },
23513
+ playwright_default: { screenHeight: 804, screenWidth: 1288 }
23514
+ };
23515
+
23516
+ // node_modules/@qawolf/flow-targets/dist/schemas/shared/platforms.js
23517
+ var platformsSchema = _enum(["web", "android", "ios"]);
23518
+
23519
+ // node_modules/@qawolf/flow-targets/dist/schemas/shared/runnerNames.js
23520
+ var runnerNamesSchema = _enum([
23521
+ "node20Basic",
23522
+ "classic",
23523
+ "node20GpuWithPlaywright",
23524
+ "node20WithPlaywright",
23525
+ "node20WithAndroid",
23526
+ "node20WithIos"
23527
+ ]);
23528
+
23529
+ // node_modules/@qawolf/flow-targets/dist/schemas/shared/baseSchemas.js
23530
+ var baseInputSchema = object({
23531
+ platform: platformsSchema,
23532
+ schemaVersion: int().positive()
23533
+ });
23534
+ var baseOutputSchema = baseInputSchema.extend({
23535
+ runnerName: runnerNamesSchema
23536
+ });
23537
+
23538
+ // node_modules/@qawolf/flow-targets/dist/schemas/android/v1/supportedModelsSchema.js
23539
+ var supportedModelsInputSchema = _enum([
23540
+ "Pixel 2",
23541
+ "Pixel Tablet",
23542
+ "Pixel 9"
23543
+ ]);
23544
+ var supportedModelsOutputSchema = _enum([
23545
+ "pixel_2",
23546
+ "pixel_tablet",
23547
+ "pixel_9"
23548
+ ]);
23549
+ var supportedModelsSchema = codec(supportedModelsInputSchema, supportedModelsOutputSchema, {
23550
+ decode(deviceModel) {
23551
+ switch (deviceModel) {
23552
+ case "Pixel 2":
23553
+ return "pixel_2";
23554
+ case "Pixel 9":
23555
+ return "pixel_9";
23556
+ case "Pixel Tablet":
23557
+ return "pixel_tablet";
23558
+ default:
23559
+ throw Error(`Unsupported device model: ${deviceModel}`);
23560
+ }
23561
+ },
23562
+ encode(deviceModel) {
23563
+ switch (deviceModel) {
23564
+ case "pixel_2":
23565
+ return "Pixel 2";
23566
+ case "pixel_9":
23567
+ return "Pixel 9";
23568
+ case "pixel_tablet":
23569
+ return "Pixel Tablet";
23570
+ default:
23571
+ throw Error(`Unsupported device model: ${deviceModel}`);
23572
+ }
23573
+ }
23574
+ });
23575
+
23576
+ // node_modules/@qawolf/flow-targets/dist/schemas/android/v1/input.js
23577
+ var supportedVersionsSchema = _enum(["34", "35", "36"]);
23578
+ var androidMetaInputSchema = discriminatedUnion("deviceModel", [
23579
+ object({
23580
+ androidVersion: supportedVersionsSchema.extract(["34"]),
23581
+ deviceModel: supportedModelsInputSchema.extract(["Pixel 2"])
23582
+ }),
23583
+ object({
23584
+ androidVersion: supportedVersionsSchema.extract(["34"]),
23585
+ deviceModel: supportedModelsInputSchema.extract(["Pixel Tablet"])
23586
+ }),
23587
+ object({
23588
+ androidVersion: supportedVersionsSchema.extract(["34", "35", "36"]),
23589
+ deviceModel: supportedModelsInputSchema.extract(["Pixel 9"])
23590
+ })
23591
+ ]);
23592
+ var androidTargetInputSchema = baseInputSchema.extend({
23593
+ meta: androidMetaInputSchema,
23594
+ platform: platformsSchema.extract(["android"]),
23595
+ schemaVersion: literal(1)
23596
+ });
23597
+
23598
+ // node_modules/@qawolf/flow-targets/dist/schemas/android/v1/output.js
23599
+ var androidTargetOutputSchema = baseOutputSchema.extend({
23600
+ ...androidTargetInputSchema.shape,
23601
+ meta: object({
23602
+ androidVersion: supportedVersionsSchema,
23603
+ deviceModel: supportedModelsOutputSchema,
23604
+ screenHeight: number2(),
23605
+ screenWidth: number2()
23606
+ }),
23607
+ runnerName: runnerNamesSchema.extract(["node20WithAndroid"])
23608
+ });
23609
+
23610
+ // node_modules/@qawolf/flow-targets/dist/schemas/android/v1/index.js
23611
+ var androidExecutionTargetSchema = codec(androidTargetInputSchema, androidTargetOutputSchema, {
23612
+ decode(value) {
23613
+ return {
23614
+ ...value,
23615
+ meta: {
23616
+ ...value.meta,
23617
+ deviceModel: supportedModelsSchema.decode(value.meta.deviceModel),
23618
+ ...screenDimensions[value.meta.deviceModel]
23619
+ },
23620
+ runnerName: "node20WithAndroid"
23621
+ };
23622
+ },
23623
+ encode(value) {
23624
+ return androidTargetInputSchema.parse({
23625
+ ...value,
23626
+ meta: {
23627
+ ...value.meta,
23628
+ deviceModel: supportedModelsSchema.encode(value.meta.deviceModel)
23629
+ }
23630
+ });
23631
+ }
23632
+ });
23633
+
23634
+ // node_modules/@qawolf/flow-targets/dist/schemas/android/index.js
23635
+ var androidExecutionTargetInputSchema = discriminatedUnion("schemaVersion", [androidTargetInputSchema]);
23636
+ var androidExecutionTargetOutputSchema = discriminatedUnion("schemaVersion", [androidTargetOutputSchema]);
23637
+ var androidExecutionTargetSchema2 = discriminatedUnion("schemaVersion", [androidExecutionTargetSchema]);
23638
+
23639
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/v1/supportedModelsSchema.js
23640
+ var supportedModelsInputSchema2 = _enum([
23641
+ "iPhone 15",
23642
+ "iPhone 15 Plus",
23643
+ "iPhone 15 Pro Max",
23644
+ "iPhone 16 Plus",
23645
+ "iPhone 17",
23646
+ "iPad 11"
23647
+ ]);
23648
+ var supportedModelsOutputSchema2 = _enum([
23649
+ "iphone15_4",
23650
+ "iphone15_5",
23651
+ "iphone16_2",
23652
+ "iphone17_4",
23653
+ "iphone18_3",
23654
+ "ipad15_7"
23655
+ ]);
23656
+ var supportedModelsSchema2 = codec(supportedModelsInputSchema2, supportedModelsOutputSchema2, {
23657
+ decode(deviceModel) {
23658
+ switch (deviceModel) {
23659
+ case "iPad 11":
23660
+ return "ipad15_7";
23661
+ case "iPhone 15":
23662
+ return "iphone15_4";
23663
+ case "iPhone 15 Plus":
23664
+ return "iphone15_5";
23665
+ case "iPhone 15 Pro Max":
23666
+ return "iphone16_2";
23667
+ case "iPhone 16 Plus":
23668
+ return "iphone17_4";
23669
+ case "iPhone 17":
23670
+ return "iphone18_3";
23671
+ default:
23672
+ throw Error(`Unsupported device model: ${deviceModel}`);
23673
+ }
23674
+ },
23675
+ encode(value) {
23676
+ switch (value) {
23677
+ case "ipad15_7":
23678
+ return "iPad 11";
23679
+ case "iphone15_4":
23680
+ return "iPhone 15";
23681
+ case "iphone15_5":
23682
+ return "iPhone 15 Plus";
23683
+ case "iphone16_2":
23684
+ return "iPhone 15 Pro Max";
23685
+ case "iphone17_4":
23686
+ return "iPhone 16 Plus";
23687
+ case "iphone18_3":
23688
+ return "iPhone 17";
23689
+ default:
23690
+ throw Error(`Unsupported device model: ${value}`);
23691
+ }
23692
+ }
23693
+ });
23694
+
23695
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/v1/supportedTargetDevicesSchema.js
23696
+ var supportedTargetDevicesInputSchema = _enum([
23697
+ "Any Device",
23698
+ "Allowlisted Device",
23699
+ "Team Dedicated Device"
23700
+ ]);
23701
+ var supportedTargetDevicesOutputSchema = _enum([
23702
+ "anyDevice",
23703
+ "allowlistedDevice",
23704
+ "teamDedicatedDevice"
23705
+ ]);
23706
+ var supportedTargetDevicesSchema = codec(supportedTargetDevicesInputSchema, supportedTargetDevicesOutputSchema, {
23707
+ decode(targetDevices) {
23708
+ switch (targetDevices) {
23709
+ case "Allowlisted Device":
23710
+ return "allowlistedDevice";
23711
+ case "Any Device":
23712
+ return "anyDevice";
23713
+ case "Team Dedicated Device":
23714
+ return "teamDedicatedDevice";
23715
+ default:
23716
+ throw Error(`Unsupported target devices: ${targetDevices}`);
23717
+ }
23718
+ },
23719
+ encode(targetDevices) {
23720
+ switch (targetDevices) {
23721
+ case "allowlistedDevice":
23722
+ return "Allowlisted Device";
23723
+ case "anyDevice":
23724
+ return "Any Device";
23725
+ case "teamDedicatedDevice":
23726
+ return "Team Dedicated Device";
23727
+ default:
23728
+ throw Error(`Unsupported target devices: ${targetDevices}`);
23729
+ }
23730
+ }
23731
+ });
23732
+
23733
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/v1/input.js
23734
+ var supportedVersionsInputSchema = _enum(["17", "18", "26"]);
23735
+ var baseMetaInputSchema = object({
23736
+ appleId: union([email2(), boolean2()]).optional(),
23737
+ deviceUdid: string2().optional(),
23738
+ targetDevices: supportedTargetDevicesInputSchema.default("Any Device")
23739
+ });
23740
+ var iosMetaInputSchema = discriminatedUnion("deviceModel", [
23741
+ baseMetaInputSchema.extend({
23742
+ deviceModel: supportedModelsInputSchema2.extract(["iPhone 15"]),
23743
+ iosVersion: supportedVersionsInputSchema.extract(["17", "18", "26"])
23744
+ }),
23745
+ baseMetaInputSchema.extend({
23746
+ deviceModel: supportedModelsInputSchema2.extract(["iPhone 15 Plus"]),
23747
+ iosVersion: supportedVersionsInputSchema.extract(["17"])
23748
+ }),
23749
+ baseMetaInputSchema.extend({
23750
+ deviceModel: supportedModelsInputSchema2.extract(["iPhone 15 Pro Max"]),
23751
+ iosVersion: supportedVersionsInputSchema.extract(["17"])
23752
+ }),
23753
+ baseMetaInputSchema.extend({
23754
+ deviceModel: supportedModelsInputSchema2.extract(["iPhone 16 Plus"]),
23755
+ iosVersion: supportedVersionsInputSchema.extract(["26"])
23756
+ }),
23757
+ baseMetaInputSchema.extend({
23758
+ deviceModel: supportedModelsInputSchema2.extract(["iPhone 17"]),
23759
+ iosVersion: supportedVersionsInputSchema.extract(["26"])
23760
+ }),
23761
+ baseMetaInputSchema.extend({
23762
+ deviceModel: supportedModelsInputSchema2.extract(["iPad 11"]),
23763
+ iosVersion: supportedVersionsInputSchema.extract(["18", "26"])
23764
+ })
23765
+ ]);
23766
+ var iosTargetInputSchema = baseInputSchema.extend({
23767
+ meta: iosMetaInputSchema,
23768
+ platform: platformsSchema.extract(["ios"]),
23769
+ schemaVersion: literal(1)
23770
+ });
23771
+
23772
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/v1/output.js
23773
+ var iosTargetOutputSchema = baseOutputSchema.extend({
23774
+ ...iosTargetInputSchema.shape,
23775
+ meta: object({
23776
+ appleId: union([email2(), boolean2()]).optional(),
23777
+ deviceModel: supportedModelsOutputSchema2,
23778
+ deviceUdid: string2().optional(),
23779
+ iosVersion: supportedVersionsInputSchema,
23780
+ screenHeight: number2(),
23781
+ screenWidth: number2(),
23782
+ targetDevices: supportedTargetDevicesOutputSchema
23783
+ }),
23784
+ runnerName: runnerNamesSchema.extract(["node20WithIos"])
23785
+ });
23786
+
23787
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/v1/index.js
23788
+ var iosExecutionTargetSchema = codec(iosTargetInputSchema, iosTargetOutputSchema, {
23789
+ decode(value) {
23790
+ return {
23791
+ ...value,
23792
+ meta: {
23793
+ ...value.meta,
23794
+ appleId: value.meta.appleId || undefined,
23795
+ deviceModel: supportedModelsSchema2.decode(value.meta.deviceModel),
23796
+ ...screenDimensions[value.meta.deviceModel],
23797
+ targetDevices: supportedTargetDevicesSchema.decode(value.meta.targetDevices)
23798
+ },
23799
+ runnerName: "node20WithIos"
23800
+ };
23801
+ },
23802
+ encode(value) {
23803
+ return iosTargetInputSchema.parse({
23804
+ ...value,
23805
+ meta: {
23806
+ ...value.meta,
23807
+ deviceModel: supportedModelsSchema2.encode(value.meta.deviceModel),
23808
+ targetDevices: supportedTargetDevicesSchema.encode(value.meta.targetDevices)
23809
+ }
23810
+ });
23811
+ }
23812
+ });
23813
+
23814
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/index.js
23815
+ var iosExecutionTargetInputSchema = discriminatedUnion("schemaVersion", [iosTargetInputSchema]);
23816
+ var iosExecutionTargetOutputSchema = discriminatedUnion("schemaVersion", [iosTargetOutputSchema]);
23817
+ var iosExecutionTargetSchema2 = discriminatedUnion("schemaVersion", [
23818
+ iosExecutionTargetSchema
23819
+ ]);
23820
+
23821
+ // node_modules/@qawolf/flow-targets/dist/schemas/web/v1/input.js
23822
+ var supportedBrowserNamesSchema = _enum(["chrome", "firefox", "safari"]);
23823
+ var defaultScreenDimensions = {
23824
+ height: 804,
23825
+ width: 1288
23826
+ };
23827
+ var webBrowserInputSchema = object({
23828
+ name: supportedBrowserNamesSchema,
23829
+ screenHeight: int().positive().default(defaultScreenDimensions.height),
23830
+ screenWidth: int().positive().default(defaultScreenDimensions.width)
23831
+ });
23832
+ var defaultBrowserInfoSchema = object({
23833
+ browser: optional(never()),
23834
+ defaultBrowser: webBrowserInputSchema,
23835
+ gpuEnabled: boolean2(),
23836
+ kind: literal("browser").default("browser")
23837
+ });
23838
+ var legacyBrowserInfoSchema = object({
23839
+ browser: webBrowserInputSchema,
23840
+ defaultBrowser: optional(never()),
23841
+ gpuEnabled: boolean2(),
23842
+ kind: literal("browser").default("browser")
23843
+ });
23844
+ var electronInfoSchema = object({
23845
+ browser: optional(never()),
23846
+ defaultBrowser: optional(never()),
23847
+ gpuEnabled: literal(false).default(false),
23848
+ kind: literal("electron")
23849
+ });
23850
+ var browserInfoSchema = union([
23851
+ defaultBrowserInfoSchema,
23852
+ legacyBrowserInfoSchema
23853
+ ]);
23854
+ var webMetaSchema = union([browserInfoSchema, electronInfoSchema]);
23855
+ var webTargetInputSchema = baseInputSchema.extend({
23856
+ meta: union([webMetaSchema, literal("legacy")]),
23857
+ platform: platformsSchema.extract(["web"]),
23858
+ schemaVersion: literal(1)
23859
+ });
23860
+ function getWebBrowserInfo(meta) {
23861
+ if ("defaultBrowser" in meta && meta.defaultBrowser)
23862
+ return webBrowserInputSchema.parse(meta.defaultBrowser);
23863
+ if ("browser" in meta && meta.browser)
23864
+ return webBrowserInputSchema.parse(meta.browser);
23865
+ throw Error("Expected either defaultBrowser or browser in web meta");
23866
+ }
23867
+
23868
+ // node_modules/@qawolf/flow-targets/dist/schemas/web/v1/webRunnerNames.js
23869
+ var webRunnerNamesSchema = runnerNamesSchema.extract([
23870
+ "node20Basic",
23871
+ "node20WithPlaywright",
23872
+ "node20GpuWithPlaywright"
23873
+ ]);
23874
+
23875
+ // node_modules/@qawolf/flow-targets/dist/schemas/web/v1/output.js
23876
+ var webOutputMetaSchema = union([
23877
+ object({
23878
+ defaultBrowser: webBrowserInputSchema,
23879
+ gpuEnabled: boolean2(),
23880
+ kind: literal("browser").default("browser")
23881
+ }),
23882
+ object({
23883
+ browser: webBrowserInputSchema,
23884
+ gpuEnabled: boolean2(),
23885
+ kind: literal("browser").default("browser")
23886
+ }),
23887
+ object({
23888
+ gpuEnabled: literal(false),
23889
+ kind: literal("electron")
23890
+ }),
23891
+ literal("legacy")
23892
+ ]);
23893
+ var webTargetOutputSchema = baseOutputSchema.extend({
23894
+ meta: webOutputMetaSchema,
23895
+ platform: literal("web"),
23896
+ runnerName: webRunnerNamesSchema,
23897
+ schemaVersion: literal(1)
23898
+ });
23899
+
23900
+ // node_modules/@qawolf/flow-targets/dist/schemas/web/v1/index.js
23901
+ var webExecutionTargetSchema = codec(webTargetInputSchema, webTargetOutputSchema, {
23902
+ decode(value) {
23903
+ const runnerName = value.meta === "legacy" ? "node20Basic" : value.meta.gpuEnabled ? "node20GpuWithPlaywright" : "node20WithPlaywright";
23904
+ let meta;
23905
+ if (value.meta === "legacy")
23906
+ meta = value.meta;
23907
+ else if (value.meta.kind === "electron") {
23908
+ meta = {
23909
+ gpuEnabled: false,
23910
+ kind: "electron"
23911
+ };
23912
+ } else {
23913
+ const browserInfo = getWebBrowserInfo(value.meta);
23914
+ meta = {
23915
+ defaultBrowser: browserInfo,
23916
+ gpuEnabled: value.meta.gpuEnabled,
23917
+ kind: "browser"
23918
+ };
23919
+ }
23920
+ return {
23921
+ ...value,
23922
+ meta,
23923
+ runnerName
23924
+ };
23925
+ },
23926
+ encode(value) {
23927
+ return webTargetInputSchema.parse(value);
23928
+ }
23929
+ });
23930
+
23931
+ // node_modules/@qawolf/flow-targets/dist/schemas/web/index.js
23932
+ var webExecutionTargetInputSchema = discriminatedUnion("schemaVersion", [webTargetInputSchema]);
23933
+ var webExecutionTargetOutputSchema = discriminatedUnion("schemaVersion", [webTargetOutputSchema]);
23934
+ var webExecutionTargetSchema2 = discriminatedUnion("schemaVersion", [
23935
+ webExecutionTargetSchema
23936
+ ]);
23937
+
23938
+ // node_modules/@qawolf/flow-targets/dist/schemas/index.js
23939
+ var executionTargetInputSchema = discriminatedUnion("platform", [
23940
+ androidExecutionTargetInputSchema,
23941
+ iosExecutionTargetInputSchema,
23942
+ webExecutionTargetInputSchema
23943
+ ]);
23944
+ var executionTargetOutputSchema = discriminatedUnion("platform", [
23945
+ androidExecutionTargetOutputSchema,
23946
+ iosExecutionTargetOutputSchema,
23947
+ webExecutionTargetOutputSchema
23948
+ ]);
23949
+ var executionTargetSchema = discriminatedUnion("platform", [
23950
+ androidExecutionTargetSchema2,
23951
+ iosExecutionTargetSchema2,
23952
+ webExecutionTargetSchema2
23953
+ ]);
23954
+
23955
+ // node_modules/@qawolf/flow-targets/dist/parseExecutionTarget.js
23956
+ var isPresetLiteral = (input) => presetLiteralsSchema.safeParse(input).success;
23957
+ var parseExecutionTarget = (input) => executionTargetSchema.decode(isPresetLiteral(input) ? presetTargetMap[input] : input);
23958
+ // node_modules/@qawolf/flow-targets/dist/index.js
23959
+ var presetLiteralOrAdHocTargetSchema = union([
23960
+ presetLiteralsSchema,
23961
+ executionTargetInputSchema
23962
+ ]);
23963
+
22290
23964
  // src/core/androidTargets.ts
22291
- import { parseExecutionTarget } from "@qawolf/flow-targets";
22292
23965
  function makeAvdName(deviceModel, androidVersion) {
22293
23966
  return `qawolf_${deviceModel.toLowerCase().replace(/ /g, "_")}_api${androidVersion}`;
22294
23967
  }
@@ -22333,11 +24006,11 @@ async function* batchMap(items, fn, size) {
22333
24006
  }
22334
24007
 
22335
24008
  // src/core/nodeModulesBins.ts
22336
- import { join as join15 } from "node:path";
24009
+ import { join as join16 } from "node:path";
22337
24010
  function nodeModulesBinCandidates(envDir, name, platform) {
22338
- const binDir = join15(envDir, "node_modules", ".bin");
24011
+ const binDir = join16(envDir, "node_modules", ".bin");
22339
24012
  const names = platform === "win32" ? [`${name}.cmd`, `${name}.exe`] : [name];
22340
- return names.map((shim) => join15(binDir, shim));
24013
+ return names.map((shim) => join16(binDir, shim));
22341
24014
  }
22342
24015
  function appiumCliCandidates(envDir, platform) {
22343
24016
  return nodeModulesBinCandidates(envDir, "appium", platform);
@@ -22394,21 +24067,21 @@ async function checkUiautomator2(spawn, appiumBin, platform) {
22394
24067
  }
22395
24068
 
22396
24069
  // src/core/androidBins.ts
22397
- import { join as join16 } from "node:path";
24070
+ import { join as join17 } from "node:path";
22398
24071
  function withExeSuffix(name, platform) {
22399
24072
  return platform === "win32" ? `${name}.exe` : name;
22400
24073
  }
22401
24074
  function emulatorBin(home, platform) {
22402
24075
  const name = withExeSuffix("emulator", platform);
22403
- return home ? join16(home, "emulator", name) : name;
24076
+ return home ? join17(home, "emulator", name) : name;
22404
24077
  }
22405
24078
  function adbBin(home, platform) {
22406
24079
  const name = withExeSuffix("adb", platform);
22407
- return home ? join16(home, "platform-tools", name) : name;
24080
+ return home ? join17(home, "platform-tools", name) : name;
22408
24081
  }
22409
24082
  function cmdlineToolsBin(home, name, platform) {
22410
24083
  const file = platform === "win32" ? `${name}.bat` : name;
22411
- return join16(home, "cmdline-tools", "latest", "bin", file);
24084
+ return join17(home, "cmdline-tools", "latest", "bin", file);
22412
24085
  }
22413
24086
  function sdkManagerBin(home, platform) {
22414
24087
  return cmdlineToolsBin(home, "sdkmanager", platform);
@@ -22705,9 +24378,9 @@ async function checkNpmRegistry(deps) {
22705
24378
  }
22706
24379
 
22707
24380
  // src/core/playwrightCli.ts
22708
- import { join as join17 } from "node:path";
24381
+ import { join as join18 } from "node:path";
22709
24382
  function playwrightCliJsPath(envDir) {
22710
- return join17(envDir, "node_modules", "playwright", "cli.js");
24383
+ return join18(envDir, "node_modules", "playwright", "cli.js");
22711
24384
  }
22712
24385
  function playwrightCliInvocation(args) {
22713
24386
  return {
@@ -22721,7 +24394,7 @@ function playwrightCliInvocation(args) {
22721
24394
  var flowsVersion = "0.1.4";
22722
24395
  var playwrightVersion = "1.62.0";
22723
24396
  var emailsVersion = "1.1.1";
22724
- var testkitVersion = "1.1.1";
24397
+ var testkitVersion = "1.2.1";
22725
24398
  var appiumVersion = "3.6.0";
22726
24399
  var appiumUiautomator2DriverVersion = "8.4.0";
22727
24400
  var expectWebdriverioVersion = "5.6.5";
@@ -22857,11 +24530,11 @@ function renderResults(ui, results) {
22857
24530
  }
22858
24531
 
22859
24532
  // src/domains/flows/ensureDeps.ts
22860
- import { dirname as dirname4, join as join18 } from "node:path";
24533
+ import { dirname as dirname4, join as join19 } from "node:path";
22861
24534
  function findEnvDir(flowPath, fs) {
22862
24535
  let dir = dirname4(flowPath);
22863
24536
  while (true) {
22864
- if (fs.existsSync(join18(dir, "package.json")))
24537
+ if (fs.existsSync(join19(dir, "package.json")))
22865
24538
  return dir;
22866
24539
  const parent = dirname4(dir);
22867
24540
  if (parent === dir)
@@ -22887,7 +24560,7 @@ function resolveProjectDirSafe(files, fs) {
22887
24560
  }
22888
24561
 
22889
24562
  // src/domains/flows/expand.ts
22890
- import { join as join19, resolve as resolve3 } from "node:path";
24563
+ import { join as join20, resolve as resolve3 } from "node:path";
22891
24564
 
22892
24565
  // node_modules/tinyglobby/dist/index.mjs
22893
24566
  import { readdir, readdirSync, realpath, realpathSync, stat, statSync } from "fs";
@@ -23716,7 +25389,6 @@ async function glob(globInput, options) {
23716
25389
  }
23717
25390
 
23718
25391
  // src/core/flowMeta.ts
23719
- import { getWebBrowserInfo, parseExecutionTarget as parseExecutionTarget2 } from "@qawolf/flow-targets";
23720
25392
  import { basename as basename2 } from "node:path";
23721
25393
 
23722
25394
  // src/core/stringLiteral.ts
@@ -23832,7 +25504,7 @@ var browserNameToPlaywright = {
23832
25504
  function classifyTarget(target) {
23833
25505
  let parsed;
23834
25506
  try {
23835
- parsed = parseExecutionTarget2(target);
25507
+ parsed = parseExecutionTarget(target);
23836
25508
  } catch {
23837
25509
  return { kind: "unrecognized" };
23838
25510
  }
@@ -23876,11 +25548,11 @@ function makePeekFlowMeta(fs) {
23876
25548
  };
23877
25549
  }
23878
25550
  async function resolveGlobRoots(cwd, fs) {
23879
- const qawolfPath = join19(cwd, ".qawolf");
25551
+ const qawolfPath = join20(cwd, ".qawolf");
23880
25552
  let envDirs = [];
23881
25553
  try {
23882
25554
  const entries = await fs.readdirWithTypes(qawolfPath);
23883
- envDirs = entries.filter((e) => e.isDirectory()).map((e) => join19(qawolfPath, e.name));
25555
+ envDirs = entries.filter((e) => e.isDirectory()).map((e) => join20(qawolfPath, e.name));
23884
25556
  } catch {}
23885
25557
  return [cwd, ...envDirs];
23886
25558
  }
@@ -23906,11 +25578,11 @@ async function expandPatterns(patterns, cwd, logger, fs) {
23906
25578
  }
23907
25579
 
23908
25580
  // src/domains/runtimeEnv/clearRuntimeEnv.ts
23909
- import { join as join21 } from "node:path";
25581
+ import { join as join22 } from "node:path";
23910
25582
 
23911
25583
  // src/domains/runtimeEnv/managedEnvDir.ts
23912
25584
  import { createHash } from "node:crypto";
23913
- import { join as join20, resolve as resolve4 } from "node:path";
25585
+ import { join as join21, resolve as resolve4 } from "node:path";
23914
25586
 
23915
25587
  // src/domains/runtimeEnv/pinnedPackages.ts
23916
25588
  var pinnedPackages = [
@@ -23941,19 +25613,19 @@ function managedEnvBaseDir() {
23941
25613
  const override = process.env["QAWOLF_RUNTIME_DIR"]?.trim();
23942
25614
  if (override)
23943
25615
  return resolve4(override);
23944
- return join20(getDataDir(), "runtime");
25616
+ return join21(getDataDir(), "runtime");
23945
25617
  }
23946
25618
  function runStagingRoot() {
23947
25619
  return `${managedEnvBaseDir()}-runs`;
23948
25620
  }
23949
25621
  function managedEnvDir() {
23950
- return join20(managedEnvBaseDir(), managedEnvHash());
25622
+ return join21(managedEnvBaseDir(), managedEnvHash());
23951
25623
  }
23952
25624
  async function scaffoldManagedEnv(dir, fs) {
23953
25625
  await fs.mkdir(dir, { recursive: true });
23954
25626
  const dependencies = Object.fromEntries(pinnedPackages.map(({ name, version }) => [name, version]));
23955
- await fs.writeFile(join20(dir, "package.json"), JSON.stringify({ name: "qawolf-runtime", private: true, dependencies }, undefined, 2));
23956
- await fs.writeFile(join20(dir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
25627
+ await fs.writeFile(join21(dir, "package.json"), JSON.stringify({ name: "qawolf-runtime", private: true, dependencies }, undefined, 2));
25628
+ await fs.writeFile(join21(dir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
23957
25629
  `);
23958
25630
  }
23959
25631
 
@@ -23975,14 +25647,14 @@ async function isManagedRuntimeBase(dir, fs) {
23975
25647
  for (const entry of entries) {
23976
25648
  if (!entry.isDirectory())
23977
25649
  return false;
23978
- if (!await hasRuntimeSentinel(join21(dir, entry.name), fs))
25650
+ if (!await hasRuntimeSentinel(join22(dir, entry.name), fs))
23979
25651
  return false;
23980
25652
  }
23981
25653
  return true;
23982
25654
  }
23983
25655
  async function hasRuntimeSentinel(hashDir, fs) {
23984
25656
  try {
23985
- const pkg = JSON.parse(await fs.readFile(join21(hashDir, "package.json")));
25657
+ const pkg = JSON.parse(await fs.readFile(join22(hashDir, "package.json")));
23986
25658
  return pkg.name === runtimeSentinel;
23987
25659
  } catch {
23988
25660
  return false;
@@ -24008,10 +25680,10 @@ function spawnNpmInstall(cwd, spawn2 = spawn) {
24008
25680
  }
24009
25681
 
24010
25682
  // src/domains/runtimeEnv/resolvePinned.ts
24011
- import { join as join22 } from "node:path";
25683
+ import { join as join23 } from "node:path";
24012
25684
  function readInstalledVersion(dir, pkgName, fs) {
24013
25685
  try {
24014
- const pkgPath = join22(dir, "node_modules", ...pkgName.split("/"), "package.json");
25686
+ const pkgPath = join23(dir, "node_modules", ...pkgName.split("/"), "package.json");
24015
25687
  const raw = JSON.parse(fs.readFileSync(pkgPath));
24016
25688
  const { version } = raw;
24017
25689
  return typeof version === "string" ? version : undefined;
@@ -24059,10 +25731,10 @@ function allPinnedResolved(dir, fs, platform) {
24059
25731
  }
24060
25732
 
24061
25733
  // src/domains/runtimeEnv/shimDeps.ts
24062
- import { join as join24 } from "node:path";
25734
+ import { join as join25 } from "node:path";
24063
25735
 
24064
25736
  // src/shell/resolveExport.ts
24065
- import { join as join23 } from "node:path";
25737
+ import { join as join24 } from "node:path";
24066
25738
  function pickPath(entry, preference) {
24067
25739
  if (typeof entry === "string")
24068
25740
  return entry;
@@ -24094,28 +25766,28 @@ function resolveFromEnvDir(envDir, specifier, preference = "esm", fs = makeDefau
24094
25766
  const pkgName = parts.slice(0, pkgPartCount).join("/");
24095
25767
  const rest = parts.slice(pkgPartCount);
24096
25768
  const subpath = rest.length > 0 ? "./" + rest.join("/") : ".";
24097
- const pkgDir = join23(envDir, "node_modules", ...pkgName.split("/"));
25769
+ const pkgDir = join24(envDir, "node_modules", ...pkgName.split("/"));
24098
25770
  let pkg;
24099
25771
  try {
24100
- pkg = JSON.parse(fs.readFileSync(join23(pkgDir, "package.json")));
25772
+ pkg = JSON.parse(fs.readFileSync(join24(pkgDir, "package.json")));
24101
25773
  } catch (err) {
24102
25774
  if (isNoEntError(err))
24103
- throw new Error(`Package '${pkgName}' not found in ${join23(envDir, "node_modules")}`, { cause: err });
25775
+ throw new Error(`Package '${pkgName}' not found in ${join24(envDir, "node_modules")}`, { cause: err });
24104
25776
  throw err;
24105
25777
  }
24106
25778
  const exportsMap = normalizeExports(pkg.exports);
24107
25779
  const entry = exportsMap?.[subpath];
24108
25780
  const relative = pickPath(entry, preference) ?? (subpath === "." ? pkg.module ?? pkg.main : undefined);
24109
25781
  if (!relative) {
24110
- throw new Error(`No entry for "${subpath}" in exports of ${join23(pkgDir, "package.json")}`);
25782
+ throw new Error(`No entry for "${subpath}" in exports of ${join24(pkgDir, "package.json")}`);
24111
25783
  }
24112
- return join23(pkgDir, relative);
25784
+ return join24(pkgDir, relative);
24113
25785
  }
24114
25786
 
24115
25787
  // src/domains/runtimeEnv/shimDeps.ts
24116
25788
  function readShimMarker(shimDir, fs) {
24117
25789
  try {
24118
- const pkg = JSON.parse(fs.readFileSync(join24(shimDir, "package.json")));
25790
+ const pkg = JSON.parse(fs.readFileSync(join25(shimDir, "package.json")));
24119
25791
  if (pkg._qawolf_format) {
24120
25792
  return {
24121
25793
  _qawolf_version: pkg._qawolf_version ?? "",
@@ -24131,11 +25803,11 @@ function resolveBunBuild(bunBuild) {
24131
25803
  return bunBuild === false ? undefined : { build: bunBuild };
24132
25804
  }
24133
25805
  async function removeStaleShims(flowsDir, flowsDeps, fs) {
24134
- const shimsDir = join24(flowsDir, "node_modules");
25806
+ const shimsDir = join25(flowsDir, "node_modules");
24135
25807
  if (!fs.existsSync(shimsDir))
24136
25808
  return;
24137
25809
  for (const dep of flowsDeps) {
24138
- const shimDepDir = join24(shimsDir, ...dep.split("/"));
25810
+ const shimDepDir = join25(shimsDir, ...dep.split("/"));
24139
25811
  if (readShimMarker(shimDepDir, fs)) {
24140
25812
  await fs.rm(shimDepDir, { recursive: true, force: true });
24141
25813
  }
@@ -24144,17 +25816,17 @@ async function removeStaleShims(flowsDir, flowsDeps, fs) {
24144
25816
  async function buildDepShim(args) {
24145
25817
  const { envDir, flowsDir, dep, fs, build } = args;
24146
25818
  const depParts = dep.split("/");
24147
- const depDir = join24(envDir, "node_modules", ...depParts);
25819
+ const depDir = join25(envDir, "node_modules", ...depParts);
24148
25820
  if (!fs.existsSync(depDir))
24149
25821
  return;
24150
25822
  let depVersion;
24151
25823
  try {
24152
- const pkg = JSON.parse(fs.readFileSync(join24(depDir, "package.json")));
25824
+ const pkg = JSON.parse(fs.readFileSync(join25(depDir, "package.json")));
24153
25825
  depVersion = pkg.version ?? "unknown";
24154
25826
  } catch {
24155
25827
  depVersion = "unknown";
24156
25828
  }
24157
- const shimDir = join24(flowsDir, "node_modules", ...depParts);
25829
+ const shimDir = join25(flowsDir, "node_modules", ...depParts);
24158
25830
  if (fs.existsSync(shimDir)) {
24159
25831
  const marker = readShimMarker(shimDir, fs);
24160
25832
  if (!marker)
@@ -24181,20 +25853,20 @@ async function buildDepShim(args) {
24181
25853
  }
24182
25854
  const shimCode = await output.text();
24183
25855
  await fs.mkdir(shimDir, { recursive: true });
24184
- await fs.writeFile(join24(shimDir, "package.json"), JSON.stringify({
25856
+ await fs.writeFile(join25(shimDir, "package.json"), JSON.stringify({
24185
25857
  name: dep,
24186
25858
  _qawolf_version: depVersion,
24187
25859
  _qawolf_format: "bun-build-v1"
24188
25860
  }));
24189
- await fs.writeFile(join24(shimDir, "index.js"), shimCode);
25861
+ await fs.writeFile(join25(shimDir, "index.js"), shimCode);
24190
25862
  }
24191
25863
  async function shimFlowsDeps(envDir, fs = makeDefaultFs(), bunBuild) {
24192
- const flowsDir = join24(envDir, "node_modules", "@qawolf", "flows");
25864
+ const flowsDir = join25(envDir, "node_modules", "@qawolf", "flows");
24193
25865
  if (!fs.existsSync(flowsDir))
24194
25866
  return;
24195
25867
  let flowsDeps;
24196
25868
  try {
24197
- const flowsPkg = JSON.parse(fs.readFileSync(join24(flowsDir, "package.json")));
25869
+ const flowsPkg = JSON.parse(fs.readFileSync(join25(flowsDir, "package.json")));
24198
25870
  flowsDeps = Object.keys(flowsPkg.dependencies ?? {});
24199
25871
  } catch {
24200
25872
  return;
@@ -24375,11 +26047,11 @@ function collectValue(value, previous) {
24375
26047
  }
24376
26048
 
24377
26049
  // src/shell/manifest/pulledEnv.ts
24378
- import { join as join26 } from "node:path";
26050
+ import { join as join27 } from "node:path";
24379
26051
 
24380
26052
  // src/shell/manifest/io.ts
24381
26053
  import { createHash as createHash2 } from "node:crypto";
24382
- import { join as join25 } from "node:path";
26054
+ import { join as join26 } from "node:path";
24383
26055
  var manifestFilename = ".manifest.json";
24384
26056
  var flowEntrySchema = object({
24385
26057
  path: string2(),
@@ -24401,7 +26073,7 @@ var manifestSchema = object({
24401
26073
  async function readManifest(envDir, fs = makeDefaultFs()) {
24402
26074
  let raw;
24403
26075
  try {
24404
- raw = await fs.readFile(join25(envDir, manifestFilename));
26076
+ raw = await fs.readFile(join26(envDir, manifestFilename));
24405
26077
  } catch (err) {
24406
26078
  if (isNoEntError(err))
24407
26079
  return "missing";
@@ -24435,7 +26107,7 @@ async function readManifest(envDir, fs = makeDefaultFs()) {
24435
26107
  }
24436
26108
  async function writeManifest(envDir, manifest, fs = makeDefaultFs()) {
24437
26109
  const body = JSON.stringify(manifest, undefined, 2);
24438
- await fs.writeFile(join25(envDir, manifestFilename), `${body}
26110
+ await fs.writeFile(join26(envDir, manifestFilename), `${body}
24439
26111
  `);
24440
26112
  }
24441
26113
  function hashFile(absPath, fs = makeDefaultFs()) {
@@ -24461,7 +26133,7 @@ async function findPulledEnv(ref, cwd, fs = makeDefaultFs()) {
24461
26133
  return;
24462
26134
  }
24463
26135
  async function listPulledEnvDirs(cwd, fs = makeDefaultFs()) {
24464
- const root = join26(cwd, ".qawolf");
26136
+ const root = join27(cwd, ".qawolf");
24465
26137
  let entries;
24466
26138
  try {
24467
26139
  entries = await fs.readdirWithTypes(root);
@@ -24474,7 +26146,7 @@ async function listPulledEnvDirs(cwd, fs = makeDefaultFs()) {
24474
26146
  for (const entry of entries) {
24475
26147
  if (!entry.isDirectory())
24476
26148
  continue;
24477
- const envDir = join26(root, entry.name);
26149
+ const envDir = join27(root, entry.name);
24478
26150
  const manifest = await readManifest(envDir, fs);
24479
26151
  if (typeof manifest === "string")
24480
26152
  continue;
@@ -24866,7 +26538,7 @@ async function flowsListRemote(ctx, pattern, options) {
24866
26538
  }
24867
26539
 
24868
26540
  // src/domains/flows/pull/handler.ts
24869
- import { dirname as dirname8, join as join31, resolve as resolve8 } from "node:path";
26541
+ import { dirname as dirname8, join as join32, resolve as resolve8 } from "node:path";
24870
26542
 
24871
26543
  // src/domains/flows/pull/fetchPhase.ts
24872
26544
  async function fetchTags(ctx, envId) {
@@ -24888,6 +26560,7 @@ async function fetchBundleAndEnvVars(ctx, envId) {
24888
26560
  let bundleFetchedAt;
24889
26561
  let envVars;
24890
26562
  let envVarsFetchedAt;
26563
+ let teamId;
24891
26564
  let tags;
24892
26565
  await ctx.ui.withProgress([
24893
26566
  {
@@ -24903,10 +26576,11 @@ async function fetchBundleAndEnvVars(ctx, envId) {
24903
26576
  {
24904
26577
  message: flowsMessages.pull.fetchingEnvVars,
24905
26578
  task: async () => {
24906
- const result = await platformClient.getEnvVars(envId);
26579
+ const result = await platformClient.getEnvironmentWithVariables(envId);
24907
26580
  if (!result.ok)
24908
26581
  throw new Error(result.error);
24909
- envVars = result.value;
26582
+ envVars = result.value.environmentVariables;
26583
+ teamId = result.value.teamId;
24910
26584
  envVarsFetchedAt = new Date;
24911
26585
  }
24912
26586
  },
@@ -24917,10 +26591,17 @@ async function fetchBundleAndEnvVars(ctx, envId) {
24917
26591
  }
24918
26592
  }
24919
26593
  ], flowsMessages.pull.downloadComplete);
24920
- if (tmpArchive === undefined || bundleFetchedAt === undefined || envVars === undefined || envVarsFetchedAt === undefined) {
26594
+ if (tmpArchive === undefined || bundleFetchedAt === undefined || envVars === undefined || envVarsFetchedAt === undefined || teamId === undefined) {
24921
26595
  throw new Error("Unexpected state: the bundle and env vars were not fetched. " + "This is a bug - please report it at https://github.com/qawolf/cli/issues");
24922
26596
  }
24923
- return { tmpArchive, bundleFetchedAt, envVars, envVarsFetchedAt, tags };
26597
+ return {
26598
+ tmpArchive,
26599
+ bundleFetchedAt,
26600
+ envVars,
26601
+ envVarsFetchedAt,
26602
+ teamId,
26603
+ tags
26604
+ };
24924
26605
  }
24925
26606
 
24926
26607
  // src/domains/flows/pull/safety.ts
@@ -25010,7 +26691,7 @@ async function checkSafety(args) {
25010
26691
  }
25011
26692
 
25012
26693
  // src/domains/flows/pull/reportPull.ts
25013
- import { join as join27 } from "node:path";
26694
+ import { join as join28 } from "node:path";
25014
26695
  function reportPullResult(ui, args) {
25015
26696
  if (ui.mode !== "json")
25016
26697
  return;
@@ -25025,12 +26706,12 @@ function reportPullResult(ui, args) {
25025
26706
  assetDownloadedCount: args.assets.downloadedCount,
25026
26707
  assetReusedCount: args.assets.reusedCount,
25027
26708
  assetSkippedCount: args.assets.skippedCount,
25028
- manifestPath: join27(args.stage.envDir, manifestFilename)
26709
+ manifestPath: join28(args.stage.envDir, manifestFilename)
25029
26710
  }, "");
25030
26711
  }
25031
26712
 
25032
26713
  // src/domains/flows/pull/bundle.ts
25033
- import { join as join28, relative as relative6 } from "node:path";
26714
+ import { join as join29, relative as relative6 } from "node:path";
25034
26715
  async function flattenSingleWrapper(dir, fs = makeDefaultFs()) {
25035
26716
  const entries = await fs.readdir(dir);
25036
26717
  if (entries.length !== 1)
@@ -25038,12 +26719,12 @@ async function flattenSingleWrapper(dir, fs = makeDefaultFs()) {
25038
26719
  const innerName = entries[0];
25039
26720
  if (!innerName)
25040
26721
  return;
25041
- const inner = join28(dir, innerName);
26722
+ const inner = join29(dir, innerName);
25042
26723
  const innerStat = await fs.stat(inner);
25043
26724
  if (!innerStat.isDirectory())
25044
26725
  return;
25045
26726
  for (const e of await fs.readdir(inner)) {
25046
- await fs.rename(join28(inner, e), join28(dir, e));
26727
+ await fs.rename(join29(inner, e), join29(dir, e));
25047
26728
  }
25048
26729
  const leftovers = await fs.readdir(inner);
25049
26730
  if (leftovers.length > 0) {
@@ -25062,7 +26743,7 @@ async function buildManifest(args, fs = makeDefaultFs()) {
25062
26743
  const flowPaths = await walkForFlows(args.bundleDir, fs);
25063
26744
  const flows = await Promise.all(flowPaths.map(async (rel) => ({
25064
26745
  path: toPosix(rel),
25065
- contentHash: await hashFile(join28(args.bundleDir, rel), fs),
26746
+ contentHash: await hashFile(join29(args.bundleDir, rel), fs),
25066
26747
  tags: args.tags?.byPath.get(toPosix(rel))
25067
26748
  })));
25068
26749
  return {
@@ -25086,7 +26767,7 @@ async function walkForFlows(root, fs) {
25086
26767
  async function walk(current, root, out, fs) {
25087
26768
  const entries = await fs.readdirWithTypes(current);
25088
26769
  for (const e of entries) {
25089
- const abs = join28(current, e.name);
26770
+ const abs = join29(current, e.name);
25090
26771
  if (e.isDirectory()) {
25091
26772
  await walk(abs, root, out, fs);
25092
26773
  } else if (e.isFile() && flowExtensions.some((ext) => e.name.endsWith(ext))) {
@@ -25099,11 +26780,11 @@ async function sampleQawolfCommittedAt(bundleDir, fs = makeDefaultFs()) {
25099
26780
  const sample = flowPaths[0];
25100
26781
  if (!sample)
25101
26782
  return;
25102
- return (await fs.stat(join28(bundleDir, sample))).mtime.toISOString();
26783
+ return (await fs.stat(join29(bundleDir, sample))).mtime.toISOString();
25103
26784
  }
25104
26785
 
25105
26786
  // src/domains/flows/pull/applyTeamStorageRewrite.ts
25106
- import { join as join29, relative as relative7 } from "node:path";
26787
+ import { join as join30, relative as relative7 } from "node:path";
25107
26788
 
25108
26789
  // src/domains/flows/pull/rewriteTeamStorage.ts
25109
26790
  var runnerMountPrefix = "/home/wolf/team-storage/";
@@ -25139,7 +26820,7 @@ function isFlowFile(name) {
25139
26820
  async function walk2(dir, out, fs) {
25140
26821
  const entries = await fs.readdirWithTypes(dir);
25141
26822
  for (const e of entries) {
25142
- const abs = join29(dir, e.name);
26823
+ const abs = join30(dir, e.name);
25143
26824
  if (e.isDirectory()) {
25144
26825
  await walk2(abs, out, fs);
25145
26826
  } else if (e.isFile() && isSourceFile(e.name)) {
@@ -25167,7 +26848,7 @@ async function applyTeamStorageRewrite(rootDir, fs = makeDefaultFs()) {
25167
26848
  }
25168
26849
 
25169
26850
  // src/domains/flows/pull/envVars.ts
25170
- import { join as join30 } from "node:path";
26851
+ import { join as join31 } from "node:path";
25171
26852
 
25172
26853
  // src/core/dotenv.ts
25173
26854
  var bareKeyPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -25232,7 +26913,7 @@ function unquote(escaped) {
25232
26913
  async function writeEnvFile(envDir, vars, fs = makeDefaultFs()) {
25233
26914
  if (Object.keys(vars).length === 0)
25234
26915
  return;
25235
- await fs.writeFile(join30(envDir, ".env"), serializeDotenv(vars), {
26916
+ await fs.writeFile(join31(envDir, ".env"), serializeDotenv(vars), {
25236
26917
  mode: 384
25237
26918
  });
25238
26919
  }
@@ -25647,7 +27328,7 @@ var Jr = I.writev;
25647
27328
  var ht = Symbol("_autoClose");
25648
27329
  var H = Symbol("_close");
25649
27330
  var te = Symbol("_ended");
25650
- var m3 = Symbol("_fd");
27331
+ var m4 = Symbol("_fd");
25651
27332
  var Ni = Symbol("_finished");
25652
27333
  var tt = Symbol("_flags");
25653
27334
  var Ai = Symbol("_flush");
@@ -25674,7 +27355,7 @@ var Me = Symbol("_defaultFlag");
25674
27355
  var bt = Symbol("_errored");
25675
27356
  var _t = class extends A2 {
25676
27357
  [bt] = false;
25677
- [m3];
27358
+ [m4];
25678
27359
  [U2];
25679
27360
  [Ci];
25680
27361
  [j] = false;
@@ -25684,10 +27365,10 @@ var _t = class extends A2 {
25684
27365
  constructor(t, e) {
25685
27366
  if (e = e || {}, super(e), this.readable = true, this.writable = false, typeof t != "string")
25686
27367
  throw new TypeError("path must be a string");
25687
- this[bt] = false, this[m3] = typeof e.fd == "number" ? e.fd : undefined, this[U2] = t, this[Ci] = e.readSize || 16 * 1024 * 1024, this[j] = false, this[Fi] = typeof e.size == "number" ? e.size : 1 / 0, this[ee] = this[Fi], this[ht] = typeof e.autoClose == "boolean" ? e.autoClose : true, typeof this[m3] == "number" ? this[zt]() : this[at]();
27368
+ this[bt] = false, this[m4] = typeof e.fd == "number" ? e.fd : undefined, this[U2] = t, this[Ci] = e.readSize || 16 * 1024 * 1024, this[j] = false, this[Fi] = typeof e.size == "number" ? e.size : 1 / 0, this[ee] = this[Fi], this[ht] = typeof e.autoClose == "boolean" ? e.autoClose : true, typeof this[m4] == "number" ? this[zt]() : this[at]();
25688
27369
  }
25689
27370
  get fd() {
25690
- return this[m3];
27371
+ return this[m4];
25691
27372
  }
25692
27373
  get path() {
25693
27374
  return this[U2];
@@ -25702,7 +27383,7 @@ var _t = class extends A2 {
25702
27383
  I.open(this[U2], "r", (t, e) => this[Ht](t, e));
25703
27384
  }
25704
27385
  [Ht](t, e) {
25705
- t ? this[Ut](t) : (this[m3] = e, this.emit("open", e), this[zt]());
27386
+ t ? this[Ut](t) : (this[m4] = e, this.emit("open", e), this[zt]());
25706
27387
  }
25707
27388
  [vi]() {
25708
27389
  return Buffer.allocUnsafe(Math.min(this[Ci], this[ee]));
@@ -25713,16 +27394,16 @@ var _t = class extends A2 {
25713
27394
  let t = this[vi]();
25714
27395
  if (t.length === 0)
25715
27396
  return process.nextTick(() => this[Ii](null, 0, t));
25716
- I.read(this[m3], t, 0, t.length, null, (e, i, r) => this[Ii](e, i, r));
27397
+ I.read(this[m4], t, 0, t.length, null, (e, i, r) => this[Ii](e, i, r));
25717
27398
  }
25718
27399
  }
25719
27400
  [Ii](t, e, i) {
25720
27401
  this[j] = false, t ? this[Ut](t) : this[ki](e, i) && this[zt]();
25721
27402
  }
25722
27403
  [H]() {
25723
- if (this[ht] && typeof this[m3] == "number") {
25724
- let t = this[m3];
25725
- this[m3] = undefined, I.close(t, (e) => e ? this.emit("error", e) : this.emit("close"));
27404
+ if (this[ht] && typeof this[m4] == "number") {
27405
+ let t = this[m4];
27406
+ this[m4] = undefined, I.close(t, (e) => e ? this.emit("error", e) : this.emit("close"));
25726
27407
  }
25727
27408
  }
25728
27409
  [Ut](t) {
@@ -25738,7 +27419,7 @@ var _t = class extends A2 {
25738
27419
  case "finish":
25739
27420
  return false;
25740
27421
  case "drain":
25741
- return typeof this[m3] == "number" && this[zt](), false;
27422
+ return typeof this[m4] == "number" && this[zt](), false;
25742
27423
  case "error":
25743
27424
  return this[bt] ? false : (this[bt] = true, super.emit(t, ...e));
25744
27425
  default:
@@ -25761,7 +27442,7 @@ var Be = class extends _t {
25761
27442
  if (!this[j]) {
25762
27443
  this[j] = true;
25763
27444
  do {
25764
- let e = this[vi](), i = e.length === 0 ? 0 : I.readSync(this[m3], e, 0, e.length, null);
27445
+ let e = this[vi](), i = e.length === 0 ? 0 : I.readSync(this[m4], e, 0, e.length, null);
25765
27446
  if (!this[ki](i, e))
25766
27447
  break;
25767
27448
  } while (true);
@@ -25773,9 +27454,9 @@ var Be = class extends _t {
25773
27454
  }
25774
27455
  }
25775
27456
  [H]() {
25776
- if (this[ht] && typeof this[m3] == "number") {
25777
- let t = this[m3];
25778
- this[m3] = undefined, I.closeSync(t), this.emit("close");
27457
+ if (this[ht] && typeof this[m4] == "number") {
27458
+ let t = this[m4];
27459
+ this[m4] = undefined, I.closeSync(t), this.emit("close");
25779
27460
  }
25780
27461
  }
25781
27462
  };
@@ -25790,15 +27471,15 @@ var et = class extends Qr {
25790
27471
  [U2];
25791
27472
  [ie];
25792
27473
  [ht];
25793
- [m3];
27474
+ [m4];
25794
27475
  [Me];
25795
27476
  [tt];
25796
27477
  [Ni] = false;
25797
27478
  [ot];
25798
27479
  constructor(t, e) {
25799
- e = e || {}, super(e), this[U2] = t, this[m3] = typeof e.fd == "number" ? e.fd : undefined, this[ie] = e.mode === undefined ? 438 : e.mode, this[ot] = typeof e.start == "number" ? e.start : undefined, this[ht] = typeof e.autoClose == "boolean" ? e.autoClose : true;
27480
+ e = e || {}, super(e), this[U2] = t, this[m4] = typeof e.fd == "number" ? e.fd : undefined, this[ie] = e.mode === undefined ? 438 : e.mode, this[ot] = typeof e.start == "number" ? e.start : undefined, this[ht] = typeof e.autoClose == "boolean" ? e.autoClose : true;
25800
27481
  let i = this[ot] !== undefined ? "r+" : "w";
25801
- this[Me] = e.flags === undefined, this[tt] = e.flags === undefined ? i : e.flags, this[m3] === undefined && this[at]();
27482
+ this[Me] = e.flags === undefined, this[tt] = e.flags === undefined ? i : e.flags, this[m4] === undefined && this[at]();
25802
27483
  }
25803
27484
  emit(t, ...e) {
25804
27485
  if (t === "error") {
@@ -25809,7 +27490,7 @@ var et = class extends Qr {
25809
27490
  return super.emit(t, ...e);
25810
27491
  }
25811
27492
  get fd() {
25812
- return this[m3];
27493
+ return this[m4];
25813
27494
  }
25814
27495
  get path() {
25815
27496
  return this[U2];
@@ -25821,16 +27502,16 @@ var et = class extends Qr {
25821
27502
  I.open(this[U2], this[tt], this[ie], (t, e) => this[Ht](t, e));
25822
27503
  }
25823
27504
  [Ht](t, e) {
25824
- this[Me] && this[tt] === "r+" && t && t.code === "ENOENT" ? (this[tt] = "w", this[at]()) : t ? this[Ut](t) : (this[m3] = e, this.emit("open", e), this[gt] || this[Ai]());
27505
+ this[Me] && this[tt] === "r+" && t && t.code === "ENOENT" ? (this[tt] = "w", this[at]()) : t ? this[Ut](t) : (this[m4] = e, this.emit("open", e), this[gt] || this[Ai]());
25825
27506
  }
25826
27507
  end(t, e) {
25827
- return t && this.write(t, e), this[te] = true, !this[gt] && !this[Y].length && typeof this[m3] == "number" && this[Pt](null, 0), this;
27508
+ return t && this.write(t, e), this[te] = true, !this[gt] && !this[Y].length && typeof this[m4] == "number" && this[Pt](null, 0), this;
25828
27509
  }
25829
27510
  write(t, e) {
25830
- return typeof t == "string" && (t = Buffer.from(t, e)), this[te] ? (this.emit("error", new Error("write() after end()")), false) : this[m3] === undefined || this[gt] || this[Y].length ? (this[Y].push(t), this[ke] = true, false) : (this[gt] = true, this[ve](t), true);
27511
+ return typeof t == "string" && (t = Buffer.from(t, e)), this[te] ? (this.emit("error", new Error("write() after end()")), false) : this[m4] === undefined || this[gt] || this[Y].length ? (this[Y].push(t), this[ke] = true, false) : (this[gt] = true, this[ve](t), true);
25831
27512
  }
25832
27513
  [ve](t) {
25833
- I.write(this[m3], t, 0, t.length, this[ot], (e, i) => this[Pt](e, i));
27514
+ I.write(this[m4], t, 0, t.length, this[ot], (e, i) => this[Pt](e, i));
25834
27515
  }
25835
27516
  [Pt](t, e) {
25836
27517
  t ? this[Ut](t) : (this[ot] !== undefined && typeof e == "number" && (this[ot] += e), this[Y].length ? this[Ai]() : (this[gt] = false, this[te] && !this[Ni] ? (this[Ni] = true, this[H](), this.emit("finish")) : this[ke] && (this[ke] = false, this.emit("drain"))));
@@ -25842,13 +27523,13 @@ var et = class extends Qr {
25842
27523
  this[ve](this[Y].pop());
25843
27524
  else {
25844
27525
  let t = this[Y];
25845
- this[Y] = [], Jr(this[m3], t, this[ot], (e, i) => this[Pt](e, i));
27526
+ this[Y] = [], Jr(this[m4], t, this[ot], (e, i) => this[Pt](e, i));
25846
27527
  }
25847
27528
  }
25848
27529
  [H]() {
25849
- if (this[ht] && typeof this[m3] == "number") {
25850
- let t = this[m3];
25851
- this[m3] = undefined, I.close(t, (e) => e ? this.emit("error", e) : this.emit("close"));
27530
+ if (this[ht] && typeof this[m4] == "number") {
27531
+ let t = this[m4];
27532
+ this[m4] = undefined, I.close(t, (e) => e ? this.emit("error", e) : this.emit("close"));
25852
27533
  }
25853
27534
  }
25854
27535
  };
@@ -25868,15 +27549,15 @@ var Wt = class extends et {
25868
27549
  this[Ht](null, t);
25869
27550
  }
25870
27551
  [H]() {
25871
- if (this[ht] && typeof this[m3] == "number") {
25872
- let t = this[m3];
25873
- this[m3] = undefined, I.closeSync(t), this.emit("close");
27552
+ if (this[ht] && typeof this[m4] == "number") {
27553
+ let t = this[m4];
27554
+ this[m4] = undefined, I.closeSync(t), this.emit("close");
25874
27555
  }
25875
27556
  }
25876
27557
  [ve](t) {
25877
27558
  let e = true;
25878
27559
  try {
25879
- this[Pt](null, I.writeSync(this[m3], t, 0, t.length, this[ot])), e = false;
27560
+ this[Pt](null, I.writeSync(this[m4], t, 0, t.length, this[ot])), e = false;
25880
27561
  } finally {
25881
27562
  if (e)
25882
27563
  try {
@@ -28674,8 +30355,8 @@ async function handleFlowsPull(ctx, opts, deps) {
28674
30355
  ctx.ui.error(validation.error);
28675
30356
  return { error: validation.error };
28676
30357
  }
28677
- const destAbs = resolve8(opts.out ?? join31(".qawolf", opts.env));
28678
- const assetsAbs = join31(dirname8(destAbs), "assets");
30358
+ const destAbs = resolve8(opts.out ?? join32(".qawolf", opts.env));
30359
+ const assetsAbs = join32(dirname8(destAbs), "assets");
28679
30360
  await resolvedDeps.fs.mkdir(assetsAbs, { recursive: true });
28680
30361
  const yes = opts.yes ?? false;
28681
30362
  let archive;
@@ -28721,6 +30402,7 @@ async function handleFlowsPull(ctx, opts, deps) {
28721
30402
  message: flowsMessages.pull.downloadingTeamStorageAssets,
28722
30403
  task: async (update) => {
28723
30404
  const result = await ctx.platformClient.syncTeamStorageAssets(assetsAbs, {
30405
+ teamId: fetched.teamId,
28724
30406
  onProgress: ({ current, total }) => update(flowsMessages.pull.downloadingTeamStorageAssetsProgress(current, total))
28725
30407
  });
28726
30408
  if (!result.ok)
@@ -29009,10 +30691,10 @@ async function resolveEnvChoice(args) {
29009
30691
 
29010
30692
  // src/core/flowFailureHint.ts
29011
30693
  function flowFailureHint(errText, projectDir) {
29012
- const missingPackage = extractMissingPackage(errText);
29013
- if (missingPackage === undefined)
30694
+ const missing = extractMissingSpecifier(errText);
30695
+ if (missing === undefined)
29014
30696
  return;
29015
- return runnerMessages.moduleNotFoundHint(missingPackage, projectDir);
30697
+ return missing.kind === "path-alias" ? runnerMessages.pathAliasNotFoundHint(missing.specifier, projectDir) : runnerMessages.moduleNotFoundHint(missing.specifier, projectDir);
29016
30698
  }
29017
30699
 
29018
30700
  // src/shell/reporter/createCompositeReporter.ts
@@ -29101,9 +30783,6 @@ ${hint}`);
29101
30783
  };
29102
30784
  }
29103
30785
 
29104
- // src/domains/runner/runAndroidFlowUtils.ts
29105
- import { parseExecutionTarget as parseExecutionTarget3 } from "@qawolf/flow-targets";
29106
-
29107
30786
  // src/domains/runner/unsupportedDepNames.ts
29108
30787
  var unsupportedSharedDepNames = [
29109
30788
  "fetchLatestEnvironmentVariables",
@@ -29129,7 +30808,7 @@ function resolveAvdName(targetInput) {
29129
30808
  const { deviceModel, androidVersion } = raw.meta;
29130
30809
  return makeAvdName(deviceModel, androidVersion);
29131
30810
  }
29132
- const parsed = parseExecutionTarget3(raw);
30811
+ const parsed = parseExecutionTarget(raw);
29133
30812
  if (parsed.platform !== "android") {
29134
30813
  throw new Error(`runAndroidFlow: expected an Android target, got platform "${parsed.platform}"`);
29135
30814
  }
@@ -29550,15 +31229,15 @@ async function defaultRunWebFlowDeps(envDir = process.cwd(), signals) {
29550
31229
 
29551
31230
  // src/domains/runtimeEnv/prepareRunDir.ts
29552
31231
  import { createHash as createHash4 } from "node:crypto";
29553
- import { join as join39, resolve as resolve9 } from "node:path";
31232
+ import { join as join41, resolve as resolve9 } from "node:path";
29554
31233
 
29555
31234
  // src/domains/runtimeEnv/execSubpathImports.ts
29556
- import { join as join32 } from "node:path";
31235
+ import { join as join33 } from "node:path";
29557
31236
  var flowSubpathImports = { "#playwright": "playwright" };
29558
31237
  var packageJsonSchema = looseObject({ imports: record(string2(), unknown()).catch({}) }).catch({ imports: {} });
29559
31238
  async function writeExecSubpathImports(args) {
29560
31239
  const { execDir, fs } = args;
29561
- const pkgPath = join32(execDir, "package.json");
31240
+ const pkgPath = join33(execDir, "package.json");
29562
31241
  const pkg = packageJsonSchema.parse(await readPackageJson(pkgPath, fs));
29563
31242
  const merged = { ...pkg, imports: { ...pkg.imports, ...flowSubpathImports } };
29564
31243
  await fs.writeFile(pkgPath, JSON.stringify(merged, undefined, 2));
@@ -29580,10 +31259,10 @@ async function readPackageJson(pkgPath, fs) {
29580
31259
  }
29581
31260
 
29582
31261
  // src/domains/runtimeEnv/innerHop.ts
29583
- import { join as join34 } from "node:path";
31262
+ import { join as join35 } from "node:path";
29584
31263
 
29585
31264
  // src/domains/runtimeEnv/linkPinnedPackages.ts
29586
- import { dirname as dirname9, join as join33 } from "node:path";
31265
+ import { dirname as dirname9, join as join34 } from "node:path";
29587
31266
 
29588
31267
  // src/domains/runtimeEnv/symlinkDir.ts
29589
31268
  import { symlink } from "node:fs/promises";
@@ -29595,16 +31274,16 @@ async function createDirSymlink(source, target) {
29595
31274
  // src/domains/runtimeEnv/linkPinnedPackages.ts
29596
31275
  async function linkPinnedPackages(args) {
29597
31276
  const { depsRoot, nodeModulesDir, fs } = args;
29598
- const managedModules = join33(depsRoot, "node_modules");
31277
+ const managedModules = join34(depsRoot, "node_modules");
29599
31278
  await fs.mkdir(nodeModulesDir, { recursive: true });
29600
31279
  for (const { name } of pinnedPackages) {
29601
31280
  const segments = name.split("/");
29602
- const target = join33(nodeModulesDir, ...segments);
31281
+ const target = join34(nodeModulesDir, ...segments);
29603
31282
  if (segments.length > 1) {
29604
31283
  await fs.mkdir(dirname9(target), { recursive: true });
29605
31284
  }
29606
31285
  await fs.rm(target, { recursive: true, force: true });
29607
- await createDirSymlink(join33(managedModules, ...segments), target);
31286
+ await createDirSymlink(join34(managedModules, ...segments), target);
29608
31287
  }
29609
31288
  }
29610
31289
 
@@ -29613,18 +31292,18 @@ async function populateInnerHop(args) {
29613
31292
  const { depsRoot, execDir, fs } = args;
29614
31293
  await linkPinnedPackages({
29615
31294
  depsRoot,
29616
- nodeModulesDir: join34(execDir, "node_modules"),
31295
+ nodeModulesDir: join35(execDir, "node_modules"),
29617
31296
  fs
29618
31297
  });
29619
31298
  }
29620
31299
 
29621
31300
  // src/domains/runtimeEnv/outerHop.ts
29622
31301
  import { lstat as lstat2 } from "node:fs/promises";
29623
- import { dirname as dirname11, join as join36 } from "node:path";
31302
+ import { dirname as dirname11, join as join37 } from "node:path";
29624
31303
 
29625
31304
  // src/domains/runtimeEnv/carryOverPackages.ts
29626
31305
  import { lstat } from "node:fs/promises";
29627
- import { dirname as dirname10, join as join35 } from "node:path";
31306
+ import { dirname as dirname10, join as join36 } from "node:path";
29628
31307
 
29629
31308
  // src/domains/runtimeEnv/carryOverPlan.ts
29630
31309
  var executorPackageNames = new Set(pinnedPackages.map((p) => p.name));
@@ -29646,12 +31325,12 @@ async function carryOverPackages(args) {
29646
31325
  const linked = [];
29647
31326
  for (const name of names) {
29648
31327
  const segments = name.split("/");
29649
- const target = join35(nodeModulesDir, ...segments);
31328
+ const target = join36(nodeModulesDir, ...segments);
29650
31329
  if (await pathOccupied(target))
29651
31330
  continue;
29652
31331
  if (segments.length > 1)
29653
31332
  await fs.mkdir(dirname10(target), { recursive: true });
29654
- await createDirSymlink(join35(projectModulesDir, ...segments), target);
31333
+ await createDirSymlink(join36(projectModulesDir, ...segments), target);
29655
31334
  linked.push(name);
29656
31335
  }
29657
31336
  return linked;
@@ -29674,18 +31353,18 @@ async function listPackageNames(dir, fs) {
29674
31353
  const names = [];
29675
31354
  for (const entry of entries) {
29676
31355
  if (!entry.startsWith("@")) {
29677
- if (fs.existsSync(join35(dir, entry, "package.json")))
31356
+ if (fs.existsSync(join36(dir, entry, "package.json")))
29678
31357
  names.push(entry);
29679
31358
  continue;
29680
31359
  }
29681
31360
  let scoped;
29682
31361
  try {
29683
- scoped = await fs.readdir(join35(dir, entry));
31362
+ scoped = await fs.readdir(join36(dir, entry));
29684
31363
  } catch {
29685
31364
  continue;
29686
31365
  }
29687
31366
  for (const inner of scoped) {
29688
- if (fs.existsSync(join35(dir, entry, inner, "package.json"))) {
31367
+ if (fs.existsSync(join36(dir, entry, inner, "package.json"))) {
29689
31368
  names.push(`${entry}/${inner}`);
29690
31369
  }
29691
31370
  }
@@ -29703,7 +31382,7 @@ async function populateOuterHop(args) {
29703
31382
  const depNames = Object.keys(installableDeps);
29704
31383
  const { satisfying, rejected } = await findSatisfyingNodeModulesDir(projectDir, depNames, fs);
29705
31384
  if (satisfying !== undefined) {
29706
- await createDirSymlink(satisfying, join36(runDir, "node_modules"));
31385
+ await createDirSymlink(satisfying, join37(runDir, "node_modules"));
29707
31386
  return { mode: "symlink", nodeModulesDir: satisfying };
29708
31387
  }
29709
31388
  if (depNames.length === 0)
@@ -29712,12 +31391,12 @@ async function populateOuterHop(args) {
29712
31391
  await installOuterDeps(runDir, installableDeps, fs, args.install ?? spawnNpmInstall);
29713
31392
  await linkPinnedPackages({
29714
31393
  depsRoot: args.depsRoot,
29715
- nodeModulesDir: join36(runDir, "node_modules"),
31394
+ nodeModulesDir: join37(runDir, "node_modules"),
29716
31395
  fs
29717
31396
  });
29718
31397
  const carriedOver = await carryOverPackages({
29719
- projectModulesDir: join36(projectDir, "node_modules"),
29720
- nodeModulesDir: join36(runDir, "node_modules"),
31398
+ projectModulesDir: join37(projectDir, "node_modules"),
31399
+ nodeModulesDir: join37(runDir, "node_modules"),
29721
31400
  fs
29722
31401
  });
29723
31402
  return { mode: "install", depCount: depNames.length, rejected, carriedOver };
@@ -29726,10 +31405,10 @@ async function findSatisfyingNodeModulesDir(startDir, depNames, fs) {
29726
31405
  const rejected = [];
29727
31406
  let dir = startDir;
29728
31407
  while (true) {
29729
- const candidate = join36(dir, "node_modules");
31408
+ const candidate = join37(dir, "node_modules");
29730
31409
  const stats = await lstatSafe(candidate);
29731
31410
  if (stats !== undefined && stats.isDirectory() && !stats.isSymbolicLink()) {
29732
- const missing = depNames.filter((name) => !fs.existsSync(join36(candidate, name, "package.json")));
31411
+ const missing = depNames.filter((name) => !fs.existsSync(join37(candidate, name, "package.json")));
29733
31412
  if (missing.length === 0)
29734
31413
  return { satisfying: candidate, rejected };
29735
31414
  rejected.push({ dir: candidate, missing });
@@ -29750,7 +31429,7 @@ async function lstatSafe(path) {
29750
31429
  async function readInstallableDeps(projectDir, fs) {
29751
31430
  let content;
29752
31431
  try {
29753
- content = await fs.readFile(join36(projectDir, "package.json"));
31432
+ content = await fs.readFile(join37(projectDir, "package.json"));
29754
31433
  } catch {
29755
31434
  return {};
29756
31435
  }
@@ -29768,8 +31447,8 @@ async function readInstallableDeps(projectDir, fs) {
29768
31447
  return Object.fromEntries(Object.entries(rawDeps).filter(([name]) => !executorPackageNames2.has(name)).filter((entry) => typeof entry[1] === "string"));
29769
31448
  }
29770
31449
  async function installOuterDeps(runDir, deps, fs, install) {
29771
- await fs.writeFile(join36(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
29772
- await fs.writeFile(join36(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
31450
+ await fs.writeFile(join37(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
31451
+ await fs.writeFile(join37(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
29773
31452
  `);
29774
31453
  const result = await install(runDir);
29775
31454
  if (result.exitCode !== 0) {
@@ -29777,16 +31456,266 @@ async function installOuterDeps(runDir, deps, fs, install) {
29777
31456
  }
29778
31457
  }
29779
31458
 
31459
+ // src/domains/runtimeEnv/rewriteStagedAliases.ts
31460
+ import { join as join38 } from "node:path";
31461
+
31462
+ // src/core/aliasImports/rewriteAliasImports.ts
31463
+ import { posix as posix3 } from "node:path";
31464
+
31465
+ // src/core/aliasImports/collectAliasSpecifiers.ts
31466
+ function isRelativeSpecifier(specifier) {
31467
+ return specifier.startsWith("./") || specifier.startsWith("../");
31468
+ }
31469
+ function collectAliasSpecifiers(options) {
31470
+ const { sourceFile, typescript: compiler } = options;
31471
+ const specifiers = [];
31472
+ const addCandidate = (literal) => {
31473
+ if (isRelativeSpecifier(literal.text))
31474
+ return;
31475
+ specifiers.push({
31476
+ end: literal.getEnd(),
31477
+ specifier: literal.text,
31478
+ start: literal.getStart(sourceFile)
31479
+ });
31480
+ };
31481
+ for (const statement of sourceFile.statements) {
31482
+ if (compiler.isImportDeclaration(statement)) {
31483
+ const phase = statement.importClause?.phaseModifier;
31484
+ if (phase === compiler.SyntaxKind.TypeKeyword)
31485
+ continue;
31486
+ if (compiler.isStringLiteral(statement.moduleSpecifier)) {
31487
+ addCandidate(statement.moduleSpecifier);
31488
+ }
31489
+ continue;
31490
+ }
31491
+ if (compiler.isExportDeclaration(statement) && !statement.isTypeOnly) {
31492
+ const { moduleSpecifier } = statement;
31493
+ if (moduleSpecifier !== undefined && compiler.isStringLiteral(moduleSpecifier)) {
31494
+ addCandidate(moduleSpecifier);
31495
+ }
31496
+ }
31497
+ }
31498
+ const visit = (node) => {
31499
+ if (compiler.isCallExpression(node) && node.expression.kind === compiler.SyntaxKind.ImportKeyword) {
31500
+ const [firstArgument] = node.arguments;
31501
+ if (firstArgument !== undefined && (compiler.isStringLiteral(firstArgument) || compiler.isNoSubstitutionTemplateLiteral(firstArgument))) {
31502
+ addCandidate(firstArgument);
31503
+ }
31504
+ return;
31505
+ }
31506
+ compiler.forEachChild(node, visit);
31507
+ };
31508
+ visit(sourceFile);
31509
+ return specifiers;
31510
+ }
31511
+
31512
+ // src/core/aliasImports/filePathVariants.ts
31513
+ var sourceExtensionPattern = /\.(js|ts)$/;
31514
+ var sourceExtensions2 = [".ts", ".js"];
31515
+ function filePathVariants(importPath) {
31516
+ const hasSourceExtension = sourceExtensionPattern.test(importPath);
31517
+ const base = importPath.replace(sourceExtensionPattern, "");
31518
+ const siblings = sourceExtensions2.map((extension) => base + extension);
31519
+ const indexes = hasSourceExtension ? [] : sourceExtensions2.map((extension) => `${base}/index${extension}`);
31520
+ return [...new Set([importPath, ...siblings, ...indexes])];
31521
+ }
31522
+
31523
+ // src/core/aliasImports/tsconfigPaths.ts
31524
+ function isRecord2(value) {
31525
+ return typeof value === "object" && value !== null;
31526
+ }
31527
+ function isTsconfigPaths(value) {
31528
+ return isRecord2(value) && Object.values(value).every((targets) => Array.isArray(targets) && targets.every((target) => typeof target === "string"));
31529
+ }
31530
+ function parseTsconfigContent(tsconfigContent) {
31531
+ let parsed;
31532
+ try {
31533
+ parsed = JSON.parse(tsconfigContent);
31534
+ } catch {
31535
+ return { type: "unparseable" };
31536
+ }
31537
+ if (!isRecord2(parsed))
31538
+ return { paths: undefined, type: "parsed" };
31539
+ const compilerOptions = parsed["compilerOptions"];
31540
+ if (!isRecord2(compilerOptions))
31541
+ return { paths: undefined, type: "parsed" };
31542
+ const paths = compilerOptions["paths"];
31543
+ return {
31544
+ paths: isTsconfigPaths(paths) ? paths : undefined,
31545
+ type: "parsed"
31546
+ };
31547
+ }
31548
+ function parseTsconfigPaths(tsconfigContent) {
31549
+ const parsed = parseTsconfigContent(tsconfigContent);
31550
+ return parsed.type === "parsed" ? parsed.paths : undefined;
31551
+ }
31552
+ function parseWildcardPattern(pattern) {
31553
+ const wildcard = pattern.indexOf("*");
31554
+ if (wildcard === -1 || wildcard !== pattern.lastIndexOf("*"))
31555
+ return;
31556
+ return {
31557
+ prefix: pattern.slice(0, wildcard),
31558
+ suffix: pattern.slice(wildcard + 1)
31559
+ };
31560
+ }
31561
+ function matchesWildcard(importPath, { prefix, suffix }) {
31562
+ return importPath.length >= prefix.length + suffix.length && importPath.startsWith(prefix) && importPath.endsWith(suffix);
31563
+ }
31564
+ function longestPrefixMatch(importPath, paths) {
31565
+ return Object.entries(paths).flatMap(([pattern, targets]) => {
31566
+ const parsed = parseWildcardPattern(pattern);
31567
+ if (parsed === undefined || !matchesWildcard(importPath, parsed)) {
31568
+ return [];
31569
+ }
31570
+ return [{ pattern: parsed, targets }];
31571
+ }).reduce((best, candidate) => best === undefined || candidate.pattern.prefix.length > best.pattern.prefix.length ? candidate : best, undefined);
31572
+ }
31573
+ function resolvePathAlias(importPath, paths) {
31574
+ if (paths === undefined)
31575
+ return;
31576
+ const [exactTarget] = paths[importPath] ?? [];
31577
+ if (exactTarget !== undefined)
31578
+ return exactTarget;
31579
+ const match = longestPrefixMatch(importPath, paths);
31580
+ if (match === undefined)
31581
+ return;
31582
+ const [target] = match.targets;
31583
+ if (target === undefined)
31584
+ return;
31585
+ const substituted = importPath.slice(match.pattern.prefix.length, importPath.length - match.pattern.suffix.length);
31586
+ return target.replace("*", substituted);
31587
+ }
31588
+
31589
+ // src/core/aliasImports/rewriteAliasImports.ts
31590
+ function findTargetFilePath(options) {
31591
+ return filePathVariants(posix3.normalize(options.aliasTarget)).find((candidate) => options.projectFilePaths.has(candidate));
31592
+ }
31593
+ function toRelativeSpecifier(options) {
31594
+ const relativePath = posix3.relative(posix3.dirname(options.importingFilePath), options.targetFilePath);
31595
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
31596
+ }
31597
+ function applyEdit(code, edit) {
31598
+ const quote = code.slice(edit.start, edit.start + 1);
31599
+ return code.slice(0, edit.start) + quote + edit.relativeSpecifier + quote + code.slice(edit.end);
31600
+ }
31601
+ function rewriteAliasImports(options) {
31602
+ const { code, importingFilePath, projectFilePaths, tsconfigPaths } = options;
31603
+ const compiler = options.typescript;
31604
+ if (tsconfigPaths === undefined)
31605
+ return code;
31606
+ const sourceFile = compiler.createSourceFile(importingFilePath, code, compiler.ScriptTarget.Latest, true);
31607
+ const edits = collectAliasSpecifiers({
31608
+ sourceFile,
31609
+ typescript: compiler
31610
+ }).flatMap((candidate) => {
31611
+ const aliasTarget = resolvePathAlias(candidate.specifier, tsconfigPaths);
31612
+ if (aliasTarget === undefined)
31613
+ return [];
31614
+ const targetFilePath = findTargetFilePath({
31615
+ aliasTarget,
31616
+ projectFilePaths
31617
+ });
31618
+ if (targetFilePath === undefined)
31619
+ return [];
31620
+ return [
31621
+ {
31622
+ end: candidate.end,
31623
+ relativeSpecifier: toRelativeSpecifier({
31624
+ importingFilePath,
31625
+ targetFilePath
31626
+ }),
31627
+ start: candidate.start
31628
+ }
31629
+ ];
31630
+ });
31631
+ return edits.sort((left, right) => right.start - left.start).reduce(applyEdit, code);
31632
+ }
31633
+
31634
+ // src/domains/runtimeEnv/rewriteStagedAliases.ts
31635
+ var skippedDirs = new Set(["node_modules", ".git", ".qawolf"]);
31636
+ var sourceExtensions3 = [".ts", ".js"];
31637
+ async function rewriteStagedAliases(args) {
31638
+ const tsconfig = await readStagedTsconfig(args);
31639
+ if (tsconfig.type === "unparseable") {
31640
+ args.onTsconfigUnparsed?.();
31641
+ return [];
31642
+ }
31643
+ if (tsconfig.type === "absent" || tsconfig.paths === undefined)
31644
+ return [];
31645
+ const tsconfigPaths = tsconfig.paths;
31646
+ const stagedPaths = await listStagedSourceFiles({
31647
+ dir: args.execDir,
31648
+ fs: args.fs,
31649
+ prefix: ""
31650
+ });
31651
+ const projectFilePaths = new Set(stagedPaths);
31652
+ const aliasPrefixes = Object.keys(tsconfigPaths).map((pattern) => pattern.split("*")[0] ?? pattern);
31653
+ const mentionsAnAlias = (code) => aliasPrefixes.some((prefix) => code.includes(prefix));
31654
+ const { default: typescript } = await import("typescript");
31655
+ const rewriteOne = async (stagedPath) => {
31656
+ const absolutePath = join38(args.execDir, stagedPath);
31657
+ const code = await args.fs.readFile(absolutePath);
31658
+ if (!mentionsAnAlias(code))
31659
+ return;
31660
+ const rewritten = rewriteAliasImports({
31661
+ code,
31662
+ importingFilePath: stagedPath,
31663
+ projectFilePaths,
31664
+ tsconfigPaths,
31665
+ typescript
31666
+ });
31667
+ if (rewritten === code)
31668
+ return;
31669
+ await args.fs.writeFile(absolutePath, rewritten);
31670
+ return stagedPath;
31671
+ };
31672
+ const rewrittenPaths = [];
31673
+ for await (const stagedPath of batchMap(stagedPaths, rewriteOne, flowBatchSize)) {
31674
+ if (stagedPath !== undefined)
31675
+ rewrittenPaths.push(stagedPath);
31676
+ }
31677
+ return rewrittenPaths;
31678
+ }
31679
+ async function readStagedTsconfig(options) {
31680
+ let content;
31681
+ try {
31682
+ content = await options.fs.readFile(join38(options.execDir, "tsconfig.json"));
31683
+ } catch (err) {
31684
+ if (isNoEntError(err))
31685
+ return { type: "absent" };
31686
+ throw err;
31687
+ }
31688
+ return parseTsconfigContent(content);
31689
+ }
31690
+ async function listStagedSourceFiles(options) {
31691
+ const entries = await options.fs.readdirWithTypes(options.dir);
31692
+ const nested = await Promise.all(entries.map(async (entry) => {
31693
+ if (skippedDirs.has(entry.name))
31694
+ return [];
31695
+ const stagedPath = options.prefix === "" ? entry.name : `${options.prefix}/${entry.name}`;
31696
+ if (entry.isDirectory()) {
31697
+ return listStagedSourceFiles({
31698
+ dir: join38(options.dir, entry.name),
31699
+ fs: options.fs,
31700
+ prefix: stagedPath
31701
+ });
31702
+ }
31703
+ const isSource = sourceExtensions3.some((extension) => entry.name.endsWith(extension));
31704
+ return entry.isFile() && isSource ? [stagedPath] : [];
31705
+ }));
31706
+ return nested.flat();
31707
+ }
31708
+
29780
31709
  // src/domains/runtimeEnv/stageFlowFiles.ts
29781
31710
  import { createHash as createHash3 } from "node:crypto";
29782
- import { basename as basename6, dirname as dirname12, join as join38, sep as sep4 } from "node:path";
31711
+ import { basename as basename6, dirname as dirname12, join as join40, sep as sep4 } from "node:path";
29783
31712
 
29784
31713
  // src/shell/copyDir.ts
29785
31714
  import { cp, readdir as readdir2 } from "node:fs/promises";
29786
- import { basename as basename5, join as join37 } from "node:path";
31715
+ import { basename as basename5, join as join39 } from "node:path";
29787
31716
  async function copyDirExcluding(source, destination, excludedNames) {
29788
31717
  const entries = await readdir2(source, { withFileTypes: true });
29789
- await Promise.all(entries.filter((entry) => !excludedNames.has(entry.name)).map((entry) => cp(join37(source, entry.name), join37(destination, entry.name), {
31718
+ await Promise.all(entries.filter((entry) => !excludedNames.has(entry.name)).map((entry) => cp(join39(source, entry.name), join39(destination, entry.name), {
29790
31719
  recursive: true,
29791
31720
  filter: (path) => !excludedNames.has(basename5(path))
29792
31721
  })));
@@ -29803,14 +31732,14 @@ async function stageFlowFiles(args) {
29803
31732
  if (files.length > 1) {
29804
31733
  return Promise.all(files.map((f) => stageOneFile(f, execDir, fs)));
29805
31734
  }
29806
- await Promise.all(files.map((f) => fs.copyFile(f, join38(execDir, basename6(f)))));
29807
- return files.map((f) => join38(execDir, basename6(f)));
31735
+ await Promise.all(files.map((f) => fs.copyFile(f, join40(execDir, basename6(f)))));
31736
+ return files.map((f) => join40(execDir, basename6(f)));
29808
31737
  }
29809
31738
  async function stageOneFile(file, execDir, fs) {
29810
31739
  const dirHash = createHash3("sha256").update(dirname12(file)).digest("hex").slice(0, 8);
29811
- const subDir = join38(execDir, dirHash);
31740
+ const subDir = join40(execDir, dirHash);
29812
31741
  await fs.mkdir(subDir, { recursive: true });
29813
- const dest = join38(subDir, basename6(file));
31742
+ const dest = join40(subDir, basename6(file));
29814
31743
  await fs.copyFile(file, dest);
29815
31744
  return dest;
29816
31745
  }
@@ -29818,7 +31747,7 @@ function remapPath(file, projectDir, execDir) {
29818
31747
  if (file === projectDir)
29819
31748
  return execDir;
29820
31749
  if (file.startsWith(projectDir + sep4)) {
29821
- return join38(execDir, file.slice(projectDir.length + 1));
31750
+ return join40(execDir, file.slice(projectDir.length + 1));
29822
31751
  }
29823
31752
  return;
29824
31753
  }
@@ -29828,15 +31757,21 @@ async function prepareRunDir(args) {
29828
31757
  const { files, projectDir, depsRoot, runRoot } = args;
29829
31758
  const fs = args.fs ?? makeDefaultFs();
29830
31759
  const runId = buildRunId(projectDir, files);
29831
- const runDir = join39(runRoot, runId);
31760
+ const runDir = join41(runRoot, runId);
29832
31761
  await fs.rm(runDir, { recursive: true, force: true });
29833
31762
  await fs.mkdir(runDir, { recursive: true });
29834
- const execDir = join39(runDir, "exec");
31763
+ const execDir = join41(runDir, "exec");
29835
31764
  await fs.mkdir(execDir, { recursive: true });
29836
31765
  await populateInnerHop({ depsRoot, execDir, fs });
29837
31766
  const stagedFiles = await stageFlowFiles({ files, projectDir, execDir, fs });
29838
31767
  if (projectDir !== undefined) {
29839
31768
  await writeExecSubpathImports({ execDir, fs });
31769
+ const onTsconfigUnparsed = args.onTsconfigUnparsed;
31770
+ await rewriteStagedAliases({
31771
+ execDir,
31772
+ fs,
31773
+ ...onTsconfigUnparsed !== undefined ? { onTsconfigUnparsed: () => onTsconfigUnparsed(projectDir) } : {}
31774
+ });
29840
31775
  }
29841
31776
  const outerHop = await populateOuterHop({
29842
31777
  projectDir,
@@ -29862,17 +31797,17 @@ function buildRunId(projectDir, files) {
29862
31797
  }
29863
31798
 
29864
31799
  // src/shell/testkit.ts
29865
- import { join as join40 } from "node:path";
31800
+ import { join as join42 } from "node:path";
29866
31801
  function notAvailableLocally(name) {
29867
31802
  throw new Error(runnerMessages.notAvailableLocally(name));
29868
31803
  }
29869
31804
  async function loadSdkDeps(cwd) {
29870
- const testkitDir = join40(cwd, "node_modules", "@qawolf", "testkit");
31805
+ const testkitDir = join42(cwd, "node_modules", "@qawolf", "testkit");
29871
31806
  try {
29872
31807
  const clientMod = await importFromPath(resolveFromEnvDir(cwd, "@qawolf/testkit/client"));
29873
31808
  if (typeof clientMod.createTestkitClient !== "function")
29874
31809
  throw new Error("createTestkitClient is not a function");
29875
- const scopeMod = await importFromPath(join40(testkitDir, "dist", "clientScope.js"));
31810
+ const scopeMod = await importFromPath(join42(testkitDir, "dist", "clientScope.js"));
29876
31811
  if (typeof scopeMod.configureTestkitClient !== "function")
29877
31812
  throw new Error("configureTestkitClient is not a function");
29878
31813
  return {
@@ -29930,9 +31865,9 @@ async function registerLazyEmailsClient(getInbox, cwd, deps) {
29930
31865
  }
29931
31866
 
29932
31867
  // src/commands/flows/envVarDeps.ts
29933
- import { join as join41 } from "node:path";
31868
+ import { join as join43 } from "node:path";
29934
31869
  function envFilePath(envDir) {
29935
- return join41(envDir, ".env");
31870
+ return join43(envDir, ".env");
29936
31871
  }
29937
31872
  async function readEnv(envDir, fs) {
29938
31873
  try {
@@ -30056,7 +31991,7 @@ async function selectRunByTag(args) {
30056
31991
  }
30057
31992
 
30058
31993
  // src/commands/flows/runStagedFlows.ts
30059
- import { join as join45 } from "node:path";
31994
+ import { join as join47 } from "node:path";
30060
31995
 
30061
31996
  // src/shell/appium/createAppiumServer.ts
30062
31997
  import { existsSync as existsSync2 } from "node:fs";
@@ -30190,7 +32125,7 @@ var defaultAdb = async (args) => {
30190
32125
 
30191
32126
  // src/shell/appium/createAndroidEmulator.ts
30192
32127
  var defaultBootTimeoutMs = 120000;
30193
- var pollIntervalMs = 2000;
32128
+ var pollIntervalMs2 = 2000;
30194
32129
  var defaultSpawnEmulator = (bin, args) => {
30195
32130
  const child = spawn3(bin, args, { stdio: "ignore" });
30196
32131
  child.unref();
@@ -30208,7 +32143,7 @@ async function bootSequence(adb, serial, cancelled) {
30208
32143
  ]).catch(() => ({ stdout: "" }));
30209
32144
  if (stdout.trim() === "1")
30210
32145
  return;
30211
- await new Promise((r) => setTimeout(r, pollIntervalMs));
32146
+ await new Promise((r) => setTimeout(r, pollIntervalMs2));
30212
32147
  }
30213
32148
  }
30214
32149
  function waitForBoot(adb, serial, timeoutMs) {
@@ -31362,14 +33297,14 @@ async function runWebFlow({
31362
33297
 
31363
33298
  // src/shell/embeddedWorkerCli.ts
31364
33299
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
31365
- import { basename as basename7, join as join42 } from "node:path";
33300
+ import { basename as basename7, join as join44 } from "node:path";
31366
33301
  var embeddedCliPathEnv = "QAWOLF_EMBEDDED_CLI_PATH";
31367
33302
  function extractEmbeddedWorkerCli() {
31368
33303
  const assetPath = process.env[embeddedCliPathEnv];
31369
33304
  if (assetPath === undefined || assetPath === "")
31370
33305
  return;
31371
- const dir = join42(getDataDir(), "worker");
31372
- const dest = join42(dir, basename7(assetPath));
33306
+ const dir = join44(getDataDir(), "worker");
33307
+ const dest = join44(dir, basename7(assetPath));
31373
33308
  if (existsSync3(dest))
31374
33309
  return dest;
31375
33310
  mkdirSync3(dir, { recursive: true });
@@ -31527,33 +33462,33 @@ function makePooledDispatch(resolvedDir) {
31527
33462
  import { dirname as dirname13 } from "node:path";
31528
33463
 
31529
33464
  // src/shell/reporter/createConsoleReporter.ts
31530
- import { styleText as styleText3 } from "node:util";
33465
+ import { styleText as styleText5 } from "node:util";
31531
33466
  function fmtDuration(ms) {
31532
33467
  return `(${(ms / 1000).toFixed(2)}s)`;
31533
33468
  }
31534
33469
  function fmtStampLine(manifest) {
31535
33470
  const shortHash = manifest.contentHash.slice(0, 8);
31536
- return ` ${styleText3("dim", `env=${manifest.envId} hash=${shortHash}`)}
33471
+ return ` ${styleText5("dim", `env=${manifest.envId} hash=${shortHash}`)}
31537
33472
  `;
31538
33473
  }
31539
33474
  function createConsoleReporter(deps) {
31540
33475
  return {
31541
33476
  onFlowStart({ name, path }) {
31542
- deps.stdout.write(`${styleText3("magenta", "•")} ${styleText3(["bold", "magenta"], name)} ${styleText3("dim", path)}
33477
+ deps.stdout.write(`${styleText5("magenta", "•")} ${styleText5(["bold", "magenta"], name)} ${styleText5("dim", path)}
31543
33478
  `);
31544
33479
  },
31545
33480
  onTestResult({ label, status, durationMs }) {
31546
- const icon = status === "pass" ? styleText3("green", "✓") : styleText3("red", "✗");
31547
- deps.stdout.write(` - ${label} ${icon} ${styleText3("dim", fmtDuration(durationMs))}
33481
+ const icon = status === "pass" ? styleText5("green", "✓") : styleText5("red", "✗");
33482
+ deps.stdout.write(` - ${label} ${icon} ${styleText5("dim", fmtDuration(durationMs))}
31548
33483
  `);
31549
33484
  },
31550
33485
  onScreenshot({ path }) {
31551
- deps.stdout.write(` ${styleText3("dim", runnerMessages.screenshot(path))}
33486
+ deps.stdout.write(` ${styleText5("dim", runnerMessages.screenshot(path))}
31552
33487
  `);
31553
33488
  },
31554
33489
  onFlowPass({ tests, durationMs, manifest }) {
31555
33490
  const counts = tests.total > 0 ? `${tests.passed}/${tests.total} tests ` : "";
31556
- deps.stdout.write(` ${styleText3("green", "✓")} ${counts}passed ${styleText3("dim", fmtDuration(durationMs))}
33491
+ deps.stdout.write(` ${styleText5("green", "✓")} ${counts}passed ${styleText5("dim", fmtDuration(durationMs))}
31557
33492
  `);
31558
33493
  if (manifest)
31559
33494
  deps.stdout.write(fmtStampLine(manifest));
@@ -31564,8 +33499,8 @@ function createConsoleReporter(deps) {
31564
33499
  `);
31565
33500
  const indent = " ";
31566
33501
  const formatted = [
31567
- styleText3("red", `${indent}${firstLine}`),
31568
- ...restLines.map((line) => styleText3("dim", `${indent}${line}`))
33502
+ styleText5("red", `${indent}${firstLine}`),
33503
+ ...restLines.map((line) => styleText5("dim", `${indent}${line}`))
31569
33504
  ].join(`
31570
33505
  `);
31571
33506
  deps.stderr.write(`${formatted}
@@ -31573,12 +33508,12 @@ function createConsoleReporter(deps) {
31573
33508
  `);
31574
33509
  const hint = flowFailureHint(errStr, deps.projectDir);
31575
33510
  if (hint !== undefined) {
31576
- deps.stderr.write(`${styleText3("yellow", `${indent}${hint}`)}
33511
+ deps.stderr.write(`${styleText5("yellow", `${indent}${hint}`)}
31577
33512
 
31578
33513
  `);
31579
33514
  }
31580
33515
  const counts = tests.total > 0 ? `${tests.passed}/${tests.total} tests ` : "";
31581
- deps.stdout.write(` ${styleText3("red", "✗")} ${counts}passed ${styleText3("dim", fmtDuration(durationMs))}
33516
+ deps.stdout.write(` ${styleText5("red", "✗")} ${counts}passed ${styleText5("dim", fmtDuration(durationMs))}
31582
33517
  `);
31583
33518
  if (manifest)
31584
33519
  deps.stdout.write(fmtStampLine(manifest));
@@ -31593,21 +33528,21 @@ ${runnerMessages.retrying(attempt, maxAttempts)}
31593
33528
  if (total <= 1)
31594
33529
  return;
31595
33530
  const allPassed = summary.flowsFailed === 0;
31596
- const icon = styleText3(allPassed ? "green" : "red", allPassed ? "✓" : "✗");
33531
+ const icon = styleText5(allPassed ? "green" : "red", allPassed ? "✓" : "✗");
31597
33532
  const skippedStr = summary.flowsSkipped > 0 ? ` (${summary.flowsSkipped} skipped)` : "";
31598
33533
  const flowCount = `${summary.flowsPassed}/${total} flows passed${skippedStr}`;
31599
33534
  const testCount = summary.testsTotal > 0 ? ` · ${summary.testsPassed}/${summary.testsTotal} tests passed` : "";
31600
33535
  const width = deps.columns ?? process.stdout.columns ?? 72;
31601
33536
  deps.stdout.write(`
31602
- ${styleText3("dim", "─".repeat(width))}
33537
+ ${styleText5("dim", "─".repeat(width))}
31603
33538
  `);
31604
- deps.stdout.write(` ${icon} ${flowCount}${testCount} ${styleText3("dim", fmtDuration(summary.durationMs))}
33539
+ deps.stdout.write(` ${icon} ${flowCount}${testCount} ${styleText5("dim", fmtDuration(summary.durationMs))}
31605
33540
  `);
31606
33541
  }
31607
33542
  };
31608
33543
  }
31609
33544
  // src/shell/reporter/createJUnitReporter.ts
31610
- import { join as join43 } from "node:path";
33545
+ import { join as join45 } from "node:path";
31611
33546
 
31612
33547
  // src/shell/reporter/junitXml.ts
31613
33548
  function escapeXml(str) {
@@ -31646,7 +33581,7 @@ ${suites}`;
31646
33581
 
31647
33582
  // src/shell/reporter/createJUnitReporter.ts
31648
33583
  function resolveJUnitOutputPath(junit, outputDir) {
31649
- return typeof junit === "string" && junit.trim() !== "" ? junit : join43(outputDir, "junit-report.xml");
33584
+ return typeof junit === "string" && junit.trim() !== "" ? junit : join45(outputDir, "junit-report.xml");
31650
33585
  }
31651
33586
  function createJUnitReporter(deps) {
31652
33587
  const flows = [];
@@ -31737,11 +33672,11 @@ function buildFlowsRunDeps(args) {
31737
33672
 
31738
33673
  // src/commands/flows/loadEnvFile.ts
31739
33674
  import { readFile } from "node:fs/promises";
31740
- import { join as join44 } from "node:path";
33675
+ import { join as join46 } from "node:path";
31741
33676
  async function loadEnvFile(envDir) {
31742
33677
  let content;
31743
33678
  try {
31744
- content = await readFile(join44(envDir, ".env"), "utf8");
33679
+ content = await readFile(join46(envDir, ".env"), "utf8");
31745
33680
  } catch (err) {
31746
33681
  if (isNoEntError(err))
31747
33682
  return;
@@ -31779,14 +33714,15 @@ async function runStagedFlows(args) {
31779
33714
  projectDir,
31780
33715
  depsRoot: runtimeEnv.depsRoot,
31781
33716
  runRoot: runStagingRoot(),
31782
- onInstallStart: (depCount) => ctx.ui.info(runnerMessages.installingProjectDeps(depCount))
33717
+ onInstallStart: (depCount) => ctx.ui.info(runnerMessages.installingProjectDeps(depCount)),
33718
+ onTsconfigUnparsed: (dir) => ctx.ui.warn(runnerMessages.tsconfigUnparsedNotice(dir))
31783
33719
  });
31784
33720
  if (staged.outerHop.mode === "install") {
31785
33721
  const log = ctx.log("runner");
31786
33722
  for (const candidate of staged.outerHop.rejected) {
31787
33723
  log.debug(runnerMessages.outerHopCandidateRejected(candidate.dir, candidate.missing));
31788
33724
  }
31789
- const ownModules = projectDir === undefined ? undefined : join45(projectDir, "node_modules");
33725
+ const ownModules = projectDir === undefined ? undefined : join47(projectDir, "node_modules");
31790
33726
  const own = staged.outerHop.rejected.find((c) => c.dir === ownModules);
31791
33727
  if (own !== undefined) {
31792
33728
  ctx.ui.warn(runnerMessages.outerHopFallbackNotice(own.dir, own.missing));
@@ -31882,7 +33818,7 @@ async function chooseEnv(ctx, files, allEnvs) {
31882
33818
  }
31883
33819
 
31884
33820
  // src/commands/flows/hybridRunDefaults.ts
31885
- import { join as join46, resolve as resolve10 } from "node:path";
33821
+ import { join as join48, resolve as resolve10 } from "node:path";
31886
33822
 
31887
33823
  // src/core/selectFlowFiles.ts
31888
33824
  function selectFlowFiles(args) {
@@ -31987,7 +33923,7 @@ async function handleHybridFlowsRun(ctx, pattern, flags, options = {}) {
31987
33923
  if (validation !== "ok") {
31988
33924
  return { error: validation.error, exitCode: 2 };
31989
33925
  }
31990
- const envDir = resolve10(join46(".qawolf", flags.env));
33926
+ const envDir = resolve10(join48(".qawolf", flags.env));
31991
33927
  const patternArgs = buildPatternArgs(pattern);
31992
33928
  const globFlows = () => resolvedDeps.expandPatterns(patternArgs, envDir, ctx.log("flows"));
31993
33929
  let files = await globFlows();
@@ -32183,12 +34119,12 @@ function registerFlowsCommand(program, signals, deps = { withResolvedEnv }) {
32183
34119
  }
32184
34120
 
32185
34121
  // src/domains/init/init.ts
32186
- import { dirname as dirname14, join as join48, relative as relative9 } from "node:path";
34122
+ import { dirname as dirname14, join as join50, relative as relative9 } from "node:path";
32187
34123
 
32188
34124
  // src/domains/init/ensurePackageJson.ts
32189
- import { join as join47 } from "node:path";
34125
+ import { join as join49 } from "node:path";
32190
34126
  async function ensurePackageJson(ctx, yes, deps) {
32191
- const pkgPath = join47(deps.cwd, "package.json");
34127
+ const pkgPath = join49(deps.cwd, "package.json");
32192
34128
  if (!await deps.fs.pathExists(pkgPath)) {
32193
34129
  const confirmed = await ctx.ui.confirm(initMessages.createPackageJsonPrompt, { yes });
32194
34130
  if (!confirmed.ok || !confirmed.value) {
@@ -32214,7 +34150,7 @@ async function ensurePackageJson(ctx, yes, deps) {
32214
34150
  ctx.ui.warn(initMessages.packageJsonInvalidJson);
32215
34151
  return;
32216
34152
  }
32217
- if (!isRecord2(parsed)) {
34153
+ if (!isRecord3(parsed)) {
32218
34154
  ctx.ui.warn(initMessages.packageJsonNotAnObject);
32219
34155
  return;
32220
34156
  }
@@ -32265,14 +34201,14 @@ async function ensurePackageJson(ctx, yes, deps) {
32265
34201
  ctx.ui.warn(initMessages.typeChanged(label));
32266
34202
  }
32267
34203
  }
32268
- function isRecord2(value) {
34204
+ function isRecord3(value) {
32269
34205
  return typeof value === "object" && value !== null && !Array.isArray(value);
32270
34206
  }
32271
34207
  function readSection(ctx, pkg, key) {
32272
34208
  const value = pkg[key];
32273
34209
  if (value === undefined)
32274
34210
  return {};
32275
- if (isRecord2(value))
34211
+ if (isRecord3(value))
32276
34212
  return value;
32277
34213
  ctx.ui.warn(initMessages.packageJsonMalformedSection(key));
32278
34214
  return;
@@ -32314,9 +34250,9 @@ function makeDefaultInitDeps() {
32314
34250
  async function handleInit(ctx, opts, deps = makeDefaultInitDeps()) {
32315
34251
  ctx.ui.gap();
32316
34252
  ctx.ui.intro(initMessages.title);
32317
- const configPath = join48(deps.cwd, "qawolf.config.ts");
32318
- const flowPath = join48(deps.cwd, "src", "flows", "example.flow.ts");
32319
- const gitignorePath = join48(deps.cwd, ".qawolf", ".gitignore");
34253
+ const configPath = join50(deps.cwd, "qawolf.config.ts");
34254
+ const flowPath = join50(deps.cwd, "src", "flows", "example.flow.ts");
34255
+ const gitignorePath = join50(deps.cwd, ".qawolf", ".gitignore");
32320
34256
  await writeWithPrompt(ctx, configPath, qawolfConfigTs, opts.yes, deps);
32321
34257
  await writeWithPrompt(ctx, flowPath, exampleFlowTs, opts.yes, deps);
32322
34258
  await writeWithPrompt(ctx, gitignorePath, qawolfGitignore, opts.yes, deps);
@@ -32350,11 +34286,8 @@ Examples:
32350
34286
  });
32351
34287
  }
32352
34288
 
32353
- // src/domains/install/android/index.ts
32354
- import { parseExecutionTarget as parseExecutionTarget4 } from "@qawolf/flow-targets";
32355
-
32356
34289
  // src/domains/install/android/avd.ts
32357
- import { join as join49 } from "node:path";
34290
+ import { join as join51 } from "node:path";
32358
34291
  async function installAvds(ctx, specs, deps) {
32359
34292
  const sdkManagerPath = sdkManagerBin(deps.androidHome, deps.platform);
32360
34293
  const avdManagerPath = avdManagerBin(deps.androidHome, deps.platform);
@@ -32364,7 +34297,7 @@ async function installAvds(ctx, specs, deps) {
32364
34297
  if (versionResult.exitCode !== 0) {
32365
34298
  throw new Error(installMessages.android.sdkmanagerNotFound(sdkManagerPath));
32366
34299
  }
32367
- const licenseFile = join49(deps.androidHome, "licenses", "android-sdk-license");
34300
+ const licenseFile = join51(deps.androidHome, "licenses", "android-sdk-license");
32368
34301
  if (deps.checkExists(licenseFile)) {
32369
34302
  ctx.ui.info(installMessages.android.licensesAlreadyAccepted);
32370
34303
  } else {
@@ -32379,7 +34312,7 @@ async function installAvds(ctx, specs, deps) {
32379
34312
  for (const spec of specs) {
32380
34313
  if (installedImages.has(spec.systemImage))
32381
34314
  continue;
32382
- const systemImg = join49(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
34315
+ const systemImg = join51(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
32383
34316
  if (deps.checkExists(systemImg)) {
32384
34317
  ctx.ui.info(installMessages.android.systemImageAlreadyInstalled(spec.systemImage));
32385
34318
  installedImages.add(spec.systemImage);
@@ -32492,7 +34425,7 @@ function buildAvdSpecs(targets, arch) {
32492
34425
  const avdName = avdNameForTarget(target);
32493
34426
  if (!avdName || seen.has(avdName))
32494
34427
  continue;
32495
- const parsed = parseExecutionTarget4(target);
34428
+ const parsed = parseExecutionTarget(target);
32496
34429
  if (parsed.platform !== "android")
32497
34430
  continue;
32498
34431
  const { deviceModel, androidVersion } = parsed.meta;
@@ -32820,14 +34753,14 @@ function parseKeyValueRecord(flag, pairs) {
32820
34753
  }
32821
34754
  return { ok: true, record };
32822
34755
  }
32823
- var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
34756
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
32824
34757
  function setAtPath(input, path, value) {
32825
34758
  const leaf = path[path.length - 1];
32826
34759
  if (leaf === undefined)
32827
34760
  return;
32828
34761
  const target = path.slice(0, -1).reduce((node, segment) => {
32829
34762
  const existing = node[segment];
32830
- const next = isRecord3(existing) ? existing : {};
34763
+ const next = isRecord4(existing) ? existing : {};
32831
34764
  node[segment] = next;
32832
34765
  return next;
32833
34766
  }, input);
@@ -32881,6 +34814,8 @@ async function handlePublicApiCommand(ctx, spec, options) {
32881
34814
 
32882
34815
  // src/domains/publicApi/skippedContracts.ts
32883
34816
  var skippedContractNames = new Set([
34817
+ "agent.get",
34818
+ "agent.send",
32884
34819
  "flow.list",
32885
34820
  "runner.evaluateSnippet",
32886
34821
  "runner.get",
@@ -32899,34 +34834,6 @@ var skippedContractNames = new Set([
32899
34834
  "runner.terminate"
32900
34835
  ]);
32901
34836
 
32902
- // src/commands/publicApi/environmentOptions.ts
32903
- var optionEnvironmentVariables = new Map([
32904
- ["environmentId", "QAWOLF_ENVIRONMENT"],
32905
- ["public.run.create.aiTaskId", "QAWOLF_AI_TASK_ID"],
32906
- ["public.run.create.chatSessionId", "QAWOLF_CHAT_SESSION_ID"]
32907
- ]);
32908
- function optionEnvironmentVariable(spec, path) {
32909
- const dotted = path.join(".");
32910
- return optionEnvironmentVariables.get(`${spec.trpcPath}.${dotted}`) ?? optionEnvironmentVariables.get(dotted);
32911
- }
32912
- var exclusiveOptions = new Map([
32913
- ["public.run.create", { keep: "aiTaskId", drop: "chatSessionId" }]
32914
- ]);
32915
- function withoutExclusiveConflict(spec, command, options) {
32916
- const pair = exclusiveOptions.get(spec.trpcPath);
32917
- if (!pair)
32918
- return options;
32919
- if (options[pair.keep] === undefined || options[pair.drop] === undefined) {
32920
- return options;
32921
- }
32922
- const fromEnvironment = (key) => command.getOptionValueSource(key) === "env";
32923
- if (!fromEnvironment(pair.keep) && !fromEnvironment(pair.drop)) {
32924
- return options;
32925
- }
32926
- const dropped = fromEnvironment(pair.drop) ? pair.drop : pair.keep;
32927
- return Object.fromEntries(Object.entries(options).filter(([key]) => key !== dropped));
32928
- }
32929
-
32930
34837
  // src/commands/publicApi/index.ts
32931
34838
  var groupDescriptions = {
32932
34839
  run: "Trigger and manage QA Wolf runs on the platform"
@@ -33063,19 +34970,17 @@ function parseRunnerName(runnerName) {
33063
34970
 
33064
34971
  // src/domains/interactiveRunner/resolveRunner.ts
33065
34972
  var runnerIdEnvironmentVariable = "QAWOLF_RUNNER_ID";
33066
- async function chooseRunnerId(runner, deps) {
33067
- if (runner !== undefined)
33068
- return runner;
33069
- const fromEnvironment = deps.env[runnerIdEnvironmentVariable]?.trim();
33070
- if (fromEnvironment)
33071
- return fromEnvironment;
33072
- return deps.store.readDefaultRunnerId();
33073
- }
34973
+ var chooseRunnerId = (runner, deps) => resolveIdFrom({
34974
+ env: deps.env,
34975
+ environmentVariable: runnerIdEnvironmentVariable,
34976
+ given: runner,
34977
+ readStored: deps.store.readDefaultRunnerId
34978
+ });
33074
34979
  async function resolveRunner(ctx, options, deps) {
33075
34980
  const chosen = await chooseRunnerId(options.runner, deps);
33076
34981
  if (chosen !== undefined) {
33077
- const parsed = parseRunnerId(chosen);
33078
- return parsed.ok ? { runnerId: parsed.id, type: "resolved" } : {
34982
+ const parsed = parseRunnerId(chosen.id);
34983
+ return parsed.ok ? { runnerId: parsed.id, source: chosen.source, type: "resolved" } : {
33079
34984
  error: parsed.error,
33080
34985
  exitCode: exitCodes.invalidArgs,
33081
34986
  type: "failed"
@@ -33096,12 +35001,32 @@ async function resolveRunner(ctx, options, deps) {
33096
35001
  type: "failed"
33097
35002
  };
33098
35003
  }
33099
- return { runnerId: launched.value.id, type: "launched" };
35004
+ return {
35005
+ runnerId: launched.value.id,
35006
+ source: "launched",
35007
+ type: "launched",
35008
+ url: launched.value.url
35009
+ };
33100
35010
  }
33101
35011
  function announceRunner(ctx, resolved) {
33102
35012
  if (resolved.type === "launched") {
33103
- ctx.ui.info(interactiveRunnerMessages.launchedForCommand(resolved.runnerId));
35013
+ ctx.ui.info(interactiveRunnerMessages.launchedForCommand(resolved.runnerId, resolved.url));
35014
+ }
35015
+ }
35016
+
35017
+ // src/domains/interactiveRunner/runnerRequestFailure.ts
35018
+ function runnerRequestFailure(failure, runner) {
35019
+ const fields = failureFields(failure);
35020
+ if (failure.exitCode !== exitCodes.notFound) {
35021
+ return { ...fields, exitCode: failure.exitCode ?? exitCodes.network };
33104
35022
  }
35023
+ const cameFrom = interactiveRunnerMessages.runnerIdCameFrom(runner.runnerId, runner.source);
35024
+ return {
35025
+ ...fields,
35026
+ errorBody: fields.errorBody ? `${fields.errorBody}
35027
+ ${cameFrom}` : cameFrom,
35028
+ exitCode: exitCodes.notFound
35029
+ };
33105
35030
  }
33106
35031
 
33107
35032
  // src/core/interactiveRunner/runFiles.ts
@@ -33215,10 +35140,7 @@ async function handleRunnerExec(ctx, options, deps) {
33215
35140
  ...scope.files === undefined ? {} : { files: scope.files }
33216
35141
  }, runnerCallOptions);
33217
35142
  if (!result.ok) {
33218
- return {
33219
- ...failureFields(result),
33220
- exitCode: result.exitCode ?? exitCodes.network
33221
- };
35143
+ return runnerRequestFailure(result, resolved);
33222
35144
  }
33223
35145
  if (result.value.outcome === "failure") {
33224
35146
  return describeEvaluateSnippetFailure(result.value);
@@ -33241,7 +35163,7 @@ async function handleRunnerExec(ctx, options, deps) {
33241
35163
  }
33242
35164
 
33243
35165
  // src/shell/interactiveRunner/collectRunFiles.ts
33244
- import { join as join51 } from "node:path";
35166
+ import { join as join53 } from "node:path";
33245
35167
 
33246
35168
  // src/core/interactiveRunner/getImports.ts
33247
35169
  function getImports(options) {
@@ -33280,46 +35202,7 @@ function isPathAlias(importPath, paths) {
33280
35202
  }
33281
35203
 
33282
35204
  // src/core/interactiveRunner/resolveImportPath.ts
33283
- import { dirname as dirname15, join as join50, normalize as normalize3 } from "node:path/posix";
33284
-
33285
- // src/core/interactiveRunner/tsconfigPaths.ts
33286
- function isRecord4(value) {
33287
- return typeof value === "object" && value !== null;
33288
- }
33289
- function isTsconfigPaths(value) {
33290
- return isRecord4(value) && Object.values(value).every((targets) => Array.isArray(targets) && targets.every((target) => typeof target === "string"));
33291
- }
33292
- function parseTsconfigPaths(tsconfigContent) {
33293
- let parsed;
33294
- try {
33295
- parsed = JSON.parse(tsconfigContent);
33296
- } catch {
33297
- return;
33298
- }
33299
- if (!isRecord4(parsed))
33300
- return;
33301
- const compilerOptions = parsed["compilerOptions"];
33302
- if (!isRecord4(compilerOptions))
33303
- return;
33304
- const paths = compilerOptions["paths"];
33305
- return isTsconfigPaths(paths) ? paths : undefined;
33306
- }
33307
- function resolvePathAlias(importPath, paths) {
33308
- if (paths === undefined)
33309
- return;
33310
- for (const [pattern, targets] of Object.entries(paths)) {
33311
- const prefix = pattern.replace("*", "");
33312
- if (!importPath.startsWith(prefix))
33313
- continue;
33314
- const [target] = targets;
33315
- if (target !== undefined) {
33316
- return target.replace("*", importPath.slice(prefix.length));
33317
- }
33318
- }
33319
- return;
33320
- }
33321
-
33322
- // src/core/interactiveRunner/resolveImportPath.ts
35205
+ import { dirname as dirname15, join as join52, normalize as normalize3 } from "node:path/posix";
33323
35206
  var supportedExtensions = [".ts", ".js"];
33324
35207
  function candidatesForExplicitExtension(options) {
33325
35208
  const matching = supportedExtensions.find((extension) => options.importPath.endsWith(extension));
@@ -33333,7 +35216,7 @@ function candidatesForExplicitExtension(options) {
33333
35216
  }
33334
35217
  function resolveImportPath(options) {
33335
35218
  const aliasTarget = resolvePathAlias(options.importPath, options.tsconfigPaths);
33336
- const resolvedPath = aliasTarget === undefined ? normalize3(join50(dirname15(options.importingFilePath), options.importPath)) : normalize3(aliasTarget);
35219
+ const resolvedPath = aliasTarget === undefined ? normalize3(join52(dirname15(options.importingFilePath), options.importPath)) : normalize3(aliasTarget);
33337
35220
  const hasExplicitExtension = supportedExtensions.some((extension) => options.importPath.endsWith(extension));
33338
35221
  const candidates = hasExplicitExtension ? candidatesForExplicitExtension({
33339
35222
  importPath: options.importPath,
@@ -33357,7 +35240,7 @@ function resolveImportPath(options) {
33357
35240
  var tsconfigPath = "tsconfig.json";
33358
35241
  async function collectRunFiles(options) {
33359
35242
  const held = await listShippablePaths(options);
33360
- const readFile = (path) => options.fs.readFile(join51(options.cwd, path));
35243
+ const readFile = (path) => options.fs.readFile(join53(options.cwd, path));
33361
35244
  const tsconfigContent = held.has(tsconfigPath) ? await readFile(tsconfigPath).catch(() => {
33362
35245
  return;
33363
35246
  }) : undefined;
@@ -33440,7 +35323,7 @@ function makeRunnerId() {
33440
35323
  }
33441
35324
 
33442
35325
  // src/shell/interactiveRunner/runFilesManifest.ts
33443
- import { join as join52 } from "node:path";
35326
+ import { join as join54 } from "node:path";
33444
35327
  var manifestFileName = "runner-files.json";
33445
35328
  var manifestSchema2 = object({
33446
35329
  files: array(object({ contentHash: string2(), path: string2() })),
@@ -33448,75 +35331,37 @@ var manifestSchema2 = object({
33448
35331
  version: literal(1)
33449
35332
  });
33450
35333
  function makeRunFilesManifestStore(options) {
33451
- const directory = join52(options.cwd, qawolfDir);
33452
- const path = join52(directory, manifestFileName);
33453
- let pendingWrites = 0;
35334
+ const directory = join54(options.cwd, qawolfDir);
35335
+ const path = join54(directory, manifestFileName);
33454
35336
  return {
33455
- async read() {
33456
- const contents = await options.fs.readFile(path).catch(() => {
33457
- return;
33458
- });
33459
- if (contents === undefined)
33460
- return;
33461
- const parsed = manifestSchema2.safeParse(parseJson(contents));
33462
- return parsed.success ? parsed.data : undefined;
33463
- },
35337
+ read: () => readJsonFile(options.fs, path, manifestSchema2),
33464
35338
  async write(manifest) {
33465
- const pendingPath = `${path}.${String(process.pid)}.${String(++pendingWrites)}.tmp`;
33466
35339
  await options.fs.mkdir(directory, { recursive: true });
33467
- await options.fs.writeFile(pendingPath, `${JSON.stringify(manifest, undefined, 2)}
33468
- `);
33469
- await options.fs.rename(pendingPath, path);
35340
+ await writeJsonFileAtomically(options.fs, path, manifest);
33470
35341
  }
33471
35342
  };
33472
35343
  }
33473
- function parseJson(text) {
33474
- try {
33475
- return JSON.parse(text);
33476
- } catch {
33477
- return;
33478
- }
33479
- }
33480
35344
 
33481
35345
  // src/shell/interactiveRunner/runnerStore.ts
33482
- import { join as join53 } from "node:path";
33483
- var storeFileName = "runner.json";
35346
+ import { join as join55 } from "node:path";
35347
+ var storeFileName2 = "runner.json";
33484
35348
  var runnersDirName = "runners";
33485
35349
  var storedRunnerSchema = object({
33486
35350
  id: string2(),
33487
35351
  runnerName: string2().optional()
33488
35352
  });
33489
- var storeSchema = object({
35353
+ var storeSchema2 = object({
33490
35354
  defaultRunnerId: string2().optional()
33491
35355
  });
33492
- function parseJson2(text) {
33493
- try {
33494
- return JSON.parse(text);
33495
- } catch {
33496
- return;
33497
- }
33498
- }
33499
- var pendingWrites = 0;
33500
35356
  function makeRunnerStore(options) {
33501
- const directory = join53(options.cwd, qawolfDir);
33502
- const path = join53(directory, storeFileName);
33503
- const runnersDir = join53(directory, runnersDirName);
33504
- const nextPendingPath = (target) => `${target}.${process.pid}.${++pendingWrites}.tmp`;
33505
- const runnerPath = (runnerId) => join53(runnersDir, `${encodeURIComponent(runnerId)}.json`);
33506
- const writeAtomically = async (target, contents) => {
33507
- const pendingPath = nextPendingPath(target);
33508
- await options.fs.writeFile(pendingPath, `${JSON.stringify(contents, undefined, 2)}
33509
- `);
33510
- await options.fs.rename(pendingPath, target);
33511
- };
35357
+ const directory = join55(options.cwd, qawolfDir);
35358
+ const path = join55(directory, storeFileName2);
35359
+ const runnersDir = join55(directory, runnersDirName);
35360
+ const runnerPath = (runnerId) => join55(runnersDir, `${encodeURIComponent(runnerId)}.json`);
35361
+ const writeAtomically = (target, contents) => writeJsonFileAtomically(options.fs, target, contents);
33512
35362
  const readDefaultRunnerId = async () => {
33513
- const contents = await options.fs.readFile(path).catch(() => {
33514
- return;
33515
- });
33516
- if (contents === undefined)
33517
- return;
33518
- const parsed = storeSchema.safeParse(parseJson2(contents));
33519
- return parsed.success ? parsed.data.defaultRunnerId : undefined;
35363
+ const stored = await readJsonFile(options.fs, path, storeSchema2);
35364
+ return stored?.defaultRunnerId;
33520
35365
  };
33521
35366
  const writeDefaultRunnerId = async (runnerId) => {
33522
35367
  await options.fs.mkdir(directory, { recursive: true });
@@ -33538,15 +35383,7 @@ function makeRunnerStore(options) {
33538
35383
  },
33539
35384
  readDefaultRunnerId,
33540
35385
  async readRunners() {
33541
- const runners = await Promise.all((await readRunnerFileNames()).map(async (name) => {
33542
- const contents = await options.fs.readFile(join53(runnersDir, name)).catch(() => {
33543
- return;
33544
- });
33545
- if (contents === undefined)
33546
- return;
33547
- const parsed = storedRunnerSchema.safeParse(parseJson2(contents));
33548
- return parsed.success ? parsed.data : undefined;
33549
- }));
35386
+ const runners = await Promise.all((await readRunnerFileNames()).map((name) => readJsonFile(options.fs, join55(runnersDir, name), storedRunnerSchema)));
33550
35387
  return runners.filter((runner) => !!runner).sort((left, right) => left.id.localeCompare(right.id));
33551
35388
  },
33552
35389
  async rememberLaunch(runner) {
@@ -33558,13 +35395,13 @@ function makeRunnerStore(options) {
33558
35395
  await Promise.all(runnerIds.map((runnerId) => options.fs.rm(runnerPath(runnerId), { force: true })));
33559
35396
  const names = await readRunnerFileNames();
33560
35397
  await Promise.all(names.map(async (name) => {
33561
- const runnerFile = join53(runnersDir, name);
35398
+ const runnerFile = join55(runnersDir, name);
33562
35399
  const contents = await options.fs.readFile(runnerFile).catch(() => {
33563
35400
  return;
33564
35401
  });
33565
35402
  if (contents === undefined)
33566
35403
  return;
33567
- if (storedRunnerSchema.safeParse(parseJson2(contents)).success)
35404
+ if (storedRunnerSchema.safeParse(parseJson(contents)).success)
33568
35405
  return;
33569
35406
  await options.fs.rm(runnerFile, { force: true });
33570
35407
  }));
@@ -33673,7 +35510,7 @@ async function handleRunnerHighlightSelector(ctx, options, deps) {
33673
35510
  const selector = options.selector ?? "";
33674
35511
  const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.highlightSelector, { id: resolved.runnerId, selector }, runnerCallOptions);
33675
35512
  if (!result.ok) {
33676
- return { ...failureFields(result), exitCode: exitCodes.network };
35513
+ return runnerRequestFailure(result, resolved);
33677
35514
  }
33678
35515
  const { outcome } = result.value;
33679
35516
  switch (outcome) {
@@ -33736,7 +35573,7 @@ function registerRunnerHighlightSelectorCommand(runner, signals) {
33736
35573
  }
33737
35574
 
33738
35575
  // src/domains/interactiveRunner/importPackage.ts
33739
- import { join as join54 } from "node:path";
35576
+ import { join as join56 } from "node:path";
33740
35577
 
33741
35578
  // src/core/interactiveRunner/npmDependencies.ts
33742
35579
  var sectionsInPrecedenceOrder = ["dependencies", "devDependencies"];
@@ -33777,7 +35614,7 @@ function readNpmDependencies(packageJsonContent) {
33777
35614
  // src/domains/interactiveRunner/importPackage.ts
33778
35615
  var defaultPackageVersion = "latest";
33779
35616
  async function handleRunnerImportPackage(ctx, options, deps) {
33780
- const content = await deps.readFile(join54(deps.cwd, runPackageJsonPath)).catch(() => {
35617
+ const content = await deps.readFile(join56(deps.cwd, runPackageJsonPath)).catch(() => {
33781
35618
  return;
33782
35619
  });
33783
35620
  if (content === undefined) {
@@ -33809,10 +35646,7 @@ async function handleRunnerImportPackage(ctx, options, deps) {
33809
35646
  packageVersion
33810
35647
  }, runnerCallOptions);
33811
35648
  if (!result.ok) {
33812
- return {
33813
- ...failureFields(result),
33814
- exitCode: result.exitCode ?? exitCodes.network
33815
- };
35649
+ return runnerRequestFailure(result, resolved);
33816
35650
  }
33817
35651
  if (result.value.outcome === "failure") {
33818
35652
  const failure = result.value;
@@ -33879,7 +35713,7 @@ async function handleRunnerInspect(ctx, options, deps) {
33879
35713
  }
33880
35714
  const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.inspect, { id: resolved.runnerId, request: built.request }, runnerCallOptions);
33881
35715
  if (!result.ok) {
33882
- return { ...failureFields(result), exitCode: exitCodes.network };
35716
+ return runnerRequestFailure(result, resolved);
33883
35717
  }
33884
35718
  if (result.value.outcome === "failure") {
33885
35719
  const failure = result.value;
@@ -34014,7 +35848,7 @@ async function handleRunnerInspectMobile(ctx, options, deps) {
34014
35848
  }
34015
35849
  const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.inspectMobile, { id: resolved.runnerId, request: built.request }, runnerCallOptions);
34016
35850
  if (!result.ok) {
34017
- return { ...failureFields(result), exitCode: exitCodes.network };
35851
+ return runnerRequestFailure(result, resolved);
34018
35852
  }
34019
35853
  if (result.value.outcome === "success") {
34020
35854
  const value = result.value;
@@ -34306,14 +36140,11 @@ async function handleRunnerAct(ctx, options, deps) {
34306
36140
  ...options.screenshot === undefined ? {} : { withScreenshot: true }
34307
36141
  }, runnerCallOptions);
34308
36142
  if (!result.ok) {
34309
- const fields = failureFields(result);
34310
- return {
36143
+ const fields = runnerRequestFailure(result, resolved);
36144
+ return result.mayHaveArrived ? {
34311
36145
  ...fields,
34312
- ...result.mayHaveArrived ? {
34313
- error: appendSentence(fields.error, interactiveRunnerMessages.actionMayHaveHappened)
34314
- } : {},
34315
- exitCode: exitCodes.network
34316
- };
36146
+ error: appendSentence(fields.error, interactiveRunnerMessages.actionMayHaveHappened)
36147
+ } : fields;
34317
36148
  }
34318
36149
  const answer = result.value;
34319
36150
  if (answer.outcome === "success") {
@@ -34358,9 +36189,8 @@ async function handleRunnerScreenshot(ctx, options, deps) {
34358
36189
  };
34359
36190
  }
34360
36191
  const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.takeScreenshot, { id: resolved.runnerId }, runnerCallOptions);
34361
- if (!result.ok) {
34362
- return { ...failureFields(result), exitCode: exitCodes.network };
34363
- }
36192
+ if (!result.ok)
36193
+ return runnerRequestFailure(result, resolved);
34364
36194
  if (result.value.outcome === "success") {
34365
36195
  const written = await deps.writeScreenshot({
34366
36196
  imageJpegBase64: result.value.imageJpegBase64,
@@ -34465,20 +36295,16 @@ var unreachableFailure = {
34465
36295
  error: interactiveRunnerMessages.runnerUnreachable,
34466
36296
  exitCode: exitCodes.network
34467
36297
  };
34468
- async function readJournal(ctx, runnerId, request) {
36298
+ async function readJournal(ctx, runner, request) {
34469
36299
  const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.readJournal, {
34470
- id: runnerId,
36300
+ id: runner.runnerId,
34471
36301
  stream: request.stream,
34472
36302
  ...request.runId === undefined ? {} : { runId: request.runId },
34473
36303
  ...request.sinceSequence === undefined ? {} : { sinceSequence: request.sinceSequence },
34474
36304
  ...request.tail === undefined ? {} : { tail: request.tail }
34475
36305
  }, runnerCallOptions);
34476
36306
  if (!result.ok) {
34477
- return {
34478
- ...failureFields(result),
34479
- exitCode: exitCodes.network,
34480
- type: "failed"
34481
- };
36307
+ return { ...runnerRequestFailure(result, runner), type: "failed" };
34482
36308
  }
34483
36309
  if (result.value.outcome === "failure") {
34484
36310
  result.value.failureReason;
@@ -34493,7 +36319,7 @@ async function handleRunnerKeepalive(ctx, options, deps) {
34493
36319
  if (resolved.type === "failed") {
34494
36320
  return { ...failureFields(resolved), exitCode: resolved.exitCode };
34495
36321
  }
34496
- const window = await readJournal(ctx, resolved.runnerId, {
36322
+ const window = await readJournal(ctx, resolved, {
34497
36323
  stream: "run-status",
34498
36324
  tail: 1
34499
36325
  });
@@ -34523,7 +36349,8 @@ async function handleRunnerLaunch(ctx, options, deps) {
34523
36349
  if (envRunnerId && envRunnerId !== launched.value.id) {
34524
36350
  ctx.ui.warn(interactiveRunnerMessages.envRunnerIdShadowsLaunch(launched.value.id, envRunnerId));
34525
36351
  }
34526
- ctx.ui.output(launched.value, launched.value.alreadyRunning ? interactiveRunnerMessages.alreadyRunning(launched.value.id) : interactiveRunnerMessages.launched(launched.value.id));
36352
+ const runner = launched.value;
36353
+ ctx.ui.output(runner, runner.alreadyRunning ? interactiveRunnerMessages.alreadyRunning(runner.id, runner.url) : interactiveRunnerMessages.launched(runner.id, runner.url));
34527
36354
  return;
34528
36355
  }
34529
36356
 
@@ -34561,7 +36388,8 @@ async function listRunners(ctx, deps) {
34561
36388
  id: runner.id,
34562
36389
  isDefault: runner.id === defaultRunnerId,
34563
36390
  launchedHere: heldIds.has(runner.id),
34564
- runnerName: runner.runnerName
36391
+ runnerName: runner.runnerName,
36392
+ url: runner.url
34565
36393
  }));
34566
36394
  return {
34567
36395
  items: items.sort((left, right) => rank(left) - rank(right) || left.id.localeCompare(right.id)),
@@ -34600,7 +36428,7 @@ async function handleRunnerStopRun(ctx, options, deps) {
34600
36428
  }
34601
36429
  const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.stopRun, { id: resolved.runnerId }, runnerCallOptions);
34602
36430
  if (!result.ok) {
34603
- return { ...failureFields(result), exitCode: exitCodes.network };
36431
+ return runnerRequestFailure(result, resolved);
34604
36432
  }
34605
36433
  if (result.value.outcome === "failure") {
34606
36434
  result.value.failureReason;
@@ -34623,7 +36451,7 @@ async function handleRunnerTerminate(ctx, options, deps) {
34623
36451
  id: resolved.runnerId
34624
36452
  }, runnerCallOptions);
34625
36453
  if (!result.ok) {
34626
- return { ...failureFields(result), exitCode: exitCodes.network };
36454
+ return runnerRequestFailure(result, resolved);
34627
36455
  }
34628
36456
  await deps.store.forgetRunner(resolved.runnerId).catch(() => {
34629
36457
  ctx.ui.warn(interactiveRunnerMessages.defaultNotForgotten(resolved.runnerId));
@@ -34671,7 +36499,7 @@ async function handleRunnerPromoteSnapshot(ctx, options, deps) {
34671
36499
  screenshotPath: options.screenshotPath
34672
36500
  }, runnerCallOptions);
34673
36501
  if (!result.ok) {
34674
- return { ...failureFields(result), exitCode: exitCodes.network };
36502
+ return runnerRequestFailure(result, resolved);
34675
36503
  }
34676
36504
  if (result.value.outcome === "success") {
34677
36505
  ctx.ui.output(result.value, interactiveRunnerMessages.snapshotPromoted(options.screenshotPath, options.baselinePath));
@@ -34718,16 +36546,6 @@ function registerRunnerPromoteSnapshotCommand(runner, signals) {
34718
36546
 
34719
36547
  // src/core/interactiveRunner/followTimeout.ts
34720
36548
  var defaultFollowTimeoutSeconds = 3600;
34721
- var followTimeoutSchema = exports_coerce.number().int().positive();
34722
- function parseFollowTimeout(seconds) {
34723
- if (seconds === undefined) {
34724
- return { ok: true, seconds: defaultFollowTimeoutSeconds };
34725
- }
34726
- const parsed = followTimeoutSchema.safeParse(seconds);
34727
- if (!parsed.success)
34728
- return { error: prettifyError(parsed.error), ok: false };
34729
- return { ok: true, seconds: parsed.data };
34730
- }
34731
36549
 
34732
36550
  // src/core/interactiveRunner/journal.ts
34733
36551
  function formatJournalLine(entry, options) {
@@ -34805,12 +36623,12 @@ function parseEventsOptions(options) {
34805
36623
  }
34806
36624
 
34807
36625
  // src/domains/interactiveRunner/journalCursor.ts
34808
- function createJournalCursor(ctx, runnerId, request) {
36626
+ function createJournalCursor(ctx, runner, request) {
34809
36627
  let sinceSequence = request.sinceSequence;
34810
36628
  let tail = request.tail;
34811
36629
  let warnedUnsearchedHistory = false;
34812
36630
  return async function read() {
34813
- const window = await readJournal(ctx, runnerId, {
36631
+ const window = await readJournal(ctx, runner, {
34814
36632
  ...request,
34815
36633
  sinceSequence,
34816
36634
  tail
@@ -34832,8 +36650,8 @@ function createJournalCursor(ctx, runnerId, request) {
34832
36650
  return { entries: window.value.entries, type: "entries" };
34833
36651
  };
34834
36652
  }
34835
- function createPrintingCursor(ctx, runnerId, request, format) {
34836
- const read = createJournalCursor(ctx, runnerId, request);
36653
+ function createPrintingCursor(ctx, runner, request, format) {
36654
+ const read = createJournalCursor(ctx, runner, request);
34837
36655
  return async () => {
34838
36656
  const window = await read();
34839
36657
  if (window.type !== "entries")
@@ -34857,12 +36675,12 @@ function createUnreachableBudget(pollIntervalMs) {
34857
36675
  }
34858
36676
 
34859
36677
  // src/domains/interactiveRunner/events.ts
34860
- var pollIntervalMs2 = 1000;
36678
+ var pollIntervalMs3 = 1000;
34861
36679
  async function handleRunnerEvents(ctx, options, deps) {
34862
36680
  const parsed = parseEventsOptions(options);
34863
36681
  if (!parsed.ok)
34864
36682
  return { error: parsed.error, exitCode: exitCodes.invalidArgs };
34865
- const timeout = parseFollowTimeout(options.timeout);
36683
+ const timeout = parseFollowTimeout(options.timeout, defaultFollowTimeoutSeconds);
34866
36684
  if (!timeout.ok) {
34867
36685
  return { error: timeout.error, exitCode: exitCodes.invalidArgs };
34868
36686
  }
@@ -34874,9 +36692,9 @@ async function handleRunnerEvents(ctx, options, deps) {
34874
36692
  if (!known.includes(parsed.value.stream)) {
34875
36693
  ctx.ui.warn(interactiveRunnerMessages.unknownStream(parsed.value.stream, known));
34876
36694
  }
34877
- const read = createJournalCursor(ctx, resolved.runnerId, parsed.value);
34878
- const unreachable = createUnreachableBudget(pollIntervalMs2);
34879
- const maxPolls = Math.max(1, Math.ceil(timeout.seconds * 1000 / pollIntervalMs2));
36695
+ const read = createJournalCursor(ctx, resolved, parsed.value);
36696
+ const unreachable = createUnreachableBudget(pollIntervalMs3);
36697
+ const maxPolls = Math.max(1, Math.ceil(timeout.seconds * 1000 / pollIntervalMs3));
34880
36698
  for (let poll = 1;; poll++) {
34881
36699
  const window = await read();
34882
36700
  if (window.type === "failed")
@@ -34902,7 +36720,7 @@ async function handleRunnerEvents(ctx, options, deps) {
34902
36720
  exitCode: exitCodes.timeout
34903
36721
  };
34904
36722
  }
34905
- await deps.sleep(pollIntervalMs2);
36723
+ await deps.sleep(pollIntervalMs3);
34906
36724
  }
34907
36725
  }
34908
36726
 
@@ -34930,12 +36748,12 @@ var anchorPollIntervalMs = 1000;
34930
36748
  async function resolveRecorderAnchor(ctx, resolved, deps) {
34931
36749
  if (resolved.type === "launched")
34932
36750
  return { ok: true, sinceSequence: 0 };
34933
- return anchorRecorderCursor(ctx, resolved.runnerId, deps);
36751
+ return anchorRecorderCursor(ctx, resolved, deps);
34934
36752
  }
34935
- async function anchorRecorderCursor(ctx, runnerId, deps) {
36753
+ async function anchorRecorderCursor(ctx, runner, deps) {
34936
36754
  const unreachable = createUnreachableBudget(anchorPollIntervalMs);
34937
36755
  for (;; ) {
34938
- const anchor = await readJournal(ctx, runnerId, {
36756
+ const anchor = await readJournal(ctx, runner, {
34939
36757
  stream: "recorder",
34940
36758
  tail: 1
34941
36759
  });
@@ -34955,20 +36773,20 @@ function createFollowPrinters(ctx, options) {
34955
36773
  const jsonLine = (payload) => JSON.stringify(payload);
34956
36774
  const printers = [];
34957
36775
  if (options.logs) {
34958
- printers.push(createPrintingCursor(ctx, options.runnerId, { runId: options.runId, stream: "run-logs" }, formatRunLogLine));
36776
+ printers.push(createPrintingCursor(ctx, options.runner, { runId: options.runId, stream: "run-logs" }, formatRunLogLine));
34959
36777
  }
34960
36778
  if (options.runEvents) {
34961
- printers.push(createPrintingCursor(ctx, options.runnerId, { runId: options.runId, stream: "run-events" }, jsonLine));
36779
+ printers.push(createPrintingCursor(ctx, options.runner, { runId: options.runId, stream: "run-events" }, jsonLine));
34962
36780
  }
34963
36781
  if (options.recorderSinceSequence !== undefined) {
34964
- printers.push(createPrintingCursor(ctx, options.runnerId, { sinceSequence: options.recorderSinceSequence, stream: "recorder" }, jsonLine));
36782
+ printers.push(createPrintingCursor(ctx, options.runner, { sinceSequence: options.recorderSinceSequence, stream: "recorder" }, jsonLine));
34965
36783
  }
34966
36784
  return printers;
34967
36785
  }
34968
36786
 
34969
36787
  // src/domains/interactiveRunner/followRun.ts
34970
- var pollIntervalMs3 = 1000;
34971
- function reportSettlement(ctx, settlement) {
36788
+ var pollIntervalMs4 = 1000;
36789
+ function reportSettlement2(ctx, settlement) {
34972
36790
  if (settlement.type === "passed") {
34973
36791
  ctx.ui.success(interactiveRunnerMessages.runPassed);
34974
36792
  return;
@@ -34980,11 +36798,11 @@ function reportSettlement(ctx, settlement) {
34980
36798
  }
34981
36799
  async function followRun(ctx, options, deps) {
34982
36800
  const printers = createFollowPrinters(ctx, options);
34983
- const readStatus = createJournalCursor(ctx, options.runnerId, {
36801
+ const readStatus = createJournalCursor(ctx, options.runner, {
34984
36802
  runId: options.runId,
34985
36803
  stream: "run-status"
34986
36804
  });
34987
- const unreachable = createUnreachableBudget(pollIntervalMs3);
36805
+ const unreachable = createUnreachableBudget(pollIntervalMs4);
34988
36806
  const printAll = async () => {
34989
36807
  for (const print of printers) {
34990
36808
  const window = await print();
@@ -35003,7 +36821,7 @@ async function followRun(ctx, options, deps) {
35003
36821
  progressReported = true;
35004
36822
  ctx.ui.stream(entry, interactiveRunnerMessages.runInProgress);
35005
36823
  };
35006
- const maxPolls = Math.max(1, Math.ceil(options.timeoutSeconds * 1000 / pollIntervalMs3));
36824
+ const maxPolls = Math.max(1, Math.ceil(options.timeoutSeconds * 1000 / pollIntervalMs4));
35007
36825
  for (let poll = 1;; poll++) {
35008
36826
  const interrupted = await printAll();
35009
36827
  if (interrupted?.type === "failed")
@@ -35022,16 +36840,16 @@ async function followRun(ctx, options, deps) {
35022
36840
  if (await printAll() !== undefined) {
35023
36841
  ctx.ui.warn(interactiveRunnerMessages.followEndCutShort);
35024
36842
  }
35025
- return reportSettlement(ctx, settlement);
36843
+ return reportSettlement2(ctx, settlement);
35026
36844
  }
35027
36845
  }
35028
36846
  if (poll >= maxPolls) {
35029
36847
  return {
35030
- error: interactiveRunnerMessages.followTimedOut(options.runId, options.runnerId, options.timeoutSeconds),
36848
+ error: interactiveRunnerMessages.followTimedOut(options.runId, options.runner.runnerId, options.timeoutSeconds),
35031
36849
  exitCode: exitCodes.timeout
35032
36850
  };
35033
36851
  }
35034
- await deps.sleep(pollIntervalMs3);
36852
+ await deps.sleep(pollIntervalMs4);
35035
36853
  }
35036
36854
  }
35037
36855
 
@@ -35235,10 +37053,7 @@ async function sendRunFlowRequest(ctx, options, delta) {
35235
37053
  }, runnerCallOptions);
35236
37054
  if (!result.ok) {
35237
37055
  return {
35238
- failure: {
35239
- ...failureFields(result),
35240
- exitCode: result.exitCode ?? exitCodes.network
35241
- },
37056
+ failure: runnerRequestFailure(result, options.resolved),
35242
37057
  type: "failed"
35243
37058
  };
35244
37059
  }
@@ -35311,7 +37126,7 @@ async function finish(sent, fileSync, options, deps) {
35311
37126
 
35312
37127
  // src/domains/interactiveRunner/runFlow.ts
35313
37128
  async function handleRunnerRun(ctx, options, deps) {
35314
- const timeout = parseFollowTimeout(options.timeout);
37129
+ const timeout = parseFollowTimeout(options.timeout, defaultFollowTimeoutSeconds);
35315
37130
  if (!timeout.ok) {
35316
37131
  return { error: timeout.error, exitCode: exitCodes.invalidArgs };
35317
37132
  }
@@ -35371,7 +37186,7 @@ async function handleRunnerRun(ctx, options, deps) {
35371
37186
  recorderSinceSequence,
35372
37187
  runEvents: options.runEvents,
35373
37188
  runId,
35374
- runnerId: resolved.runnerId,
37189
+ runner: resolved,
35375
37190
  timeoutSeconds: timeout.seconds
35376
37191
  }, deps);
35377
37192
  }
@@ -35422,6 +37237,7 @@ function createProgram({
35422
37237
  const program = new Command2().name("qawolf").description("Run QA Wolf flows locally").version(package_default.version).option("--verbose", "Enable debug logging to stderr").option("--json", "Output as JSON").option("--agent", "Output for agent consumption").exitOverride((err) => {
35423
37238
  exit(err.exitCode === 0 ? exitCodes.success : exitCodes.invalidArgs);
35424
37239
  });
37240
+ registerAgentCommand(program, signals);
35425
37241
  registerAuthCommand(program, signals);
35426
37242
  registerDoctorCommand(program, signals);
35427
37243
  registerFlowsCommand(program, signals);
@@ -35442,4 +37258,4 @@ createProgram({ signals }).parseAsync().catch(() => {
35442
37258
  process.exitCode = 1;
35443
37259
  }).finally(() => exitWhenIdle(typeof process.exitCode === "number" ? process.exitCode : 0));
35444
37260
 
35445
- //# debugId=424C032AEE2722C964756E2164756E21
37261
+ //# debugId=59D2DE48FE68917864756E2164756E21