@validate.qa/runner 1.0.3 → 1.0.4

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
@@ -7552,7 +7552,7 @@ Each entry is exactly one of:
7552
7552
  const systemPrompt = context.mode === "survey" ? SURVEY_SYSTEM_PROMPT : exports2.SCENARIO_SYSTEM_PROMPT;
7553
7553
  const userPrompt = formatEvidenceForPlanner(compressed, evidence, context);
7554
7554
  logs2.push(`AI planner: ~${Math.ceil(userPrompt.length / 3.5)} input tokens`);
7555
- const aiClient = options.modelOverride ? (0, ai_provider_js_1.getClientForModel)(options.modelOverride) : (0, ai_provider_js_1.getAIClient)();
7555
+ const aiClient = options.aiClient ?? (options.modelOverride ? (0, ai_provider_js_1.getClientForModel)(options.modelOverride) : (0, ai_provider_js_1.getAIClient)());
7556
7556
  const messages = [
7557
7557
  { role: "system", content: systemPrompt },
7558
7558
  { role: "user", content: userPrompt }
@@ -7764,6 +7764,7 @@ var require_mobile_discovery_prompts = __commonJS({
7764
7764
  exports2.formatMobileEvidenceForPlanner = formatMobileEvidenceForPlanner;
7765
7765
  exports2.buildMobileSiteMap = buildMobileSiteMap;
7766
7766
  exports2.runMobileAIPlanner = runMobileAIPlanner;
7767
+ var ai_provider_js_1 = require_ai_provider();
7767
7768
  var discover_ai_planner_js_1 = require_discover_ai_planner();
7768
7769
  exports2.MOBILE_SURVEY_SYSTEM_PROMPT = `You are a feature analyst for a native mobile application (iOS or Android). You receive evidence from an AI explorer that mapped the app's screens by tapping, typing, and swiping through the live UI. Your job is to organize screens into meaningful feature groups.
7769
7770
 
@@ -8163,6 +8164,8 @@ ${constraints.join("\n")}`);
8163
8164
  }
8164
8165
  async function runMobileAIPlanner(input, options = {}) {
8165
8166
  const planner = options.planner ?? discover_ai_planner_js_1.runAIPlanner;
8167
+ const shouldBuildMobileAIClient = (process.env.SERVER_URL ?? "").trim().length > 0 || !options.planner;
8168
+ const aiClient = options.aiClient ?? (shouldBuildMobileAIClient ? options.modelOverride ? (0, ai_provider_js_1.getMobileClientForModel)(options.modelOverride) : (0, ai_provider_js_1.getMobileAIClient)() : void 0);
8166
8169
  const siteMap = buildMobileSiteMap(input.graph, {
8167
8170
  projectId: input.projectId,
8168
8171
  appId: input.appId
@@ -8188,7 +8191,8 @@ ${constraints.join("\n")}`);
8188
8191
  testBudget: input.testBudget
8189
8192
  }, {
8190
8193
  modelOverride: options.modelOverride,
8191
- onAICall: options.onAICall
8194
+ onAICall: options.onAICall,
8195
+ ...aiClient ? { aiClient } : {}
8192
8196
  });
8193
8197
  }
8194
8198
  }
@@ -324796,7 +324800,7 @@ var require_package4 = __commonJS({
324796
324800
  "package.json"(exports2, module2) {
324797
324801
  module2.exports = {
324798
324802
  name: "@validate.qa/runner",
324799
- version: "1.0.3",
324803
+ version: "1.0.4",
324800
324804
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
324801
324805
  bin: {
324802
324806
  "validate-runner": "dist/cli.js"
@@ -326314,6 +326318,7 @@ var polling = false;
326314
326318
  var sessionStartInFlight = null;
326315
326319
  var sessionReaperTimer = null;
326316
326320
  var mirrorClientsEmptyAt = null;
326321
+ var mirrorSessionOwner = null;
326317
326322
  var executorBusy = false;
326318
326323
  var rateLimitBuckets = /* @__PURE__ */ new Map();
326319
326324
  function isRateLimited(clientIp) {
@@ -326650,6 +326655,7 @@ async function startSession(validated, res) {
326650
326655
  }
326651
326656
  state.appiumSessionId = null;
326652
326657
  state.platform = null;
326658
+ mirrorSessionOwner = null;
326653
326659
  }
326654
326660
  stopAllMirrorClients();
326655
326661
  const realCaps = isPhysicalDevice(deviceId, targetPlatform) ? realDeviceCapabilities2(targetPlatform) : {};
@@ -326680,11 +326686,14 @@ async function startSession(validated, res) {
326680
326686
  })
326681
326687
  });
326682
326688
  state.appiumSessionId = result?.value?.sessionId ?? result?.sessionId ?? null;
326683
- state.platform = targetPlatform === "IOS" ? "IOS" : "ANDROID";
326684
326689
  if (!state.appiumSessionId) {
326690
+ state.platform = null;
326691
+ mirrorSessionOwner = null;
326685
326692
  sendJSON(res, 500, { error: "Appium session creation returned no session ID" });
326686
326693
  return;
326687
326694
  }
326695
+ state.platform = targetPlatform === "IOS" ? "IOS" : "ANDROID";
326696
+ mirrorSessionOwner = "recording";
326688
326697
  if (launchMode === "DEEP_LINK" && deeplink) {
326689
326698
  try {
326690
326699
  const args = targetPlatform === "IOS" ? [{ url: deeplink, bundleId: appId }] : [{ url: deeplink, package: appId }];
@@ -326709,6 +326718,7 @@ async function handleSessionStop(_req, res) {
326709
326718
  }
326710
326719
  state.appiumSessionId = null;
326711
326720
  state.platform = null;
326721
+ mirrorSessionOwner = null;
326712
326722
  }
326713
326723
  stopAllMirrorClients();
326714
326724
  stopSessionReaper();
@@ -326789,6 +326799,7 @@ async function pollAndBroadcastFrame() {
326789
326799
  if (!alive) {
326790
326800
  state.appiumSessionId = null;
326791
326801
  state.platform = null;
326802
+ mirrorSessionOwner = null;
326792
326803
  stopAllMirrorClients();
326793
326804
  return;
326794
326805
  }
@@ -326859,6 +326870,7 @@ async function reapIdleSession() {
326859
326870
  const sessionId = state.appiumSessionId;
326860
326871
  state.appiumSessionId = null;
326861
326872
  state.platform = null;
326873
+ mirrorSessionOwner = null;
326862
326874
  mirrorClientsEmptyAt = null;
326863
326875
  stopSessionReaper();
326864
326876
  try {
@@ -327169,6 +327181,7 @@ async function stopMobileBridge() {
327169
327181
  }
327170
327182
  state.appiumSessionId = null;
327171
327183
  state.platform = null;
327184
+ mirrorSessionOwner = null;
327172
327185
  }
327173
327186
  if (ngrokProcess) {
327174
327187
  try {
@@ -327199,13 +327212,37 @@ function getBridgeState() {
327199
327212
  tunnelUrl: state.tunnelUrl,
327200
327213
  tunnelToken: state.tunnelToken,
327201
327214
  hasActiveSession: !!state.appiumSessionId,
327202
- recordingActive: !!state.appiumSessionId,
327215
+ recordingActive: !!state.appiumSessionId && mirrorSessionOwner === "recording",
327203
327216
  platform: state.platform
327204
327217
  };
327205
327218
  }
327206
327219
  function setExecutorBusy(busy) {
327207
327220
  executorBusy = busy;
327208
327221
  }
327222
+ function attachMirrorSession(sessionId, platform3) {
327223
+ validateSessionId(sessionId);
327224
+ if (state.appiumSessionId && state.appiumSessionId !== sessionId) {
327225
+ stopAllMirrorClients();
327226
+ }
327227
+ state.appiumSessionId = sessionId;
327228
+ state.platform = platform3;
327229
+ mirrorSessionOwner = "runner";
327230
+ mirrorNullTicks = 0;
327231
+ mirrorClientsEmptyAt = null;
327232
+ ensureSessionReaper();
327233
+ if (state.mirrorClients.size > 0) {
327234
+ ensureMirrorPolling();
327235
+ void pollAndBroadcastFrame();
327236
+ }
327237
+ }
327238
+ function detachMirrorSession(sessionId) {
327239
+ if (sessionId && state.appiumSessionId && state.appiumSessionId !== sessionId) return;
327240
+ state.appiumSessionId = null;
327241
+ state.platform = null;
327242
+ mirrorSessionOwner = null;
327243
+ stopAllMirrorClients();
327244
+ stopSessionReaper();
327245
+ }
327209
327246
 
327210
327247
  // src/services/mobile/mobile-bridge-driver.ts
327211
327248
  var import_runner_core7 = __toESM(require_dist2());
@@ -329864,6 +329901,7 @@ ${finalVerifyResult.logs ?? ""}`;
329864
329901
  deeplink: mobileTarget.deeplink
329865
329902
  });
329866
329903
  mobileOnLog(`Appium session created: ${session.sessionId} on device ${session.deviceId}`);
329904
+ attachMirrorSession(session.sessionId, platform3);
329867
329905
  const driver = new MobileBridgeDriver({
329868
329906
  sessionId: session.sessionId,
329869
329907
  platform: platform3,
@@ -329904,6 +329942,7 @@ ${finalVerifyResult.logs ?? ""}`;
329904
329942
  }
329905
329943
  }
329906
329944
  if (session) {
329945
+ detachMirrorSession(session.sessionId);
329907
329946
  try {
329908
329947
  await session.stop();
329909
329948
  } catch {
package/dist/cli.mjs CHANGED
@@ -7557,7 +7557,7 @@ Each entry is exactly one of:
7557
7557
  const systemPrompt = context.mode === "survey" ? SURVEY_SYSTEM_PROMPT : exports.SCENARIO_SYSTEM_PROMPT;
7558
7558
  const userPrompt = formatEvidenceForPlanner(compressed, evidence, context);
7559
7559
  logs2.push(`AI planner: ~${Math.ceil(userPrompt.length / 3.5)} input tokens`);
7560
- const aiClient = options.modelOverride ? (0, ai_provider_js_1.getClientForModel)(options.modelOverride) : (0, ai_provider_js_1.getAIClient)();
7560
+ const aiClient = options.aiClient ?? (options.modelOverride ? (0, ai_provider_js_1.getClientForModel)(options.modelOverride) : (0, ai_provider_js_1.getAIClient)());
7561
7561
  const messages = [
7562
7562
  { role: "system", content: systemPrompt },
7563
7563
  { role: "user", content: userPrompt }
@@ -7769,6 +7769,7 @@ var require_mobile_discovery_prompts = __commonJS({
7769
7769
  exports.formatMobileEvidenceForPlanner = formatMobileEvidenceForPlanner;
7770
7770
  exports.buildMobileSiteMap = buildMobileSiteMap;
7771
7771
  exports.runMobileAIPlanner = runMobileAIPlanner;
7772
+ var ai_provider_js_1 = require_ai_provider();
7772
7773
  var discover_ai_planner_js_1 = require_discover_ai_planner();
7773
7774
  exports.MOBILE_SURVEY_SYSTEM_PROMPT = `You are a feature analyst for a native mobile application (iOS or Android). You receive evidence from an AI explorer that mapped the app's screens by tapping, typing, and swiping through the live UI. Your job is to organize screens into meaningful feature groups.
7774
7775
 
@@ -8168,6 +8169,8 @@ ${constraints.join("\n")}`);
8168
8169
  }
8169
8170
  async function runMobileAIPlanner(input, options = {}) {
8170
8171
  const planner = options.planner ?? discover_ai_planner_js_1.runAIPlanner;
8172
+ const shouldBuildMobileAIClient = (process.env.SERVER_URL ?? "").trim().length > 0 || !options.planner;
8173
+ const aiClient = options.aiClient ?? (shouldBuildMobileAIClient ? options.modelOverride ? (0, ai_provider_js_1.getMobileClientForModel)(options.modelOverride) : (0, ai_provider_js_1.getMobileAIClient)() : void 0);
8171
8174
  const siteMap = buildMobileSiteMap(input.graph, {
8172
8175
  projectId: input.projectId,
8173
8176
  appId: input.appId
@@ -8193,7 +8196,8 @@ ${constraints.join("\n")}`);
8193
8196
  testBudget: input.testBudget
8194
8197
  }, {
8195
8198
  modelOverride: options.modelOverride,
8196
- onAICall: options.onAICall
8199
+ onAICall: options.onAICall,
8200
+ ...aiClient ? { aiClient } : {}
8197
8201
  });
8198
8202
  }
