@@ -5,6 +5,7 @@ import type {
55 ResourceMonitorEvent ,
66 ResourceMonitorExternalProcess ,
77 ResourceMonitorHelloEvent ,
8+ ResourceMonitorProcessTableEntry ,
89 ResourceMonitorSnapshotEvent ,
910 ResourceTelemetrySourceStatus ,
1011} from "@t3tools/contracts" ;
@@ -44,6 +45,7 @@ const BATTERY_SAMPLE_INTERVAL_MS = 5_000;
4445const CONSTRAINED_SAMPLE_INTERVAL_MS = 15_000 ;
4546const HANDSHAKE_TIMEOUT = Duration . seconds ( 5 ) ;
4647const SAMPLE_REQUEST_TIMEOUT = Duration . seconds ( 5 ) ;
48+ const PROCESS_TABLE_REQUEST_TIMEOUT = Duration . seconds ( 5 ) ;
4749const HISTORY_REQUEST_TIMEOUT = Duration . seconds ( 15 ) ;
4850const INITIAL_RESTART_DELAY = Duration . millis ( 500 ) ;
4951const MAX_RESTART_DELAY = Duration . seconds ( 10 ) ;
@@ -76,7 +78,7 @@ export class NativeTelemetryHandshakeTimedOut extends Schema.TaggedErrorClass<Na
7678class NativeTelemetryRequestTimedOut extends Schema . TaggedErrorClass < NativeTelemetryRequestTimedOut > ( ) (
7779 "NativeTelemetryRequestTimedOut" ,
7880 {
79- operation : Schema . Literals ( [ "readHistory" , "sampleNow" ] ) ,
81+ operation : Schema . Literals ( [ "processTable" , " readHistory", "sampleNow" ] ) ,
8082 timeoutMs : Schema . Number ,
8183 } ,
8284) {
@@ -192,6 +194,10 @@ export class NativeTelemetryClient extends Context.Service<
192194 snapshot : HostPowerSnapshot ,
193195 ) => Effect . Effect < void , NativeTelemetryClientError > ;
194196 readonly sampleNow : Effect . Effect < NativeTelemetrySnapshot , NativeTelemetryClientError > ;
197+ readonly processTable : Effect . Effect <
198+ ReadonlyArray < ResourceMonitorProcessTableEntry > ,
199+ NativeTelemetryClientError
200+ > ;
195201 readonly retry : Effect . Effect < boolean > ;
196202 readonly health : Effect . Effect < NativeTelemetryClientHealth > ;
197203 readonly subscribeHealth : Effect . Effect <
@@ -382,6 +388,12 @@ export const make = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(fu
382388 const pendingSamples = yield * Ref . make (
383389 new Map < string , Deferred . Deferred < NativeTelemetrySnapshot , NativeTelemetryClientError > > ( ) ,
384390 ) ;
391+ const pendingProcessTables = yield * Ref . make (
392+ new Map <
393+ string ,
394+ Deferred . Deferred < ReadonlyArray < ResourceMonitorProcessTableEntry > , NativeTelemetryClientError >
395+ > ( ) ,
396+ ) ;
385397 const pendingHistories = yield * Ref . make ( new Map < string , PendingHistoryRequest > ( ) ) ;
386398 const snapshots = yield * PubSub . sliding < NativeTelemetrySnapshot > ( 8 ) ;
387399 const healthChanges = yield * PubSub . sliding < NativeTelemetryClientHealth > ( 4 ) ;
@@ -399,10 +411,14 @@ export const make = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(fu
399411 const failPending = ( error : NativeTelemetryClientError ) =>
400412 Effect . gen ( function * ( ) {
401413 const samples = yield * Ref . getAndSet ( pendingSamples , new Map ( ) ) ;
414+ const processTables = yield * Ref . getAndSet ( pendingProcessTables , new Map ( ) ) ;
402415 const histories = yield * Ref . getAndSet ( pendingHistories , new Map ( ) ) ;
403416 yield * Effect . forEach ( samples . values ( ) , ( deferred ) => Deferred . fail ( deferred , error ) , {
404417 discard : true ,
405418 } ) ;
419+ yield * Effect . forEach ( processTables . values ( ) , ( deferred ) => Deferred . fail ( deferred , error ) , {
420+ discard : true ,
421+ } ) ;
406422 yield * Effect . forEach (
407423 histories . values ( ) ,
408424 ( request ) => Deferred . fail ( request . deferred , error ) ,
@@ -481,6 +497,21 @@ export const make = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(fu
481497 }
482498 }
483499 } ) ;
500+ case "processTable" :
501+ return Ref . modify ( pendingProcessTables , ( pending ) => {
502+ const next = new Map ( pending ) ;
503+ const deferred = next . get ( event . requestId ) ;
504+ next . delete ( event . requestId ) ;
505+ return [ Option . fromUndefinedOr ( deferred ) , next ] as const ;
506+ } ) . pipe (
507+ Effect . flatMap (
508+ Option . match ( {
509+ onNone : ( ) => Effect . void ,
510+ onSome : ( deferred ) => Deferred . succeed ( deferred , event . processes ) ,
511+ } ) ,
512+ ) ,
513+ Effect . asVoid ,
514+ ) ;
484515 case "historyChunk" :
485516 return Effect . gen ( function * ( ) {
486517 const latestSnapshot = event . snapshots . at ( - 1 ) ;
@@ -936,6 +967,60 @@ export const make = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(fu
936967 ) ;
937968 } ) ;
938969
970+ const processTable : NativeTelemetryClient [ "Service" ] [ "processTable" ] = Effect . gen ( function * ( ) {
971+ const current = yield * Ref . get ( state ) ;
972+ if ( ! canCommandNativeTelemetrySidecar ( current . status , Option . isSome ( current . handle ) ) ) {
973+ return yield * new NativeTelemetryUnavailable ( {
974+ reason : Option . getOrElse ( current . lastError , ( ) => "sidecar is not running" ) ,
975+ } ) ;
976+ }
977+
978+ const requestId = yield * crypto . randomUUIDv4 . pipe (
979+ Effect . mapError (
980+ ( cause ) => new NativeTelemetryCommandFailed ( { operation : "createRequestId" , cause } ) ,
981+ ) ,
982+ ) ;
983+ const deferred = yield * Deferred . make <
984+ ReadonlyArray < ResourceMonitorProcessTableEntry > ,
985+ NativeTelemetryClientError
986+ > ( ) ;
987+ yield * Ref . update ( pendingProcessTables , ( pending ) => {
988+ const next = new Map ( pending ) ;
989+ next . set ( requestId , deferred ) ;
990+ return next ;
991+ } ) ;
992+ return yield * writeCommand ( Option . getOrThrow ( current . handle ) , {
993+ version : RESOURCE_MONITOR_PROTOCOL_VERSION ,
994+ type : "processTable" ,
995+ requestId,
996+ } ) . pipe (
997+ Effect . andThen (
998+ Deferred . await ( deferred ) . pipe (
999+ Effect . timeoutOption ( PROCESS_TABLE_REQUEST_TIMEOUT ) ,
1000+ Effect . flatMap (
1001+ Option . match ( {
1002+ onNone : ( ) =>
1003+ Effect . fail (
1004+ new NativeTelemetryRequestTimedOut ( {
1005+ operation : "processTable" ,
1006+ timeoutMs : Duration . toMillis ( PROCESS_TABLE_REQUEST_TIMEOUT ) ,
1007+ } ) ,
1008+ ) ,
1009+ onSome : Effect . succeed ,
1010+ } ) ,
1011+ ) ,
1012+ ) ,
1013+ ) ,
1014+ Effect . ensuring (
1015+ Ref . update ( pendingProcessTables , ( pending ) => {
1016+ const next = new Map ( pending ) ;
1017+ next . delete ( requestId ) ;
1018+ return next ;
1019+ } ) ,
1020+ ) ,
1021+ ) ;
1022+ } ) ;
1023+
9391024 const health = currentHealth ;
9401025
9411026 return NativeTelemetryClient . of ( {
@@ -957,6 +1042,7 @@ export const make = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(fu
9571042 setExternalProcesses,
9581043 setHostPowerState,
9591044 sampleNow,
1045+ processTable,
9601046 retry : Ref . get ( state ) . pipe (
9611047 Effect . flatMap ( ( current ) =>
9621048 ! canRequestNativeTelemetryRetry ( current . status , Option . isSome ( current . handle ) )
@@ -1010,6 +1096,11 @@ export const layerTest = (
10101096 reason : "No resource monitor sample was configured for this test." ,
10111097 } ) ,
10121098 ) ,
1099+ processTable : Effect . fail (
1100+ new NativeTelemetryUnavailable ( {
1101+ reason : "No resource monitor process table was configured for this test." ,
1102+ } ) ,
1103+ ) ,
10131104 retry : Effect . succeed ( false ) ,
10141105 health,
10151106 subscribeHealth :
0 commit comments