vibeview 0.12.1 → 0.12.2

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.
@@ -128,8 +128,11 @@ function loadDevConfig(cwd) {
128
128
  `vibeview.json: unknown platform "${key}" (expected one of ${DEV_PLATFORMS.join(", ")})`
129
129
  );
130
130
  }
131
- if (!entry || typeof entry.appId !== "string" || !entry.appId) {
132
- throw new Error(`vibeview.json: platforms.${key}.appId is required`);
131
+ if (!entry || typeof entry !== "object") {
132
+ throw new Error(`vibeview.json: platforms.${key} must be an object`);
133
+ }
134
+ if (entry.appId !== void 0 && (typeof entry.appId !== "string" || !entry.appId)) {
135
+ throw new Error(`vibeview.json: platforms.${key}.appId must be a non-empty string`);
133
136
  }
134
137
  if (entry.build !== void 0) {
135
138
  if (typeof entry.build !== "object" || entry.build === null) {
@@ -411,6 +414,15 @@ function platformChoices(cwd) {
411
414
  if (detectFlavor(cwd) === "roku") choices.unshift({ value: "roku", name: "Roku (beta)" });
412
415
  return choices;
413
416
  }
417
+ function appsForPlatform(apps, platform) {
418
+ return apps.filter((a) => a.platform === platform);
419
+ }
420
+ function noAppsMessage(platform, hasBuildConfig) {
421
+ if (hasBuildConfig) {
422
+ return `No ${platform} apps found \u2014 run \`vibeview dev --build\` (or \`vibeview build\`) to build and upload one.`;
423
+ }
424
+ return `No ${platform} apps found. Run \`vibeview build --platform ${platform}\` to build and upload one (it will ask for your build command), or upload a build with \`vibeview upload-app\`.`;
425
+ }
414
426
  function firstRunAppStrategy(apps, buildConfigured) {
415
427
  if (!buildConfigured) return "picker";
416
428
  return apps.length === 0 ? "build_now" : "ask";
@@ -699,34 +711,59 @@ async function resolveBarePlatform(cwd, cfg, opts) {
699
711
  }
700
712
  return platform;
701
713
  }
714
+ function persistPromptedBuild(cwd, cfg, platform, build) {
715
+ const next = cfg ?? { defaultPlatform: platform, platforms: {} };
716
+ const entry = next.platforms[platform];
717
+ if (entry) {
718
+ entry.build = build;
719
+ } else {
720
+ next.platforms[platform] = { build };
721
+ }
722
+ saveDevConfig(cwd, next);
723
+ return next;
724
+ }
725
+ function persistUploadedAppId(cwd, cfg, platform, build, appId) {
726
+ const next = cfg ?? { defaultPlatform: platform, platforms: {} };
727
+ const entry = next.platforms[platform];
728
+ if (!entry) {
729
+ next.platforms[platform] = { appId, build };
730
+ saveDevConfig(cwd, next);
731
+ return;
732
+ }
733
+ if (entry.appId !== appId || !entry.build) {
734
+ entry.appId = appId;
735
+ entry.build = build;
736
+ saveDevConfig(cwd, next);
737
+ }
738
+ }
739
+ async function resolveBuildConfig(cwd, cfgRef, platform, opts) {
740
+ const existing = cfgRef.current?.platforms?.[platform]?.build;
741
+ if (existing) return existing;
742
+ if (opts.json || !process.stdin.isTTY) {
743
+ fail(
744
+ opts,
745
+ `no build config for ${platform}: add platforms.${platform}.build {command, artifact} to vibeview.json`,
746
+ "config_required"
747
+ );
748
+ }
749
+ const answered = await promptBuildConfig(cwd, platform);
750
+ if (!answered) {
751
+ fail(opts, "build config is required to run vibeview build", "config_required");
752
+ }
753
+ cfgRef.current = persistPromptedBuild(cwd, cfgRef.current, platform, answered);
754
+ return answered;
755
+ }
702
756
  async function buildCommand(opts, cloudDeps) {
703
757
  const cwd = process.cwd();
704
- const cfg = loadDevConfig(cwd);
758
+ const cfgRef = { current: loadDevConfig(cwd) };
705
759
  try {
706
760
  if (opts.cloud) {
707
- await runCloudBuildCommand(cwd, cfg, opts, cloudDeps);
761
+ await runCloudBuildCommand(cwd, cfgRef, opts, cloudDeps);
708
762
  return;
709
763
  }
710
- const platform = await resolveBarePlatform(cwd, cfg, opts);
711
- let build = cfg?.platforms?.[platform]?.build;
712
- if (!build) {
713
- if (opts.json || !process.stdin.isTTY) {
714
- fail(
715
- opts,
716
- `no build config for ${platform}: add platforms.${platform}.build {command, artifact} to vibeview.json`,
717
- "config_required"
718
- );
719
- }
720
- const answered = await promptBuildConfig(cwd, platform);
721
- if (!answered) {
722
- fail(opts, "build config is required to run vibeview build", "config_required");
723
- }
724
- build = answered;
725
- if (cfg?.platforms?.[platform]) {
726
- cfg.platforms[platform].build = build;
727
- saveDevConfig(cwd, cfg);
728
- }
729
- }
764
+ const platform = await resolveBarePlatform(cwd, cfgRef.current, opts);
765
+ const build = await resolveBuildConfig(cwd, cfgRef, platform, opts);
766
+ const cfg = cfgRef.current;
730
767
  const result = await runBuildForPlatform(
731
768
  cwd,
732
769
  platform,
@@ -735,16 +772,7 @@ async function buildCommand(opts, cloudDeps) {
735
772
  (l) => console.error(chalk.dim(l))
736
773
  );
737
774
  for (const w of result.warnings ?? []) console.error(chalk.yellow(`\u26A0 ${w}`));
738
- const nextCfg = cfg ?? { defaultPlatform: platform, platforms: {} };
739
- const entry = nextCfg.platforms[platform];
740
- if (!entry) {
741
- nextCfg.platforms[platform] = { appId: result.public_id, build };
742
- saveDevConfig(cwd, nextCfg);
743
- } else if (entry.appId !== result.public_id || !entry.build) {
744
- entry.appId = result.public_id;
745
- entry.build = build;
746
- saveDevConfig(cwd, nextCfg);
747
- }
775
+ persistUploadedAppId(cwd, cfg, platform, build, result.public_id);
748
776
  if (opts.json) {
749
777
  console.log(
750
778
  JSON.stringify({ app_id: result.public_id, name: result.name, platform: result.platform })
@@ -773,22 +801,29 @@ function fail(opts, message, code) {
773
801
  function emitCloud(json, evt) {
774
802
  if (json) console.log(JSON.stringify(evt));
775
803
  }
776
- async function resolveCloudPlatforms(cwd, cfg, opts) {
804
+ async function resolveCloudPlatforms(cwd, cfgRef, opts) {
777
805
  if (opts.platform === "all") {
778
- const configured = Object.entries(cfg?.platforms ?? {});
806
+ const configured = Object.entries(cfgRef.current?.platforms ?? {});
779
807
  const withBuild = configured.filter(([p, entry]) => isCloudPlatform(p) && entry?.build);
780
- if (withBuild.length === 0) {
808
+ if (withBuild.length > 0) return withBuild.map(([p]) => p);
809
+ if (opts.json || !process.stdin.isTTY) {
781
810
  fail(
782
811
  opts,
783
812
  `--platform all: ${describeAllExclusions(configured)} \u2014 add platforms.<platform>.build {command, artifact} to vibeview.json`,
784
813
  "config_required"
785
814
  );
786
815
  }
787
- return withBuild.map(([p]) => p);
816
+ const chosen = await resolveBarePlatform(cwd, cfgRef.current, {
817
+ ...opts,
818
+ platform: void 0
819
+ });
820
+ requireCloudCapable(opts, chosen);
821
+ await resolveBuildConfig(cwd, cfgRef, chosen, opts);
822
+ return [chosen];
788
823
  }
789
- const platform = opts.platform ? validateExplicitPlatform(opts.platform) : await resolveBarePlatform(cwd, cfg, opts);
824
+ const platform = opts.platform ? validateExplicitPlatform(opts.platform) : await resolveBarePlatform(cwd, cfgRef.current, opts);
790
825
  requireCloudCapable(opts, platform);
791
- requireBuildBlock(opts, cfg, platform);
826
+ await resolveBuildConfig(cwd, cfgRef, platform, opts);
792
827
  return [platform];
793
828
  }
794
829
  function validateExplicitPlatform(raw) {
@@ -808,14 +843,54 @@ function requireCloudCapable(opts, platform) {
808
843
  );
809
844
  }
810
845
  }
811
- function requireBuildBlock(opts, cfg, platform) {
812
- if (!cfg?.platforms?.[platform]?.build) {
846
+ function persistPickedAppId(cwd, cfg, platform, appId) {
847
+ const next = cfg ?? { defaultPlatform: platform, platforms: {} };
848
+ const entry = next.platforms[platform];
849
+ if (entry) {
850
+ entry.appId = appId;
851
+ } else {
852
+ next.platforms[platform] = { appId };
853
+ }
854
+ saveDevConfig(cwd, next);
855
+ return next;
856
+ }
857
+ async function resolveCloudAppIds(cwd, cfgRef, platforms, opts, listApps) {
858
+ const missing = platforms.filter((p) => !cfgRef.current?.platforms?.[p]?.appId);
859
+ if (missing.length === 0) return;
860
+ if (opts.json || !process.stdin.isTTY) {
813
861
  fail(
814
862
  opts,
815
- `no build config for ${platform}: add platforms.${platform}.build {command, artifact} to vibeview.json`,
863
+ `no app configured for ${missing[0]}: cloud builds attach to an existing app. Run \`vibeview build --platform ${missing[0]}\` once to build and upload locally, or \`vibeview upload-app\`, then retry --cloud`,
816
864
  "config_required"
817
865
  );
818
866
  }
867
+ let allApps;
868
+ try {
869
+ allApps = await listApps();
870
+ } catch (err) {
871
+ fail(
872
+ opts,
873
+ `could not fetch your app list \u2014 ${err instanceof Error ? err.message : String(err)}`
874
+ );
875
+ }
876
+ for (const platform of missing) {
877
+ const apps = appsForPlatform(allApps, platform);
878
+ if (apps.length === 0) {
879
+ fail(
880
+ opts,
881
+ `${noAppsMessage(platform, cfgRef.current?.platforms?.[platform]?.build !== void 0)} Then retry \`vibeview build --cloud\`.`,
882
+ "config_required"
883
+ );
884
+ }
885
+ const appId = await select2({
886
+ message: platforms.length > 1 ? `Which app should these ${platform} builds attach to?` : "Which app should these builds attach to?",
887
+ // No platform suffix on the rows: the list is already filtered to this
888
+ // one platform, so `(ios)` on every row would be pure noise (same
889
+ // reasoning as dev.ts's picker).
890
+ choices: apps.map((a) => ({ value: a.public_id, name: a.name }))
891
+ });
892
+ cfgRef.current = persistPickedAppId(cwd, cfgRef.current, platform, appId);
893
+ }
819
894
  }
820
895
  function describeAllExclusions(configured) {
821
896
  if (configured.length === 0) return "no platforms are configured";
@@ -857,6 +932,7 @@ function defaultCloudBuildDeps(client) {
857
932
  return {
858
933
  listSourceFiles,
859
934
  packSource,
935
+ listApps: client.listApps.bind(client),
860
936
  createCloudBuilds: client.createCloudBuilds.bind(client),
861
937
  getCloudBuildLogs: client.getCloudBuildLogs.bind(client),
862
938
  getCloudBuild: client.getCloudBuild.bind(client),
@@ -876,8 +952,13 @@ function formatDuration(startedAt, finishedAt) {
876
952
  function formatCost(costMicro) {
877
953
  return `$${(costMicro / 1e6).toFixed(2)}`;
878
954
  }
879
- async function runCloudBuildCommand(cwd, cfg, opts, deps) {
880
- const platforms = await resolveCloudPlatforms(cwd, cfg, opts);
955
+ async function runCloudBuildCommand(cwd, cfgRef, opts, deps) {
956
+ const platforms = await resolveCloudPlatforms(cwd, cfgRef, opts);
957
+ const { token, baseUrl } = resolveAuth(opts);
958
+ const client = new ApiClient(baseUrl, token);
959
+ const d = deps ?? defaultCloudBuildDeps(client);
960
+ await resolveCloudAppIds(cwd, cfgRef, platforms, opts, d.listApps);
961
+ const cfg = cfgRef.current;
881
962
  for (const p of platforms) {
882
963
  try {
883
964
  validateBuildEnv(cfg?.platforms?.[p]?.build?.env, p);
@@ -885,9 +966,6 @@ async function runCloudBuildCommand(cwd, cfg, opts, deps) {
885
966
  fail(opts, err instanceof Error ? err.message : String(err));
886
967
  }
887
968
  }
888
- const { token, baseUrl } = resolveAuth(opts);
889
- const client = new ApiClient(baseUrl, token);
890
- const d = deps ?? defaultCloudBuildDeps(client);
891
969
  let cleanupTar;
892
970
  try {
893
971
  let files;
@@ -927,6 +1005,9 @@ ${offenders}`);
927
1005
  const build = entry.build;
928
1006
  return {
929
1007
  platform: p,
1008
+ // Non-null assertion is safe: resolveCloudAppIds ran for every entry
1009
+ // in `platforms` above — it either persisted a picked appId onto the
1010
+ // entry or failed the whole command.
930
1011
  app_public_id: entry.appId,
931
1012
  command: build.command,
932
1013
  artifact_path: build.artifact,
@@ -1845,20 +1926,11 @@ var PLATFORM_DEVICE = {
1845
1926
  // handheld type plus category tv) — the worker registers it as such.
1846
1927
  roku: { deviceType: "roku", tv: true }
1847
1928
  };
1848
- function appsForPlatform(apps, platform) {
1849
- return apps.filter((a) => a.platform === platform);
1850
- }
1851
1929
  function validateAppChoice(requested, fromFlag, apps) {
1852
1930
  if (!requested) return {};
1853
1931
  if (apps.some((a) => a.public_id === requested)) return { appId: requested };
1854
1932
  return fromFlag ? { staleFlag: requested } : { staleCached: requested };
1855
1933
  }
1856
- function noAppsMessage(platform, hasBuildConfig) {
1857
- if (hasBuildConfig) {
1858
- return `No ${platform} apps found \u2014 run \`vibeview dev --build\` (or \`vibeview build\`) to build and upload one.`;
1859
- }
1860
- return `No ${platform} apps found. Run \`vibeview build --platform ${platform}\` to build and upload one (it will ask for your build command), or upload a build with \`vibeview upload-app\`.`;
1861
- }
1862
1934
  function requireNonInteractive(json) {
1863
1935
  if (!json) return;
1864
1936
  emit(json, {
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  readDevState,
14
14
  readMetroState,
15
15
  runAgentVerb
16
- } from "./chunk-NFFLJNUI.js";
16
+ } from "./chunk-OYLVR6XA.js";
17
17
  import {
18
18
  ApiClient,
19
19
  EXIT_INFRA_ERROR,
@@ -1717,7 +1717,7 @@ program.command("dev-stop").description("Stop a `vibeview dev --detach` session
1717
1717
  program.command("mcp").description(
1718
1718
  "Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop as tools"
1719
1719
  ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
1720
- const { runMcpServer } = await import("./server-A44Z2HUK.js");
1720
+ const { runMcpServer } = await import("./server-DRMLNXEZ.js");
1721
1721
  await runMcpServer(opts);
1722
1722
  });
1723
1723
  program.command("agent-setup").description(
@@ -11,7 +11,7 @@ import {
11
11
  readDevState,
12
12
  renderEnvelope,
13
13
  startDevSession
14
- } from "./chunk-NFFLJNUI.js";
14
+ } from "./chunk-OYLVR6XA.js";
15
15
  import {
16
16
  ApiClient,
17
17
  resolveAuth
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeview",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "VibeView CLI -- run mobile tests from CI/CD pipelines",
5
5
  "type": "module",
6
6
  "bin": {