Files
vphone-cli/sources/VPhoneCore/VPhoneLaunchLayout.swift
zqxwceandClaude Opus 4.8 828be6dbc6 feat: add VPhoneCore library — VM bundle model + host primitives
The library the consolidated CLI is built on:
- bundle/library model (VPhoneBundle, VPhoneLibrary, VPhoneVirtualMachineManifest),
  bundle ops + reporting, restore helpers (VPhoneRestoreOps)
- host process primitives: VPhoneProcessRunner, VPhoneManagedProcess (spawn +
  stdout pattern-match + SIGKILL-escalating terminate), VPhoneLaunchLayout,
  VPhoneBootPatterns
- VPhoneResources: bundled-.app vs dev asset resolution, and Python resolution
  that provisions a per-user venv (~/.vphone/venv) on demand so the app is
  portable — never depends on the repo's .venv
- VPhoneVerbosity (quiet/info/debug/trace) and VPhoneVMPicker
Manifest moves out of the executable target into VPhoneCore. Full unit-test
suite under tests/VPhoneCoreTests.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Y4VDqWf5pVakcFLqB23CKe
2026-07-27 19:46:27 +03:00

51 lines
1.8 KiB
Swift

import Foundation
// MARK: - VPhoneLaunchLayout
public struct VPhoneLaunchLayout: Sendable {
public let resources: VPhoneResources
public init(resources: VPhoneResources) { self.resources = resources }
public init(projectRoot: URL) { self.init(resources: VPhoneResources(base: projectRoot)) }
public var preflightScript: URL { resources.preflightScript }
public var fwPrepareScript: URL { resources.fwPrepareScript }
public var cfwInstallHostScript: URL { resources.cfwInstallHostScript }
public var pmd3Bridge: URL { resources.pmd3Bridge }
public var vphoned: URL { resources.vphoned }
public func python() throws -> URL { try resources.pythonExecutable() }
/// Copy the built vphoned into the bundle if present and different.
@discardableResult
public func stageVphoned(into bundle: VPhoneBundle) throws -> Bool {
let fm = FileManager.default
guard fm.fileExists(atPath: vphoned.path) else { return false }
let dst = bundle.url.appendingPathComponent(".vphoned.signed")
if fm.fileExists(atPath: dst.path),
let a = try? Data(contentsOf: vphoned),
let b = try? Data(contentsOf: dst), a == b {
return false
}
if fm.fileExists(atPath: dst.path) { try fm.removeItem(at: dst) }
try fm.copyItem(at: vphoned, to: dst)
return true
}
}
// MARK: - VPhoneLsof
public enum VPhoneLsof {
public static func parsePIDs(_ output: String) -> [Int32] {
var seen = Set<Int32>()
var pids: [Int32] = []
for line in output.split(whereSeparator: \.isNewline) {
let trimmed = line.trimmingCharacters(in: .whitespaces)
guard let pid = Int32(trimmed), !seen.contains(pid) else { continue }
seen.insert(pid)
pids.append(pid)
}
return pids.sorted()
}
}