bleam 0.0.10 → 0.0.12
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.
- package/dist/ai.cjs +1872 -1182
- package/dist/ai.d.cts +267 -234
- package/dist/ai.d.ts +267 -234
- package/dist/ai.js +1859 -1167
- package/dist/cli.cjs +3 -183
- package/dist/cli.d.cts +3 -47
- package/dist/cli.d.ts +3 -47
- package/dist/cli.js +3 -183
- package/dist/config.d.cts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/{files-Ds1wT8C2.js → files-BXVkPrPN.js} +6 -1
- package/dist/{files-Bo7h9fik.cjs → files-DxaQ-Nv0.cjs} +11 -0
- package/dist/files.cjs +1 -1
- package/dist/files.js +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/platform.cjs +1 -13
- package/dist/platform.d.cts +1 -28
- package/dist/platform.d.ts +1 -28
- package/dist/platform.js +1 -10
- package/dist/schema-B5BfdswF.js +226 -0
- package/dist/schema-BnVZOXfu.cjs +286 -0
- package/dist/schema-D5eImHxu.d.cts +125 -0
- package/dist/schema-SSjokbtw.d.ts +125 -0
- package/dist/schema.cjs +10 -2
- package/dist/schema.d.cts +2 -2
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js +2 -2
- package/dist/state-Dh3HLixb.js +874 -0
- package/dist/state-LssDgpff.cjs +973 -0
- package/dist/state.cjs +16 -12
- package/dist/state.d.cts +144 -140
- package/dist/state.d.ts +145 -140
- package/dist/state.js +3 -3
- package/dist/{ui-Bg11tvlc.d.ts → ui-1WepaMS4.d.cts} +1 -1
- package/dist/{ui-Dd7SXdbg.d.cts → ui-D7bRLYee.d.ts} +7 -7
- package/dist/ui.d.cts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/window.d.cts +1 -1
- package/dist/window.d.ts +1 -1
- package/package.json +3 -1
- package/templates/foundation-models/app/index.tsx +77 -15
- package/templates/image-generation/app/index.tsx +4 -2
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/ModelRegistry.swift +1 -1
- package/templates/native/ios/Bleam/AI/Flux2/Pipeline/FluxImageRunner.swift +7 -2
- package/templates/native/ios/Bleam/AI/TextGeneration/Bonsai/BonsaiTextGenerator.swift +46 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Bonsai/BonsaiTextModel.swift +107 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Bonsai/BonsaiTextSnapshotValidator.swift +201 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Core/GenerationTypes.swift +85 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Core/ModelCapabilities.swift +21 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Core/TextKVCache.swift +23 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Core/TokenSampler.swift +79 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3CheckpointLoader.swift +161 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3Configuration.swift +119 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3InferenceSession.swift +84 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3Layers.swift +197 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3Model.swift +130 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Checkpoint.swift +102 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Configuration.swift +140 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Model.swift +112 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Session.swift +121 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridCache.swift +42 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridFullAttention.swift +57 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridLayers.swift +58 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridLinearAttention.swift +111 -0
- package/templates/native/ios/Bleam/AppDelegate.swift +0 -206
- package/templates/native/ios/Bleam.xcodeproj/project.pbxproj +135 -129
- package/templates/native/ios/GenerationService/BonsaiTextGenerationRunner.swift +103 -0
- package/templates/native/ios/GenerationService/ImageGenerationRunner.swift +19 -7
- package/templates/native/ios/GenerationService/main.swift +174 -48
- package/templates/native/ios/Podfile.lock +173 -173
- package/templates/native/ios/Shared/Generation/GenerationServiceProtocol.swift +3 -0
- package/templates/native/ios/Shared/Generation/GenerationWorkerProtocol.swift +44 -0
- package/templates/native/modules/bleam-runtime/ios/AIModule.swift +239 -341
- package/templates/native/modules/bleam-runtime/ios/GenerationContracts.swift +47 -0
- package/templates/native/modules/bleam-runtime/ios/PlatformModule.swift +0 -191
- package/templates/text-generation/app/index.tsx +124 -0
- package/templates/text-generation/app.config.ts +5 -0
- package/templates/updates/README.md +43 -199
- package/templates/updates/src/index.ts +5 -98
- package/templates/updates/src/schema.ts +18 -173
- package/dist/schema-Bo5Jvqus.js +0 -90
- package/dist/schema-DOOjfXvs.d.ts +0 -58
- package/dist/schema-ENSMX_1t.d.cts +0 -58
- package/dist/schema-rQ13mrpD.cjs +0 -102
- package/dist/state-Bx0VlTlO.cjs +0 -852
- package/dist/state-CAwe-Vw1.js +0 -767
- package/templates/native/ios/PlatformHelper/main.swift +0 -726
- /package/dist/{config-Cms0rvqg.d.ts → config-COcRnn5a.d.cts} +0 -0
- /package/dist/{config-CufOVJV3.d.cts → config-Chi-flpJ.d.ts} +0 -0
package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridLinearAttention.swift
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import MLX
|
|
3
|
+
import MLXNN
|
|
4
|
+
|
|
5
|
+
public final class QwenHybridLinearAttention: Module, @unchecked Sendable {
|
|
6
|
+
private let numValueHeads: Int
|
|
7
|
+
private let numKeyHeads: Int
|
|
8
|
+
private let keyHeadDim: Int
|
|
9
|
+
private let valueHeadDim: Int
|
|
10
|
+
private let keyDimension: Int
|
|
11
|
+
private let valueDimension: Int
|
|
12
|
+
private let convolutionDimension: Int
|
|
13
|
+
private let kernelSize: Int
|
|
14
|
+
private let keyNormWeight: MLXArray
|
|
15
|
+
|
|
16
|
+
@ModuleInfo public var in_proj_qkv: Linear
|
|
17
|
+
@ModuleInfo public var in_proj_z: Linear
|
|
18
|
+
@ModuleInfo public var in_proj_b: Linear
|
|
19
|
+
@ModuleInfo public var in_proj_a: Linear
|
|
20
|
+
@ModuleInfo public var out_proj: Linear
|
|
21
|
+
@ModuleInfo public var norm: QwenHybridGatedRMSNorm
|
|
22
|
+
@ModuleInfo public var conv1d: QwenHybridDepthwiseConv1D
|
|
23
|
+
@ParameterInfo public var dt_bias: MLXArray
|
|
24
|
+
@ParameterInfo public var A_log: MLXArray
|
|
25
|
+
|
|
26
|
+
public init(config: any QwenHybridConfiguration) {
|
|
27
|
+
numValueHeads = config.linearNumValueHeads
|
|
28
|
+
numKeyHeads = config.linearNumKeyHeads
|
|
29
|
+
keyHeadDim = config.linearKeyHeadDim
|
|
30
|
+
valueHeadDim = config.linearValueHeadDim
|
|
31
|
+
keyDimension = config.linearNumKeyHeads * config.linearKeyHeadDim
|
|
32
|
+
valueDimension = config.linearNumValueHeads * config.linearValueHeadDim
|
|
33
|
+
convolutionDimension = keyDimension * 2 + valueDimension
|
|
34
|
+
kernelSize = config.linearConvKernelDim
|
|
35
|
+
keyNormWeight = MLX.ones([config.linearKeyHeadDim])
|
|
36
|
+
_in_proj_qkv = ModuleInfo(wrappedValue: Linear(config.hiddenSize, convolutionDimension, bias: false))
|
|
37
|
+
_in_proj_z = ModuleInfo(wrappedValue: Linear(config.hiddenSize, valueDimension, bias: false))
|
|
38
|
+
_in_proj_b = ModuleInfo(wrappedValue: Linear(config.hiddenSize, numValueHeads, bias: false))
|
|
39
|
+
_in_proj_a = ModuleInfo(wrappedValue: Linear(config.hiddenSize, numValueHeads, bias: false))
|
|
40
|
+
_out_proj = ModuleInfo(wrappedValue: Linear(valueDimension, config.hiddenSize, bias: false))
|
|
41
|
+
_norm = ModuleInfo(wrappedValue: QwenHybridGatedRMSNorm(dimensions: valueHeadDim, epsilon: config.rmsNormEps))
|
|
42
|
+
_conv1d = ModuleInfo(wrappedValue: QwenHybridDepthwiseConv1D(channels: convolutionDimension, kernelSize: kernelSize))
|
|
43
|
+
_dt_bias = ParameterInfo(wrappedValue: MLX.ones([numValueHeads]))
|
|
44
|
+
_A_log = ParameterInfo(wrappedValue: MLX.zeros([numValueHeads]))
|
|
45
|
+
super.init()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public func callAsFunction(_ input: MLXArray, cache: QwenHybridLayerCache) -> MLXArray {
|
|
49
|
+
let batch = input.dim(0)
|
|
50
|
+
let length = input.dim(1)
|
|
51
|
+
let mixed = causalConvolution(in_proj_qkv(input), cache: cache)
|
|
52
|
+
let pieces = mixed.split(indices: [keyDimension, keyDimension * 2], axis: -1)
|
|
53
|
+
var query = pieces[0].reshaped([batch, length, numKeyHeads, keyHeadDim])
|
|
54
|
+
var key = pieces[1].reshaped([batch, length, numKeyHeads, keyHeadDim])
|
|
55
|
+
let value = pieces[2].reshaped([batch, length, numValueHeads, valueHeadDim]).asType(.float32)
|
|
56
|
+
query = MLXFast.rmsNorm(query.asType(.float32), weight: keyNormWeight, eps: 1e-6) / Float(keyHeadDim)
|
|
57
|
+
key = MLXFast.rmsNorm(key.asType(.float32), weight: keyNormWeight, eps: 1e-6) / sqrt(Float(keyHeadDim))
|
|
58
|
+
let gate = MLX.exp(-MLX.exp(A_log.asType(.float32)) * MLXNN.softplus(in_proj_a(input).asType(.float32) + dt_bias))
|
|
59
|
+
let beta = MLX.sigmoid(in_proj_b(input)).asType(.float32)
|
|
60
|
+
let initialState = cache.recurrentState ?? MLX.zeros([batch, numValueHeads, valueHeadDim, keyHeadDim], dtype: .float32)
|
|
61
|
+
let updated = QwenHybridGatedDelta.update(query: query, key: key, value: value, gate: gate, beta: beta, state: initialState)
|
|
62
|
+
cache.recurrentState = updated.state
|
|
63
|
+
let normalized = norm(
|
|
64
|
+
updated.output.asType(input.dtype).reshaped([-1, valueHeadDim]),
|
|
65
|
+
gate: in_proj_z(input).reshaped([-1, valueHeadDim])
|
|
66
|
+
)
|
|
67
|
+
return out_proj(normalized.reshaped([batch, length, valueDimension]))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private func causalConvolution(_ input: MLXArray, cache: QwenHybridLayerCache) -> MLXArray {
|
|
71
|
+
let length = input.dim(1)
|
|
72
|
+
if let previous = cache.convolutionState {
|
|
73
|
+
let combined = MLX.concatenated([previous, input], axis: 1)
|
|
74
|
+
cache.convolutionState = combined[0..., (combined.dim(1) - kernelSize + 1)..., 0...]
|
|
75
|
+
return MLXNN.silu(MLX.conv1d(combined, conv1d.weight, padding: 0, groups: convolutionDimension))
|
|
76
|
+
}
|
|
77
|
+
let result = MLXNN.silu(MLX.conv1d(input, conv1d.weight, padding: kernelSize - 1, groups: convolutionDimension))
|
|
78
|
+
let paddedInput = padded(input, widths: [[0, 0], [max(0, kernelSize - 1 - length), 0], [0, 0]])
|
|
79
|
+
cache.convolutionState = paddedInput[0..., (paddedInput.dim(1) - kernelSize + 1)..., 0...]
|
|
80
|
+
return result[0..., 0..<length, 0...]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public enum QwenHybridGatedDelta {
|
|
85
|
+
public static func update(
|
|
86
|
+
query: MLXArray,
|
|
87
|
+
key: MLXArray,
|
|
88
|
+
value: MLXArray,
|
|
89
|
+
gate: MLXArray,
|
|
90
|
+
beta: MLXArray,
|
|
91
|
+
state initialState: MLXArray
|
|
92
|
+
) -> (output: MLXArray, state: MLXArray) {
|
|
93
|
+
let repeats = value.dim(2) / key.dim(2)
|
|
94
|
+
let query = repeats == 1 ? query : MLX.repeated(query, count: repeats, axis: 2)
|
|
95
|
+
let key = repeats == 1 ? key : MLX.repeated(key, count: repeats, axis: 2)
|
|
96
|
+
var state = initialState
|
|
97
|
+
var outputs: [MLXArray] = []
|
|
98
|
+
outputs.reserveCapacity(query.dim(1))
|
|
99
|
+
for index in 0..<query.dim(1) {
|
|
100
|
+
let q = query[0..., index, 0..., 0...]
|
|
101
|
+
let k = key[0..., index, 0..., 0...]
|
|
102
|
+
let v = value[0..., index, 0..., 0...]
|
|
103
|
+
state = state * gate[0..., index, 0...].expandedDimensions(axes: [2, 3])
|
|
104
|
+
let remembered = (state * k.expandedDimensions(axis: 2)).sum(axis: -1)
|
|
105
|
+
let delta = (v - remembered) * beta[0..., index, 0...].expandedDimensions(axis: -1)
|
|
106
|
+
state = state + k.expandedDimensions(axis: 2) * delta.expandedDimensions(axis: -1)
|
|
107
|
+
outputs.append((state * q.expandedDimensions(axis: 2)).sum(axis: -1))
|
|
108
|
+
}
|
|
109
|
+
return (MLX.stacked(outputs, axis: 1), state)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -4,7 +4,6 @@ import ReactAppDependencyProvider
|
|
|
4
4
|
import UIKit
|
|
5
5
|
import CryptoKit
|
|
6
6
|
import Darwin
|
|
7
|
-
import Security
|
|
8
7
|
|
|
9
8
|
func launchArgument(_ name: String) -> String? {
|
|
10
9
|
let arguments = ProcessInfo.processInfo.arguments
|
|
@@ -22,17 +21,6 @@ func launchArgument(_ name: String) -> String? {
|
|
|
22
21
|
|
|
23
22
|
@main
|
|
24
23
|
class AppDelegate: ExpoAppDelegate {
|
|
25
|
-
public override func application(
|
|
26
|
-
_ application: UIApplication,
|
|
27
|
-
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
|
28
|
-
) -> Bool {
|
|
29
|
-
let launched = super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
30
|
-
if launched {
|
|
31
|
-
PlatformHelperInstaller.refreshIfInstalledApp()
|
|
32
|
-
}
|
|
33
|
-
return launched
|
|
34
|
-
}
|
|
35
|
-
|
|
36
24
|
public func application(
|
|
37
25
|
_ application: UIApplication,
|
|
38
26
|
configurationForConnecting connectingSceneSession: UISceneSession,
|
|
@@ -63,200 +51,6 @@ class AppDelegate: ExpoAppDelegate {
|
|
|
63
51
|
}
|
|
64
52
|
}
|
|
65
53
|
|
|
66
|
-
private enum PlatformHelperInstaller {
|
|
67
|
-
static func refreshIfInstalledApp() {
|
|
68
|
-
#if targetEnvironment(macCatalyst)
|
|
69
|
-
let homeDirectory = URL(fileURLWithPath: NSHomeDirectory(), isDirectory: true)
|
|
70
|
-
let homeApplications = homeDirectory
|
|
71
|
-
.appendingPathComponent("Applications", isDirectory: true)
|
|
72
|
-
.standardizedFileURL.path + "/"
|
|
73
|
-
guard Bundle.main.bundleURL.standardizedFileURL.path.hasPrefix(homeApplications) else {
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return }
|
|
77
|
-
|
|
78
|
-
let serviceName = "PlatformHelper"
|
|
79
|
-
let bundledHelper = Bundle.main.bundleURL
|
|
80
|
-
.appendingPathComponent("Contents/Helpers/\(serviceName)")
|
|
81
|
-
guard FileManager.default.isExecutableFile(atPath: bundledHelper.path) else {
|
|
82
|
-
NSLog("Bleam helper unavailable: bundled executable is missing")
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
|
-
guard
|
|
86
|
-
let teamIdentifier = Bundle.main.object(forInfoDictionaryKey: "BleamTeamIdentifier") as? String,
|
|
87
|
-
let platformVersion = Bundle.main.object(forInfoDictionaryKey: "BleamRuntimeVersion") as? String,
|
|
88
|
-
validSignature(at: bundledHelper, teamIdentifier: teamIdentifier)
|
|
89
|
-
else {
|
|
90
|
-
NSLog("Bleam helper unavailable: bundled executable has an invalid identity")
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
let label = "\(bundleIdentifier).PlatformHelper"
|
|
95
|
-
let domain = "gui/\(getuid())"
|
|
96
|
-
let helperRoot = homeDirectory
|
|
97
|
-
.appendingPathComponent("Library/Application Support/Bleam/Helpers", isDirectory: true)
|
|
98
|
-
.appendingPathComponent(bundleIdentifier, isDirectory: true)
|
|
99
|
-
let installedHelper = helperRoot.appendingPathComponent(serviceName)
|
|
100
|
-
let stagedHelper = helperRoot.appendingPathComponent(".\(serviceName).\(UUID().uuidString)")
|
|
101
|
-
let backupHelper = helperRoot.appendingPathComponent(".\(serviceName).previous.\(UUID().uuidString)")
|
|
102
|
-
let failedHelper = helperRoot.appendingPathComponent(".\(serviceName).failed.\(UUID().uuidString)")
|
|
103
|
-
let pendingApply = homeDirectory
|
|
104
|
-
.appendingPathComponent("Library/Application Support/Bleam/Updates", isDirectory: true)
|
|
105
|
-
.appendingPathComponent(bundleIdentifier, isDirectory: true)
|
|
106
|
-
.appendingPathComponent("platform-apply.pending")
|
|
107
|
-
let launchAgents = homeDirectory
|
|
108
|
-
.appendingPathComponent("Library/LaunchAgents", isDirectory: true)
|
|
109
|
-
let plistURL = launchAgents.appendingPathComponent("\(label).plist")
|
|
110
|
-
let propertyList: [String: Any] = [
|
|
111
|
-
"Label": label,
|
|
112
|
-
"ProgramArguments": [installedHelper.path],
|
|
113
|
-
"ProcessType": "Background",
|
|
114
|
-
"MachServices": [label: true],
|
|
115
|
-
"KeepAlive": [
|
|
116
|
-
"SuccessfulExit": false,
|
|
117
|
-
"PathState": [pendingApply.path: true],
|
|
118
|
-
],
|
|
119
|
-
"EnvironmentVariables": [
|
|
120
|
-
"XPC_SERVICE_NAME": label,
|
|
121
|
-
"BLEAM_BUNDLE_IDENTIFIER": bundleIdentifier,
|
|
122
|
-
"BLEAM_TEAM_IDENTIFIER": teamIdentifier,
|
|
123
|
-
"BLEAM_HELPER_VERSION": platformVersion,
|
|
124
|
-
"BLEAM_PLATFORM_VERSION": platformVersion,
|
|
125
|
-
],
|
|
126
|
-
]
|
|
127
|
-
|
|
128
|
-
do {
|
|
129
|
-
defer { try? FileManager.default.removeItem(at: stagedHelper) }
|
|
130
|
-
try FileManager.default.createDirectory(
|
|
131
|
-
at: helperRoot,
|
|
132
|
-
withIntermediateDirectories: true
|
|
133
|
-
)
|
|
134
|
-
try FileManager.default.copyItem(at: bundledHelper, to: stagedHelper)
|
|
135
|
-
guard
|
|
136
|
-
FileManager.default.isExecutableFile(atPath: stagedHelper.path),
|
|
137
|
-
validSignature(at: stagedHelper, teamIdentifier: teamIdentifier)
|
|
138
|
-
else {
|
|
139
|
-
throw HelperInstallError.invalidSignature
|
|
140
|
-
}
|
|
141
|
-
try FileManager.default.createDirectory(
|
|
142
|
-
at: launchAgents,
|
|
143
|
-
withIntermediateDirectories: true
|
|
144
|
-
)
|
|
145
|
-
let data = try PropertyListSerialization.data(
|
|
146
|
-
fromPropertyList: propertyList,
|
|
147
|
-
format: .xml,
|
|
148
|
-
options: 0
|
|
149
|
-
)
|
|
150
|
-
let previousPlist = try? Data(contentsOf: plistURL)
|
|
151
|
-
let wasRegistered = runLaunchctl(["print", "\(domain)/\(label)"]) == 0
|
|
152
|
-
if wasRegistered, runLaunchctl(["bootout", "\(domain)/\(label)"]) != 0 {
|
|
153
|
-
throw HelperInstallError.registrationFailed
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
do {
|
|
157
|
-
if FileManager.default.fileExists(atPath: installedHelper.path),
|
|
158
|
-
rename(installedHelper.path, backupHelper.path) != 0
|
|
159
|
-
{
|
|
160
|
-
throw HelperInstallError.replacementFailed
|
|
161
|
-
}
|
|
162
|
-
guard rename(stagedHelper.path, installedHelper.path) == 0 else {
|
|
163
|
-
throw HelperInstallError.replacementFailed
|
|
164
|
-
}
|
|
165
|
-
try data.write(to: plistURL, options: .atomic)
|
|
166
|
-
let bootstrapStatus = runLaunchctl(["bootstrap", domain, plistURL.path])
|
|
167
|
-
guard
|
|
168
|
-
bootstrapStatus == 0 || runLaunchctl(["print", "\(domain)/\(label)"]) == 0
|
|
169
|
-
else {
|
|
170
|
-
throw HelperInstallError.registrationFailed
|
|
171
|
-
}
|
|
172
|
-
try? FileManager.default.removeItem(at: backupHelper)
|
|
173
|
-
try? FileManager.default.removeItem(
|
|
174
|
-
at: helperRoot.appendingPathComponent("PlatformHelper.xpc", isDirectory: true)
|
|
175
|
-
)
|
|
176
|
-
} catch let installError {
|
|
177
|
-
do {
|
|
178
|
-
let newJobRegistered = runLaunchctl(["print", "\(domain)/\(label)"]) == 0
|
|
179
|
-
if newJobRegistered,
|
|
180
|
-
runLaunchctl(["bootout", "\(domain)/\(label)"]) != 0
|
|
181
|
-
{
|
|
182
|
-
throw HelperInstallError.rollbackFailed
|
|
183
|
-
}
|
|
184
|
-
if FileManager.default.fileExists(atPath: installedHelper.path),
|
|
185
|
-
rename(installedHelper.path, failedHelper.path) != 0
|
|
186
|
-
{
|
|
187
|
-
throw HelperInstallError.rollbackFailed
|
|
188
|
-
}
|
|
189
|
-
if FileManager.default.fileExists(atPath: backupHelper.path),
|
|
190
|
-
rename(backupHelper.path, installedHelper.path) != 0
|
|
191
|
-
{
|
|
192
|
-
throw HelperInstallError.rollbackFailed
|
|
193
|
-
}
|
|
194
|
-
if let previousPlist {
|
|
195
|
-
try previousPlist.write(to: plistURL, options: .atomic)
|
|
196
|
-
if wasRegistered,
|
|
197
|
-
runLaunchctl(["bootstrap", domain, plistURL.path]) != 0
|
|
198
|
-
{
|
|
199
|
-
throw HelperInstallError.rollbackFailed
|
|
200
|
-
}
|
|
201
|
-
} else {
|
|
202
|
-
try? FileManager.default.removeItem(at: plistURL)
|
|
203
|
-
}
|
|
204
|
-
try? FileManager.default.removeItem(at: failedHelper)
|
|
205
|
-
} catch {
|
|
206
|
-
NSLog("Bleam helper rollback failed: %@", error.localizedDescription)
|
|
207
|
-
throw error
|
|
208
|
-
}
|
|
209
|
-
throw installError
|
|
210
|
-
}
|
|
211
|
-
} catch {
|
|
212
|
-
NSLog("Bleam helper install failed: %@", error.localizedDescription)
|
|
213
|
-
return
|
|
214
|
-
}
|
|
215
|
-
#endif
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
private static func validSignature(at url: URL, teamIdentifier: String) -> Bool {
|
|
219
|
-
var staticCode: SecStaticCode?
|
|
220
|
-
guard
|
|
221
|
-
SecStaticCodeCreateWithPath(url as CFURL, [], &staticCode) == errSecSuccess,
|
|
222
|
-
let staticCode,
|
|
223
|
-
SecStaticCodeCheckValidity(
|
|
224
|
-
staticCode,
|
|
225
|
-
SecCSFlags(rawValue: kSecCSStrictValidate),
|
|
226
|
-
nil
|
|
227
|
-
) == errSecSuccess
|
|
228
|
-
else { return false }
|
|
229
|
-
|
|
230
|
-
var signingInfo: CFDictionary?
|
|
231
|
-
guard
|
|
232
|
-
SecCodeCopySigningInformation(
|
|
233
|
-
staticCode,
|
|
234
|
-
SecCSFlags(rawValue: kSecCSSigningInformation),
|
|
235
|
-
&signingInfo
|
|
236
|
-
) == errSecSuccess
|
|
237
|
-
else { return false }
|
|
238
|
-
return (signingInfo as? [String: Any])?[kSecCodeInfoTeamIdentifier as String] as? String == teamIdentifier
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
private static func runLaunchctl(_ arguments: [String]) -> Int32 {
|
|
242
|
-
let values = ["launchctl"] + arguments
|
|
243
|
-
var argv = values.map { strdup($0) }
|
|
244
|
-
argv.append(nil)
|
|
245
|
-
defer { argv.compactMap { $0 }.forEach { free($0) } }
|
|
246
|
-
|
|
247
|
-
var pid: pid_t = 0
|
|
248
|
-
let result = posix_spawn(&pid, "/bin/launchctl", nil, nil, &argv, environ)
|
|
249
|
-
guard result == 0 else { return Int32(result) }
|
|
250
|
-
var status: Int32 = 0
|
|
251
|
-
guard waitpid(pid, &status, 0) == pid else { return -1 }
|
|
252
|
-
return status
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
private enum HelperInstallError: Error {
|
|
257
|
-
case invalidSignature, registrationFailed, replacementFailed, rollbackFailed
|
|
258
|
-
}
|
|
259
|
-
|
|
260
54
|
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
|
|
261
55
|
private let bundleOverrideURL: URL?
|
|
262
56
|
|