import { HostProcessArchitecture, HostProcessEnvironment, HostProcessPlatform, } from "@t3tools/shared/hostProcess"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import { ServerConfig } from "../config.ts"; export class ResourceMonitorBinaryUnsupported extends Schema.TaggedError()( "ResourceMonitorBinaryUnsupported", { platform: Schema.String, architecture: Schema.String, }, ) { override get message(): string { return `Resource monitoring is unsupported on ${this.platform}/${this.architecture}.`; } } export class ResourceMonitorBinaryNotFound extends Schema.TaggedError()( "ResourceMonitorBinaryNotFound", { platform: Schema.String, architecture: Schema.String, candidates: Schema.Array(Schema.String), }, ) { override get message(): string { return `Resource monitor binary was not found for ${this.platform}/${this.architecture}.`; } } export class ResourceMonitorBinaryNotExecutable extends Schema.TaggedError()( "ResourceMonitorBinaryNotExecutable", { path: Schema.String, mode: Schema.Number, }, ) { override get message(): string { return `Resource monitor binary at '${this.path}' is not executable.`; } } export type ResourceMonitorBinaryError = | ResourceMonitorBinaryUnsupported | ResourceMonitorBinaryNotFound | ResourceMonitorBinaryNotExecutable; export class ResourceMonitorBinary extends Context.Service< ResourceMonitorBinary, { readonly resolve: Effect.Effect; } >()("t3/resourceTelemetry/ResourceMonitorBinary") {} function binaryName(platform: NodeJS.Platform): string { return platform === "win32" ? "t3-resource-monitor.exe" : "t3-resource-monitor"; } export type ResourceMonitorLinuxLibc = "gnu" | "musl"; function detectResourceMonitorLinuxLibc(): ResourceMonitorLinuxLibc { try { const report = process.report?.getReport() as | { readonly header?: { readonly glibcVersionRuntime?: unknown; }; } | undefined; return typeof report?.header?.glibcVersionRuntime === "string" ? "gnu" : "musl"; } catch { return "musl"; } } export const ResourceMonitorHostLinuxLibc = Context.Reference( "t3/resourceTelemetry/ResourceMonitorHostLinuxLibc", { defaultValue: detectResourceMonitorLinuxLibc, }, ); function resourceMonitorPlatformKey( platform: NodeJS.Platform, architecture: NodeJS.Architecture, ): string | undefined { if ( (platform !== "darwin" && platform !== "linux" && platform !== "win32") || (architecture !== "arm64" && architecture !== "x64") ) { return undefined; } return `${platform}-${architecture}`; } function resourceMonitorRustTarget( platform: NodeJS.Platform, architecture: NodeJS.Architecture, linuxLibc?: ResourceMonitorLinuxLibc, ): string | undefined { if (platform === "darwin") { return architecture === "arm64" ? "aarch64-apple-darwin" : architecture === "x64" ? "x86_64-apple-darwin" : undefined; } if (platform === "linux") { if (linuxLibc !== "gnu") { return undefined; } return architecture === "arm64" ? "aarch64-unknown-linux-gnu" : architecture === "x64" ? "x86_64-unknown-linux-gnu" : undefined; } if (platform === "win32") { return architecture === "arm64" ? "aarch64-pc-windows-msvc" : architecture === "x64" ? "x86_64-pc-windows-msvc" : undefined; } return undefined; } export const make = Effect.fn("resourceTelemetry.resourceMonitorBinary.make")(function* () { const config = yield* ServerConfig; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const platform = yield* HostProcessPlatform; const architecture = yield* HostProcessArchitecture; const environment = yield* HostProcessEnvironment; const linuxLibc = platform === "linux" ? yield* ResourceMonitorHostLinuxLibc : undefined; const executableName = binaryName(platform); const platformKey = resourceMonitorPlatformKey(platform, architecture); const rustTarget = resourceMonitorRustTarget(platform, architecture, linuxLibc); const overrideCandidates = [ environment.T3CODE_RESOURCE_MONITOR_PATH, config.resourceMonitorPath, ].filter((candidate): candidate is string => Boolean(candidate)); const bundledCandidates = platformKey === undefined || rustTarget === undefined ? [] : [ path.resolve(import.meta.dirname, "resource-monitor", platformKey, executableName), path.resolve(import.meta.dirname, "resource-monitor", executableName), path.resolve(import.meta.dirname, "../resource-monitor", executableName), path.resolve( import.meta.dirname, "../../../../native/resource-monitor/target", rustTarget, "release", executableName, ), path.resolve( import.meta.dirname, "../../../native/resource-monitor/target", rustTarget, "release", executableName, ), path.resolve( import.meta.dirname, "../../../../native/resource-monitor/target/release", executableName, ), path.resolve( import.meta.dirname, "../../../../native/resource-monitor/target/debug", executableName, ), ]; if (overrideCandidates.length === 0 && bundledCandidates.length === 0) { return ResourceMonitorBinary.of({ resolve: Effect.fail( new ResourceMonitorBinaryUnsupported({ platform, architecture, }), ), }); } const candidates = [...overrideCandidates, ...bundledCandidates]; const resolve: ResourceMonitorBinary["Service"]["resolve"] = Effect.gen(function* () { for (const candidate of candidates) { const exists = yield* fileSystem.exists(candidate).pipe(Effect.orElseSucceed(() => false)); if (!exists) continue; if (platform !== "win32") { const stat = yield* fileSystem.stat(candidate).pipe(Effect.option); if (Option.isSome(stat) && (stat.value.mode & 0o111) === 0) { return yield* new ResourceMonitorBinaryNotExecutable({ path: candidate, mode: stat.value.mode, }); } } return candidate; } return yield* new ResourceMonitorBinaryNotFound({ platform, architecture, candidates, }); }); return ResourceMonitorBinary.of({ resolve }); }); export const layer = Layer.effect(ResourceMonitorBinary, make());