@qawolf/cli 1.28.0 → 1.30.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
@@ -8390,16 +8390,38 @@ function extractMissingPackage(text) {
8390
8390
  return specifier;
8391
8391
  }
8392
8392
 
8393
- // src/core/pluralize.ts
8394
- function pluralize(count, singular, plural = `${singular}s`) {
8395
- return `${String(count)} ${count === 1 ? singular : plural}`;
8396
- }
8397
-
8398
8393
  // src/core/formatSeconds.ts
8399
8394
  function formatSeconds(ms) {
8400
8395
  return `${Math.round(ms / 1000)}s`;
8401
8396
  }
8402
8397
 
8398
+ // src/core/messages/agent.ts
8399
+ var followAgain = (sessionId) => `qawolf agent get --follow --session ${sessionId}`;
8400
+ var sessionGivenTwice = (argument, flag) => `Two different sessions were named: "${argument}" as an argument and "${flag}" as --session. Pass one of them.`;
8401
+ var agentMessages = {
8402
+ answerCancelled: "No answer sent, so QA Wolf is still waiting on one. The session keeps its place: answer it whenever you like.",
8403
+ answerPrompt: "Your answer",
8404
+ answerSendFailed: (reason) => `The answer could not be sent: ${reason}. The session is still waiting, so try again.`,
8405
+ blocked: (sessionId) => `QA Wolf asked a question and cannot continue until it is answered. Answer it with qawolf agent send "<your answer>" --session ${sessionId}.`,
8406
+ detachHint: "Press Ctrl-C to stop following. The session keeps running in the background.",
8407
+ detached: (sessionId) => `Stopped following. The session is still running — pick it back up with ${followAgain(sessionId)}.`,
8408
+ 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.`,
8409
+ noSession: "No session named. Pass --session, set QAWOLF_SESSION_ID, or start one with qawolf agent send.",
8410
+ sessionNotRemembered: (sessionId) => `The session id could not be saved to .qawolf/agent.json, so pass --session ${sessionId} to pick it up later.`,
8411
+ sessionCancelled: (url) => `The session was cancelled before it finished. Read how far it got at ${url}.`,
8412
+ sessionCompleted: "QA Wolf finished the work.",
8413
+ sessionFailed: (url) => `The session failed. Read what happened at ${url}.`,
8414
+ sessionAt: (url) => `Following ${url}`,
8415
+ standing: (status, url) => `${status} — ${url}`,
8416
+ started: (sessionId, url) => `Started session ${sessionId} — ${url}`,
8417
+ waiting: "Waiting for QA Wolf",
8418
+ working: "QA Wolf is working. It reports back here as it goes, which can take a few minutes."
8419
+ };
8420
+ // src/core/pluralize.ts
8421
+ function pluralize(count, singular, plural = `${singular}s`) {
8422
+ return `${String(count)} ${count === 1 ? singular : plural}`;
8423
+ }
8424
+
8403
8425
  // src/core/messages/authErrors.ts
8404
8426
  var authErrorMessages = {
8405
8427
  identity: {
@@ -8669,7 +8691,7 @@ var flowsMessages = {
8669
8691
  extractingBundle: "Extracting bundle",
8670
8692
  downloadingTeamStorageAssets: "Downloading team-storage assets",
8671
8693
  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.",
8694
+ 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
8695
  summary: (result, assetsAbs) => {
8674
8696
  const flows = pluralize(result.flowCount, "flow");
8675
8697
  const envVars = result.envVarCount === 0 ? "" : ` and ${pluralize(result.envVarCount, "environment variable")}`;
@@ -8844,17 +8866,18 @@ var interactMessages = {
8844
8866
  };
8845
8867
 
8846
8868
  // src/core/messages/interactiveRunner/lifecycle.ts
8869
+ var pageAt = (url) => `Its runner page is at ${url}`;
8847
8870
  var noRunnerId = "No runner id. Pass --runner, set QAWOLF_RUNNER_ID, or run qawolf runner launch first.";
8848
8871
  var lifecycleMessages = {
8849
- alreadyRunning: (id) => `Runner ${id} was already running.`,
8872
+ alreadyRunning: (id, url) => `Runner ${id} was already running. ${pageAt(url)}`,
8850
8873
  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
8874
  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
8875
  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
8876
  keptAlive: (id) => `Runner ${id} is alive, and its inactivity clock has been reset.`,
8854
8877
  launchFailed: (id, error) => `Launching runner ${id} failed. ${error}`,
8855
8878
  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.`,
8879
+ launched: (id, url) => `Launched runner ${id}. ${pageAt(url)}`,
8880
+ 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
8881
  noRunnerIdForImport: `${noRunnerId} An install also needs a live run to go into, which a runner gets from qawolf runner run.`,
8859
8882
  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
8883
  noRunnerIdForInspect: `${noRunnerId} Inspecting also needs a page, which a runner gets from its first run: run a flow on it with qawolf runner run.`,
@@ -9237,8 +9260,10 @@ function resolveHostUrl(env) {
9237
9260
  return configuredHostUrl.replace(/\/+$/, "");
9238
9261
  }
9239
9262
 
9240
- // src/domains/auth/resolve.ts
9241
- import { Entry as Entry5 } from "@napi-rs/keyring";
9263
+ // src/shell/keyring.ts
9264
+ function loadEntryClass() {
9265
+ return import("@napi-rs/keyring").then((keyring) => keyring.Entry);
9266
+ }
9242
9267
 
9243
9268
  // src/core/deviceAuth/tokenExpiry.ts
9244
9269
  function readAccessTokenExpiry(accessToken) {
@@ -11881,6 +11906,56 @@ function handlePipeResult(left, next, ctx) {
11881
11906
  }
11882
11907
  return next._zod.run({ value: left.value, issues: left.issues, fallback: left.fallback }, ctx);
11883
11908
  }
11909
+ var $ZodCodec = /* @__PURE__ */ $constructor("$ZodCodec", (inst, def) => {
11910
+ $ZodType.init(inst, def);
11911
+ defineLazy(inst._zod, "values", () => def.in._zod.values);
11912
+ defineLazy(inst._zod, "optin", () => def.in._zod.optin);
11913
+ defineLazy(inst._zod, "optout", () => def.out._zod.optout);
11914
+ defineLazy(inst._zod, "propValues", () => def.in._zod.propValues);
11915
+ inst._zod.parse = (payload, ctx) => {
11916
+ const direction = ctx.direction || "forward";
11917
+ if (direction === "forward") {
11918
+ const left = def.in._zod.run(payload, ctx);
11919
+ if (left instanceof Promise) {
11920
+ return left.then((left) => handleCodecAResult(left, def, ctx));
11921
+ }
11922
+ return handleCodecAResult(left, def, ctx);
11923
+ } else {
11924
+ const right = def.out._zod.run(payload, ctx);
11925
+ if (right instanceof Promise) {
11926
+ return right.then((right) => handleCodecAResult(right, def, ctx));
11927
+ }
11928
+ return handleCodecAResult(right, def, ctx);
11929
+ }
11930
+ };
11931
+ });
11932
+ function handleCodecAResult(result, def, ctx) {
11933
+ if (result.issues.length) {
11934
+ result.aborted = true;
11935
+ return result;
11936
+ }
11937
+ const direction = ctx.direction || "forward";
11938
+ if (direction === "forward") {
11939
+ const transformed = def.transform(result.value, result);
11940
+ if (transformed instanceof Promise) {
11941
+ return transformed.then((value) => handleCodecTxResult(result, value, def.out, ctx));
11942
+ }
11943
+ return handleCodecTxResult(result, transformed, def.out, ctx);
11944
+ } else {
11945
+ const transformed = def.reverseTransform(result.value, result);
11946
+ if (transformed instanceof Promise) {
11947
+ return transformed.then((value) => handleCodecTxResult(result, value, def.in, ctx));
11948
+ }
11949
+ return handleCodecTxResult(result, transformed, def.in, ctx);
11950
+ }
11951
+ }
11952
+ function handleCodecTxResult(left, value, nextSchema, ctx) {
11953
+ if (left.issues.length) {
11954
+ left.aborted = true;
11955
+ return left;
11956
+ }
11957
+ return nextSchema._zod.run({ value, issues: left.issues }, ctx);
11958
+ }
11884
11959
  var $ZodReadonly = /* @__PURE__ */ $constructor("$ZodReadonly", (inst, def) => {
11885
11960
  $ZodType.init(inst, def);
11886
11961
  defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues);
@@ -14426,6 +14501,19 @@ function pipe(in_, out) {
14426
14501
  out
14427
14502
  });
14428
14503
  }
