mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-10-01 23:54:35 +08:00
Reword user-facing errors, app info and Launchpad copy in plain written English
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
8682c98aaa
commit
090df08195
@@ -276,7 +276,7 @@ enum GuestIrisinInstaller {
|
||||
let packageData = try fetch(release.url, reportDownload: true)
|
||||
let digest = SHA256.hash(data: packageData).map { String(format: "%02x", $0) }.joined()
|
||||
guard digest == release.digest else {
|
||||
throw GuestAPIError.operationFailed("Irisin release asset SHA-256 mismatch")
|
||||
throw GuestAPIError.operationFailed("The download is damaged. Try again.")
|
||||
}
|
||||
try packageData.write(to: package, options: .atomic)
|
||||
tag = release.tag
|
||||
|
||||
@@ -19,7 +19,7 @@ extension GuestAPI {
|
||||
return container + "/File Provider Storage"
|
||||
}
|
||||
}
|
||||
throw GuestAPIError.operationFailed("The Files app storage container was not found")
|
||||
throw GuestAPIError.operationFailed("Files app storage not found. Open Files once and try again.")
|
||||
}
|
||||
|
||||
/// Moves an uploaded file into On My iPhone › vphone-drop. Items there are
|
||||
|
||||
@@ -78,6 +78,7 @@ enum GuestAPI {
|
||||
"screenshot",
|
||||
"device_info",
|
||||
"display",
|
||||
"display_orientation",
|
||||
"audio",
|
||||
"input_gestures",
|
||||
"ui_inspection",
|
||||
@@ -346,7 +347,7 @@ enum GuestAPI {
|
||||
let next = cache + ".next"
|
||||
let data = try Data(contentsOf: URL(fileURLWithPath: next), options: .mappedIfSafe)
|
||||
let actual = sha256Hex(data)
|
||||
guard actual == expected else { throw GuestAPIError.invalidRequest("Update hash mismatch") }
|
||||
guard actual == expected else { throw GuestAPIError.invalidRequest("The update is damaged. Try again.") }
|
||||
guard chmod(next, 0o755) == 0 else {
|
||||
throw GuestAPIError.operationFailed("Could not make update executable")
|
||||
}
|
||||
|
||||
+1
-1
@@ -381,7 +381,7 @@ struct VPhoneCustomFirmwareInstaller {
|
||||
let root = try work.directory.mountedVolume(name)
|
||||
let source = try root.mountedFrom()
|
||||
guard source == "/dev/\(device)" else {
|
||||
throw ValidationError("Expected /dev/\(device) on \(work.file(name).path), found \(source). Try again.")
|
||||
throw ValidationError("The VM disk image did not attach correctly. Try again.")
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
+3
-3
@@ -33,7 +33,7 @@ private enum VPhoneVirtualMachineCreationError: Error, CustomStringConvertible {
|
||||
case let .udidECIDMismatch(udid, ecid):
|
||||
"The UDID and ECID in the device identity file do not match (\(udid), 0x\(ecid)). Run vm create again to regenerate it."
|
||||
case .recoveryTimeout:
|
||||
"Timed out waiting for the device to enter recovery mode."
|
||||
"The device did not enter recovery mode in time. Run vm create again."
|
||||
// No exit code any more: the restore backend is in this process, so
|
||||
// what a failure carries is the reason it gave.
|
||||
case let .restoreUpdateFailed(reason):
|
||||
@@ -43,9 +43,9 @@ private enum VPhoneVirtualMachineCreationError: Error, CustomStringConvertible {
|
||||
case .bootAnalysisPanic:
|
||||
"Boot check failed: the guest panicked. Run vm create again."
|
||||
case let .bootAnalysisExited(code):
|
||||
"Boot check ended before vphoned connected (exit code \(code))."
|
||||
"Boot check ended before the guest connected (exit code \(code)). Run vm create again."
|
||||
case .bootAnalysisTimeout:
|
||||
"Boot check timed out waiting for vphoned."
|
||||
"Boot check timed out waiting for the guest. Run vm create again."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -77,19 +77,19 @@ extension VPhoneRestoreBackendError: CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .ecidEmpty:
|
||||
"ECID is empty"
|
||||
"The ECID is empty. Enter up to 16 hexadecimal digits."
|
||||
case let .ecidInvalid(value):
|
||||
"Invalid ECID: \(value)"
|
||||
"\(value) is not a valid ECID. Enter up to 16 hexadecimal digits."
|
||||
case let .ecidTooLarge(value):
|
||||
"ECID is too long: \(value). Enter up to 16 hexadecimal digits."
|
||||
case let .noRestoreDirectory(dir):
|
||||
"No iPhone*_Restore directory found in \(dir.path)"
|
||||
"No restore folder was found in \(dir.path). Prepare the firmware, then try again."
|
||||
case .multipleRestoreDirectories:
|
||||
"Multiple iPhone*_Restore directories found; keep only one active restore tree"
|
||||
"More than one restore folder was found. Keep only one, then try again."
|
||||
case let .recoveryProbeTimedOut(mode):
|
||||
"Timed out waiting for \(mode) endpoint"
|
||||
"The device did not enter \(mode) mode in time. Check the connection, then try again."
|
||||
case .recoveryDeviceUnreadable:
|
||||
"The recovery endpoint stopped answering while it was being read"
|
||||
"The device disconnected while it was being read. Reconnect it, then try again."
|
||||
case .restoreAlreadyRunning:
|
||||
"Another restore is already running. Wait for it to finish, then try again."
|
||||
case let .restoreDirectoryUnusable(dir):
|
||||
|
||||
+3
-3
@@ -62,7 +62,7 @@ final class VPhoneAppBrowserModel {
|
||||
case let .uninstalling(name): String(localized: "Uninstalling \(name)…", bundle: VPhoneLocalization.bundle)
|
||||
case let .installing(file): String(localized: "Installing \(file)…", bundle: VPhoneLocalization.bundle)
|
||||
case .openingURL: String(localized: "Opening URL…", bundle: VPhoneLocalization.bundle)
|
||||
case .repairingNetwork: String(localized: "Repairing network policy…", bundle: VPhoneLocalization.bundle)
|
||||
case .repairingNetwork: String(localized: "Allowing network access…", bundle: VPhoneLocalization.bundle)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,11 +449,11 @@ final class VPhoneAppBrowserModel {
|
||||
status = VPhoneGuestToolStatus(
|
||||
message: allowed
|
||||
? String(localized: "\(name) can use Wi-Fi and cellular data.", bundle: VPhoneLocalization.bundle)
|
||||
: String(localized: "The network policy of \(name) did not change. Try again after relaunching the app.", bundle: VPhoneLocalization.bundle),
|
||||
: String(localized: "Network access for \(name) did not change. Relaunch the app and try again.", bundle: VPhoneLocalization.bundle),
|
||||
isError: !allowed,
|
||||
)
|
||||
} catch {
|
||||
status = failure(String(localized: "Unable to repair the network policy of \(name).", bundle: VPhoneLocalization.bundle), error)
|
||||
status = failure(String(localized: "Unable to allow network access for \(name).", bundle: VPhoneLocalization.bundle), error)
|
||||
}
|
||||
activity = nil
|
||||
}
|
||||
|
||||
+3
-3
@@ -161,10 +161,10 @@ struct VPhoneAppInfoView: View {
|
||||
}
|
||||
|
||||
private func networkSection(_ detail: VPhoneAppDetail) -> some View {
|
||||
VPhoneAppInfoSection(title: "Network Policy", error: detail.networkPolicyError) {
|
||||
VPhoneAppInfoSection(title: "Network Access", error: detail.networkPolicyError) {
|
||||
if let policy = detail.networkPolicy {
|
||||
Label {
|
||||
Text(policy.allowed ? "Wi-Fi and cellular data allowed" : "Network access may be restricted")
|
||||
Text(policy.allowed ? "Wi-Fi and cellular data allowed" : "Network access is restricted")
|
||||
} icon: {
|
||||
Image(systemName: policy.allowed ? "checkmark.circle.fill" : "exclamationmark.triangle.fill")
|
||||
.foregroundStyle(policy.allowed ? .green : .orange)
|
||||
@@ -173,7 +173,7 @@ struct VPhoneAppInfoView: View {
|
||||
if !policy.entries.isEmpty {
|
||||
VPhoneAppInfoGrid(rows: policy.entries.map { ($0.title, $0.value) }, localizeTitles: false)
|
||||
}
|
||||
Button("Repair") { Task { await model.repairNetworkPolicy() } }
|
||||
Button("Allow Network") { Task { await model.repairNetworkPolicy() } }
|
||||
.controlSize(.small)
|
||||
.disabled(policy.allowed || model.isBusy || !model.control.isConnected)
|
||||
.help("Allow Wi-Fi and cellular data for this app")
|
||||
|
||||
@@ -13,11 +13,11 @@ public enum VPhoneAPFSSnapshotError: Error, CustomStringConvertible {
|
||||
case let .prefixLengthMismatch(given, required):
|
||||
"--new-prefix must be exactly \(required) bytes (got \(given))"
|
||||
case let .cannotOpen(url, err):
|
||||
"Could not open \(url.path): \(String(cString: strerror(err)))"
|
||||
"Unable to open \(url.path): \(String(cString: strerror(err))). Check the file and try again."
|
||||
case let .cannotMap(url, err):
|
||||
"Could not map \(url.path): \(String(cString: strerror(err)))"
|
||||
"Unable to read \(url.path): \(String(cString: strerror(err))). Check the file and try again."
|
||||
case let .cannotAccess(url, operation, err):
|
||||
"Could not \(operation) \(url.path): \(String(cString: strerror(err)))"
|
||||
"Unable to \(operation) \(url.path): \(String(cString: strerror(err))). Check the file and try again."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ public enum VPhonePCCGPUDriver {
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case let .invalidBundle(path):
|
||||
"GPU driver bundle is incomplete or has the wrong identifier: \(path.path)"
|
||||
"The GPU driver at \(path.path) is incomplete or invalid. Download the firmware again and retry."
|
||||
case let .wrongPlatformVersion(path, expected, actual):
|
||||
"GPU driver at \(path.path) is for iPhoneOS \(actual), expected cloudOS \(expected)"
|
||||
"The GPU driver at \(path.path) is for iPhoneOS \(actual), but cloudOS \(expected) is required. Choose a matching driver and try again."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ extension VPhoneVirtualMachineManifest.PlatformFusing: ExpressibleByArgument {}
|
||||
public struct VPhoneBootCommand: ParsableCommand {
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "boot",
|
||||
abstract: "Boot a virtual iPhone (PV=3)",
|
||||
abstract: "Boot a virtual iPhone",
|
||||
discussion: """
|
||||
Creates a Virtualization.framework VM with platform version 3 (vphone)
|
||||
and boots it from a manifest plist that describes all paths and hardware.
|
||||
@@ -111,7 +111,7 @@ public struct VPhoneBootCommand: ParsableCommand {
|
||||
existingVariant != "jb"
|
||||
{
|
||||
throw ValidationError(
|
||||
"This VM was created as '\(existingVariant)'. Only JB VMs are supported by this build.",
|
||||
"This VM was created as '\(existingVariant)', which this build does not support. Create a new VM and try again.",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ public enum VPhoneGuestBinaries {
|
||||
switch self {
|
||||
case let .missing(name, searched):
|
||||
"""
|
||||
Guest binary '\(name)' not found. Build the VPhone scheme in VPhone.xcworkspace.
|
||||
\(name) not found. The app bundle is incomplete. Rebuild the VPhone scheme in Xcode.
|
||||
Searched: \(searched.map(\.path).joined(separator: ", "))
|
||||
"""
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ public enum VPhoneLibraryError: Error, Equatable {
|
||||
extension VPhoneLibraryError: CustomStringConvertible, LocalizedError {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case let .notFound(name): "VM '\(name)' not found"
|
||||
case let .alreadyExists(name): "VM '\(name)' already exists"
|
||||
case let .notFound(name): "VM '\(name)' not found. Check the name and try again."
|
||||
case let .alreadyExists(name): "VM '\(name)' already exists. Choose a different name."
|
||||
case let .invalidName(name):
|
||||
"Invalid VM name '\(name)'. Use a name that is not empty, has no '/', and does not start with '.'."
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public struct VPhoneAPIClient: Sendable {
|
||||
throw error
|
||||
}
|
||||
guard (200 ..< 300).contains(response.statusCode), let result = value.result else {
|
||||
throw VPhoneAPIError(code: "protocol", message: "Missing result (HTTP \(response.statusCode))")
|
||||
throw VPhoneAPIError(code: "protocol", message: "The VM returned an incomplete response. Try again.")
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -138,7 +138,7 @@ public struct VPhoneAPIClient: Sendable {
|
||||
let url = try fileURL(guestPath)
|
||||
let (temporary, response) = try await session.download(for: authorized(URLRequest(url: url)))
|
||||
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
|
||||
throw VPhoneAPIError(code: "download", message: "Guest file download failed")
|
||||
throw VPhoneAPIError(code: "download", message: "Unable to download the file from the VM. Try again.")
|
||||
}
|
||||
let destination = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent("vphone-" + UUID().uuidString)
|
||||
@@ -164,7 +164,7 @@ public struct VPhoneAPIClient: Sendable {
|
||||
}
|
||||
guard let response = response as? HTTPURLResponse,
|
||||
response.statusCode == 200, let result = object["result"]
|
||||
else { throw VPhoneAPIError(code: "upload", message: "Guest file upload failed") }
|
||||
else { throw VPhoneAPIError(code: "upload", message: "Unable to upload the file to the VM. Try again.") }
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ public struct VPhoneAPIClient: Sendable {
|
||||
guard guestPath.hasPrefix("/"), !guestPath.contains("\0"),
|
||||
var components = URLComponents(url: baseURL.appending(path: "v1/files/content"),
|
||||
resolvingAgainstBaseURL: false)
|
||||
else { throw VPhoneAPIError(code: "path", message: "Guest path must be absolute") }
|
||||
else { throw VPhoneAPIError(code: "path", message: "The VM file path must start with '/'. Enter an absolute path.") }
|
||||
components.queryItems = [URLQueryItem(name: "path", value: guestPath)]
|
||||
if let mode {
|
||||
components.queryItems?.append(URLQueryItem(name: "mode", value: mode))
|
||||
|
||||
@@ -581,7 +581,7 @@ struct VPhoneLaunchpadControlCommands {
|
||||
onLine: emit,
|
||||
)
|
||||
guard status == 0 else {
|
||||
throw VPhoneLaunchpadError("cfw install exited with status \(status).")
|
||||
throw VPhoneLaunchpadError("Unable to install custom firmware. Check the log for details.")
|
||||
}
|
||||
return ["name": machine.name, "bundle": version, "status": status]
|
||||
}
|
||||
@@ -662,10 +662,10 @@ nonisolated enum VPhoneLaunchpadGuestSocket {
|
||||
let line = try JSONSerialization.data(withJSONObject: object) + Data([0x0A])
|
||||
let response = try await Task.detached { try exchange(line, socketPath: socketPath) }.value
|
||||
guard let json = try? JSONSerialization.jsonObject(with: response) else {
|
||||
throw VPhoneLaunchpadError("The machine answered with something other than JSON.", detail: String(decoding: response.prefix(512), as: UTF8.self))
|
||||
throw VPhoneLaunchpadError("The machine sent a reply that could not be read. Try again.", detail: String(decoding: response.prefix(512), as: UTF8.self))
|
||||
}
|
||||
if let dictionary = json as? [String: Any], dictionary["ok"] as? Bool == false {
|
||||
throw VPhoneLaunchpadError("The guest refused the request.", detail: dictionary["error"] as? String)
|
||||
throw VPhoneLaunchpadError("The machine refused the request. Try again.", detail: dictionary["error"] as? String)
|
||||
}
|
||||
return json
|
||||
}
|
||||
@@ -697,7 +697,7 @@ nonisolated enum VPhoneLaunchpadGuestSocket {
|
||||
}
|
||||
}
|
||||
guard connected == 0 else {
|
||||
throw VPhoneLaunchpadError("The machine's vphone.sock is not answering.", detail: String(cString: strerror(errno)))
|
||||
throw VPhoneLaunchpadError("The machine is not responding. Make sure it is running, then try again.", detail: String(cString: strerror(errno)))
|
||||
}
|
||||
guard VPhoneLaunchpadControl.write(line, to: fd) else {
|
||||
throw VPhoneLaunchpadError("Unable to send the request to the machine.")
|
||||
|
||||
@@ -44,7 +44,7 @@ final class VPhoneLaunchpadCoreBundle {
|
||||
case .prepare: String(localized: "Prepare bundle")
|
||||
case .download: String(localized: "Download")
|
||||
case .verify: String(localized: "Verify SHA-256")
|
||||
case .install: String(localized: "Install as root")
|
||||
case .install: String(localized: "Install with administrator access")
|
||||
case .policy: String(localized: "Allow bundle to run")
|
||||
case .preflight: String(localized: "Host preflight")
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ struct VPhoneLaunchpadCoreBundleView: View {
|
||||
} header: {
|
||||
Text("Installed")
|
||||
} footer: {
|
||||
Text("Stored in \(VPhoneLaunchpadBundleStore.root.path), owned by root and written only by the helper.")
|
||||
Text("Stored in \(VPhoneLaunchpadBundleStore.root.path) and managed by the helper.")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,30 @@
|
||||
"state" : "new",
|
||||
"value" : "vphone-launchpad"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "vphone-launchpad"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "vphone-launchpad"
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "vphone-launchpad"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "vphone-launchpad"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -22,6 +46,30 @@
|
||||
"state" : "new",
|
||||
"value" : "vphone-launchpad"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "vphone-launchpad"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "vphone-launchpad"
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "vphone-launchpad"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "vphone-launchpad"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"" : {
|
||||
|
||||
},
|
||||
"%@ · %@ · Expires %@" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -1448,6 +1445,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Allow Network" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Allow Network"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Allowing network access…" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Allowing network access…"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Allowed" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -4463,6 +4480,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Every patch in this bundle, including advanced ones that a freshly restored 26.4 guest may not survive." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Every patch in this bundle, including advanced ones that a freshly restored 26.4 guest may not survive."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Every patch in this bundle, including advanced ones that may stop a newly restored 26.4 machine from starting." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Every patch in this bundle, including advanced ones that may stop a newly restored 26.4 machine from starting."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Every patch this bundle declares, including the Frida Stalker relaxations and the hv_vmm_present concealment that a freshly restored 26.4 guest does not survive." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -5998,6 +6035,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Install custom firmware" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Install custom firmware"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Install with administrator access" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Install with administrator access"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Install CFW" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -7398,6 +7455,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Network Access" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Network Access"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Network access is restricted" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Network access is restricted"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Network access for %@ did not change. Relaunch the app and try again." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Network access for %@ did not change. Relaunch the app and try again."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Network" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -8658,6 +8745,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Patches every machine needs to start, with a working display and camera." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Patches every machine needs to start, with a working display and camera."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Patches" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -11632,6 +11729,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Stored in %@ and managed by the helper." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Stored in %@ and managed by the helper."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Stored in %@, owned by root and written only by the helper." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -12905,6 +13012,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Unable to allow network access for %@." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Unable to allow network access for %@."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Unable to install custom firmware. Check the log for details." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Unable to install custom firmware. Check the log for details."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Unable to Complete Action" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
|
||||
@@ -62,7 +62,7 @@ final class VPhoneLaunchpadCreationPipeline {
|
||||
case .waitDFU: String(localized: "Wait for DFU")
|
||||
case .restore: String(localized: "Restore")
|
||||
case .stopDFU: String(localized: "Stop machine")
|
||||
case .installCFW: String(localized: "Install CFW")
|
||||
case .installCFW: String(localized: "Install custom firmware")
|
||||
case .firstBoot: String(localized: "First boot")
|
||||
}
|
||||
}
|
||||
@@ -355,7 +355,7 @@ final class VPhoneLaunchpadCreationPipeline {
|
||||
onLine: output,
|
||||
)
|
||||
guard status == 0 else {
|
||||
throw VPhoneLaunchpadError(String(localized: "Unable to install CFW. Check the log for details."), detail: log.tail)
|
||||
throw VPhoneLaunchpadError(String(localized: "Unable to install custom firmware. Check the log for details."), detail: log.tail)
|
||||
}
|
||||
|
||||
case .firstBoot:
|
||||
|
||||
@@ -29,8 +29,8 @@ nonisolated struct VPhoneLaunchpadPatchCatalog: Decodable, Sendable {
|
||||
|
||||
var displaySummary: String {
|
||||
switch identifier {
|
||||
case "standard": String(localized: "The patches every vphone VM needs to boot, jailbroken, with a working display and camera.")
|
||||
case "extended": String(localized: "Every patch this bundle declares, including the Frida Stalker relaxations and the hv_vmm_present concealment that a freshly restored 26.4 guest does not survive.")
|
||||
case "standard": String(localized: "Patches every machine needs to start, with a working display and camera.")
|
||||
case "extended": String(localized: "Every patch in this bundle, including advanced ones that may stop a newly restored 26.4 machine from starting.")
|
||||
default: summary
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ enum VPhoneLaunchpadHelperAMFI {
|
||||
info.st_uid == 0,
|
||||
info.st_mode & 0o022 == 0
|
||||
else {
|
||||
throw VPhoneLaunchpadHelperError("vphone-escalator is missing or is not root-owned. Reinstall VPhone.bundle.")
|
||||
throw VPhoneLaunchpadHelperError("A required tool in VPhone.bundle is missing or was changed. Reinstall VPhone.bundle.")
|
||||
}
|
||||
|
||||
let process = Process()
|
||||
@@ -44,13 +44,13 @@ enum VPhoneLaunchpadHelperAMFI {
|
||||
do {
|
||||
try process.run()
|
||||
} catch {
|
||||
throw VPhoneLaunchpadHelperError("Unable to start vphone-escalator: \(error.localizedDescription)")
|
||||
throw VPhoneLaunchpadHelperError("Unable to allow the bundle to run. Try again.")
|
||||
}
|
||||
let text = String(decoding: output.fileHandleForReading.readDataToEndOfFile(), as: UTF8.self)
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
process.waitUntilExit()
|
||||
guard process.terminationReason == .exit, process.terminationStatus == 0 else {
|
||||
throw VPhoneLaunchpadHelperError(text.isEmpty ? "vphone-escalator failed. Check the host's SIP settings." : text)
|
||||
throw VPhoneLaunchpadHelperError(text.isEmpty ? "Unable to allow the bundle to run. Check the System Integrity Protection settings on this Mac." : text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user