-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCLIInstallRunner.swift
More file actions
320 lines (302 loc) · 14.3 KB
/
Copy pathCLIInstallRunner.swift
File metadata and controls
320 lines (302 loc) · 14.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
import Darwin
import Foundation
/// Runs the public CLI-track `install.sh` non-interactively from Malibu.app.
/// Option A onboarding: Malibu delegates install/autotune/launchd to the script.
enum CLIInstallRunner {
enum Error: Swift.Error, LocalizedError {
case installScriptNotFound
case compatibilityManifestNotFound
case invalidPinnedVersion(String)
case referralFailure(ReferralFailure)
case nonZeroExit(Int32)
case launchFailed(String)
enum ReferralFailure: Int32, Equatable {
case required = 20
case invalid = 21
case expired = 22
case revoked = 23
case exhausted = 24
case conflict = 25
case rateLimited = 26
case unavailable = 27
var message: String {
switch self {
case .required: return "A referral code is required to join right now. Enter an invite and retry."
case .invalid: return "This referral code is invalid. Check the code or invite link and retry."
case .expired: return "This referral code has expired. Ask the sender for a current invite."
case .revoked: return "This referral code was revoked. Ask the sender for a different invite."
case .exhausted: return "This referral code has no redemptions left. Ask the sender for a different invite."
case .conflict: return "This provider identity is already bound to a different referral attempt. Retry the original invite or contact support."
case .rateLimited: return "Too many referral attempts were submitted. Wait before retrying."
case .unavailable: return "Invite setup is temporarily unavailable. Your provider identity is preserved; retry later."
}
}
}
var errorDescription: String? {
switch self {
case .installScriptNotFound:
return "The bundled macprovider installer script was not found."
case .compatibilityManifestNotFound:
return "The signed provider compatibility manifest was not found."
case let .invalidPinnedVersion(version):
return "Provider install version pin is invalid: \(version)."
case let .referralFailure(failure):
return failure.message
case let .nonZeroExit(code):
return "Provider install failed (exit \(code)). See the log above for details."
case let .launchFailed(message):
return "Could not start the provider installer: \(message)"
}
}
}
/// Invokes `install.sh` with `MACPROVIDER_NO_PROMPT=1`. Delivers stdout/stderr
/// lines to `onLogLine` on the main actor.
static func run(
pinnedVersion: String? = nil,
referralCode: String? = nil,
replacingIncumbentProvider: Bool = false,
repairExistingInstall: Bool = false,
onLogLine: @escaping @Sendable @MainActor (String) -> Void
) async throws {
let scriptURL = try resolveInstallScriptURL()
let manifestURL = try resolveCompatibilityManifestURL()
let verifiedScript = try BundledInstallContractVerifier.verify(
scriptURL: scriptURL,
manifestURL: manifestURL
)
let referralFileURL = try referralCode.map { try ReferralCodeFile.create(code: $0) }
defer {
if let referralFileURL { try? FileManager.default.removeItem(at: referralFileURL) }
}
let installPort = resolveInstallPort()
let environment = try installerEnvironment(
parentEnvironment: ProcessInfo.processInfo.environment,
installPort: installPort,
pinnedVersion: pinnedVersion,
referralCodeFile: referralFileURL,
replacingIncumbentProvider: replacingIncumbentProvider,
repairExistingInstall: repairExistingInstall
)
if let installPort {
await onLogLine("[macprovider-install] Using local HTTP port \(installPort) for provider install.")
}
let exitCode: Int32 = try await withCheckedThrowingContinuation { continuation in
DispatchQueue.global(qos: .userInitiated).async {
let process = Process()
let stdin = Pipe()
let stdout = Pipe()
let stderr = Pipe()
process.executableURL = URL(fileURLWithPath: "/bin/bash")
// Execute the exact authenticated bytes without reopening an
// owner-writable path between verification and bash parsing.
process.arguments = ["-s", "--"]
process.standardInput = stdin
process.standardOutput = stdout
process.standardError = stderr
process.environment = environment
let emit: (Pipe) -> Void = { pipe in
pipe.fileHandleForReading.readabilityHandler = { handle in
let chunk = handle.availableData
guard !chunk.isEmpty else { return }
let text = String(decoding: chunk, as: UTF8.self)
for line in text.split(whereSeparator: \.isNewline) {
let trimmed = String(line)
guard !trimmed.isEmpty else { continue }
Task { @MainActor in onLogLine(trimmed) }
}
}
}
emit(stdout)
emit(stderr)
do {
try process.run()
try stdin.fileHandleForWriting.write(contentsOf: verifiedScript)
try stdin.fileHandleForWriting.close()
} catch {
try? stdin.fileHandleForWriting.close()
if process.isRunning { process.terminate() }
continuation.resume(throwing: Error.launchFailed(error.localizedDescription))
return
}
process.waitUntilExit()
stdout.fileHandleForReading.readabilityHandler = nil
stderr.fileHandleForReading.readabilityHandler = nil
continuation.resume(returning: process.terminationStatus)
}
}
if exitCode == 0 {
return
}
if let failure = Error.ReferralFailure(rawValue: exitCode) {
throw Error.referralFailure(failure)
}
// Every non-zero installer exit leaves the durable transaction
// uncommitted and triggers rollback. A healthy local process may be
// the restored previous release, so it cannot prove this install won.
throw Error.nonZeroExit(exitCode)
}
static func installerEnvironment(
parentEnvironment: [String: String],
installPort: Int?,
pinnedVersion: String?,
referralCodeFile: URL? = nil,
replacingIncumbentProvider: Bool = false,
repairExistingInstall: Bool = false,
homeDirectory: URL = FileManager.default.homeDirectoryForCurrentUser,
fileManager: FileManager = .default
) throws -> [String: String] {
// Deliberately do not inherit the parent environment. install.sh has
// authority-changing knobs for repositories, public keys, acceptance
// candidates, emergency rollback, paths, and launchd. Malibu supplies
// only the values this invocation owns, and never forwards parent
// tokens or dynamic-loader/shell configuration.
_ = parentEnvironment
var explicit = [
"PATH": "/usr/bin:/bin:/usr/sbin:/sbin",
"HOME": homeDirectory.path,
"TMPDIR": "/tmp",
"LC_ALL": "C",
"MACPROVIDER_NO_PROMPT": "1",
]
let configuredProgram = InstalledProviderMonitor.configuredProviderProgram(
homeDirectory: homeDirectory,
fileManager: fileManager
)
let defaultProgram = homeDirectory.appendingPathComponent("macprovider/macprovider-cli").standardizedFileURL
if configuredProgram != defaultProgram {
explicit["MACPROVIDER_INSTALL_DIR"] = configuredProgram.deletingLastPathComponent().path
}
if let installPort {
explicit["MACPROVIDER_PORT"] = String(installPort)
}
if let pinnedVersion {
guard let normalized = ProviderCLIVersion.strictNormalize(pinnedVersion) else {
throw Error.invalidPinnedVersion(pinnedVersion)
}
explicit["MACPROVIDER_VERSION"] = "v\(normalized)"
}
if let referralCodeFile {
explicit["MACPROVIDER_REFERRAL_CODE_FILE"] = referralCodeFile.path
if replacingIncumbentProvider {
explicit["MACPROVIDER_REFERRAL_REPLACE_INCUMBENT"] = "1"
}
}
if repairExistingInstall {
explicit["MACPROVIDER_REPAIR_EXISTING_INSTALL"] = "1"
}
return try ProcessEnvironmentSanitizer.sanitized(
from: [:],
extraEnvironment: explicit
)
}
/// Reports whether an already-installed provider is locally healthy. This
/// is used to resume onboarding, never to override a failed install exit.
static func localInstallSucceeded() async -> Bool {
guard let port = ProviderConfig.readHTTPPort(),
ProviderConfig.readProviderID() != nil else {
return false
}
let manifest = FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent("Library/Application Support/macprovider/install_manifest.json")
let hasManifest = FileManager.default.isReadableFile(atPath: manifest.path)
let launchdPlist = FileManager.default.isReadableFile(
atPath: NSHomeDirectory() + "/Library/LaunchAgents/live.streamvc.macprovider.plist"
)
guard hasManifest || launchdPlist else { return false }
guard InstalledProviderMonitor.launchdServiceRepairState() == .validExecutable else {
return false
}
return await InstalledProviderMonitor.isHealthy(port: port)
}
/// Human-readable hint for onboarding UI while `install.sh` runs long silent phases.
enum ActivityMonitor {
static func snapshot() -> String {
let lines = macproviderProcessLines()
if lines.contains(where: { $0.contains("autotune --recommend") }) {
return "Benchmarking models for your Mac — often 10–30 minutes on first run. The log can look frozen; that is normal."
}
if let bench = lines.first(where: { $0.contains("serve --no-join") }) {
let model = extractFlag("--model", from: bench) ?? "a candidate model"
return "Testing \(shortModelName(model)) performance…"
}
if lines.contains(where: { $0.contains("models pull") || $0.contains("huggingface") }) {
return "Downloading model weights from Hugging Face…"
}
let cliPath = NSHomeDirectory() + "/macprovider/macprovider-cli"
if FileManager.default.isExecutableFile(atPath: cliPath) {
return "Provider CLI installed. Running autotune and model checks next…"
}
if lines.contains(where: { $0.contains("curl") && $0.contains("github") }) {
return "Downloading provider release from GitHub…"
}
return "Install in progress…"
}
private static func macproviderProcessLines() -> [String] {
let process = Process()
let pipe = Pipe()
process.executableURL = URL(fileURLWithPath: "/bin/ps")
process.arguments = ["-ax", "-o", "command="]
process.standardOutput = pipe
process.standardError = Pipe()
do {
try process.run()
} catch {
return []
}
process.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let text = String(decoding: data, as: UTF8.self)
return text
.split(whereSeparator: \.isNewline)
.map(String.init)
.filter { $0.contains("macprovider-cli") && !$0.contains("/bin/ps") }
}
private static func extractFlag(_ flag: String, from command: String) -> String? {
let parts = command.split(separator: " ").map(String.init)
guard let index = parts.firstIndex(of: flag), index + 1 < parts.count else { return nil }
return parts[index + 1]
}
private static func shortModelName(_ raw: String) -> String {
if raw.contains("/") {
return raw.split(separator: "/").last.map(String.init) ?? raw
}
if raw.hasPrefix("/") {
return URL(fileURLWithPath: raw).lastPathComponent
}
return raw
}
}
static func resolveInstallScriptURL() throws -> URL {
if let bundled = Bundle.main.url(forResource: "install", withExtension: "sh") {
return bundled
}
let devRelative = Bundle.main.bundleURL
.deletingLastPathComponent()
.deletingLastPathComponent()
.deletingLastPathComponent()
.appendingPathComponent("dist/install.sh")
if FileManager.default.isReadableFile(atPath: devRelative.path) {
return devRelative
}
throw Error.installScriptNotFound
}
static func resolveCompatibilityManifestURL() throws -> URL {
guard let bundled = Bundle.main.url(forResource: "compatibility-set", withExtension: "json") else {
throw Error.compatibilityManifestNotFound
}
return bundled
}
/// Port for `install.sh`: explicit env override, existing config, else a probed free port.
/// Avoids exit 6 when the default 8080 is occupied (common on dev Macs running Node).
static func resolveInstallPort(
environment: [String: String] = ProcessInfo.processInfo.environment
) -> Int? {
if let raw = environment["MACPROVIDER_PORT"], let port = Int(raw), port > 0 {
return port
}
if let configured = ProviderConfig.readHTTPPort() {
return configured
}
return try? FreePortProbe.probe()
}
}