Files
luma/Sources/LumaCore/HookPackLibrary.swift
Ole André Vadla Ravnås 82c22cf427 core: Route LumaCore prints through engine events
Add an .engine RuntimeEvent source so host-tier issues
(devices, packages, attach, spawn-gating, instruments,
hookpacks, repl, itrace, editor) flow into the existing
event stream instead of stdout. HookPackLibrary takes a
callback for decode errors, and ITraceDecoder returns
collected panic strings so ProcessNode can yield them as
engine events.
2026-05-16 03:14:37 +02:00

84 lines
2.5 KiB
Swift

import Foundation
import Observation
@Observable
@MainActor
public final class HookPackLibrary {
public private(set) var packs: [HookPack] = []
@ObservationIgnored public var onError: ((String) -> Void)?
private let directory: URL
public init(directory: URL) {
self.directory = directory
}
public func reload() {
packs = discover()
}
public func pack(withId id: String) -> HookPack? {
packs.first { $0.id == id }
}
public func descriptors() -> [InstrumentDescriptor] {
packs.map { pack in
let packID = pack.id
let icon = pack.resolvedIcon
let displayName = pack.manifest.name
let initialFeatures = Dictionary(
uniqueKeysWithValues: pack.manifest.features.map {
($0.id, FeatureState(enabled: $0.enabledByDefault, value: $0.schema.defaultValue))
}
)
return InstrumentDescriptor(
id: "hook-pack:\(packID)",
kind: .hookPack,
sourceIdentifier: packID,
displayName: displayName,
icon: icon,
compatibility: pack.manifest.compatibility,
makeInitialConfigJSON: {
HookPackConfig(packId: packID, features: initialFeatures).encode()
}
)
}
}
private func discover() -> [HookPack] {
let fm = FileManager.default
guard
let contents = try? fm.contentsOfDirectory(
at: directory,
includingPropertiesForKeys: [.isDirectoryKey],
options: [.skipsHiddenFiles]
)
else {
return []
}
var result: [HookPack] = []
for url in contents {
guard
let rv = try? url.resourceValues(forKeys: [.isDirectoryKey]),
rv.isDirectory == true
else { continue }
let manifestURL = url.appendingPathComponent("manifest.json")
guard let data = try? Data(contentsOf: manifestURL) else { continue }
do {
let manifest = try JSONDecoder().decode(HookPackManifest.self, from: data)
let id = url.lastPathComponent
result.append(HookPack(id: id, manifest: manifest, folderURL: url))
} catch {
onError?("Failed to decode hook-pack manifest at \(manifestURL.path): \(error)")
}
}
return result
}
}