vibeview 0.37.0 → 0.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -885,7 +885,7 @@ function rokuDefaults(cwd) {
|
|
|
885
885
|
}
|
|
886
886
|
return { buildCommand: ROKU_ZIP_COMMAND, artifact };
|
|
887
887
|
}
|
|
888
|
-
function defaultsFor(cwd, platform) {
|
|
888
|
+
function defaultsFor(cwd, platform, evaluatedIosName) {
|
|
889
889
|
const flavor = detectFlavor(cwd);
|
|
890
890
|
if (platform === "roku") return flavor === "roku" ? rokuDefaults(cwd) : {};
|
|
891
891
|
if (flavor === "unknown" || flavor === "roku") return {};
|
|
@@ -900,7 +900,7 @@ function defaultsFor(cwd, platform) {
|
|
|
900
900
|
};
|
|
901
901
|
}
|
|
902
902
|
if (isManagedExpo(cwd, "ios")) {
|
|
903
|
-
const name = managedExpoIosName(cwd);
|
|
903
|
+
const name = managedExpoIosName(cwd, evaluatedIosName);
|
|
904
904
|
return {
|
|
905
905
|
buildCommand: name ? managedExpoIosCommand(cwd, name) : void 0,
|
|
906
906
|
artifact: IOS_ARTIFACT,
|
|
@@ -925,10 +925,13 @@ function managedExpoAndroidProductionCommand(cwd) {
|
|
|
925
925
|
function expoSanitizedName(name) {
|
|
926
926
|
return name.replace(/[\W_]+/g, "").normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
927
927
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
928
|
+
var DYNAMIC_EXPO_CONFIGS = ["app.config.ts", "app.config.js", "app.config.mjs", "app.config.cjs"];
|
|
929
|
+
function dynamicExpoConfig(cwd) {
|
|
930
|
+
return DYNAMIC_EXPO_CONFIGS.find((f) => existsSync3(join5(cwd, f)));
|
|
931
|
+
}
|
|
932
|
+
function managedExpoIosName(cwd, evaluatedName) {
|
|
933
|
+
if (evaluatedName !== void 0) return expoSanitizedName(evaluatedName) || null;
|
|
934
|
+
if (dynamicExpoConfig(cwd)) return null;
|
|
932
935
|
const raw = readJson(cwd, "app.config.json") ?? readJson(cwd, "app.json");
|
|
933
936
|
if (!raw) return null;
|
|
934
937
|
const config = "expo" in raw ? raw.expo : raw;
|
|
@@ -943,7 +946,10 @@ function iosArchiveExportCommand(ws) {
|
|
|
943
946
|
const exportCmd = `xcodebuild -exportArchive -archivePath ${archivePath} -exportPath ios/build -exportOptionsPlist "$VIBEVIEW_EXPORT_OPTIONS_PLIST"`;
|
|
944
947
|
return `${archiveCmd} && ${exportCmd}`;
|
|
945
948
|
}
|
|
946
|
-
function
|
|
949
|
+
function managedExpoIosProductionCommand(cwd, name) {
|
|
950
|
+
return `${installCommand(cwd)} && npx expo prebuild --platform ios && ` + iosArchiveExportCommand(name);
|
|
951
|
+
}
|
|
952
|
+
function productionDefaultsFor(cwd, platform, evaluatedIosName) {
|
|
947
953
|
if (platform === "android" || platform === "androidtv") {
|
|
948
954
|
return {
|
|
949
955
|
buildCommand: isManagedExpo(cwd, "android") ? managedExpoAndroidProductionCommand(cwd) : androidProductionCommand(cwd),
|
|
@@ -955,10 +961,10 @@ function productionDefaultsFor(cwd, platform) {
|
|
|
955
961
|
}
|
|
956
962
|
if (platform === "ios") {
|
|
957
963
|
if (isManagedExpo(cwd, "ios")) {
|
|
958
|
-
const name = managedExpoIosName(cwd);
|
|
964
|
+
const name = managedExpoIosName(cwd, evaluatedIosName);
|
|
959
965
|
if (!name) return { artifact: IOS_PRODUCTION_ARTIFACT };
|
|
960
966
|
return {
|
|
961
|
-
buildCommand:
|
|
967
|
+
buildCommand: managedExpoIosProductionCommand(cwd, name),
|
|
962
968
|
artifact: IOS_PRODUCTION_ARTIFACT
|
|
963
969
|
};
|
|
964
970
|
}
|
|
@@ -994,36 +1000,65 @@ function pbxprojText(cwd) {
|
|
|
994
1000
|
return null;
|
|
995
1001
|
}
|
|
996
1002
|
}
|
|
997
|
-
|
|
1003
|
+
function parseExpoConfigJson(stdout) {
|
|
1004
|
+
const lines = stdout.trim().split("\n");
|
|
1005
|
+
for (const text of [stdout, lines[lines.length - 1]]) {
|
|
1006
|
+
try {
|
|
1007
|
+
const parsed = JSON.parse(text);
|
|
1008
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1009
|
+
return parsed;
|
|
1010
|
+
}
|
|
1011
|
+
} catch {
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
var EXPO_CONFIG_TIMEOUT_MS = 3e4;
|
|
1017
|
+
var readExpoConfig = (cwd, timeoutMs = EXPO_CONFIG_TIMEOUT_MS) => {
|
|
998
1018
|
const cli = join5(cwd, "node_modules", "expo", "bin", "cli");
|
|
999
1019
|
if (!existsSync3(cli)) {
|
|
1000
|
-
return Promise.reject(
|
|
1020
|
+
return Promise.reject(
|
|
1021
|
+
new Error("Expo isn't installed in this project yet (run your install command first)")
|
|
1022
|
+
);
|
|
1001
1023
|
}
|
|
1002
1024
|
return new Promise((resolve, reject) => {
|
|
1003
|
-
|
|
1025
|
+
let settled = false;
|
|
1026
|
+
const settle = (fn) => {
|
|
1027
|
+
if (settled) return;
|
|
1028
|
+
settled = true;
|
|
1029
|
+
clearTimeout(timer);
|
|
1030
|
+
fn();
|
|
1031
|
+
};
|
|
1032
|
+
const child = execFile(
|
|
1004
1033
|
process.execPath,
|
|
1005
1034
|
[cli, "config", "--json", "--type", "public"],
|
|
1006
1035
|
{
|
|
1007
1036
|
cwd,
|
|
1008
|
-
timeout: 3e4,
|
|
1009
1037
|
maxBuffer: 8 * 1024 * 1024,
|
|
1010
1038
|
env: { ...process.env, EXPO_NO_TELEMETRY: "1", CI: "1" }
|
|
1011
1039
|
},
|
|
1012
1040
|
(err, stdout, stderr) => {
|
|
1013
1041
|
if (err) {
|
|
1014
1042
|
const why = String(stderr || err.message).split("\n").map((l) => l.trim()).find((l) => l.length > 0);
|
|
1015
|
-
reject(new Error(`expo config failed${why ? `: ${why}` : ""}`));
|
|
1043
|
+
settle(() => reject(new Error(`expo config failed${why ? `: ${why}` : ""}`)));
|
|
1016
1044
|
return;
|
|
1017
1045
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
} catch {
|
|
1023
|
-
reject(new Error("expo config printed something that is not JSON"));
|
|
1024
|
-
}
|
|
1046
|
+
const parsed = parseExpoConfigJson(stdout);
|
|
1047
|
+
settle(
|
|
1048
|
+
() => parsed ? resolve(parsed) : reject(new Error("expo config printed something that is not JSON"))
|
|
1049
|
+
);
|
|
1025
1050
|
}
|
|
1026
1051
|
);
|
|
1052
|
+
child.stdin?.end();
|
|
1053
|
+
const timer = setTimeout(() => {
|
|
1054
|
+
child.kill("SIGKILL");
|
|
1055
|
+
child.stdout?.destroy();
|
|
1056
|
+
child.stderr?.destroy();
|
|
1057
|
+
settle(
|
|
1058
|
+
() => reject(new Error(`expo config timed out after ${Math.round(timeoutMs / 1e3)} s`))
|
|
1059
|
+
);
|
|
1060
|
+
}, timeoutMs);
|
|
1061
|
+
timer.unref();
|
|
1027
1062
|
});
|
|
1028
1063
|
};
|
|
1029
1064
|
async function localAppIdentity(cwd, platform, readConfig = readExpoConfig) {
|
|
@@ -1034,9 +1069,7 @@ async function localAppIdentity(cwd, platform, readConfig = readExpoConfig) {
|
|
|
1034
1069
|
let version = null;
|
|
1035
1070
|
let bundleId = null;
|
|
1036
1071
|
let versionUnreadable = null;
|
|
1037
|
-
const dynamicName =
|
|
1038
|
-
(f) => existsSync3(join5(cwd, f))
|
|
1039
|
-
);
|
|
1072
|
+
const dynamicName = dynamicExpoConfig(cwd);
|
|
1040
1073
|
const staticRaw = readJson(cwd, "app.config.json") ?? readJson(cwd, "app.json");
|
|
1041
1074
|
const isExpo = Boolean(dynamicName) || staticRaw !== null && "expo" in staticRaw || existsSync3(join5(cwd, "node_modules", "expo", "package.json"));
|
|
1042
1075
|
let expo = null;
|
|
@@ -1075,6 +1108,41 @@ async function localAppIdentity(cwd, platform, readConfig = readExpoConfig) {
|
|
|
1075
1108
|
}
|
|
1076
1109
|
return { reactNative, version, bundleId, versionUnreadable: version ? null : versionUnreadable };
|
|
1077
1110
|
}
|
|
1111
|
+
var evaluatedNameCache = /* @__PURE__ */ new Map();
|
|
1112
|
+
function evaluatedExpoName(cwd, readConfig, onEvaluate) {
|
|
1113
|
+
const cached = evaluatedNameCache.get(cwd);
|
|
1114
|
+
if (cached) return cached;
|
|
1115
|
+
onEvaluate?.();
|
|
1116
|
+
const pending = Promise.resolve().then(() => readConfig(cwd)).then(
|
|
1117
|
+
(config) => typeof config.name === "string" ? { name: config.name } : { error: "the resolved config has no name" },
|
|
1118
|
+
(err) => ({
|
|
1119
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1120
|
+
})
|
|
1121
|
+
);
|
|
1122
|
+
evaluatedNameCache.set(cwd, pending);
|
|
1123
|
+
return pending;
|
|
1124
|
+
}
|
|
1125
|
+
async function resolvedDefaultsFor(cwd, platform, opts = {}) {
|
|
1126
|
+
const { production = false, readConfig = (c) => readExpoConfig(c), onEvaluate } = opts;
|
|
1127
|
+
const base = production ? productionDefaultsFor : defaultsFor;
|
|
1128
|
+
const dynamic = dynamicExpoConfig(cwd);
|
|
1129
|
+
if (platform !== "ios" || !dynamic || !isManagedExpo(cwd, "ios")) {
|
|
1130
|
+
return base(cwd, platform);
|
|
1131
|
+
}
|
|
1132
|
+
const evaluated = await evaluatedExpoName(cwd, readConfig, onEvaluate);
|
|
1133
|
+
if ("error" in evaluated) {
|
|
1134
|
+
return { ...base(cwd, platform), evaluatedConfig: dynamic, evaluationError: evaluated.error };
|
|
1135
|
+
}
|
|
1136
|
+
const named = base(cwd, platform, evaluated.name);
|
|
1137
|
+
if (!named.buildCommand) {
|
|
1138
|
+
return {
|
|
1139
|
+
...named,
|
|
1140
|
+
evaluatedConfig: dynamic,
|
|
1141
|
+
evaluationError: `the name "${evaluated.name}" has no letters or digits`
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
return { ...named, evaluatedConfig: dynamic };
|
|
1145
|
+
}
|
|
1078
1146
|
|
|
1079
1147
|
// src/lib/dev-setup.ts
|
|
1080
1148
|
function platformChoices(cwd) {
|
|
@@ -1109,6 +1177,37 @@ function fillLaterHint(skipped, platform) {
|
|
|
1109
1177
|
}
|
|
1110
1178
|
return lines.join("\n");
|
|
1111
1179
|
}
|
|
1180
|
+
async function promptDefaults(cwd, platform, opts = {}) {
|
|
1181
|
+
const progress = {};
|
|
1182
|
+
try {
|
|
1183
|
+
return await resolvedDefaultsFor(cwd, platform, {
|
|
1184
|
+
...opts,
|
|
1185
|
+
onEvaluate: () => {
|
|
1186
|
+
progress.spinner = createSpinner("Reading Expo config\u2026").start();
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
} finally {
|
|
1190
|
+
progress.spinner?.stop();
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
function evaluatedConfigNote(cwd, defaults) {
|
|
1194
|
+
const file = defaults.evaluatedConfig;
|
|
1195
|
+
if (!file) return;
|
|
1196
|
+
if (defaults.evaluationError) {
|
|
1197
|
+
console.log(chalk2.dim(` Couldn't read the name from ${file}: ${defaults.evaluationError}`));
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
if (notedConfigDirs.has(cwd)) return;
|
|
1201
|
+
notedConfigDirs.add(cwd);
|
|
1202
|
+
console.log(
|
|
1203
|
+
chalk2.dim(
|
|
1204
|
+
` The iOS name was read from ${file} using your local environment.
|
|
1205
|
+
If it depends on environment variables, set the same ones for the cloud build
|
|
1206
|
+
(build.env, or build variables).`
|
|
1207
|
+
)
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
var notedConfigDirs = /* @__PURE__ */ new Set();
|
|
1112
1211
|
async function commandSelect(message, detected) {
|
|
1113
1212
|
const choices = [];
|
|
1114
1213
|
if (detected) choices.push({ value: "default", name: `Use detected default: ${detected}` });
|
|
@@ -1117,7 +1216,8 @@ async function commandSelect(message, detected) {
|
|
|
1117
1216
|
return select({ message, choices });
|
|
1118
1217
|
}
|
|
1119
1218
|
async function promptBuildConfig(cwd, platform) {
|
|
1120
|
-
const defaults =
|
|
1219
|
+
const defaults = await promptDefaults(cwd, platform);
|
|
1220
|
+
evaluatedConfigNote(cwd, defaults);
|
|
1121
1221
|
const choice = await commandSelect(`Build command for ${platform}`, defaults.buildCommand);
|
|
1122
1222
|
if (choice === "skip") return null;
|
|
1123
1223
|
if (choice === "default") {
|
|
@@ -1137,7 +1237,8 @@ async function promptBuildConfig(cwd, platform) {
|
|
|
1137
1237
|
return { command, artifact };
|
|
1138
1238
|
}
|
|
1139
1239
|
async function promptProductionBuildConfig(cwd, platform) {
|
|
1140
|
-
const defaults =
|
|
1240
|
+
const defaults = await promptDefaults(cwd, platform, { production: true });
|
|
1241
|
+
evaluatedConfigNote(cwd, defaults);
|
|
1141
1242
|
const choice = await commandSelect(
|
|
1142
1243
|
`Production build command for ${platform}`,
|
|
1143
1244
|
defaults.buildCommand
|
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-NJ2IXB5H.js";
|
|
29
29
|
import {
|
|
30
30
|
ApiClient,
|
|
31
31
|
ApiStatusError,
|
|
@@ -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-DF3CDWL4.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.37.
|
|
3
|
+
"version": "0.37.1",
|
|
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",
|