14504
+ var ZodCodec = /* @__PURE__ */ $constructor("ZodCodec", (inst, def) => {
14505
+ ZodPipe.init(inst, def);
14506
+ $ZodCodec.init(inst, def);
14507
+ });
14508
+ function codec(in_, out, params) {
14509
+ return new ZodCodec({
14510
+ type: "pipe",
14511
+ in: in_,
14512
+ out,
14513
+ transform: params.decode,
14514
+ reverseTransform: params.encode
14515
+ });
14516
+ }
14429
14517
  var ZodReadonly = /* @__PURE__ */ $constructor("ZodReadonly", (inst, def) => {
14430
14518
  $ZodReadonly.init(inst, def);
14431
14519
  ZodType.init(inst, def);
@@ -14635,7 +14723,6 @@ function resolveWorkosConfig(publishedClientId) {
14635
14723
 
14636
14724
  // src/domains/auth/store/delete.ts
14637
14725
  import { join as join2 } from "node:path";
14638
- import { Entry } from "@napi-rs/keyring";
14639
14726
 
14640
14727
  // src/domains/auth/store/constants.ts
14641
14728
  var service = "qawolf-cli";
@@ -14645,8 +14732,9 @@ var tokensAccount = "oauth-tokens";
14645
14732
  var tokensFile = "tokens.json";
14646
14733
 
14647
14734
  // src/domains/auth/store/delete.ts
14648
- function deleteFromKeychain() {
14735
+ async function deleteFromKeychain() {
14649
14736
  try {
14737
+ const Entry = await loadEntryClass();
14650
14738
  new Entry(service, account).deletePassword();
14651
14739
  return "deleted";
14652
14740
  } catch {
@@ -14665,14 +14753,13 @@ async function deleteFromFile(configDir, fs) {
14665
14753
  }
14666
14754
  async function deleteApiKey(configDir, fs) {
14667
14755
  const [keychain, file] = await Promise.all([
14668
- Promise.resolve(deleteFromKeychain()),
14756
+ deleteFromKeychain(),
14669
14757
  deleteFromFile(configDir, fs)
14670
14758
  ]);
14671
14759
  return { keychain, file };
14672
14760
  }
14673
14761
  // src/domains/auth/store/hasStoredCredentials.ts
14674
14762
  import { join as join3 } from "node:path";
14675
- import { Entry as Entry2 } from "@napi-rs/keyring";
14676
14763
  async function fileExists(path, fs) {
14677
14764
  try {
14678
14765
  await fs.readFile(path);
@@ -14681,9 +14768,10 @@ async function fileExists(path, fs) {
14681
14768
  return !isNoEntError(err);
14682
14769
  }
14683
14770
  }
14684
- function keychainHolds(entryAccount) {
14771
+ async function keychainHolds(entryAccount) {
14685
14772
  try {
14686
- return Boolean(new Entry2(service, entryAccount).getPassword());
14773
+ const Entry = await loadEntryClass();
14774
+ return Boolean(new Entry(service, entryAccount).getPassword());
14687
14775
  } catch {
14688
14776
  return false;
14689
14777
  }
@@ -14693,7 +14781,9 @@ async function hasStoredCredentials(configDir, fs) {
14693
14781
  fileExists(join3(configDir, credentialsFile), fs),
14694
14782
  fileExists(join3(configDir, tokensFile), fs)
14695
14783
  ]);
14696
- return apiKeyFile || tokenFile || keychainHolds(account) || keychainHolds(tokensAccount);
14784
+ if (apiKeyFile || tokenFile)
14785
+ return true;
14786
+ return await keychainHolds(account) || await keychainHolds(tokensAccount);
14697
14787
  }
14698
14788
  // src/domains/auth/store/load.ts
14699
14789
  import { join as join4 } from "node:path";
@@ -14716,7 +14806,8 @@ var oauthTokensSchema = object({
14716
14806
  async function loadApiKey(configDir, deps) {
14717
14807
  const errors = {};
14718
14808
  try {
14719
- const entry = new deps.EntryClass(service, account);
14809
+ const Entry = await deps.loadEntryClass();
14810
+ const entry = new Entry(service, account);
14720
14811
  const key = entry.getPassword();
14721
14812
  if (key)
14722
14813
  return { found: true, key, source: "keychain" };
@@ -14737,7 +14828,6 @@ async function loadApiKey(configDir, deps) {
14737
14828
  }
14738
14829
  // src/domains/auth/store/save.ts
14739
14830
  import { join as join5 } from "node:path";
14740
- import { Entry as Entry3 } from "@napi-rs/keyring";
14741
14831
  async function saveToFile(configDir, key, fs) {
14742
14832
  const payload = { apiKey: key };
14743
14833
  await fs.mkdir(configDir, { recursive: true, mode: 448 });
@@ -14745,7 +14835,8 @@ async function saveToFile(configDir, key, fs) {
14745
14835
  }
14746
14836
  async function saveApiKey(configDir, key, fs) {
14747
14837
  try {
14748
- const entry = new Entry3(service, account);
14838
+ const Entry = await loadEntryClass();
14839
+ const entry = new Entry(service, account);
14749
14840
  entry.setPassword(key);
14750
14841
  return { stored: "keychain" };
14751
14842
  } catch (err) {
@@ -14773,7 +14864,8 @@ function parseTokens(raw) {
14773
14864
  async function loadTokens(configDir, deps) {
14774
14865
  const errors = {};
14775
14866
  try {
14776
- const raw = new deps.EntryClass(service, tokensAccount).getPassword();
14867
+ const Entry = await deps.loadEntryClass();
14868
+ const raw = new Entry(service, tokensAccount).getPassword();
14777
14869
  if (raw) {
14778
14870
  const tokens = parseTokens(raw);
14779
14871
  if (tokens)
@@ -14798,7 +14890,6 @@ async function loadTokens(configDir, deps) {
14798
14890
  // src/domains/auth/store/saveTokens.ts
14799
14891
  import { randomUUID } from "node:crypto";
14800
14892
  import { join as join7 } from "node:path";
14801
- import { Entry as Entry4 } from "@napi-rs/keyring";
14802
14893
  async function saveToFile2(configDir, tokens, fs) {
14803
14894
  await fs.mkdir(configDir, { recursive: true, mode: 448 });
14804
14895
  const target = join7(configDir, tokensFile);
@@ -14815,11 +14906,13 @@ async function saveToFile2(configDir, tokens, fs) {
14815
14906
  }
14816
14907
  async function saveTokens(configDir, tokens, fs) {
14817
14908
  try {
14818
- new Entry4(service, tokensAccount).setPassword(JSON.stringify(tokens));
14909
+ const Entry = await loadEntryClass();
14910
+ new Entry(service, tokensAccount).setPassword(JSON.stringify(tokens));
14819
14911
  return { stored: "keychain" };
14820
14912
  } catch (err) {
14821
14913
  try {
14822
- new Entry4(service, tokensAccount).deletePassword();
14914
+ const Entry = await loadEntryClass();
14915
+ new Entry(service, tokensAccount).deletePassword();
14823
14916
  } catch {}
14824
14917
  await saveToFile2(configDir, tokens, fs);
14825
14918
  return { stored: "file", keychainError: errorMessage(err) };
@@ -14882,7 +14975,7 @@ async function resolveOauthToken(configDir, deps) {
14882
14975
  // src/domains/auth/resolve.ts
14883
14976
  function makeOauthDeps(fs) {
14884
14977
  return {
14885
- loadTokens: (configDir) => loadTokens(configDir, { EntryClass: Entry5, fs }),
14978
+ loadTokens: (configDir) => loadTokens(configDir, { loadEntryClass, fs }),
14886
14979
  refreshTokens: async ({ refreshToken, organizationId, clientId }) => {
14887
14980
  const config = resolveWorkosConfig(clientId);
14888
14981
  if (!config.configured) {
@@ -14904,7 +14997,7 @@ function makeOauthDeps(fs) {
14904
14997
  }
14905
14998
  function makeDefaultDeps(fs) {
14906
14999
  return {
14907
- loadApiKey: (configDir) => loadApiKey(configDir, { EntryClass: Entry5, fs }),
15000
+ loadApiKey: (configDir) => loadApiKey(configDir, { loadEntryClass, fs }),
14908
15001
  resolveOauth: (configDir) => resolveOauthToken(configDir, makeOauthDeps(fs)),
14909
15002
  env: process.env
14910
15003
  };
@@ -18988,7 +19081,8 @@ var environmentListItemSchema = object({
18988
19081
  slug: string2()
18989
19082
  });
18990
19083
  var environmentWithVariablesResponseSchema = object({
18991
- environmentVariables: record(string2(), string2())
19084
+ environmentVariables: record(string2(), string2()),
19085
+ teamId: string2()
18992
19086
  });
18993
19087
  var flowListItemSchema = object({
18994
19088
  flowId: string2(),
@@ -19265,7 +19359,10 @@ async function writeTeamStorageAssets(args, deps) {
19265
19359
 
19266
19360
  // src/shell/platform/teamStorageMethods.ts
19267
19361
  function createTeamStorageMethods(trpc, deps, fs, getIdentity) {
19268
- async function list() {
19362
+ async function list(opts) {
19363
+ if (opts?.teamId !== undefined) {
19364
+ return listTeamStorageFiles(trpc, { teamId: opts.teamId }, deps);
19365
+ }
19269
19366
  if (deps.workspaceId !== undefined) {
19270
19367
  return listTeamStorageFiles(trpc, { teamId: deps.workspaceId }, deps);
19271
19368
  }
@@ -19275,7 +19372,7 @@ function createTeamStorageMethods(trpc, deps, fs, getIdentity) {
19275
19372
  if (!("team" in identity.value)) {
19276
19373
  return {
19277
19374
  ok: false,
19278
- error: flowsMessages.pull.teamStorageRequiresTeamKey
19375
+ error: flowsMessages.pull.teamStorageRequiresTeam
19279
19376
  };
19280
19377
  }
19281
19378
  return listTeamStorageFiles(trpc, { teamId: identity.value.team.id }, deps);
@@ -19283,7 +19380,7 @@ function createTeamStorageMethods(trpc, deps, fs, getIdentity) {
19283
19380
  return {
19284
19381
  listTeamStorageFiles: list,
19285
19382
  async syncTeamStorageAssets(assetsAbs, opts) {
19286
- const files = await list();
19383
+ const files = await list({ teamId: opts?.teamId });
19287
19384
  if (!files.ok)
19288
19385
  return files;
19289
19386
  return downloadTeamStorageAssets({ assetsAbs, files: files.value }, { fetch: deps.fetch, fs, onProgress: opts?.onProgress });
@@ -19313,16 +19410,14 @@ function createPlatformClient(apiKey, deps) {
19313
19410
  ...createTeamStorageMethods(trpc, deps, fs, identityMethods.getIdentity),
19314
19411
  getFlowsBundleUrl: getFlowsBundleUrlImpl,
19315
19412
  callPublicApi: makeCallPublicApiMethod(trpc, deps, requestBackoffMs2),
19316
- async getEnvVars(envId) {
19413
+ async getEnvironmentWithVariables(envId) {
19317
19414
  const result = await requestWithRetry({
19318
19415
  call: () => trpc.query("environment.getEnvironmentWithVariables", { id: envId }, environmentWithVariablesResponseSchema),
19319
19416
  backoffMs: requestBackoffMs2,
19320
19417
  describe: (err) => describeRequestError(err, deps.baseUrl, "env-vars"),
19321
19418
  sleep: deps.sleep
19322
19419
  });
19323
- if (!result.ok)
19324
- return result;
19325
- return { ok: true, value: result.value.environmentVariables };
19420
+ return result;
19326
19421
  },
19327
19422
  async downloadBundle(envId) {
19328
19423
  const urlResult = await getFlowsBundleUrlImpl(envId);
@@ -20142,6 +20237,30 @@ class a extends V {
20142
20237
  });
20143
20238
  }
20144
20239
  }
20240
+ class n extends V {
20241
+ get userInputWithCursor() {
20242
+ if (this.state === "submit")
20243
+ return this.userInput;
20244
+ const t = this.userInput;
20245
+ if (this.cursor >= t.length)
20246
+ return `${this.userInput}█`;
20247
+ const e = t.slice(0, this.cursor), [s, ...r] = t.slice(this.cursor);
20248
+ return `${e}${styleText("inverse", s)}${r.join("")}`;
20249
+ }
20250
+ get cursor() {
20251
+ return this._cursor;
20252
+ }
20253
+ constructor(t) {
20254
+ super({
20255
+ ...t,
20256
+ initialUserInput: t.initialUserInput ?? t.initialValue
20257
+ }), this.on("userInput", (e) => {
20258
+ this._setValue(e);
20259
+ }), this.on("finalize", () => {
20260
+ this.value || (this.value = t.defaultValue), this.value === undefined && (this.value = "");
20261
+ });
20262
+ }
20263
+ }
20145
20264
 
20146
20265
  // node_modules/@clack/prompts/dist/index.mjs
20147
20266
  import { styleText as styleText2, stripVTControlCharacters } from "node:util";
@@ -20752,14 +20871,56 @@ ${e.value}` : e.value;
20752
20871
  }
20753
20872
  };
20754
20873
  };
20874
+ var text = (t) => new n({
20875
+ validate: t.validate,
20876
+ placeholder: t.placeholder,
20877
+ defaultValue: t.defaultValue,
20878
+ initialValue: t.initialValue,
20879
+ output: t.output,
20880
+ signal: t.signal,
20881
+ input: t.input,
20882
+ render() {
20883
+ const i = t?.withGuide ?? settings.withGuide, s = `${`${i ? `${styleText2("gray", S_BAR)}
20884
+ ` : ""}${symbol(this.state)} `}${t.message}
20885
+ `, 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 ?? "";
20886
+ switch (this.state) {
20887
+ case "error": {
20888
+ const n = this.error ? ` ${styleText2("yellow", this.error)}` : "", r = i ? `${styleText2("yellow", S_BAR)} ` : "", d = i ? styleText2("yellow", S_BAR_END) : "";
20889
+ return `${s.trim()}
20890
+ ${r}${o}
20891
+ ${d}${n}
20892
+ `;
20893
+ }
20894
+ case "submit": {
20895
+ const n = a ? ` ${styleText2("dim", a)}` : "", r = i ? styleText2("gray", S_BAR) : "";
20896
+ return `${s}${r}${n}`;
20897
+ }
20898
+ case "cancel": {
20899
+ const n = a ? ` ${styleText2(["strikethrough", "dim"], a)}` : "", r = i ? styleText2("gray", S_BAR) : "";
20900
+ return `${s}${r}${n}${a.trim() ? `
20901
+ ${r}` : ""}`;
20902
+ }
20903
+ default: {
20904
+ const n = i ? `${styleText2("cyan", S_BAR)} ` : "", r = i ? styleText2("cyan", S_BAR_END) : "";
20905
+ return `${s}${n}${o}
20906
+ ${r}
20907
+ `;
20908
+ }
20909
+ }
20910
+ }
20911
+ }).prompt();
20755
20912
 
20756
20913
  // src/shell/ui/clack/theme.ts
20914
+ import { styleText as styleText3 } from "node:util";
20757
20915
  var qaWolfBlueBg = "\x1B[48;2;59;59;239m";
20758
20916
  var whiteBold = "\x1B[97;1m";
20759
20917
  var reset = "\x1B[0m";
20760
20918
  function styledTitle(title) {
20761
20919
  return `${qaWolfBlueBg}${whiteBold} ${title} ${reset}`;
20762
20920
  }
20921
+ function replySymbol() {
20922
+ return styleText3("cyan", "◇");
20923
+ }
20763
20924
 
20764
20925
  // src/shell/ui/clack/styledClack.ts
20765
20926
  function createStyledClack() {
@@ -20770,6 +20931,7 @@ function createStyledClack() {
20770
20931
  cancel,
20771
20932
  confirm,
20772
20933
  password,
20934
+ text,
20773
20935
  autocomplete,
20774
20936
  select,
20775
20937
  isCancel,
@@ -20873,6 +21035,12 @@ function createAgentRenderers() {
20873
21035
  gap: () => writeStderrLine(""),
20874
21036
  stream: (_data, line) => writeStdoutRaw(`${line}
20875
21037
  `),
21038
+ transcript: ({ body, headline }) => writeStdoutRaw(`**${headline}**
21039
+
21040
+ ${body}
21041
+
21042
+ `),
21043
+ wait: () => ({ stop: () => {} }),
20876
21044
  write: (text) => writeStderrRaw(text),
20877
21045
  withProgress: async (steps, done) => {
20878
21046
  const results = [];
@@ -20889,7 +21057,69 @@ function createAgentRenderers() {
20889
21057
  };
20890
21058
  }
20891
21059
 
21060
+ // src/core/wrapText.ts
21061
+ function wrapLine(rawLine, width) {
21062
+ const line = rawLine.trimEnd();
21063
+ if (line.length <= width)
21064
+ return [line];
21065
+ const indent = /^\s*/.exec(line)?.[0] ?? "";
21066
+ const lines = [];
21067
+ let current = "";
21068
+ for (const word of line.trimStart().split(" ")) {
21069
+ if (current === "") {
21070
+ current = word;
21071
+ } else if (indent.length + current.length + 1 + word.length <= width) {
21072
+ current = `${current} ${word}`;
21073
+ } else {
21074
+ lines.push(indent + current);
21075
+ current = word;
21076
+ }
21077
+ }
21078
+ lines.push(indent + current);
21079
+ return lines;
21080
+ }
21081
+ function wrapText(text, width) {
21082
+ return text.split(`
21083
+ `).flatMap((line) => wrapLine(line, width)).join(`
21084
+ `);
21085
+ }
21086
+
21087
+ // src/shell/ui/renderers/waitLine.ts
21088
+ import { styleText as styleText4 } from "node:util";
21089
+ var frames = ["◒", "◐", "◓", "◑"];
21090
+ var frameMs = 120;
21091
+ function elapsed(sinceMs) {
21092
+ const seconds = Math.floor((Date.now() - sinceMs) / 1000);
21093
+ const minutes = Math.floor(seconds / 60);
21094
+ return minutes > 0 ? `[${minutes}m ${seconds % 60}s]` : `[${seconds}s]`;
21095
+ }
21096
+ function startWaitLine(message, output = process.stdout) {
21097
+ const startedAt = Date.now();
21098
+ let frame = 0;
21099
+ const draw = () => {
21100
+ const mark = styleText4("magenta", frames[frame] ?? "◒");
21101
+ output.write(`\r\x1B[2K${mark} ${message} ${elapsed(startedAt)}`);
21102
+ frame = (frame + 1) % frames.length;
21103
+ };
21104
+ draw();
21105
+ const timer = setInterval(draw, frameMs);
21106
+ let stopped = false;
21107
+ return {
21108
+ stop: () => {
21109
+ if (stopped)
21110
+ return;
21111
+ stopped = true;
21112
+ clearInterval(timer);
21113
+ output.write("\r\x1B[2K");
21114
+ }
21115
+ };
21116
+ }
21117
+
20892
21118
  // src/shell/ui/renderers/modes/human.ts
21119
+ function transcriptWidth() {
21120
+ const columns = process.stdout.columns || 80;
21121
+ return Math.min(Math.max(columns - 4, 20), 100);
21122
+ }
20893
21123
  function createHumanRenderers(clack, verboseTarget) {
20894
21124
  return {
20895
21125
  intro: (title) => clack.intro(title),
@@ -20911,6 +21141,12 @@ ${body}` : ""));
20911
21141
  `),
20912
21142
  stream: (_data, line) => writeStdoutRaw(`${line}
20913
21143
  `),
21144
+ transcript: ({ body, headline }) => {
21145
+ const lines = wrapText(body, transcriptWidth()).split(`
21146
+ `);
21147
+ clack.log.message([headline, ...lines], { symbol: replySymbol() });
21148
+ },
21149
+ wait: (message) => startWaitLine(message),
20914
21150
  write: (text) => writeStdoutRaw(text),
20915
21151
  withProgress: async (steps, done) => {
20916
21152
  const results = [];
@@ -20984,6 +21220,8 @@ function createJsonRenderers() {
20984
21220
  output: (data, _humanMessage) => writeJsonLine(data),
20985
21221
  gap: () => {},
20986
21222
  stream: (data, _line) => writeJsonLine(data),
21223
+ transcript: ({ data }) => writeJsonLine(data),
21224
+ wait: () => ({ stop: () => {} }),
20987
21225
  write: () => {},
20988
21226
  withProgress: async (steps, done) => {
20989
21227
  const results = [];
@@ -21080,6 +21318,20 @@ function createSelect({ mode, clack }) {
21080
21318
  };
21081
21319
  }
21082
21320
 
21321
+ // src/shell/ui/renderers/text.ts
21322
+ function createText({ mode, clack }) {
21323
+ return async (message) => {
21324
+ assertHumanMode(mode, "text");
21325
+ const value = await clack.text({ message });
21326
+ if (clack.isCancel(value))
21327
+ return { ok: false };
21328
+ const answer = value.trim();
21329
+ if (answer.length === 0)
21330
+ return { ok: false };
21331
+ return { ok: true, value: answer };
21332
+ };
21333
+ }
21334
+
21083
21335
  // src/shell/ui/createUi.ts
21084
21336
  function createUI(mode, opts = {}) {
21085
21337
  const clack = opts.clack ?? createStyledClack();
@@ -21089,6 +21341,7 @@ function createUI(mode, opts = {}) {
21089
21341
  confirm: createConfirm({ mode, clack }),
21090
21342
  password: createPassword({ mode, clack }),
21091
21343
  select: createSelect({ mode, clack }),
21344
+ text: createText({ mode, clack }),
21092
21345
  json: createJson()
21093
21346
  };
21094
21347
  }
@@ -21213,7 +21466,7 @@ function startUpdateCheck(deps) {
21213
21466
  // package.json
21214
21467
  var package_default = {
21215
21468
  name: "@qawolf/cli",
21216
- version: "1.28.0",
21469
+ version: "1.30.0",
21217
21470
  description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
21218
21471
  keywords: [
21219
21472
  "automation",
@@ -21288,7 +21541,7 @@ var package_default = {
21288
21541
  "@qawolf/emails": "1.1.1",
21289
21542
  "@qawolf/flow-targets": "1.0.0",
21290
21543
  "@qawolf/flows": "0.1.4",
21291
- "@qawolf/testkit": "1.1.1",
21544
+ "@qawolf/testkit": "1.2.1",
21292
21545
  commander: "14.0.3",
21293
21546
  "env-paths": "4.0.0",
21294
21547
  picomatch: "4.0.4",
@@ -21429,111 +21682,576 @@ function withAuthContext(signals, fn, deps = {}) {
21429
21682
  };
21430
21683
  }
21431
21684
 
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);
21685
+ // src/core/agent/session.ts
21686
+ var defaultAgentFollowTimeoutSeconds = 1800;
21687
+ var settledStatuses = [
21688
+ "cancelled",
21689
+ "completed",
21690
+ "failed"
21691
+ ];
21692
+ function isSettled(status) {
21693
+ return settledStatuses.includes(status);
21694
+ }
21695
+ function pendingQuestion(status, replies) {
21696
+ if (status !== "waiting-for-you")
21437
21697
  return;
21438
- }
21439
- const apiKey = result.value.trim();
21440
- if (!apiKey) {
21441
- ctx.ui.cancel(authMessages.cancelled);
21698
+ return replies[replies.length - 1];
21699
+ }
21700
+
21701
+ // src/core/agent/formatReply.ts
21702
+ var speaker = "QA Wolf";
21703
+ function replyTime(askedAt) {
21704
+ const at = new Date(askedAt);
21705
+ if (Number.isNaN(at.getTime()))
21706
+ return;
21707
+ return at.toTimeString().slice(0, 8);
21708
+ }
21709
+ function replyParts(reply, options) {
21710
+ const time = replyTime(reply.askedAt);
21711
+ const choices = options.listChoices && reply.choices !== undefined ? ["", ...reply.choices.map((choice) => `- ${choice}`)] : [];
21712
+ return {
21713
+ body: [reply.text.trim(), ...choices].join(`
21714
+ `),
21715
+ headline: time === undefined ? speaker : `${speaker} ${time}`
21716
+ };
21717
+ }
21718
+
21719
+ // src/core/followTimeout.ts
21720
+ var followTimeoutSchema = exports_coerce.number().int().positive();
21721
+ function parseFollowTimeout(seconds, defaultSeconds) {
21722
+ if (seconds === undefined)
21723
+ return { ok: true, seconds: defaultSeconds };
21724
+ const parsed = followTimeoutSchema.safeParse(seconds);
21725
+ if (!parsed.success)
21726
+ return { error: prettifyError(parsed.error), ok: false };
21727
+ return { ok: true, seconds: parsed.data };
21728
+ }
21729
+
21730
+ // src/domains/agent/settlement.ts
21731
+ function endWithSession(ctx, session) {
21732
+ if (ctx.ui.mode !== "human")
21733
+ ctx.ui.json(session);
21734
+ }
21735
+ function reportSettlement(ctx, session) {
21736
+ endWithSession(ctx, session);
21737
+ if (session.status === "completed") {
21738
+ ctx.ui.success(agentMessages.sessionCompleted);
21442
21739
  return;
21443
21740
  }
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
- })
21741
+ return {
21742
+ error: session.status === "cancelled" ? agentMessages.sessionCancelled(session.url) : agentMessages.sessionFailed(session.url),
21743
+ exitCode: exitCodes.testFailure
21744
+ };
21745
+ }
21746
+
21747
+ // src/domains/agent/answerQuestion.ts
21748
+ async function ask(ctx, question) {
21749
+ const choices = question.choices ?? [];
21750
+ const answer = choices.length > 0 ? await ctx.ui.select(agentMessages.answerPrompt, choices.map((choice) => ({ label: choice, value: choice }))) : await ctx.ui.text(agentMessages.answerPrompt);
21751
+ return answer.ok ? answer.value : undefined;
21752
+ }
21753
+ async function answerQuestion(ctx, options) {
21754
+ const answer = await ask(ctx, options.question);
21755
+ if (answer === undefined) {
21756
+ ctx.ui.warn(agentMessages.answerCancelled);
21757
+ return { type: "declined" };
21758
+ }
21759
+ const sent = await ctx.platformClient.callPublicApi(publicContractsV1.agent.send, {
21760
+ message: answer,
21761
+ sessionId: options.sessionId,
21762
+ ...options.workspaceId === undefined ? {} : { workspaceId: options.workspaceId }
21763
+ });
21764
+ if (!sent.ok) {
21765
+ const failure = failureFields(sent);
21766
+ return {
21767
+ result: {
21768
+ exitCode: exitCodes.network,
21769
+ ...failure,
21770
+ error: agentMessages.answerSendFailed(failure.error)
21771
+ },
21772
+ type: "failed"
21773
+ };
21774
+ }
21775
+ return { repliesAtAnswer: options.repliesSoFar, type: "answered" };
21776
+ }
21777
+ async function handleQuestion(ctx, options) {
21778
+ const { session, sessionId } = options;
21779
+ if (!options.canPrompt) {
21780
+ ctx.ui.info(agentMessages.blocked(sessionId));
21781
+ endWithSession(ctx, session);
21782
+ return { result: undefined, type: "ended" };
21783
+ }
21784
+ const outcome = await answerQuestion(ctx, {
21785
+ question: options.question,
21786
+ repliesSoFar: session.replies.length,
21787
+ sessionId,
21788
+ workspaceId: options.workspaceId
21789
+ });
21790
+ if (outcome.type === "failed")
21791
+ return { result: outcome.result, type: "ended" };
21792
+ if (outcome.type === "declined")
21793
+ return { result: undefined, type: "ended" };
21794
+ return outcome;
21795
+ }
21796
+
21797
+ // src/domains/agent/followSession.ts
21798
+ var pollIntervalMs = 5000;
21799
+ async function followSession(ctx, options, deps) {
21800
+ const { sessionId, workspaceId } = options;
21801
+ const canPrompt = ctx.ui.mode === "human" && ctx.isInteractive;
21802
+ const startedAt = deps.now();
21803
+ let promptedMs = 0;
21804
+ const timedOut = () => deps.now() - startedAt - promptedMs >= options.timeoutSeconds * 1000;
21805
+ let pending = options.initial;
21806
+ let printed = 0;
21807
+ let { repliesAtAnswer } = options;
21808
+ let saidDetachHint = false;
21809
+ let waiting;
21810
+ const stopWaiting = () => {
21811
+ waiting?.stop();
21812
+ waiting = undefined;
21813
+ };
21814
+ const detach = ctx.signals.register(() => {
21815
+ stopWaiting();
21816
+ ctx.ui.info(agentMessages.detached(sessionId));
21817
+ });
21818
+ try {
21819
+ for (let poll = 1;; poll++) {
21820
+ let session;
21821
+ if (pending !== undefined) {
21822
+ session = pending;
21823
+ pending = undefined;
21824
+ } else {
21825
+ const read = await ctx.platformClient.callPublicApi(publicContractsV1.agent.get, { sessionId });
21826
+ if (!read.ok) {
21827
+ return { exitCode: exitCodes.network, ...failureFields(read) };
21828
+ }
21829
+ session = read.value;
21830
+ }
21831
+ const { replies, status } = session;
21832
+ const fresh = replies.slice(printed);
21833
+ printed = replies.length;
21834
+ if (fresh.length > 0)
21835
+ stopWaiting();
21836
+ for (const reply of fresh) {
21837
+ ctx.ui.transcript({
21838
+ ...replyParts(reply, { listChoices: !canPrompt }),
21839
+ data: reply
21453
21840
  });
21454
- if (!v.valid)
21455
- throw Error(v.error);
21456
21841
  }
21457
- },
21458
- {
21459
- message: authMessages.storing,
21460
- task: async () => saveApiKey(ctx.configDir, apiKey, ctx.fs)
21842
+ if (poll === 1 && replies.length === 0 && !isSettled(status)) {
21843
+ ctx.ui.info(agentMessages.working);
21844
+ }
21845
+ if (isSettled(status)) {
21846
+ stopWaiting();
21847
+ return reportSettlement(ctx, session);
21848
+ }
21849
+ const question = pendingQuestion(status, replies);
21850
+ const awaitingOurAnswer = repliesAtAnswer !== undefined && replies.length <= repliesAtAnswer;
21851
+ if (question !== undefined && !awaitingOurAnswer) {
21852
+ stopWaiting();
21853
+ const promptStartedAt = deps.now();
21854
+ const step = await handleQuestion(ctx, {
21855
+ canPrompt,
21856
+ question,
21857
+ session,
21858
+ sessionId,
21859
+ workspaceId
21860
+ });
21861
+ promptedMs += deps.now() - promptStartedAt;
21862
+ if (step.type === "ended")
21863
+ return step.result;
21864
+ repliesAtAnswer = step.repliesAtAnswer;
21865
+ }
21866
+ if (!saidDetachHint && ctx.ui.mode === "human") {
21867
+ saidDetachHint = true;
21868
+ ctx.ui.info(agentMessages.detachHint);
21869
+ }
21870
+ waiting ??= ctx.ui.wait(agentMessages.waiting);
21871
+ await deps.sleep(pollIntervalMs);
21872
+ if (timedOut()) {
21873
+ return {
21874
+ error: agentMessages.followTimedOut(sessionId, options.timeoutSeconds),
21875
+ exitCode: exitCodes.timeout
21876
+ };
21877
+ }
21461
21878
  }
21462
- ], ([, saveResult]) => {
21463
- return saveResult.stored === "file" ? authMessages.storedFile : authMessages.storedKeychain;
21464
- });
21465
- ctx.ui.outro(authMessages.outroSuccess);
21879
+ } finally {
21880
+ stopWaiting();
21881
+ detach();
21882
+ }
21466
21883
  }
21467
21884
 
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));
21885
+ // src/core/resolveId.ts
21886
+ async function resolveIdFrom(options) {
21887
+ if (options.given !== undefined)
21888
+ return options.given;
21889
+ const fromEnvironment = options.env[options.environmentVariable]?.trim();
21890
+ if (fromEnvironment)
21891
+ return fromEnvironment;
21892
+ return options.readStored();
21473
21893
  }
21474
- function nextPollStep(state, response, nowMs) {
21475
- if (response.kind === "tokens") {
21476
- return { action: "done", tokens: response.tokens };
21477
- }
21478
- if (nowMs > state.deadlineMs) {
21479
- return { action: "fail", reason: "timeout", detail: undefined };
21894
+
21895
+ // src/domains/agent/resolveSessionId.ts
21896
+ var sessionIdEnvironmentVariable = "QAWOLF_SESSION_ID";
21897
+ function chooseGivenSessionId(given) {
21898
+ const { argument, flag } = given;
21899
+ if (argument !== undefined && flag !== undefined && argument !== flag) {
21900
+ return { error: sessionGivenTwice(argument, flag), ok: false };
21480
21901
  }
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
- };
21902
+ return { ok: true, sessionId: argument ?? flag };
21903
+ }
21904
+ var resolveSessionId = (session, deps) => resolveIdFrom({
21905
+ env: deps.env,
21906
+ environmentVariable: sessionIdEnvironmentVariable,
21907
+ given: session,
21908
+ readStored: deps.store.readLastSessionId
21909
+ });
21910
+
21911
+ // src/domains/agent/get.ts
21912
+ function renderSession(ctx, session) {
21913
+ if (ctx.ui.mode !== "json") {
21914
+ for (const reply of session.replies) {
21915
+ ctx.ui.transcript({
21916
+ ...replyParts(reply, { listChoices: true }),
21917
+ data: reply
21918
+ });
21513
21919
  }
21514
- case "error":
21515
- return { action: "fail", reason: "network", detail: response.detail };
21516
21920
  }
21921
+ ctx.ui.output(session, agentMessages.standing(session.status, session.url));
21517
21922
  }
21518
-
21519
- // src/domains/auth/deviceLogin.ts
21520
- var cancelled = {
21521
- ok: false,
21522
- reason: "cancelled",
21523
- detail: undefined
21524
- };
21525
- async function deviceLogin(deps) {
21526
- if (deps.isCancelled())
21527
- return cancelled;
21528
- const authorization = await deps.requestAuthorization();
21529
- if (!authorization.ok) {
21530
- return { ok: false, reason: "unavailable", detail: authorization.error };
21923
+ async function handleAgentGet(ctx, options, deps) {
21924
+ const timeout = parseFollowTimeout(options.timeout, defaultAgentFollowTimeoutSeconds);
21925
+ if (!timeout.ok) {
21926
+ return { error: timeout.error, exitCode: exitCodes.invalidArgs };
21531
21927
  }
21532
- await deps.onPrompt(authorization.value);
21533
- let state = {
21534
- intervalMs: authorization.value.intervalSec * 1000,
21535
- deadlineMs: deps.now() + authorization.value.expiresInSec * 1000
21536
- };
21928
+ const given = chooseGivenSessionId({
21929
+ argument: options.sessionArgument,
21930
+ flag: options.session
21931
+ });
21932
+ if (!given.ok) {
21933
+ return { error: given.error, exitCode: exitCodes.invalidArgs };
21934
+ }
21935
+ const sessionId = await resolveSessionId(given.sessionId, deps);
21936
+ if (sessionId === undefined) {
21937
+ return { error: agentMessages.noSession, exitCode: exitCodes.invalidArgs };
21938
+ }
21939
+ const read = await ctx.platformClient.callPublicApi(publicContractsV1.agent.get, { sessionId });
21940
+ if (!read.ok)
21941
+ return { exitCode: exitCodes.network, ...failureFields(read) };
21942
+ if (!options.follow) {
21943
+ renderSession(ctx, read.value);
21944
+ return;
21945
+ }
21946
+ ctx.ui.info(agentMessages.sessionAt(read.value.url));
21947
+ return followSession(ctx, {
21948
+ initial: read.value,
21949
+ repliesAtAnswer: undefined,
21950
+ sessionId,
21951
+ timeoutSeconds: timeout.seconds,
21952
+ workspaceId: options.workspaceId
21953
+ }, deps);
21954
+ }
21955
+
21956
+ // src/shell/agent/sessionStore.ts
21957
+ import { join as join14 } from "node:path";
21958
+
21959
+ // src/core/parseJson.ts
21960
+ function parseJson(text) {
21961
+ try {
21962
+ return JSON.parse(text);
21963
+ } catch {
21964
+ return;
21965
+ }
21966
+ }
21967
+
21968
+ // src/shell/jsonFile.ts
21969
+ var pendingWrites = 0;
21970
+ async function readJsonFile(fs, path, schema) {
21971
+ const contents = await fs.readFile(path).catch(() => {
21972
+ return;
21973
+ });
21974
+ if (contents === undefined)
21975
+ return;
21976
+ const parsed = schema.safeParse(parseJson(contents));
21977
+ return parsed.success ? parsed.data : undefined;
21978
+ }
21979
+ async function writeJsonFileAtomically(fs, path, value) {
21980
+ const pendingPath = `${path}.${String(process.pid)}.${String(++pendingWrites)}.tmp`;
21981
+ try {
21982
+ await fs.writeFile(pendingPath, `${JSON.stringify(value, undefined, 2)}
21983
+ `);
21984
+ await fs.rename(pendingPath, path);
21985
+ } finally {
21986
+ await fs.rm(pendingPath, { force: true }).catch(() => {});
21987
+ }
21988
+ }
21989
+
21990
+ // src/shell/agent/sessionStore.ts
21991
+ var storeFileName = "agent.json";
21992
+ var storeSchema = object({ lastSessionId: string2().optional() });
21993
+ function makeAgentSessionStore(options) {
21994
+ const directory = join14(options.cwd, qawolfDir);
21995
+ const path = join14(directory, storeFileName);
21996
+ return {
21997
+ async readLastSessionId() {
21998
+ const stored = await readJsonFile(options.fs, path, storeSchema);
21999
+ return stored?.lastSessionId;
22000
+ },
22001
+ async rememberSession(sessionId) {
22002
+ await options.fs.mkdir(directory, { recursive: true });
22003
+ await writeJsonFileAtomically(options.fs, path, {
22004
+ lastSessionId: sessionId
22005
+ });
22006
+ }
22007
+ };
22008
+ }
22009
+
22010
+ // src/domains/agent/deps.ts
22011
+ function makeAgentDeps(options) {
22012
+ return {
22013
+ env: options.env,
22014
+ now: Date.now,
22015
+ sleep,
22016
+ store: makeAgentSessionStore({ cwd: options.cwd, fs: options.fs })
22017
+ };
22018
+ }
22019
+
22020
+ // src/commands/agent/context.ts
22021
+ var sessionFlagDescription = `Session to target. Defaults to ${sessionIdEnvironmentVariable}, then the session this directory last started`;
22022
+ 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";
22023
+ function agentDeps(ctx) {
22024
+ return makeAgentDeps({
22025
+ cwd: process.cwd(),
22026
+ env: process.env,
22027
+ fs: ctx.fs
22028
+ });
22029
+ }
22030
+
22031
+ // src/commands/agent/get.register.ts
22032
+ var getExamples = `
22033
+ Examples:
22034
+ $ qawolf agent get
22035
+ $ qawolf agent get --follow
22036
+ $ qawolf agent get <sessionId> --follow
22037
+ $ qawolf agent get --session <sessionId>`;
22038
+ function registerAgentGetCommand(agent, signals) {
22039
+ 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, {
22040
+ follow: opts.follow,
22041
+ session: opts.session,
22042
+ sessionArgument: session,
22043
+ timeout: opts.timeout,
22044
+ workspaceId: opts.workspaceId
22045
+ }, agentDeps(ctx)))(opts, command));
22046
+ }
22047
+
22048
+ // src/commands/publicApi/environmentOptions.ts
22049
+ var environmentIdEnvironmentVariable = "QAWOLF_ENVIRONMENT";
22050
+ var optionEnvironmentVariables = new Map([
22051
+ ["environmentId", environmentIdEnvironmentVariable],
22052
+ ["public.run.create.aiTaskId", "QAWOLF_AI_TASK_ID"],
22053
+ ["public.run.create.chatSessionId", "QAWOLF_CHAT_SESSION_ID"]
22054
+ ]);
22055
+ function optionEnvironmentVariable(spec, path) {
22056
+ const dotted = path.join(".");
22057
+ return optionEnvironmentVariables.get(`${spec.trpcPath}.${dotted}`) ?? optionEnvironmentVariables.get(dotted);
22058
+ }
22059
+ var exclusiveOptions = new Map([
22060
+ ["public.run.create", { keep: "aiTaskId", drop: "chatSessionId" }]
22061
+ ]);
22062
+ function withoutExclusiveConflict(spec, command, options) {
22063
+ const pair = exclusiveOptions.get(spec.trpcPath);
22064
+ if (!pair)
22065
+ return options;
22066
+ if (options[pair.keep] === undefined || options[pair.drop] === undefined) {
22067
+ return options;
22068
+ }
22069
+ const fromEnvironment = (key) => command.getOptionValueSource(key) === "env";
22070
+ if (!fromEnvironment(pair.keep) && !fromEnvironment(pair.drop)) {
22071
+ return options;
22072
+ }
22073
+ const dropped = fromEnvironment(pair.drop) ? pair.drop : pair.keep;
22074
+ return Object.fromEntries(Object.entries(options).filter(([key]) => key !== dropped));
22075
+ }
22076
+
22077
+ // src/domains/agent/send.ts
22078
+ function parseInput(options) {
22079
+ return publicContractsV1.agent.send.input.safeParse({
22080
+ message: options.message,
22081
+ ...options.environmentId === undefined ? {} : { environmentId: options.environmentId },
22082
+ ...options.filePaths === undefined ? {} : { filePaths: options.filePaths },
22083
+ ...options.session === undefined ? {} : { sessionId: options.session },
22084
+ ...options.workspaceId === undefined ? {} : { workspaceId: options.workspaceId }
22085
+ });
22086
+ }
22087
+ async function handleAgentSend(ctx, options, deps) {
22088
+ const timeout = parseFollowTimeout(options.timeout, defaultAgentFollowTimeoutSeconds);
22089
+ if (!timeout.ok) {
22090
+ return { error: timeout.error, exitCode: exitCodes.invalidArgs };
22091
+ }
22092
+ const input = parseInput(options);
22093
+ if (!input.success) {
22094
+ return {
22095
+ error: prettifyError(input.error),
22096
+ exitCode: exitCodes.invalidArgs
22097
+ };
22098
+ }
22099
+ let repliesAtAnswer;
22100
+ if (options.follow && options.session !== undefined) {
22101
+ const before = await ctx.platformClient.callPublicApi(publicContractsV1.agent.get, { sessionId: options.session });
22102
+ if (before.ok)
22103
+ repliesAtAnswer = before.value.replies.length;
22104
+ }
22105
+ const sent = await ctx.platformClient.callPublicApi(publicContractsV1.agent.send, input.data);
22106
+ if (!sent.ok)
22107
+ return { exitCode: exitCodes.network, ...failureFields(sent) };
22108
+ ctx.ui.output(sent.value, agentMessages.started(sent.value.sessionId, sent.value.url));
22109
+ await deps.store.rememberSession(sent.value.sessionId).catch(() => {
22110
+ ctx.ui.warn(agentMessages.sessionNotRemembered(sent.value.sessionId));
22111
+ });
22112
+ if (!options.follow)
22113
+ return;
22114
+ return followSession(ctx, {
22115
+ initial: undefined,
22116
+ repliesAtAnswer,
22117
+ sessionId: sent.value.sessionId,
22118
+ timeoutSeconds: timeout.seconds,
22119
+ workspaceId: options.workspaceId
22120
+ }, deps);
22121
+ }
22122
+
22123
+ // src/commands/agent/send.register.ts
22124
+ var sendExamples = `
22125
+ Examples:
22126
+ $ qawolf agent send "cover the checkout journey"
22127
+ $ qawolf agent send "cover the checkout journey" --follow
22128
+ $ qawolf agent send "use the staging login test@example.com" --session <id>
22129
+ $ qawolf agent send "cover signup" --environment-id staging --follow
22130
+ $ qawolf agent send "cover every journey in the sheet" --file-paths uploads/journeys.csv --follow`;
22131
+ function registerAgentSendCommand(agent, signals) {
22132
+ 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, {
22133
+ environmentId: opts.environmentId,
22134
+ filePaths: opts.filePaths,
22135
+ follow: opts.follow,
22136
+ message,
22137
+ session: opts.session,
22138
+ timeout: opts.timeout,
22139
+ workspaceId: opts.workspaceId
22140
+ }, agentDeps(ctx)))(opts, command));
22141
+ }
22142
+
22143
+ // src/commands/agent/index.ts
22144
+ function registerAgentCommand(program, signals) {
22145
+ const agent = program.command("agent").description("Ask the QA Wolf AI for work and follow what it does");
22146
+ registerAgentSendCommand(agent, signals);
22147
+ registerAgentGetCommand(agent, signals);
22148
+ }
22149
+
22150
+ // src/commands/auth/loginApiKey.ts
22151
+ async function loginWithApiKey(ctx) {
22152
+ const result = await ctx.ui.password(authMessages.promptApiKey, "Set QAWOLF_API_KEY to authenticate in non-interactive environments.");
22153
+ if (!result.ok) {
22154
+ ctx.ui.cancel(authMessages.cancelled);
22155
+ return;
22156
+ }
22157
+ const apiKey = result.value.trim();
22158
+ if (!apiKey) {
22159
+ ctx.ui.cancel(authMessages.cancelled);
22160
+ return;
22161
+ }
22162
+ await ctx.ui.withProgress([
22163
+ {
22164
+ message: authMessages.verifying,
22165
+ task: async () => {
22166
+ const v = await validateApiKey({
22167
+ platformClient: createPlatformClient(apiKey, {
22168
+ baseUrl: ctx.apiBaseUrl,
22169
+ fetch: globalThis.fetch
22170
+ })
22171
+ });
22172
+ if (!v.valid)
22173
+ throw Error(v.error);
22174
+ }
22175
+ },
22176
+ {
22177
+ message: authMessages.storing,
22178
+ task: async () => saveApiKey(ctx.configDir, apiKey, ctx.fs)
22179
+ }
22180
+ ], ([, saveResult]) => {
22181
+ return saveResult.stored === "file" ? authMessages.storedFile : authMessages.storedKeychain;
22182
+ });
22183
+ ctx.ui.outro(authMessages.outroSuccess);
22184
+ }
22185
+
22186
+ // src/core/deviceAuth/pollState.ts
22187
+ var slowDownIncrementMs = 5000;
22188
+ var unreachableBackoffFactor = 2;
22189
+ function capToLifetime(delayMs, state, nowMs) {
22190
+ return Math.min(delayMs, Math.max(1, state.deadlineMs - nowMs));
22191
+ }
22192
+ function nextPollStep(state, response, nowMs) {
22193
+ if (response.kind === "tokens") {
22194
+ return { action: "done", tokens: response.tokens };
22195
+ }
22196
+ if (nowMs > state.deadlineMs) {
22197
+ return { action: "fail", reason: "timeout", detail: undefined };
22198
+ }
22199
+ switch (response.kind) {
22200
+ case "pending":
22201
+ return {
22202
+ action: "poll",
22203
+ delayMs: capToLifetime(state.intervalMs, state, nowMs),
22204
+ state
22205
+ };
22206
+ case "slow-down": {
22207
+ const slowed = {
22208
+ intervalMs: state.intervalMs + slowDownIncrementMs,
22209
+ deadlineMs: state.deadlineMs
22210
+ };
22211
+ return {
22212
+ action: "poll",
22213
+ delayMs: capToLifetime(slowed.intervalMs, slowed, nowMs),
22214
+ state: slowed
22215
+ };
22216
+ }
22217
+ case "denied":
22218
+ return { action: "fail", reason: "access-denied", detail: undefined };
22219
+ case "expired":
22220
+ return { action: "fail", reason: "expired", detail: undefined };
22221
+ case "unreachable": {
22222
+ const backedOff = {
22223
+ intervalMs: state.intervalMs * unreachableBackoffFactor,
22224
+ deadlineMs: state.deadlineMs
22225
+ };
22226
+ return {
22227
+ action: "poll",
22228
+ delayMs: capToLifetime(backedOff.intervalMs, backedOff, nowMs),
22229
+ state: backedOff
22230
+ };
22231
+ }
22232
+ case "error":
22233
+ return { action: "fail", reason: "network", detail: response.detail };
22234
+ }
22235
+ }
22236
+
22237
+ // src/domains/auth/deviceLogin.ts
22238
+ var cancelled = {
22239
+ ok: false,
22240
+ reason: "cancelled",
22241
+ detail: undefined
22242
+ };
22243
+ async function deviceLogin(deps) {
22244
+ if (deps.isCancelled())
22245
+ return cancelled;
22246
+ const authorization = await deps.requestAuthorization();
22247
+ if (!authorization.ok) {
22248
+ return { ok: false, reason: "unavailable", detail: authorization.error };
22249
+ }
22250
+ await deps.onPrompt(authorization.value);
22251
+ let state = {
22252
+ intervalMs: authorization.value.intervalSec * 1000,
22253
+ deadlineMs: deps.now() + authorization.value.expiresInSec * 1000
22254
+ };
21537
22255
  for (;; ) {
21538
22256
  if (deps.isCancelled())
21539
22257
  return cancelled;
@@ -21841,10 +22559,9 @@ async function selectWorkspace(deps) {
21841
22559
  }
21842
22560
 
21843
22561
  // src/domains/auth/store/saveWorkspaceId.ts
21844
- import { Entry as Entry6 } from "@napi-rs/keyring";
21845
22562
  function makeSaveWorkspaceIdDeps(configDir, fs) {
21846
22563
  return {
21847
- loadTokens: () => loadTokens(configDir, { EntryClass: Entry6, fs }),
22564
+ loadTokens: () => loadTokens(configDir, { loadEntryClass, fs }),
21848
22565
  saveTokens: (session) => saveTokens(configDir, session, fs)
21849
22566
  };
21850
22567
  }
@@ -22050,11 +22767,11 @@ async function handleLogin(ctx, deps = {}) {
22050
22767
  }
22051
22768
 
22052
22769
  // 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() {
22770
+ import { join as join15 } from "node:path";
22771
+ async function deleteFromKeychain2() {
22056
22772
  try {
22057
- new Entry7(service, tokensAccount).deletePassword();
22773
+ const Entry = await loadEntryClass();
22774
+ new Entry(service, tokensAccount).deletePassword();
22058
22775
  return "deleted";
22059
22776
  } catch {
22060
22777
  return "unavailable";
@@ -22062,7 +22779,7 @@ function deleteFromKeychain2() {
22062
22779
  }
22063
22780
  async function deleteFromFile2(configDir, fs) {
22064
22781
  try {
22065
- await fs.unlink(join14(configDir, tokensFile));
22782
+ await fs.unlink(join15(configDir, tokensFile));
22066
22783
  return "deleted";
22067
22784
  } catch (err) {
22068
22785
  if (isNoEntError(err))
@@ -22072,7 +22789,7 @@ async function deleteFromFile2(configDir, fs) {
22072
22789
  }
22073
22790
  async function deleteTokens(configDir, fs) {
22074
22791
  const [keychain, file] = await Promise.all([
22075
- Promise.resolve(deleteFromKeychain2()),
22792
+ deleteFromKeychain2(),
22076
22793
  deleteFromFile2(configDir, fs)
22077
22794
  ]);
22078
22795
  return { keychain, file };
@@ -22121,10 +22838,9 @@ async function handleLogout(ctx, deps = {}) {
22121
22838
  }
22122
22839
 
22123
22840
  // src/domains/auth/refreshStoredSession.ts
22124
- import { Entry as Entry8 } from "@napi-rs/keyring";
22125
22841
  function makeDefaultDeps2(fs) {
22126
22842
  return {
22127
- loadTokens: (configDir) => loadTokens(configDir, { EntryClass: Entry8, fs }),
22843
+ loadTokens: (configDir) => loadTokens(configDir, { loadEntryClass, fs }),
22128
22844
  resolveOauth: (configDir) => resolveOauthToken(configDir, makeOauthDeps(fs))
22129
22845
  };
22130
22846
  }
@@ -22223,72 +22939,927 @@ async function handleWhoami(ctx, deps = {}) {
22223
22939
  valid: false
22224
22940
  }, authMessages.whoami.authFailed(resolved.source, identity.error));
22225
22941
  }
22226
- return {
22227
- error: "invalid key",
22228
- ...identity.exitCode === undefined ? {} : { exitCode: identity.exitCode }
22229
- };
22942
+ return {
22943
+ error: "invalid key",
22944
+ ...identity.exitCode === undefined ? {} : { exitCode: identity.exitCode }
22945
+ };
22946
+ }
22947
+ const { value } = identity;
22948
+ if ("user" in value) {
22949
+ const { organization, user } = value;
22950
+ const found = value.organizations.flatMap((candidate) => candidate.workspaces.map((workspace) => ({
22951
+ organization: candidate.name,
22952
+ workspace: workspace.name,
22953
+ id: workspace.id
22954
+ }))).find((entry) => entry.id === resolved.workspaceId);
22955
+ const activeWorkspace = resolved.workspaceId ? authMessages.whoami.activeWorkspace(resolved.workspaceId, found) : undefined;
22956
+ if (ctx.ui.mode === "human") {
22957
+ ctx.ui.note(authMessages.whoami.userNote({
22958
+ organization,
22959
+ source: resolved.source,
22960
+ user,
22961
+ activeWorkspace,
22962
+ organizations: value.organizations
22963
+ }), authMessages.whoamiAuthenticated);
22964
+ ctx.ui.outro(authMessages.outroReady);
22965
+ } else {
22966
+ ctx.ui.output({
22967
+ authenticated: true,
22968
+ organization,
22969
+ source: resolved.source,
22970
+ user,
22971
+ workspaceId: resolved.workspaceId,
22972
+ organizations: value.organizations
22973
+ }, authMessages.whoami.authenticatedAs(user.email, resolved.source));
22974
+ }
22975
+ return;
22976
+ }
22977
+ if ("organization" in value) {
22978
+ const { organization } = value;
22979
+ if (ctx.ui.mode === "human") {
22980
+ ctx.ui.note(authMessages.whoami.organizationNote({
22981
+ organization,
22982
+ source: resolved.source
22983
+ }), authMessages.whoamiAuthenticated);
22984
+ ctx.ui.outro(authMessages.outroReady);
22985
+ } else {
22986
+ ctx.ui.output({
22987
+ authenticated: true,
22988
+ organization,
22989
+ source: resolved.source
22990
+ }, authMessages.whoami.authenticatedAs(organization.name, resolved.source));
22991
+ }
22992
+ return;
22993
+ }
22994
+ reportTeamIdentity(ctx, value, resolved.source);
22995
+ }
22996
+
22997
+ // src/commands/auth/index.ts
22998
+ function registerAuthCommand(program, signals) {
22999
+ const auth = program.command("auth").description("Manage authentication with QA Wolf");
23000
+ declareCommandKind(auth.command("login"), "local").description("Authenticate with QA Wolf in a browser or with an API key").action(withContext(signals, handleLogin));
23001
+ declareCommandKind(auth.command("logout"), "local").description("Remove stored credentials").action(withContext(signals, handleLogout));
23002
+ declareCommandKind(auth.command("switch"), "local").description("Choose which workspace to work in").action(withContext(signals, handleSwitchWorkspace));
23003
+ declareCommandKind(auth.command("whoami"), "read").description("Show authentication status").action(withContext(signals, handleWhoami));
23004
+ }
23005
+
23006
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel2Android14.js
23007
+ var androidPixel2Android14Target = {
23008
+ meta: {
23009
+ androidVersion: "34",
23010
+ deviceModel: "Pixel 2"
23011
+ },
23012
+ platform: "android",
23013
+ schemaVersion: 1
23014
+ };
23015
+
23016
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel9.js
23017
+ var androidPixel9Target = {
23018
+ meta: {
23019
+ androidVersion: "34",
23020
+ deviceModel: "Pixel 9"
23021
+ },
23022
+ platform: "android",
23023
+ schemaVersion: 1
23024
+ };
23025
+
23026
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel9Android14.js
23027
+ var androidPixel9Android14Target = {
23028
+ meta: {
23029
+ androidVersion: "34",
23030
+ deviceModel: "Pixel 9"
23031
+ },
23032
+ platform: "android",
23033
+ schemaVersion: 1
23034
+ };
23035
+
23036
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel9Android15.js
23037
+ var androidPixel9Android15Target = {
23038
+ meta: {
23039
+ androidVersion: "35",
23040
+ deviceModel: "Pixel 9"
23041
+ },
23042
+ platform: "android",
23043
+ schemaVersion: 1
23044
+ };
23045
+
23046
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel9Android16.js
23047
+ var androidPixel9Android16Target = {
23048
+ meta: {
23049
+ androidVersion: "36",
23050
+ deviceModel: "Pixel 9"
23051
+ },
23052
+ platform: "android",
23053
+ schemaVersion: 1
23054
+ };
23055
+
23056
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixel.js
23057
+ var androidPixelTarget = {
23058
+ meta: {
23059
+ androidVersion: "34",
23060
+ deviceModel: "Pixel 2"
23061
+ },
23062
+ platform: "android",
23063
+ schemaVersion: 1
23064
+ };
23065
+
23066
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidPixelTabletAndroid14.js
23067
+ var androidPixelTabletAndroid14Target = {
23068
+ meta: {
23069
+ androidVersion: "34",
23070
+ deviceModel: "Pixel Tablet"
23071
+ },
23072
+ platform: "android",
23073
+ schemaVersion: 1
23074
+ };
23075
+
23076
+ // node_modules/@qawolf/flow-targets/dist/presets/android/androidTablet.js
23077
+ var androidTabletTarget = {
23078
+ meta: {
23079
+ androidVersion: "34",
23080
+ deviceModel: "Pixel Tablet"
23081
+ },
23082
+ platform: "android",
23083
+ schemaVersion: 1
23084
+ };
23085
+
23086
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/allowlistedIphone.js
23087
+ var allowlistedIphoneTarget = {
23088
+ meta: {
23089
+ deviceModel: "iPhone 15",
23090
+ iosVersion: "26",
23091
+ targetDevices: "Allowlisted Device"
23092
+ },
23093
+ platform: "ios",
23094
+ schemaVersion: 1
23095
+ };
23096
+
23097
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/appleIdEnabledIpad.js
23098
+ var appleIdEnabledIpadTarget = {
23099
+ meta: {
23100
+ appleId: true,
23101
+ deviceModel: "iPad 11",
23102
+ iosVersion: "18"
23103
+ },
23104
+ platform: "ios",
23105
+ schemaVersion: 1
23106
+ };
23107
+
23108
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/appleIdEnabledIphone.js
23109
+ var appleIdEnabledIphoneTarget = {
23110
+ meta: {
23111
+ appleId: true,
23112
+ deviceModel: "iPhone 15",
23113
+ iosVersion: "26"
23114
+ },
23115
+ platform: "ios",
23116
+ schemaVersion: 1
23117
+ };
23118
+
23119
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/ipad11ios18.js
23120
+ var ipad11ios18Target = {
23121
+ meta: {
23122
+ deviceModel: "iPad 11",
23123
+ iosVersion: "18"
23124
+ },
23125
+ platform: "ios",
23126
+ schemaVersion: 1
23127
+ };
23128
+
23129
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/ipad11ios18AppleId.js
23130
+ var ipad11ios18AppleIdTarget = {
23131
+ meta: {
23132
+ appleId: true,
23133
+ deviceModel: "iPad 11",
23134
+ iosVersion: "18"
23135
+ },
23136
+ platform: "ios",
23137
+ schemaVersion: 1
23138
+ };
23139
+
23140
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/ipad11ios26.js
23141
+ var ipad11ios26Target = {
23142
+ meta: {
23143
+ deviceModel: "iPad 11",
23144
+ iosVersion: "26"
23145
+ },
23146
+ platform: "ios",
23147
+ schemaVersion: 1
23148
+ };
23149
+
23150
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/ipad.js
23151
+ var ipadTarget = {
23152
+ meta: {
23153
+ deviceModel: "iPad 11",
23154
+ iosVersion: "18"
23155
+ },
23156
+ platform: "ios",
23157
+ schemaVersion: 1
23158
+ };
23159
+
23160
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios17.js
23161
+ var iphone15ios17Target = {
23162
+ meta: {
23163
+ deviceModel: "iPhone 15",
23164
+ iosVersion: "17"
23165
+ },
23166
+ platform: "ios",
23167
+ schemaVersion: 1
23168
+ };
23169
+
23170
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios18.js
23171
+ var iphone15ios18Target = {
23172
+ meta: {
23173
+ deviceModel: "iPhone 15",
23174
+ iosVersion: "18"
23175
+ },
23176
+ platform: "ios",
23177
+ schemaVersion: 1
23178
+ };
23179
+
23180
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios26.js
23181
+ var iphone15ios26Target = {
23182
+ meta: {
23183
+ deviceModel: "iPhone 15",
23184
+ iosVersion: "26"
23185
+ },
23186
+ platform: "ios",
23187
+ schemaVersion: 1
23188
+ };
23189
+
23190
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios26Allowlisted.js
23191
+ var iphone15ios26AllowlistedTarget = {
23192
+ meta: {
23193
+ deviceModel: "iPhone 15",
23194
+ iosVersion: "26",
23195
+ targetDevices: "Allowlisted Device"
23196
+ },
23197
+ platform: "ios",
23198
+ schemaVersion: 1
23199
+ };
23200
+
23201
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios26AppleId.js
23202
+ var iphone15ios26AppleIdTarget = {
23203
+ meta: {
23204
+ appleId: true,
23205
+ deviceModel: "iPhone 15",
23206
+ iosVersion: "26"
23207
+ },
23208
+ platform: "ios",
23209
+ schemaVersion: 1
23210
+ };
23211
+
23212
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone15ios26Private.js
23213
+ var iphone15ios26PrivateTarget = {
23214
+ meta: {
23215
+ deviceModel: "iPhone 15",
23216
+ iosVersion: "26",
23217
+ targetDevices: "Team Dedicated Device"
23218
+ },
23219
+ platform: "ios",
23220
+ schemaVersion: 1
23221
+ };
23222
+
23223
+ // node_modules/@qawolf/flow-targets/dist/presets/ios/iphone17ios26.js
23224
+ var iphone17ios26Target = {
23225
+ meta: {
23226
+ deviceModel: "iPhone 17",
23227
+ iosVersion: "26"
23228
+ },
23229
+ platform: "ios",
23230
+ schemaVersion: 1
23231
+ };
23232
+
23233
+ // node_modules/@qawolf/flow-targets/dist/presets/web/basic.js
23234
+ var webBasicTarget = {
23235
+ meta: "legacy",
23236
+ platform: "web",
23237
+ schemaVersion: 1
23238
+ };
23239
+
23240
+ // node_modules/@qawolf/flow-targets/dist/presets/web/chrome.js
23241
+ var webChromeTarget = {
23242
+ meta: {
23243
+ defaultBrowser: {
23244
+ name: "chrome"
23245
+ },
23246
+ gpuEnabled: false
23247
+ },
23248
+ platform: "web",
23249
+ schemaVersion: 1
23250
+ };
23251
+
23252
+ // node_modules/@qawolf/flow-targets/dist/presets/web/chromeWithGpu.js
23253
+ var webChromeWithGpuTarget = {
23254
+ meta: {
23255
+ defaultBrowser: {
23256
+ name: "chrome"
23257
+ },
23258
+ gpuEnabled: true
23259
+ },
23260
+ platform: "web",
23261
+ schemaVersion: 1
23262
+ };
23263
+
23264
+ // node_modules/@qawolf/flow-targets/dist/presets/web/electron.js
23265
+ var webElectronTarget = {
23266
+ meta: {
23267
+ gpuEnabled: false,
23268
+ kind: "electron"
23269
+ },
23270
+ platform: "web",
23271
+ schemaVersion: 1
23272
+ };
23273
+
23274
+ // node_modules/@qawolf/flow-targets/dist/presets/web/firefox.js
23275
+ var webFirefoxTarget = {
23276
+ meta: {
23277
+ defaultBrowser: {
23278
+ name: "firefox"
23279
+ },
23280
+ gpuEnabled: false
23281
+ },
23282
+ platform: "web",
23283
+ schemaVersion: 1
23284
+ };
23285
+
23286
+ // node_modules/@qawolf/flow-targets/dist/presets/web/firefoxWithGpu.js
23287
+ var webFirefoxWithGpuTarget = {
23288
+ meta: {
23289
+ defaultBrowser: {
23290
+ name: "firefox"
23291
+ },
23292
+ gpuEnabled: true
23293
+ },
23294
+ platform: "web",
23295
+ schemaVersion: 1
23296
+ };
23297
+
23298
+ // node_modules/@qawolf/flow-targets/dist/presets/web/safari.js
23299
+ var webSafariTarget = {
23300
+ meta: {
23301
+ defaultBrowser: {
23302
+ name: "safari"
23303
+ },
23304
+ gpuEnabled: false
23305
+ },
23306
+ platform: "web",
23307
+ schemaVersion: 1
23308
+ };
23309
+
23310
+ // node_modules/@qawolf/flow-targets/dist/presets/web/safariWithGpu.js
23311
+ var webSafariWithGpuTarget = {
23312
+ meta: {
23313
+ defaultBrowser: {
23314
+ name: "safari"
23315
+ },
23316
+ gpuEnabled: true
23317
+ },
23318
+ platform: "web",
23319
+ schemaVersion: 1
23320
+ };
23321
+
23322
+ // node_modules/@qawolf/flow-targets/dist/presets/index.js
23323
+ var androidPresetLiteralRaw = [
23324
+ "Android - Pixel",
23325
+ "Android - Pixel 2 (Android 14)",
23326
+ "Android - Pixel 9",
23327
+ "Android - Pixel 9 (Android 14)",
23328
+ "Android - Pixel 9 (Android 15)",
23329
+ "Android - Pixel 9 (Android 16)",
23330
+ "Android - Pixel Tablet (Android 14)",
23331
+ "Android - Tablet"
23332
+ ];
23333
+ var androidPresetLiteralSchema = _enum(androidPresetLiteralRaw);
23334
+ var iosPresetLiteralRaw = [
23335
+ "iOS - Allowlisted iPhone",
23336
+ "iOS - Apple ID Enabled iPad",
23337
+ "iOS - Apple ID Enabled iPhone",
23338
+ "iOS - iPad",
23339
+ "iOS - iPad 11 (iOS 18)",
23340
+ "iOS - iPad 11 (iOS 18) (Apple ID)",
23341
+ "iOS - iPad 11 (iOS 26)",
23342
+ "iOS - iPhone 15 (iOS 17)",
23343
+ "iOS - iPhone 15 (iOS 18)",
23344
+ "iOS - iPhone 15 (iOS 26)",
23345
+ "iOS - iPhone 15 (iOS 26) (Apple ID)",
23346
+ "iOS - iPhone 15 (iOS 26) (allowlisted)",
23347
+ "iOS - iPhone 15 (iOS 26) (private)",
23348
+ "iOS - iPhone 17 (iOS 26)"
23349
+ ];
23350
+ var iosPresetLiteralSchema = _enum(iosPresetLiteralRaw);
23351
+ var webPresetLiteralRaw = [
23352
+ "Basic",
23353
+ "Electron",
23354
+ "Web - Chrome",
23355
+ "Web - Chrome (GPU)",
23356
+ "Web - Firefox",
23357
+ "Web - Firefox (GPU)",
23358
+ "Web - Safari",
23359
+ "Web - Safari (GPU)"
23360
+ ];
23361
+ var webPresetLiteralSchema = _enum(webPresetLiteralRaw);
23362
+ var presetLiteralsSchema = union([
23363
+ androidPresetLiteralSchema,
23364
+ iosPresetLiteralSchema,
23365
+ webPresetLiteralSchema
23366
+ ]);
23367
+ var presetTargetMap = {
23368
+ "Android - Pixel": androidPixelTarget,
23369
+ "Android - Pixel 2 (Android 14)": androidPixel2Android14Target,
23370
+ "Android - Pixel 9": androidPixel9Target,
23371
+ "Android - Pixel 9 (Android 14)": androidPixel9Android14Target,
23372
+ "Android - Pixel 9 (Android 15)": androidPixel9Android15Target,
23373
+ "Android - Pixel 9 (Android 16)": androidPixel9Android16Target,
23374
+ "Android - Pixel Tablet (Android 14)": androidPixelTabletAndroid14Target,
23375
+ "Android - Tablet": androidTabletTarget,
23376
+ Basic: webBasicTarget,
23377
+ Electron: webElectronTarget,
23378
+ "iOS - Allowlisted iPhone": allowlistedIphoneTarget,
23379
+ "iOS - Apple ID Enabled iPad": appleIdEnabledIpadTarget,
23380
+ "iOS - Apple ID Enabled iPhone": appleIdEnabledIphoneTarget,
23381
+ "iOS - iPad": ipadTarget,
23382
+ "iOS - iPad 11 (iOS 18)": ipad11ios18Target,
23383
+ "iOS - iPad 11 (iOS 18) (Apple ID)": ipad11ios18AppleIdTarget,
23384
+ "iOS - iPad 11 (iOS 26)": ipad11ios26Target,
23385
+ "iOS - iPhone 15 (iOS 17)": iphone15ios17Target,
23386
+ "iOS - iPhone 15 (iOS 18)": iphone15ios18Target,
23387
+ "iOS - iPhone 15 (iOS 26)": iphone15ios26Target,
23388
+ "iOS - iPhone 15 (iOS 26) (allowlisted)": iphone15ios26AllowlistedTarget,
23389
+ "iOS - iPhone 15 (iOS 26) (Apple ID)": iphone15ios26AppleIdTarget,
23390
+ "iOS - iPhone 15 (iOS 26) (private)": iphone15ios26PrivateTarget,
23391
+ "iOS - iPhone 17 (iOS 26)": iphone17ios26Target,
23392
+ "Web - Chrome": webChromeTarget,
23393
+ "Web - Chrome (GPU)": webChromeWithGpuTarget,
23394
+ "Web - Firefox": webFirefoxTarget,
23395
+ "Web - Firefox (GPU)": webFirefoxWithGpuTarget,
23396
+ "Web - Safari": webSafariTarget,
23397
+ "Web - Safari (GPU)": webSafariWithGpuTarget
23398
+ };
23399
+
23400
+ // node_modules/@qawolf/flow-targets/dist/schemas/shared/screenDimensions.js
23401
+ var screenDimensions = {
23402
+ "iPad 11": { screenHeight: 2360, screenWidth: 1640 },
23403
+ "iPhone 15": { screenHeight: 2556, screenWidth: 1179 },
23404
+ "iPhone 15 Plus": { screenHeight: 2796, screenWidth: 1290 },
23405
+ "iPhone 15 Pro Max": { screenHeight: 2868, screenWidth: 1320 },
23406
+ "iPhone 16 Plus": { screenHeight: 2796, screenWidth: 1290 },
23407
+ "iPhone 17": { screenHeight: 2622, screenWidth: 1206 },
23408
+ "Pixel 2": { screenHeight: 1920, screenWidth: 1080 },
23409
+ "Pixel 9": { screenHeight: 2424, screenWidth: 1080 },
23410
+ "Pixel Tablet": { screenHeight: 1600, screenWidth: 2560 },
23411
+ playwright_default: { screenHeight: 804, screenWidth: 1288 }
23412
+ };
23413
+
23414
+ // node_modules/@qawolf/flow-targets/dist/schemas/shared/platforms.js
23415
+ var platformsSchema = _enum(["web", "android", "ios"]);
23416
+
23417
+ // node_modules/@qawolf/flow-targets/dist/schemas/shared/runnerNames.js
23418
+ var runnerNamesSchema = _enum([
23419
+ "node20Basic",
23420
+ "classic",
23421
+ "node20GpuWithPlaywright",
23422
+ "node20WithPlaywright",
23423
+ "node20WithAndroid",
23424
+ "node20WithIos"
23425
+ ]);
23426
+
23427
+ // node_modules/@qawolf/flow-targets/dist/schemas/shared/baseSchemas.js
23428
+ var baseInputSchema = object({
23429
+ platform: platformsSchema,
23430
+ schemaVersion: int().positive()
23431
+ });
23432
+ var baseOutputSchema = baseInputSchema.extend({
23433
+ runnerName: runnerNamesSchema
23434
+ });
23435
+
23436
+ // node_modules/@qawolf/flow-targets/dist/schemas/android/v1/supportedModelsSchema.js
23437
+ var supportedModelsInputSchema = _enum([
23438
+ "Pixel 2",
23439
+ "Pixel Tablet",
23440
+ "Pixel 9"
23441
+ ]);
23442
+ var supportedModelsOutputSchema = _enum([
23443
+ "pixel_2",
23444
+ "pixel_tablet",
23445
+ "pixel_9"
23446
+ ]);
23447
+ var supportedModelsSchema = codec(supportedModelsInputSchema, supportedModelsOutputSchema, {
23448
+ decode(deviceModel) {
23449
+ switch (deviceModel) {
23450
+ case "Pixel 2":
23451
+ return "pixel_2";
23452
+ case "Pixel 9":
23453
+ return "pixel_9";
23454
+ case "Pixel Tablet":
23455
+ return "pixel_tablet";
23456
+ default:
23457
+ throw Error(`Unsupported device model: ${deviceModel}`);
23458
+ }
23459
+ },
23460
+ encode(deviceModel) {
23461
+ switch (deviceModel) {
23462
+ case "pixel_2":
23463
+ return "Pixel 2";
23464
+ case "pixel_9":
23465
+ return "Pixel 9";
23466
+ case "pixel_tablet":
23467
+ return "Pixel Tablet";
23468
+ default:
23469
+ throw Error(`Unsupported device model: ${deviceModel}`);
23470
+ }
23471
+ }
23472
+ });
23473
+
23474
+ // node_modules/@qawolf/flow-targets/dist/schemas/android/v1/input.js
23475
+ var supportedVersionsSchema = _enum(["34", "35", "36"]);
23476
+ var androidMetaInputSchema = discriminatedUnion("deviceModel", [
23477
+ object({
23478
+ androidVersion: supportedVersionsSchema.extract(["34"]),
23479
+ deviceModel: supportedModelsInputSchema.extract(["Pixel 2"])
23480
+ }),
23481
+ object({
23482
+ androidVersion: supportedVersionsSchema.extract(["34"]),
23483
+ deviceModel: supportedModelsInputSchema.extract(["Pixel Tablet"])
23484
+ }),
23485
+ object({
23486
+ androidVersion: supportedVersionsSchema.extract(["34", "35", "36"]),
23487
+ deviceModel: supportedModelsInputSchema.extract(["Pixel 9"])
23488
+ })
23489
+ ]);
23490
+ var androidTargetInputSchema = baseInputSchema.extend({
23491
+ meta: androidMetaInputSchema,
23492
+ platform: platformsSchema.extract(["android"]),
23493
+ schemaVersion: literal(1)
23494
+ });
23495
+
23496
+ // node_modules/@qawolf/flow-targets/dist/schemas/android/v1/output.js
23497
+ var androidTargetOutputSchema = baseOutputSchema.extend({
23498
+ ...androidTargetInputSchema.shape,
23499
+ meta: object({
23500
+ androidVersion: supportedVersionsSchema,
23501
+ deviceModel: supportedModelsOutputSchema,
23502
+ screenHeight: number2(),
23503
+ screenWidth: number2()
23504
+ }),
23505
+ runnerName: runnerNamesSchema.extract(["node20WithAndroid"])
23506
+ });
23507
+
23508
+ // node_modules/@qawolf/flow-targets/dist/schemas/android/v1/index.js
23509
+ var androidExecutionTargetSchema = codec(androidTargetInputSchema, androidTargetOutputSchema, {
23510
+ decode(value) {
23511
+ return {
23512
+ ...value,
23513
+ meta: {
23514
+ ...value.meta,
23515
+ deviceModel: supportedModelsSchema.decode(value.meta.deviceModel),
23516
+ ...screenDimensions[value.meta.deviceModel]
23517
+ },
23518
+ runnerName: "node20WithAndroid"
23519
+ };
23520
+ },
23521
+ encode(value) {
23522
+ return androidTargetInputSchema.parse({
23523
+ ...value,
23524
+ meta: {
23525
+ ...value.meta,
23526
+ deviceModel: supportedModelsSchema.encode(value.meta.deviceModel)
23527
+ }
23528
+ });
23529
+ }
23530
+ });
23531
+
23532
+ // node_modules/@qawolf/flow-targets/dist/schemas/android/index.js
23533
+ var androidExecutionTargetInputSchema = discriminatedUnion("schemaVersion", [androidTargetInputSchema]);
23534
+ var androidExecutionTargetOutputSchema = discriminatedUnion("schemaVersion", [androidTargetOutputSchema]);
23535
+ var androidExecutionTargetSchema2 = discriminatedUnion("schemaVersion", [androidExecutionTargetSchema]);
23536
+
23537
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/v1/supportedModelsSchema.js
23538
+ var supportedModelsInputSchema2 = _enum([
23539
+ "iPhone 15",
23540
+ "iPhone 15 Plus",
23541
+ "iPhone 15 Pro Max",
23542
+ "iPhone 16 Plus",
23543
+ "iPhone 17",
23544
+ "iPad 11"
23545
+ ]);
23546
+ var supportedModelsOutputSchema2 = _enum([
23547
+ "iphone15_4",
23548
+ "iphone15_5",
23549
+ "iphone16_2",
23550
+ "iphone17_4",
23551
+ "iphone18_3",
23552
+ "ipad15_7"
23553
+ ]);
23554
+ var supportedModelsSchema2 = codec(supportedModelsInputSchema2, supportedModelsOutputSchema2, {
23555
+ decode(deviceModel) {
23556
+ switch (deviceModel) {
23557
+ case "iPad 11":
23558
+ return "ipad15_7";
23559
+ case "iPhone 15":
23560
+ return "iphone15_4";
23561
+ case "iPhone 15 Plus":
23562
+ return "iphone15_5";
23563
+ case "iPhone 15 Pro Max":
23564
+ return "iphone16_2";
23565
+ case "iPhone 16 Plus":
23566
+ return "iphone17_4";
23567
+ case "iPhone 17":
23568
+ return "iphone18_3";
23569
+ default:
23570
+ throw Error(`Unsupported device model: ${deviceModel}`);
23571
+ }
23572
+ },
23573
+ encode(value) {
23574
+ switch (value) {
23575
+ case "ipad15_7":
23576
+ return "iPad 11";
23577
+ case "iphone15_4":
23578
+ return "iPhone 15";
23579
+ case "iphone15_5":
23580
+ return "iPhone 15 Plus";
23581
+ case "iphone16_2":
23582
+ return "iPhone 15 Pro Max";
23583
+ case "iphone17_4":
23584
+ return "iPhone 16 Plus";
23585
+ case "iphone18_3":
23586
+ return "iPhone 17";
23587
+ default:
23588
+ throw Error(`Unsupported device model: ${value}`);
23589
+ }
22230
23590
  }
22231
- const { value } = identity;
22232
- if ("user" in value) {
22233
- const { organization, user } = value;
22234
- const found = value.organizations.flatMap((candidate) => candidate.workspaces.map((workspace) => ({
22235
- organization: candidate.name,
22236
- workspace: workspace.name,
22237
- id: workspace.id
22238
- }))).find((entry) => entry.id === resolved.workspaceId);
22239
- const activeWorkspace = resolved.workspaceId ? authMessages.whoami.activeWorkspace(resolved.workspaceId, found) : undefined;
22240
- if (ctx.ui.mode === "human") {
22241
- ctx.ui.note(authMessages.whoami.userNote({
22242
- organization,
22243
- source: resolved.source,
22244
- user,
22245
- activeWorkspace,
22246
- organizations: value.organizations
22247
- }), authMessages.whoamiAuthenticated);
22248
- ctx.ui.outro(authMessages.outroReady);
22249
- } else {
22250
- ctx.ui.output({
22251
- authenticated: true,
22252
- organization,
22253
- source: resolved.source,
22254
- user,
22255
- workspaceId: resolved.workspaceId,
22256
- organizations: value.organizations
22257
- }, authMessages.whoami.authenticatedAs(user.email, resolved.source));
23591
+ });
23592
+
23593
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/v1/supportedTargetDevicesSchema.js
23594
+ var supportedTargetDevicesInputSchema = _enum([
23595
+ "Any Device",
23596
+ "Allowlisted Device",
23597
+ "Team Dedicated Device"
23598
+ ]);
23599
+ var supportedTargetDevicesOutputSchema = _enum([
23600
+ "anyDevice",
23601
+ "allowlistedDevice",
23602
+ "teamDedicatedDevice"
23603
+ ]);
23604
+ var supportedTargetDevicesSchema = codec(supportedTargetDevicesInputSchema, supportedTargetDevicesOutputSchema, {
23605
+ decode(targetDevices) {
23606
+ switch (targetDevices) {
23607
+ case "Allowlisted Device":
23608
+ return "allowlistedDevice";
23609
+ case "Any Device":
23610
+ return "anyDevice";
23611
+ case "Team Dedicated Device":
23612
+ return "teamDedicatedDevice";
23613
+ default:
23614
+ throw Error(`Unsupported target devices: ${targetDevices}`);
23615
+ }
23616
+ },
23617
+ encode(targetDevices) {
23618
+ switch (targetDevices) {
23619
+ case "allowlistedDevice":
23620
+ return "Allowlisted Device";
23621
+ case "anyDevice":
23622
+ return "Any Device";
23623
+ case "teamDedicatedDevice":
23624
+ return "Team Dedicated Device";
23625
+ default:
23626
+ throw Error(`Unsupported target devices: ${targetDevices}`);
22258
23627
  }
22259
- return;
22260
23628
  }
22261
- if ("organization" in value) {
22262
- const { organization } = value;
22263
- if (ctx.ui.mode === "human") {
22264
- ctx.ui.note(authMessages.whoami.organizationNote({
22265
- organization,
22266
- source: resolved.source
22267
- }), authMessages.whoamiAuthenticated);
22268
- ctx.ui.outro(authMessages.outroReady);
23629
+ });
23630
+
23631
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/v1/input.js
23632
+ var supportedVersionsInputSchema = _enum(["17", "18", "26"]);
23633
+ var baseMetaInputSchema = object({
23634
+ appleId: union([email2(), boolean2()]).optional(),
23635
+ deviceUdid: string2().optional(),
23636
+ targetDevices: supportedTargetDevicesInputSchema.default("Any Device")
23637
+ });
23638
+ var iosMetaInputSchema = discriminatedUnion("deviceModel", [
23639
+ baseMetaInputSchema.extend({
23640
+ deviceModel: supportedModelsInputSchema2.extract(["iPhone 15"]),
23641
+ iosVersion: supportedVersionsInputSchema.extract(["17", "18", "26"])
23642
+ }),
23643
+ baseMetaInputSchema.extend({
23644
+ deviceModel: supportedModelsInputSchema2.extract(["iPhone 15 Plus"]),
23645
+ iosVersion: supportedVersionsInputSchema.extract(["17"])
23646
+ }),
23647
+ baseMetaInputSchema.extend({
23648
+ deviceModel: supportedModelsInputSchema2.extract(["iPhone 15 Pro Max"]),
23649
+ iosVersion: supportedVersionsInputSchema.extract(["17"])
23650
+ }),
23651
+ baseMetaInputSchema.extend({
23652
+ deviceModel: supportedModelsInputSchema2.extract(["iPhone 16 Plus"]),
23653
+ iosVersion: supportedVersionsInputSchema.extract(["26"])
23654
+ }),
23655
+ baseMetaInputSchema.extend({
23656
+ deviceModel: supportedModelsInputSchema2.extract(["iPhone 17"]),
23657
+ iosVersion: supportedVersionsInputSchema.extract(["26"])
23658
+ }),
23659
+ baseMetaInputSchema.extend({
23660
+ deviceModel: supportedModelsInputSchema2.extract(["iPad 11"]),
23661
+ iosVersion: supportedVersionsInputSchema.extract(["18", "26"])
23662
+ })
23663
+ ]);
23664
+ var iosTargetInputSchema = baseInputSchema.extend({
23665
+ meta: iosMetaInputSchema,
23666
+ platform: platformsSchema.extract(["ios"]),
23667
+ schemaVersion: literal(1)
23668
+ });
23669
+
23670
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/v1/output.js
23671
+ var iosTargetOutputSchema = baseOutputSchema.extend({
23672
+ ...iosTargetInputSchema.shape,
23673
+ meta: object({
23674
+ appleId: union([email2(), boolean2()]).optional(),
23675
+ deviceModel: supportedModelsOutputSchema2,
23676
+ deviceUdid: string2().optional(),
23677
+ iosVersion: supportedVersionsInputSchema,
23678
+ screenHeight: number2(),
23679
+ screenWidth: number2(),
23680
+ targetDevices: supportedTargetDevicesOutputSchema
23681
+ }),
23682
+ runnerName: runnerNamesSchema.extract(["node20WithIos"])
23683
+ });
23684
+
23685
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/v1/index.js
23686
+ var iosExecutionTargetSchema = codec(iosTargetInputSchema, iosTargetOutputSchema, {
23687
+ decode(value) {
23688
+ return {
23689
+ ...value,
23690
+ meta: {
23691
+ ...value.meta,
23692
+ appleId: value.meta.appleId || undefined,
23693
+ deviceModel: supportedModelsSchema2.decode(value.meta.deviceModel),
23694
+ ...screenDimensions[value.meta.deviceModel],
23695
+ targetDevices: supportedTargetDevicesSchema.decode(value.meta.targetDevices)
23696
+ },
23697
+ runnerName: "node20WithIos"
23698
+ };
23699
+ },
23700
+ encode(value) {
23701
+ return iosTargetInputSchema.parse({
23702
+ ...value,
23703
+ meta: {
23704
+ ...value.meta,
23705
+ deviceModel: supportedModelsSchema2.encode(value.meta.deviceModel),
23706
+ targetDevices: supportedTargetDevicesSchema.encode(value.meta.targetDevices)
23707
+ }
23708
+ });
23709
+ }
23710
+ });
23711
+
23712
+ // node_modules/@qawolf/flow-targets/dist/schemas/ios/index.js
23713
+ var iosExecutionTargetInputSchema = discriminatedUnion("schemaVersion", [iosTargetInputSchema]);
23714
+ var iosExecutionTargetOutputSchema = discriminatedUnion("schemaVersion", [iosTargetOutputSchema]);
23715
+ var iosExecutionTargetSchema2 = discriminatedUnion("schemaVersion", [
23716
+ iosExecutionTargetSchema
23717
+ ]);
23718
+
23719
+ // node_modules/@qawolf/flow-targets/dist/schemas/web/v1/input.js
23720
+ var supportedBrowserNamesSchema = _enum(["chrome", "firefox", "safari"]);
23721
+ var defaultScreenDimensions = {
23722
+ height: 804,
23723
+ width: 1288
23724
+ };
23725
+ var webBrowserInputSchema = object({
23726
+ name: supportedBrowserNamesSchema,
23727
+ screenHeight: int().positive().default(defaultScreenDimensions.height),
23728
+ screenWidth: int().positive().default(defaultScreenDimensions.width)
23729
+ });
23730
+ var defaultBrowserInfoSchema = object({
23731
+ browser: optional(never()),
23732
+ defaultBrowser: webBrowserInputSchema,
23733
+ gpuEnabled: boolean2(),
23734
+ kind: literal("browser").default("browser")
23735
+ });
23736
+ var legacyBrowserInfoSchema = object({
23737
+ browser: webBrowserInputSchema,
23738
+ defaultBrowser: optional(never()),
23739
+ gpuEnabled: boolean2(),
23740
+ kind: literal("browser").default("browser")
23741
+ });
23742
+ var electronInfoSchema = object({
23743
+ browser: optional(never()),
23744
+ defaultBrowser: optional(never()),
23745
+ gpuEnabled: literal(false).default(false),
23746
+ kind: literal("electron")
23747
+ });
23748
+ var browserInfoSchema = union([
23749
+ defaultBrowserInfoSchema,
23750
+ legacyBrowserInfoSchema
23751
+ ]);
23752
+ var webMetaSchema = union([browserInfoSchema, electronInfoSchema]);
23753
+ var webTargetInputSchema = baseInputSchema.extend({
23754
+ meta: union([webMetaSchema, literal("legacy")]),
23755
+ platform: platformsSchema.extract(["web"]),
23756
+ schemaVersion: literal(1)
23757
+ });
23758
+ function getWebBrowserInfo(meta) {
23759
+ if ("defaultBrowser" in meta && meta.defaultBrowser)
23760
+ return webBrowserInputSchema.parse(meta.defaultBrowser);
23761
+ if ("browser" in meta && meta.browser)
23762
+ return webBrowserInputSchema.parse(meta.browser);
23763
+ throw Error("Expected either defaultBrowser or browser in web meta");
23764
+ }
23765
+
23766
+ // node_modules/@qawolf/flow-targets/dist/schemas/web/v1/webRunnerNames.js
23767
+ var webRunnerNamesSchema = runnerNamesSchema.extract([
23768
+ "node20Basic",
23769
+ "node20WithPlaywright",
23770
+ "node20GpuWithPlaywright"
23771
+ ]);
23772
+
23773
+ // node_modules/@qawolf/flow-targets/dist/schemas/web/v1/output.js
23774
+ var webOutputMetaSchema = union([
23775
+ object({
23776
+ defaultBrowser: webBrowserInputSchema,
23777
+ gpuEnabled: boolean2(),
23778
+ kind: literal("browser").default("browser")
23779
+ }),
23780
+ object({
23781
+ browser: webBrowserInputSchema,
23782
+ gpuEnabled: boolean2(),
23783
+ kind: literal("browser").default("browser")
23784
+ }),
23785
+ object({
23786
+ gpuEnabled: literal(false),
23787
+ kind: literal("electron")
23788
+ }),
23789
+ literal("legacy")
23790
+ ]);
23791
+ var webTargetOutputSchema = baseOutputSchema.extend({
23792
+ meta: webOutputMetaSchema,
23793
+ platform: literal("web"),
23794
+ runnerName: webRunnerNamesSchema,
23795
+ schemaVersion: literal(1)
23796
+ });
23797
+
23798
+ // node_modules/@qawolf/flow-targets/dist/schemas/web/v1/index.js
23799
+ var webExecutionTargetSchema = codec(webTargetInputSchema, webTargetOutputSchema, {
23800
+ decode(value) {
23801
+ const runnerName = value.meta === "legacy" ? "node20Basic" : value.meta.gpuEnabled ? "node20GpuWithPlaywright" : "node20WithPlaywright";
23802
+ let meta;
23803
+ if (value.meta === "legacy")
23804
+ meta = value.meta;
23805
+ else if (value.meta.kind === "electron") {
23806
+ meta = {
23807
+ gpuEnabled: false,
23808
+ kind: "electron"
23809
+ };
22269
23810
  } else {
22270
- ctx.ui.output({
22271
- authenticated: true,
22272
- organization,
22273
- source: resolved.source
22274
- }, authMessages.whoami.authenticatedAs(organization.name, resolved.source));
23811
+ const browserInfo = getWebBrowserInfo(value.meta);
23812
+ meta = {
23813
+ defaultBrowser: browserInfo,
23814
+ gpuEnabled: value.meta.gpuEnabled,
23815
+ kind: "browser"
23816
+ };
22275
23817
  }
22276
- return;
23818
+ return {
23819
+ ...value,
23820
+ meta,
23821
+ runnerName
23822
+ };
23823
+ },
23824
+ encode(value) {
23825
+ return webTargetInputSchema.parse(value);
22277
23826
  }
22278
- reportTeamIdentity(ctx, value, resolved.source);
22279
- }
23827
+ });
22280
23828
 
22281
- // src/commands/auth/index.ts
22282
- function registerAuthCommand(program, signals) {
22283
- const auth = program.command("auth").description("Manage authentication with QA Wolf");
22284
- declareCommandKind(auth.command("login"), "local").description("Authenticate with QA Wolf in a browser or with an API key").action(withContext(signals, handleLogin));
22285
- declareCommandKind(auth.command("logout"), "local").description("Remove stored credentials").action(withContext(signals, handleLogout));
22286
- declareCommandKind(auth.command("switch"), "local").description("Choose which workspace to work in").action(withContext(signals, handleSwitchWorkspace));
22287
- declareCommandKind(auth.command("whoami"), "read").description("Show authentication status").action(withContext(signals, handleWhoami));
22288
- }
23829
+ // node_modules/@qawolf/flow-targets/dist/schemas/web/index.js
23830
+ var webExecutionTargetInputSchema = discriminatedUnion("schemaVersion", [webTargetInputSchema]);
23831
+ var webExecutionTargetOutputSchema = discriminatedUnion("schemaVersion", [webTargetOutputSchema]);
23832
+ var webExecutionTargetSchema2 = discriminatedUnion("schemaVersion", [
23833
+ webExecutionTargetSchema
23834
+ ]);
23835
+
23836
+ // node_modules/@qawolf/flow-targets/dist/schemas/index.js
23837
+ var executionTargetInputSchema = discriminatedUnion("platform", [
23838
+ androidExecutionTargetInputSchema,
23839
+ iosExecutionTargetInputSchema,
23840
+ webExecutionTargetInputSchema
23841
+ ]);
23842
+ var executionTargetOutputSchema = discriminatedUnion("platform", [
23843
+ androidExecutionTargetOutputSchema,
23844
+ iosExecutionTargetOutputSchema,
23845
+ webExecutionTargetOutputSchema
23846
+ ]);
23847
+ var executionTargetSchema = discriminatedUnion("platform", [
23848
+ androidExecutionTargetSchema2,
23849
+ iosExecutionTargetSchema2,
23850
+ webExecutionTargetSchema2
23851
+ ]);
23852
+
23853
+ // node_modules/@qawolf/flow-targets/dist/parseExecutionTarget.js
23854
+ var isPresetLiteral = (input) => presetLiteralsSchema.safeParse(input).success;
23855
+ var parseExecutionTarget = (input) => executionTargetSchema.decode(isPresetLiteral(input) ? presetTargetMap[input] : input);
23856
+ // node_modules/@qawolf/flow-targets/dist/index.js
23857
+ var presetLiteralOrAdHocTargetSchema = union([
23858
+ presetLiteralsSchema,
23859
+ executionTargetInputSchema
23860
+ ]);
22289
23861
 
22290
23862
  // src/core/androidTargets.ts
22291
- import { parseExecutionTarget } from "@qawolf/flow-targets";
22292
23863
  function makeAvdName(deviceModel, androidVersion) {
22293
23864
  return `qawolf_${deviceModel.toLowerCase().replace(/ /g, "_")}_api${androidVersion}`;
22294
23865
  }
@@ -22333,11 +23904,11 @@ async function* batchMap(items, fn, size) {
22333
23904
  }
22334
23905
 
22335
23906
  // src/core/nodeModulesBins.ts
22336
- import { join as join15 } from "node:path";
23907
+ import { join as join16 } from "node:path";
22337
23908
  function nodeModulesBinCandidates(envDir, name, platform) {
22338
- const binDir = join15(envDir, "node_modules", ".bin");
23909
+ const binDir = join16(envDir, "node_modules", ".bin");
22339
23910
  const names = platform === "win32" ? [`${name}.cmd`, `${name}.exe`] : [name];
22340
- return names.map((shim) => join15(binDir, shim));
23911
+ return names.map((shim) => join16(binDir, shim));
22341
23912
  }
22342
23913
  function appiumCliCandidates(envDir, platform) {
22343
23914
  return nodeModulesBinCandidates(envDir, "appium", platform);
@@ -22394,21 +23965,21 @@ async function checkUiautomator2(spawn, appiumBin, platform) {
22394
23965
  }
22395
23966
 
22396
23967
  // src/core/androidBins.ts
22397
- import { join as join16 } from "node:path";
23968
+ import { join as join17 } from "node:path";
22398
23969
  function withExeSuffix(name, platform) {
22399
23970
  return platform === "win32" ? `${name}.exe` : name;
22400
23971
  }
22401
23972
  function emulatorBin(home, platform) {
22402
23973
  const name = withExeSuffix("emulator", platform);
22403
- return home ? join16(home, "emulator", name) : name;
23974
+ return home ? join17(home, "emulator", name) : name;
22404
23975
  }
22405
23976
  function adbBin(home, platform) {
22406
23977
  const name = withExeSuffix("adb", platform);
22407
- return home ? join16(home, "platform-tools", name) : name;
23978
+ return home ? join17(home, "platform-tools", name) : name;
22408
23979
  }
22409
23980
  function cmdlineToolsBin(home, name, platform) {
22410
23981
  const file = platform === "win32" ? `${name}.bat` : name;
22411
- return join16(home, "cmdline-tools", "latest", "bin", file);
23982
+ return join17(home, "cmdline-tools", "latest", "bin", file);
22412
23983
  }
22413
23984
  function sdkManagerBin(home, platform) {
22414
23985
  return cmdlineToolsBin(home, "sdkmanager", platform);
@@ -22705,9 +24276,9 @@ async function checkNpmRegistry(deps) {
22705
24276
  }
22706
24277
 
22707
24278
  // src/core/playwrightCli.ts
22708
- import { join as join17 } from "node:path";
24279
+ import { join as join18 } from "node:path";
22709
24280
  function playwrightCliJsPath(envDir) {
22710
- return join17(envDir, "node_modules", "playwright", "cli.js");
24281
+ return join18(envDir, "node_modules", "playwright", "cli.js");
22711
24282
  }
22712
24283
  function playwrightCliInvocation(args) {
22713
24284
  return {
@@ -22721,7 +24292,7 @@ function playwrightCliInvocation(args) {
22721
24292
  var flowsVersion = "0.1.4";
22722
24293
  var playwrightVersion = "1.62.0";
22723
24294
  var emailsVersion = "1.1.1";
22724
- var testkitVersion = "1.1.1";
24295
+ var testkitVersion = "1.2.1";
22725
24296
  var appiumVersion = "3.6.0";
22726
24297
  var appiumUiautomator2DriverVersion = "8.4.0";
22727
24298
  var expectWebdriverioVersion = "5.6.5";
@@ -22857,11 +24428,11 @@ function renderResults(ui, results) {
22857
24428
  }
22858
24429
 
22859
24430
  // src/domains/flows/ensureDeps.ts
22860
- import { dirname as dirname4, join as join18 } from "node:path";
24431
+ import { dirname as dirname4, join as join19 } from "node:path";
22861
24432
  function findEnvDir(flowPath, fs) {
22862
24433
  let dir = dirname4(flowPath);
22863
24434
  while (true) {
22864
- if (fs.existsSync(join18(dir, "package.json")))
24435
+ if (fs.existsSync(join19(dir, "package.json")))
22865
24436
  return dir;
22866
24437
  const parent = dirname4(dir);
22867
24438
  if (parent === dir)
@@ -22887,7 +24458,7 @@ function resolveProjectDirSafe(files, fs) {
22887
24458
  }
22888
24459
 
22889
24460
  // src/domains/flows/expand.ts
22890
- import { join as join19, resolve as resolve3 } from "node:path";
24461
+ import { join as join20, resolve as resolve3 } from "node:path";
22891
24462
 
22892
24463
  // node_modules/tinyglobby/dist/index.mjs
22893
24464
  import { readdir, readdirSync, realpath, realpathSync, stat, statSync } from "fs";
@@ -23716,7 +25287,6 @@ async function glob(globInput, options) {
23716
25287
  }
23717
25288
 
23718
25289
  // src/core/flowMeta.ts
23719
- import { getWebBrowserInfo, parseExecutionTarget as parseExecutionTarget2 } from "@qawolf/flow-targets";
23720
25290
  import { basename as basename2 } from "node:path";
23721
25291
 
23722
25292
  // src/core/stringLiteral.ts
@@ -23832,7 +25402,7 @@ var browserNameToPlaywright = {
23832
25402
  function classifyTarget(target) {
23833
25403
  let parsed;
23834
25404
  try {
23835
- parsed = parseExecutionTarget2(target);
25405
+ parsed = parseExecutionTarget(target);
23836
25406
  } catch {
23837
25407
  return { kind: "unrecognized" };
23838
25408
  }
@@ -23876,11 +25446,11 @@ function makePeekFlowMeta(fs) {
23876
25446
  };
23877
25447
  }
23878
25448
  async function resolveGlobRoots(cwd, fs) {
23879
- const qawolfPath = join19(cwd, ".qawolf");
25449
+ const qawolfPath = join20(cwd, ".qawolf");
23880
25450
  let envDirs = [];
23881
25451
  try {
23882
25452
  const entries = await fs.readdirWithTypes(qawolfPath);
23883
- envDirs = entries.filter((e) => e.isDirectory()).map((e) => join19(qawolfPath, e.name));
25453
+ envDirs = entries.filter((e) => e.isDirectory()).map((e) => join20(qawolfPath, e.name));
23884
25454
  } catch {}
23885
25455
  return [cwd, ...envDirs];
23886
25456
  }
@@ -23906,11 +25476,11 @@ async function expandPatterns(patterns, cwd, logger, fs) {
23906
25476
  }
23907
25477
 
23908
25478
  // src/domains/runtimeEnv/clearRuntimeEnv.ts
23909
- import { join as join21 } from "node:path";
25479
+ import { join as join22 } from "node:path";
23910
25480
 
23911
25481
  // src/domains/runtimeEnv/managedEnvDir.ts
23912
25482
  import { createHash } from "node:crypto";
23913
- import { join as join20, resolve as resolve4 } from "node:path";
25483
+ import { join as join21, resolve as resolve4 } from "node:path";
23914
25484
 
23915
25485
  // src/domains/runtimeEnv/pinnedPackages.ts
23916
25486
  var pinnedPackages = [
@@ -23941,19 +25511,19 @@ function managedEnvBaseDir() {
23941
25511
  const override = process.env["QAWOLF_RUNTIME_DIR"]?.trim();
23942
25512
  if (override)
23943
25513
  return resolve4(override);
23944
- return join20(getDataDir(), "runtime");
25514
+ return join21(getDataDir(), "runtime");
23945
25515
  }
23946
25516
  function runStagingRoot() {
23947
25517
  return `${managedEnvBaseDir()}-runs`;
23948
25518
  }
23949
25519
  function managedEnvDir() {
23950
- return join20(managedEnvBaseDir(), managedEnvHash());
25520
+ return join21(managedEnvBaseDir(), managedEnvHash());
23951
25521
  }
23952
25522
  async function scaffoldManagedEnv(dir, fs) {
23953
25523
  await fs.mkdir(dir, { recursive: true });
23954
25524
  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/
25525
+ await fs.writeFile(join21(dir, "package.json"), JSON.stringify({ name: "qawolf-runtime", private: true, dependencies }, undefined, 2));
25526
+ await fs.writeFile(join21(dir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
23957
25527
  `);
23958
25528
  }
23959
25529
 
@@ -23975,14 +25545,14 @@ async function isManagedRuntimeBase(dir, fs) {
23975
25545
  for (const entry of entries) {
23976
25546
  if (!entry.isDirectory())
23977
25547
  return false;
23978
- if (!await hasRuntimeSentinel(join21(dir, entry.name), fs))
25548
+ if (!await hasRuntimeSentinel(join22(dir, entry.name), fs))
23979
25549
  return false;
23980
25550
  }
23981
25551
  return true;
23982
25552
  }
23983
25553
  async function hasRuntimeSentinel(hashDir, fs) {
23984
25554
  try {
23985
- const pkg = JSON.parse(await fs.readFile(join21(hashDir, "package.json")));
25555
+ const pkg = JSON.parse(await fs.readFile(join22(hashDir, "package.json")));
23986
25556
  return pkg.name === runtimeSentinel;
23987
25557
  } catch {
23988
25558
  return false;
@@ -24008,10 +25578,10 @@ function spawnNpmInstall(cwd, spawn2 = spawn) {
24008
25578
  }
24009
25579
 
24010
25580
  // src/domains/runtimeEnv/resolvePinned.ts
24011
- import { join as join22 } from "node:path";
25581
+ import { join as join23 } from "node:path";
24012
25582
  function readInstalledVersion(dir, pkgName, fs) {
24013
25583
  try {
24014
- const pkgPath = join22(dir, "node_modules", ...pkgName.split("/"), "package.json");
25584
+ const pkgPath = join23(dir, "node_modules", ...pkgName.split("/"), "package.json");
24015
25585
  const raw = JSON.parse(fs.readFileSync(pkgPath));
24016
25586
  const { version } = raw;
24017
25587
  return typeof version === "string" ? version : undefined;
@@ -24059,10 +25629,10 @@ function allPinnedResolved(dir, fs, platform) {
24059
25629
  }
24060
25630
 
24061
25631
  // src/domains/runtimeEnv/shimDeps.ts
24062
- import { join as join24 } from "node:path";
25632
+ import { join as join25 } from "node:path";
24063
25633
 
24064
25634
  // src/shell/resolveExport.ts
24065
- import { join as join23 } from "node:path";
25635
+ import { join as join24 } from "node:path";
24066
25636
  function pickPath(entry, preference) {
24067
25637
  if (typeof entry === "string")
24068
25638
  return entry;
@@ -24094,28 +25664,28 @@ function resolveFromEnvDir(envDir, specifier, preference = "esm", fs = makeDefau
24094
25664
  const pkgName = parts.slice(0, pkgPartCount).join("/");
24095
25665
  const rest = parts.slice(pkgPartCount);
24096
25666
  const subpath = rest.length > 0 ? "./" + rest.join("/") : ".";
24097
- const pkgDir = join23(envDir, "node_modules", ...pkgName.split("/"));
25667
+ const pkgDir = join24(envDir, "node_modules", ...pkgName.split("/"));
24098
25668
  let pkg;
24099
25669
  try {
24100
- pkg = JSON.parse(fs.readFileSync(join23(pkgDir, "package.json")));
25670
+ pkg = JSON.parse(fs.readFileSync(join24(pkgDir, "package.json")));
24101
25671
  } catch (err) {
24102
25672
  if (isNoEntError(err))
24103
- throw new Error(`Package '${pkgName}' not found in ${join23(envDir, "node_modules")}`, { cause: err });
25673
+ throw new Error(`Package '${pkgName}' not found in ${join24(envDir, "node_modules")}`, { cause: err });
24104
25674
  throw err;
24105
25675
  }
24106
25676
  const exportsMap = normalizeExports(pkg.exports);
24107
25677
  const entry = exportsMap?.[subpath];
24108
25678
  const relative = pickPath(entry, preference) ?? (subpath === "." ? pkg.module ?? pkg.main : undefined);
24109
25679
  if (!relative) {
24110
- throw new Error(`No entry for "${subpath}" in exports of ${join23(pkgDir, "package.json")}`);
25680
+ throw new Error(`No entry for "${subpath}" in exports of ${join24(pkgDir, "package.json")}`);
24111
25681
  }
24112
- return join23(pkgDir, relative);
25682
+ return join24(pkgDir, relative);
24113
25683
  }
24114
25684
 
24115
25685
  // src/domains/runtimeEnv/shimDeps.ts
24116
25686
  function readShimMarker(shimDir, fs) {
24117
25687
  try {
24118
- const pkg = JSON.parse(fs.readFileSync(join24(shimDir, "package.json")));
25688
+ const pkg = JSON.parse(fs.readFileSync(join25(shimDir, "package.json")));
24119
25689
  if (pkg._qawolf_format) {
24120
25690
  return {
24121
25691
  _qawolf_version: pkg._qawolf_version ?? "",
@@ -24131,11 +25701,11 @@ function resolveBunBuild(bunBuild) {
24131
25701
  return bunBuild === false ? undefined : { build: bunBuild };
24132
25702
  }
24133
25703
  async function removeStaleShims(flowsDir, flowsDeps, fs) {
24134
- const shimsDir = join24(flowsDir, "node_modules");
25704
+ const shimsDir = join25(flowsDir, "node_modules");
24135
25705
  if (!fs.existsSync(shimsDir))
24136
25706
  return;
24137
25707
  for (const dep of flowsDeps) {
24138
- const shimDepDir = join24(shimsDir, ...dep.split("/"));
25708
+ const shimDepDir = join25(shimsDir, ...dep.split("/"));
24139
25709
  if (readShimMarker(shimDepDir, fs)) {
24140
25710
  await fs.rm(shimDepDir, { recursive: true, force: true });
24141
25711
  }
@@ -24144,17 +25714,17 @@ async function removeStaleShims(flowsDir, flowsDeps, fs) {
24144
25714
  async function buildDepShim(args) {
24145
25715
  const { envDir, flowsDir, dep, fs, build } = args;
24146
25716
  const depParts = dep.split("/");
24147
- const depDir = join24(envDir, "node_modules", ...depParts);
25717
+ const depDir = join25(envDir, "node_modules", ...depParts);
24148
25718
  if (!fs.existsSync(depDir))
24149
25719
  return;
24150
25720
  let depVersion;
24151
25721
  try {
24152
- const pkg = JSON.parse(fs.readFileSync(join24(depDir, "package.json")));
25722
+ const pkg = JSON.parse(fs.readFileSync(join25(depDir, "package.json")));
24153
25723
  depVersion = pkg.version ?? "unknown";
24154
25724
  } catch {
24155
25725
  depVersion = "unknown";
24156
25726
  }
24157
- const shimDir = join24(flowsDir, "node_modules", ...depParts);
25727
+ const shimDir = join25(flowsDir, "node_modules", ...depParts);
24158
25728
  if (fs.existsSync(shimDir)) {
24159
25729
  const marker = readShimMarker(shimDir, fs);
24160
25730
  if (!marker)
@@ -24181,20 +25751,20 @@ async function buildDepShim(args) {
24181
25751
  }
24182
25752
  const shimCode = await output.text();
24183
25753
  await fs.mkdir(shimDir, { recursive: true });
24184
- await fs.writeFile(join24(shimDir, "package.json"), JSON.stringify({
25754
+ await fs.writeFile(join25(shimDir, "package.json"), JSON.stringify({
24185
25755
  name: dep,
24186
25756
  _qawolf_version: depVersion,
24187
25757
  _qawolf_format: "bun-build-v1"
24188
25758
  }));
24189
- await fs.writeFile(join24(shimDir, "index.js"), shimCode);
25759
+ await fs.writeFile(join25(shimDir, "index.js"), shimCode);
24190
25760
  }
24191
25761
  async function shimFlowsDeps(envDir, fs = makeDefaultFs(), bunBuild) {
24192
- const flowsDir = join24(envDir, "node_modules", "@qawolf", "flows");
25762
+ const flowsDir = join25(envDir, "node_modules", "@qawolf", "flows");
24193
25763
  if (!fs.existsSync(flowsDir))
24194
25764
  return;
24195
25765
  let flowsDeps;
24196
25766
  try {
24197
- const flowsPkg = JSON.parse(fs.readFileSync(join24(flowsDir, "package.json")));
25767
+ const flowsPkg = JSON.parse(fs.readFileSync(join25(flowsDir, "package.json")));
24198
25768
  flowsDeps = Object.keys(flowsPkg.dependencies ?? {});
24199
25769
  } catch {
24200
25770
  return;
@@ -24375,11 +25945,11 @@ function collectValue(value, previous) {
24375
25945
  }
24376
25946
 
24377
25947
  // src/shell/manifest/pulledEnv.ts
24378
- import { join as join26 } from "node:path";
25948
+ import { join as join27 } from "node:path";
24379
25949
 
24380
25950
  // src/shell/manifest/io.ts
24381
25951
  import { createHash as createHash2 } from "node:crypto";
24382
- import { join as join25 } from "node:path";
25952
+ import { join as join26 } from "node:path";
24383
25953
  var manifestFilename = ".manifest.json";
24384
25954
  var flowEntrySchema = object({
24385
25955
  path: string2(),
@@ -24401,7 +25971,7 @@ var manifestSchema = object({
24401
25971
  async function readManifest(envDir, fs = makeDefaultFs()) {
24402
25972
  let raw;
24403
25973
  try {
24404
- raw = await fs.readFile(join25(envDir, manifestFilename));
25974
+ raw = await fs.readFile(join26(envDir, manifestFilename));
24405
25975
  } catch (err) {
24406
25976
  if (isNoEntError(err))
24407
25977
  return "missing";
@@ -24435,7 +26005,7 @@ async function readManifest(envDir, fs = makeDefaultFs()) {
24435
26005
  }
24436
26006
  async function writeManifest(envDir, manifest, fs = makeDefaultFs()) {
24437
26007
  const body = JSON.stringify(manifest, undefined, 2);
24438
- await fs.writeFile(join25(envDir, manifestFilename), `${body}
26008
+ await fs.writeFile(join26(envDir, manifestFilename), `${body}
24439
26009
  `);
24440
26010
  }
24441
26011
  function hashFile(absPath, fs = makeDefaultFs()) {
@@ -24461,7 +26031,7 @@ async function findPulledEnv(ref, cwd, fs = makeDefaultFs()) {
24461
26031
  return;
24462
26032
  }
24463
26033
  async function listPulledEnvDirs(cwd, fs = makeDefaultFs()) {
24464
- const root = join26(cwd, ".qawolf");
26034
+ const root = join27(cwd, ".qawolf");
24465
26035
  let entries;
24466
26036
  try {
24467
26037
  entries = await fs.readdirWithTypes(root);
@@ -24474,7 +26044,7 @@ async function listPulledEnvDirs(cwd, fs = makeDefaultFs()) {
24474
26044
  for (const entry of entries) {
24475
26045
  if (!entry.isDirectory())
24476
26046
  continue;
24477
- const envDir = join26(root, entry.name);
26047
+ const envDir = join27(root, entry.name);
24478
26048
  const manifest = await readManifest(envDir, fs);
24479
26049
  if (typeof manifest === "string")
24480
26050
  continue;
@@ -24866,7 +26436,7 @@ async function flowsListRemote(ctx, pattern, options) {
24866
26436
  }
24867
26437
 
24868
26438
  // src/domains/flows/pull/handler.ts
24869
- import { dirname as dirname8, join as join31, resolve as resolve8 } from "node:path";
26439
+ import { dirname as dirname8, join as join32, resolve as resolve8 } from "node:path";
24870
26440
 
24871
26441
  // src/domains/flows/pull/fetchPhase.ts
24872
26442
  async function fetchTags(ctx, envId) {
@@ -24888,6 +26458,7 @@ async function fetchBundleAndEnvVars(ctx, envId) {
24888
26458
  let bundleFetchedAt;
24889
26459
  let envVars;
24890
26460
  let envVarsFetchedAt;
26461
+ let teamId;
24891
26462
  let tags;
24892
26463
  await ctx.ui.withProgress([
24893
26464
  {
@@ -24903,10 +26474,11 @@ async function fetchBundleAndEnvVars(ctx, envId) {
24903
26474
  {
24904
26475
  message: flowsMessages.pull.fetchingEnvVars,
24905
26476
  task: async () => {
24906
- const result = await platformClient.getEnvVars(envId);
26477
+ const result = await platformClient.getEnvironmentWithVariables(envId);
24907
26478
  if (!result.ok)
24908
26479
  throw new Error(result.error);
24909
- envVars = result.value;
26480
+ envVars = result.value.environmentVariables;
26481
+ teamId = result.value.teamId;
24910
26482
  envVarsFetchedAt = new Date;
24911
26483
  }
24912
26484
  },
@@ -24917,10 +26489,17 @@ async function fetchBundleAndEnvVars(ctx, envId) {
24917
26489
  }
24918
26490
  }
24919
26491
  ], flowsMessages.pull.downloadComplete);
24920
- if (tmpArchive === undefined || bundleFetchedAt === undefined || envVars === undefined || envVarsFetchedAt === undefined) {
26492
+ if (tmpArchive === undefined || bundleFetchedAt === undefined || envVars === undefined || envVarsFetchedAt === undefined || teamId === undefined) {
24921
26493
  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
26494
  }
24923
- return { tmpArchive, bundleFetchedAt, envVars, envVarsFetchedAt, tags };
26495
+ return {
26496
+ tmpArchive,
26497
+ bundleFetchedAt,
26498
+ envVars,
26499
+ envVarsFetchedAt,
26500
+ teamId,
26501
+ tags
26502
+ };
24924
26503
  }
24925
26504
 
24926
26505
  // src/domains/flows/pull/safety.ts
@@ -25010,7 +26589,7 @@ async function checkSafety(args) {
25010
26589
  }
25011
26590
 
25012
26591
  // src/domains/flows/pull/reportPull.ts
25013
- import { join as join27 } from "node:path";
26592
+ import { join as join28 } from "node:path";
25014
26593
  function reportPullResult(ui, args) {
25015
26594
  if (ui.mode !== "json")
25016
26595
  return;
@@ -25025,12 +26604,12 @@ function reportPullResult(ui, args) {
25025
26604
  assetDownloadedCount: args.assets.downloadedCount,
25026
26605
  assetReusedCount: args.assets.reusedCount,
25027
26606
  assetSkippedCount: args.assets.skippedCount,
25028
- manifestPath: join27(args.stage.envDir, manifestFilename)
26607
+ manifestPath: join28(args.stage.envDir, manifestFilename)
25029
26608
  }, "");
25030
26609
  }
25031
26610
 
25032
26611
  // src/domains/flows/pull/bundle.ts
25033
- import { join as join28, relative as relative6 } from "node:path";
26612
+ import { join as join29, relative as relative6 } from "node:path";
25034
26613
  async function flattenSingleWrapper(dir, fs = makeDefaultFs()) {
25035
26614
  const entries = await fs.readdir(dir);
25036
26615
  if (entries.length !== 1)
@@ -25038,12 +26617,12 @@ async function flattenSingleWrapper(dir, fs = makeDefaultFs()) {
25038
26617
  const innerName = entries[0];
25039
26618
  if (!innerName)
25040
26619
  return;
25041
- const inner = join28(dir, innerName);
26620
+ const inner = join29(dir, innerName);
25042
26621
  const innerStat = await fs.stat(inner);
25043
26622
  if (!innerStat.isDirectory())
25044
26623
  return;
25045
26624
  for (const e of await fs.readdir(inner)) {
25046
- await fs.rename(join28(inner, e), join28(dir, e));
26625
+ await fs.rename(join29(inner, e), join29(dir, e));
25047
26626
  }
25048
26627
  const leftovers = await fs.readdir(inner);
25049
26628
  if (leftovers.length > 0) {
@@ -25062,7 +26641,7 @@ async function buildManifest(args, fs = makeDefaultFs()) {
25062
26641
  const flowPaths = await walkForFlows(args.bundleDir, fs);
25063
26642
  const flows = await Promise.all(flowPaths.map(async (rel) => ({
25064
26643
  path: toPosix(rel),
25065
- contentHash: await hashFile(join28(args.bundleDir, rel), fs),
26644
+ contentHash: await hashFile(join29(args.bundleDir, rel), fs),
25066
26645
  tags: args.tags?.byPath.get(toPosix(rel))
25067
26646
  })));
25068
26647
  return {
@@ -25086,7 +26665,7 @@ async function walkForFlows(root, fs) {
25086
26665
  async function walk(current, root, out, fs) {
25087
26666
  const entries = await fs.readdirWithTypes(current);
25088
26667
  for (const e of entries) {
25089
- const abs = join28(current, e.name);
26668
+ const abs = join29(current, e.name);
25090
26669
  if (e.isDirectory()) {
25091
26670
  await walk(abs, root, out, fs);
25092
26671
  } else if (e.isFile() && flowExtensions.some((ext) => e.name.endsWith(ext))) {
@@ -25099,11 +26678,11 @@ async function sampleQawolfCommittedAt(bundleDir, fs = makeDefaultFs()) {
25099
26678
  const sample = flowPaths[0];
25100
26679
  if (!sample)
25101
26680
  return;
25102
- return (await fs.stat(join28(bundleDir, sample))).mtime.toISOString();
26681
+ return (await fs.stat(join29(bundleDir, sample))).mtime.toISOString();
25103
26682
  }
25104
26683
 
25105
26684
  // src/domains/flows/pull/applyTeamStorageRewrite.ts
25106
- import { join as join29, relative as relative7 } from "node:path";
26685
+ import { join as join30, relative as relative7 } from "node:path";
25107
26686
 
25108
26687
  // src/domains/flows/pull/rewriteTeamStorage.ts
25109
26688
  var runnerMountPrefix = "/home/wolf/team-storage/";
@@ -25139,7 +26718,7 @@ function isFlowFile(name) {
25139
26718
  async function walk2(dir, out, fs) {
25140
26719
  const entries = await fs.readdirWithTypes(dir);
25141
26720
  for (const e of entries) {
25142
- const abs = join29(dir, e.name);
26721
+ const abs = join30(dir, e.name);
25143
26722
  if (e.isDirectory()) {
25144
26723
  await walk2(abs, out, fs);
25145
26724
  } else if (e.isFile() && isSourceFile(e.name)) {
@@ -25167,7 +26746,7 @@ async function applyTeamStorageRewrite(rootDir, fs = makeDefaultFs()) {
25167
26746
  }
25168
26747
 
25169
26748
  // src/domains/flows/pull/envVars.ts
25170
- import { join as join30 } from "node:path";
26749
+ import { join as join31 } from "node:path";
25171
26750
 
25172
26751
  // src/core/dotenv.ts
25173
26752
  var bareKeyPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -25232,7 +26811,7 @@ function unquote(escaped) {
25232
26811
  async function writeEnvFile(envDir, vars, fs = makeDefaultFs()) {
25233
26812
  if (Object.keys(vars).length === 0)
25234
26813
  return;
25235
- await fs.writeFile(join30(envDir, ".env"), serializeDotenv(vars), {
26814
+ await fs.writeFile(join31(envDir, ".env"), serializeDotenv(vars), {
25236
26815
  mode: 384
25237
26816
  });
25238
26817
  }
@@ -28674,8 +30253,8 @@ async function handleFlowsPull(ctx, opts, deps) {
28674
30253
  ctx.ui.error(validation.error);
28675
30254
  return { error: validation.error };
28676
30255
  }
28677
- const destAbs = resolve8(opts.out ?? join31(".qawolf", opts.env));
28678
- const assetsAbs = join31(dirname8(destAbs), "assets");
30256
+ const destAbs = resolve8(opts.out ?? join32(".qawolf", opts.env));
30257
+ const assetsAbs = join32(dirname8(destAbs), "assets");
28679
30258
  await resolvedDeps.fs.mkdir(assetsAbs, { recursive: true });
28680
30259
  const yes = opts.yes ?? false;
28681
30260
  let archive;
@@ -28721,6 +30300,7 @@ async function handleFlowsPull(ctx, opts, deps) {
28721
30300
  message: flowsMessages.pull.downloadingTeamStorageAssets,
28722
30301
  task: async (update) => {
28723
30302
  const result = await ctx.platformClient.syncTeamStorageAssets(assetsAbs, {
30303
+ teamId: fetched.teamId,
28724
30304
  onProgress: ({ current, total }) => update(flowsMessages.pull.downloadingTeamStorageAssetsProgress(current, total))
28725
30305
  });
28726
30306
  if (!result.ok)
@@ -29101,9 +30681,6 @@ ${hint}`);
29101
30681
  };
29102
30682
  }
29103
30683
 
29104
- // src/domains/runner/runAndroidFlowUtils.ts
29105
- import { parseExecutionTarget as parseExecutionTarget3 } from "@qawolf/flow-targets";
29106
-
29107
30684
  // src/domains/runner/unsupportedDepNames.ts
29108
30685
  var unsupportedSharedDepNames = [
29109
30686
  "fetchLatestEnvironmentVariables",
@@ -29129,7 +30706,7 @@ function resolveAvdName(targetInput) {
29129
30706
  const { deviceModel, androidVersion } = raw.meta;
29130
30707
  return makeAvdName(deviceModel, androidVersion);
29131
30708
  }
29132
- const parsed = parseExecutionTarget3(raw);
30709
+ const parsed = parseExecutionTarget(raw);
29133
30710
  if (parsed.platform !== "android") {
29134
30711
  throw new Error(`runAndroidFlow: expected an Android target, got platform "${parsed.platform}"`);
29135
30712
  }
@@ -29550,15 +31127,15 @@ async function defaultRunWebFlowDeps(envDir = process.cwd(), signals) {
29550
31127
 
29551
31128
  // src/domains/runtimeEnv/prepareRunDir.ts
29552
31129
  import { createHash as createHash4 } from "node:crypto";
29553
- import { join as join39, resolve as resolve9 } from "node:path";
31130
+ import { join as join40, resolve as resolve9 } from "node:path";
29554
31131
 
29555
31132
  // src/domains/runtimeEnv/execSubpathImports.ts
29556
- import { join as join32 } from "node:path";
31133
+ import { join as join33 } from "node:path";
29557
31134
  var flowSubpathImports = { "#playwright": "playwright" };
29558
31135
  var packageJsonSchema = looseObject({ imports: record(string2(), unknown()).catch({}) }).catch({ imports: {} });
29559
31136
  async function writeExecSubpathImports(args) {
29560
31137
  const { execDir, fs } = args;
29561
- const pkgPath = join32(execDir, "package.json");
31138
+ const pkgPath = join33(execDir, "package.json");
29562
31139
  const pkg = packageJsonSchema.parse(await readPackageJson(pkgPath, fs));
29563
31140
  const merged = { ...pkg, imports: { ...pkg.imports, ...flowSubpathImports } };
29564
31141
  await fs.writeFile(pkgPath, JSON.stringify(merged, undefined, 2));
@@ -29580,10 +31157,10 @@ async function readPackageJson(pkgPath, fs) {
29580
31157
  }
29581
31158
 
29582
31159
  // src/domains/runtimeEnv/innerHop.ts
29583
- import { join as join34 } from "node:path";
31160
+ import { join as join35 } from "node:path";
29584
31161
 
29585
31162
  // src/domains/runtimeEnv/linkPinnedPackages.ts
29586
- import { dirname as dirname9, join as join33 } from "node:path";
31163
+ import { dirname as dirname9, join as join34 } from "node:path";
29587
31164
 
29588
31165
  // src/domains/runtimeEnv/symlinkDir.ts
29589
31166
  import { symlink } from "node:fs/promises";
@@ -29595,16 +31172,16 @@ async function createDirSymlink(source, target) {
29595
31172
  // src/domains/runtimeEnv/linkPinnedPackages.ts
29596
31173
  async function linkPinnedPackages(args) {
29597
31174
  const { depsRoot, nodeModulesDir, fs } = args;
29598
- const managedModules = join33(depsRoot, "node_modules");
31175
+ const managedModules = join34(depsRoot, "node_modules");
29599
31176
  await fs.mkdir(nodeModulesDir, { recursive: true });
29600
31177
  for (const { name } of pinnedPackages) {
29601
31178
  const segments = name.split("/");
29602
- const target = join33(nodeModulesDir, ...segments);
31179
+ const target = join34(nodeModulesDir, ...segments);
29603
31180
  if (segments.length > 1) {
29604
31181
  await fs.mkdir(dirname9(target), { recursive: true });
29605
31182
  }
29606
31183
  await fs.rm(target, { recursive: true, force: true });
29607
- await createDirSymlink(join33(managedModules, ...segments), target);
31184
+ await createDirSymlink(join34(managedModules, ...segments), target);
29608
31185
  }
29609
31186
  }
29610
31187
 
@@ -29613,18 +31190,18 @@ async function populateInnerHop(args) {
29613
31190
  const { depsRoot, execDir, fs } = args;
29614
31191
  await linkPinnedPackages({
29615
31192
  depsRoot,
29616
- nodeModulesDir: join34(execDir, "node_modules"),
31193
+ nodeModulesDir: join35(execDir, "node_modules"),
29617
31194
  fs
29618
31195
  });
29619
31196
  }
29620
31197
 
29621
31198
  // src/domains/runtimeEnv/outerHop.ts
29622
31199
  import { lstat as lstat2 } from "node:fs/promises";
29623
- import { dirname as dirname11, join as join36 } from "node:path";
31200
+ import { dirname as dirname11, join as join37 } from "node:path";
29624
31201
 
29625
31202
  // src/domains/runtimeEnv/carryOverPackages.ts
29626
31203
  import { lstat } from "node:fs/promises";
29627
- import { dirname as dirname10, join as join35 } from "node:path";
31204
+ import { dirname as dirname10, join as join36 } from "node:path";
29628
31205
 
29629
31206
  // src/domains/runtimeEnv/carryOverPlan.ts
29630
31207
  var executorPackageNames = new Set(pinnedPackages.map((p) => p.name));
@@ -29646,12 +31223,12 @@ async function carryOverPackages(args) {
29646
31223
  const linked = [];
29647
31224
  for (const name of names) {
29648
31225
  const segments = name.split("/");
29649
- const target = join35(nodeModulesDir, ...segments);
31226
+ const target = join36(nodeModulesDir, ...segments);
29650
31227
  if (await pathOccupied(target))
29651
31228
  continue;
29652
31229
  if (segments.length > 1)
29653
31230
  await fs.mkdir(dirname10(target), { recursive: true });
29654
- await createDirSymlink(join35(projectModulesDir, ...segments), target);
31231
+ await createDirSymlink(join36(projectModulesDir, ...segments), target);
29655
31232
  linked.push(name);
29656
31233
  }
29657
31234
  return linked;
@@ -29674,18 +31251,18 @@ async function listPackageNames(dir, fs) {
29674
31251
  const names = [];
29675
31252
  for (const entry of entries) {
29676
31253
  if (!entry.startsWith("@")) {
29677
- if (fs.existsSync(join35(dir, entry, "package.json")))
31254
+ if (fs.existsSync(join36(dir, entry, "package.json")))
29678
31255
  names.push(entry);
29679
31256
  continue;
29680
31257
  }
29681
31258
  let scoped;
29682
31259
  try {
29683
- scoped = await fs.readdir(join35(dir, entry));
31260
+ scoped = await fs.readdir(join36(dir, entry));
29684
31261
  } catch {
29685
31262
  continue;
29686
31263
  }
29687
31264
  for (const inner of scoped) {
29688
- if (fs.existsSync(join35(dir, entry, inner, "package.json"))) {
31265
+ if (fs.existsSync(join36(dir, entry, inner, "package.json"))) {
29689
31266
  names.push(`${entry}/${inner}`);
29690
31267
  }
29691
31268
  }
@@ -29703,7 +31280,7 @@ async function populateOuterHop(args) {
29703
31280
  const depNames = Object.keys(installableDeps);
29704
31281
  const { satisfying, rejected } = await findSatisfyingNodeModulesDir(projectDir, depNames, fs);
29705
31282
  if (satisfying !== undefined) {
29706
- await createDirSymlink(satisfying, join36(runDir, "node_modules"));
31283
+ await createDirSymlink(satisfying, join37(runDir, "node_modules"));
29707
31284
  return { mode: "symlink", nodeModulesDir: satisfying };
29708
31285
  }
29709
31286
  if (depNames.length === 0)
@@ -29712,12 +31289,12 @@ async function populateOuterHop(args) {
29712
31289
  await installOuterDeps(runDir, installableDeps, fs, args.install ?? spawnNpmInstall);
29713
31290
  await linkPinnedPackages({
29714
31291
  depsRoot: args.depsRoot,
29715
- nodeModulesDir: join36(runDir, "node_modules"),
31292
+ nodeModulesDir: join37(runDir, "node_modules"),
29716
31293
  fs
29717
31294
  });
29718
31295
  const carriedOver = await carryOverPackages({
29719
- projectModulesDir: join36(projectDir, "node_modules"),
29720
- nodeModulesDir: join36(runDir, "node_modules"),
31296
+ projectModulesDir: join37(projectDir, "node_modules"),
31297
+ nodeModulesDir: join37(runDir, "node_modules"),
29721
31298
  fs
29722
31299
  });
29723
31300
  return { mode: "install", depCount: depNames.length, rejected, carriedOver };
@@ -29726,10 +31303,10 @@ async function findSatisfyingNodeModulesDir(startDir, depNames, fs) {
29726
31303
  const rejected = [];
29727
31304
  let dir = startDir;
29728
31305
  while (true) {
29729
- const candidate = join36(dir, "node_modules");
31306
+ const candidate = join37(dir, "node_modules");
29730
31307
  const stats = await lstatSafe(candidate);
29731
31308
  if (stats !== undefined && stats.isDirectory() && !stats.isSymbolicLink()) {
29732
- const missing = depNames.filter((name) => !fs.existsSync(join36(candidate, name, "package.json")));
31309
+ const missing = depNames.filter((name) => !fs.existsSync(join37(candidate, name, "package.json")));
29733
31310
  if (missing.length === 0)
29734
31311
  return { satisfying: candidate, rejected };
29735
31312
  rejected.push({ dir: candidate, missing });
@@ -29750,7 +31327,7 @@ async function lstatSafe(path) {
29750
31327
  async function readInstallableDeps(projectDir, fs) {
29751
31328
  let content;
29752
31329
  try {
29753
- content = await fs.readFile(join36(projectDir, "package.json"));
31330
+ content = await fs.readFile(join37(projectDir, "package.json"));
29754
31331
  } catch {
29755
31332
  return {};
29756
31333
  }
@@ -29768,8 +31345,8 @@ async function readInstallableDeps(projectDir, fs) {
29768
31345
  return Object.fromEntries(Object.entries(rawDeps).filter(([name]) => !executorPackageNames2.has(name)).filter((entry) => typeof entry[1] === "string"));
29769
31346
  }
29770
31347
  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/
31348
+ await fs.writeFile(join37(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
31349
+ await fs.writeFile(join37(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
29773
31350
  `);
29774
31351
  const result = await install(runDir);
29775
31352
  if (result.exitCode !== 0) {
@@ -29779,14 +31356,14 @@ async function installOuterDeps(runDir, deps, fs, install) {
29779
31356
 
29780
31357
  // src/domains/runtimeEnv/stageFlowFiles.ts
29781
31358
  import { createHash as createHash3 } from "node:crypto";
29782
- import { basename as basename6, dirname as dirname12, join as join38, sep as sep4 } from "node:path";
31359
+ import { basename as basename6, dirname as dirname12, join as join39, sep as sep4 } from "node:path";
29783
31360
 
29784
31361
  // src/shell/copyDir.ts
29785
31362
  import { cp, readdir as readdir2 } from "node:fs/promises";
29786
- import { basename as basename5, join as join37 } from "node:path";
31363
+ import { basename as basename5, join as join38 } from "node:path";
29787
31364
  async function copyDirExcluding(source, destination, excludedNames) {
29788
31365
  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), {
31366
+ await Promise.all(entries.filter((entry) => !excludedNames.has(entry.name)).map((entry) => cp(join38(source, entry.name), join38(destination, entry.name), {
29790
31367
  recursive: true,
29791
31368
  filter: (path) => !excludedNames.has(basename5(path))
29792
31369
  })));
@@ -29803,14 +31380,14 @@ async function stageFlowFiles(args) {
29803
31380
  if (files.length > 1) {
29804
31381
  return Promise.all(files.map((f) => stageOneFile(f, execDir, fs)));
29805
31382
  }
29806
- await Promise.all(files.map((f) => fs.copyFile(f, join38(execDir, basename6(f)))));
29807
- return files.map((f) => join38(execDir, basename6(f)));
31383
+ await Promise.all(files.map((f) => fs.copyFile(f, join39(execDir, basename6(f)))));
31384
+ return files.map((f) => join39(execDir, basename6(f)));
29808
31385
  }
29809
31386
  async function stageOneFile(file, execDir, fs) {
29810
31387
  const dirHash = createHash3("sha256").update(dirname12(file)).digest("hex").slice(0, 8);
29811
- const subDir = join38(execDir, dirHash);
31388
+ const subDir = join39(execDir, dirHash);
29812
31389
  await fs.mkdir(subDir, { recursive: true });
29813
- const dest = join38(subDir, basename6(file));
31390
+ const dest = join39(subDir, basename6(file));
29814
31391
  await fs.copyFile(file, dest);
29815
31392
  return dest;
29816
31393
  }
@@ -29818,7 +31395,7 @@ function remapPath(file, projectDir, execDir) {
29818
31395
  if (file === projectDir)
29819
31396
  return execDir;
29820
31397
  if (file.startsWith(projectDir + sep4)) {
29821
- return join38(execDir, file.slice(projectDir.length + 1));
31398
+ return join39(execDir, file.slice(projectDir.length + 1));
29822
31399
  }
29823
31400
  return;
29824
31401
  }
@@ -29828,10 +31405,10 @@ async function prepareRunDir(args) {
29828
31405
  const { files, projectDir, depsRoot, runRoot } = args;
29829
31406
  const fs = args.fs ?? makeDefaultFs();
29830
31407
  const runId = buildRunId(projectDir, files);
29831
- const runDir = join39(runRoot, runId);
31408
+ const runDir = join40(runRoot, runId);
29832
31409
  await fs.rm(runDir, { recursive: true, force: true });
29833
31410
  await fs.mkdir(runDir, { recursive: true });
29834
- const execDir = join39(runDir, "exec");
31411
+ const execDir = join40(runDir, "exec");
29835
31412
  await fs.mkdir(execDir, { recursive: true });
29836
31413
  await populateInnerHop({ depsRoot, execDir, fs });
29837
31414
  const stagedFiles = await stageFlowFiles({ files, projectDir, execDir, fs });
@@ -29862,17 +31439,17 @@ function buildRunId(projectDir, files) {
29862
31439
  }
29863
31440
 
29864
31441
  // src/shell/testkit.ts
29865
- import { join as join40 } from "node:path";
31442
+ import { join as join41 } from "node:path";
29866
31443
  function notAvailableLocally(name) {
29867
31444
  throw new Error(runnerMessages.notAvailableLocally(name));
29868
31445
  }
29869
31446
  async function loadSdkDeps(cwd) {
29870
- const testkitDir = join40(cwd, "node_modules", "@qawolf", "testkit");
31447
+ const testkitDir = join41(cwd, "node_modules", "@qawolf", "testkit");
29871
31448
  try {
29872
31449
  const clientMod = await importFromPath(resolveFromEnvDir(cwd, "@qawolf/testkit/client"));
29873
31450
  if (typeof clientMod.createTestkitClient !== "function")
29874
31451
  throw new Error("createTestkitClient is not a function");
29875
- const scopeMod = await importFromPath(join40(testkitDir, "dist", "clientScope.js"));
31452
+ const scopeMod = await importFromPath(join41(testkitDir, "dist", "clientScope.js"));
29876
31453
  if (typeof scopeMod.configureTestkitClient !== "function")
29877
31454
  throw new Error("configureTestkitClient is not a function");
29878
31455
  return {
@@ -29930,9 +31507,9 @@ async function registerLazyEmailsClient(getInbox, cwd, deps) {
29930
31507
  }
29931
31508
 
29932
31509
  // src/commands/flows/envVarDeps.ts
29933
- import { join as join41 } from "node:path";
31510
+ import { join as join42 } from "node:path";
29934
31511
  function envFilePath(envDir) {
29935
- return join41(envDir, ".env");
31512
+ return join42(envDir, ".env");
29936
31513
  }
29937
31514
  async function readEnv(envDir, fs) {
29938
31515
  try {
@@ -30056,7 +31633,7 @@ async function selectRunByTag(args) {
30056
31633
  }
30057
31634
 
30058
31635
  // src/commands/flows/runStagedFlows.ts
30059
- import { join as join45 } from "node:path";
31636
+ import { join as join46 } from "node:path";
30060
31637
 
30061
31638
  // src/shell/appium/createAppiumServer.ts
30062
31639
  import { existsSync as existsSync2 } from "node:fs";
@@ -30190,7 +31767,7 @@ var defaultAdb = async (args) => {
30190
31767
 
30191
31768
  // src/shell/appium/createAndroidEmulator.ts
30192
31769
  var defaultBootTimeoutMs = 120000;
30193
- var pollIntervalMs = 2000;
31770
+ var pollIntervalMs2 = 2000;
30194
31771
  var defaultSpawnEmulator = (bin, args) => {
30195
31772
  const child = spawn3(bin, args, { stdio: "ignore" });
30196
31773
  child.unref();
@@ -30208,7 +31785,7 @@ async function bootSequence(adb, serial, cancelled) {
30208
31785
  ]).catch(() => ({ stdout: "" }));
30209
31786
  if (stdout.trim() === "1")
30210
31787
  return;
30211
- await new Promise((r) => setTimeout(r, pollIntervalMs));
31788
+ await new Promise((r) => setTimeout(r, pollIntervalMs2));
30212
31789
  }
30213
31790
  }
30214
31791
  function waitForBoot(adb, serial, timeoutMs) {
@@ -31362,14 +32939,14 @@ async function runWebFlow({
31362
32939
 
31363
32940
  // src/shell/embeddedWorkerCli.ts
31364
32941
  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";
32942
+ import { basename as basename7, join as join43 } from "node:path";
31366
32943
  var embeddedCliPathEnv = "QAWOLF_EMBEDDED_CLI_PATH";
31367
32944
  function extractEmbeddedWorkerCli() {
31368
32945
  const assetPath = process.env[embeddedCliPathEnv];
31369
32946
  if (assetPath === undefined || assetPath === "")
31370
32947
  return;
31371
- const dir = join42(getDataDir(), "worker");
31372
- const dest = join42(dir, basename7(assetPath));
32948
+ const dir = join43(getDataDir(), "worker");
32949
+ const dest = join43(dir, basename7(assetPath));
31373
32950
  if (existsSync3(dest))
31374
32951
  return dest;
31375
32952
  mkdirSync3(dir, { recursive: true });
@@ -31527,33 +33104,33 @@ function makePooledDispatch(resolvedDir) {
31527
33104
  import { dirname as dirname13 } from "node:path";
31528
33105
 
31529
33106
  // src/shell/reporter/createConsoleReporter.ts
31530
- import { styleText as styleText3 } from "node:util";
33107
+ import { styleText as styleText5 } from "node:util";
31531
33108
  function fmtDuration(ms) {
31532
33109
  return `(${(ms / 1000).toFixed(2)}s)`;
31533
33110
  }
31534
33111
  function fmtStampLine(manifest) {
31535
33112
  const shortHash = manifest.contentHash.slice(0, 8);
31536
- return ` ${styleText3("dim", `env=${manifest.envId} hash=${shortHash}`)}
33113
+ return ` ${styleText5("dim", `env=${manifest.envId} hash=${shortHash}`)}
31537
33114
  `;
31538
33115
  }
31539
33116
  function createConsoleReporter(deps) {
31540
33117
  return {
31541
33118
  onFlowStart({ name, path }) {
31542
- deps.stdout.write(`${styleText3("magenta", "•")} ${styleText3(["bold", "magenta"], name)} ${styleText3("dim", path)}
33119
+ deps.stdout.write(`${styleText5("magenta", "•")} ${styleText5(["bold", "magenta"], name)} ${styleText5("dim", path)}
31543
33120
  `);
31544
33121
  },
31545
33122
  onTestResult({ label, status, durationMs }) {
31546
- const icon = status === "pass" ? styleText3("green", "✓") : styleText3("red", "✗");
31547
- deps.stdout.write(` - ${label} ${icon} ${styleText3("dim", fmtDuration(durationMs))}
33123
+ const icon = status === "pass" ? styleText5("green", "✓") : styleText5("red", "✗");
33124
+ deps.stdout.write(` - ${label} ${icon} ${styleText5("dim", fmtDuration(durationMs))}
31548
33125
  `);
31549
33126
  },
31550
33127
  onScreenshot({ path }) {
31551
- deps.stdout.write(` ${styleText3("dim", runnerMessages.screenshot(path))}
33128
+ deps.stdout.write(` ${styleText5("dim", runnerMessages.screenshot(path))}
31552
33129
  `);
31553
33130
  },
31554
33131
  onFlowPass({ tests, durationMs, manifest }) {
31555
33132
  const counts = tests.total > 0 ? `${tests.passed}/${tests.total} tests ` : "";
31556
- deps.stdout.write(` ${styleText3("green", "✓")} ${counts}passed ${styleText3("dim", fmtDuration(durationMs))}
33133
+ deps.stdout.write(` ${styleText5("green", "✓")} ${counts}passed ${styleText5("dim", fmtDuration(durationMs))}
31557
33134
  `);
31558
33135
  if (manifest)
31559
33136
  deps.stdout.write(fmtStampLine(manifest));
@@ -31564,8 +33141,8 @@ function createConsoleReporter(deps) {
31564
33141
  `);
31565
33142
  const indent = " ";
31566
33143
  const formatted = [
31567
- styleText3("red", `${indent}${firstLine}`),
31568
- ...restLines.map((line) => styleText3("dim", `${indent}${line}`))
33144
+ styleText5("red", `${indent}${firstLine}`),
33145
+ ...restLines.map((line) => styleText5("dim", `${indent}${line}`))
31569
33146
  ].join(`
31570
33147
  `);
31571
33148
  deps.stderr.write(`${formatted}
@@ -31573,12 +33150,12 @@ function createConsoleReporter(deps) {
31573
33150
  `);
31574
33151
  const hint = flowFailureHint(errStr, deps.projectDir);
31575
33152
  if (hint !== undefined) {
31576
- deps.stderr.write(`${styleText3("yellow", `${indent}${hint}`)}
33153
+ deps.stderr.write(`${styleText5("yellow", `${indent}${hint}`)}
31577
33154
 
31578
33155
  `);
31579
33156
  }
31580
33157
  const counts = tests.total > 0 ? `${tests.passed}/${tests.total} tests ` : "";
31581
- deps.stdout.write(` ${styleText3("red", "✗")} ${counts}passed ${styleText3("dim", fmtDuration(durationMs))}
33158
+ deps.stdout.write(` ${styleText5("red", "✗")} ${counts}passed ${styleText5("dim", fmtDuration(durationMs))}
31582
33159
  `);
31583
33160
  if (manifest)
31584
33161
  deps.stdout.write(fmtStampLine(manifest));
@@ -31593,21 +33170,21 @@ ${runnerMessages.retrying(attempt, maxAttempts)}
31593
33170
  if (total <= 1)
31594
33171
  return;
31595
33172
  const allPassed = summary.flowsFailed === 0;
31596
- const icon = styleText3(allPassed ? "green" : "red", allPassed ? "✓" : "✗");
33173
+ const icon = styleText5(allPassed ? "green" : "red", allPassed ? "✓" : "✗");
31597
33174
  const skippedStr = summary.flowsSkipped > 0 ? ` (${summary.flowsSkipped} skipped)` : "";
31598
33175
  const flowCount = `${summary.flowsPassed}/${total} flows passed${skippedStr}`;
31599
33176
  const testCount = summary.testsTotal > 0 ? ` · ${summary.testsPassed}/${summary.testsTotal} tests passed` : "";
31600
33177
  const width = deps.columns ?? process.stdout.columns ?? 72;
31601
33178
  deps.stdout.write(`
31602
- ${styleText3("dim", "─".repeat(width))}
33179
+ ${styleText5("dim", "─".repeat(width))}
31603
33180
  `);
31604
- deps.stdout.write(` ${icon} ${flowCount}${testCount} ${styleText3("dim", fmtDuration(summary.durationMs))}
33181
+ deps.stdout.write(` ${icon} ${flowCount}${testCount} ${styleText5("dim", fmtDuration(summary.durationMs))}
31605
33182
  `);
31606
33183
  }
31607
33184
  };
31608
33185
  }
31609
33186
  // src/shell/reporter/createJUnitReporter.ts
31610
- import { join as join43 } from "node:path";
33187
+ import { join as join44 } from "node:path";
31611
33188
 
31612
33189
  // src/shell/reporter/junitXml.ts
31613
33190
  function escapeXml(str) {
@@ -31646,7 +33223,7 @@ ${suites}`;
31646
33223
 
31647
33224
  // src/shell/reporter/createJUnitReporter.ts
31648
33225
  function resolveJUnitOutputPath(junit, outputDir) {
31649
- return typeof junit === "string" && junit.trim() !== "" ? junit : join43(outputDir, "junit-report.xml");
33226
+ return typeof junit === "string" && junit.trim() !== "" ? junit : join44(outputDir, "junit-report.xml");
31650
33227
  }
31651
33228
  function createJUnitReporter(deps) {
31652
33229
  const flows = [];
@@ -31737,11 +33314,11 @@ function buildFlowsRunDeps(args) {
31737
33314
 
31738
33315
  // src/commands/flows/loadEnvFile.ts
31739
33316
  import { readFile } from "node:fs/promises";
31740
- import { join as join44 } from "node:path";
33317
+ import { join as join45 } from "node:path";
31741
33318
  async function loadEnvFile(envDir) {
31742
33319
  let content;
31743
33320
  try {
31744
- content = await readFile(join44(envDir, ".env"), "utf8");
33321
+ content = await readFile(join45(envDir, ".env"), "utf8");
31745
33322
  } catch (err) {
31746
33323
  if (isNoEntError(err))
31747
33324
  return;
@@ -31786,7 +33363,7 @@ async function runStagedFlows(args) {
31786
33363
  for (const candidate of staged.outerHop.rejected) {
31787
33364
  log.debug(runnerMessages.outerHopCandidateRejected(candidate.dir, candidate.missing));
31788
33365
  }
31789
- const ownModules = projectDir === undefined ? undefined : join45(projectDir, "node_modules");
33366
+ const ownModules = projectDir === undefined ? undefined : join46(projectDir, "node_modules");
31790
33367
  const own = staged.outerHop.rejected.find((c) => c.dir === ownModules);
31791
33368
  if (own !== undefined) {
31792
33369
  ctx.ui.warn(runnerMessages.outerHopFallbackNotice(own.dir, own.missing));
@@ -31882,7 +33459,7 @@ async function chooseEnv(ctx, files, allEnvs) {
31882
33459
  }
31883
33460
 
31884
33461
  // src/commands/flows/hybridRunDefaults.ts
31885
- import { join as join46, resolve as resolve10 } from "node:path";
33462
+ import { join as join47, resolve as resolve10 } from "node:path";
31886
33463
 
31887
33464
  // src/core/selectFlowFiles.ts
31888
33465
  function selectFlowFiles(args) {
@@ -31987,7 +33564,7 @@ async function handleHybridFlowsRun(ctx, pattern, flags, options = {}) {
31987
33564
  if (validation !== "ok") {
31988
33565
  return { error: validation.error, exitCode: 2 };
31989
33566
  }
31990
- const envDir = resolve10(join46(".qawolf", flags.env));
33567
+ const envDir = resolve10(join47(".qawolf", flags.env));
31991
33568
  const patternArgs = buildPatternArgs(pattern);
31992
33569
  const globFlows = () => resolvedDeps.expandPatterns(patternArgs, envDir, ctx.log("flows"));
31993
33570
  let files = await globFlows();
@@ -32183,12 +33760,12 @@ function registerFlowsCommand(program, signals, deps = { withResolvedEnv }) {
32183
33760
  }
32184
33761
 
32185
33762
  // src/domains/init/init.ts
32186
- import { dirname as dirname14, join as join48, relative as relative9 } from "node:path";
33763
+ import { dirname as dirname14, join as join49, relative as relative9 } from "node:path";
32187
33764
 
32188
33765
  // src/domains/init/ensurePackageJson.ts
32189
- import { join as join47 } from "node:path";
33766
+ import { join as join48 } from "node:path";
32190
33767
  async function ensurePackageJson(ctx, yes, deps) {
32191
- const pkgPath = join47(deps.cwd, "package.json");
33768
+ const pkgPath = join48(deps.cwd, "package.json");
32192
33769
  if (!await deps.fs.pathExists(pkgPath)) {
32193
33770
  const confirmed = await ctx.ui.confirm(initMessages.createPackageJsonPrompt, { yes });
32194
33771
  if (!confirmed.ok || !confirmed.value) {
@@ -32314,9 +33891,9 @@ function makeDefaultInitDeps() {
32314
33891
  async function handleInit(ctx, opts, deps = makeDefaultInitDeps()) {
32315
33892
  ctx.ui.gap();
32316
33893
  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");
33894
+ const configPath = join49(deps.cwd, "qawolf.config.ts");
33895
+ const flowPath = join49(deps.cwd, "src", "flows", "example.flow.ts");
33896
+ const gitignorePath = join49(deps.cwd, ".qawolf", ".gitignore");
32320
33897
  await writeWithPrompt(ctx, configPath, qawolfConfigTs, opts.yes, deps);
32321
33898
  await writeWithPrompt(ctx, flowPath, exampleFlowTs, opts.yes, deps);
32322
33899
  await writeWithPrompt(ctx, gitignorePath, qawolfGitignore, opts.yes, deps);
@@ -32350,11 +33927,8 @@ Examples:
32350
33927
  });
32351
33928
  }
32352
33929
 
32353
- // src/domains/install/android/index.ts
32354
- import { parseExecutionTarget as parseExecutionTarget4 } from "@qawolf/flow-targets";
32355
-
32356
33930
  // src/domains/install/android/avd.ts
32357
- import { join as join49 } from "node:path";
33931
+ import { join as join50 } from "node:path";
32358
33932
  async function installAvds(ctx, specs, deps) {
32359
33933
  const sdkManagerPath = sdkManagerBin(deps.androidHome, deps.platform);
32360
33934
  const avdManagerPath = avdManagerBin(deps.androidHome, deps.platform);
@@ -32364,7 +33938,7 @@ async function installAvds(ctx, specs, deps) {
32364
33938
  if (versionResult.exitCode !== 0) {
32365
33939
  throw new Error(installMessages.android.sdkmanagerNotFound(sdkManagerPath));
32366
33940
  }
32367
- const licenseFile = join49(deps.androidHome, "licenses", "android-sdk-license");
33941
+ const licenseFile = join50(deps.androidHome, "licenses", "android-sdk-license");
32368
33942
  if (deps.checkExists(licenseFile)) {
32369
33943
  ctx.ui.info(installMessages.android.licensesAlreadyAccepted);
32370
33944
  } else {
@@ -32379,7 +33953,7 @@ async function installAvds(ctx, specs, deps) {
32379
33953
  for (const spec of specs) {
32380
33954
  if (installedImages.has(spec.systemImage))
32381
33955
  continue;
32382
- const systemImg = join49(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
33956
+ const systemImg = join50(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
32383
33957
  if (deps.checkExists(systemImg)) {
32384
33958
  ctx.ui.info(installMessages.android.systemImageAlreadyInstalled(spec.systemImage));
32385
33959
  installedImages.add(spec.systemImage);
@@ -32492,7 +34066,7 @@ function buildAvdSpecs(targets, arch) {
32492
34066
  const avdName = avdNameForTarget(target);
32493
34067
  if (!avdName || seen.has(avdName))
32494
34068
  continue;
32495
- const parsed = parseExecutionTarget4(target);
34069
+ const parsed = parseExecutionTarget(target);
32496
34070
  if (parsed.platform !== "android")
32497
34071
  continue;
32498
34072
  const { deviceModel, androidVersion } = parsed.meta;
@@ -32881,6 +34455,8 @@ async function handlePublicApiCommand(ctx, spec, options) {
32881
34455
 
32882
34456
  // src/domains/publicApi/skippedContracts.ts
32883
34457
  var skippedContractNames = new Set([
34458
+ "agent.get",
34459
+ "agent.send",
32884
34460
  "flow.list",
32885
34461
  "runner.evaluateSnippet",
32886
34462
  "runner.get",
@@ -32899,34 +34475,6 @@ var skippedContractNames = new Set([
32899
34475
  "runner.terminate"
32900
34476
  ]);
32901
34477
 
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
34478
  // src/commands/publicApi/index.ts
32931
34479
  var groupDescriptions = {
32932
34480
  run: "Trigger and manage QA Wolf runs on the platform"
@@ -33063,14 +34611,12 @@ function parseRunnerName(runnerName) {
33063
34611
 
33064
34612
  // src/domains/interactiveRunner/resolveRunner.ts
33065
34613
  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
- }
34614
+ var chooseRunnerId = (runner, deps) => resolveIdFrom({
34615
+ env: deps.env,
34616
+ environmentVariable: runnerIdEnvironmentVariable,
34617
+ given: runner,
34618
+ readStored: deps.store.readDefaultRunnerId
34619
+ });
33074
34620
  async function resolveRunner(ctx, options, deps) {
33075
34621
  const chosen = await chooseRunnerId(options.runner, deps);
33076
34622
  if (chosen !== undefined) {
@@ -33096,11 +34642,15 @@ async function resolveRunner(ctx, options, deps) {
33096
34642
  type: "failed"
33097
34643
  };
33098
34644
  }
33099
- return { runnerId: launched.value.id, type: "launched" };
34645
+ return {
34646
+ runnerId: launched.value.id,
34647
+ type: "launched",
34648
+ url: launched.value.url
34649
+ };
33100
34650
  }
33101
34651
  function announceRunner(ctx, resolved) {
33102
34652
  if (resolved.type === "launched") {
33103
- ctx.ui.info(interactiveRunnerMessages.launchedForCommand(resolved.runnerId));
34653
+ ctx.ui.info(interactiveRunnerMessages.launchedForCommand(resolved.runnerId, resolved.url));
33104
34654
  }
33105
34655
  }
33106
34656
 
@@ -33241,7 +34791,7 @@ async function handleRunnerExec(ctx, options, deps) {
33241
34791
  }
33242
34792
 
33243
34793
  // src/shell/interactiveRunner/collectRunFiles.ts
33244
- import { join as join51 } from "node:path";
34794
+ import { join as join52 } from "node:path";
33245
34795
 
33246
34796
  // src/core/interactiveRunner/getImports.ts
33247
34797
  function getImports(options) {
@@ -33280,7 +34830,7 @@ function isPathAlias(importPath, paths) {
33280
34830
  }
33281
34831
 
33282
34832
  // src/core/interactiveRunner/resolveImportPath.ts
33283
- import { dirname as dirname15, join as join50, normalize as normalize3 } from "node:path/posix";
34833
+ import { dirname as dirname15, join as join51, normalize as normalize3 } from "node:path/posix";
33284
34834
 
33285
34835
  // src/core/interactiveRunner/tsconfigPaths.ts
33286
34836
  function isRecord4(value) {
@@ -33333,7 +34883,7 @@ function candidatesForExplicitExtension(options) {
33333
34883
  }
33334
34884
  function resolveImportPath(options) {
33335
34885
  const aliasTarget = resolvePathAlias(options.importPath, options.tsconfigPaths);
33336
- const resolvedPath = aliasTarget === undefined ? normalize3(join50(dirname15(options.importingFilePath), options.importPath)) : normalize3(aliasTarget);
34886
+ const resolvedPath = aliasTarget === undefined ? normalize3(join51(dirname15(options.importingFilePath), options.importPath)) : normalize3(aliasTarget);
33337
34887
  const hasExplicitExtension = supportedExtensions.some((extension) => options.importPath.endsWith(extension));
33338
34888
  const candidates = hasExplicitExtension ? candidatesForExplicitExtension({
33339
34889
  importPath: options.importPath,
@@ -33357,7 +34907,7 @@ function resolveImportPath(options) {
33357
34907
  var tsconfigPath = "tsconfig.json";
33358
34908
  async function collectRunFiles(options) {
33359
34909
  const held = await listShippablePaths(options);
33360
- const readFile = (path) => options.fs.readFile(join51(options.cwd, path));
34910
+ const readFile = (path) => options.fs.readFile(join52(options.cwd, path));
33361
34911
  const tsconfigContent = held.has(tsconfigPath) ? await readFile(tsconfigPath).catch(() => {
33362
34912
  return;
33363
34913
  }) : undefined;
@@ -33440,7 +34990,7 @@ function makeRunnerId() {
33440
34990
  }
33441
34991
 
33442
34992
  // src/shell/interactiveRunner/runFilesManifest.ts
33443
- import { join as join52 } from "node:path";
34993
+ import { join as join53 } from "node:path";
33444
34994
  var manifestFileName = "runner-files.json";
33445
34995
  var manifestSchema2 = object({
33446
34996
  files: array(object({ contentHash: string2(), path: string2() })),
@@ -33448,75 +34998,37 @@ var manifestSchema2 = object({
33448
34998
  version: literal(1)
33449
34999
  });
33450
35000
  function makeRunFilesManifestStore(options) {
33451
- const directory = join52(options.cwd, qawolfDir);
33452
- const path = join52(directory, manifestFileName);
33453
- let pendingWrites = 0;
35001
+ const directory = join53(options.cwd, qawolfDir);
35002
+ const path = join53(directory, manifestFileName);
33454
35003
  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
- },
35004
+ read: () => readJsonFile(options.fs, path, manifestSchema2),
33464
35005
  async write(manifest) {
33465
- const pendingPath = `${path}.${String(process.pid)}.${String(++pendingWrites)}.tmp`;
33466
35006
  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);
35007
+ await writeJsonFileAtomically(options.fs, path, manifest);
33470
35008
  }
33471
35009
  };
33472
35010
  }
33473
- function parseJson(text) {
33474
- try {
33475
- return JSON.parse(text);
33476
- } catch {
33477
- return;
33478
- }
33479
- }
33480
35011
 
33481
35012
  // src/shell/interactiveRunner/runnerStore.ts
33482
- import { join as join53 } from "node:path";
33483
- var storeFileName = "runner.json";
35013
+ import { join as join54 } from "node:path";
35014
+ var storeFileName2 = "runner.json";
33484
35015
  var runnersDirName = "runners";
33485
35016
  var storedRunnerSchema = object({
33486
35017
  id: string2(),
33487
35018
  runnerName: string2().optional()
33488
35019
  });
33489
- var storeSchema = object({
35020
+ var storeSchema2 = object({
33490
35021
  defaultRunnerId: string2().optional()
33491
35022
  });
33492
- function parseJson2(text) {
33493
- try {
33494
- return JSON.parse(text);
33495
- } catch {
33496
- return;
33497
- }
33498
- }
33499
- var pendingWrites = 0;
33500
35023
  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
- };
35024
+ const directory = join54(options.cwd, qawolfDir);
35025
+ const path = join54(directory, storeFileName2);
35026
+ const runnersDir = join54(directory, runnersDirName);
35027
+ const runnerPath = (runnerId) => join54(runnersDir, `${encodeURIComponent(runnerId)}.json`);
35028
+ const writeAtomically = (target, contents) => writeJsonFileAtomically(options.fs, target, contents);
33512
35029
  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;
35030
+ const stored = await readJsonFile(options.fs, path, storeSchema2);
35031
+ return stored?.defaultRunnerId;
33520
35032
  };
33521
35033
  const writeDefaultRunnerId = async (runnerId) => {
33522
35034
  await options.fs.mkdir(directory, { recursive: true });
@@ -33538,15 +35050,7 @@ function makeRunnerStore(options) {
33538
35050
  },
33539
35051
  readDefaultRunnerId,
33540
35052
  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
- }));
35053
+ const runners = await Promise.all((await readRunnerFileNames()).map((name) => readJsonFile(options.fs, join54(runnersDir, name), storedRunnerSchema)));
33550
35054
  return runners.filter((runner) => !!runner).sort((left, right) => left.id.localeCompare(right.id));
33551
35055
  },
33552
35056
  async rememberLaunch(runner) {
@@ -33558,13 +35062,13 @@ function makeRunnerStore(options) {
33558
35062
  await Promise.all(runnerIds.map((runnerId) => options.fs.rm(runnerPath(runnerId), { force: true })));
33559
35063
  const names = await readRunnerFileNames();
33560
35064
  await Promise.all(names.map(async (name) => {
33561
- const runnerFile = join53(runnersDir, name);
35065
+ const runnerFile = join54(runnersDir, name);
33562
35066
  const contents = await options.fs.readFile(runnerFile).catch(() => {
33563
35067
  return;
33564
35068
  });
33565
35069
  if (contents === undefined)
33566
35070
  return;
33567
- if (storedRunnerSchema.safeParse(parseJson2(contents)).success)
35071
+ if (storedRunnerSchema.safeParse(parseJson(contents)).success)
33568
35072
  return;
33569
35073
  await options.fs.rm(runnerFile, { force: true });
33570
35074
  }));
@@ -33736,7 +35240,7 @@ function registerRunnerHighlightSelectorCommand(runner, signals) {
33736
35240
  }
33737
35241
 
33738
35242
  // src/domains/interactiveRunner/importPackage.ts
33739
- import { join as join54 } from "node:path";
35243
+ import { join as join55 } from "node:path";
33740
35244
 
33741
35245
  // src/core/interactiveRunner/npmDependencies.ts
33742
35246
  var sectionsInPrecedenceOrder = ["dependencies", "devDependencies"];
@@ -33777,7 +35281,7 @@ function readNpmDependencies(packageJsonContent) {
33777
35281
  // src/domains/interactiveRunner/importPackage.ts
33778
35282
  var defaultPackageVersion = "latest";
33779
35283
  async function handleRunnerImportPackage(ctx, options, deps) {
33780
- const content = await deps.readFile(join54(deps.cwd, runPackageJsonPath)).catch(() => {
35284
+ const content = await deps.readFile(join55(deps.cwd, runPackageJsonPath)).catch(() => {
33781
35285
  return;
33782
35286
  });
33783
35287
  if (content === undefined) {
@@ -33913,27 +35417,50 @@ async function handleRunnerInspect(ctx, options, deps) {
33913
35417
  }
33914
35418
 
33915
35419
  // src/core/interactiveRunner/inspectMobileRequest.ts
35420
+ var blankInspectMobileFlags = {
35421
+ context: undefined,
35422
+ partial: undefined,
35423
+ selector: undefined,
35424
+ strategy: undefined,
35425
+ text: undefined,
35426
+ x: undefined,
35427
+ y: undefined
35428
+ };
33916
35429
  function toNumber(value) {
33917
35430
  return value.trim() === "" ? Number.NaN : Number(value);
33918
35431
  }
33919
- function buildInspectMobileRequest(what, flags) {
33920
- if (flags.by === "point" && (flags.text !== undefined || flags.partial !== undefined)) {
33921
- return {
33922
- error: "--by point matches by pixel, so --text/--partial would be ignored rather than searching by text. Pass --by text instead, or drop --text/--partial.",
33923
- ok: false
33924
- };
35432
+ var groupsOf = (flags) => ({
35433
+ point: flags.x !== undefined || flags.y !== undefined,
35434
+ selector: flags.selector !== undefined || flags.strategy !== undefined,
35435
+ text: flags.text !== undefined || flags.partial !== undefined
35436
+ });
35437
+ function describeGroup(group) {
35438
+ switch (group) {
35439
+ case "point":
35440
+ return "--x/--y";
35441
+ case "selector":
35442
+ return "--selector/--strategy";
35443
+ case "text":
35444
+ return "--text/--partial";
33925
35445
  }
33926
- if (flags.by === "text" && (flags.x !== undefined || flags.y !== undefined)) {
35446
+ }
35447
+ function buildInspectMobileRequest(what, flags) {
35448
+ const groups = groupsOf(flags);
35449
+ const present = ["point", "text", "selector"].filter((group) => groups[group]);
35450
+ if (present.length > 1) {
33927
35451
  return {
33928
- error: "--by text matches by text, so --x/--y would be ignored rather than matching a point. Pass --by point instead, or drop --x/--y.",
35452
+ error: `${describeGroup(present[0])} and ${describeGroup(present[1])} were both passed, but elements are found by a point, by text, or by a selector — never more than one at once. Drop all but one of them.`,
33929
35453
  ok: false
33930
35454
  };
33931
35455
  }
35456
+ const by = present[0];
33932
35457
  const candidate = {
33933
35458
  what,
33934
- ...flags.by === undefined ? {} : { by: flags.by },
35459
+ ...by === undefined ? {} : { by },
33935
35460
  ...flags.context === undefined ? {} : { context: flags.context },
33936
35461
  ...flags.partial === undefined ? {} : { partial: flags.partial },
35462
+ ...flags.selector === undefined ? {} : { selector: flags.selector },
35463
+ ...flags.strategy === undefined ? {} : { strategy: flags.strategy },
33937
35464
  ...flags.text === undefined ? {} : { text: flags.text },
33938
35465
  ...flags.x === undefined ? {} : { x: toNumber(flags.x) },
33939
35466
  ...flags.y === undefined ? {} : { y: toNumber(flags.y) }
@@ -34041,46 +35568,26 @@ async function handleRunnerInspectMobile(ctx, options, deps) {
34041
35568
  // src/commands/runner/inspectMobile.register.ts
34042
35569
  function registerRunnerInspectMobileCommands(inspect, signals) {
34043
35570
  declareCommandKind(inspect.command("session"), "read").description("Print the Appium session's status: ready, or why not").option("--runner <id>", runnerFlagDescription).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerInspectMobile(ctx, {
34044
- flags: {
34045
- by: undefined,
34046
- context: undefined,
34047
- partial: undefined,
34048
- text: undefined,
34049
- x: undefined,
34050
- y: undefined
34051
- },
35571
+ flags: blankInspectMobileFlags,
34052
35572
  runner: opts.runner,
34053
35573
  what: "session"
34054
35574
  }, runnerDeps(ctx)))(opts, command));
34055
35575
  declareCommandKind(inspect.command("contexts"), "read").description("List the WebView contexts available, and which is current").option("--runner <id>", runnerFlagDescription).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerInspectMobile(ctx, {
34056
- flags: {
34057
- by: undefined,
34058
- context: undefined,
34059
- partial: undefined,
34060
- text: undefined,
34061
- x: undefined,
34062
- y: undefined
34063
- },
35576
+ flags: blankInspectMobileFlags,
34064
35577
  runner: opts.runner,
34065
35578
  what: "contexts"
34066
35579
  }, runnerDeps(ctx)))(opts, command));
34067
35580
  declareCommandKind(inspect.command("page-source"), "read").description("Print the current context's page source, as a tree").option("--context <name>", "Read this context instead of the current one").option("--runner <id>", runnerFlagDescription).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerInspectMobile(ctx, {
34068
- flags: {
34069
- by: undefined,
34070
- context: opts.context,
34071
- partial: undefined,
34072
- text: undefined,
34073
- x: undefined,
34074
- y: undefined
34075
- },
35581
+ flags: { ...blankInspectMobileFlags, context: opts.context },
34076
35582
  runner: opts.runner,
34077
35583
  what: "page"
34078
35584
  }, runnerDeps(ctx)))(opts, command));
34079
- declareCommandKind(inspect.command("elements"), "read").description("Find elements at a screen point, or elements carrying some text").requiredOption("--by <by>", "point or text").option("--context <name>", "Read this context instead of the current one").option("--partial", "text: match text containing this, rather than exactly this").option("--runner <id>", runnerFlagDescription).option("--text <text>", "text: the text to match").option("--x <pixels>", "point: whole pixels on the device's own screen").option("--y <pixels>", "point: whole pixels on the device's own screen").action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerInspectMobile(ctx, {
35585
+ declareCommandKind(inspect.command("elements"), "read").description("Find elements at a screen point, carrying some text, or matching a selector").option("--context <name>", "Read this context instead of the current one").option("--partial", "text: match text containing this, rather than exactly this").option("--runner <id>", runnerFlagDescription).option("--selector <selector>", "Resolved the same way a screen object's own selector is").option("--strategy <strategy>", "selector: xpath, ios-predicate, or shadow (defaults to xpath)").option("--text <text>", "text: the text to match").option("--x <pixels>", "point: whole pixels on the device's own screen").option("--y <pixels>", "point: whole pixels on the device's own screen").action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerInspectMobile(ctx, {
34080
35586
  flags: {
34081
- by: opts.by,
34082
35587
  context: opts.context,
34083
35588
  partial: opts.partial,
35589
+ selector: opts.selector,
35590
+ strategy: opts.strategy,
34084
35591
  text: opts.text,
34085
35592
  x: opts.x,
34086
35593
  y: opts.y
@@ -34099,8 +35606,9 @@ Examples:
34099
35606
  $ qawolf runner inspect session
34100
35607
  $ qawolf runner inspect contexts
34101
35608
  $ qawolf runner inspect page-source --context WEBVIEW_1
34102
- $ qawolf runner inspect elements --by point --x 200 --y 400
34103
- $ qawolf runner inspect elements --by text --text "Sign in" --partial`;
35609
+ $ qawolf runner inspect elements --x 200 --y 400
35610
+ $ qawolf runner inspect elements --text "Sign in" --partial
35611
+ $ qawolf runner inspect elements --selector "//android.widget.Button[@text='Sign in']"`;
34104
35612
  function registerRunnerInspectCommands(runner, signals) {
34105
35613
  const inspect = runner.command("inspect").description("Read one thing off a runner's live page (browser) or Appium session (mobile)").addHelpText("after", inspectExamples);
34106
35614
  declareCommandKind(inspect.command("element-html"), "read").description("Print the HTML of the first element a selector matches").requiredOption("--selector <selector>", "Playwright selector to inspect").option("--runner <id>", runnerFlagDescription).action((opts, command) => withAuthContext(signals, (ctx) => handleRunnerInspect(ctx, {
@@ -34519,7 +36027,8 @@ async function handleRunnerLaunch(ctx, options, deps) {
34519
36027
  if (envRunnerId && envRunnerId !== launched.value.id) {
34520
36028
  ctx.ui.warn(interactiveRunnerMessages.envRunnerIdShadowsLaunch(launched.value.id, envRunnerId));
34521
36029
  }
34522
- ctx.ui.output(launched.value, launched.value.alreadyRunning ? interactiveRunnerMessages.alreadyRunning(launched.value.id) : interactiveRunnerMessages.launched(launched.value.id));
36030
+ const runner = launched.value;
36031
+ ctx.ui.output(runner, runner.alreadyRunning ? interactiveRunnerMessages.alreadyRunning(runner.id, runner.url) : interactiveRunnerMessages.launched(runner.id, runner.url));
34523
36032
  return;
34524
36033
  }
34525
36034
 
@@ -34557,7 +36066,8 @@ async function listRunners(ctx, deps) {
34557
36066
  id: runner.id,
34558
36067
  isDefault: runner.id === defaultRunnerId,
34559
36068
  launchedHere: heldIds.has(runner.id),
34560
- runnerName: runner.runnerName
36069
+ runnerName: runner.runnerName,
36070
+ url: runner.url
34561
36071
  }));
34562
36072
  return {
34563
36073
  items: items.sort((left, right) => rank(left) - rank(right) || left.id.localeCompare(right.id)),
@@ -34714,16 +36224,6 @@ function registerRunnerPromoteSnapshotCommand(runner, signals) {
34714
36224
 
34715
36225
  // src/core/interactiveRunner/followTimeout.ts
34716
36226
  var defaultFollowTimeoutSeconds = 3600;
34717
- var followTimeoutSchema = exports_coerce.number().int().positive();
34718
- function parseFollowTimeout(seconds) {
34719
- if (seconds === undefined) {
34720
- return { ok: true, seconds: defaultFollowTimeoutSeconds };
34721
- }
34722
- const parsed = followTimeoutSchema.safeParse(seconds);
34723
- if (!parsed.success)
34724
- return { error: prettifyError(parsed.error), ok: false };
34725
- return { ok: true, seconds: parsed.data };
34726
- }
34727
36227
 
34728
36228
  // src/core/interactiveRunner/journal.ts
34729
36229
  function formatJournalLine(entry, options) {
@@ -34853,12 +36353,12 @@ function createUnreachableBudget(pollIntervalMs) {
34853
36353
  }
34854
36354
 
34855
36355
  // src/domains/interactiveRunner/events.ts
34856
- var pollIntervalMs2 = 1000;
36356
+ var pollIntervalMs3 = 1000;
34857
36357
  async function handleRunnerEvents(ctx, options, deps) {
34858
36358
  const parsed = parseEventsOptions(options);
34859
36359
  if (!parsed.ok)
34860
36360
  return { error: parsed.error, exitCode: exitCodes.invalidArgs };
34861
- const timeout = parseFollowTimeout(options.timeout);
36361
+ const timeout = parseFollowTimeout(options.timeout, defaultFollowTimeoutSeconds);
34862
36362
  if (!timeout.ok) {
34863
36363
  return { error: timeout.error, exitCode: exitCodes.invalidArgs };
34864
36364
  }
@@ -34871,8 +36371,8 @@ async function handleRunnerEvents(ctx, options, deps) {
34871
36371
  ctx.ui.warn(interactiveRunnerMessages.unknownStream(parsed.value.stream, known));
34872
36372
  }
34873
36373
  const read = createJournalCursor(ctx, resolved.runnerId, parsed.value);
34874
- const unreachable = createUnreachableBudget(pollIntervalMs2);
34875
- const maxPolls = Math.max(1, Math.ceil(timeout.seconds * 1000 / pollIntervalMs2));
36374
+ const unreachable = createUnreachableBudget(pollIntervalMs3);
36375
+ const maxPolls = Math.max(1, Math.ceil(timeout.seconds * 1000 / pollIntervalMs3));
34876
36376
  for (let poll = 1;; poll++) {
34877
36377
  const window = await read();
34878
36378
  if (window.type === "failed")
@@ -34898,7 +36398,7 @@ async function handleRunnerEvents(ctx, options, deps) {
34898
36398
  exitCode: exitCodes.timeout
34899
36399
  };
34900
36400
  }
34901
- await deps.sleep(pollIntervalMs2);
36401
+ await deps.sleep(pollIntervalMs3);
34902
36402
  }
34903
36403
  }
34904
36404
 
@@ -34963,8 +36463,8 @@ function createFollowPrinters(ctx, options) {
34963
36463
  }
34964
36464
 
34965
36465
  // src/domains/interactiveRunner/followRun.ts
34966
- var pollIntervalMs3 = 1000;
34967
- function reportSettlement(ctx, settlement) {
36466
+ var pollIntervalMs4 = 1000;
36467
+ function reportSettlement2(ctx, settlement) {
34968
36468
  if (settlement.type === "passed") {
34969
36469
  ctx.ui.success(interactiveRunnerMessages.runPassed);
34970
36470
  return;
@@ -34980,7 +36480,7 @@ async function followRun(ctx, options, deps) {
34980
36480
  runId: options.runId,
34981
36481
  stream: "run-status"
34982
36482
  });
34983
- const unreachable = createUnreachableBudget(pollIntervalMs3);
36483
+ const unreachable = createUnreachableBudget(pollIntervalMs4);
34984
36484
  const printAll = async () => {
34985
36485
  for (const print of printers) {
34986
36486
  const window = await print();
@@ -34999,7 +36499,7 @@ async function followRun(ctx, options, deps) {
34999
36499
  progressReported = true;
35000
36500
  ctx.ui.stream(entry, interactiveRunnerMessages.runInProgress);
35001
36501
  };
35002
- const maxPolls = Math.max(1, Math.ceil(options.timeoutSeconds * 1000 / pollIntervalMs3));
36502
+ const maxPolls = Math.max(1, Math.ceil(options.timeoutSeconds * 1000 / pollIntervalMs4));
35003
36503
  for (let poll = 1;; poll++) {
35004
36504
  const interrupted = await printAll();
35005
36505
  if (interrupted?.type === "failed")
@@ -35018,7 +36518,7 @@ async function followRun(ctx, options, deps) {
35018
36518
  if (await printAll() !== undefined) {
35019
36519
  ctx.ui.warn(interactiveRunnerMessages.followEndCutShort);
35020
36520
  }
35021
- return reportSettlement(ctx, settlement);
36521
+ return reportSettlement2(ctx, settlement);
35022
36522
  }
35023
36523
  }
35024
36524
  if (poll >= maxPolls) {
@@ -35027,7 +36527,7 @@ async function followRun(ctx, options, deps) {
35027
36527
  exitCode: exitCodes.timeout
35028
36528
  };
35029
36529
  }
35030
- await deps.sleep(pollIntervalMs3);
36530
+ await deps.sleep(pollIntervalMs4);
35031
36531
  }
35032
36532
  }
35033
36533
 
@@ -35307,7 +36807,7 @@ async function finish(sent, fileSync, options, deps) {
35307
36807
 
35308
36808
  // src/domains/interactiveRunner/runFlow.ts
35309
36809
  async function handleRunnerRun(ctx, options, deps) {
35310
- const timeout = parseFollowTimeout(options.timeout);
36810
+ const timeout = parseFollowTimeout(options.timeout, defaultFollowTimeoutSeconds);
35311
36811
  if (!timeout.ok) {
35312
36812
  return { error: timeout.error, exitCode: exitCodes.invalidArgs };
35313
36813
  }
@@ -35418,6 +36918,7 @@ function createProgram({
35418
36918
  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) => {
35419
36919
  exit(err.exitCode === 0 ? exitCodes.success : exitCodes.invalidArgs);
35420
36920
  });
36921
+ registerAgentCommand(program, signals);
35421
36922
  registerAuthCommand(program, signals);
35422
36923
  registerDoctorCommand(program, signals);
35423
36924
  registerFlowsCommand(program, signals);
@@ -35438,4 +36939,4 @@ createProgram({ signals }).parseAsync().catch(() => {
35438
36939
  process.exitCode = 1;
35439
36940
  }).finally(() => exitWhenIdle(typeof process.exitCode === "number" ? process.exitCode : 0));
35440
36941
 
35441
- //# debugId=AAB38BDE68A61B2564756E2164756E21
36942
+ //# debugId=65A771FAB553A0D964756E2164756E21