vibeview 0.31.0 → 0.32.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.
@@ -461,6 +461,10 @@ function writeDevState(cwd, state) {
461
461
  ensureGitignored(cwd);
462
462
  }
463
463
  function readDevState(cwd) {
464
+ const found = readDevStateFile(cwd);
465
+ return found && found.alive ? found.state : null;
466
+ }
467
+ function readDevStateFile(cwd) {
464
468
  let raw;
465
469
  try {
466
470
  raw = readFileSync2(statePath(cwd), "utf-8");
@@ -473,10 +477,10 @@ function readDevState(cwd) {
473
477
  } catch {
474
478
  return null;
475
479
  }
476
- if (typeof parsed.pid !== "number" || !isPidAlive(parsed.pid)) {
480
+ if (!parsed || typeof parsed.pid !== "number" || typeof parsed.session_id !== "string") {
477
481
  return null;
478
482
  }
479
- return parsed;
483
+ return { state: parsed, alive: isPidAlive(parsed.pid) };
480
484
  }
481
485
  function clearDevState(cwd) {
482
486
  const path = statePath(cwd);
@@ -2575,7 +2579,7 @@ async function pollUntilActive(api, sessionId, timeoutMs, intervalMs = 2e3, onIn
2575
2579
  }
2576
2580
  function sessionLivenessVerdict(info) {
2577
2581
  if (info && (info.status === "stopped" || info.status === "failed")) {
2578
- return { end: true, message: `session ${info.status} \u2014 ending the reload loop` };
2582
+ return { end: true, message: `session ${info.status} \u2014 ending the dev session` };
2579
2583
  }
2580
2584
  return { end: false };
2581
2585
  }
@@ -3197,6 +3201,8 @@ async function runDevInProcess(opts) {
3197
3201
  process.stdin.pause();
3198
3202
  });
3199
3203
  }
3204
+ }
3205
+ if (rokuBuild || standalone) {
3200
3206
  const sessionId = handle.sessionId;
3201
3207
  const reportedWarnings = handle.reportedWarnings;
3202
3208
  let keepAlive;
@@ -4221,6 +4227,7 @@ export {
4221
4227
  formatBuildRowsHuman,
4222
4228
  listBuilds,
4223
4229
  readDevState,
4230
+ readDevStateFile,
4224
4231
  clearDevState,
4225
4232
  readMetroState,
4226
4233
  killOwnedMetro,
package/dist/cli.js CHANGED
@@ -21,9 +21,10 @@ import {
21
21
  printOutcomeLine,
22
22
  printTransition,
23
23
  readDevState,
24
+ readDevStateFile,
24
25
  readMetroState,
25
26
  runAgentVerb
26
- } from "./chunk-P6UIJALK.js";
27
+ } from "./chunk-T6H5XTBX.js";
27
28
  import {
28
29
  ApiClient,
29
30
  ApiStatusError,
@@ -543,15 +544,20 @@ function formatAction(action) {
543
544
  function formatToolCall(tc) {
544
545
  return formatAction({ tool: tc.name, args: tc.args });
545
546
  }
547
+ function runStartedLine(evt) {
548
+ const steps = `${evt.total_steps} step(s)`;
549
+ const model = chalk2.bold(evt.model || "default");
550
+ if (evt.mode === "replay") return `${chalk2.cyan("\u25B6")} Replay started \u2014 ${steps} (no AI)`;
551
+ if (evt.mode === "hybrid") {
552
+ return `${chalk2.cyan("\u25B6")} Hybrid test started \u2014 ${steps}, replaying the recording, AI fallback model: ${model}`;
553
+ }
554
+ return `${chalk2.cyan("\u25B6")} AI test started \u2014 ${steps}, model: ${model}`;
555
+ }
546
556
  function displayProgressEvent(evt, ctx) {
547
557
  const { spinner } = ctx;
548
558
  switch (evt.event) {
549
559
  case "run_started":
550
- printBelowSpinner(
551
- spinner,
552
- `${chalk2.cyan("\u25B6")} AI test started \u2014 ${evt.total_steps} step(s), model: ${chalk2.bold(evt.model || "default")}`,
553
- " Starting test..."
554
- );
560
+ printBelowSpinner(spinner, runStartedLine(evt), " Starting test...");
555
561
  ctx.totalSteps = evt.total_steps ?? ctx.totalSteps;
556
562
  break;
557
563
  case "step_started": {
@@ -1348,6 +1354,9 @@ async function resolveSteps(options) {
1348
1354
  }
1349
1355
  throw new Error("No steps provided. Use --steps, --steps-file, or pipe JSON to stdin.");
1350
1356
  }
1357
+ function testCaseUrl(baseUrl, publicId) {
1358
+ return `${baseUrl}/tests/cases/${encodeURIComponent(publicId)}`;
1359
+ }
1351
1360
  function assertHasSteps(steps) {
1352
1361
  if (!Array.isArray(steps)) {
1353
1362
  throw new Error(
@@ -1407,7 +1416,7 @@ async function createTest(options) {
1407
1416
  name: result.name,
1408
1417
  app_id: appPublicId || null,
1409
1418
  outcome: result.outcome ?? null,
1410
- url: `${baseUrl}/tests?tab=standalone`
1419
+ url: testCaseUrl(baseUrl, result.public_id)
1411
1420
  },
1412
1421
  null,
1413
1422
  2
@@ -1423,7 +1432,7 @@ async function createTest(options) {
1423
1432
  console.log(` ${chalk10.dim(`Platform: ${result.platform}`)}`);
1424
1433
  }
1425
1434
  console.log(` ${chalk10.dim(`Steps: ${steps.length}`)}`);
1426
- console.log(chalk10.dim(` ${baseUrl}/tests?tab=standalone
1435
+ console.log(chalk10.dim(` ${testCaseUrl(baseUrl, result.public_id)}
1427
1436
  `));
1428
1437
  }
1429
1438
  } catch (err) {
@@ -1519,8 +1528,8 @@ function buildLine(info) {
1519
1528
  return `${info.app_build_id}${version}`;
1520
1529
  }
1521
1530
  async function devStatus(opts) {
1522
- const state = readDevState(process.cwd());
1523
- if (!state) {
1531
+ const found = readDevStateFile(process.cwd());
1532
+ if (!found) {
1524
1533
  if (opts.json) {
1525
1534
  console.log(JSON.stringify({ running: false }));
1526
1535
  } else {
@@ -1531,6 +1540,11 @@ async function devStatus(opts) {
1531
1540
  }
1532
1541
  const { token, baseUrl } = resolveAuth(opts);
1533
1542
  const api = new ApiClient(baseUrl, token);
1543
+ if (!found.alive) {
1544
+ await reportOrphan(api, found.state, opts);
1545
+ return;
1546
+ }
1547
+ const state = found.state;
1534
1548
  let sessionStatus;
1535
1549
  let info;
1536
1550
  try {
@@ -1571,11 +1585,46 @@ async function devStatus(opts) {
1571
1585
  if (metro) console.error(chalk11.dim(`Metro: pid ${metro.pid} on :${metro.port} (CLI-owned)`));
1572
1586
  process.exit(0);
1573
1587
  }
1588
+ var LIVE_STATUSES = /* @__PURE__ */ new Set(["pending", "queued", "starting", "active"]);
1589
+ async function reportOrphan(api, state, opts) {
1590
+ let status;
1591
+ try {
1592
+ status = (await api.getSession(state.session_id)).status;
1593
+ } catch {
1594
+ status = void 0;
1595
+ }
1596
+ const live = status !== void 0 && LIVE_STATUSES.has(status);
1597
+ if (!live && status !== void 0) clearDevState(process.cwd());
1598
+ if (opts.json) {
1599
+ console.log(
1600
+ JSON.stringify({
1601
+ running: false,
1602
+ pid: state.pid,
1603
+ session_id: state.session_id,
1604
+ session_status: status ?? null,
1605
+ orphaned: live
1606
+ })
1607
+ );
1608
+ } else if (live) {
1609
+ console.error(
1610
+ chalk11.yellow(
1611
+ `\u26A0 The dev process (pid ${state.pid}) has exited, but session ${state.session_id} is still ${status}.`
1612
+ )
1613
+ );
1614
+ console.error(` End it: vibeview stop ${state.session_id}`);
1615
+ } else {
1616
+ console.error(
1617
+ `No dev session running \u2014 the dev process (pid ${state.pid}) has exited` + (status ? ` and session ${state.session_id} is ${status}.` : ".")
1618
+ );
1619
+ }
1620
+ process.exit(1);
1621
+ }
1574
1622
 
1575
1623
  // src/commands/dev-stop.ts
1576
1624
  async function devStop(opts) {
1577
- const state = readDevState(process.cwd());
1578
- if (!state) {
1625
+ const found = readDevStateFile(process.cwd());
1626
+ const state = found?.state;
1627
+ if (!found || !state) {
1579
1628
  if (opts.json) {
1580
1629
  console.log(JSON.stringify({ stopped: false, reason: "no_dev_session" }));
1581
1630
  } else {
@@ -1588,10 +1637,12 @@ async function devStop(opts) {
1588
1637
  const api = new ApiClient(baseUrl, token);
1589
1638
  await api.stopSession(state.session_id).catch(() => {
1590
1639
  });
1591
- try {
1592
- process.kill(state.pid, "SIGTERM");
1593
- } catch (err) {
1594
- if (err.code !== "ESRCH") throw err;
1640
+ if (found.alive) {
1641
+ try {
1642
+ process.kill(state.pid, "SIGTERM");
1643
+ } catch (err) {
1644
+ if (err.code !== "ESRCH") throw err;
1645
+ }
1595
1646
  }
1596
1647
  clearDevState(process.cwd());
1597
1648
  killOwnedMetro(process.cwd());
@@ -4028,7 +4079,7 @@ program.command("dev-reload").description(
4028
4079
  program.command("mcp").description(
4029
4080
  "Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
4030
4081
  ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
4031
- const { runMcpServer } = await import("./server-GPFOZOFL.js");
4082
+ const { runMcpServer } = await import("./server-FJSBWEHZ.js");
4032
4083
  await runMcpServer({ ...opts, version: pkg.version });
4033
4084
  });
4034
4085
  program.command("agent-setup").description(
@@ -24,7 +24,7 @@ import {
24
24
  renderEnvelope,
25
25
  resolveMetro,
26
26
  startDevSession
27
- } from "./chunk-P6UIJALK.js";
27
+ } from "./chunk-T6H5XTBX.js";
28
28
  import {
29
29
  ApiClient,
30
30
  NotFoundError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeview",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "VibeView CLI -- run your app on cloud iOS simulators, Android emulators, Apple TV, Android TV and Roku (beta) devices: a React Native dev loop, device control for you or your coding agent (CLI or MCP), and tests from CI",
5
5
  "mcpName": "io.github.vibeview/vibeview",
6
6
  "type": "module",