vibeview 0.40.0 → 0.42.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.
|
@@ -831,10 +831,23 @@ function detectFlavor(cwd) {
|
|
|
831
831
|
} catch {
|
|
832
832
|
}
|
|
833
833
|
if ("expo" in deps) return "expo";
|
|
834
|
-
if (
|
|
834
|
+
if (isFlutterProject(cwd)) return "flutter";
|
|
835
|
+
const nativeDirs = existsSync3(join5(cwd, "android", "gradlew")) || iosWorkspaceName(cwd);
|
|
836
|
+
const rnDirs = existsSync3(join5(cwd, "ios")) || existsSync3(join5(cwd, "android"));
|
|
837
|
+
if ("react-native" in deps && rnDirs) return "bare-rn";
|
|
838
|
+
if ("@capacitor/core" in deps || CAPACITOR_CONFIGS.some((f) => isFile(join5(cwd, f)))) {
|
|
839
|
+
return "capacitor";
|
|
840
|
+
}
|
|
841
|
+
if (nativeDirs) return isFile(join5(cwd, "package.json")) ? "bare-rn" : "native";
|
|
835
842
|
if (isFile(join5(cwd, "manifest"))) return "roku";
|
|
843
|
+
if (isFile(join5(cwd, "gradlew")) || xcodeProjects(cwd).length > 0) return "native";
|
|
836
844
|
return "unknown";
|
|
837
845
|
}
|
|
846
|
+
function isFlutterProject(cwd) {
|
|
847
|
+
const pubspec = readText(join5(cwd, "pubspec.yaml"));
|
|
848
|
+
return pubspec !== null && /^\s*sdk:\s*flutter\s*(#.*)?$/m.test(pubspec) && !/^\s+plugin:\s*(#.*)?$/m.test(pubspec);
|
|
849
|
+
}
|
|
850
|
+
var CAPACITOR_CONFIGS = ["capacitor.config.ts", "capacitor.config.js", "capacitor.config.json"];
|
|
838
851
|
function nativeDirShipped(cwd, dir) {
|
|
839
852
|
if (!existsSync3(join5(cwd, dir))) return false;
|
|
840
853
|
try {
|
|
@@ -862,6 +875,163 @@ function iosWorkspaceName(cwd) {
|
|
|
862
875
|
}
|
|
863
876
|
var IOS_ARTIFACT = "ios/build/Build/Products/Debug-iphonesimulator/*.app";
|
|
864
877
|
var ANDROID_ARTIFACT = "android/app/build/outputs/apk/debug/*.apk";
|
|
878
|
+
var NATIVE_SEARCH_SKIP = /* @__PURE__ */ new Set(["node_modules", "build", "Pods", "DerivedData"]);
|
|
879
|
+
function topLevelDirs(cwd) {
|
|
880
|
+
try {
|
|
881
|
+
return readdirSync2(cwd, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && !NATIVE_SEARCH_SKIP.has(e.name)).map((e) => e.name).sort();
|
|
882
|
+
} catch {
|
|
883
|
+
return [];
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
var SAFE_SEGMENT = /^[A-Za-z0-9._-]+$/;
|
|
887
|
+
function dirShipped(cwd, rel) {
|
|
888
|
+
const slash = rel.indexOf("/");
|
|
889
|
+
const checks = [["", rel]];
|
|
890
|
+
if (slash !== -1) checks.push([rel.slice(0, slash), rel.slice(slash + 1)]);
|
|
891
|
+
return checks.every(([dir, path]) => {
|
|
892
|
+
try {
|
|
893
|
+
return !ignore().add(readFileSync5(join5(cwd, dir, ".gitignore"), "utf-8")).ignores(`${path}/`);
|
|
894
|
+
} catch {
|
|
895
|
+
return true;
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
function xcodeProjects(cwd) {
|
|
900
|
+
const found = [];
|
|
901
|
+
for (const dir of ["", ...topLevelDirs(cwd)]) {
|
|
902
|
+
try {
|
|
903
|
+
for (const f of readdirSync2(join5(cwd, dir)).sort()) {
|
|
904
|
+
const rel = dir ? `${dir}/${f}` : f;
|
|
905
|
+
if (f.endsWith(".xcodeproj") && dirShipped(cwd, rel)) found.push(rel);
|
|
906
|
+
}
|
|
907
|
+
} catch {
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return found;
|
|
911
|
+
}
|
|
912
|
+
function xcodeScheme(cwd, project) {
|
|
913
|
+
const shared = (() => {
|
|
914
|
+
try {
|
|
915
|
+
return readdirSync2(join5(cwd, project, "xcshareddata", "xcschemes")).filter((f) => f.endsWith(".xcscheme")).map((f) => f.replace(/\.xcscheme$/, ""));
|
|
916
|
+
} catch {
|
|
917
|
+
return [];
|
|
918
|
+
}
|
|
919
|
+
})();
|
|
920
|
+
if (shared.length === 1) return shared[0];
|
|
921
|
+
if (shared.length > 1) return null;
|
|
922
|
+
const pbx = readText(join5(cwd, project, "project.pbxproj"));
|
|
923
|
+
if (!pbx) return null;
|
|
924
|
+
const apps = pbx.split("isa = PBXNativeTarget;").slice(1).map((block) => block.slice(0, block.indexOf("};"))).filter((block) => block.includes('productType = "com.apple.product-type.application";')).map((block) => /\bname = ("?)([^";]+)\1;/.exec(block)?.[2]?.trim()).filter((name) => Boolean(name));
|
|
925
|
+
return apps.length === 1 ? apps[0] : null;
|
|
926
|
+
}
|
|
927
|
+
function shellWord(word) {
|
|
928
|
+
return /^[A-Za-z0-9._/-]+$/.test(word) ? word : `'${word.replace(/'/g, `'\\''`)}'`;
|
|
929
|
+
}
|
|
930
|
+
function nativeIosDefaults(cwd) {
|
|
931
|
+
const projects = xcodeProjects(cwd);
|
|
932
|
+
if (projects.length !== 1) return {};
|
|
933
|
+
const project = projects[0];
|
|
934
|
+
const slash = project.lastIndexOf("/");
|
|
935
|
+
const dir = slash === -1 ? "" : project.slice(0, slash);
|
|
936
|
+
const base = project.slice(slash + 1).replace(/\.xcodeproj$/, "");
|
|
937
|
+
const derived = dir && SAFE_SEGMENT.test(dir) ? `${dir}/build` : "build";
|
|
938
|
+
const artifact = `${derived}/Build/Products/Debug-iphonesimulator/*.app`;
|
|
939
|
+
const scheme = xcodeScheme(cwd, project);
|
|
940
|
+
if (!scheme) return { artifact };
|
|
941
|
+
const pods = isFile(join5(cwd, dir, "Podfile"));
|
|
942
|
+
if (pods && isFile(join5(cwd, "gradlew"))) return { artifact };
|
|
943
|
+
const podLeg = pods ? dir ? `cd ${shellWord(dir)} && pod install && cd .. && ` : "pod install && " : "";
|
|
944
|
+
const target = pods ? `-workspace ${shellWord(dir ? `${dir}/${base}.xcworkspace` : `${base}.xcworkspace`)}` : `-project ${shellWord(project)}`;
|
|
945
|
+
return {
|
|
946
|
+
buildCommand: `${podLeg}xcodebuild ${target} -scheme ${shellWord(scheme)} -configuration Debug -sdk iphonesimulator -derivedDataPath ${shellWord(derived)} CODE_SIGNING_ALLOWED=NO`,
|
|
947
|
+
artifact
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
var GRADLE_LOGIC_DIRS = /* @__PURE__ */ new Set(["buildSrc", "build-logic", "gradle"]);
|
|
951
|
+
var APPLIES_ANDROID_APP = /(?:com\.android\.application|libs\.plugins\.android\.?application)\b(?![^\n]*\bapply\s*\(?\s*false)/i;
|
|
952
|
+
function androidAppModules(root) {
|
|
953
|
+
return topLevelDirs(root).filter((dir) => {
|
|
954
|
+
if (!SAFE_SEGMENT.test(dir) || GRADLE_LOGIC_DIRS.has(dir)) return false;
|
|
955
|
+
const script = readText(join5(root, dir, "build.gradle.kts")) ?? readText(join5(root, dir, "build.gradle"));
|
|
956
|
+
return script !== null && APPLIES_ANDROID_APP.test(script);
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
function nativeAndroidDefaults(cwd, production) {
|
|
960
|
+
if (!isFile(join5(cwd, "gradlew"))) {
|
|
961
|
+
if (!isFile(join5(cwd, "android", "gradlew"))) return {};
|
|
962
|
+
const inAndroid = androidAppModules(join5(cwd, "android"));
|
|
963
|
+
const m2 = inAndroid.length === 1 ? inAndroid[0] : "app";
|
|
964
|
+
return production ? {
|
|
965
|
+
buildCommand: "cd android && ./gradlew bundleRelease",
|
|
966
|
+
artifact: `android/${m2}/build/outputs/bundle/release/*.aab`
|
|
967
|
+
} : {
|
|
968
|
+
buildCommand: "cd android && ./gradlew assembleDebug",
|
|
969
|
+
artifact: `android/${m2}/build/outputs/apk/debug/*.apk`
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
const modules = androidAppModules(cwd);
|
|
973
|
+
if (modules.length !== 1) {
|
|
974
|
+
return {};
|
|
975
|
+
}
|
|
976
|
+
const m = modules[0];
|
|
977
|
+
return production ? {
|
|
978
|
+
buildCommand: `./gradlew :${m}:bundleRelease`,
|
|
979
|
+
artifact: `${m}/build/outputs/bundle/release/*.aab`
|
|
980
|
+
} : {
|
|
981
|
+
buildCommand: `./gradlew :${m}:assembleDebug`,
|
|
982
|
+
artifact: `${m}/build/outputs/apk/debug/*.apk`
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
var FLUTTER_ANDROID_ARTIFACT = "build/app/outputs/flutter-apk/app-debug.apk";
|
|
986
|
+
var FLUTTER_IOS_ARTIFACT = "build/ios/iphonesimulator/Runner.app";
|
|
987
|
+
var FLUTTER_ANDROID_PRODUCTION_ARTIFACT = "build/app/outputs/bundle/release/app-release.aab";
|
|
988
|
+
function webBuildCommand(cwd) {
|
|
989
|
+
const scripts = readJson(cwd, "package.json")?.scripts ?? {};
|
|
990
|
+
if (typeof scripts.build !== "string") return null;
|
|
991
|
+
const install = installCommand(cwd);
|
|
992
|
+
if (install.startsWith("yarn ")) return "yarn build";
|
|
993
|
+
if (install.startsWith("pnpm ")) return "pnpm build";
|
|
994
|
+
return "npm run build";
|
|
995
|
+
}
|
|
996
|
+
function gradleWrapperVersion(root) {
|
|
997
|
+
const props = readText(join5(root, "gradle", "wrapper", "gradle-wrapper.properties"));
|
|
998
|
+
const m = props && /distributionUrl=.*gradle-(\d+)\.(\d+)/.exec(props);
|
|
999
|
+
return m ? [Number(m[1]), Number(m[2])] : null;
|
|
1000
|
+
}
|
|
1001
|
+
function dependencyMajor(cwd, name) {
|
|
1002
|
+
const pkg = readJson(cwd, "package.json");
|
|
1003
|
+
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
1004
|
+
const range = deps[name];
|
|
1005
|
+
const m = typeof range === "string" ? /(\d+)/.exec(range) : null;
|
|
1006
|
+
return m ? Number(m[1]) : null;
|
|
1007
|
+
}
|
|
1008
|
+
function capacitorDefaults(cwd, platform, production) {
|
|
1009
|
+
const web = webBuildCommand(cwd);
|
|
1010
|
+
const prefix = `${installCommand(cwd)} && ${web ? `${web} && ` : ""}npx cap sync ${platform} && `;
|
|
1011
|
+
if (platform === "android") {
|
|
1012
|
+
const artifact = production ? ANDROID_PRODUCTION_ARTIFACT : ANDROID_ARTIFACT;
|
|
1013
|
+
if (!nativeDirShipped(cwd, "android") || !isFile(join5(cwd, "android", "gradlew"))) {
|
|
1014
|
+
return { artifact };
|
|
1015
|
+
}
|
|
1016
|
+
const major = dependencyMajor(cwd, "@capacitor/android") ?? dependencyMajor(cwd, "@capacitor/core");
|
|
1017
|
+
const wrapper = gradleWrapperVersion(join5(cwd, "android"));
|
|
1018
|
+
const on21 = wrapper ? wrapper[0] > 8 || wrapper[0] === 8 && wrapper[1] >= 5 : major !== null && major >= 7;
|
|
1019
|
+
const java = on21 ? "JAVA_HOME=${JAVA_HOME_21:-$JAVA_HOME} " : "";
|
|
1020
|
+
return {
|
|
1021
|
+
buildCommand: `${prefix}cd android && ${java}./gradlew ${production ? "bundleRelease" : "assembleDebug"}`,
|
|
1022
|
+
artifact
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
if (production) return { artifact: IOS_PRODUCTION_ARTIFACT };
|
|
1026
|
+
const project = "ios/App/App.xcodeproj";
|
|
1027
|
+
const scheme = nativeDirShipped(cwd, "ios") ? xcodeScheme(cwd, project) : null;
|
|
1028
|
+
if (!scheme) return { artifact: IOS_ARTIFACT };
|
|
1029
|
+
const target = isFile(join5(cwd, "ios", "App", "Podfile")) ? "-workspace ios/App/App.xcworkspace" : `-project ${project}`;
|
|
1030
|
+
return {
|
|
1031
|
+
buildCommand: `${prefix}xcodebuild ${target} -scheme ${shellWord(scheme)} -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build CODE_SIGNING_ALLOWED=NO`,
|
|
1032
|
+
artifact: IOS_ARTIFACT
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
865
1035
|
function installCommand(cwd) {
|
|
866
1036
|
const hit = JS_LOCKFILE_INSTALLS.find(([lockfile]) => existsSync3(join5(cwd, lockfile)));
|
|
867
1037
|
return hit ? hit[1] : "npm install";
|
|
@@ -914,12 +1084,17 @@ function defaultsFor(cwd, platform, evaluatedIosName) {
|
|
|
914
1084
|
const flavor = detectFlavor(cwd);
|
|
915
1085
|
if (platform === "roku") return flavor === "roku" ? rokuDefaults(cwd) : {};
|
|
916
1086
|
if (flavor === "unknown" || flavor === "roku") return {};
|
|
1087
|
+
if (flavor === "flutter" || flavor === "capacitor" || flavor === "native") {
|
|
1088
|
+
if (platform === "tvos" || platform === "androidtv") return {};
|
|
1089
|
+
return otherFrameworkDefaults(cwd, flavor, platform, false);
|
|
1090
|
+
}
|
|
917
1091
|
const metroCommand = flavor === "expo" ? "npx expo start" : "yarn start";
|
|
918
1092
|
if (platform === "tvos" || platform === "androidtv") return { metroCommand };
|
|
919
1093
|
if (platform === "android") {
|
|
920
1094
|
const managed = isManagedExpo(cwd, "android");
|
|
1095
|
+
const wrapper = isFile(join5(cwd, "android", "gradlew"));
|
|
921
1096
|
return {
|
|
922
|
-
buildCommand: managed ? managedExpoAndroidCommand(cwd) : `${bareInstallPrefix(cwd, "android")}cd android && ./gradlew assembleDebug
|
|
1097
|
+
buildCommand: managed ? managedExpoAndroidCommand(cwd) : wrapper ? `${bareInstallPrefix(cwd, "android")}cd android && ./gradlew assembleDebug` : void 0,
|
|
923
1098
|
artifact: ANDROID_ARTIFACT,
|
|
924
1099
|
metroCommand
|
|
925
1100
|
};
|
|
@@ -939,6 +1114,29 @@ function defaultsFor(cwd, platform, evaluatedIosName) {
|
|
|
939
1114
|
metroCommand
|
|
940
1115
|
};
|
|
941
1116
|
}
|
|
1117
|
+
function otherFrameworkDefaults(cwd, flavor, platform, production) {
|
|
1118
|
+
if (flavor === "flutter") {
|
|
1119
|
+
if (!existsSync3(join5(cwd, platform))) return {};
|
|
1120
|
+
if (platform === "android") {
|
|
1121
|
+
return production ? {
|
|
1122
|
+
buildCommand: "flutter build appbundle --release",
|
|
1123
|
+
artifact: FLUTTER_ANDROID_PRODUCTION_ARTIFACT
|
|
1124
|
+
} : { buildCommand: "flutter build apk --debug", artifact: FLUTTER_ANDROID_ARTIFACT };
|
|
1125
|
+
}
|
|
1126
|
+
return production ? {} : { buildCommand: "flutter build ios --simulator --debug", artifact: FLUTTER_IOS_ARTIFACT };
|
|
1127
|
+
}
|
|
1128
|
+
if (flavor === "capacitor") return capacitorDefaults(cwd, platform, production);
|
|
1129
|
+
if (platform === "android") return nativeAndroidDefaults(cwd, production);
|
|
1130
|
+
if (!production) return nativeIosDefaults(cwd);
|
|
1131
|
+
const ws = iosWorkspaceName(cwd);
|
|
1132
|
+
const scheme = ws ? xcodeScheme(cwd, `ios/${ws}.xcodeproj`) : null;
|
|
1133
|
+
if (!ws) return {};
|
|
1134
|
+
if (!scheme) return { artifact: IOS_PRODUCTION_ARTIFACT };
|
|
1135
|
+
return {
|
|
1136
|
+
buildCommand: `${bareInstallPrefix(cwd, "ios")}${iosArchiveExportCommand(ws, scheme)}`,
|
|
1137
|
+
artifact: IOS_PRODUCTION_ARTIFACT
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
942
1140
|
var ANDROID_PRODUCTION_ARTIFACT = "android/app/build/outputs/bundle/release/*.aab";
|
|
943
1141
|
function androidProductionCommand(cwd) {
|
|
944
1142
|
return `${bareInstallPrefix(cwd, "android")}cd android && ./gradlew bundleRelease`;
|
|
@@ -965,9 +1163,9 @@ function managedExpoIosName(cwd, evaluatedName) {
|
|
|
965
1163
|
if (typeof name !== "string") return null;
|
|
966
1164
|
return expoSanitizedName(name) || null;
|
|
967
1165
|
}
|
|
968
|
-
function iosArchiveExportCommand(ws) {
|
|
1166
|
+
function iosArchiveExportCommand(ws, scheme = ws) {
|
|
969
1167
|
const archivePath = `ios/build/${ws}.xcarchive`;
|
|
970
|
-
const archiveCmd = `xcodebuild archive -workspace ios/${ws}.xcworkspace -scheme ${
|
|
1168
|
+
const archiveCmd = `xcodebuild archive -workspace ios/${ws}.xcworkspace -scheme ${shellWord(scheme)} -configuration Release -archivePath ${archivePath} CODE_SIGN_STYLE=Manual \${VIBEVIEW_DEVELOPMENT_TEAM:+"DEVELOPMENT_TEAM=$VIBEVIEW_DEVELOPMENT_TEAM"} \${VIBEVIEW_PROVISIONING_PROFILE:+"PROVISIONING_PROFILE_SPECIFIER=$VIBEVIEW_PROVISIONING_PROFILE"} \${VIBEVIEW_CODE_SIGN_IDENTITY:+"CODE_SIGN_IDENTITY=$VIBEVIEW_CODE_SIGN_IDENTITY"}`;
|
|
971
1169
|
const exportCmd = `xcodebuild -exportArchive -archivePath ${archivePath} -exportPath ios/build -exportOptionsPlist "$VIBEVIEW_EXPORT_OPTIONS_PLIST"`;
|
|
972
1170
|
return `${archiveCmd} && ${exportCmd}`;
|
|
973
1171
|
}
|
|
@@ -975,9 +1173,13 @@ function managedExpoIosProductionCommand(cwd, name) {
|
|
|
975
1173
|
return `${installCommand(cwd)} && npx expo prebuild --platform ios && ` + iosArchiveExportCommand(name);
|
|
976
1174
|
}
|
|
977
1175
|
function productionDefaultsFor(cwd, platform, evaluatedIosName) {
|
|
1176
|
+
const flavor = detectFlavor(cwd);
|
|
1177
|
+
if ((flavor === "flutter" || flavor === "capacitor" || flavor === "native") && platform !== "tvos" && platform !== "roku") {
|
|
1178
|
+
return otherFrameworkDefaults(cwd, flavor, platform === "ios" ? "ios" : "android", true);
|
|
1179
|
+
}
|
|
978
1180
|
if (platform === "android" || platform === "androidtv") {
|
|
979
1181
|
return {
|
|
980
|
-
buildCommand: isManagedExpo(cwd, "android") ? managedExpoAndroidProductionCommand(cwd) : androidProductionCommand(cwd),
|
|
1182
|
+
buildCommand: isManagedExpo(cwd, "android") ? managedExpoAndroidProductionCommand(cwd) : flavor !== "bare-rn" || isFile(join5(cwd, "android", "gradlew")) ? androidProductionCommand(cwd) : void 0,
|
|
981
1183
|
artifact: ANDROID_PRODUCTION_ARTIFACT
|
|
982
1184
|
};
|
|
983
1185
|
}
|
package/dist/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
runAgentVerb,
|
|
31
31
|
runStepsNotChecked,
|
|
32
32
|
stepsNotCheckedLabel
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-4WIEZBTP.js";
|
|
34
34
|
import {
|
|
35
35
|
ApiClient,
|
|
36
36
|
ApiStatusError,
|
|
@@ -657,11 +657,13 @@ function formatToolCall(tc) {
|
|
|
657
657
|
function runStartedLine(evt) {
|
|
658
658
|
const steps = `${evt.total_steps} step(s)`;
|
|
659
659
|
const model = chalk2.bold(evt.model || "default");
|
|
660
|
-
if (evt.mode === "replay")
|
|
660
|
+
if (evt.mode === "replay") {
|
|
661
|
+
return `${chalk2.cyan("\u25B6")} No AI run started \u2014 ${steps}, replays only (no AI)`;
|
|
662
|
+
}
|
|
661
663
|
if (evt.mode === "hybrid") {
|
|
662
|
-
return `${chalk2.cyan("\u25B6")}
|
|
664
|
+
return `${chalk2.cyan("\u25B6")} Smart run started \u2014 ${steps}, replays recorded and learned paths, AI where needed (model: ${model})`;
|
|
663
665
|
}
|
|
664
|
-
return `${chalk2.cyan("\u25B6")} AI
|
|
666
|
+
return `${chalk2.cyan("\u25B6")} AI only run started \u2014 ${steps}, model: ${model}`;
|
|
665
667
|
}
|
|
666
668
|
function displayProgressEvent(evt, ctx) {
|
|
667
669
|
const { spinner } = ctx;
|
|
@@ -775,6 +777,37 @@ function parseRunPlatform(value) {
|
|
|
775
777
|
}
|
|
776
778
|
return value;
|
|
777
779
|
}
|
|
780
|
+
var RUN_MODES = ["hybrid", "ai", "replay"];
|
|
781
|
+
function parseRunMode(value) {
|
|
782
|
+
if (value === void 0) return void 0;
|
|
783
|
+
if (!RUN_MODES.includes(value)) {
|
|
784
|
+
throw new Error(
|
|
785
|
+
`--mode must be one of ${RUN_MODES.join(", ")} (Smart, AI only, No AI), got "${value}"`
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
return value;
|
|
789
|
+
}
|
|
790
|
+
var MAX_TIMEOUT_S = 7200;
|
|
791
|
+
var POSITIVE_INT = /^[1-9][0-9]*$/;
|
|
792
|
+
function parseTimeoutSeconds(value) {
|
|
793
|
+
const n = POSITIVE_INT.test(value.trim()) ? Number(value.trim()) : NaN;
|
|
794
|
+
if (!Number.isSafeInteger(n) || n < 1 || n > MAX_TIMEOUT_S) {
|
|
795
|
+
throw new Error(
|
|
796
|
+
`--timeout must be a whole number of seconds from 1 to ${MAX_TIMEOUT_S}, got "${value}"`
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
return n;
|
|
800
|
+
}
|
|
801
|
+
function parseBuildId(value) {
|
|
802
|
+
if (value === void 0) return void 0;
|
|
803
|
+
const n = POSITIVE_INT.test(value.trim()) ? Number(value.trim()) : NaN;
|
|
804
|
+
if (!Number.isSafeInteger(n)) {
|
|
805
|
+
throw new Error(
|
|
806
|
+
`--build must be a build id (a positive whole number, see \`vibeview list-builds\`), got "${value}"`
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
return n;
|
|
810
|
+
}
|
|
778
811
|
function refusePlatformWithApp(options) {
|
|
779
812
|
if (options.platform && options.app) {
|
|
780
813
|
throw new Error(
|
|
@@ -4079,11 +4112,27 @@ function resolveMetadata(opts) {
|
|
|
4079
4112
|
process.exit(2);
|
|
4080
4113
|
}
|
|
4081
4114
|
}
|
|
4115
|
+
function parseRunFlags(opts) {
|
|
4116
|
+
try {
|
|
4117
|
+
return {
|
|
4118
|
+
mode: parseRunMode(opts.mode),
|
|
4119
|
+
timeout: parseTimeoutSeconds(opts.timeout),
|
|
4120
|
+
build: parseBuildId(opts.build)
|
|
4121
|
+
};
|
|
4122
|
+
} catch (err) {
|
|
4123
|
+
process.stderr.write(`error: ${err.message}
|
|
4124
|
+
`);
|
|
4125
|
+
process.exit(2);
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4082
4128
|
var program = new Command();
|
|
4083
4129
|
program.name("vibeview").description(
|
|
4084
4130
|
"VibeView CLI -- run your app on cloud iOS simulators, Android emulators, Apple TV, Android TV and Roku devices: a React Native dev loop (dev), device control for you or your coding agent (ui-tree, tap, mcp), and tests from CI (run-test, run-suites)"
|
|
4085
4131
|
).version(pkg.version);
|
|
4086
|
-
program.command("run-suite <suite-id>").description("Run all test cases in a suite").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-case breakdown", false).option("--timeout <seconds>", "Max wait time in seconds (queue wait plus execution)", "1800").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option(
|
|
4132
|
+
program.command("run-suite <suite-id>").description("Run all test cases in a suite").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-case breakdown", false).option("--timeout <seconds>", "Max wait time in seconds (queue wait plus execution)", "1800").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option(
|
|
4133
|
+
"--mode <mode>",
|
|
4134
|
+
"Execution mode: hybrid (Smart, the default), ai (AI only), replay (No AI)"
|
|
4135
|
+
).option(
|
|
4087
4136
|
"--device-category <category>",
|
|
4088
4137
|
"Device category: phone, tablet, tv (default: tv for a TV platform, else phone)",
|
|
4089
4138
|
parseDeviceCategory
|
|
@@ -4112,12 +4161,12 @@ program.command("run-suite <suite-id>").description("Run all test cases in a sui
|
|
|
4112
4161
|
`);
|
|
4113
4162
|
process.exit(2);
|
|
4114
4163
|
}
|
|
4164
|
+
const runFlags = parseRunFlags(opts);
|
|
4115
4165
|
const metadata = resolveMetadata(opts);
|
|
4116
4166
|
return runSuite(suiteId, {
|
|
4117
4167
|
...opts,
|
|
4118
4168
|
metadata,
|
|
4119
|
-
|
|
4120
|
-
build: opts.build ? parseInt(opts.build, 10) : void 0
|
|
4169
|
+
...runFlags
|
|
4121
4170
|
});
|
|
4122
4171
|
});
|
|
4123
4172
|
program.command("run-suites").description("Run all suites matching a tag filter").requiredOption(
|
|
@@ -4128,7 +4177,10 @@ program.command("run-suites").description("Run all suites matching a tag filter"
|
|
|
4128
4177
|
).option("--match <mode>", "any (default) or all", "any").option(
|
|
4129
4178
|
"--platform <platform>",
|
|
4130
4179
|
"Filter matched suites to one platform: ios, android, tvos, androidtv, roku (default: no filter)"
|
|
4131
|
-
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-suite progress", false).option("--timeout <seconds>", "Per-suite timeout, capped at 2 hours total", "1800").option("--commit-sha <sha>", "Git commit SHA propagated to every SuiteRun").option("--model <model>", "AI model").option("--provider <provider>", "AI provider").option(
|
|
4180
|
+
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-suite progress", false).option("--timeout <seconds>", "Per-suite timeout, capped at 2 hours total", "1800").option("--commit-sha <sha>", "Git commit SHA propagated to every SuiteRun").option("--model <model>", "AI model").option("--provider <provider>", "AI provider").option(
|
|
4181
|
+
"--mode <mode>",
|
|
4182
|
+
"Execution mode: hybrid (Smart, the default), ai (AI only), replay (No AI)"
|
|
4183
|
+
).option(
|
|
4132
4184
|
"--device-category <category>",
|
|
4133
4185
|
"Device category: phone, tablet, tv (default: tv for a TV platform, else phone)",
|
|
4134
4186
|
parseDeviceCategory
|
|
@@ -4150,15 +4202,18 @@ program.command("run-suites").description("Run all suites matching a tag filter"
|
|
|
4150
4202
|
`);
|
|
4151
4203
|
process.exit(2);
|
|
4152
4204
|
}
|
|
4205
|
+
const runFlags = parseRunFlags(opts);
|
|
4153
4206
|
const metadata = resolveMetadata(opts);
|
|
4154
4207
|
return runSuites({
|
|
4155
4208
|
...opts,
|
|
4156
4209
|
metadata,
|
|
4157
|
-
|
|
4158
|
-
build: opts.build ? parseInt(opts.build, 10) : void 0
|
|
4210
|
+
...runFlags
|
|
4159
4211
|
});
|
|
4160
4212
|
});
|
|
4161
|
-
program.command("run-test <test-id>").description("Run a single test case").option("--token <token>", "API token").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show step details", false).option("--timeout <seconds>", "Max wait time in seconds (queue wait plus execution)", "1800").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option(
|
|
4213
|
+
program.command("run-test <test-id>").description("Run a single test case").option("--token <token>", "API token").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show step details", false).option("--timeout <seconds>", "Max wait time in seconds (queue wait plus execution)", "1800").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option(
|
|
4214
|
+
"--mode <mode>",
|
|
4215
|
+
"Execution mode: hybrid (Smart, the default), ai (AI only), replay (No AI)"
|
|
4216
|
+
).option(
|
|
4162
4217
|
"--device-category <category>",
|
|
4163
4218
|
"Device category: phone, tablet, tv (default: tv for a TV platform, else phone)",
|
|
4164
4219
|
parseDeviceCategory
|
|
@@ -4187,12 +4242,12 @@ program.command("run-test <test-id>").description("Run a single test case").opti
|
|
|
4187
4242
|
`);
|
|
4188
4243
|
process.exit(2);
|
|
4189
4244
|
}
|
|
4245
|
+
const runFlags = parseRunFlags(opts);
|
|
4190
4246
|
const metadata = resolveMetadata(opts);
|
|
4191
4247
|
return runTest(testId, {
|
|
4192
4248
|
...opts,
|
|
4193
4249
|
metadata,
|
|
4194
|
-
|
|
4195
|
-
build: opts.build ? parseInt(opts.build, 10) : void 0
|
|
4250
|
+
...runFlags
|
|
4196
4251
|
});
|
|
4197
4252
|
});
|
|
4198
4253
|
program.command("list-suites").description("List available test suites").option("--token <token>", "API token").option("--output <format>", "Output format: human, json", "human").option(
|
|
@@ -4406,7 +4461,7 @@ program.command("dev-reload").description(
|
|
|
4406
4461
|
program.command("mcp").description(
|
|
4407
4462
|
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
|
|
4408
4463
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
4409
|
-
const { runMcpServer } = await import("./server-
|
|
4464
|
+
const { runMcpServer } = await import("./server-VPNWTXOY.js");
|
|
4410
4465
|
await runMcpServer({ ...opts, version: pkg.version });
|
|
4411
4466
|
});
|
|
4412
4467
|
program.command("agent-setup").description(
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
renderEnvelope,
|
|
31
31
|
resolveMetro,
|
|
32
32
|
startDevSession
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-4WIEZBTP.js";
|
|
34
34
|
import {
|
|
35
35
|
ApiClient,
|
|
36
36
|
NotFoundError,
|
|
@@ -48,7 +48,7 @@ var SESSION_ID_PROPERTY = {
|
|
|
48
48
|
};
|
|
49
49
|
var AI_USE_PROPERTY = {
|
|
50
50
|
type: "string",
|
|
51
|
-
description: "smart (default): replays
|
|
51
|
+
description: "smart (default): replays recorded and learned paths, and uses AI only where the screen changed or a step has no path yet; no_ai: replays only, no AI and no usage credit (a step with no path is not checked); ai_only: the AI does every step.",
|
|
52
52
|
enum: ["smart", "no_ai", "ai_only"]
|
|
53
53
|
};
|
|
54
54
|
var AI_USE_MODE = { smart: "hybrid", no_ai: "replay", ai_only: "ai" };
|
|
@@ -419,6 +419,9 @@ async function handleRun(kind, args, deps) {
|
|
|
419
419
|
return errorResult(`error: ${testToolErrorMessage(err)}`);
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
|
+
function withoutNotRunPrefix(reason) {
|
|
423
|
+
return reason ? reason.replace(/^Not run:\s*/, "") : reason;
|
|
424
|
+
}
|
|
422
425
|
function notCheckedNote(status, n) {
|
|
423
426
|
if (status !== "passed" || typeof n !== "number" || n <= 0) return "";
|
|
424
427
|
return ` \u2014 ${n} step${n === 1 ? "" : "s"} not checked (no recorded actions; not verified)`;
|
|
@@ -461,12 +464,25 @@ function formatRunForAgent(run) {
|
|
|
461
464
|
lines.push(head);
|
|
462
465
|
for (const r of lane.runs ?? []) {
|
|
463
466
|
const count = r.kind === "group" && typeof r.tests_total === "number" ? ` (${r.tests_passed ?? 0}/${r.tests_total} tests passed${testsNotChecked(r.tests_not_checked)}${quarantinedCount(rowQuarantined(r))})` : "";
|
|
464
|
-
const
|
|
467
|
+
const cell = r.kind === "test" ? r.tests?.[0] ?? {} : {};
|
|
468
|
+
const q = r.kind === "test" && quarantinedFailure(cell) ? " (quarantined)" : "";
|
|
469
|
+
const why = withoutNotRunPrefix(r.error_message || cell.not_run_reason);
|
|
470
|
+
const unchecked = r.kind === "test" ? notCheckedNote(outcomeOf(cell), cell.steps_not_checked) : "";
|
|
465
471
|
lines.push(
|
|
466
|
-
` ${r.name}: ${agentStatus(r)}${q}${count}${
|
|
472
|
+
` ${r.name}: ${agentStatus(r)}${q}${count}${why ? ` \u2014 ${why}` : ""}${unchecked}`
|
|
467
473
|
);
|
|
468
474
|
if (r.kind !== "group") continue;
|
|
469
475
|
for (const t of r.tests ?? []) {
|
|
476
|
+
if (t.status === "not_run") {
|
|
477
|
+
const reason = withoutNotRunPrefix(t.not_run_reason || t.error_message);
|
|
478
|
+
lines.push(` ${t.name}: not run${reason ? ` \u2014 ${reason}` : ""}`);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
const note = notCheckedNote(outcomeOf(t), t.steps_not_checked);
|
|
482
|
+
if (note) {
|
|
483
|
+
lines.push(` ${t.name}: ${agentStatus(t)}${note}`);
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
470
486
|
if (t.status !== "failed" && t.status !== "error") continue;
|
|
471
487
|
const tq = quarantinedFailure(t) ? " (quarantined)" : "";
|
|
472
488
|
lines.push(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeview",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.0",
|
|
4
4
|
"description": "VibeView CLI -- run your app on cloud iOS simulators, Android emulators, Apple TV, Android TV and Roku devices: a React Native dev loop, device control for you or your coding agent (CLI or MCP), and tests from CI",
|
|
5
5
|
"mcpName": "io.github.vibeview/vibeview",
|
|
6
6
|
"type": "module",
|