vibeview 0.29.0 → 0.31.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/dist/{chunk-T6EQQV6E.js → chunk-P6UIJALK.js} +256 -81
- package/dist/{chunk-PBMB6TG4.js → chunk-ZZK7YQSP.js} +33 -6
- package/dist/cli.js +402 -205
- package/dist/{server-IGY2VPYT.js → server-GPFOZOFL.js} +8 -4
- package/dist/{upload-app-FEZG23NU.js → upload-app-2LVG4LMB.js} +1 -1
- package/package.json +1 -1
- package/skills/vibeview-agent/SKILL.md +10 -5
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
resolveArtifact,
|
|
14
14
|
resolveAuth,
|
|
15
15
|
uploadAppBuild
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-ZZK7YQSP.js";
|
|
17
17
|
|
|
18
18
|
// src/lib/dev-config.ts
|
|
19
19
|
import { readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
@@ -179,9 +179,33 @@ function saveDevConfig(cwd, cfg) {
|
|
|
179
179
|
function updateDevConfig(cwd, platform, apply) {
|
|
180
180
|
const fresh = loadDevConfig(cwd) ?? { defaultPlatform: platform, platforms: {} };
|
|
181
181
|
if (apply(fresh) === false) return fresh;
|
|
182
|
+
const onDisk = readRawConfig(cwd);
|
|
183
|
+
if (onDisk !== void 0 && canonicalJson(onDisk) === canonicalJson(fresh)) return fresh;
|
|
182
184
|
saveDevConfig(cwd, fresh);
|
|
183
185
|
return fresh;
|
|
184
186
|
}
|
|
187
|
+
function readRawConfig(cwd) {
|
|
188
|
+
try {
|
|
189
|
+
return JSON.parse(readFileSync(join(cwd, FILE), "utf-8"));
|
|
190
|
+
} catch {
|
|
191
|
+
return void 0;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function canonicalJson(value) {
|
|
195
|
+
const sortKeys = (v) => {
|
|
196
|
+
if (Array.isArray(v)) return v.map(sortKeys);
|
|
197
|
+
if (v && typeof v === "object") {
|
|
198
|
+
const out = {};
|
|
199
|
+
for (const k of Object.keys(v).sort()) {
|
|
200
|
+
const child = v[k];
|
|
201
|
+
if (child !== void 0) out[k] = sortKeys(child);
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
return v;
|
|
206
|
+
};
|
|
207
|
+
return JSON.stringify(sortKeys(value));
|
|
208
|
+
}
|
|
185
209
|
function configLeaves(value, path = [], out = []) {
|
|
186
210
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
187
211
|
for (const [k, v] of Object.entries(value)) {
|
|
@@ -408,30 +432,25 @@ function isPidAlive(pid) {
|
|
|
408
432
|
}
|
|
409
433
|
}
|
|
410
434
|
var WHOLE_DIR_LINE = /^(\/?)\.vibeview\/?$/;
|
|
435
|
+
var VIBEVIEW_DIR_GITIGNORE = "# VibeView local state (written by the vibeview CLI). test-spec.json is committed for CI.\n*\n!test-spec.json\n";
|
|
411
436
|
function ensureGitignored(cwd) {
|
|
437
|
+
try {
|
|
438
|
+
const dir = join2(cwd, ".vibeview");
|
|
439
|
+
mkdirSync(dir, { recursive: true });
|
|
440
|
+
const inner = join2(dir, ".gitignore");
|
|
441
|
+
if (!existsSync(inner)) writeFileSync2(inner, VIBEVIEW_DIR_GITIGNORE);
|
|
442
|
+
} catch {
|
|
443
|
+
}
|
|
412
444
|
try {
|
|
413
445
|
const gitignore = join2(cwd, ".gitignore");
|
|
414
446
|
if (!existsSync(gitignore)) return;
|
|
415
447
|
const content = readFileSync2(gitignore, "utf-8");
|
|
416
448
|
const lines = content.split("\n");
|
|
417
|
-
const
|
|
418
|
-
if (
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
lines.splice(i, 1, `${anchor}.vibeview/*`, `!${anchor}.vibeview/test-spec.json`);
|
|
423
|
-
writeFileSync2(gitignore, lines.join("\n"));
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
const prefix = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
|
|
427
|
-
writeFileSync2(
|
|
428
|
-
gitignore,
|
|
429
|
-
`${content}${prefix}
|
|
430
|
-
# VibeView local session state (test-spec.json is committed for CI)
|
|
431
|
-
.vibeview/*
|
|
432
|
-
!.vibeview/test-spec.json
|
|
433
|
-
`
|
|
434
|
-
);
|
|
449
|
+
const i = lines.findIndex((l) => WHOLE_DIR_LINE.test(l.trim()));
|
|
450
|
+
if (i === -1) return;
|
|
451
|
+
const anchor = WHOLE_DIR_LINE.exec(lines[i].trim())[1];
|
|
452
|
+
lines.splice(i, 1, `${anchor}.vibeview/*`, `!${anchor}.vibeview/test-spec.json`);
|
|
453
|
+
writeFileSync2(gitignore, lines.join("\n"));
|
|
435
454
|
} catch {
|
|
436
455
|
}
|
|
437
456
|
}
|
|
@@ -479,6 +498,108 @@ async function checkMetro(port, timeoutMs = 1500) {
|
|
|
479
498
|
}
|
|
480
499
|
}
|
|
481
500
|
|
|
501
|
+
// src/lib/local-config.ts
|
|
502
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
503
|
+
import { join as join3 } from "path";
|
|
504
|
+
var DIR = ".vibeview";
|
|
505
|
+
var FILE2 = "local.json";
|
|
506
|
+
function localConfigPath(cwd) {
|
|
507
|
+
return join3(cwd, DIR, FILE2);
|
|
508
|
+
}
|
|
509
|
+
function loadLocalDevConfig(cwd) {
|
|
510
|
+
let parsed;
|
|
511
|
+
try {
|
|
512
|
+
parsed = JSON.parse(readFileSync3(localConfigPath(cwd), "utf-8"));
|
|
513
|
+
} catch {
|
|
514
|
+
return {};
|
|
515
|
+
}
|
|
516
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
517
|
+
const raw = parsed;
|
|
518
|
+
const out = {};
|
|
519
|
+
if (isDevPlatform(raw.defaultPlatform)) out.defaultPlatform = raw.defaultPlatform;
|
|
520
|
+
if (raw.platforms && typeof raw.platforms === "object") {
|
|
521
|
+
for (const [key, value] of Object.entries(raw.platforms)) {
|
|
522
|
+
if (!isDevPlatform(key) || !value || typeof value !== "object") continue;
|
|
523
|
+
const v = value;
|
|
524
|
+
const entry = {};
|
|
525
|
+
if (typeof v.appId === "string" && v.appId) entry.appId = v.appId;
|
|
526
|
+
if (typeof v.deviceCategory === "string" && v.deviceCategory) {
|
|
527
|
+
entry.deviceCategory = v.deviceCategory;
|
|
528
|
+
}
|
|
529
|
+
if (typeof v.metroPort === "number" && Number.isInteger(v.metroPort) && v.metroPort > 0) {
|
|
530
|
+
entry.metroPort = v.metroPort;
|
|
531
|
+
}
|
|
532
|
+
if (Object.keys(entry).length) (out.platforms ??= {})[key] = entry;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return out;
|
|
536
|
+
}
|
|
537
|
+
function prune(cfg) {
|
|
538
|
+
const out = {};
|
|
539
|
+
if (cfg.defaultPlatform) out.defaultPlatform = cfg.defaultPlatform;
|
|
540
|
+
for (const [key, entry] of Object.entries(cfg.platforms ?? {})) {
|
|
541
|
+
const kept = Object.fromEntries(
|
|
542
|
+
Object.entries(entry ?? {}).filter(([, v]) => v !== void 0)
|
|
543
|
+
);
|
|
544
|
+
if (Object.keys(kept).length) (out.platforms ??= {})[key] = kept;
|
|
545
|
+
}
|
|
546
|
+
return out;
|
|
547
|
+
}
|
|
548
|
+
function saveLocalDevConfig(cwd, cfg) {
|
|
549
|
+
const pruned = prune(cfg);
|
|
550
|
+
const target = localConfigPath(cwd);
|
|
551
|
+
if (!pruned.defaultPlatform && !pruned.platforms) {
|
|
552
|
+
rmSync3(target, { force: true });
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
mkdirSync2(join3(cwd, DIR), { recursive: true });
|
|
556
|
+
const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
557
|
+
try {
|
|
558
|
+
writeFileSync3(tmp, JSON.stringify(pruned, null, 2) + "\n");
|
|
559
|
+
renameSync2(tmp, target);
|
|
560
|
+
} catch (err) {
|
|
561
|
+
rmSync3(tmp, { force: true });
|
|
562
|
+
throw err;
|
|
563
|
+
}
|
|
564
|
+
ensureGitignored(cwd);
|
|
565
|
+
}
|
|
566
|
+
function effectiveDevConfig(committed, local) {
|
|
567
|
+
if (!committed && !local.defaultPlatform && !local.platforms) return null;
|
|
568
|
+
const base = committed ? { ...committed, platforms: { ...committed.platforms } } : { defaultPlatform: local.defaultPlatform ?? "ios", platforms: {} };
|
|
569
|
+
if (local.defaultPlatform) base.defaultPlatform = local.defaultPlatform;
|
|
570
|
+
for (const [key, entry] of Object.entries(local.platforms ?? {})) {
|
|
571
|
+
const platform = key;
|
|
572
|
+
const merged = { ...base.platforms[platform] };
|
|
573
|
+
if (entry?.appId) merged.appId = entry.appId;
|
|
574
|
+
if (entry?.deviceCategory) merged.deviceCategory = entry.deviceCategory;
|
|
575
|
+
if (entry?.metroPort !== void 0) merged.metroPort = entry.metroPort;
|
|
576
|
+
base.platforms[platform] = merged;
|
|
577
|
+
}
|
|
578
|
+
return base;
|
|
579
|
+
}
|
|
580
|
+
function describeLocalChanges(before, after) {
|
|
581
|
+
const changes = [];
|
|
582
|
+
const a = prune(before);
|
|
583
|
+
const b = prune(after);
|
|
584
|
+
if (a.defaultPlatform !== b.defaultPlatform) {
|
|
585
|
+
changes.push(
|
|
586
|
+
b.defaultPlatform ? `defaultPlatform ${b.defaultPlatform}` : `defaultPlatform (removed, was ${a.defaultPlatform})`
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a.platforms ?? {}), ...Object.keys(b.platforms ?? {})]);
|
|
590
|
+
for (const key of keys) {
|
|
591
|
+
const pa = a.platforms?.[key] ?? {};
|
|
592
|
+
const pb = b.platforms?.[key] ?? {};
|
|
593
|
+
for (const field of ["appId", "deviceCategory", "metroPort"]) {
|
|
594
|
+
if (pa[field] === pb[field]) continue;
|
|
595
|
+
changes.push(
|
|
596
|
+
pb[field] === void 0 ? `${key}.${field} (removed, was ${String(pa[field])})` : `${key}.${field} ${String(pb[field])}`
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return changes;
|
|
601
|
+
}
|
|
602
|
+
|
|
482
603
|
// src/commands/build.ts
|
|
483
604
|
import { spawn as spawn2 } from "child_process";
|
|
484
605
|
import { createInterface } from "readline";
|
|
@@ -490,12 +611,12 @@ import chalk2 from "chalk";
|
|
|
490
611
|
import { input, select } from "@inquirer/prompts";
|
|
491
612
|
|
|
492
613
|
// src/lib/project-detect.ts
|
|
493
|
-
import { existsSync as existsSync2, readFileSync as
|
|
494
|
-
import { join as
|
|
614
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4, readdirSync, statSync } from "fs";
|
|
615
|
+
import { join as join4 } from "path";
|
|
495
616
|
import ignore from "ignore";
|
|
496
617
|
function readJson(cwd, name) {
|
|
497
618
|
try {
|
|
498
|
-
return JSON.parse(
|
|
619
|
+
return JSON.parse(readFileSync4(join4(cwd, name), "utf-8"));
|
|
499
620
|
} catch {
|
|
500
621
|
return null;
|
|
501
622
|
}
|
|
@@ -510,19 +631,19 @@ function isFile(path) {
|
|
|
510
631
|
function detectFlavor(cwd) {
|
|
511
632
|
let deps = {};
|
|
512
633
|
try {
|
|
513
|
-
const pkg = JSON.parse(
|
|
634
|
+
const pkg = JSON.parse(readFileSync4(join4(cwd, "package.json"), "utf-8"));
|
|
514
635
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
515
636
|
} catch {
|
|
516
637
|
}
|
|
517
638
|
if ("expo" in deps) return "expo";
|
|
518
|
-
if (existsSync2(
|
|
519
|
-
if (isFile(
|
|
639
|
+
if (existsSync2(join4(cwd, "android", "gradlew")) || iosWorkspaceName(cwd)) return "bare-rn";
|
|
640
|
+
if (isFile(join4(cwd, "manifest"))) return "roku";
|
|
520
641
|
return "unknown";
|
|
521
642
|
}
|
|
522
643
|
function nativeDirShipped(cwd, dir) {
|
|
523
|
-
if (!existsSync2(
|
|
644
|
+
if (!existsSync2(join4(cwd, dir))) return false;
|
|
524
645
|
try {
|
|
525
|
-
const ig = ignore().add(
|
|
646
|
+
const ig = ignore().add(readFileSync4(join4(cwd, ".gitignore"), "utf-8"));
|
|
526
647
|
return !ig.ignores(`${dir}/`);
|
|
527
648
|
} catch {
|
|
528
649
|
return true;
|
|
@@ -538,7 +659,7 @@ function isManagedExpo(cwd, platform) {
|
|
|
538
659
|
}
|
|
539
660
|
function iosWorkspaceName(cwd) {
|
|
540
661
|
try {
|
|
541
|
-
const found = readdirSync(
|
|
662
|
+
const found = readdirSync(join4(cwd, "ios")).find((f) => f.endsWith(".xcworkspace"));
|
|
542
663
|
return found ? found.replace(/\.xcworkspace$/, "") : null;
|
|
543
664
|
} catch {
|
|
544
665
|
return null;
|
|
@@ -547,9 +668,9 @@ function iosWorkspaceName(cwd) {
|
|
|
547
668
|
var IOS_ARTIFACT = "ios/build/Build/Products/Debug-iphonesimulator/*.app";
|
|
548
669
|
var ANDROID_ARTIFACT = "android/app/build/outputs/apk/debug/*.apk";
|
|
549
670
|
function installCommand(cwd) {
|
|
550
|
-
if (existsSync2(
|
|
551
|
-
if (existsSync2(
|
|
552
|
-
if (existsSync2(
|
|
671
|
+
if (existsSync2(join4(cwd, "yarn.lock"))) return "yarn install --immutable";
|
|
672
|
+
if (existsSync2(join4(cwd, "pnpm-lock.yaml"))) return "pnpm install --frozen-lockfile";
|
|
673
|
+
if (existsSync2(join4(cwd, "package-lock.json"))) return "npm ci";
|
|
553
674
|
return "yarn install --immutable";
|
|
554
675
|
}
|
|
555
676
|
function managedExpoAndroidCommand(cwd) {
|
|
@@ -563,7 +684,7 @@ var ROKU_ZIP_COMMAND = "zip -r out/channel.zip manifest source components images
|
|
|
563
684
|
function rokuDefaults(cwd) {
|
|
564
685
|
const pkg = readJson(cwd, "package.json");
|
|
565
686
|
const scripts = pkg?.scripts ?? {};
|
|
566
|
-
const runner = existsSync2(
|
|
687
|
+
const runner = existsSync2(join4(cwd, "yarn.lock")) ? "yarn" : "npm run";
|
|
567
688
|
const packageScript = Object.entries(scripts).find(
|
|
568
689
|
([name, body]) => !name.startsWith("deploy") && (name.startsWith("package") || body.includes("--package-only") || body.includes("zipPackage"))
|
|
569
690
|
);
|
|
@@ -621,7 +742,7 @@ function expoSanitizedName(name) {
|
|
|
621
742
|
return name.replace(/[\W_]+/g, "").normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
622
743
|
}
|
|
623
744
|
function managedExpoIosName(cwd) {
|
|
624
|
-
if (existsSync2(
|
|
745
|
+
if (existsSync2(join4(cwd, "app.config.js")) || existsSync2(join4(cwd, "app.config.ts"))) {
|
|
625
746
|
return null;
|
|
626
747
|
}
|
|
627
748
|
const raw = readJson(cwd, "app.config.json") ?? readJson(cwd, "app.json");
|
|
@@ -766,9 +887,9 @@ async function promptMetroCommand(cwd, platform) {
|
|
|
766
887
|
|
|
767
888
|
// src/lib/source-pack.ts
|
|
768
889
|
import { execFile, spawn } from "child_process";
|
|
769
|
-
import { lstatSync, mkdtempSync, readFileSync as
|
|
890
|
+
import { lstatSync, mkdtempSync, readFileSync as readFileSync5, rmSync as rmSync4, statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
770
891
|
import { tmpdir } from "os";
|
|
771
|
-
import { join as
|
|
892
|
+
import { join as join5 } from "path";
|
|
772
893
|
import ignore2 from "ignore";
|
|
773
894
|
var NotAGitRepoError = class extends Error {
|
|
774
895
|
constructor(message) {
|
|
@@ -794,15 +915,15 @@ async function listSourceFiles(projectRoot) {
|
|
|
794
915
|
const stdout = await runGitLsFiles(projectRoot);
|
|
795
916
|
const rawPaths = stdout.length ? stdout.split("\0").filter((p) => p.length > 0) : [];
|
|
796
917
|
let filtered = rawPaths;
|
|
797
|
-
const vvignorePath =
|
|
918
|
+
const vvignorePath = join5(projectRoot, ".vvignore");
|
|
798
919
|
if (statOrNull(vvignorePath)?.isFile()) {
|
|
799
|
-
const ig = ignore2().add(
|
|
920
|
+
const ig = ignore2().add(readFileSync5(vvignorePath, "utf-8"));
|
|
800
921
|
filtered = filtered.filter((p) => !ig.ignores(p));
|
|
801
922
|
}
|
|
802
923
|
const result = [];
|
|
803
924
|
for (const relPath of filtered) {
|
|
804
925
|
if (isForceExcluded(relPath)) continue;
|
|
805
|
-
const st = statOrNull(
|
|
926
|
+
const st = statOrNull(join5(projectRoot, relPath));
|
|
806
927
|
if (!st) continue;
|
|
807
928
|
result.push(relPath);
|
|
808
929
|
}
|
|
@@ -852,7 +973,7 @@ async function packSource(projectRoot, files, opts = {}) {
|
|
|
852
973
|
let totalBytes = 0;
|
|
853
974
|
const sized = [];
|
|
854
975
|
for (const relPath of files) {
|
|
855
|
-
const bytes = lstatSync(
|
|
976
|
+
const bytes = lstatSync(join5(projectRoot, relPath)).size;
|
|
856
977
|
totalBytes += bytes;
|
|
857
978
|
sized.push({ path: relPath, bytes });
|
|
858
979
|
}
|
|
@@ -863,14 +984,14 @@ async function packSource(projectRoot, files, opts = {}) {
|
|
|
863
984
|
largest
|
|
864
985
|
);
|
|
865
986
|
}
|
|
866
|
-
const tmpDir = mkdtempSync(
|
|
867
|
-
const cleanup = () =>
|
|
987
|
+
const tmpDir = mkdtempSync(join5(tmpdir(), "vibeview-source-pack-"));
|
|
988
|
+
const cleanup = () => rmSync4(tmpDir, { recursive: true, force: true });
|
|
868
989
|
try {
|
|
869
990
|
const supportsNull = await probeTarNullSupport();
|
|
870
|
-
const tarPath =
|
|
991
|
+
const tarPath = join5(tmpDir, "source.tgz");
|
|
871
992
|
if (supportsNull) {
|
|
872
|
-
const listPath =
|
|
873
|
-
|
|
993
|
+
const listPath = join5(tmpDir, "files.list");
|
|
994
|
+
writeFileSync4(listPath, files.map((f) => `${f}\0`).join(""));
|
|
874
995
|
await runTar(["-czf", tarPath, "-C", projectRoot, "--null", "-T", listPath]);
|
|
875
996
|
} else {
|
|
876
997
|
const badName = files.find((f) => f.includes("\n"));
|
|
@@ -879,8 +1000,8 @@ async function packSource(projectRoot, files, opts = {}) {
|
|
|
879
1000
|
`cannot pack source: filename contains a newline (${JSON.stringify(badName)}) and the installed tar doesn't support --null -T; rename the file or install a tar that supports --null`
|
|
880
1001
|
);
|
|
881
1002
|
}
|
|
882
|
-
const listPath =
|
|
883
|
-
|
|
1003
|
+
const listPath = join5(tmpDir, "files.list");
|
|
1004
|
+
writeFileSync4(listPath, files.map((f) => `${f}
|
|
884
1005
|
`).join(""));
|
|
885
1006
|
await runTar(["-czf", tarPath, "-C", projectRoot, "-T", listPath]);
|
|
886
1007
|
}
|
|
@@ -900,12 +1021,12 @@ var nullSupportCache = null;
|
|
|
900
1021
|
function probeTarNullSupport() {
|
|
901
1022
|
if (!nullSupportCache) {
|
|
902
1023
|
nullSupportCache = new Promise((resolve) => {
|
|
903
|
-
const tmp = mkdtempSync(
|
|
904
|
-
const listPath =
|
|
905
|
-
|
|
906
|
-
const out =
|
|
1024
|
+
const tmp = mkdtempSync(join5(tmpdir(), "vibeview-tar-probe-"));
|
|
1025
|
+
const listPath = join5(tmp, "empty.list");
|
|
1026
|
+
writeFileSync4(listPath, "");
|
|
1027
|
+
const out = join5(tmp, "probe.tgz");
|
|
907
1028
|
execFile("tar", ["-czf", out, "-C", tmp, "--null", "-T", listPath], (err) => {
|
|
908
|
-
|
|
1029
|
+
rmSync4(tmp, { recursive: true, force: true });
|
|
909
1030
|
resolve(!err);
|
|
910
1031
|
});
|
|
911
1032
|
});
|
|
@@ -1777,7 +1898,7 @@ your build command must produce a zip \u2014 use the package/zip step, not deplo
|
|
|
1777
1898
|
|
|
1778
1899
|
// src/commands/dev.ts
|
|
1779
1900
|
import { spawn as spawn4 } from "child_process";
|
|
1780
|
-
import { createWriteStream, mkdirSync as
|
|
1901
|
+
import { createWriteStream, mkdirSync as mkdirSync4 } from "fs";
|
|
1781
1902
|
import { dirname as dirname4 } from "path";
|
|
1782
1903
|
import { createInterface as createInterface3 } from "readline";
|
|
1783
1904
|
import { select as select3 } from "@inquirer/prompts";
|
|
@@ -1973,15 +2094,15 @@ import { spawn as spawn3 } from "child_process";
|
|
|
1973
2094
|
import { createInterface as createInterface2 } from "readline";
|
|
1974
2095
|
|
|
1975
2096
|
// src/lib/metro-state.ts
|
|
1976
|
-
import { existsSync as existsSync3, mkdirSync as
|
|
1977
|
-
import { dirname as dirname2, join as
|
|
2097
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync6, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
2098
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
1978
2099
|
var STATE_DIR2 = ".vibeview";
|
|
1979
2100
|
var STATE_FILE2 = "metro-state.json";
|
|
1980
2101
|
function statePath2(cwd) {
|
|
1981
|
-
return
|
|
2102
|
+
return join6(cwd, STATE_DIR2, STATE_FILE2);
|
|
1982
2103
|
}
|
|
1983
2104
|
function metroLogPath(cwd) {
|
|
1984
|
-
return
|
|
2105
|
+
return join6(cwd, STATE_DIR2, "metro.log");
|
|
1985
2106
|
}
|
|
1986
2107
|
function isPidAlive2(pid) {
|
|
1987
2108
|
try {
|
|
@@ -1993,13 +2114,13 @@ function isPidAlive2(pid) {
|
|
|
1993
2114
|
}
|
|
1994
2115
|
function writeMetroState(cwd, state) {
|
|
1995
2116
|
const path = statePath2(cwd);
|
|
1996
|
-
|
|
1997
|
-
|
|
2117
|
+
mkdirSync3(dirname2(path), { recursive: true });
|
|
2118
|
+
writeFileSync5(path, JSON.stringify(state, null, 2) + "\n");
|
|
1998
2119
|
}
|
|
1999
2120
|
function readMetroState(cwd) {
|
|
2000
2121
|
let parsed;
|
|
2001
2122
|
try {
|
|
2002
|
-
parsed = JSON.parse(
|
|
2123
|
+
parsed = JSON.parse(readFileSync6(statePath2(cwd), "utf-8"));
|
|
2003
2124
|
} catch {
|
|
2004
2125
|
return null;
|
|
2005
2126
|
}
|
|
@@ -2011,7 +2132,7 @@ function readMetroState(cwd) {
|
|
|
2011
2132
|
}
|
|
2012
2133
|
function clearMetroState(cwd) {
|
|
2013
2134
|
const path = statePath2(cwd);
|
|
2014
|
-
if (existsSync3(path))
|
|
2135
|
+
if (existsSync3(path)) rmSync5(path, { force: true });
|
|
2015
2136
|
}
|
|
2016
2137
|
function killOwnedMetro(cwd) {
|
|
2017
2138
|
const state = readMetroState(cwd);
|
|
@@ -2524,17 +2645,19 @@ function requireNonInteractive(json, message) {
|
|
|
2524
2645
|
}
|
|
2525
2646
|
async function ensureConfig(api, opts) {
|
|
2526
2647
|
const existing = loadDevConfig(process.cwd());
|
|
2648
|
+
const local = loadLocalDevConfig(process.cwd());
|
|
2649
|
+
const effective = effectiveDevConfig(existing, local);
|
|
2527
2650
|
if (opts.platform && !isDevPlatform(opts.platform)) {
|
|
2528
2651
|
console.error(chalk4.red(`error: --platform must be one of ${DEV_PLATFORMS.join(", ")}`));
|
|
2529
2652
|
process.exit(2);
|
|
2530
2653
|
}
|
|
2531
|
-
if (!opts.platform && !
|
|
2654
|
+
if (!opts.platform && !effective?.defaultPlatform) {
|
|
2532
2655
|
requireNonInteractive(
|
|
2533
2656
|
opts.json,
|
|
2534
2657
|
`--platform is required (no vibeview.json default yet) \u2014 pass --platform <${DEV_PLATFORMS.join("|")}>, or run once interactively to set a default.`
|
|
2535
2658
|
);
|
|
2536
2659
|
}
|
|
2537
|
-
const platform = opts.platform ||
|
|
2660
|
+
const platform = opts.platform || effective?.defaultPlatform || await select3({
|
|
2538
2661
|
message: "Platform to run on",
|
|
2539
2662
|
choices: platformChoices(process.cwd())
|
|
2540
2663
|
});
|
|
@@ -2571,7 +2694,7 @@ async function ensureConfig(api, opts) {
|
|
|
2571
2694
|
}
|
|
2572
2695
|
}
|
|
2573
2696
|
}
|
|
2574
|
-
const entry =
|
|
2697
|
+
const entry = effective?.platforms?.[platform];
|
|
2575
2698
|
let allApps;
|
|
2576
2699
|
try {
|
|
2577
2700
|
allApps = await api.listApps();
|
|
@@ -2591,6 +2714,7 @@ ${hint}`);
|
|
|
2591
2714
|
process.exit(2);
|
|
2592
2715
|
}
|
|
2593
2716
|
let appId = choice.appId;
|
|
2717
|
+
const appFromFlag = Boolean(opts.app) && appId === opts.app;
|
|
2594
2718
|
let builtThisRun = false;
|
|
2595
2719
|
if (choice.staleCached) {
|
|
2596
2720
|
console.error(
|
|
@@ -2663,22 +2787,51 @@ ${hint}`);
|
|
|
2663
2787
|
});
|
|
2664
2788
|
}
|
|
2665
2789
|
const deviceCategory = isTv ? "tv" : opts.deviceCategory || entry?.deviceCategory || "phone";
|
|
2666
|
-
const
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
);
|
|
2790
|
+
const metroPortFlag = opts.metroPort ? parseInt(opts.metroPort, 10) : void 0;
|
|
2791
|
+
const metroResolved = resolveMetro(effective, platform, metroPortFlag);
|
|
2792
|
+
const committedEntry = existing?.platforms?.[platform];
|
|
2793
|
+
const nextLocal = JSON.parse(JSON.stringify(local));
|
|
2794
|
+
const localEntry = (nextLocal.platforms ??= {})[platform] ??= {};
|
|
2795
|
+
let committedAppId = committedEntry?.appId;
|
|
2796
|
+
if (!committedAppId) {
|
|
2797
|
+
committedAppId = appId;
|
|
2798
|
+
} else if (appId !== committedAppId) {
|
|
2799
|
+
if (appFromFlag || local.platforms?.[platform]?.appId !== void 0) {
|
|
2800
|
+
localEntry.appId = appId;
|
|
2801
|
+
} else {
|
|
2802
|
+
committedAppId = appId;
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
if (appId === committedAppId) delete localEntry.appId;
|
|
2806
|
+
let committedCategory = committedEntry?.deviceCategory;
|
|
2807
|
+
if (!isTv) {
|
|
2808
|
+
if (!committedCategory) committedCategory = deviceCategory;
|
|
2809
|
+
if (deviceCategory !== committedCategory) localEntry.deviceCategory = deviceCategory;
|
|
2810
|
+
else delete localEntry.deviceCategory;
|
|
2811
|
+
}
|
|
2812
|
+
const committedDefault = existing?.defaultPlatform ?? platform;
|
|
2813
|
+
if (opts.platform) {
|
|
2814
|
+
if (platform !== committedDefault) nextLocal.defaultPlatform = platform;
|
|
2815
|
+
else delete nextLocal.defaultPlatform;
|
|
2816
|
+
}
|
|
2671
2817
|
const cfg = buildSavedConfig({
|
|
2672
2818
|
existing,
|
|
2673
2819
|
platform,
|
|
2674
|
-
appId,
|
|
2675
|
-
deviceCategory,
|
|
2820
|
+
appId: committedAppId,
|
|
2821
|
+
deviceCategory: committedCategory ?? deviceCategory,
|
|
2676
2822
|
isTv,
|
|
2677
|
-
|
|
2823
|
+
// Never the flag: vibeview.json keeps (or, on the first run, seeds) the
|
|
2824
|
+
// shared default; the flag's port is saved locally below.
|
|
2825
|
+
metroPortFlag: void 0,
|
|
2678
2826
|
isFirstRun,
|
|
2679
2827
|
firstRunMetro,
|
|
2680
2828
|
firstRunBuild
|
|
2681
2829
|
});
|
|
2830
|
+
cfg.defaultPlatform = committedDefault;
|
|
2831
|
+
if (metroPortFlag !== void 0 && PLATFORM_NEEDS_METRO[platform]) {
|
|
2832
|
+
if (metroPortFlag !== resolveMetro(cfg, platform).port) localEntry.metroPort = metroPortFlag;
|
|
2833
|
+
else delete localEntry.metroPort;
|
|
2834
|
+
}
|
|
2682
2835
|
const commitConfig = () => {
|
|
2683
2836
|
updateDevConfig(process.cwd(), platform, (fresh) => {
|
|
2684
2837
|
applyConfigChanges(fresh, existing, cfg);
|
|
@@ -2689,6 +2842,15 @@ ${hint}`);
|
|
|
2689
2842
|
console.error(chalk4.dim(`Updated vibeview.json: ${changes.join(", ")}`));
|
|
2690
2843
|
}
|
|
2691
2844
|
}
|
|
2845
|
+
const localChanges = describeLocalChanges(local, nextLocal);
|
|
2846
|
+
if (localChanges.length) {
|
|
2847
|
+
saveLocalDevConfig(process.cwd(), nextLocal);
|
|
2848
|
+
console.error(
|
|
2849
|
+
chalk4.dim(
|
|
2850
|
+
`Saved to .vibeview/local.json (yours only, not committed): ${localChanges.join(", ")}`
|
|
2851
|
+
)
|
|
2852
|
+
);
|
|
2853
|
+
}
|
|
2692
2854
|
if (isFirstRun) {
|
|
2693
2855
|
console.error(chalk4.dim(`Created vibeview.json (platform: ${platform}, app: ${appId}).`));
|
|
2694
2856
|
const hint = fillLaterHint(
|
|
@@ -2698,7 +2860,7 @@ ${hint}`);
|
|
|
2698
2860
|
if (hint) console.error(chalk4.dim(hint));
|
|
2699
2861
|
}
|
|
2700
2862
|
};
|
|
2701
|
-
const finalMetro = isFirstRun ? resolveMetro(cfg,
|
|
2863
|
+
const finalMetro = isFirstRun ? resolveMetro(effectiveDevConfig(cfg, nextLocal), platform, metroPortFlag) : metroResolved;
|
|
2702
2864
|
return {
|
|
2703
2865
|
platform,
|
|
2704
2866
|
appId,
|
|
@@ -2793,12 +2955,21 @@ async function runDevInProcess(opts) {
|
|
|
2793
2955
|
(l) => console.error(chalk4.dim(l))
|
|
2794
2956
|
);
|
|
2795
2957
|
cfg.appId = result.public_id;
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2958
|
+
const localNow = loadLocalDevConfig(process.cwd());
|
|
2959
|
+
const localApp = localNow.platforms?.[cfg.platform];
|
|
2960
|
+
if (localApp?.appId !== void 0) {
|
|
2961
|
+
const committedApp = loadDevConfig(process.cwd())?.platforms?.[cfg.platform]?.appId;
|
|
2962
|
+
if (result.public_id === committedApp) delete localApp.appId;
|
|
2963
|
+
else localApp.appId = result.public_id;
|
|
2964
|
+
saveLocalDevConfig(process.cwd(), localNow);
|
|
2965
|
+
} else {
|
|
2966
|
+
updateDevConfig(process.cwd(), cfg.platform, (fresh) => {
|
|
2967
|
+
const entry = fresh.platforms[cfg.platform];
|
|
2968
|
+
if (!entry || entry.appId === result.public_id) return false;
|
|
2969
|
+
entry.appId = result.public_id;
|
|
2970
|
+
return true;
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2802
2973
|
} catch (err) {
|
|
2803
2974
|
const message = err.message;
|
|
2804
2975
|
console.error(chalk4.red(`error: ${message}`));
|
|
@@ -2824,7 +2995,7 @@ async function runDevInProcess(opts) {
|
|
|
2824
2995
|
const captureMetroLine = (line) => {
|
|
2825
2996
|
try {
|
|
2826
2997
|
if (!metroLogStream) {
|
|
2827
|
-
|
|
2998
|
+
mkdirSync4(dirname4(metroLogPath(process.cwd())), { recursive: true });
|
|
2828
2999
|
metroLogStream = createWriteStream(metroLogPath(process.cwd()), { flags: "w" });
|
|
2829
3000
|
}
|
|
2830
3001
|
metroLogStream.write(line.replace(/^\[metro\] /, "") + "\n");
|
|
@@ -3807,7 +3978,7 @@ function agentExitCode(env) {
|
|
|
3807
3978
|
}
|
|
3808
3979
|
|
|
3809
3980
|
// src/commands/agent.ts
|
|
3810
|
-
import { writeFileSync as
|
|
3981
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
3811
3982
|
var REF_RE = /^@?(e\d+)$/;
|
|
3812
3983
|
var COORDS_RE = /^(-?\d+),\s*(-?\d+)$/;
|
|
3813
3984
|
var FlagError = class extends Error {
|
|
@@ -4017,7 +4188,7 @@ async function runAgentVerb(cmd, positionals, opts) {
|
|
|
4017
4188
|
`);
|
|
4018
4189
|
process.exit(1);
|
|
4019
4190
|
}
|
|
4020
|
-
|
|
4191
|
+
writeFileSync6(outPath, imageBytes);
|
|
4021
4192
|
if (opts.json) {
|
|
4022
4193
|
console.log(JSON.stringify(envelope));
|
|
4023
4194
|
} else {
|
|
@@ -4040,8 +4211,10 @@ export {
|
|
|
4040
4211
|
DEV_PLATFORMS,
|
|
4041
4212
|
PLATFORM_NEEDS_METRO,
|
|
4042
4213
|
isDevPlatform,
|
|
4214
|
+
platformLabel,
|
|
4043
4215
|
platformWithArticle,
|
|
4044
4216
|
loadDevConfig,
|
|
4217
|
+
resolveMetro,
|
|
4045
4218
|
parseBuildId,
|
|
4046
4219
|
checkDevBuild,
|
|
4047
4220
|
buildRows,
|
|
@@ -4052,6 +4225,8 @@ export {
|
|
|
4052
4225
|
readMetroState,
|
|
4053
4226
|
killOwnedMetro,
|
|
4054
4227
|
checkMetro,
|
|
4228
|
+
loadLocalDevConfig,
|
|
4229
|
+
effectiveDevConfig,
|
|
4055
4230
|
buildCommand,
|
|
4056
4231
|
formatDuration,
|
|
4057
4232
|
BUILD_TERMINAL_STATUSES,
|