vibeview 0.36.0 → 0.37.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
|
@@ -838,10 +838,24 @@ function iosWorkspaceName(cwd) {
|
|
|
838
838
|
var IOS_ARTIFACT = "ios/build/Build/Products/Debug-iphonesimulator/*.app";
|
|
839
839
|
var ANDROID_ARTIFACT = "android/app/build/outputs/apk/debug/*.apk";
|
|
840
840
|
function installCommand(cwd) {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
841
|
+
const hit = JS_LOCKFILE_INSTALLS.find(([lockfile]) => existsSync3(join5(cwd, lockfile)));
|
|
842
|
+
return hit ? hit[1] : "npm install";
|
|
843
|
+
}
|
|
844
|
+
var JS_LOCKFILE_INSTALLS = [
|
|
845
|
+
["yarn.lock", "yarn install --immutable"],
|
|
846
|
+
["pnpm-lock.yaml", "pnpm install --frozen-lockfile"],
|
|
847
|
+
["package-lock.json", "npm ci"],
|
|
848
|
+
["npm-shrinkwrap.json", "npm ci"],
|
|
849
|
+
["bun.lock", "npm install"],
|
|
850
|
+
["bun.lockb", "npm install"]
|
|
851
|
+
];
|
|
852
|
+
function bareInstallPrefix(cwd, platform) {
|
|
853
|
+
const legs = [];
|
|
854
|
+
if (isFile(join5(cwd, "package.json"))) legs.push(installCommand(cwd));
|
|
855
|
+
if (platform === "ios" && isFile(join5(cwd, "ios", "Podfile"))) {
|
|
856
|
+
legs.push("cd ios && pod install && cd ..");
|
|
857
|
+
}
|
|
858
|
+
return legs.map((l) => `${l} && `).join("");
|
|
845
859
|
}
|
|
846
860
|
function managedExpoAndroidCommand(cwd) {
|
|
847
861
|
return `${installCommand(cwd)} && npx expo prebuild --platform android --no-install && cd android && ./gradlew assembleDebug`;
|
|
@@ -880,7 +894,7 @@ function defaultsFor(cwd, platform) {
|
|
|
880
894
|
if (platform === "android") {
|
|
881
895
|
const managed = isManagedExpo(cwd, "android");
|
|
882
896
|
return {
|
|
883
|
-
buildCommand: managed ? managedExpoAndroidCommand(cwd) : "cd android && ./gradlew assembleDebug
|
|
897
|
+
buildCommand: managed ? managedExpoAndroidCommand(cwd) : `${bareInstallPrefix(cwd, "android")}cd android && ./gradlew assembleDebug`,
|
|
884
898
|
artifact: ANDROID_ARTIFACT,
|
|
885
899
|
metroCommand
|
|
886
900
|
};
|
|
@@ -895,14 +909,14 @@ function defaultsFor(cwd, platform) {
|
|
|
895
909
|
}
|
|
896
910
|
const ws = iosWorkspaceName(cwd);
|
|
897
911
|
return {
|
|
898
|
-
buildCommand: ws ?
|
|
912
|
+
buildCommand: ws ? `${bareInstallPrefix(cwd, "ios")}xcodebuild -workspace ios/${ws}.xcworkspace -scheme ${ws} -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build CODE_SIGNING_ALLOWED=NO` : void 0,
|
|
899
913
|
artifact: IOS_ARTIFACT,
|
|
900
914
|
metroCommand
|
|
901
915
|
};
|
|
902
916
|
}
|
|
903
917
|
var ANDROID_PRODUCTION_ARTIFACT = "android/app/build/outputs/bundle/release/*.aab";
|
|
904
918
|
function androidProductionCommand(cwd) {
|
|
905
|
-
return `${
|
|
919
|
+
return `${bareInstallPrefix(cwd, "android")}cd android && ./gradlew bundleRelease`;
|
|
906
920
|
}
|
|
907
921
|
var IOS_PRODUCTION_ARTIFACT = "ios/build/*.ipa";
|
|
908
922
|
function managedExpoAndroidProductionCommand(cwd) {
|
|
@@ -951,7 +965,7 @@ function productionDefaultsFor(cwd, platform) {
|
|
|
951
965
|
const ws = iosWorkspaceName(cwd);
|
|
952
966
|
if (!ws) return { artifact: IOS_PRODUCTION_ARTIFACT };
|
|
953
967
|
return {
|
|
954
|
-
buildCommand: `${
|
|
968
|
+
buildCommand: `${bareInstallPrefix(cwd, "ios")}${iosArchiveExportCommand(ws)}`,
|
|
955
969
|
artifact: IOS_PRODUCTION_ARTIFACT
|
|
956
970
|
};
|
|
957
971
|
}
|
|
@@ -1329,6 +1343,203 @@ function runTar(args) {
|
|
|
1329
1343
|
});
|
|
1330
1344
|
}
|
|
1331
1345
|
|
|
1346
|
+
// src/lib/build-preflight.ts
|
|
1347
|
+
import { execFile as execFile3 } from "child_process";
|
|
1348
|
+
import { realpathSync, statSync as statSync4 } from "fs";
|
|
1349
|
+
import { dirname as dirname2, isAbsolute, join as join7, relative } from "path";
|
|
1350
|
+
import { parse } from "shell-quote";
|
|
1351
|
+
var DIRECTORY_CHANGERS = /* @__PURE__ */ new Set(["cd", "pushd"]);
|
|
1352
|
+
var DIRECTORY_FLAGS = /^(--cwd|--prefix|--dir|-C)(=|$)/;
|
|
1353
|
+
var VALUE_FLAGS = /^(--cwd|--prefix|--dir|-C|--loglevel|--registry|--workspace|--filter)$/;
|
|
1354
|
+
var GLOBAL_FLAGS = /* @__PURE__ */ new Set(["-g", "--global", "--location=global"]);
|
|
1355
|
+
var SEPARATORS = /* @__PURE__ */ new Set(["&&", "||", ";", ";;", "|", "|&", "&", "(", ")"]);
|
|
1356
|
+
var REDIRECTIONS = /* @__PURE__ */ new Set([">", ">>", "<", ">&", "<&", "&>", "&>>", ">|", "<>"]);
|
|
1357
|
+
var WRAPPERS = /* @__PURE__ */ new Set(["time", "env", "corepack", "command", "exec", "nohup"]);
|
|
1358
|
+
var JS_PACKAGE_MANAGERS = /* @__PURE__ */ new Set(["npm", "npx", "yarn", "pnpm", "pnpx", "bun", "bunx"]);
|
|
1359
|
+
var ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
1360
|
+
function simpleCommands(command) {
|
|
1361
|
+
const out = [];
|
|
1362
|
+
const flush = (words) => {
|
|
1363
|
+
while (words.length > 0 && ASSIGNMENT.test(words[0])) words.shift();
|
|
1364
|
+
while (words.length > 0 && WRAPPERS.has(words[0])) {
|
|
1365
|
+
words.shift();
|
|
1366
|
+
while (words.length > 0 && (words[0].startsWith("-") || ASSIGNMENT.test(words[0]))) {
|
|
1367
|
+
words.shift();
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
if (words.length === 0) return;
|
|
1371
|
+
const base = words[0].replace(/@[^/]*$/, "");
|
|
1372
|
+
const program = JS_PACKAGE_MANAGERS.has(base) ? base : words[0];
|
|
1373
|
+
out.push({ program, args: words.slice(1) });
|
|
1374
|
+
};
|
|
1375
|
+
for (const line of command.replace(/\\\r?\n/g, " ").split(/\r?\n/)) {
|
|
1376
|
+
let tokens;
|
|
1377
|
+
try {
|
|
1378
|
+
tokens = parse(line, (name) => `$${name}`);
|
|
1379
|
+
} catch {
|
|
1380
|
+
continue;
|
|
1381
|
+
}
|
|
1382
|
+
let words = [];
|
|
1383
|
+
let redirectTarget = false;
|
|
1384
|
+
for (const t of tokens) {
|
|
1385
|
+
if (typeof t === "string") {
|
|
1386
|
+
if (redirectTarget) {
|
|
1387
|
+
redirectTarget = false;
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
if (t === "{" || t === "}") flush(words.splice(0));
|
|
1391
|
+
else if (t.length > 0) words.push(t);
|
|
1392
|
+
} else if ("comment" in t) {
|
|
1393
|
+
break;
|
|
1394
|
+
} else if ("op" in t && t.op === "glob") {
|
|
1395
|
+
words.push(t.pattern);
|
|
1396
|
+
} else if ("op" in t && SEPARATORS.has(t.op)) {
|
|
1397
|
+
flush(words);
|
|
1398
|
+
words = [];
|
|
1399
|
+
} else if ("op" in t && REDIRECTIONS.has(t.op)) {
|
|
1400
|
+
if (words.length > 1 && /^\d+$/.test(words[words.length - 1])) words.pop();
|
|
1401
|
+
redirectTarget = true;
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
flush(words);
|
|
1405
|
+
}
|
|
1406
|
+
return out;
|
|
1407
|
+
}
|
|
1408
|
+
function positionals(args) {
|
|
1409
|
+
const out = [];
|
|
1410
|
+
for (let i = 0; i < args.length; i++) {
|
|
1411
|
+
const a = args[i];
|
|
1412
|
+
if (a.startsWith("-")) {
|
|
1413
|
+
if (VALUE_FLAGS.test(a)) i++;
|
|
1414
|
+
continue;
|
|
1415
|
+
}
|
|
1416
|
+
out.push(a);
|
|
1417
|
+
}
|
|
1418
|
+
return out;
|
|
1419
|
+
}
|
|
1420
|
+
var isGlobal = (args) => args.some((a) => GLOBAL_FLAGS.has(a));
|
|
1421
|
+
var VERSION_OR_HELP = /* @__PURE__ */ new Set(["-v", "--version", "-h", "--help"]);
|
|
1422
|
+
var NPM_INSTALL_VERBS = /* @__PURE__ */ new Set([
|
|
1423
|
+
"ci",
|
|
1424
|
+
"clean-install",
|
|
1425
|
+
"install-clean",
|
|
1426
|
+
"install",
|
|
1427
|
+
"i",
|
|
1428
|
+
"add",
|
|
1429
|
+
"install-test",
|
|
1430
|
+
"it"
|
|
1431
|
+
]);
|
|
1432
|
+
var NEEDS_MANIFEST_VERBS = /* @__PURE__ */ new Set([
|
|
1433
|
+
"ci",
|
|
1434
|
+
"clean-install",
|
|
1435
|
+
"install-clean",
|
|
1436
|
+
"install-test",
|
|
1437
|
+
"it",
|
|
1438
|
+
"run",
|
|
1439
|
+
"run-script"
|
|
1440
|
+
]);
|
|
1441
|
+
function installsInStep({ program, args }) {
|
|
1442
|
+
if (isGlobal(args)) return false;
|
|
1443
|
+
const [verb, ...rest] = positionals(args);
|
|
1444
|
+
if (program === "yarn") {
|
|
1445
|
+
if (verb === void 0) return !args.some((a) => VERSION_OR_HELP.has(a));
|
|
1446
|
+
if (verb === "install" || verb === "add") return true;
|
|
1447
|
+
return verb === "workspaces" && rest[0] === "focus";
|
|
1448
|
+
}
|
|
1449
|
+
if (program === "npm" || program === "pnpm" || program === "bun") {
|
|
1450
|
+
return verb !== void 0 && NPM_INSTALL_VERBS.has(verb);
|
|
1451
|
+
}
|
|
1452
|
+
return false;
|
|
1453
|
+
}
|
|
1454
|
+
function needsManifest({ program, args }) {
|
|
1455
|
+
if (isGlobal(args) || args.some((a) => DIRECTORY_FLAGS.test(a))) return false;
|
|
1456
|
+
const [verb, ...rest] = positionals(args);
|
|
1457
|
+
if (program === "yarn") {
|
|
1458
|
+
if (verb === void 0) return !args.some((a) => VERSION_OR_HELP.has(a));
|
|
1459
|
+
if (verb === "install" || verb === "run") return true;
|
|
1460
|
+
return verb === "workspaces" && rest[0] === "focus";
|
|
1461
|
+
}
|
|
1462
|
+
if (program === "npm" || program === "pnpm" || program === "bun") {
|
|
1463
|
+
if (verb === void 0) return false;
|
|
1464
|
+
if (NEEDS_MANIFEST_VERBS.has(verb)) return true;
|
|
1465
|
+
return (verb === "install" || verb === "i") && rest.length === 0;
|
|
1466
|
+
}
|
|
1467
|
+
return false;
|
|
1468
|
+
}
|
|
1469
|
+
function packageManagerAtRoot(command) {
|
|
1470
|
+
for (const step of simpleCommands(command)) {
|
|
1471
|
+
if (DIRECTORY_CHANGERS.has(step.program)) return null;
|
|
1472
|
+
if (JS_PACKAGE_MANAGERS.has(step.program) && positionals(step.args)[0] === "init") return null;
|
|
1473
|
+
if (needsManifest(step)) return step.program;
|
|
1474
|
+
}
|
|
1475
|
+
return null;
|
|
1476
|
+
}
|
|
1477
|
+
function installsDependencies(command) {
|
|
1478
|
+
return simpleCommands(command).some(installsInStep);
|
|
1479
|
+
}
|
|
1480
|
+
function missingPackageJsonError(command, files, dir) {
|
|
1481
|
+
const pm = packageManagerAtRoot(command);
|
|
1482
|
+
if (!pm || files.has("package.json")) return null;
|
|
1483
|
+
return `No package.json in ${dir}. Run vibeview build from your project root. (Your build command runs \`${pm}\` there; nothing was uploaded or charged.)`;
|
|
1484
|
+
}
|
|
1485
|
+
function missingInstallWarning(command, files) {
|
|
1486
|
+
const hit = JS_LOCKFILE_INSTALLS.find(([lockfile]) => files.has(lockfile));
|
|
1487
|
+
if (!hit || files.has(".pnp.cjs") || installsDependencies(command)) return null;
|
|
1488
|
+
return `Your build command doesn't install dependencies; node_modules is never uploaded, so the build will likely fail. Add \`${hit[1]} &&\` at the start.`;
|
|
1489
|
+
}
|
|
1490
|
+
function missingLockfileWarning(command, files) {
|
|
1491
|
+
if (!files.has("package.json")) return null;
|
|
1492
|
+
if (JS_LOCKFILE_INSTALLS.some(([lockfile]) => files.has(lockfile))) return null;
|
|
1493
|
+
if (!installsDependencies(command)) return null;
|
|
1494
|
+
return "No lockfile in the upload (yarn.lock, package-lock.json, pnpm-lock.yaml): a frozen install (`npm ci`, `--immutable`, `--frozen-lockfile`) will fail, and any other install resolves fresh versions. Commit your lockfile, or run vibeview build from the directory that has it.";
|
|
1495
|
+
}
|
|
1496
|
+
function bunWarning(command) {
|
|
1497
|
+
const step = simpleCommands(command).find((s) => s.program === "bun" || s.program === "bunx");
|
|
1498
|
+
if (!step) return null;
|
|
1499
|
+
return `Your build command runs \`${step.program}\`, but cloud builders don't have Bun, so that step will fail unless your command installs Bun first. Use npm, yarn or pnpm instead.`;
|
|
1500
|
+
}
|
|
1501
|
+
function buildCommandWarnings(command, files) {
|
|
1502
|
+
return [
|
|
1503
|
+
bunWarning(command),
|
|
1504
|
+
missingInstallWarning(command, files),
|
|
1505
|
+
missingLockfileWarning(command, files)
|
|
1506
|
+
].filter((w) => w !== null);
|
|
1507
|
+
}
|
|
1508
|
+
function projectRootHint(cwd, gitRoot, files, hasPackageJson = (dir) => isFile2(join7(dir, "package.json"))) {
|
|
1509
|
+
if (!gitRoot || files.has("package.json")) return null;
|
|
1510
|
+
cwd = realPathOr(cwd);
|
|
1511
|
+
gitRoot = realPathOr(gitRoot);
|
|
1512
|
+
const rel = relative(gitRoot, cwd);
|
|
1513
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) return null;
|
|
1514
|
+
for (let dir = dirname2(cwd); ; dir = dirname2(dir)) {
|
|
1515
|
+
if (hasPackageJson(dir)) {
|
|
1516
|
+
return `You're running from ${cwd}; the project root looks like ${dir}. Only ${cwd} will be uploaded.`;
|
|
1517
|
+
}
|
|
1518
|
+
if (dir === gitRoot || dir === dirname2(dir)) return null;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
function realPathOr(path) {
|
|
1522
|
+
try {
|
|
1523
|
+
return realpathSync(path);
|
|
1524
|
+
} catch {
|
|
1525
|
+
return path;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
function isFile2(path) {
|
|
1529
|
+
try {
|
|
1530
|
+
return statSync4(path).isFile();
|
|
1531
|
+
} catch {
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
async function gitToplevel(cwd) {
|
|
1536
|
+
return new Promise((resolve) => {
|
|
1537
|
+
execFile3("git", ["rev-parse", "--show-toplevel"], { cwd }, (err, stdout) => {
|
|
1538
|
+
resolve(err ? null : stdout.trim() || null);
|
|
1539
|
+
});
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1332
1543
|
// src/commands/build.ts
|
|
1333
1544
|
var CLOUD_PLATFORMS = ["ios", "android", "tvos", "androidtv"];
|
|
1334
1545
|
var PRODUCTION_SIGNING_PLATFORMS = ["ios", "android", "androidtv", "tvos"];
|
|
@@ -1795,6 +2006,25 @@ async function runCloudBuildCommand(cwd, cfgRef, opts, deps) {
|
|
|
1795
2006
|
}
|
|
1796
2007
|
throw err;
|
|
1797
2008
|
}
|
|
2009
|
+
const uploaded = new Set(files);
|
|
2010
|
+
const warnings = /* @__PURE__ */ new Map();
|
|
2011
|
+
for (const p of platforms) {
|
|
2012
|
+
const command = productionBuilds.get(p)?.command ?? cfg.platforms[p].build.command;
|
|
2013
|
+
const missingPkg = missingPackageJsonError(command, uploaded, cwd);
|
|
2014
|
+
if (missingPkg) fail(opts, missingPkg, "no_package_json");
|
|
2015
|
+
for (const w of buildCommandWarnings(command, uploaded)) {
|
|
2016
|
+
warnings.set(w, [...warnings.get(w) ?? [], p]);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
const rootHint = projectRootHint(cwd, await (d.gitToplevel ?? gitToplevel)(cwd), uploaded);
|
|
2020
|
+
if (rootHint) warnings.set(rootHint, [...platforms]);
|
|
2021
|
+
for (const [message, ps] of warnings) {
|
|
2022
|
+
for (const p of ps) emitCloud(opts.json, { event: "warning", platform: p, message });
|
|
2023
|
+
if (!opts.json) {
|
|
2024
|
+
const prefix = platforms.length > 1 && ps.length < platforms.length ? `${ps.join(", ")}: ` : "";
|
|
2025
|
+
console.error(chalk3.yellow(`\u26A0 ${prefix}${message}`));
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
1798
2028
|
const packSpinner = !opts.json ? createSpinner(`packing source (${files.length} files)`) : null;
|
|
1799
2029
|
packSpinner?.start();
|
|
1800
2030
|
let tarPath;
|
|
@@ -2173,7 +2403,7 @@ your build command must produce a zip \u2014 use the package/zip step, not deplo
|
|
|
2173
2403
|
// src/commands/dev.ts
|
|
2174
2404
|
import { spawn as spawn4 } from "child_process";
|
|
2175
2405
|
import { createWriteStream, mkdirSync as mkdirSync5 } from "fs";
|
|
2176
|
-
import { dirname as
|
|
2406
|
+
import { dirname as dirname5 } from "path";
|
|
2177
2407
|
import { createInterface as createInterface3 } from "readline";
|
|
2178
2408
|
import { select as select3 } from "@inquirer/prompts";
|
|
2179
2409
|
import chalk4 from "chalk";
|
|
@@ -2376,14 +2606,14 @@ import { createInterface as createInterface2 } from "readline";
|
|
|
2376
2606
|
|
|
2377
2607
|
// src/lib/metro-state.ts
|
|
2378
2608
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync7, rmSync as rmSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
2379
|
-
import { dirname as
|
|
2609
|
+
import { dirname as dirname3, join as join8 } from "path";
|
|
2380
2610
|
var STATE_DIR2 = ".vibeview";
|
|
2381
2611
|
var STATE_FILE2 = "metro-state.json";
|
|
2382
2612
|
function statePath2(cwd) {
|
|
2383
|
-
return
|
|
2613
|
+
return join8(cwd, STATE_DIR2, STATE_FILE2);
|
|
2384
2614
|
}
|
|
2385
2615
|
function metroLogPath(cwd) {
|
|
2386
|
-
return
|
|
2616
|
+
return join8(cwd, STATE_DIR2, "metro.log");
|
|
2387
2617
|
}
|
|
2388
2618
|
function isPidAlive2(pid) {
|
|
2389
2619
|
try {
|
|
@@ -2395,7 +2625,7 @@ function isPidAlive2(pid) {
|
|
|
2395
2625
|
}
|
|
2396
2626
|
function writeMetroState(cwd, state) {
|
|
2397
2627
|
const path = statePath2(cwd);
|
|
2398
|
-
mkdirSync4(
|
|
2628
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
2399
2629
|
writeFileSync6(path, JSON.stringify(state, null, 2) + "\n");
|
|
2400
2630
|
}
|
|
2401
2631
|
function readMetroState(cwd) {
|
|
@@ -2534,7 +2764,7 @@ async function waitFor(cond, timeoutMs, everyMs) {
|
|
|
2534
2764
|
|
|
2535
2765
|
// src/lib/reload-loop.ts
|
|
2536
2766
|
import { watch } from "fs";
|
|
2537
|
-
import { basename, dirname as
|
|
2767
|
+
import { basename, dirname as dirname4 } from "path";
|
|
2538
2768
|
function createReloadLoop(deps) {
|
|
2539
2769
|
let inFlight = null;
|
|
2540
2770
|
let pendingFollowUp = false;
|
|
@@ -2615,11 +2845,11 @@ function isIgnoredWatchPath(relPath, artifact, userIgnore = []) {
|
|
|
2615
2845
|
if (IGNORED_FILES.includes(rel)) return true;
|
|
2616
2846
|
if (userIgnore.some((entry) => isUnderPath(rel, entry))) return true;
|
|
2617
2847
|
const artifactPattern = artifact.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
2618
|
-
const artifactDir =
|
|
2848
|
+
const artifactDir = dirname4(artifactPattern);
|
|
2619
2849
|
if (artifactDir !== "." && (rel === artifactDir || rel.startsWith(`${artifactDir}/`))) {
|
|
2620
2850
|
return true;
|
|
2621
2851
|
}
|
|
2622
|
-
if (
|
|
2852
|
+
if (dirname4(rel) !== artifactDir) return false;
|
|
2623
2853
|
const name = basename(artifactPattern);
|
|
2624
2854
|
const re = new RegExp(`^${name.split("*").map(escapeRegExp).join(".*")}$`);
|
|
2625
2855
|
return re.test(basename(rel));
|
|
@@ -3497,7 +3727,7 @@ async function runDevInProcess(opts) {
|
|
|
3497
3727
|
const captureMetroLine = (line) => {
|
|
3498
3728
|
try {
|
|
3499
3729
|
if (!metroLogStream) {
|
|
3500
|
-
mkdirSync5(
|
|
3730
|
+
mkdirSync5(dirname5(metroLogPath(process.cwd())), { recursive: true });
|
|
3501
3731
|
metroLogStream = createWriteStream(metroLogPath(process.cwd()), { flags: "w" });
|
|
3502
3732
|
}
|
|
3503
3733
|
metroLogStream.write(line.replace(/^\[metro\] /, "") + "\n");
|
|
@@ -4618,9 +4848,9 @@ function flagKey(cli) {
|
|
|
4618
4848
|
const name = cli.replace(/^--/, "");
|
|
4619
4849
|
return name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
4620
4850
|
}
|
|
4621
|
-
function buildToolCall(cmd,
|
|
4851
|
+
function buildToolCall(cmd, positionals2, opts) {
|
|
4622
4852
|
if (cmd.verb === "tap") {
|
|
4623
|
-
const target =
|
|
4853
|
+
const target = positionals2[0];
|
|
4624
4854
|
if (!target) throw new Error('tap requires a target: an element ref (@e5) or "x,y"');
|
|
4625
4855
|
const coords = parseCoords(target);
|
|
4626
4856
|
if (coords) {
|
|
@@ -4635,7 +4865,7 @@ function buildToolCall(cmd, positionals, opts) {
|
|
|
4635
4865
|
return { tool: "tap", args: { ref: stripRef(target) } };
|
|
4636
4866
|
}
|
|
4637
4867
|
if (cmd.verb === "long-press") {
|
|
4638
|
-
const target =
|
|
4868
|
+
const target = positionals2[0];
|
|
4639
4869
|
if (!target) throw new Error("long-press requires a ref, e.g. @e5");
|
|
4640
4870
|
if (parseCoords(target) || target.includes(",") && !REF_RE.test(target)) {
|
|
4641
4871
|
throw new Error(
|
|
@@ -4649,8 +4879,8 @@ function buildToolCall(cmd, positionals, opts) {
|
|
|
4649
4879
|
return { tool: cmd.tool, args: args2 };
|
|
4650
4880
|
}
|
|
4651
4881
|
if (cmd.verb === "drag") {
|
|
4652
|
-
const from =
|
|
4653
|
-
const to =
|
|
4882
|
+
const from = positionals2[0];
|
|
4883
|
+
const to = positionals2[1];
|
|
4654
4884
|
if (!from || !to) {
|
|
4655
4885
|
throw new Error('drag requires two targets: <from> <to>, each an element ref (@e5) or "x,y"');
|
|
4656
4886
|
}
|
|
@@ -4691,7 +4921,7 @@ function buildToolCall(cmd, positionals, opts) {
|
|
|
4691
4921
|
return { tool: cmd.tool, args: args2 };
|
|
4692
4922
|
}
|
|
4693
4923
|
if (cmd.verb === "scroll") {
|
|
4694
|
-
const direction =
|
|
4924
|
+
const direction = positionals2[0];
|
|
4695
4925
|
const valid = ["up", "down", "left", "right"];
|
|
4696
4926
|
if (!direction || !valid.includes(direction)) {
|
|
4697
4927
|
throw new Error(`scroll: direction must be one of ${valid.join(", ")}, got "${direction}"`);
|
|
@@ -4699,7 +4929,7 @@ function buildToolCall(cmd, positionals, opts) {
|
|
|
4699
4929
|
return { tool: cmd.tool, args: { name: `scroll_${direction}` } };
|
|
4700
4930
|
}
|
|
4701
4931
|
if (cmd.verb === "swipe") {
|
|
4702
|
-
const direction =
|
|
4932
|
+
const direction = positionals2[0];
|
|
4703
4933
|
const valid = ["up", "down", "left", "right"];
|
|
4704
4934
|
if (!direction || !valid.includes(direction)) {
|
|
4705
4935
|
throw new Error(`swipe: direction must be one of ${valid.join(", ")}, got "${direction}"`);
|
|
@@ -4711,7 +4941,7 @@ function buildToolCall(cmd, positionals, opts) {
|
|
|
4711
4941
|
return { tool: cmd.tool, args: args2 };
|
|
4712
4942
|
}
|
|
4713
4943
|
if (cmd.verb === "set-posture") {
|
|
4714
|
-
const hasPosture =
|
|
4944
|
+
const hasPosture = positionals2[0] !== void 0 && positionals2[0] !== "";
|
|
4715
4945
|
const hasAngle = opts.angle !== void 0 && opts.angle !== "";
|
|
4716
4946
|
if (hasPosture === hasAngle) {
|
|
4717
4947
|
throw new FlagError(
|
|
@@ -4724,7 +4954,7 @@ function buildToolCall(cmd, positionals, opts) {
|
|
|
4724
4954
|
for (const param of cmd.params) {
|
|
4725
4955
|
let raw;
|
|
4726
4956
|
if (param.cli === "positional") {
|
|
4727
|
-
raw =
|
|
4957
|
+
raw = positionals2[positionalIndex];
|
|
4728
4958
|
positionalIndex += 1;
|
|
4729
4959
|
} else {
|
|
4730
4960
|
const key = flagKey(param.cli);
|
|
@@ -4774,7 +5004,7 @@ var NON_USE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
4774
5004
|
function countsAsUse(tool) {
|
|
4775
5005
|
return !NON_USE_TOOLS.has(tool);
|
|
4776
5006
|
}
|
|
4777
|
-
async function runAgentVerb(cmd,
|
|
5007
|
+
async function runAgentVerb(cmd, positionals2, opts) {
|
|
4778
5008
|
const { token, baseUrl } = resolveAuth(opts);
|
|
4779
5009
|
const sessionId = opts.session || readDevState(process.cwd())?.session_id;
|
|
4780
5010
|
if (!sessionId) {
|
|
@@ -4783,7 +5013,7 @@ async function runAgentVerb(cmd, positionals, opts) {
|
|
|
4783
5013
|
}
|
|
4784
5014
|
let call;
|
|
4785
5015
|
try {
|
|
4786
|
-
call = buildToolCall(cmd,
|
|
5016
|
+
call = buildToolCall(cmd, positionals2, opts);
|
|
4787
5017
|
} catch (err) {
|
|
4788
5018
|
process.stderr.write(`error: ${err instanceof Error ? err.message : String(err)}
|
|
4789
5019
|
`);
|
package/dist/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
readMetroState,
|
|
26
26
|
recordDevUse,
|
|
27
27
|
runAgentVerb
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-6LNOXEPC.js";
|
|
29
29
|
import {
|
|
30
30
|
ApiClient,
|
|
31
31
|
ApiStatusError,
|
|
@@ -3662,8 +3662,10 @@ var CLAUDE_MD_SUGGESTION = "After changing UI or app behavior, verify it on a li
|
|
|
3662
3662
|
function claudeCliAvailable() {
|
|
3663
3663
|
return spawnSync("claude", ["--version"], { stdio: "ignore" }).error === void 0;
|
|
3664
3664
|
}
|
|
3665
|
+
var MCP_COMMAND = ["npx", "-y", "vibeview", "mcp"];
|
|
3666
|
+
var MCP_ADD_HINT = `claude mcp add vibeview -- ${MCP_COMMAND.join(" ")}`;
|
|
3665
3667
|
function registerMcp() {
|
|
3666
|
-
const res = spawnSync("claude", ["mcp", "add", "vibeview", "--",
|
|
3668
|
+
const res = spawnSync("claude", ["mcp", "add", "vibeview", "--", ...MCP_COMMAND], {
|
|
3667
3669
|
encoding: "utf8"
|
|
3668
3670
|
});
|
|
3669
3671
|
if (res.status === 0) {
|
|
@@ -3676,7 +3678,7 @@ function registerMcp() {
|
|
|
3676
3678
|
return;
|
|
3677
3679
|
}
|
|
3678
3680
|
console.error(chalk21.yellow(`\u26A0 could not register the MCP server: ${out.trim() || res.error}`));
|
|
3679
|
-
console.error(chalk21.yellow(
|
|
3681
|
+
console.error(chalk21.yellow(` register it manually: ${MCP_ADD_HINT}`));
|
|
3680
3682
|
}
|
|
3681
3683
|
async function agentSetup(opts) {
|
|
3682
3684
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -3702,9 +3704,7 @@ async function agentSetup(opts) {
|
|
|
3702
3704
|
if (!claudeCliAvailable()) {
|
|
3703
3705
|
if (opts.mcp === true) {
|
|
3704
3706
|
console.error(
|
|
3705
|
-
chalk21.yellow(
|
|
3706
|
-
"\u26A0 claude CLI not found \u2014 install Claude Code, then run: claude mcp add vibeview -- vibeview mcp"
|
|
3707
|
-
)
|
|
3707
|
+
chalk21.yellow(`\u26A0 claude CLI not found \u2014 install Claude Code, then run: ${MCP_ADD_HINT}`)
|
|
3708
3708
|
);
|
|
3709
3709
|
}
|
|
3710
3710
|
} else if (opts.mcp === true || process.stdin.isTTY && await confirm({
|
|
@@ -4147,7 +4147,7 @@ program.command("dev-reload").description(
|
|
|
4147
4147
|
program.command("mcp").description(
|
|
4148
4148
|
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
|
|
4149
4149
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
4150
|
-
const { runMcpServer } = await import("./server-
|
|
4150
|
+
const { runMcpServer } = await import("./server-QRIH34XN.js");
|
|
4151
4151
|
await runMcpServer({ ...opts, version: pkg.version });
|
|
4152
4152
|
});
|
|
4153
4153
|
program.command("agent-setup").description(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeview",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "VibeView CLI -- run your app on cloud iOS simulators, Android emulators, Apple TV, Android TV and Roku 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",
|
|
@@ -33,11 +33,13 @@
|
|
|
33
33
|
"ignore": "^7.0.0",
|
|
34
34
|
"open": "^11.0.0",
|
|
35
35
|
"ora": "^9.0.0",
|
|
36
|
+
"shell-quote": "^1.8.3",
|
|
36
37
|
"ws": "^8.18.0",
|
|
37
38
|
"zod": "^4.0.0"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@types/node": "^20.0.0",
|
|
42
|
+
"@types/shell-quote": "^1.7.5",
|
|
41
43
|
"@types/ws": "^8.5.0",
|
|
42
44
|
"dotenv": "^16.4.5",
|
|
43
45
|
"tsup": "^8.0.0",
|