vibeview 0.28.1 → 0.30.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-3DKY443B.js → chunk-NOZGZT3G.js} +355 -117
- package/dist/{chunk-PBMB6TG4.js → chunk-ZZK7YQSP.js} +33 -6
- package/dist/cli.js +123 -17
- package/dist/{server-BGQJOVSN.js → server-2JNYWF5L.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 +9 -5
|
@@ -13,10 +13,10 @@ 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
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
19
|
+
import { readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
20
20
|
import { join } from "path";
|
|
21
21
|
var DEV_PLATFORMS = ["ios", "android", "tvos", "androidtv", "roku"];
|
|
22
22
|
var ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -166,7 +166,58 @@ function validateBuildEnv(env, platformKey) {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
function saveDevConfig(cwd, cfg) {
|
|
169
|
-
|
|
169
|
+
const target = join(cwd, FILE);
|
|
170
|
+
const tmp = join(cwd, `.${FILE}.${process.pid}.${Date.now()}.tmp`);
|
|
171
|
+
try {
|
|
172
|
+
writeFileSync(tmp, JSON.stringify(cfg, null, 2) + "\n");
|
|
173
|
+
renameSync(tmp, target);
|
|
174
|
+
} catch (err) {
|
|
175
|
+
rmSync(tmp, { force: true });
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function updateDevConfig(cwd, platform, apply) {
|
|
180
|
+
const fresh = loadDevConfig(cwd) ?? { defaultPlatform: platform, platforms: {} };
|
|
181
|
+
if (apply(fresh) === false) return fresh;
|
|
182
|
+
saveDevConfig(cwd, fresh);
|
|
183
|
+
return fresh;
|
|
184
|
+
}
|
|
185
|
+
function configLeaves(value, path = [], out = []) {
|
|
186
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
187
|
+
for (const [k, v] of Object.entries(value)) {
|
|
188
|
+
configLeaves(v, [...path, k], out);
|
|
189
|
+
}
|
|
190
|
+
} else if (value !== void 0) {
|
|
191
|
+
out.push({ path, value });
|
|
192
|
+
}
|
|
193
|
+
return out;
|
|
194
|
+
}
|
|
195
|
+
function applyConfigChanges(target, before, after) {
|
|
196
|
+
const key = (path) => JSON.stringify(path);
|
|
197
|
+
const prev = new Map(configLeaves(before ?? {}).map((l) => [key(l.path), l.value]));
|
|
198
|
+
const root = target;
|
|
199
|
+
for (const { path, value } of configLeaves(after)) {
|
|
200
|
+
const old = prev.get(key(path));
|
|
201
|
+
prev.delete(key(path));
|
|
202
|
+
if (old !== void 0 && JSON.stringify(old) === JSON.stringify(value)) continue;
|
|
203
|
+
let node = root;
|
|
204
|
+
for (const segment of path.slice(0, -1)) {
|
|
205
|
+
const child = node[segment];
|
|
206
|
+
if (!child || typeof child !== "object" || Array.isArray(child)) node[segment] = {};
|
|
207
|
+
node = node[segment];
|
|
208
|
+
}
|
|
209
|
+
node[path[path.length - 1]] = value;
|
|
210
|
+
}
|
|
211
|
+
for (const k of prev.keys()) {
|
|
212
|
+
const path = JSON.parse(k);
|
|
213
|
+
let node = root;
|
|
214
|
+
for (const segment of path.slice(0, -1)) {
|
|
215
|
+
node = node && typeof node === "object" ? node[segment] : void 0;
|
|
216
|
+
}
|
|
217
|
+
if (node && typeof node === "object") {
|
|
218
|
+
delete node[path[path.length - 1]];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
170
221
|
}
|
|
171
222
|
function describeConfigChanges(before, after) {
|
|
172
223
|
const flat = (value, prefix, out) => {
|
|
@@ -220,6 +271,10 @@ function buildSavedConfig(input2) {
|
|
|
220
271
|
platforms: {
|
|
221
272
|
...existing?.platforms,
|
|
222
273
|
[platform]: {
|
|
274
|
+
// Everything else on the entry (`submit` defaults, keys a newer CLI
|
|
275
|
+
// wrote) is carried forward untouched; the fields below are the ones
|
|
276
|
+
// `vibeview dev` owns.
|
|
277
|
+
...existing?.platforms?.[platform],
|
|
223
278
|
appId,
|
|
224
279
|
deviceCategory: isTv ? void 0 : deviceCategory,
|
|
225
280
|
...existing?.platforms?.[platform]?.build ? { build: { ...existing.platforms[platform].build } } : {},
|
|
@@ -235,6 +290,11 @@ function buildSavedConfig(input2) {
|
|
|
235
290
|
}
|
|
236
291
|
|
|
237
292
|
// src/lib/dev-build.ts
|
|
293
|
+
function canUseMetro(build) {
|
|
294
|
+
if (build.can_use_metro === true || build.can_use_metro === false) return build.can_use_metro;
|
|
295
|
+
if (build.is_debug === true) return true;
|
|
296
|
+
return build.is_debug === false ? false : null;
|
|
297
|
+
}
|
|
238
298
|
function parseBuildId(raw) {
|
|
239
299
|
const text = typeof raw === "number" ? String(raw) : typeof raw === "string" ? raw.trim() : "";
|
|
240
300
|
if (!/^\d+$/.test(text) || Number(text) < 1) {
|
|
@@ -244,9 +304,10 @@ function parseBuildId(raw) {
|
|
|
244
304
|
}
|
|
245
305
|
return Number(text);
|
|
246
306
|
}
|
|
247
|
-
function buildKind(
|
|
248
|
-
|
|
249
|
-
if (
|
|
307
|
+
function buildKind(build) {
|
|
308
|
+
const usable = build !== null && typeof build === "object" ? canUseMetro(build) : canUseMetro({ is_debug: build });
|
|
309
|
+
if (usable === true) return "debug";
|
|
310
|
+
if (usable === false) return "release";
|
|
250
311
|
return "unknown";
|
|
251
312
|
}
|
|
252
313
|
function checkDevBuild(app, buildId, platform, opts = {}) {
|
|
@@ -266,10 +327,11 @@ function checkDevBuild(app, buildId, platform, opts = {}) {
|
|
|
266
327
|
`build ${buildId} is not a build of app ${app.public_id} \u2014 see \`vibeview list-builds ${app.public_id}\``
|
|
267
328
|
);
|
|
268
329
|
}
|
|
269
|
-
|
|
270
|
-
|
|
330
|
+
const usable = canUseMetro(build);
|
|
331
|
+
if (!opts.standalone && usable !== true) {
|
|
332
|
+
const why = usable === false ? "it is a release build (or not a React Native build), so it cannot load JavaScript from Metro" : "it could not be identified as a React Native debug build";
|
|
271
333
|
throw new Error(
|
|
272
|
-
`build ${buildId} is not a debug build \u2014 ${why}. \`vibeview dev\` needs ${platformWithArticle(platform)} debug simulator/emulator build
|
|
334
|
+
`build ${buildId} is not a debug build \u2014 ${why}. \`vibeview dev\` needs ${platformWithArticle(platform)} React Native debug simulator/emulator build (see \`vibeview list-builds ${app.public_id}\`); to run this build as it is, with no Metro, use \`vibeview dev --standalone --build ${buildId}\``
|
|
273
335
|
);
|
|
274
336
|
}
|
|
275
337
|
return build;
|
|
@@ -283,8 +345,9 @@ function buildRows(app) {
|
|
|
283
345
|
version: b.version ?? null,
|
|
284
346
|
build_number: b.build_number ?? null,
|
|
285
347
|
platform: app.platform,
|
|
286
|
-
kind: buildKind(b
|
|
348
|
+
kind: buildKind(b),
|
|
287
349
|
is_debug: b.is_debug ?? null,
|
|
350
|
+
can_use_metro: canUseMetro(b),
|
|
288
351
|
uploaded_at: b.uploaded_at ?? null,
|
|
289
352
|
note: b.note ?? null
|
|
290
353
|
}));
|
|
@@ -329,7 +392,7 @@ async function listBuilds(appId, opts, deps) {
|
|
|
329
392
|
}
|
|
330
393
|
|
|
331
394
|
// src/lib/dev-state.ts
|
|
332
|
-
import { existsSync, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
395
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
333
396
|
import { dirname, join as join2 } from "path";
|
|
334
397
|
var STATE_DIR = ".vibeview";
|
|
335
398
|
var STATE_FILE = "dev-state.json";
|
|
@@ -399,7 +462,7 @@ function readDevState(cwd) {
|
|
|
399
462
|
function clearDevState(cwd) {
|
|
400
463
|
const path = statePath(cwd);
|
|
401
464
|
if (existsSync(path)) {
|
|
402
|
-
|
|
465
|
+
rmSync2(path, { force: true });
|
|
403
466
|
}
|
|
404
467
|
}
|
|
405
468
|
|
|
@@ -416,6 +479,108 @@ async function checkMetro(port, timeoutMs = 1500) {
|
|
|
416
479
|
}
|
|
417
480
|
}
|
|
418
481
|
|
|
482
|
+
// src/lib/local-config.ts
|
|
483
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
484
|
+
import { join as join3 } from "path";
|
|
485
|
+
var DIR = ".vibeview";
|
|
486
|
+
var FILE2 = "local.json";
|
|
487
|
+
function localConfigPath(cwd) {
|
|
488
|
+
return join3(cwd, DIR, FILE2);
|
|
489
|
+
}
|
|
490
|
+
function loadLocalDevConfig(cwd) {
|
|
491
|
+
let parsed;
|
|
492
|
+
try {
|
|
493
|
+
parsed = JSON.parse(readFileSync3(localConfigPath(cwd), "utf-8"));
|
|
494
|
+
} catch {
|
|
495
|
+
return {};
|
|
496
|
+
}
|
|
497
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
498
|
+
const raw = parsed;
|
|
499
|
+
const out = {};
|
|
500
|
+
if (isDevPlatform(raw.defaultPlatform)) out.defaultPlatform = raw.defaultPlatform;
|
|
501
|
+
if (raw.platforms && typeof raw.platforms === "object") {
|
|
502
|
+
for (const [key, value] of Object.entries(raw.platforms)) {
|
|
503
|
+
if (!isDevPlatform(key) || !value || typeof value !== "object") continue;
|
|
504
|
+
const v = value;
|
|
505
|
+
const entry = {};
|
|
506
|
+
if (typeof v.appId === "string" && v.appId) entry.appId = v.appId;
|
|
507
|
+
if (typeof v.deviceCategory === "string" && v.deviceCategory) {
|
|
508
|
+
entry.deviceCategory = v.deviceCategory;
|
|
509
|
+
}
|
|
510
|
+
if (typeof v.metroPort === "number" && Number.isInteger(v.metroPort) && v.metroPort > 0) {
|
|
511
|
+
entry.metroPort = v.metroPort;
|
|
512
|
+
}
|
|
513
|
+
if (Object.keys(entry).length) (out.platforms ??= {})[key] = entry;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return out;
|
|
517
|
+
}
|
|
518
|
+
function prune(cfg) {
|
|
519
|
+
const out = {};
|
|
520
|
+
if (cfg.defaultPlatform) out.defaultPlatform = cfg.defaultPlatform;
|
|
521
|
+
for (const [key, entry] of Object.entries(cfg.platforms ?? {})) {
|
|
522
|
+
const kept = Object.fromEntries(
|
|
523
|
+
Object.entries(entry ?? {}).filter(([, v]) => v !== void 0)
|
|
524
|
+
);
|
|
525
|
+
if (Object.keys(kept).length) (out.platforms ??= {})[key] = kept;
|
|
526
|
+
}
|
|
527
|
+
return out;
|
|
528
|
+
}
|
|
529
|
+
function saveLocalDevConfig(cwd, cfg) {
|
|
530
|
+
const pruned = prune(cfg);
|
|
531
|
+
const target = localConfigPath(cwd);
|
|
532
|
+
if (!pruned.defaultPlatform && !pruned.platforms) {
|
|
533
|
+
rmSync3(target, { force: true });
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
mkdirSync2(join3(cwd, DIR), { recursive: true });
|
|
537
|
+
const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
538
|
+
try {
|
|
539
|
+
writeFileSync3(tmp, JSON.stringify(pruned, null, 2) + "\n");
|
|
540
|
+
renameSync2(tmp, target);
|
|
541
|
+
} catch (err) {
|
|
542
|
+
rmSync3(tmp, { force: true });
|
|
543
|
+
throw err;
|
|
544
|
+
}
|
|
545
|
+
ensureGitignored(cwd);
|
|
546
|
+
}
|
|
547
|
+
function effectiveDevConfig(committed, local) {
|
|
548
|
+
if (!committed && !local.defaultPlatform && !local.platforms) return null;
|
|
549
|
+
const base = committed ? { ...committed, platforms: { ...committed.platforms } } : { defaultPlatform: local.defaultPlatform ?? "ios", platforms: {} };
|
|
550
|
+
if (local.defaultPlatform) base.defaultPlatform = local.defaultPlatform;
|
|
551
|
+
for (const [key, entry] of Object.entries(local.platforms ?? {})) {
|
|
552
|
+
const platform = key;
|
|
553
|
+
const merged = { ...base.platforms[platform] };
|
|
554
|
+
if (entry?.appId) merged.appId = entry.appId;
|
|
555
|
+
if (entry?.deviceCategory) merged.deviceCategory = entry.deviceCategory;
|
|
556
|
+
if (entry?.metroPort !== void 0) merged.metroPort = entry.metroPort;
|
|
557
|
+
base.platforms[platform] = merged;
|
|
558
|
+
}
|
|
559
|
+
return base;
|
|
560
|
+
}
|
|
561
|
+
function describeLocalChanges(before, after) {
|
|
562
|
+
const changes = [];
|
|
563
|
+
const a = prune(before);
|
|
564
|
+
const b = prune(after);
|
|
565
|
+
if (a.defaultPlatform !== b.defaultPlatform) {
|
|
566
|
+
changes.push(
|
|
567
|
+
b.defaultPlatform ? `defaultPlatform ${b.defaultPlatform}` : `defaultPlatform (removed, was ${a.defaultPlatform})`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a.platforms ?? {}), ...Object.keys(b.platforms ?? {})]);
|
|
571
|
+
for (const key of keys) {
|
|
572
|
+
const pa = a.platforms?.[key] ?? {};
|
|
573
|
+
const pb = b.platforms?.[key] ?? {};
|
|
574
|
+
for (const field of ["appId", "deviceCategory", "metroPort"]) {
|
|
575
|
+
if (pa[field] === pb[field]) continue;
|
|
576
|
+
changes.push(
|
|
577
|
+
pb[field] === void 0 ? `${key}.${field} (removed, was ${String(pa[field])})` : `${key}.${field} ${String(pb[field])}`
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return changes;
|
|
582
|
+
}
|
|
583
|
+
|
|
419
584
|
// src/commands/build.ts
|
|
420
585
|
import { spawn as spawn2 } from "child_process";
|
|
421
586
|
import { createInterface } from "readline";
|
|
@@ -427,12 +592,12 @@ import chalk2 from "chalk";
|
|
|
427
592
|
import { input, select } from "@inquirer/prompts";
|
|
428
593
|
|
|
429
594
|
// src/lib/project-detect.ts
|
|
430
|
-
import { existsSync as existsSync2, readFileSync as
|
|
431
|
-
import { join as
|
|
595
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4, readdirSync, statSync } from "fs";
|
|
596
|
+
import { join as join4 } from "path";
|
|
432
597
|
import ignore from "ignore";
|
|
433
598
|
function readJson(cwd, name) {
|
|
434
599
|
try {
|
|
435
|
-
return JSON.parse(
|
|
600
|
+
return JSON.parse(readFileSync4(join4(cwd, name), "utf-8"));
|
|
436
601
|
} catch {
|
|
437
602
|
return null;
|
|
438
603
|
}
|
|
@@ -447,19 +612,19 @@ function isFile(path) {
|
|
|
447
612
|
function detectFlavor(cwd) {
|
|
448
613
|
let deps = {};
|
|
449
614
|
try {
|
|
450
|
-
const pkg = JSON.parse(
|
|
615
|
+
const pkg = JSON.parse(readFileSync4(join4(cwd, "package.json"), "utf-8"));
|
|
451
616
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
452
617
|
} catch {
|
|
453
618
|
}
|
|
454
619
|
if ("expo" in deps) return "expo";
|
|
455
|
-
if (existsSync2(
|
|
456
|
-
if (isFile(
|
|
620
|
+
if (existsSync2(join4(cwd, "android", "gradlew")) || iosWorkspaceName(cwd)) return "bare-rn";
|
|
621
|
+
if (isFile(join4(cwd, "manifest"))) return "roku";
|
|
457
622
|
return "unknown";
|
|
458
623
|
}
|
|
459
624
|
function nativeDirShipped(cwd, dir) {
|
|
460
|
-
if (!existsSync2(
|
|
625
|
+
if (!existsSync2(join4(cwd, dir))) return false;
|
|
461
626
|
try {
|
|
462
|
-
const ig = ignore().add(
|
|
627
|
+
const ig = ignore().add(readFileSync4(join4(cwd, ".gitignore"), "utf-8"));
|
|
463
628
|
return !ig.ignores(`${dir}/`);
|
|
464
629
|
} catch {
|
|
465
630
|
return true;
|
|
@@ -475,7 +640,7 @@ function isManagedExpo(cwd, platform) {
|
|
|
475
640
|
}
|
|
476
641
|
function iosWorkspaceName(cwd) {
|
|
477
642
|
try {
|
|
478
|
-
const found = readdirSync(
|
|
643
|
+
const found = readdirSync(join4(cwd, "ios")).find((f) => f.endsWith(".xcworkspace"));
|
|
479
644
|
return found ? found.replace(/\.xcworkspace$/, "") : null;
|
|
480
645
|
} catch {
|
|
481
646
|
return null;
|
|
@@ -484,9 +649,9 @@ function iosWorkspaceName(cwd) {
|
|
|
484
649
|
var IOS_ARTIFACT = "ios/build/Build/Products/Debug-iphonesimulator/*.app";
|
|
485
650
|
var ANDROID_ARTIFACT = "android/app/build/outputs/apk/debug/*.apk";
|
|
486
651
|
function installCommand(cwd) {
|
|
487
|
-
if (existsSync2(
|
|
488
|
-
if (existsSync2(
|
|
489
|
-
if (existsSync2(
|
|
652
|
+
if (existsSync2(join4(cwd, "yarn.lock"))) return "yarn install --immutable";
|
|
653
|
+
if (existsSync2(join4(cwd, "pnpm-lock.yaml"))) return "pnpm install --frozen-lockfile";
|
|
654
|
+
if (existsSync2(join4(cwd, "package-lock.json"))) return "npm ci";
|
|
490
655
|
return "yarn install --immutable";
|
|
491
656
|
}
|
|
492
657
|
function managedExpoAndroidCommand(cwd) {
|
|
@@ -500,7 +665,7 @@ var ROKU_ZIP_COMMAND = "zip -r out/channel.zip manifest source components images
|
|
|
500
665
|
function rokuDefaults(cwd) {
|
|
501
666
|
const pkg = readJson(cwd, "package.json");
|
|
502
667
|
const scripts = pkg?.scripts ?? {};
|
|
503
|
-
const runner = existsSync2(
|
|
668
|
+
const runner = existsSync2(join4(cwd, "yarn.lock")) ? "yarn" : "npm run";
|
|
504
669
|
const packageScript = Object.entries(scripts).find(
|
|
505
670
|
([name, body]) => !name.startsWith("deploy") && (name.startsWith("package") || body.includes("--package-only") || body.includes("zipPackage"))
|
|
506
671
|
);
|
|
@@ -558,7 +723,7 @@ function expoSanitizedName(name) {
|
|
|
558
723
|
return name.replace(/[\W_]+/g, "").normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
559
724
|
}
|
|
560
725
|
function managedExpoIosName(cwd) {
|
|
561
|
-
if (existsSync2(
|
|
726
|
+
if (existsSync2(join4(cwd, "app.config.js")) || existsSync2(join4(cwd, "app.config.ts"))) {
|
|
562
727
|
return null;
|
|
563
728
|
}
|
|
564
729
|
const raw = readJson(cwd, "app.config.json") ?? readJson(cwd, "app.json");
|
|
@@ -703,9 +868,9 @@ async function promptMetroCommand(cwd, platform) {
|
|
|
703
868
|
|
|
704
869
|
// src/lib/source-pack.ts
|
|
705
870
|
import { execFile, spawn } from "child_process";
|
|
706
|
-
import { lstatSync, mkdtempSync, readFileSync as
|
|
871
|
+
import { lstatSync, mkdtempSync, readFileSync as readFileSync5, rmSync as rmSync4, statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
707
872
|
import { tmpdir } from "os";
|
|
708
|
-
import { join as
|
|
873
|
+
import { join as join5 } from "path";
|
|
709
874
|
import ignore2 from "ignore";
|
|
710
875
|
var NotAGitRepoError = class extends Error {
|
|
711
876
|
constructor(message) {
|
|
@@ -731,15 +896,15 @@ async function listSourceFiles(projectRoot) {
|
|
|
731
896
|
const stdout = await runGitLsFiles(projectRoot);
|
|
732
897
|
const rawPaths = stdout.length ? stdout.split("\0").filter((p) => p.length > 0) : [];
|
|
733
898
|
let filtered = rawPaths;
|
|
734
|
-
const vvignorePath =
|
|
899
|
+
const vvignorePath = join5(projectRoot, ".vvignore");
|
|
735
900
|
if (statOrNull(vvignorePath)?.isFile()) {
|
|
736
|
-
const ig = ignore2().add(
|
|
901
|
+
const ig = ignore2().add(readFileSync5(vvignorePath, "utf-8"));
|
|
737
902
|
filtered = filtered.filter((p) => !ig.ignores(p));
|
|
738
903
|
}
|
|
739
904
|
const result = [];
|
|
740
905
|
for (const relPath of filtered) {
|
|
741
906
|
if (isForceExcluded(relPath)) continue;
|
|
742
|
-
const st = statOrNull(
|
|
907
|
+
const st = statOrNull(join5(projectRoot, relPath));
|
|
743
908
|
if (!st) continue;
|
|
744
909
|
result.push(relPath);
|
|
745
910
|
}
|
|
@@ -789,7 +954,7 @@ async function packSource(projectRoot, files, opts = {}) {
|
|
|
789
954
|
let totalBytes = 0;
|
|
790
955
|
const sized = [];
|
|
791
956
|
for (const relPath of files) {
|
|
792
|
-
const bytes = lstatSync(
|
|
957
|
+
const bytes = lstatSync(join5(projectRoot, relPath)).size;
|
|
793
958
|
totalBytes += bytes;
|
|
794
959
|
sized.push({ path: relPath, bytes });
|
|
795
960
|
}
|
|
@@ -800,14 +965,14 @@ async function packSource(projectRoot, files, opts = {}) {
|
|
|
800
965
|
largest
|
|
801
966
|
);
|
|
802
967
|
}
|
|
803
|
-
const tmpDir = mkdtempSync(
|
|
804
|
-
const cleanup = () =>
|
|
968
|
+
const tmpDir = mkdtempSync(join5(tmpdir(), "vibeview-source-pack-"));
|
|
969
|
+
const cleanup = () => rmSync4(tmpDir, { recursive: true, force: true });
|
|
805
970
|
try {
|
|
806
971
|
const supportsNull = await probeTarNullSupport();
|
|
807
|
-
const tarPath =
|
|
972
|
+
const tarPath = join5(tmpDir, "source.tgz");
|
|
808
973
|
if (supportsNull) {
|
|
809
|
-
const listPath =
|
|
810
|
-
|
|
974
|
+
const listPath = join5(tmpDir, "files.list");
|
|
975
|
+
writeFileSync4(listPath, files.map((f) => `${f}\0`).join(""));
|
|
811
976
|
await runTar(["-czf", tarPath, "-C", projectRoot, "--null", "-T", listPath]);
|
|
812
977
|
} else {
|
|
813
978
|
const badName = files.find((f) => f.includes("\n"));
|
|
@@ -816,8 +981,8 @@ async function packSource(projectRoot, files, opts = {}) {
|
|
|
816
981
|
`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`
|
|
817
982
|
);
|
|
818
983
|
}
|
|
819
|
-
const listPath =
|
|
820
|
-
|
|
984
|
+
const listPath = join5(tmpDir, "files.list");
|
|
985
|
+
writeFileSync4(listPath, files.map((f) => `${f}
|
|
821
986
|
`).join(""));
|
|
822
987
|
await runTar(["-czf", tarPath, "-C", projectRoot, "-T", listPath]);
|
|
823
988
|
}
|
|
@@ -837,12 +1002,12 @@ var nullSupportCache = null;
|
|
|
837
1002
|
function probeTarNullSupport() {
|
|
838
1003
|
if (!nullSupportCache) {
|
|
839
1004
|
nullSupportCache = new Promise((resolve) => {
|
|
840
|
-
const tmp = mkdtempSync(
|
|
841
|
-
const listPath =
|
|
842
|
-
|
|
843
|
-
const out =
|
|
1005
|
+
const tmp = mkdtempSync(join5(tmpdir(), "vibeview-tar-probe-"));
|
|
1006
|
+
const listPath = join5(tmp, "empty.list");
|
|
1007
|
+
writeFileSync4(listPath, "");
|
|
1008
|
+
const out = join5(tmp, "probe.tgz");
|
|
844
1009
|
execFile("tar", ["-czf", out, "-C", tmp, "--null", "-T", listPath], (err) => {
|
|
845
|
-
|
|
1010
|
+
rmSync4(tmp, { recursive: true, force: true });
|
|
846
1011
|
resolve(!err);
|
|
847
1012
|
});
|
|
848
1013
|
});
|
|
@@ -946,30 +1111,28 @@ async function resolveBarePlatform(cwd, cfg, opts) {
|
|
|
946
1111
|
}
|
|
947
1112
|
return platform;
|
|
948
1113
|
}
|
|
949
|
-
function persistPromptedBuild(cwd,
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
return next;
|
|
1114
|
+
function persistPromptedBuild(cwd, platform, build) {
|
|
1115
|
+
return updateDevConfig(cwd, platform, (next) => {
|
|
1116
|
+
const entry = next.platforms[platform];
|
|
1117
|
+
if (entry) {
|
|
1118
|
+
entry.build = build;
|
|
1119
|
+
} else {
|
|
1120
|
+
next.platforms[platform] = { build };
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
959
1123
|
}
|
|
960
|
-
function persistUploadedAppId(cwd,
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
if (entry.appId !== appId || !entry.build) {
|
|
1124
|
+
function persistUploadedAppId(cwd, platform, build, appId) {
|
|
1125
|
+
updateDevConfig(cwd, platform, (next) => {
|
|
1126
|
+
const entry = next.platforms[platform];
|
|
1127
|
+
if (!entry) {
|
|
1128
|
+
next.platforms[platform] = { appId, build };
|
|
1129
|
+
return true;
|
|
1130
|
+
}
|
|
1131
|
+
if (entry.appId === appId && entry.build) return false;
|
|
969
1132
|
entry.appId = appId;
|
|
970
|
-
entry.build = build;
|
|
971
|
-
|
|
972
|
-
}
|
|
1133
|
+
if (!entry.build) entry.build = build;
|
|
1134
|
+
return true;
|
|
1135
|
+
});
|
|
973
1136
|
}
|
|
974
1137
|
async function resolveBuildConfig(cwd, cfgRef, platform, opts) {
|
|
975
1138
|
const existing = cfgRef.current?.platforms?.[platform]?.build;
|
|
@@ -985,11 +1148,15 @@ async function resolveBuildConfig(cwd, cfgRef, platform, opts) {
|
|
|
985
1148
|
if (!answered) {
|
|
986
1149
|
fail(opts, "build config is required to run vibeview build", "config_required");
|
|
987
1150
|
}
|
|
988
|
-
cfgRef.current = persistPromptedBuild(cwd,
|
|
1151
|
+
cfgRef.current = persistPromptedBuild(cwd, platform, answered);
|
|
989
1152
|
return answered;
|
|
990
1153
|
}
|
|
991
|
-
function persistPromptedProductionBuild(cwd,
|
|
992
|
-
|
|
1154
|
+
function persistPromptedProductionBuild(cwd, platform, production) {
|
|
1155
|
+
return updateDevConfig(cwd, platform, (next) => {
|
|
1156
|
+
mergeProductionPair(next, platform, production);
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
function mergeProductionPair(next, platform, production) {
|
|
993
1160
|
const existingBuild = next.platforms[platform]?.build;
|
|
994
1161
|
if (!existingBuild?.command || !existingBuild?.artifact) {
|
|
995
1162
|
throw new Error(
|
|
@@ -997,8 +1164,6 @@ function persistPromptedProductionBuild(cwd, cfg, platform, production) {
|
|
|
997
1164
|
);
|
|
998
1165
|
}
|
|
999
1166
|
next.platforms[platform].build = { ...existingBuild, ...production };
|
|
1000
|
-
saveDevConfig(cwd, next);
|
|
1001
|
-
return next;
|
|
1002
1167
|
}
|
|
1003
1168
|
async function resolveProductionBuildConfig(cwd, cfgRef, platform, opts) {
|
|
1004
1169
|
const entry = cfgRef.current?.platforms?.[platform]?.build;
|
|
@@ -1016,7 +1181,7 @@ async function resolveProductionBuildConfig(cwd, cfgRef, platform, opts) {
|
|
|
1016
1181
|
if (!answered) {
|
|
1017
1182
|
fail(opts, "a production build config is required for --production", "config_required");
|
|
1018
1183
|
}
|
|
1019
|
-
cfgRef.current = persistPromptedProductionBuild(cwd,
|
|
1184
|
+
cfgRef.current = persistPromptedProductionBuild(cwd, platform, answered);
|
|
1020
1185
|
return { command: answered.productionCommand, artifact: answered.productionArtifact };
|
|
1021
1186
|
}
|
|
1022
1187
|
async function buildCommand(opts, cloudDeps) {
|
|
@@ -1036,7 +1201,6 @@ async function buildCommand(opts, cloudDeps) {
|
|
|
1036
1201
|
}
|
|
1037
1202
|
const platform = await resolveBarePlatform(cwd, cfgRef.current, opts);
|
|
1038
1203
|
const build = await resolveBuildConfig(cwd, cfgRef, platform, opts);
|
|
1039
|
-
const cfg = cfgRef.current;
|
|
1040
1204
|
const result = await runBuildForPlatform(
|
|
1041
1205
|
cwd,
|
|
1042
1206
|
platform,
|
|
@@ -1045,7 +1209,7 @@ async function buildCommand(opts, cloudDeps) {
|
|
|
1045
1209
|
(l) => console.error(chalk3.dim(l))
|
|
1046
1210
|
);
|
|
1047
1211
|
for (const w of result.warnings ?? []) console.error(chalk3.yellow(`\u26A0 ${w}`));
|
|
1048
|
-
persistUploadedAppId(cwd,
|
|
1212
|
+
persistUploadedAppId(cwd, platform, build, result.public_id);
|
|
1049
1213
|
if (opts.json) {
|
|
1050
1214
|
console.log(
|
|
1051
1215
|
JSON.stringify({ app_id: result.public_id, name: result.name, platform: result.platform })
|
|
@@ -1148,16 +1312,17 @@ function requireProductionCapable(opts, platform) {
|
|
|
1148
1312
|
fail(opts, productionUnsupportedReason(platform));
|
|
1149
1313
|
}
|
|
1150
1314
|
}
|
|
1151
|
-
function persistPickedAppId(cwd,
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
entry
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1315
|
+
function persistPickedAppId(cwd, platform, appId, opts = {}) {
|
|
1316
|
+
return updateDevConfig(cwd, platform, (next) => {
|
|
1317
|
+
const entry = next.platforms[platform];
|
|
1318
|
+
if (opts.onlyIfUnset && entry?.appId) return false;
|
|
1319
|
+
if (entry) {
|
|
1320
|
+
entry.appId = appId;
|
|
1321
|
+
} else {
|
|
1322
|
+
next.platforms[platform] = { appId };
|
|
1323
|
+
}
|
|
1324
|
+
return true;
|
|
1325
|
+
});
|
|
1161
1326
|
}
|
|
1162
1327
|
var APP_PICKER_SENTINEL = {
|
|
1163
1328
|
CREATE_NEW_APP: "__vibeview_create_new_app__",
|
|
@@ -1225,7 +1390,7 @@ async function resolveCloudAppIds(cwd, cfgRef, platforms, opts, listApps) {
|
|
|
1225
1390
|
createFromArtifact.add(platform);
|
|
1226
1391
|
continue;
|
|
1227
1392
|
}
|
|
1228
|
-
cfgRef.current = persistPickedAppId(cwd,
|
|
1393
|
+
cfgRef.current = persistPickedAppId(cwd, platform, appId);
|
|
1229
1394
|
}
|
|
1230
1395
|
return createFromArtifact;
|
|
1231
1396
|
}
|
|
@@ -1235,7 +1400,7 @@ function persistResolvedAppIds(cwd, cfgRef, createFromArtifact, finished) {
|
|
|
1235
1400
|
const platform = b.platform;
|
|
1236
1401
|
if (!createFromArtifact.has(platform)) continue;
|
|
1237
1402
|
if (cfgRef.current?.platforms?.[platform]?.appId) continue;
|
|
1238
|
-
cfgRef.current = persistPickedAppId(cwd,
|
|
1403
|
+
cfgRef.current = persistPickedAppId(cwd, platform, b.app_public_id, { onlyIfUnset: true });
|
|
1239
1404
|
}
|
|
1240
1405
|
}
|
|
1241
1406
|
function describeAllExclusions(configured) {
|
|
@@ -1714,7 +1879,7 @@ your build command must produce a zip \u2014 use the package/zip step, not deplo
|
|
|
1714
1879
|
|
|
1715
1880
|
// src/commands/dev.ts
|
|
1716
1881
|
import { spawn as spawn4 } from "child_process";
|
|
1717
|
-
import { createWriteStream, mkdirSync as
|
|
1882
|
+
import { createWriteStream, mkdirSync as mkdirSync4 } from "fs";
|
|
1718
1883
|
import { dirname as dirname4 } from "path";
|
|
1719
1884
|
import { createInterface as createInterface3 } from "readline";
|
|
1720
1885
|
import { select as select3 } from "@inquirer/prompts";
|
|
@@ -1910,15 +2075,15 @@ import { spawn as spawn3 } from "child_process";
|
|
|
1910
2075
|
import { createInterface as createInterface2 } from "readline";
|
|
1911
2076
|
|
|
1912
2077
|
// src/lib/metro-state.ts
|
|
1913
|
-
import { existsSync as existsSync3, mkdirSync as
|
|
1914
|
-
import { dirname as dirname2, join as
|
|
2078
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync6, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
2079
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
1915
2080
|
var STATE_DIR2 = ".vibeview";
|
|
1916
2081
|
var STATE_FILE2 = "metro-state.json";
|
|
1917
2082
|
function statePath2(cwd) {
|
|
1918
|
-
return
|
|
2083
|
+
return join6(cwd, STATE_DIR2, STATE_FILE2);
|
|
1919
2084
|
}
|
|
1920
2085
|
function metroLogPath(cwd) {
|
|
1921
|
-
return
|
|
2086
|
+
return join6(cwd, STATE_DIR2, "metro.log");
|
|
1922
2087
|
}
|
|
1923
2088
|
function isPidAlive2(pid) {
|
|
1924
2089
|
try {
|
|
@@ -1930,13 +2095,13 @@ function isPidAlive2(pid) {
|
|
|
1930
2095
|
}
|
|
1931
2096
|
function writeMetroState(cwd, state) {
|
|
1932
2097
|
const path = statePath2(cwd);
|
|
1933
|
-
|
|
1934
|
-
|
|
2098
|
+
mkdirSync3(dirname2(path), { recursive: true });
|
|
2099
|
+
writeFileSync5(path, JSON.stringify(state, null, 2) + "\n");
|
|
1935
2100
|
}
|
|
1936
2101
|
function readMetroState(cwd) {
|
|
1937
2102
|
let parsed;
|
|
1938
2103
|
try {
|
|
1939
|
-
parsed = JSON.parse(
|
|
2104
|
+
parsed = JSON.parse(readFileSync6(statePath2(cwd), "utf-8"));
|
|
1940
2105
|
} catch {
|
|
1941
2106
|
return null;
|
|
1942
2107
|
}
|
|
@@ -1948,7 +2113,7 @@ function readMetroState(cwd) {
|
|
|
1948
2113
|
}
|
|
1949
2114
|
function clearMetroState(cwd) {
|
|
1950
2115
|
const path = statePath2(cwd);
|
|
1951
|
-
if (existsSync3(path))
|
|
2116
|
+
if (existsSync3(path)) rmSync5(path, { force: true });
|
|
1952
2117
|
}
|
|
1953
2118
|
function killOwnedMetro(cwd) {
|
|
1954
2119
|
const state = readMetroState(cwd);
|
|
@@ -2461,17 +2626,19 @@ function requireNonInteractive(json, message) {
|
|
|
2461
2626
|
}
|
|
2462
2627
|
async function ensureConfig(api, opts) {
|
|
2463
2628
|
const existing = loadDevConfig(process.cwd());
|
|
2629
|
+
const local = loadLocalDevConfig(process.cwd());
|
|
2630
|
+
const effective = effectiveDevConfig(existing, local);
|
|
2464
2631
|
if (opts.platform && !isDevPlatform(opts.platform)) {
|
|
2465
2632
|
console.error(chalk4.red(`error: --platform must be one of ${DEV_PLATFORMS.join(", ")}`));
|
|
2466
2633
|
process.exit(2);
|
|
2467
2634
|
}
|
|
2468
|
-
if (!opts.platform && !
|
|
2635
|
+
if (!opts.platform && !effective?.defaultPlatform) {
|
|
2469
2636
|
requireNonInteractive(
|
|
2470
2637
|
opts.json,
|
|
2471
2638
|
`--platform is required (no vibeview.json default yet) \u2014 pass --platform <${DEV_PLATFORMS.join("|")}>, or run once interactively to set a default.`
|
|
2472
2639
|
);
|
|
2473
2640
|
}
|
|
2474
|
-
const platform = opts.platform ||
|
|
2641
|
+
const platform = opts.platform || effective?.defaultPlatform || await select3({
|
|
2475
2642
|
message: "Platform to run on",
|
|
2476
2643
|
choices: platformChoices(process.cwd())
|
|
2477
2644
|
});
|
|
@@ -2508,7 +2675,7 @@ async function ensureConfig(api, opts) {
|
|
|
2508
2675
|
}
|
|
2509
2676
|
}
|
|
2510
2677
|
}
|
|
2511
|
-
const entry =
|
|
2678
|
+
const entry = effective?.platforms?.[platform];
|
|
2512
2679
|
let allApps;
|
|
2513
2680
|
try {
|
|
2514
2681
|
allApps = await api.listApps();
|
|
@@ -2528,6 +2695,7 @@ ${hint}`);
|
|
|
2528
2695
|
process.exit(2);
|
|
2529
2696
|
}
|
|
2530
2697
|
let appId = choice.appId;
|
|
2698
|
+
const appFromFlag = Boolean(opts.app) && appId === opts.app;
|
|
2531
2699
|
let builtThisRun = false;
|
|
2532
2700
|
if (choice.staleCached) {
|
|
2533
2701
|
console.error(
|
|
@@ -2600,30 +2768,70 @@ ${hint}`);
|
|
|
2600
2768
|
});
|
|
2601
2769
|
}
|
|
2602
2770
|
const deviceCategory = isTv ? "tv" : opts.deviceCategory || entry?.deviceCategory || "phone";
|
|
2603
|
-
const
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
);
|
|
2771
|
+
const metroPortFlag = opts.metroPort ? parseInt(opts.metroPort, 10) : void 0;
|
|
2772
|
+
const metroResolved = resolveMetro(effective, platform, metroPortFlag);
|
|
2773
|
+
const committedEntry = existing?.platforms?.[platform];
|
|
2774
|
+
const nextLocal = JSON.parse(JSON.stringify(local));
|
|
2775
|
+
const localEntry = (nextLocal.platforms ??= {})[platform] ??= {};
|
|
2776
|
+
let committedAppId = committedEntry?.appId;
|
|
2777
|
+
if (!committedAppId) {
|
|
2778
|
+
committedAppId = appId;
|
|
2779
|
+
} else if (appId !== committedAppId) {
|
|
2780
|
+
if (appFromFlag || local.platforms?.[platform]?.appId !== void 0) {
|
|
2781
|
+
localEntry.appId = appId;
|
|
2782
|
+
} else {
|
|
2783
|
+
committedAppId = appId;
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
if (appId === committedAppId) delete localEntry.appId;
|
|
2787
|
+
let committedCategory = committedEntry?.deviceCategory;
|
|
2788
|
+
if (!isTv) {
|
|
2789
|
+
if (!committedCategory) committedCategory = deviceCategory;
|
|
2790
|
+
if (deviceCategory !== committedCategory) localEntry.deviceCategory = deviceCategory;
|
|
2791
|
+
else delete localEntry.deviceCategory;
|
|
2792
|
+
}
|
|
2793
|
+
const committedDefault = existing?.defaultPlatform ?? platform;
|
|
2794
|
+
if (opts.platform) {
|
|
2795
|
+
if (platform !== committedDefault) nextLocal.defaultPlatform = platform;
|
|
2796
|
+
else delete nextLocal.defaultPlatform;
|
|
2797
|
+
}
|
|
2608
2798
|
const cfg = buildSavedConfig({
|
|
2609
2799
|
existing,
|
|
2610
2800
|
platform,
|
|
2611
|
-
appId,
|
|
2612
|
-
deviceCategory,
|
|
2801
|
+
appId: committedAppId,
|
|
2802
|
+
deviceCategory: committedCategory ?? deviceCategory,
|
|
2613
2803
|
isTv,
|
|
2614
|
-
|
|
2804
|
+
// Never the flag: vibeview.json keeps (or, on the first run, seeds) the
|
|
2805
|
+
// shared default; the flag's port is saved locally below.
|
|
2806
|
+
metroPortFlag: void 0,
|
|
2615
2807
|
isFirstRun,
|
|
2616
2808
|
firstRunMetro,
|
|
2617
2809
|
firstRunBuild
|
|
2618
2810
|
});
|
|
2811
|
+
cfg.defaultPlatform = committedDefault;
|
|
2812
|
+
if (metroPortFlag !== void 0 && PLATFORM_NEEDS_METRO[platform]) {
|
|
2813
|
+
if (metroPortFlag !== resolveMetro(cfg, platform).port) localEntry.metroPort = metroPortFlag;
|
|
2814
|
+
else delete localEntry.metroPort;
|
|
2815
|
+
}
|
|
2619
2816
|
const commitConfig = () => {
|
|
2620
|
-
|
|
2817
|
+
updateDevConfig(process.cwd(), platform, (fresh) => {
|
|
2818
|
+
applyConfigChanges(fresh, existing, cfg);
|
|
2819
|
+
});
|
|
2621
2820
|
if (existing) {
|
|
2622
2821
|
const changes = describeConfigChanges(existing, cfg);
|
|
2623
2822
|
if (changes.length) {
|
|
2624
2823
|
console.error(chalk4.dim(`Updated vibeview.json: ${changes.join(", ")}`));
|
|
2625
2824
|
}
|
|
2626
2825
|
}
|
|
2826
|
+
const localChanges = describeLocalChanges(local, nextLocal);
|
|
2827
|
+
if (localChanges.length) {
|
|
2828
|
+
saveLocalDevConfig(process.cwd(), nextLocal);
|
|
2829
|
+
console.error(
|
|
2830
|
+
chalk4.dim(
|
|
2831
|
+
`Saved to .vibeview/local.json (yours only, not committed): ${localChanges.join(", ")}`
|
|
2832
|
+
)
|
|
2833
|
+
);
|
|
2834
|
+
}
|
|
2627
2835
|
if (isFirstRun) {
|
|
2628
2836
|
console.error(chalk4.dim(`Created vibeview.json (platform: ${platform}, app: ${appId}).`));
|
|
2629
2837
|
const hint = fillLaterHint(
|
|
@@ -2633,7 +2841,7 @@ ${hint}`);
|
|
|
2633
2841
|
if (hint) console.error(chalk4.dim(hint));
|
|
2634
2842
|
}
|
|
2635
2843
|
};
|
|
2636
|
-
const finalMetro = isFirstRun ? resolveMetro(cfg,
|
|
2844
|
+
const finalMetro = isFirstRun ? resolveMetro(effectiveDevConfig(cfg, nextLocal), platform, metroPortFlag) : metroResolved;
|
|
2637
2845
|
return {
|
|
2638
2846
|
platform,
|
|
2639
2847
|
appId,
|
|
@@ -2657,6 +2865,13 @@ async function runDevInProcess(opts) {
|
|
|
2657
2865
|
emit(opts.json, { event: "error", code: "invalid_flag", message });
|
|
2658
2866
|
process.exit(2);
|
|
2659
2867
|
}
|
|
2868
|
+
const standalone = opts.standalone === true;
|
|
2869
|
+
if (standalone && opts.platform === "roku") {
|
|
2870
|
+
const message = "--standalone is not for roku \u2014 a Roku dev session has no Metro; it re-packages the channel";
|
|
2871
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2872
|
+
emit(opts.json, { event: "error", code: "invalid_flag", message });
|
|
2873
|
+
process.exit(2);
|
|
2874
|
+
}
|
|
2660
2875
|
let pinnedBuildId;
|
|
2661
2876
|
if (typeof opts.build === "string") {
|
|
2662
2877
|
try {
|
|
@@ -2685,7 +2900,7 @@ async function runDevInProcess(opts) {
|
|
|
2685
2900
|
const cfg = await ensureConfig(api, opts);
|
|
2686
2901
|
if (pinnedBuildId !== void 0) {
|
|
2687
2902
|
try {
|
|
2688
|
-
checkDevBuild(await api.getApp(cfg.appId), pinnedBuildId, cfg.platform);
|
|
2903
|
+
checkDevBuild(await api.getApp(cfg.appId), pinnedBuildId, cfg.platform, { standalone });
|
|
2689
2904
|
} catch (err) {
|
|
2690
2905
|
const message = err.message;
|
|
2691
2906
|
console.error(chalk4.red(`error: ${message}`));
|
|
@@ -2721,9 +2936,20 @@ async function runDevInProcess(opts) {
|
|
|
2721
2936
|
(l) => console.error(chalk4.dim(l))
|
|
2722
2937
|
);
|
|
2723
2938
|
cfg.appId = result.public_id;
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2939
|
+
const localNow = loadLocalDevConfig(process.cwd());
|
|
2940
|
+
const localApp = localNow.platforms?.[cfg.platform];
|
|
2941
|
+
if (localApp?.appId !== void 0) {
|
|
2942
|
+
const committedApp = loadDevConfig(process.cwd())?.platforms?.[cfg.platform]?.appId;
|
|
2943
|
+
if (result.public_id === committedApp) delete localApp.appId;
|
|
2944
|
+
else localApp.appId = result.public_id;
|
|
2945
|
+
saveLocalDevConfig(process.cwd(), localNow);
|
|
2946
|
+
} else {
|
|
2947
|
+
updateDevConfig(process.cwd(), cfg.platform, (fresh) => {
|
|
2948
|
+
const entry = fresh.platforms[cfg.platform];
|
|
2949
|
+
if (!entry || entry.appId === result.public_id) return false;
|
|
2950
|
+
entry.appId = result.public_id;
|
|
2951
|
+
return true;
|
|
2952
|
+
});
|
|
2727
2953
|
}
|
|
2728
2954
|
} catch (err) {
|
|
2729
2955
|
const message = err.message;
|
|
@@ -2735,7 +2961,14 @@ async function runDevInProcess(opts) {
|
|
|
2735
2961
|
let metroOwnedThisRun = false;
|
|
2736
2962
|
let rokuBuild;
|
|
2737
2963
|
let watchIgnore = [];
|
|
2738
|
-
if (
|
|
2964
|
+
if (standalone && cfg.platform === "roku") {
|
|
2965
|
+
const message = "--standalone is not for roku \u2014 a Roku dev session has no Metro";
|
|
2966
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2967
|
+
emit(opts.json, { event: "error", code: "invalid_flag", message });
|
|
2968
|
+
process.exit(2);
|
|
2969
|
+
}
|
|
2970
|
+
if (standalone) {
|
|
2971
|
+
} else if (PLATFORM_NEEDS_METRO[cfg.platform]) {
|
|
2739
2972
|
const metroPort = cfg.metroPort ?? 8081;
|
|
2740
2973
|
let metroResult;
|
|
2741
2974
|
try {
|
|
@@ -2743,7 +2976,7 @@ async function runDevInProcess(opts) {
|
|
|
2743
2976
|
const captureMetroLine = (line) => {
|
|
2744
2977
|
try {
|
|
2745
2978
|
if (!metroLogStream) {
|
|
2746
|
-
|
|
2979
|
+
mkdirSync4(dirname4(metroLogPath(process.cwd())), { recursive: true });
|
|
2747
2980
|
metroLogStream = createWriteStream(metroLogPath(process.cwd()), { flags: "w" });
|
|
2748
2981
|
}
|
|
2749
2982
|
metroLogStream.write(line.replace(/^\[metro\] /, "") + "\n");
|
|
@@ -2787,7 +3020,7 @@ async function runDevInProcess(opts) {
|
|
|
2787
3020
|
}
|
|
2788
3021
|
console.error(
|
|
2789
3022
|
chalk4.dim(
|
|
2790
|
-
`Starting ${platformWithArticle(cfg.platform)} dev session for ${cfg.appId}${pinnedBuildId !== void 0 ? ` (build ${pinnedBuildId})` : ""}...`
|
|
3023
|
+
`Starting ${platformWithArticle(cfg.platform)} ${standalone ? "standalone session (no Metro)" : "dev session"} for ${cfg.appId}${pinnedBuildId !== void 0 ? ` (build ${pinnedBuildId})` : ""}...`
|
|
2791
3024
|
)
|
|
2792
3025
|
);
|
|
2793
3026
|
let stopping = false;
|
|
@@ -2820,6 +3053,7 @@ async function runDevInProcess(opts) {
|
|
|
2820
3053
|
deviceCategory: cfg.deviceCategory,
|
|
2821
3054
|
model: cfg.model,
|
|
2822
3055
|
buildId: pinnedBuildId,
|
|
3056
|
+
...standalone ? { standalone } : {},
|
|
2823
3057
|
metroPort: cfg.metroPort ?? 8081,
|
|
2824
3058
|
// startDevSession's own browser-open happens before we've written the
|
|
2825
3059
|
// state file / emitted session_ready — open it ourselves afterward
|
|
@@ -3725,7 +3959,7 @@ function agentExitCode(env) {
|
|
|
3725
3959
|
}
|
|
3726
3960
|
|
|
3727
3961
|
// src/commands/agent.ts
|
|
3728
|
-
import { writeFileSync as
|
|
3962
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
3729
3963
|
var REF_RE = /^@?(e\d+)$/;
|
|
3730
3964
|
var COORDS_RE = /^(-?\d+),\s*(-?\d+)$/;
|
|
3731
3965
|
var FlagError = class extends Error {
|
|
@@ -3935,7 +4169,7 @@ async function runAgentVerb(cmd, positionals, opts) {
|
|
|
3935
4169
|
`);
|
|
3936
4170
|
process.exit(1);
|
|
3937
4171
|
}
|
|
3938
|
-
|
|
4172
|
+
writeFileSync6(outPath, imageBytes);
|
|
3939
4173
|
if (opts.json) {
|
|
3940
4174
|
console.log(JSON.stringify(envelope));
|
|
3941
4175
|
} else {
|
|
@@ -3958,8 +4192,10 @@ export {
|
|
|
3958
4192
|
DEV_PLATFORMS,
|
|
3959
4193
|
PLATFORM_NEEDS_METRO,
|
|
3960
4194
|
isDevPlatform,
|
|
4195
|
+
platformLabel,
|
|
3961
4196
|
platformWithArticle,
|
|
3962
4197
|
loadDevConfig,
|
|
4198
|
+
resolveMetro,
|
|
3963
4199
|
parseBuildId,
|
|
3964
4200
|
checkDevBuild,
|
|
3965
4201
|
buildRows,
|
|
@@ -3970,6 +4206,8 @@ export {
|
|
|
3970
4206
|
readMetroState,
|
|
3971
4207
|
killOwnedMetro,
|
|
3972
4208
|
checkMetro,
|
|
4209
|
+
loadLocalDevConfig,
|
|
4210
|
+
effectiveDevConfig,
|
|
3973
4211
|
buildCommand,
|
|
3974
4212
|
formatDuration,
|
|
3975
4213
|
BUILD_TERMINAL_STATUSES,
|