// @effect-diagnostics nodeBuiltinImport:off import * as NodeFS from "node:fs"; import * as NodeStream from "@effect/platform-node/NodeStream"; import { DesktopHostTelemetryMessage, type DesktopHostTelemetryMessage as DesktopHostTelemetryMessageValue, type DesktopHostTelemetrySnapshot, DesktopTelemetryControlMessage, type ResourceTelemetrySourceStatus, } from "@t3tools/contracts"; import * as Context from "effect/Context"; import * as DateTime from "effect/DateTime"; import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as PubSub from "effect/PubSub"; import * as Ref from "effect/Ref"; import * as Schema from "effect/Schema"; import * as Semaphore from "effect/Semaphore"; import * as Stream from "effect/Stream"; import * as Ndjson from "effect/unstable/encoding/Ndjson"; import { ServerConfig } from "../config.ts"; const STALE_AFTER_MS = 90_000; const STALE_CHECK_INTERVAL = Duration.seconds(30); export class DesktopTelemetryDescriptorUnavailable extends Schema.TaggedErrorClass()( "DesktopTelemetryDescriptorUnavailable", { mode: Schema.String, }, ) { override get message(): string { return `Desktop telemetry descriptor is unavailable in '${this.mode}' mode.`; } } export class DesktopTelemetryProtocolMismatch extends Schema.TaggedErrorClass()( "DesktopTelemetryProtocolMismatch", { expectedVersion: Schema.Number, receivedVersion: Schema.Number, }, ) { override get message(): string { return `Desktop telemetry protocol ${this.receivedVersion} is incompatible with expected protocol ${this.expectedVersion}.`; } } export class DesktopTelemetryDecodeFailed extends Schema.TaggedErrorClass()( "DesktopTelemetryDecodeFailed", { cause: Schema.Defect(), }, ) { override get message(): string { return "Failed to decode desktop telemetry."; } } export class DesktopTelemetryStreamFailed extends Schema.TaggedErrorClass()( "DesktopTelemetryStreamFailed", { fd: Schema.Number, cause: Schema.Defect(), }, ) { override get message(): string { return `Desktop telemetry stream on fd ${this.fd} failed.`; } } export class DesktopTelemetryStreamClosed extends Schema.TaggedErrorClass()( "DesktopTelemetryStreamClosed", { fd: Schema.Number, }, ) { override get message(): string { return `Desktop telemetry stream on fd ${this.fd} closed.`; } } export class DesktopTelemetryStale extends Schema.TaggedErrorClass()( "DesktopTelemetryStale", { fd: Schema.Number, staleAfterMs: Schema.Number, }, ) { override get message(): string { return `Desktop telemetry on fd ${this.fd} has not updated for ${this.staleAfterMs}ms.`; } } export type DesktopTelemetryReceiverError = | DesktopTelemetryDescriptorUnavailable | DesktopTelemetryProtocolMismatch | DesktopTelemetryDecodeFailed | DesktopTelemetryStreamFailed | DesktopTelemetryStreamClosed; export class DesktopTelemetryControlFailed extends Schema.TaggedErrorClass()( "DesktopTelemetryControlFailed", { fd: Schema.Number, operation: Schema.String, cause: Schema.Defect(), }, ) { override get message(): string { return `Desktop telemetry control '${this.operation}' failed on fd ${this.fd}.`; } } export interface DesktopTelemetryReceiverHealth { readonly status: ResourceTelemetrySourceStatus; readonly lastSampleAt: Option.Option; readonly lastError: Option.Option; } export interface DesktopTelemetryReceiverShape { readonly latest: Effect.Effect>; readonly changes: Stream.Stream; readonly health: Effect.Effect; readonly healthChanges: Stream.Stream; readonly setDiagnosticsDemand: ( enabled: boolean, ) => Effect.Effect; } export class DesktopTelemetryReceiver extends Context.Service< DesktopTelemetryReceiver, DesktopTelemetryReceiverShape >()("t3/resourceTelemetry/DesktopTelemetryReceiver") {} const decodeMessage = Schema.decodeUnknownEffect(DesktopHostTelemetryMessage); const encodeControlMessage = Schema.encodeEffect( Schema.fromJsonString(DesktopTelemetryControlMessage), ); const isDescriptorUnavailable = Schema.is(DesktopTelemetryDescriptorUnavailable); const isProtocolMismatch = Schema.is(DesktopTelemetryProtocolMismatch); const isDecodeFailed = Schema.is(DesktopTelemetryDecodeFailed); const isStreamFailed = Schema.is(DesktopTelemetryStreamFailed); function normalizeReceiverError(error: unknown): DesktopTelemetryReceiverError { if ( isDescriptorUnavailable(error) || isProtocolMismatch(error) || isDecodeFailed(error) || isStreamFailed(error) ) { return error; } return new DesktopTelemetryDecodeFailed({ cause: error }); } function messageVersion(value: unknown): number | undefined { if (typeof value !== "object" || value === null) return undefined; const version = Reflect.get(value, "version"); return typeof version === "number" ? version : undefined; } export const make = Effect.fn("resourceTelemetry.desktopTelemetryReceiver.make")(function* () { const config = yield* ServerConfig; const latest = yield* Ref.make(Option.none()); const changes = yield* PubSub.sliding(8); const healthChanges = yield* PubSub.sliding(4); const controlMutex = yield* Semaphore.make(1); const health = yield* Ref.make({ status: config.desktopTelemetryFd === undefined ? "unavailable" : "starting", lastSampleAt: Option.none(), lastError: config.desktopTelemetryFd === undefined ? Option.some( new DesktopTelemetryDescriptorUnavailable({ mode: config.mode, }).message, ) : Option.none(), }); const updateHealth = ( update: (current: DesktopTelemetryReceiverHealth) => DesktopTelemetryReceiverHealth, ) => Ref.modify(health, (current) => { const next = update(current); return [next, next]; }).pipe( Effect.flatMap((next) => PubSub.publish(healthChanges, next)), Effect.asVoid, ); const updateSampleHealth = (sampledAt: DateTime.Utc) => Ref.modify(health, (current) => { const next: DesktopTelemetryReceiverHealth = { status: "healthy", lastSampleAt: Option.some(sampledAt), lastError: Option.none(), }; return [ current.status !== "healthy" || Option.isSome(current.lastError) ? Option.some(next) : Option.none(), next, ] as const; }).pipe( Effect.flatMap( Option.match({ onNone: () => Effect.void, onSome: (next) => PubSub.publish(healthChanges, next), }), ), Effect.asVoid, ); const setDiagnosticsDemand: DesktopTelemetryReceiverShape["setDiagnosticsDemand"] = (enabled) => controlMutex.withPermits(1)( Effect.gen(function* () { const fd = config.desktopTelemetryControlFd; if (fd === undefined) return; const encoded = yield* encodeControlMessage({ version: 1, type: "setDiagnosticsDemand", enabled, }).pipe( Effect.mapError( (cause) => new DesktopTelemetryControlFailed({ fd, operation: "encode", cause, }), ), ); yield* Effect.try({ try: () => { const payload = Buffer.from(`${encoded}\n`); let offset = 0; while (offset < payload.byteLength) { const written = NodeFS.writeSync(fd, payload, offset); if (written <= 0) throw new Error("desktop telemetry control pipe accepted no bytes"); offset += written; } }, catch: (cause) => new DesktopTelemetryControlFailed({ fd, operation: "write", cause, }), }).pipe( Effect.tapError((error) => updateHealth((current) => ({ ...current, status: "degraded", lastError: Option.some(error.message), })), ), ); }), ); if (config.desktopTelemetryFd !== undefined) { const fd = config.desktopTelemetryFd; const readable = yield* Effect.acquireRelease( Effect.try({ try: () => NodeFS.createReadStream("", { fd, autoClose: true, }), catch: (cause) => new DesktopTelemetryStreamFailed({ fd, cause }), }), (stream) => Effect.sync(() => { stream.destroy(); }), ); const messages: Stream.Stream = NodeStream.fromReadable({ evaluate: () => readable, closeOnDone: true, onError: (cause) => new DesktopTelemetryStreamFailed({ fd, cause }), }).pipe( Stream.pipeThroughChannel(Ndjson.decode({ ignoreEmptyLines: true })), Stream.mapEffect( ( value, ): Effect.Effect< DesktopHostTelemetryMessageValue, DesktopTelemetryProtocolMismatch | DesktopTelemetryDecodeFailed > => { const version = messageVersion(value); if (version !== undefined && version !== 1) { return Effect.fail( new DesktopTelemetryProtocolMismatch({ expectedVersion: 1, receivedVersion: version, }), ); } return decodeMessage(value).pipe( Effect.mapError((cause) => new DesktopTelemetryDecodeFailed({ cause })), ); }, ), Stream.mapError(normalizeReceiverError), ); yield* messages.pipe( Stream.runForEach((message) => { if (message.type === "desktopTelemetryHello") { return updateHealth( (current): DesktopTelemetryReceiverHealth => ({ ...current, status: "healthy", lastError: Option.none(), }), ); } const sampledAt = DateTime.makeUnsafe(message.sampledAtUnixMs); return Ref.set(latest, Option.some(message)).pipe( Effect.andThen(updateSampleHealth(sampledAt)), Effect.andThen(PubSub.publish(changes, message)), Effect.asVoid, ); }), Effect.andThen( updateHealth( (current): DesktopTelemetryReceiverHealth => ({ ...current, status: "stopped", lastError: Option.some(new DesktopTelemetryStreamClosed({ fd }).message), }), ), ), Effect.catch((error) => updateHealth( (current): DesktopTelemetryReceiverHealth => ({ ...current, status: "degraded", lastError: Option.some(error.message), }), ), ), Effect.forkScoped, ); yield* Effect.forever( Effect.sleep(STALE_CHECK_INTERVAL).pipe( Effect.andThen( Effect.gen(function* () { const current = yield* Ref.get(latest); if (Option.isNone(current) || current.value.power.stale) return; const now = yield* DateTime.now; if (DateTime.toEpochMillis(now) - current.value.sampledAtUnixMs < STALE_AFTER_MS) return; const staleSnapshot: DesktopHostTelemetrySnapshot = { ...current.value, power: { ...current.value.power, stale: true }, }; yield* Ref.set(latest, Option.some(staleSnapshot)); yield* updateHealth((currentHealth) => ({ ...currentHealth, status: currentHealth.status === "stopped" ? "stopped" : "degraded", lastError: currentHealth.status === "stopped" ? currentHealth.lastError : Option.some( new DesktopTelemetryStale({ fd, staleAfterMs: STALE_AFTER_MS }).message, ), })); yield* PubSub.publish(changes, staleSnapshot); }), ), ), ).pipe(Effect.forkScoped); } return DesktopTelemetryReceiver.of({ latest: Ref.get(latest), changes: Stream.fromPubSub(changes), health: Ref.get(health), healthChanges: Stream.fromPubSub(healthChanges), setDiagnosticsDemand, }); }); export const layer = Layer.effect(DesktopTelemetryReceiver, make()); export const layerTest = ( overrides: Partial = {}, ): Layer.Layer => Layer.succeed( DesktopTelemetryReceiver, DesktopTelemetryReceiver.of({ latest: Effect.succeedNone, changes: Stream.empty, health: Effect.succeed({ status: "unavailable", lastSampleAt: Option.none(), lastError: Option.some("Desktop telemetry test implementation is unavailable."), }), healthChanges: Stream.empty, setDiagnosticsDemand: () => Effect.void, ...overrides, }), );