vibeview 0.9.0 → 0.10.1
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
|
@@ -50,6 +50,11 @@ https://vibeview.io/docs/preparing-your-build.
|
|
|
50
50
|
fires once the session accepts commands (device up, app installed) —
|
|
51
51
|
no need to poll `dev-status` first. If the app ships no native changes
|
|
52
52
|
since the last run, skip step 1 and just do steps 2–4.
|
|
53
|
+
5. **Immediately tell the developer the session `url`** — "watch or take
|
|
54
|
+
over at any time: `<url>`". The page is a live, fully interactive view
|
|
55
|
+
of the device; the developer watching along is a feature, not a risk.
|
|
56
|
+
(For a session you attached to with `--session` instead of starting,
|
|
57
|
+
the page is `https://vibeview.io/sandbox/<session_id>`.)
|
|
53
58
|
|
|
54
59
|
Running `dev` writes two things into the project root: a `.vibeview/`
|
|
55
60
|
directory (live session state — local machine state, never commit) and a
|
|
@@ -103,6 +108,17 @@ it rather than letting it appear unexplained in their `git status`.
|
|
|
103
108
|
way you will ever see the screen. It costs a lot of context, so use it
|
|
104
109
|
when appearance matters — not to confirm a tap landed, which the
|
|
105
110
|
response's observation already tells you.
|
|
111
|
+
- **When something breaks — a crash, a frozen screen, an unexpectedly empty
|
|
112
|
+
tree — read the app logs before guessing.** `logs` returns the app's own
|
|
113
|
+
output from the device: JS console lines, native errors, and crash
|
|
114
|
+
messages (not full system logs, and not Metro's terminal — native crashes
|
|
115
|
+
never appear there). The response ends with a `cursor`; pass it back as
|
|
116
|
+
`--since` to see only lines that arrived after your last read, which is
|
|
117
|
+
the natural loop for "did my action log what I expected".
|
|
118
|
+
```bash
|
|
119
|
+
vibeview logs --session <id> # recent lines (default 100)
|
|
120
|
+
vibeview logs --since 42 --session <id> # only lines after cursor 42
|
|
121
|
+
```
|
|
106
122
|
- **Off-screen elements**: use `scroll-to <text>` rather than repeating
|
|
107
123
|
`scroll down`. It scrolls until an element whose label, value, or
|
|
108
124
|
accessibility id contains that text is visible, and stops early when the
|
|
@@ -139,7 +155,26 @@ it rather than letting it appear unexplained in their `git status`.
|
|
|
139
155
|
focus directly with `tap-focused` (moves focus AND activates) / `focus`
|
|
140
156
|
(moves focus only, no activation).
|
|
141
157
|
|
|
142
|
-
## 4.
|
|
158
|
+
## 4. When only a human can act — stop and hand off
|
|
159
|
+
|
|
160
|
+
Some screens need things you must never guess or invent: **login
|
|
161
|
+
credentials, 2FA/OTP codes, CAPTCHAs, destructive confirmations
|
|
162
|
+
("Delete account?"), anything payment- or real-account-shaped.** When you
|
|
163
|
+
hit one:
|
|
164
|
+
|
|
165
|
+
1. **Stop driving.** Do not type guessed credentials, do not tap through a
|
|
166
|
+
destructive confirmation to "see what happens".
|
|
167
|
+
2. **Ask the developer, naming exactly what you need and where**: "I'm at
|
|
168
|
+
the login screen — please sign in at `<session url>` and tell me when
|
|
169
|
+
you're done." The session page is fully interactive; the developer can
|
|
170
|
+
drive the device directly while you wait.
|
|
171
|
+
3. **On resume, run `ui-tree` FIRST** — the human just changed the screen,
|
|
172
|
+
so every ref you held is stale — and **verify the blocker is actually
|
|
173
|
+
gone from the tree** (e.g. the login form is absent) instead of trusting
|
|
174
|
+
the confirmation alone. If it's still there, say what you still see and
|
|
175
|
+
ask again.
|
|
176
|
+
|
|
177
|
+
## 5. Cleanup
|
|
143
178
|
|
|
144
179
|
**Always** stop the session when done verifying — a running session bills
|
|
145
180
|
streaming minutes even when idle:
|
|
@@ -147,7 +182,7 @@ streaming minutes even when idle:
|
|
|
147
182
|
vibeview dev-stop
|
|
148
183
|
```
|
|
149
184
|
|
|
150
|
-
##
|
|
185
|
+
## 6. Command reference
|
|
151
186
|
|
|
152
187
|
Every verb accepts `--session <id>` to target a specific session (falls
|
|
153
188
|
back to the current project's `vibeview dev --detach` session if omitted)
|
|
@@ -157,6 +192,7 @@ human-readable text.
|
|
|
157
192
|
| Verb | Args | Purpose |
|
|
158
193
|
|------|------|---------|
|
|
159
194
|
| `ui-tree` | — | Fetch the ref-annotated UI tree for the current screen. |
|
|
195
|
+
| `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
196
|
| `screenshot` | `[--out <path>]` | Capture a screenshot of the current screen and save it to disk. |
|
|
161
197
|
| `tap` | `<target>` | Tap an element by ref (e.g. `@e5`), or raw coordinates (e.g. `100,200`). |
|
|
162
198
|
| `long-press` | `<ref>` `[--ms <n>]` | Long-press (touch and hold) an element by its ref. |
|
|
@@ -182,7 +218,7 @@ Dev-loop lifecycle commands (not registry verbs, but needed for every run):
|
|
|
182
218
|
| `dev-status` | Check on the current project's detached dev session. |
|
|
183
219
|
| `dev-stop` | End the current project's detached dev session. |
|
|
184
220
|
|
|
185
|
-
##
|
|
221
|
+
## 7. If you're calling VibeView over MCP
|
|
186
222
|
|
|
187
223
|
Everything above is also exposed as MCP tools by `vibeview mcp`, so an agent
|
|
188
224
|
with no shell can run the whole loop. Tool names are the CLI verbs with
|
|
@@ -198,7 +234,7 @@ steps the CLI does with plain shell commands:
|
|
|
198
234
|
| MCP tool | Args | Purpose |
|
|
199
235
|
|----------|------|---------|
|
|
200
236
|
| `upload_app` | `file` (required path), `name` | Upload a debug build. The MCP equivalent of `vibeview upload-app`, so step 1 of the loop needs no shell. Returns the `app_id` to pass to `dev_start`. |
|
|
201
|
-
| `dev_start` | `platform` (required: `ios`/`android`/`tvos`/`androidtv`), `app`, `metro_port` | Start a dev-loop session held open by the MCP server. The MCP equivalent of `vibeview dev --detach`. |
|
|
237
|
+
| `dev_start` | `platform` (required: `ios`/`android`/`tvos`/`androidtv`), `app`, `metro_port` | Start a dev-loop session held open by the MCP server. The MCP equivalent of `vibeview dev --detach`. Returns `page_url` — relay it to the developer immediately, same as step 5 of the loop. |
|
|
202
238
|
| `dev_stop` | — | Stop the session `dev_start` started. The MCP equivalent of `vibeview dev-stop`. |
|
|
203
239
|
|
|
204
240
|
**`dev_start` is stateful — it changes the default session for every later
|