vibeview 0.20.4 → 0.21.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.
@@ -2068,16 +2068,33 @@ async function startDevSession(cfg, onEvent = () => {
2068
2068
  const sessionId = created.session_id;
2069
2069
  let stopping = false;
2070
2070
  let tunnel = null;
2071
+ let queueEntryId = null;
2071
2072
  const stopSession = async (code) => {
2072
2073
  if (stopping) return;
2073
2074
  stopping = true;
2074
2075
  tunnel?.stop();
2076
+ if (queueEntryId !== null) {
2077
+ await api.cancelQueueEntry(queueEntryId).catch(() => {
2078
+ });
2079
+ queueEntryId = null;
2080
+ }
2075
2081
  await api.stopSession(sessionId).catch(() => {
2076
2082
  });
2077
2083
  onEvent({ event: "session_ended", session_id: sessionId, code });
2078
2084
  };
2079
2085
  try {
2080
- await api.startSession(sessionId);
2086
+ const started = await api.startSession(sessionId);
2087
+ if (started.status === "queued") {
2088
+ queueEntryId = started.queue_entry_id ?? null;
2089
+ onEvent({
2090
+ event: "queued",
2091
+ session_id: sessionId,
2092
+ position: started.position ?? null,
2093
+ queue_entry_id: queueEntryId
2094
+ });
2095
+ await pollWhileQueued(api, sessionId, QUEUE_WAIT_MS);
2096
+ queueEntryId = null;
2097
+ }
2081
2098
  const pageUrl = `${cfg.webUrl || cfg.baseUrl}/sandbox/${sessionId}`;
2082
2099
  const reportedWarnings = /* @__PURE__ */ new Set();
2083
2100
  if (usesTunnel) {
@@ -2142,6 +2159,25 @@ var BLOCKING_WARNING_PREFIXES = ["No debug build", "App install failed", "App no
2142
2159
  function findBlockingWarning(info) {
2143
2160
  return (info.warnings ?? []).find((w) => BLOCKING_WARNING_PREFIXES.some((p) => w.includes(p)));
2144
2161
  }
2162
+ var QUEUE_WAIT_MS = 30 * 6e4;
2163
+ async function pollWhileQueued(api, sessionId, timeoutMs, intervalMs = 3e3) {
2164
+ const deadline = Date.now() + timeoutMs;
2165
+ for (; ; ) {
2166
+ const info = await api.getSession(sessionId);
2167
+ const blocking = findBlockingWarning(info);
2168
+ if (blocking) throw new Error(blocking);
2169
+ if (info.status === "stopped" || info.status === "failed") {
2170
+ throw new Error(`session ${info.status} while waiting for a device`);
2171
+ }
2172
+ if (info.status !== "pending" && info.status !== "queued") return info;
2173
+ if (Date.now() > deadline) {
2174
+ throw new Error(
2175
+ `no device became free within ${Math.round(timeoutMs / 6e4)} minutes \u2014 try again later or pick another device`
2176
+ );
2177
+ }
2178
+ await new Promise((r) => setTimeout(r, intervalMs));
2179
+ }
2180
+ }
2145
2181
  async function pollUntilTunnelReady(api, sessionId, timeoutMs, intervalMs = 2e3) {
2146
2182
  const deadline = Date.now() + timeoutMs;
2147
2183
  for (; ; ) {
@@ -2537,6 +2573,13 @@ async function runDevInProcess(opts) {
2537
2573
  } else {
2538
2574
  console.error(chalk3.yellow(`\u26A0 ${evt.message}`));
2539
2575
  }
2576
+ } else if (evt.event === "queued") {
2577
+ const where = evt.position ? ` (position ${evt.position})` : "";
2578
+ console.error(
2579
+ chalk3.yellow(
2580
+ `\u23F3 every device is busy \u2014 waiting in the queue${where}, Ctrl-C to give up`
2581
+ )
2582
+ );
2540
2583
  } else if (evt.event === "tunnel_connected") {
2541
2584
  everConnected = true;
2542
2585
  console.error(chalk3.green("\u2713 tunnel connected \u2014 edit away, Fast Refresh is live"));
package/dist/cli.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  readDevState,
16
16
  readMetroState,
17
17
  runAgentVerb
18
- } from "./chunk-BBGXBKJQ.js";
18
+ } from "./chunk-VVKF7GGL.js";
19
19
  import {
20
20
  ApiClient,
21
21
  EXIT_INFRA_ERROR,
@@ -3076,7 +3076,7 @@ function resolveMetadata(opts) {
3076
3076
  }
3077
3077
  var program = new Command();
3078
3078
  program.name("vibeview").description("VibeView CLI -- run mobile tests from CI/CD pipelines").version(pkg.version);
3079
- program.command("run-suite <suite-id>").description("Run all test cases in a suite").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-case breakdown", false).option("--timeout <seconds>", "Max wait time in seconds", "300").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3079
+ program.command("run-suite <suite-id>").description("Run all test cases in a suite").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-case breakdown", false).option("--timeout <seconds>", "Max wait time in seconds (queue wait plus execution)", "1800").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3080
3080
  "--device-category <category>",
3081
3081
  "Device category: phone, tablet, tv (default: phone when --device is not set)",
3082
3082
  parseDeviceCategory
@@ -3104,7 +3104,7 @@ program.command("run-suites").description("Run all suites matching a tag filter"
3104
3104
  ).option("--match <mode>", "any (default) or all", "any").option(
3105
3105
  "--platform <platform>",
3106
3106
  "Filter matched suites to one platform: ios, android, tvos, androidtv (default: no filter)"
3107
- ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-suite progress", false).option("--timeout <seconds>", "Per-suite timeout, capped at 2 hours total", "300").option("--commit-sha <sha>", "Git commit SHA propagated to every SuiteRun").option("--model <model>", "AI model").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3107
+ ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-suite progress", false).option("--timeout <seconds>", "Per-suite timeout, capped at 2 hours total", "1800").option("--commit-sha <sha>", "Git commit SHA propagated to every SuiteRun").option("--model <model>", "AI model").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3108
3108
  "--device-category <category>",
3109
3109
  "Device category: phone, tablet, tv (default: phone when --device is not set)",
3110
3110
  parseDeviceCategory
@@ -3124,7 +3124,7 @@ program.command("run-suites").description("Run all suites matching a tag filter"
3124
3124
  build: opts.build ? parseInt(opts.build, 10) : void 0
3125
3125
  });
3126
3126
  });
3127
- program.command("run-test <test-id>").description("Run a single test case").option("--token <token>", "API token").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show step details", false).option("--timeout <seconds>", "Max wait time in seconds", "300").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3127
+ program.command("run-test <test-id>").description("Run a single test case").option("--token <token>", "API token").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show step details", false).option("--timeout <seconds>", "Max wait time in seconds (queue wait plus execution)", "1800").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3128
3128
  "--device-category <category>",
3129
3129
  "Device category: phone, tablet, tv (default: phone when --device is not set)",
3130
3130
  parseDeviceCategory
@@ -3310,7 +3310,7 @@ program.command("dev-stop").description("Stop a `vibeview dev --detach` session
3310
3310
  program.command("mcp").description(
3311
3311
  "Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop as tools"
3312
3312
  ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
3313
- const { runMcpServer } = await import("./server-WOIB6CDQ.js");
3313
+ const { runMcpServer } = await import("./server-WWNWYD4Q.js");
3314
3314
  await runMcpServer(opts);
3315
3315
  });
3316
3316
  program.command("agent-setup").description(
@@ -11,7 +11,7 @@ import {
11
11
  readDevState,
12
12
  renderEnvelope,
13
13
  startDevSession
14
- } from "./chunk-BBGXBKJQ.js";
14
+ } from "./chunk-VVKF7GGL.js";
15
15
  import {
16
16
  ApiClient,
17
17
  resolveAuth
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeview",
3
- "version": "0.20.4",
3
+ "version": "0.21.0",
4
4
  "description": "VibeView CLI -- run mobile tests from CI/CD pipelines",
5
5
  "mcpName": "io.github.vibeview/vibeview",
6
6
  "type": "module",
@@ -5,8 +5,8 @@ description: Verify a React Native change on a real running app — install/laun
5
5
 
6
6
  # VibeView agent control
7
7
 
8
- Drive a live iOS/Android VibeView session from the CLI to verify a React
9
- Native change actually works, not just that it compiles. Requires the
8
+ Drive a live iOS, Android, Apple TV or Android TV VibeView session from the
9
+ CLI to verify a React Native change actually works, not just that it compiles. Requires the
10
10
  `vibeview` CLI installed and authenticated (`vibeview login`, or
11
11
  `VIBEVIEW_API_TOKEN` set).
12
12
 
@@ -137,13 +137,15 @@ it rather than letting it appear unexplained in their `git status`.
137
137
  ```
138
138
  Use `--press-ms 500` or more for long-press-to-reorder lists, and
139
139
  `--velocity 150` for slow, precise slider adjustments.
140
- - **iOS system alerts** (permission prompts and similar) sit above the app
140
+ - **System alerts** (permission prompts and similar) sit above the app
141
141
  and block it. Call `alert get` first to read the message and the exact
142
142
  button labels, then act on one:
143
143
  ```bash
144
144
  vibeview alert get --session <id>
145
145
  vibeview alert accept --button "Allow" --session <id>
146
146
  ```
147
+ On Apple TV this also covers the "Open in …?" prompt a deep link raises;
148
+ the button is answered with the remote.
147
149
  - Use `open-url` to jump straight to a deep link instead of navigating by
148
150
  hand:
149
151
  ```bash
@@ -200,7 +202,7 @@ human-readable text.
200
202
  | `scroll` | `<direction>` | Scroll the current view up, down, left, or right using a gesture preset. |
201
203
  | `scroll-to` | `<text>` `[--direction <up\|down\|left\|right>] [--max-scrolls <n>] [--element-type <type>]` | Scroll until an element matching the text is visible. Stops at end of list. |
202
204
  | `drag` | `<from>` `<to>` `[--velocity <n>] [--press-ms <n>] [--hold-ms <n>]` | Drag between two points, each an `@ref` or `x,y`. The only precise two-point gesture. |
203
- | `alert` | `<get\|accept\|dismiss>` `[--button <label>]` | Inspect or respond to a visible iOS system alert. |
205
+ | `alert` | `<get\|accept\|dismiss>` `[--button <label>]` | Inspect or respond to a system alert on iOS or Apple TV. |
204
206
  | `type` | `<text>` | Type text into the currently focused input field. |
205
207
  | `clear-text` | — | Clear the text in the currently focused input field. |
206
208
  | `press` | `<button>` | Press a device button or perform a system gesture (home, back, d-pad, etc). |