vibeview 0.37.1 → 0.39.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/README.md CHANGED
@@ -141,11 +141,11 @@ Other MCP clients (Cursor, Claude Desktop, VS Code Copilot) run command
141
141
  # Upload the build under test (zipped simulator .app for iOS, .apk for Android)
142
142
  vibeview upload-app ./MyApp.zip
143
143
 
144
- # Run a single test or a whole suite
144
+ # Run a single test or a whole group
145
145
  vibeview run-test <test-id> --output json
146
146
  vibeview run-suite <suite-id> --output junit
147
147
 
148
- # Run several suites with an aggregated exit code
148
+ # Run several groups with an aggregated exit code
149
149
  vibeview run-suites --tag smoke
150
150
  ```
151
151
 
@@ -181,11 +181,12 @@ Output formats: `human`, `json`, and `junit` for any CI system. The exit code re
181
181
  | `vibeview list-apps` | List your apps |
182
182
  | `vibeview list-builds <app>` | List an app's uploaded builds (ids for `dev --build <id>`, `run-test --build`) |
183
183
  | `vibeview list-devices` | List available device types |
184
- | `vibeview list-suites` | List test suites |
184
+ | `vibeview list-suites` | List groups (the CLI calls them suites) |
185
185
  | `vibeview create-test [name]` | Create a test case |
186
+ | `vibeview import-maestro <path>` | Import Maestro flows (file, `.zip` or folder) as tests; `--dry-run` previews |
186
187
  | `vibeview run-test <test-id>` | Run a single test |
187
- | `vibeview run-suite <suite-id>` | Run a test suite |
188
- | `vibeview run-suites` | Run multiple suites |
188
+ | `vibeview run-suite <suite-id>` | Run a test group |
189
+ | `vibeview run-suites` | Run multiple groups |
189
190
 
190
191
  Run any command with `--help` for its options.
191
192
 
@@ -13,7 +13,32 @@ import {
13
13
  resolveArtifact,
14
14
  resolveAuth,
15
15
  uploadAppBuild
16
- } from "./chunk-QRJAFBH3.js";
16
+ } from "./chunk-MYXUGCR5.js";
17
+
18
+ // src/lib/formatters/not-checked.ts
19
+ function notCheckedSteps(steps) {
20
+ return (steps ?? []).filter((s) => s.status === "not_checked");
21
+ }
22
+ function runStepsNotChecked(result) {
23
+ if (typeof result.steps_not_checked === "number") return result.steps_not_checked;
24
+ return notCheckedSteps(result.steps).length;
25
+ }
26
+ function stepsNotCheckedLabel(n) {
27
+ return `${n} step${n === 1 ? "" : "s"} not checked`;
28
+ }
29
+ function notCheckedSuffix(status, n) {
30
+ return status === "passed" && n && n > 0 ? ` (${stepsNotCheckedLabel(n)})` : "";
31
+ }
32
+ function outcomeOf(r) {
33
+ if (r.not_checked === true || r.status === "not_checked") return "not_checked";
34
+ if (r.not_run === true || r.status === "cancelled" && isNotRunMessage(r.error_message)) {
35
+ return "not_run";
36
+ }
37
+ return r.status ?? "";
38
+ }
39
+ function isNotRunMessage(message) {
40
+ return (message ?? "").startsWith("Not run:");
41
+ }
17
42
 
18
43
  // src/lib/dev-config.ts
19
44
  import { readFileSync, renameSync, rmSync, writeFileSync } from "fs";
@@ -4658,10 +4683,10 @@ var AGENT_COMMANDS = [
4658
4683
  {
4659
4684
  // relaunch_app: {} — terminate the session's app and launch it again the
4660
4685
  // way the session launched it (same launch params); its data is kept.
4661
- // Refused on embed sessions (their watchdog owns launching) and on Roku.
4686
+ // Refused on embed sessions (their watchdog owns launching).
4662
4687
  verb: "relaunch-app",
4663
4688
  tool: "relaunch_app",
4664
- summary: "Close the app under test and start it again: a cold start that keeps the app's data, e.g. to check that something persists. Its log keeps streaming across the restart. Not available on Roku or embed sessions.",
4689
+ summary: "Close the app under test and start it again: a cold start that keeps the app's data, e.g. to check that something persists. Its log keeps streaming across the restart. Not available on embed sessions.",
4665
4690
  mutating: true,
4666
4691
  params: []
4667
4692
  },
@@ -5168,6 +5193,11 @@ async function runAgentVerb(cmd, positionals2, opts) {
5168
5193
  }
5169
5194
 
5170
5195
  export {
5196
+ notCheckedSteps,
5197
+ runStepsNotChecked,
5198
+ stepsNotCheckedLabel,
5199
+ notCheckedSuffix,
5200
+ outcomeOf,
5171
5201
  DEV_PLATFORMS,
5172
5202
  PLATFORM_NEEDS_METRO,
5173
5203
  isDevPlatform,
@@ -163,9 +163,16 @@ async function parseJsonBody(resp) {
163
163
  return withUtcTimestamps(await resp.json());
164
164
  }
165
165
  var ApiClient = class {
166
- constructor(baseUrl, token) {
166
+ constructor(baseUrl, token, opts = {}) {
167
167
  this.baseUrl = baseUrl;
168
168
  this.token = token;
169
+ this.opts = opts;
170
+ }
171
+ extraHeaders() {
172
+ const h = {};
173
+ if (this.opts.client) h["X-VibeView-Client"] = this.opts.client;
174
+ if (process.env.CI) h["X-VibeView-CI"] = "1";
175
+ return h;
169
176
  }
170
177
  /**
171
178
  * fetch() rejects with undici's bare "TypeError: fetch failed" on any
@@ -189,6 +196,7 @@ var ApiClient = class {
189
196
  const url = `${this.baseUrl}/api/v1/ci${path}`;
190
197
  const headers = {
191
198
  Authorization: `Bearer ${this.token}`,
199
+ ...this.extraHeaders(),
192
200
  "Content-Type": "application/json"
193
201
  };
194
202
  const resp = await this.fetchOrExplain(url, {
@@ -231,7 +239,8 @@ var ApiClient = class {
231
239
  build_id: options?.build,
232
240
  metadata: options?.metadata,
233
241
  platform: options?.platform,
234
- app_id: options?.app
242
+ app_id: options?.app,
243
+ retry_failed: options?.retryFailed
235
244
  });
236
245
  }
237
246
  async runTest(testId, options) {
@@ -246,7 +255,8 @@ var ApiClient = class {
246
255
  build_id: options?.build,
247
256
  metadata: options?.metadata,
248
257
  platform: options?.platform,
249
- app_id: options?.app
258
+ app_id: options?.app,
259
+ retry_failed: options?.retryFailed
250
260
  });
251
261
  }
252
262
  /** Cancel a still-queued CI run by its queue entry id. Idempotent best-effort. */
@@ -271,6 +281,7 @@ var ApiClient = class {
271
281
  const url = `${this.baseUrl}/api/v1${path}`;
272
282
  const headers = {
273
283
  Authorization: `Bearer ${this.token}`,
284
+ ...this.extraHeaders(),
274
285
  "Content-Type": "application/json"
275
286
  };
276
287
  const resp = await this.fetchOrExplain(url, {
@@ -295,7 +306,8 @@ var ApiClient = class {
295
306
  async requestFormData(method, path, formData) {
296
307
  const url = `${this.baseUrl}/api/v1${path}`;
297
308
  const headers = {
298
- Authorization: `Bearer ${this.token}`
309
+ Authorization: `Bearer ${this.token}`,
310
+ ...this.extraHeaders()
299
311
  // Do NOT set Content-Type — let fetch set multipart boundary automatically
300
312
  };
301
313
  const resp = await this.fetchOrExplain(url, {
@@ -351,12 +363,35 @@ var ApiClient = class {
351
363
  }
352
364
  return this.requestFormData("POST", `/apps/${appPublicId}/builds`, formData);
353
365
  }
366
+ /** POST /api/v1/tests/import/maestro (multipart): preview (`dryRun`) or
367
+ * import Maestro flows as tests of one app. `group` is a group id or name. */
368
+ async importMaestro(upload, opts) {
369
+ const formData = new FormData();
370
+ formData.append("file", new Blob([Buffer.from(upload.bytes)]), upload.name);
371
+ formData.append("app_id", opts.appId);
372
+ if (opts.group) formData.append("group", opts.group);
373
+ formData.append("dry_run", opts.dryRun ? "true" : "false");
374
+ if (opts.skip && opts.skip.length > 0) formData.append("skip", JSON.stringify(opts.skip));
375
+ return this.requestFormData("POST", "/tests/import/maestro", formData);
376
+ }
354
377
  async createTest(body) {
355
378
  return this.requestRaw("POST", "/tests/cases/", body);
356
379
  }
357
380
  async runSuites(body) {
358
381
  return this.request("POST", "/run-suites", body);
359
382
  }
383
+ async startRun(body) {
384
+ return this.requestRaw("POST", "/tests/runs", body);
385
+ }
386
+ async startRunOnSession(sessionId, body) {
387
+ return this.requestRaw("POST", `/sessions/${encodeURIComponent(sessionId)}/runs`, body);
388
+ }
389
+ async getRunBatch(batchId) {
390
+ return this.requestRaw("GET", `/tests/batches/${encodeURIComponent(batchId)}`);
391
+ }
392
+ async resolveRun(runId) {
393
+ return this.requestRaw("GET", `/tests/runs/${encodeURIComponent(runId)}/resolve`);
394
+ }
360
395
  async getMultiSuiteRun(aggregateId) {
361
396
  return this.request("GET", `/multi-suite-runs/${aggregateId}`);
362
397
  }
@@ -474,7 +509,8 @@ var ApiClient = class {
474
509
  async downloadCloudBuildArtifact(id, destDir) {
475
510
  const url = `${this.baseUrl}/api/v1/builds/${id}/artifact`;
476
511
  const headers = {
477
- Authorization: `Bearer ${this.token}`
512
+ Authorization: `Bearer ${this.token}`,
513
+ ...this.extraHeaders()
478
514
  };
479
515
  const resp = await this.fetchOrExplain(url, { method: "GET", headers });
480
516
  if (!resp.ok) {