8199
8203
  }
@@ -324801,7 +324805,7 @@ var require_package4 = __commonJS({
324801
324805
  "package.json"(exports, module) {
324802
324806
  module.exports = {
324803
324807
  name: "@validate.qa/runner",
324804
- version: "1.0.3",
324808
+ version: "1.0.4",
324805
324809
  description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
324806
324810
  bin: {
324807
324811
  "validate-runner": "dist/cli.js"
@@ -326319,6 +326323,7 @@ var polling = false;
326319
326323
  var sessionStartInFlight = null;
326320
326324
  var sessionReaperTimer = null;
326321
326325
  var mirrorClientsEmptyAt = null;
326326
+ var mirrorSessionOwner = null;
326322
326327
  var executorBusy = false;
326323
326328
  var rateLimitBuckets = /* @__PURE__ */ new Map();
326324
326329
  function isRateLimited(clientIp) {
@@ -326655,6 +326660,7 @@ async function startSession(validated, res) {
326655
326660
  }
326656
326661
  state.appiumSessionId = null;
326657
326662
  state.platform = null;
326663
+ mirrorSessionOwner = null;
326658
326664
  }
326659
326665
  stopAllMirrorClients();
326660
326666
  const realCaps = isPhysicalDevice(deviceId, targetPlatform) ? realDeviceCapabilities2(targetPlatform) : {};
@@ -326685,11 +326691,14 @@ async function startSession(validated, res) {
326685
326691
  })
326686
326692
  });
326687
326693
  state.appiumSessionId = result?.value?.sessionId ?? result?.sessionId ?? null;
326688
- state.platform = targetPlatform === "IOS" ? "IOS" : "ANDROID";
326689
326694
  if (!state.appiumSessionId) {
326695
+ state.platform = null;
326696
+ mirrorSessionOwner = null;
326690
326697
  sendJSON(res, 500, { error: "Appium session creation returned no session ID" });
326691
326698
  return;
326692
326699
  }
326700
+ state.platform = targetPlatform === "IOS" ? "IOS" : "ANDROID";
326701
+ mirrorSessionOwner = "recording";
326693
326702
  if (launchMode === "DEEP_LINK" && deeplink) {
326694
326703
  try {
326695
326704
  const args = targetPlatform === "IOS" ? [{ url: deeplink, bundleId: appId }] : [{ url: deeplink, package: appId }];
@@ -326714,6 +326723,7 @@ async function handleSessionStop(_req, res) {
326714
326723
  }
326715
326724
  state.appiumSessionId = null;
326716
326725
  state.platform = null;
326726
+ mirrorSessionOwner = null;
326717
326727
  }
326718
326728
  stopAllMirrorClients();
326719
326729
  stopSessionReaper();
@@ -326794,6 +326804,7 @@ async function pollAndBroadcastFrame() {
326794
326804
  if (!alive) {
326795
326805
  state.appiumSessionId = null;
326796
326806
  state.platform = null;
326807
+ mirrorSessionOwner = null;
326797
326808
  stopAllMirrorClients();
326798
326809
  return;
326799
326810
  }
@@ -326864,6 +326875,7 @@ async function reapIdleSession() {
326864
326875
  const sessionId = state.appiumSessionId;
326865
326876
  state.appiumSessionId = null;
326866
326877
  state.platform = null;
326878
+ mirrorSessionOwner = null;
326867
326879
  mirrorClientsEmptyAt = null;
326868
326880
  stopSessionReaper();
326869
326881
  try {
@@ -327174,6 +327186,7 @@ async function stopMobileBridge() {
327174
327186
  }
327175
327187
  state.appiumSessionId = null;
327176
327188
  state.platform = null;
327189
+ mirrorSessionOwner = null;
327177
327190
  }
327178
327191
  if (ngrokProcess) {
327179
327192
  try {
@@ -327204,13 +327217,37 @@ function getBridgeState() {
327204
327217
  tunnelUrl: state.tunnelUrl,
327205
327218
  tunnelToken: state.tunnelToken,
327206
327219
  hasActiveSession: !!state.appiumSessionId,
327207
- recordingActive: !!state.appiumSessionId,
327220
+ recordingActive: !!state.appiumSessionId && mirrorSessionOwner === "recording",
327208
327221
  platform: state.platform
327209
327222
  };
327210
327223
  }
327211
327224
  function setExecutorBusy(busy) {
327212
327225
  executorBusy = busy;
327213
327226
  }
327227
+ function attachMirrorSession(sessionId, platform3) {
327228
+ validateSessionId(sessionId);
327229
+ if (state.appiumSessionId && state.appiumSessionId !== sessionId) {
327230
+ stopAllMirrorClients();
327231
+ }
327232
+ state.appiumSessionId = sessionId;
327233
+ state.platform = platform3;
327234
+ mirrorSessionOwner = "runner";
327235
+ mirrorNullTicks = 0;
327236
+ mirrorClientsEmptyAt = null;
327237
+ ensureSessionReaper();
327238
+ if (state.mirrorClients.size > 0) {
327239
+ ensureMirrorPolling();
327240
+ void pollAndBroadcastFrame();
327241
+ }
327242
+ }
327243
+ function detachMirrorSession(sessionId) {
327244
+ if (sessionId && state.appiumSessionId && state.appiumSessionId !== sessionId) return;
327245
+ state.appiumSessionId = null;
327246
+ state.platform = null;
327247
+ mirrorSessionOwner = null;
327248
+ stopAllMirrorClients();
327249
+ stopSessionReaper();
327250
+ }
327214
327251
 
327215
327252
  // src/services/mobile/mobile-bridge-driver.ts
327216
327253
  var import_runner_core7 = __toESM(require_dist2());
@@ -329872,6 +329909,7 @@ ${finalVerifyResult.logs ?? ""}`;
329872
329909
  deeplink: mobileTarget.deeplink
329873
329910
  });
329874
329911
  mobileOnLog(`Appium session created: ${session.sessionId} on device ${session.deviceId}`);
329912
+ attachMirrorSession(session.sessionId, platform3);
329875
329913
  const driver = new MobileBridgeDriver({
329876
329914
  sessionId: session.sessionId,
329877
329915
  platform: platform3,
@@ -329912,6 +329950,7 @@ ${finalVerifyResult.logs ?? ""}`;
329912
329950
  }
329913
329951
  }
329914
329952
  if (session) {
329953
+ detachMirrorSession(session.sessionId);
329915
329954
  try {
329916
329955
  await session.stop();
329917
329956
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@validate.qa/runner",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
5
5
  "bin": {
6
6
  "validate-runner": "dist/cli.js"