vibeview 0.9.0 → 0.10.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
|
@@ -95,6 +95,7 @@ Output formats: `human`, `json`, and `junit` for any CI system. The exit code re
|
|
|
95
95
|
| `vibeview agent-setup` | Make VibeView discoverable to your coding agent (skill + MCP) |
|
|
96
96
|
| `vibeview mcp` | Run the MCP server so an agent can drive the device |
|
|
97
97
|
| `vibeview ui-tree` | The current screen's elements, each with a ref to act on |
|
|
98
|
+
| `vibeview logs` | Recent app logs from the device (console output, errors, crashes) |
|
|
98
99
|
| `vibeview screenshot` | Capture the screen to a PNG |
|
|
99
100
|
| `vibeview tap` / `type` / `scroll` / `press` | Drive the device |
|
|
100
101
|
| `vibeview scroll-to` / `drag` / `alert` / `find` | Search, precise gestures, iOS system alerts |
|
|
@@ -495,6 +495,8 @@ function fail(opts, message, code) {
|
|
|
495
495
|
|
|
496
496
|
// src/commands/dev.ts
|
|
497
497
|
import { spawn as spawn3 } from "child_process";
|
|
498
|
+
import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
|
|
499
|
+
import { dirname as dirname4 } from "path";
|
|
498
500
|
import { createInterface as createInterface3 } from "readline";
|
|
499
501
|
import { select as select3 } from "@inquirer/prompts";
|
|
500
502
|
import chalk2 from "chalk";
|
|
@@ -676,6 +678,9 @@ var STATE_FILE2 = "metro-state.json";
|
|
|
676
678
|
function statePath2(cwd) {
|
|
677
679
|
return join5(cwd, STATE_DIR2, STATE_FILE2);
|
|
678
680
|
}
|
|
681
|
+
function metroLogPath(cwd) {
|
|
682
|
+
return join5(cwd, STATE_DIR2, "metro.log");
|
|
683
|
+
}
|
|
679
684
|
function isPidAlive2(pid) {
|
|
680
685
|
try {
|
|
681
686
|
process.kill(pid, 0);
|
|
@@ -720,6 +725,11 @@ function killOwnedMetro(cwd) {
|
|
|
720
725
|
|
|
721
726
|
// src/lib/metro-manager.ts
|
|
722
727
|
var TAIL_LINES2 = 20;
|
|
728
|
+
var METRO_ECHO_RE = /error|failed|unable to|exception|bundled |bundling failed/i;
|
|
729
|
+
function shouldEchoMetroLine(line, debug) {
|
|
730
|
+
if (debug) return true;
|
|
731
|
+
return METRO_ECHO_RE.test(line);
|
|
732
|
+
}
|
|
723
733
|
async function ensureMetro(cwd, metro, log, timeoutMs = 9e4) {
|
|
724
734
|
const warnings = [];
|
|
725
735
|
const status = await checkMetro(metro.port);
|
|
@@ -1151,6 +1161,18 @@ async function runDevInProcess(opts) {
|
|
|
1151
1161
|
const metroPort = cfg.metroPort ?? 8081;
|
|
1152
1162
|
let metroResult;
|
|
1153
1163
|
try {
|
|
1164
|
+
let metroLogStream = null;
|
|
1165
|
+
const captureMetroLine = (line) => {
|
|
1166
|
+
try {
|
|
1167
|
+
if (!metroLogStream) {
|
|
1168
|
+
mkdirSync3(dirname4(metroLogPath(process.cwd())), { recursive: true });
|
|
1169
|
+
metroLogStream = createWriteStream(metroLogPath(process.cwd()), { flags: "w" });
|
|
1170
|
+
}
|
|
1171
|
+
metroLogStream.write(line.replace(/^\[metro\] /, "") + "\n");
|
|
1172
|
+
} catch {
|
|
1173
|
+
}
|
|
1174
|
+
if (shouldEchoMetroLine(line, opts.debug === true)) console.error(chalk2.dim(line));
|
|
1175
|
+
};
|
|
1154
1176
|
metroResult = await ensureMetro(
|
|
1155
1177
|
process.cwd(),
|
|
1156
1178
|
{
|
|
@@ -1158,7 +1180,7 @@ async function runDevInProcess(opts) {
|
|
|
1158
1180
|
port: metroPort,
|
|
1159
1181
|
platformHasOverride: cfg.platformHasMetroOverride
|
|
1160
1182
|
},
|
|
1161
|
-
|
|
1183
|
+
captureMetroLine
|
|
1162
1184
|
);
|
|
1163
1185
|
} catch (err) {
|
|
1164
1186
|
const message = err.message;
|
|
@@ -1169,6 +1191,9 @@ async function runDevInProcess(opts) {
|
|
|
1169
1191
|
for (const w of metroResult.warnings) console.error(chalk2.yellow(`\u26A0 ${w}`));
|
|
1170
1192
|
if (metroResult.status === "spawned" || metroResult.status === "respawned") {
|
|
1171
1193
|
console.error(chalk2.green(`\u2713 started Metro (${cfg.metroCommand})`));
|
|
1194
|
+
console.error(
|
|
1195
|
+
chalk2.dim(` full Metro output: ${metroLogPath(process.cwd())} (--debug echoes it here)`)
|
|
1196
|
+
);
|
|
1172
1197
|
}
|
|
1173
1198
|
const metroOwnedThisRun = metroResult.status === "spawned" || metroResult.status === "respawned";
|
|
1174
1199
|
console.error(chalk2.dim(`Starting a ${cfg.platform} dev session for ${cfg.appId}...`));
|
|
@@ -1352,6 +1377,28 @@ var AGENT_COMMANDS = [
|
|
|
1352
1377
|
mutating: false,
|
|
1353
1378
|
params: []
|
|
1354
1379
|
},
|
|
1380
|
+
{
|
|
1381
|
+
verb: "logs",
|
|
1382
|
+
tool: "logs",
|
|
1383
|
+
summary: "Read recent app logs from the device \u2014 JS console output, native errors, and crash messages from the app under test (not full system logs). Check after a crash, a frozen screen, or an unexpected blank tree, and to confirm an expected log line fired. Pass the previous response's cursor as --since to get only lines that arrived after it.",
|
|
1384
|
+
mutating: false,
|
|
1385
|
+
params: [
|
|
1386
|
+
{
|
|
1387
|
+
name: "tail",
|
|
1388
|
+
cli: "--tail",
|
|
1389
|
+
type: "number",
|
|
1390
|
+
required: false,
|
|
1391
|
+
description: "Maximum lines to return, newest kept (default 100, max 500)."
|
|
1392
|
+
},
|
|
1393
|
+
{
|
|
1394
|
+
name: "since",
|
|
1395
|
+
cli: "--since",
|
|
1396
|
+
type: "number",
|
|
1397
|
+
required: false,
|
|
1398
|
+
description: "Only lines newer than this cursor (use the previous response's `cursor` value)."
|
|
1399
|
+
}
|
|
1400
|
+
]
|
|
1401
|
+
},
|
|
1355
1402
|
{
|
|
1356
1403
|
verb: "screenshot",
|
|
1357
1404
|
tool: "screenshot",
|
|
@@ -1749,6 +1796,20 @@ function resultLines(result) {
|
|
|
1749
1796
|
if (!result || typeof result !== "object") return [];
|
|
1750
1797
|
const r = result;
|
|
1751
1798
|
const lines = [];
|
|
1799
|
+
if (Array.isArray(r.entries) && typeof r.cursor === "number") {
|
|
1800
|
+
if (r.entries.length === 0) {
|
|
1801
|
+
lines.push(chalk3.dim("(no log lines)"));
|
|
1802
|
+
}
|
|
1803
|
+
for (const e of r.entries) {
|
|
1804
|
+
if (!e || typeof e !== "object") continue;
|
|
1805
|
+
const entry = e;
|
|
1806
|
+
const ts = typeof entry.timestamp === "number" ? new Date(entry.timestamp).toISOString().slice(11, 23) : "??:??:??.???";
|
|
1807
|
+
const level = typeof entry.level === "string" ? entry.level : "info";
|
|
1808
|
+
const levelTag = level === "error" ? chalk3.red(level) : level === "warn" ? chalk3.yellow(level) : chalk3.dim(level);
|
|
1809
|
+
lines.push(`${chalk3.dim(ts)} ${levelTag} ${String(entry.message ?? "")}`);
|
|
1810
|
+
}
|
|
1811
|
+
lines.push(chalk3.dim(`cursor: ${r.cursor}`));
|
|
1812
|
+
}
|
|
1752
1813
|
if (r.found === true && typeof r.ref === "string" && r.ref) {
|
|
1753
1814
|
let found = `found: @${r.ref}`;
|
|
1754
1815
|
if (typeof r.text === "string" && r.text) found += ` ${JSON.stringify(r.text)}`;
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
readDevState,
|
|
9
9
|
readMetroState,
|
|
10
10
|
runAgentVerb
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NA3JOV5Z.js";
|
|
12
12
|
import {
|
|
13
13
|
ApiClient,
|
|
14
14
|
EXIT_INFRA_ERROR,
|
|
@@ -1448,7 +1448,7 @@ program.command("dev-stop").description("Stop a `vibeview dev --detach` session
|
|
|
1448
1448
|
program.command("mcp").description(
|
|
1449
1449
|
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop as tools"
|
|
1450
1450
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
1451
|
-
const { runMcpServer } = await import("./server-
|
|
1451
|
+
const { runMcpServer } = await import("./server-B2PDQJLR.js");
|
|
1452
1452
|
await runMcpServer(opts);
|
|
1453
1453
|
});
|
|
1454
1454
|
program.command("agent-setup").description(
|
package/package.json
CHANGED
|
@@ -103,6 +103,17 @@ it rather than letting it appear unexplained in their `git status`.
|
|
|
103
103
|
way you will ever see the screen. It costs a lot of context, so use it
|
|
104
104
|
when appearance matters — not to confirm a tap landed, which the
|
|
105
105
|
response's observation already tells you.
|
|
106
|
+
- **When something breaks — a crash, a frozen screen, an unexpectedly empty
|
|
107
|
+
tree — read the app logs before guessing.** `logs` returns the app's own
|
|
108
|
+
output from the device: JS console lines, native errors, and crash
|
|
109
|
+
messages (not full system logs, and not Metro's terminal — native crashes
|
|
110
|
+
never appear there). The response ends with a `cursor`; pass it back as
|
|
111
|
+
`--since` to see only lines that arrived after your last read, which is
|
|
112
|
+
the natural loop for "did my action log what I expected".
|
|
113
|
+
```bash
|
|
114
|
+
vibeview logs --session <id> # recent lines (default 100)
|
|
115
|
+
vibeview logs --since 42 --session <id> # only lines after cursor 42
|
|
116
|
+
```
|
|
106
117
|
- **Off-screen elements**: use `scroll-to <text>` rather than repeating
|
|
107
118
|
`scroll down`. It scrolls until an element whose label, value, or
|
|
108
119
|
accessibility id contains that text is visible, and stops early when the
|
|
@@ -157,6 +168,7 @@ human-readable text.
|
|
|
157
168
|
| Verb | Args | Purpose |
|
|
158
169
|
|------|------|---------|
|
|
159
170
|
| `ui-tree` | — | Fetch the ref-annotated UI tree for the current screen. |
|
|
171
|
+
| `logs` | `[--tail <n>] [--since <cursor>]` | Read recent app logs from the device (JS console, native errors, crash messages). The response's `cursor` feeds the next call's `--since`. |
|
|
160
172
|
| `screenshot` | `[--out <path>]` | Capture a screenshot of the current screen and save it to disk. |
|
|
161
173
|
| `tap` | `<target>` | Tap an element by ref (e.g. `@e5`), or raw coordinates (e.g. `100,200`). |
|
|
162
174
|
| `long-press` | `<ref>` `[--ms <n>]` | Long-press (touch and hold) an element by its ref. |
|