Merge nucleic/upbeat-slate-lemur-7euo into dev

This commit is contained in:
2026-07-17 22:21:12 -07:00
parent 45c62b58ef
commit f219491d54
19 changed files with 102 additions and 64 deletions
+2 -2
View File
@@ -437,7 +437,7 @@ struct TranscriptRow: View, Equatable {
/// Human-readable tool-result text: a plain string, the joined text of an MCP
/// content array, or the canonical JSON as a last resort.
static func resultText(_ value: JSONValue) -> String {
nonisolated static func resultText(_ value: JSONValue) -> String {
if let string = value.stringValue { return string }
if let array = value.arrayValue {
let texts = array.compactMap { $0["text"]?.stringValue }
@@ -457,7 +457,7 @@ struct TranscriptRow: View, Equatable {
/// delivers. (Sole divergence: a hand-built NaN/Infinity number, whose encode failure
/// `canonicalString()` collapses to "null"; JSON can't carry one, and the expanded
/// branches re-check `resultText` anyway, so a false promise degrades to an empty body.)
static func resultHasText(_ value: JSONValue) -> Bool {
nonisolated static func resultHasText(_ value: JSONValue) -> Bool {
if let string = value.stringValue { return !string.isEmpty }
if let array = value.arrayValue {
let texts = array.compactMap { $0["text"]?.stringValue }
+1 -1
View File
@@ -4232,7 +4232,7 @@ public final class AppStore: ConflictArbiter {
if Task.isCancelled { break }
if await self.runNvrsionSweep() { break } // nothing left warm park
}
await self?.clearNvrsionKeepWarmTask()
self?.clearNvrsionKeepWarmTask()
}
}
private func clearNvrsionKeepWarmTask() { nvrsionKeepWarmTask = nil }
@@ -1523,7 +1523,7 @@ public actor MCPApprovalServer {
await prior?.value
try? await self.sendResponse(response, on: conn)
}
await self.handlerTaskFinished(taskID, token: token)
self.handlerTaskFinished(taskID, token: token)
}
previous = task
spawned.append(taskID)
+17 -6
View File
@@ -4,6 +4,16 @@
import Foundation
import Security
// `SecKeychainGet/SetUserInteractionAllowed` are formally deprecated but remain the ONLY API that
// suppresses the legacy Keychain ACL/partition-list password panel (the data-protection
// `kSecUseAuthenticationUI*` flags govern only Touch ID / passcode presence, not that dialog). Bind
// the C symbols directly the deprecation rides on their Swift imports, not the raw symbols so
// the required calls in `withoutLegacyKeychainUI` compile without deprecation warnings.
@_silgen_name("SecKeychainGetUserInteractionAllowed")
private func nucleic_SecKeychainGetUserInteractionAllowed(_ state: UnsafeMutablePointer<DarwinBoolean>) -> OSStatus
@_silgen_name("SecKeychainSetUserInteractionAllowed")
private func nucleic_SecKeychainSetUserInteractionAllowed(_ state: DarwinBoolean) -> OSStatus
/// Shared persistence for Nucleic's own secrets that is structurally incapable of presenting a
/// Keychain authorization panel.
///
@@ -93,14 +103,15 @@ enum KeychainOwnedAccess {
}
/// Run `body` with the legacy Keychain's interactive authorization panel disabled, restoring the
/// prior setting afterward. `SecKeychain*` is formally deprecated (its three uses below emit
/// expected deprecation warnings) but remains the ONLY API that suppresses that panel its
/// data-protection successors don't govern the legacy ACL dialog at all.
/// prior setting afterward. `SecKeychain*` is formally deprecated but remains the ONLY API that
/// suppresses that panel its data-protection successors don't govern the legacy ACL dialog at
/// all. The three symbols are bound directly (`nucleic_SecKeychain*`, top of file) so the
/// required calls compile without deprecation warnings.
private static func withoutLegacyKeychainUI<T>(_ body: () -> T) -> T {
var previous = DarwinBoolean(true)
SecKeychainGetUserInteractionAllowed(&previous)
SecKeychainSetUserInteractionAllowed(false)
defer { SecKeychainSetUserInteractionAllowed(previous.boolValue) }
_ = nucleic_SecKeychainGetUserInteractionAllowed(&previous)
_ = nucleic_SecKeychainSetUserInteractionAllowed(false)
defer { _ = nucleic_SecKeychainSetUserInteractionAllowed(previous) }
return body()
}
+3 -4
View File
@@ -343,7 +343,7 @@ public enum ContainerServiceSettings {
@TaskLocal private static var defaultsBox = DefaultsBox(defaults: .standard)
/// The store every accessor reads and `enableService()` writes. Production always resolves
/// to `.standard`; tests bind a fresh isolated suite via ``withDefaults(_:isolation:operation:)``
/// to `.standard`; tests bind a fresh isolated suite via ``withDefaults(_:operation:)``
/// so they can flip these switches without ever mutating the process-global domain. Task-local
/// because swift-testing runs suites in parallel: a global (even save/restored) override of
/// `serviceEnabledKey` is visible to every concurrently running test, and a leaked `true`
@@ -357,11 +357,10 @@ public enum ContainerServiceSettings {
/// invisible to concurrent tasks the isolation seam tests bind a per-test suite through.
public static func withDefaults<R>(
_ store: UserDefaults,
isolation: isolated (any Actor)? = #isolation,
operation: () async throws -> R
operation: nonisolated(nonsending) () async throws -> R
) async rethrows -> R {
try await $defaultsBox.withValue(
DefaultsBox(defaults: store), operation: operation, isolation: isolation)
DefaultsBox(defaults: store), operation: operation)
}
/// Master switch. Off the per-project "run sessions in a sandbox container" option is
@@ -210,8 +210,6 @@ public actor SessionTransferImporter {
entry.staged = true
try await store.updateTransferState(transferID: chunk.transferID, to: .ready, at: now())
return [ack, .transferReady(chunk.transferID)]
} catch let reject as TransferReject {
return [ack] + failStaging(entry, reject.reason, reject.message)
} catch {
return [ack] + failStaging(entry, .internalError, "Staging failed: \(error)")
}
+2 -2
View File
@@ -45,7 +45,7 @@ enum DERBuilder {
static func constructed(_ id: ASN1Identifier, _ children: [[UInt8]]) -> [UInt8] {
var s = DER.Serializer()
// appendConstructedNode never throws for raw-byte writes.
try! s.appendConstructedNode(identifier: id) { inner in
s.appendConstructedNode(identifier: id) { inner in
for c in children { inner.serializeRawBytes(c) }
}
return s.serializedBytes
@@ -53,7 +53,7 @@ enum DERBuilder {
static func primitive(_ id: ASN1Identifier, _ write: (inout [UInt8]) -> Void) -> [UInt8] {
var s = DER.Serializer()
try! s.appendPrimitiveNode(identifier: id) { write(&$0) }
s.appendPrimitiveNode(identifier: id) { write(&$0) }
return s.serializedBytes
}
@@ -2,7 +2,7 @@ import Foundation
/// Temporary opt-in tracing for the direct-upgrade handshake (set NUCLEIC_DIRECT_DEBUG=1).
enum DirectDebug {
nonisolated(unsafe) static let on = ProcessInfo.processInfo.environment["NUCLEIC_DIRECT_DEBUG"] == "1"
static let on = ProcessInfo.processInfo.environment["NUCLEIC_DIRECT_DEBUG"] == "1"
static func log(_ s: String) {
if on { FileHandle.standardError.write(Data("DIRECT: \(s)\n".utf8)) }
}
@@ -90,7 +90,7 @@ struct AppStoreSyncBridgeTests {
let repo = try await GitTestRepo()
defer { repo.cleanup() }
let store = makeStore(repo: repo)
#expect(await store.capabilities.canSyncTranscripts == true)
#expect(store.capabilities.canSyncTranscripts == true)
}
@Test func transcriptEventsServesFullHistoryAndHeader() async throws {
@@ -131,7 +131,7 @@ struct AppStoreSyncBridgeTests {
let repo = try await GitTestRepo()
defer { repo.cleanup() }
let store = makeStore(repo: repo)
#expect(await store.capabilities.canReceiveSessionTransfer == true)
#expect(store.capabilities.canReceiveSessionTransfer == true)
}
@Test func offerForUnknownProjectIsRejected() async throws {
+3 -3
View File
@@ -2369,7 +2369,7 @@ struct AppStoreTests {
let store = makeStore(repo: repo)
let project = await store.addProject(name: "demo", rootPath: repo.root, defaultBranch: "main")!
let sessionID = try await store.newSession(in: project)!
let sessionID = await store.newSession(in: project)!
await store.renameOpenSession(to: "my-cool-chat")
#expect(store.openSession?.title == "my-cool-chat")
#expect(store.summaries.first { $0.id == sessionID }?.title == "my-cool-chat")
@@ -2383,7 +2383,7 @@ struct AppStoreTests {
defer { repo.cleanup() }
let store = makeStore(repo: repo)
let project = await store.addProject(name: "demo", rootPath: repo.root, defaultBranch: "main")!
let sessionID = try await store.newSession(in: project)!
let sessionID = await store.newSession(in: project)!
await store.setOpenSessionModel("opus")
await store.setOpenSessionEffort("high")
@@ -2878,7 +2878,7 @@ struct AppStoreTests {
store.defaultAuto = true
let project = await store.addProject(name: "demo", rootPath: repo.root, defaultBranch: "main")!
_ = try await store.newSession(in: project)
_ = await store.newSession(in: project)
await waitFor { store.openSession != nil }
#expect(store.openSession?.model == "claude-sonnet-4-6")
#expect(store.openSession?.effort == "low")
+7 -7
View File
@@ -274,7 +274,7 @@ import Testing
@Test func restoreImageURLSettingReads() async throws {
let suite = UserDefaults(suiteName: "macvm-url-\(UUID().uuidString)")!
try await ContainerServiceSettings.withDefaults(suite) {
await ContainerServiceSettings.withDefaults(suite) {
#expect(MacVMSettings.restoreImageURL == nil)
suite.set("https://example.com/UniversalMac_27.0.ipsw", forKey: MacVMSettings.restoreImageURLKey)
#expect(MacVMSettings.restoreImageURL == "https://example.com/UniversalMac_27.0.ipsw")
@@ -381,7 +381,7 @@ import Testing
@Test func pendingBaseCarryPersistsSetAndClears() async throws {
let suite = UserDefaults(suiteName: "macvm-carry-\(UUID().uuidString)")!
try await ContainerServiceSettings.withDefaults(suite) {
await ContainerServiceSettings.withDefaults(suite) {
#expect(MacVMSettings.pendingBaseCarryApps.isEmpty)
#expect(MacVMSettings.pendingBaseCarryPackages.isEmpty)
// A base being deleted stashes its recorded set for the next build to restore.
@@ -508,7 +508,7 @@ import Testing
@Test func settingsDefaultsAndOverrides() async throws {
let suite = UserDefaults(suiteName: "macvm-test-\(UUID().uuidString)")!
try await ContainerServiceSettings.withDefaults(suite) {
await ContainerServiceSettings.withDefaults(suite) {
// Defaults with an empty suite.
#expect(MacVMSettings.serviceEnabled == false)
#expect(MacVMSettings.exposeByDefault == false) // false while service is off
@@ -981,7 +981,7 @@ import Testing
@Test func agentPasswordSettingReads() async throws {
let suite = UserDefaults(suiteName: "macvm-pw-\(UUID().uuidString)")!
try await ContainerServiceSettings.withDefaults(suite) {
await ContainerServiceSettings.withDefaults(suite) {
#expect(MacVMSettings.agentPassword == nil)
suite.set(" hunter2 ", forKey: MacVMSettings.agentPasswordKey)
#expect(MacVMSettings.agentPassword == "hunter2")
@@ -999,7 +999,7 @@ import Testing
@Test func computerUseSettingGatedOnService() async throws {
let suite = UserDefaults(suiteName: "macvm-cu-\(UUID().uuidString)")!
try await ContainerServiceSettings.withDefaults(suite) {
await ContainerServiceSettings.withDefaults(suite) {
suite.set(true, forKey: MacVMSettings.computerUseByDefaultKey)
#expect(MacVMSettings.computerUseByDefault == false) // service off false
suite.set(true, forKey: MacVMSettings.serviceEnabledKey)
@@ -1066,7 +1066,7 @@ import Testing
@Test func linuxSettingsDefaultsAndOverrides() async throws {
let suite = UserDefaults(suiteName: "linuxvm-test-\(UUID().uuidString)")!
try await ContainerServiceSettings.withDefaults(suite) {
await ContainerServiceSettings.withDefaults(suite) {
#expect(MacVMSettings.linuxServiceEnabled == false)
#expect(MacVMSettings.linuxExposeByDefault == false) // false while service off
#expect(MacVMSettings.linuxBasePrebuiltPath == nil)
@@ -1139,7 +1139,7 @@ import Testing
@Test func selectedPackageIDsPersistAndFilterOutUnavailable() async throws {
let suite = UserDefaults(suiteName: "macvm-pkgs-\(UUID().uuidString)")!
try await ContainerServiceSettings.withDefaults(suite) {
await ContainerServiceSettings.withDefaults(suite) {
#expect(MacVMSettings.selectedPackageIDs.isEmpty)
// A grayed-out (xcode) or unknown id can never be selected only installable ids survive.
MacVMSettings.setSelectedPackageIDs(["chrome", "xcode", "bogus"])
+1 -1
View File
@@ -229,7 +229,7 @@ import NucleicProtocol
let (host, _, client, recorder, _) = try await makePaired(bridge: bridge)
_ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }
// The host advertises the capability so a real client would know it may ask.
#expect(await bridge.capabilities.canListPeers == false) // FakeSyncBridge default; real host sets true
#expect(bridge.capabilities.canListPeers == false) // FakeSyncBridge default; real host sets true
await client.send(.listPeers)
let event = await recorder.waitFor { if case .peerList = $0 { return true } else { return false } }
@@ -1,5 +1,10 @@
import Foundation
import Testing
#if canImport(CryptoKit)
import CryptoKit
#else
import Crypto
#endif
@testable import NucleicProtocol
@@ -118,7 +118,7 @@ import Testing
/// A host that predates `canListPeers` decodes it as false (client won't send `listPeers`).
@Test func capabilitiesTolerateMissingCanListPeers() throws {
struct LegacyCaps: Codable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
struct LegacyCaps: Encodable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
let data = try CBOREncoder().encode(LegacyCaps())
let decoded = try CBORDecoder().decode(WireCapabilities.self, from: data)
#expect(decoded.canListPeers == false)
@@ -135,7 +135,7 @@ import Testing
/// A host that predates `canMintPairingCode` decodes it as false (the phone hides the
/// "add a device" button and never sends `requestPairingCode`).
@Test func capabilitiesTolerateMissingCanMintPairingCode() throws {
struct LegacyCaps: Codable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
struct LegacyCaps: Encodable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
let data = try CBOREncoder().encode(LegacyCaps())
let decoded = try CBORDecoder().decode(WireCapabilities.self, from: data)
#expect(decoded.canMintPairingCode == false)
@@ -226,10 +226,10 @@ import Testing
/// A host/client that predates `canSyncRoster` decodes it as false falls back to the
/// display-only `listPeers` path, never sending/expecting `meshRoster`.
@Test func capabilitiesTolerateMissingCanSyncRoster() throws {
struct LegacyCaps: Codable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
struct LegacyCaps: Encodable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
let decoded = try CBORDecoder().decode(WireCapabilities.self, from: CBOREncoder().encode(LegacyCaps()))
#expect(decoded.canSyncRoster == false)
struct LegacyClientCaps: Codable { let mesh = 1 }
struct LegacyClientCaps: Encodable { let mesh = 1 }
let cc = try CBORDecoder().decode(
WireClientCapabilities.self, from: CBOREncoder().encode(LegacyClientCaps()))
#expect(cc.canSyncRoster == false)
@@ -278,7 +278,7 @@ import Testing
/// A host that predates `canSyncSettings` decodes it as false the client never sends
/// `updateSettings` and never expects `HostMsg.settings`.
@Test func capabilitiesTolerateMissingCanSyncSettings() throws {
struct LegacyCaps: Codable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
struct LegacyCaps: Encodable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
let decoded = try CBORDecoder().decode(WireCapabilities.self, from: CBOREncoder().encode(LegacyCaps()))
#expect(decoded.canSyncSettings == false)
}
@@ -328,10 +328,10 @@ import Testing
/// direct verb at them (old hosts *error* on unknown ClientMsg tags, so this gate is
/// what keeps the upgrade invisible to them).
@Test func capabilitiesTolerateMissingCanDirectConnect() throws {
struct LegacyCaps: Codable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
struct LegacyCaps: Encodable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
let host = try CBORDecoder().decode(WireCapabilities.self, from: CBOREncoder().encode(LegacyCaps()))
#expect(host.canDirectConnect == false)
struct LegacyClientCaps: Codable { let mesh = 1 }
struct LegacyClientCaps: Encodable { let mesh = 1 }
let client = try CBORDecoder().decode(
WireClientCapabilities.self, from: CBOREncoder().encode(LegacyClientCaps()))
#expect(client.canDirectConnect == false)
@@ -401,7 +401,7 @@ import Testing
/// A host that predates `canUpdateAddresses` decodes it as false (client never sends it).
@Test func capabilitiesTolerateMissingCanUpdateAddresses() throws {
struct LegacyCaps: Codable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
struct LegacyCaps: Encodable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
let decoded = try CBORDecoder().decode(
WireCapabilities.self, from: CBOREncoder().encode(LegacyCaps()))
#expect(decoded.canUpdateAddresses == false)
@@ -467,7 +467,7 @@ import Testing
StartChatRequest(projectID: ProjectID(rawValue: "p1"), message: "go")))
#expect(legacy.covalenceOriginDeviceID == nil)
// A pre-rename sender using the old "carbonOriginDeviceID" key still read via fallback.
struct LegacyStart: Codable {
struct LegacyStart: Encodable {
let projectID = "p1", message = "go"
let useWorktree = true
let carbonOriginDeviceID = "origin-legacy"
@@ -493,7 +493,7 @@ import Testing
guard case .transferOffer(let decoded) = carried else { Issue.record("no offer"); return }
#expect(decoded.record.covalenceOriginDeviceID == "origin-phone")
// A pre-Covalence source's record (no key) decodes nil the session arrives unmanaged.
struct LegacyRecord: Codable {
struct LegacyRecord: Encodable {
let sessionID = "s1", backend = "claudeCode", title = "t", branch = "nucleic/t"
let baseSHA = "b"
let lastSeq: UInt64 = 1
@@ -503,7 +503,7 @@ import Testing
SessionTransferRecord.self, from: CBOREncoder().encode(LegacyRecord()))
#expect(legacy.covalenceOriginDeviceID == nil)
// A pre-rename source using the old "carbonOriginDeviceID" key read via fallback.
struct LegacyManagedRecord: Codable {
struct LegacyManagedRecord: Encodable {
let sessionID = "s1", backend = "claudeCode", title = "t", branch = "nucleic/t"
let baseSHA = "b"
let lastSeq: UInt64 = 1
@@ -523,7 +523,7 @@ import Testing
guard case .transferAccept(let decoded) = carried else { Issue.record("no accept"); return }
#expect(decoded.alreadyHaveItems == [.transcript])
// A pre-mirror destination's accept (no key) decodes empty the source streams all.
struct LegacyAccept: Codable {
struct LegacyAccept: Encodable {
let transferID = "x", resolvedProjectID = "p"
let haveSHAs: [String] = []
}
@@ -549,7 +549,7 @@ import Testing
/// A host that predates the transfer capability decodes it false a source never offers.
@Test func capabilitiesTolerateMissingCanReceiveSessionTransfer() throws {
struct LegacyCaps: Codable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
struct LegacyCaps: Encodable { let canModifyToolInput = true; let allowAlwaysScopes: [AlwaysScope] = [] }
let decoded = try CBORDecoder().decode(
WireCapabilities.self, from: CBOREncoder().encode(LegacyCaps()))
#expect(decoded.canReceiveSessionTransfer == false)
+13 -2
View File
@@ -116,8 +116,19 @@ in-tree means the patch can't be lost to a dependency re-resolve.
(the `cctl login` write path) gains a delete-and-retry on `errSecDuplicateItem`, since the
now-silent `exists` can under-report an unreadable pre-existing item. Mirrors
`KeychainOwnedAccess.withoutLegacyKeychainUI` in NucleicCore. Host-side (shipped by a normal
`swift build`); `SecKeychain*` deprecation warnings are expected (built with
`WARNINGS_AS_ERRORS=false`). Marked `[Nucleic vendored patch]`.
`swift build`). The three `SecKeychain*` symbols are formally deprecated but are the only API
covering the legacy ACL panel; they're bound directly via `@_silgen_name` (`nucleic_SecKeychain*`,
top of file) so the required calls compile without deprecation warnings. Marked
`[Nucleic vendored patch]`.
14. **`Sources/Containerization/Vminitd.swift` — configure the gRPC pipeline before the channel goes
active.** `Vminitd.init` used the now-deprecated `HTTP2ClientTransport.WrappedChannel.wrapping(
channel:config:serviceConfig:)`, which wraps an already-connected channel best-effort and may drop
early server frames such as SETTINGS. Migrated to `wrapping(config:serviceConfig:makeChannel:)`,
which invokes the transport's `configure` inside the bootstrap's channel initializer — before the
vsock channel becomes active. `init` is now `async throws` (the new overload is async); its callers
in `VZVirtualMachineInstance` (`start`/`dialAgent`, both already async) and the integration test now
`try await`. Marked `[Nucleic vendored patch]`.
### GUEST-side patches (require rebuilding the initfs — see below)
@@ -191,7 +191,7 @@ extension VZVirtualMachineInstance: VirtualMachineInstance {
try await self.vm.start(queue: self.queue)
let agent = try Vminitd(
let agent = try await Vminitd(
connection: try await self.vm.waitForAgent(queue: self.queue),
group: self.group
)
@@ -260,7 +260,7 @@ extension VZVirtualMachineInstance: VirtualMachineInstance {
port: Vminitd.port
)
let handle = try conn.dupHandle()
return try Vminitd(connection: handle, group: self.group)
return try await Vminitd(connection: handle, group: self.group)
} catch {
if let err = error as? ContainerizationError {
throw err
@@ -34,18 +34,23 @@ public struct Vminitd: Sendable {
public let grpcClient: GRPCClient<HTTP2ClientTransport.WrappedChannel>
private let connectionTask: Task<Void, Error>
public init(connection: FileHandle, group: any EventLoopGroup) throws {
let channel = try ClientBootstrap(group: group)
.channelInitializer { channel in
channel.eventLoop.makeCompletedFuture(withResultOf: {
try channel.pipeline.syncOperations.addHandler(HTTP2ConnectBufferingHandler())
})
}
.withConnectedSocket(connection.fileDescriptor).wait()
let transport = HTTP2ClientTransport.WrappedChannel.wrapping(
channel: channel,
public init(connection: FileHandle, group: any EventLoopGroup) async throws {
// Configure the gRPC pipeline from inside the channel initializer before the channel
// becomes active so no early server frames (e.g. SETTINGS) are dropped. `configure` is
// supplied by `wrapping(config:serviceConfig:makeChannel:)` and must be called exactly once.
let fd = connection.fileDescriptor
let transport = try await HTTP2ClientTransport.WrappedChannel.wrapping(
config: .defaults { $0.connection.maxIdleTime = nil }
)
) { configure in
try await ClientBootstrap(group: group)
.withConnectedSocket(fd) { channel in
channel.eventLoop.makeCompletedFuture {
try channel.pipeline.syncOperations.addHandler(HTTP2ConnectBufferingHandler())
}.flatMap { _ in
configure(channel)
}
}
}
let grpcClient = GRPCClient(transport: transport)
self.grpcClient = grpcClient
self.client = Com_Apple_Containerization_Sandbox_V3_SandboxContext.Client(wrapping: self.grpcClient)
@@ -22,6 +22,15 @@ import Foundation
#endif
import Security // [Nucleic vendored patch] SecKeychain*UserInteractionAllowed for prompt-free reads
// [Nucleic vendored patch] `SecKeychainGet/SetUserInteractionAllowed` are formally deprecated but
// remain the ONLY API that suppresses the legacy Keychain ACL panel. Bind the C symbols directly
// the deprecation rides on their Swift imports, not the raw symbols so `withoutInteractiveUI`
// compiles without deprecation warnings. Mirrors `KeychainOwnedAccess` in NucleicCore.
@_silgen_name("SecKeychainGetUserInteractionAllowed")
private func nucleic_SecKeychainGetUserInteractionAllowed(_ state: UnsafeMutablePointer<DarwinBoolean>) -> OSStatus
@_silgen_name("SecKeychainSetUserInteractionAllowed")
private func nucleic_SecKeychainSetUserInteractionAllowed(_ state: DarwinBoolean) -> OSStatus
/// Holds the result of a query to the keychain.
public struct KeychainQueryResult {
public var username: String
@@ -260,9 +269,9 @@ public struct KeychainQuery {
/// `KeychainOwnedAccess.withoutLegacyKeychainUI` in NucleicCore.
private static func withoutInteractiveUI<T>(_ body: () -> T) -> T {
var previous = DarwinBoolean(true)
SecKeychainGetUserInteractionAllowed(&previous)
SecKeychainSetUserInteractionAllowed(false)
defer { SecKeychainSetUserInteractionAllowed(previous.boolValue) }
_ = nucleic_SecKeychainGetUserInteractionAllowed(&previous)
_ = nucleic_SecKeychainSetUserInteractionAllowed(false)
defer { _ = nucleic_SecKeychainSetUserInteractionAllowed(previous) }
return body()
}
}
@@ -1730,7 +1730,7 @@ extension IntegrationSuite {
try await assertExec(container, id: "create-fifo", cmd: "mkfifo /tmp/test-fifo")
let vsock = try await container.dialVsock(port: 1024)
let vminitd = try Vminitd(connection: vsock, group: Self.eventLoop)
let vminitd = try await Vminitd(connection: vsock, group: Self.eventLoop)
let root = URL(filePath: container.root)