cmdop 0.1.22__py3-none-any.whl → 0.1.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cmdop/__init__.py +1 -1
- cmdop/_generated/rpc_messages/browser_pb2.py +135 -85
- cmdop/_generated/rpc_messages/browser_pb2.pyi +270 -2
- cmdop/_generated/rpc_messages_pb2.pyi +25 -0
- cmdop/_generated/service_pb2.py +2 -2
- cmdop/_generated/service_pb2_grpc.py +345 -0
- cmdop/services/browser/capabilities/__init__.py +2 -0
- cmdop/services/browser/capabilities/fetch.py +1 -2
- cmdop/services/browser/capabilities/network.py +245 -0
- cmdop/services/browser/models.py +103 -0
- cmdop/services/browser/service/sync.py +204 -4
- cmdop/services/browser/session.py +32 -4
- {cmdop-0.1.22.dist-info → cmdop-0.1.23.dist-info}/METADATA +43 -4
- {cmdop-0.1.22.dist-info → cmdop-0.1.23.dist-info}/RECORD +16 -15
- {cmdop-0.1.22.dist-info → cmdop-0.1.23.dist-info}/WHEEL +0 -0
- {cmdop-0.1.22.dist-info → cmdop-0.1.23.dist-info}/licenses/LICENSE +0 -0
cmdop/_generated/service_pb2.py
CHANGED
|
@@ -125,7 +125,7 @@ except AttributeError:
|
|
|
125
125
|
file__operations_dot_requests__pb2 = file__operations__pb2.file_operations.requests_pb2
|
|
126
126
|
|
|
127
127
|
|
|
128
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rservice.proto\x12\x08terminal\x1a\x14\x61gent_messages.proto\x1a\x16\x63ontrol_messages.proto\x1a\x12rpc_messages.proto\x1a\x0e\x66ile_rpc.proto\x1a\x15\x66ile_operations.proto2\
|
|
128
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rservice.proto\x12\x08terminal\x1a\x14\x61gent_messages.proto\x1a\x16\x63ontrol_messages.proto\x1a\x12rpc_messages.proto\x1a\x0e\x66ile_rpc.proto\x1a\x15\x66ile_operations.proto2\xe6,\n\x18TerminalStreamingService\x12G\n\x0f\x43onnectTerminal\x12\x16.terminal.AgentMessage\x1a\x18.terminal.ControlMessage(\x01\x30\x01\x12P\n\rCreateSession\x12\x1e.terminal.CreateSessionRequest\x1a\x1f.terminal.CreateSessionResponse\x12M\n\x0c\x43loseSession\x12\x1d.terminal.CloseSessionRequest\x1a\x1e.terminal.CloseSessionResponse\x12Y\n\x10GetSessionStatus\x12!.terminal.GetSessionStatusRequest\x1a\".terminal.GetSessionStatusResponse\x12M\n\x0cListSessions\x12\x1d.terminal.ListSessionsRequest\x1a\x1e.terminal.ListSessionsResponse\x12\x44\n\tSendInput\x12\x1a.terminal.SendInputRequest\x1a\x1b.terminal.SendInputResponse\x12G\n\nSendResize\x12\x1b.terminal.SendResizeRequest\x1a\x1c.terminal.SendResizeResponse\x12G\n\nSendSignal\x12\x1b.terminal.SendSignalRequest\x1a\x1c.terminal.SendSignalResponse\x12G\n\nGetHistory\x12\x1b.terminal.GetHistoryRequest\x1a\x1c.terminal.GetHistoryResponse\x12J\n\x0bHealthCheck\x12\x1c.terminal.HealthCheckRequest\x1a\x1d.terminal.HealthCheckResponse\x12\x62\n\x13RegisterDeviceToken\x12$.terminal.RegisterDeviceTokenRequest\x1a%.terminal.RegisterDeviceTokenResponse\x12h\n\x15UnregisterDeviceToken\x12&.terminal.UnregisterDeviceTokenRequest\x1a\'.terminal.UnregisterDeviceTokenResponse\x12V\n\x0f\x45nterBackground\x12 .terminal.EnterBackgroundRequest\x1a!.terminal.EnterBackgroundResponse\x12_\n\x12WakeFromBackground\x12#.terminal.WakeFromBackgroundRequest\x1a$.terminal.WakeFromBackgroundResponse\x12M\n\x0cQueueCommand\x12\x1d.terminal.QueueCommandRequest\x1a\x1e.terminal.QueueCommandResponse\x12\x62\n\x11\x46ileListDirectory\x12%.terminal.FileListDirectoryRpcRequest\x1a&.terminal.FileListDirectoryRpcResponse\x12G\n\x08\x46ileRead\x12\x1c.terminal.FileReadRpcRequest\x1a\x1d.terminal.FileReadRpcResponse\x12J\n\tFileWrite\x12\x1d.terminal.FileWriteRpcRequest\x1a\x1e.terminal.FileWriteRpcResponse\x12h\n\x13\x46ileCreateDirectory\x12\'.terminal.FileCreateDirectoryRpcRequest\x1a(.terminal.FileCreateDirectoryRpcResponse\x12M\n\nFileDelete\x12\x1e.terminal.FileDeleteRpcRequest\x1a\x1f.terminal.FileDeleteRpcResponse\x12G\n\x08\x46ileMove\x12\x1c.terminal.FileMoveRpcRequest\x1a\x1d.terminal.FileMoveRpcResponse\x12G\n\x08\x46ileCopy\x12\x1c.terminal.FileCopyRpcRequest\x1a\x1d.terminal.FileCopyRpcResponse\x12P\n\x0b\x46ileGetInfo\x12\x1f.terminal.FileGetInfoRpcRequest\x1a .terminal.FileGetInfoRpcResponse\x12\x62\n\x11\x46ileCreateArchive\x12%.terminal.FileCreateArchiveRpcRequest\x1a&.terminal.FileCreateArchiveRpcResponse\x12M\n\nFileSearch\x12\x1e.terminal.FileSearchRpcRequest\x1a\x1f.terminal.FileSearchRpcResponse\x12Y\n\x0eHlsGetPlaylist\x12\".terminal.HlsGetPlaylistRpcRequest\x1a#.terminal.HlsGetPlaylistRpcResponse\x12V\n\rHlsGetSegment\x12!.terminal.HlsGetSegmentRpcRequest\x1a\".terminal.HlsGetSegmentRpcResponse\x12Y\n\x0eHlsStopSession\x12\".terminal.HlsStopSessionRpcRequest\x1a#.terminal.HlsStopSessionRpcResponse\x12[\n\x16InitiateStreamingRelay\x12\x1f.terminal.StreamingRelayRequest\x1a .terminal.StreamingRelayResponse\x12H\n\nRelayChunk\x12\x1d.terminal.StreamingRelayChunk\x1a\x1b.terminal.StreamingRelayAck\x12`\n\x17GetStreamingRelayStatus\x12%.terminal.StreamingRelayStatusRequest\x1a\x1e.terminal.StreamingRelayStatus\x12\x65\n\x14\x43\x61ncelStreamingRelay\x12%.terminal.StreamingRelayCancelRequest\x1a&.terminal.StreamingRelayCancelResponse\x12\x65\n\x14SendPushNotification\x12%.terminal.SendPushNotificationRequest\x1a&.terminal.SendPushNotificationResponse\x12>\n\x07\x45xtract\x12\x18.terminal.ExtractRequest\x1a\x19.terminal.ExtractResponse\x12\x41\n\x08RunAgent\x12\x19.terminal.RunAgentRequest\x1a\x1a.terminal.RunAgentResponse\x12J\n\x0b\x43\x61ncelAgent\x12\x1c.terminal.CancelAgentRequest\x1a\x1d.terminal.CancelAgentResponse\x12\x65\n\x14\x42rowserCreateSession\x12%.terminal.BrowserCreateSessionRequest\x1a&.terminal.BrowserCreateSessionResponse\x12\x62\n\x13\x42rowserCloseSession\x12$.terminal.BrowserCloseSessionRequest\x1a%.terminal.BrowserCloseSessionResponse\x12V\n\x0f\x42rowserNavigate\x12 .terminal.BrowserNavigateRequest\x1a!.terminal.BrowserNavigateResponse\x12M\n\x0c\x42rowserClick\x12\x1d.terminal.BrowserClickRequest\x1a\x1e.terminal.BrowserClickResponse\x12J\n\x0b\x42rowserType\x12\x1c.terminal.BrowserTypeRequest\x1a\x1d.terminal.BrowserTypeResponse\x12J\n\x0b\x42rowserWait\x12\x1c.terminal.BrowserWaitRequest\x1a\x1d.terminal.BrowserWaitResponse\x12S\n\x0e\x42rowserExtract\x12\x1f.terminal.BrowserExtractRequest\x1a .terminal.BrowserExtractResponse\x12\x62\n\x13\x42rowserExtractRegex\x12$.terminal.BrowserExtractRegexRequest\x1a%.terminal.BrowserExtractRegexResponse\x12S\n\x0e\x42rowserGetHTML\x12\x1f.terminal.BrowserGetHTMLRequest\x1a .terminal.BrowserGetHTMLResponse\x12S\n\x0e\x42rowserGetText\x12\x1f.terminal.BrowserGetTextRequest\x1a .terminal.BrowserGetTextResponse\x12\x65\n\x14\x42rowserExecuteScript\x12%.terminal.BrowserExecuteScriptRequest\x1a&.terminal.BrowserExecuteScriptResponse\x12\\\n\x11\x42rowserScreenshot\x12\".terminal.BrowserScreenshotRequest\x1a#.terminal.BrowserScreenshotResponse\x12V\n\x0f\x42rowserGetState\x12 .terminal.BrowserGetStateRequest\x1a!.terminal.BrowserGetStateResponse\x12\\\n\x11\x42rowserSetCookies\x12\".terminal.BrowserSetCookiesRequest\x1a#.terminal.BrowserSetCookiesResponse\x12\\\n\x11\x42rowserGetCookies\x12\".terminal.BrowserGetCookiesRequest\x1a#.terminal.BrowserGetCookiesResponse\x12q\n\x18\x42rowserValidateSelectors\x12).terminal.BrowserValidateSelectorsRequest\x1a*.terminal.BrowserValidateSelectorsResponse\x12_\n\x12\x42rowserExtractData\x12#.terminal.BrowserExtractDataRequest\x1a$.terminal.BrowserExtractDataResponse\x12Y\n\x10\x42rowserMouseMove\x12!.terminal.BrowserMouseMoveRequest\x1a\".terminal.BrowserMouseMoveResponse\x12M\n\x0c\x42rowserHover\x12\x1d.terminal.BrowserHoverRequest\x1a\x1e.terminal.BrowserHoverResponse\x12P\n\rBrowserScroll\x12\x1e.terminal.BrowserScrollRequest\x1a\x1f.terminal.BrowserScrollResponse\x12\x65\n\x14\x42rowserNetworkEnable\x12%.terminal.BrowserNetworkEnableRequest\x1a&.terminal.BrowserNetworkEnableResponse\x12h\n\x15\x42rowserNetworkDisable\x12&.terminal.BrowserNetworkDisableRequest\x1a\'.terminal.BrowserNetworkDisableResponse\x12w\n\x1a\x42rowserNetworkGetExchanges\x12+.terminal.BrowserNetworkGetExchangesRequest\x1a,.terminal.BrowserNetworkGetExchangesResponse\x12t\n\x19\x42rowserNetworkGetExchange\x12*.terminal.BrowserNetworkGetExchangeRequest\x1a+.terminal.BrowserNetworkGetExchangeResponse\x12h\n\x15\x42rowserNetworkGetLast\x12&.terminal.BrowserNetworkGetLastRequest\x1a\'.terminal.BrowserNetworkGetLastResponse\x12\x62\n\x13\x42rowserNetworkClear\x12$.terminal.BrowserNetworkClearRequest\x1a%.terminal.BrowserNetworkClearResponse\x12\x62\n\x13\x42rowserNetworkStats\x12$.terminal.BrowserNetworkStatsRequest\x1a%.terminal.BrowserNetworkStatsResponse\x12n\n\x17\x42rowserNetworkExportHAR\x12(.terminal.BrowserNetworkExportHARRequest\x1a).terminal.BrowserNetworkExportHARResponseB\x10Z\x0eterminal/protob\x06proto3')
|
|
129
129
|
|
|
130
130
|
_globals = globals()
|
|
131
131
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -134,5 +134,5 @@ if not _descriptor._USE_C_DESCRIPTORS:
|
|
|
134
134
|
_globals['DESCRIPTOR']._loaded_options = None
|
|
135
135
|
_globals['DESCRIPTOR']._serialized_options = b'Z\016terminal/proto'
|
|
136
136
|
_globals['_TERMINALSTREAMINGSERVICE']._serialized_start=133
|
|
137
|
-
_globals['_TERMINALSTREAMINGSERVICE']._serialized_end=
|
|
137
|
+
_globals['_TERMINALSTREAMINGSERVICE']._serialized_end=5867
|
|
138
138
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -335,6 +335,46 @@ class TerminalStreamingServiceStub(object):
|
|
|
335
335
|
request_serializer=rpc__messages_dot_browser__pb2.BrowserScrollRequest.SerializeToString,
|
|
336
336
|
response_deserializer=rpc__messages_dot_browser__pb2.BrowserScrollResponse.FromString,
|
|
337
337
|
_registered_method=True)
|
|
338
|
+
self.BrowserNetworkEnable = channel.unary_unary(
|
|
339
|
+
'/terminal.TerminalStreamingService/BrowserNetworkEnable',
|
|
340
|
+
request_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkEnableRequest.SerializeToString,
|
|
341
|
+
response_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkEnableResponse.FromString,
|
|
342
|
+
_registered_method=True)
|
|
343
|
+
self.BrowserNetworkDisable = channel.unary_unary(
|
|
344
|
+
'/terminal.TerminalStreamingService/BrowserNetworkDisable',
|
|
345
|
+
request_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkDisableRequest.SerializeToString,
|
|
346
|
+
response_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkDisableResponse.FromString,
|
|
347
|
+
_registered_method=True)
|
|
348
|
+
self.BrowserNetworkGetExchanges = channel.unary_unary(
|
|
349
|
+
'/terminal.TerminalStreamingService/BrowserNetworkGetExchanges',
|
|
350
|
+
request_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangesRequest.SerializeToString,
|
|
351
|
+
response_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangesResponse.FromString,
|
|
352
|
+
_registered_method=True)
|
|
353
|
+
self.BrowserNetworkGetExchange = channel.unary_unary(
|
|
354
|
+
'/terminal.TerminalStreamingService/BrowserNetworkGetExchange',
|
|
355
|
+
request_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangeRequest.SerializeToString,
|
|
356
|
+
response_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangeResponse.FromString,
|
|
357
|
+
_registered_method=True)
|
|
358
|
+
self.BrowserNetworkGetLast = channel.unary_unary(
|
|
359
|
+
'/terminal.TerminalStreamingService/BrowserNetworkGetLast',
|
|
360
|
+
request_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetLastRequest.SerializeToString,
|
|
361
|
+
response_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetLastResponse.FromString,
|
|
362
|
+
_registered_method=True)
|
|
363
|
+
self.BrowserNetworkClear = channel.unary_unary(
|
|
364
|
+
'/terminal.TerminalStreamingService/BrowserNetworkClear',
|
|
365
|
+
request_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkClearRequest.SerializeToString,
|
|
366
|
+
response_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkClearResponse.FromString,
|
|
367
|
+
_registered_method=True)
|
|
368
|
+
self.BrowserNetworkStats = channel.unary_unary(
|
|
369
|
+
'/terminal.TerminalStreamingService/BrowserNetworkStats',
|
|
370
|
+
request_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkStatsRequest.SerializeToString,
|
|
371
|
+
response_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkStatsResponse.FromString,
|
|
372
|
+
_registered_method=True)
|
|
373
|
+
self.BrowserNetworkExportHAR = channel.unary_unary(
|
|
374
|
+
'/terminal.TerminalStreamingService/BrowserNetworkExportHAR',
|
|
375
|
+
request_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkExportHARRequest.SerializeToString,
|
|
376
|
+
response_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkExportHARResponse.FromString,
|
|
377
|
+
_registered_method=True)
|
|
338
378
|
|
|
339
379
|
|
|
340
380
|
class TerminalStreamingServiceServicer(object):
|
|
@@ -713,6 +753,55 @@ class TerminalStreamingServiceServicer(object):
|
|
|
713
753
|
context.set_details('Method not implemented!')
|
|
714
754
|
raise NotImplementedError('Method not implemented!')
|
|
715
755
|
|
|
756
|
+
def BrowserNetworkEnable(self, request, context):
|
|
757
|
+
"""Network Capture (v2.19.0)
|
|
758
|
+
"""
|
|
759
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
760
|
+
context.set_details('Method not implemented!')
|
|
761
|
+
raise NotImplementedError('Method not implemented!')
|
|
762
|
+
|
|
763
|
+
def BrowserNetworkDisable(self, request, context):
|
|
764
|
+
"""Missing associated documentation comment in .proto file."""
|
|
765
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
766
|
+
context.set_details('Method not implemented!')
|
|
767
|
+
raise NotImplementedError('Method not implemented!')
|
|
768
|
+
|
|
769
|
+
def BrowserNetworkGetExchanges(self, request, context):
|
|
770
|
+
"""Missing associated documentation comment in .proto file."""
|
|
771
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
772
|
+
context.set_details('Method not implemented!')
|
|
773
|
+
raise NotImplementedError('Method not implemented!')
|
|
774
|
+
|
|
775
|
+
def BrowserNetworkGetExchange(self, request, context):
|
|
776
|
+
"""Missing associated documentation comment in .proto file."""
|
|
777
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
778
|
+
context.set_details('Method not implemented!')
|
|
779
|
+
raise NotImplementedError('Method not implemented!')
|
|
780
|
+
|
|
781
|
+
def BrowserNetworkGetLast(self, request, context):
|
|
782
|
+
"""Missing associated documentation comment in .proto file."""
|
|
783
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
784
|
+
context.set_details('Method not implemented!')
|
|
785
|
+
raise NotImplementedError('Method not implemented!')
|
|
786
|
+
|
|
787
|
+
def BrowserNetworkClear(self, request, context):
|
|
788
|
+
"""Missing associated documentation comment in .proto file."""
|
|
789
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
790
|
+
context.set_details('Method not implemented!')
|
|
791
|
+
raise NotImplementedError('Method not implemented!')
|
|
792
|
+
|
|
793
|
+
def BrowserNetworkStats(self, request, context):
|
|
794
|
+
"""Missing associated documentation comment in .proto file."""
|
|
795
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
796
|
+
context.set_details('Method not implemented!')
|
|
797
|
+
raise NotImplementedError('Method not implemented!')
|
|
798
|
+
|
|
799
|
+
def BrowserNetworkExportHAR(self, request, context):
|
|
800
|
+
"""Missing associated documentation comment in .proto file."""
|
|
801
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
802
|
+
context.set_details('Method not implemented!')
|
|
803
|
+
raise NotImplementedError('Method not implemented!')
|
|
804
|
+
|
|
716
805
|
|
|
717
806
|
def add_TerminalStreamingServiceServicer_to_server(servicer, server):
|
|
718
807
|
rpc_method_handlers = {
|
|
@@ -996,6 +1085,46 @@ def add_TerminalStreamingServiceServicer_to_server(servicer, server):
|
|
|
996
1085
|
request_deserializer=rpc__messages_dot_browser__pb2.BrowserScrollRequest.FromString,
|
|
997
1086
|
response_serializer=rpc__messages_dot_browser__pb2.BrowserScrollResponse.SerializeToString,
|
|
998
1087
|
),
|
|
1088
|
+
'BrowserNetworkEnable': grpc.unary_unary_rpc_method_handler(
|
|
1089
|
+
servicer.BrowserNetworkEnable,
|
|
1090
|
+
request_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkEnableRequest.FromString,
|
|
1091
|
+
response_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkEnableResponse.SerializeToString,
|
|
1092
|
+
),
|
|
1093
|
+
'BrowserNetworkDisable': grpc.unary_unary_rpc_method_handler(
|
|
1094
|
+
servicer.BrowserNetworkDisable,
|
|
1095
|
+
request_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkDisableRequest.FromString,
|
|
1096
|
+
response_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkDisableResponse.SerializeToString,
|
|
1097
|
+
),
|
|
1098
|
+
'BrowserNetworkGetExchanges': grpc.unary_unary_rpc_method_handler(
|
|
1099
|
+
servicer.BrowserNetworkGetExchanges,
|
|
1100
|
+
request_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangesRequest.FromString,
|
|
1101
|
+
response_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangesResponse.SerializeToString,
|
|
1102
|
+
),
|
|
1103
|
+
'BrowserNetworkGetExchange': grpc.unary_unary_rpc_method_handler(
|
|
1104
|
+
servicer.BrowserNetworkGetExchange,
|
|
1105
|
+
request_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangeRequest.FromString,
|
|
1106
|
+
response_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangeResponse.SerializeToString,
|
|
1107
|
+
),
|
|
1108
|
+
'BrowserNetworkGetLast': grpc.unary_unary_rpc_method_handler(
|
|
1109
|
+
servicer.BrowserNetworkGetLast,
|
|
1110
|
+
request_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetLastRequest.FromString,
|
|
1111
|
+
response_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkGetLastResponse.SerializeToString,
|
|
1112
|
+
),
|
|
1113
|
+
'BrowserNetworkClear': grpc.unary_unary_rpc_method_handler(
|
|
1114
|
+
servicer.BrowserNetworkClear,
|
|
1115
|
+
request_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkClearRequest.FromString,
|
|
1116
|
+
response_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkClearResponse.SerializeToString,
|
|
1117
|
+
),
|
|
1118
|
+
'BrowserNetworkStats': grpc.unary_unary_rpc_method_handler(
|
|
1119
|
+
servicer.BrowserNetworkStats,
|
|
1120
|
+
request_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkStatsRequest.FromString,
|
|
1121
|
+
response_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkStatsResponse.SerializeToString,
|
|
1122
|
+
),
|
|
1123
|
+
'BrowserNetworkExportHAR': grpc.unary_unary_rpc_method_handler(
|
|
1124
|
+
servicer.BrowserNetworkExportHAR,
|
|
1125
|
+
request_deserializer=rpc__messages_dot_browser__pb2.BrowserNetworkExportHARRequest.FromString,
|
|
1126
|
+
response_serializer=rpc__messages_dot_browser__pb2.BrowserNetworkExportHARResponse.SerializeToString,
|
|
1127
|
+
),
|
|
999
1128
|
}
|
|
1000
1129
|
generic_handler = grpc.method_handlers_generic_handler(
|
|
1001
1130
|
'terminal.TerminalStreamingService', rpc_method_handlers)
|
|
@@ -2522,3 +2651,219 @@ class TerminalStreamingService(object):
|
|
|
2522
2651
|
timeout,
|
|
2523
2652
|
metadata,
|
|
2524
2653
|
_registered_method=True)
|
|
2654
|
+
|
|
2655
|
+
@staticmethod
|
|
2656
|
+
def BrowserNetworkEnable(request,
|
|
2657
|
+
target,
|
|
2658
|
+
options=(),
|
|
2659
|
+
channel_credentials=None,
|
|
2660
|
+
call_credentials=None,
|
|
2661
|
+
insecure=False,
|
|
2662
|
+
compression=None,
|
|
2663
|
+
wait_for_ready=None,
|
|
2664
|
+
timeout=None,
|
|
2665
|
+
metadata=None):
|
|
2666
|
+
return grpc.experimental.unary_unary(
|
|
2667
|
+
request,
|
|
2668
|
+
target,
|
|
2669
|
+
'/terminal.TerminalStreamingService/BrowserNetworkEnable',
|
|
2670
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkEnableRequest.SerializeToString,
|
|
2671
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkEnableResponse.FromString,
|
|
2672
|
+
options,
|
|
2673
|
+
channel_credentials,
|
|
2674
|
+
insecure,
|
|
2675
|
+
call_credentials,
|
|
2676
|
+
compression,
|
|
2677
|
+
wait_for_ready,
|
|
2678
|
+
timeout,
|
|
2679
|
+
metadata,
|
|
2680
|
+
_registered_method=True)
|
|
2681
|
+
|
|
2682
|
+
@staticmethod
|
|
2683
|
+
def BrowserNetworkDisable(request,
|
|
2684
|
+
target,
|
|
2685
|
+
options=(),
|
|
2686
|
+
channel_credentials=None,
|
|
2687
|
+
call_credentials=None,
|
|
2688
|
+
insecure=False,
|
|
2689
|
+
compression=None,
|
|
2690
|
+
wait_for_ready=None,
|
|
2691
|
+
timeout=None,
|
|
2692
|
+
metadata=None):
|
|
2693
|
+
return grpc.experimental.unary_unary(
|
|
2694
|
+
request,
|
|
2695
|
+
target,
|
|
2696
|
+
'/terminal.TerminalStreamingService/BrowserNetworkDisable',
|
|
2697
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkDisableRequest.SerializeToString,
|
|
2698
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkDisableResponse.FromString,
|
|
2699
|
+
options,
|
|
2700
|
+
channel_credentials,
|
|
2701
|
+
insecure,
|
|
2702
|
+
call_credentials,
|
|
2703
|
+
compression,
|
|
2704
|
+
wait_for_ready,
|
|
2705
|
+
timeout,
|
|
2706
|
+
metadata,
|
|
2707
|
+
_registered_method=True)
|
|
2708
|
+
|
|
2709
|
+
@staticmethod
|
|
2710
|
+
def BrowserNetworkGetExchanges(request,
|
|
2711
|
+
target,
|
|
2712
|
+
options=(),
|
|
2713
|
+
channel_credentials=None,
|
|
2714
|
+
call_credentials=None,
|
|
2715
|
+
insecure=False,
|
|
2716
|
+
compression=None,
|
|
2717
|
+
wait_for_ready=None,
|
|
2718
|
+
timeout=None,
|
|
2719
|
+
metadata=None):
|
|
2720
|
+
return grpc.experimental.unary_unary(
|
|
2721
|
+
request,
|
|
2722
|
+
target,
|
|
2723
|
+
'/terminal.TerminalStreamingService/BrowserNetworkGetExchanges',
|
|
2724
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangesRequest.SerializeToString,
|
|
2725
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangesResponse.FromString,
|
|
2726
|
+
options,
|
|
2727
|
+
channel_credentials,
|
|
2728
|
+
insecure,
|
|
2729
|
+
call_credentials,
|
|
2730
|
+
compression,
|
|
2731
|
+
wait_for_ready,
|
|
2732
|
+
timeout,
|
|
2733
|
+
metadata,
|
|
2734
|
+
_registered_method=True)
|
|
2735
|
+
|
|
2736
|
+
@staticmethod
|
|
2737
|
+
def BrowserNetworkGetExchange(request,
|
|
2738
|
+
target,
|
|
2739
|
+
options=(),
|
|
2740
|
+
channel_credentials=None,
|
|
2741
|
+
call_credentials=None,
|
|
2742
|
+
insecure=False,
|
|
2743
|
+
compression=None,
|
|
2744
|
+
wait_for_ready=None,
|
|
2745
|
+
timeout=None,
|
|
2746
|
+
metadata=None):
|
|
2747
|
+
return grpc.experimental.unary_unary(
|
|
2748
|
+
request,
|
|
2749
|
+
target,
|
|
2750
|
+
'/terminal.TerminalStreamingService/BrowserNetworkGetExchange',
|
|
2751
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangeRequest.SerializeToString,
|
|
2752
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkGetExchangeResponse.FromString,
|
|
2753
|
+
options,
|
|
2754
|
+
channel_credentials,
|
|
2755
|
+
insecure,
|
|
2756
|
+
call_credentials,
|
|
2757
|
+
compression,
|
|
2758
|
+
wait_for_ready,
|
|
2759
|
+
timeout,
|
|
2760
|
+
metadata,
|
|
2761
|
+
_registered_method=True)
|
|
2762
|
+
|
|
2763
|
+
@staticmethod
|
|
2764
|
+
def BrowserNetworkGetLast(request,
|
|
2765
|
+
target,
|
|
2766
|
+
options=(),
|
|
2767
|
+
channel_credentials=None,
|
|
2768
|
+
call_credentials=None,
|
|
2769
|
+
insecure=False,
|
|
2770
|
+
compression=None,
|
|
2771
|
+
wait_for_ready=None,
|
|
2772
|
+
timeout=None,
|
|
2773
|
+
metadata=None):
|
|
2774
|
+
return grpc.experimental.unary_unary(
|
|
2775
|
+
request,
|
|
2776
|
+
target,
|
|
2777
|
+
'/terminal.TerminalStreamingService/BrowserNetworkGetLast',
|
|
2778
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkGetLastRequest.SerializeToString,
|
|
2779
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkGetLastResponse.FromString,
|
|
2780
|
+
options,
|
|
2781
|
+
channel_credentials,
|
|
2782
|
+
insecure,
|
|
2783
|
+
call_credentials,
|
|
2784
|
+
compression,
|
|
2785
|
+
wait_for_ready,
|
|
2786
|
+
timeout,
|
|
2787
|
+
metadata,
|
|
2788
|
+
_registered_method=True)
|
|
2789
|
+
|
|
2790
|
+
@staticmethod
|
|
2791
|
+
def BrowserNetworkClear(request,
|
|
2792
|
+
target,
|
|
2793
|
+
options=(),
|
|
2794
|
+
channel_credentials=None,
|
|
2795
|
+
call_credentials=None,
|
|
2796
|
+
insecure=False,
|
|
2797
|
+
compression=None,
|
|
2798
|
+
wait_for_ready=None,
|
|
2799
|
+
timeout=None,
|
|
2800
|
+
metadata=None):
|
|
2801
|
+
return grpc.experimental.unary_unary(
|
|
2802
|
+
request,
|
|
2803
|
+
target,
|
|
2804
|
+
'/terminal.TerminalStreamingService/BrowserNetworkClear',
|
|
2805
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkClearRequest.SerializeToString,
|
|
2806
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkClearResponse.FromString,
|
|
2807
|
+
options,
|
|
2808
|
+
channel_credentials,
|
|
2809
|
+
insecure,
|
|
2810
|
+
call_credentials,
|
|
2811
|
+
compression,
|
|
2812
|
+
wait_for_ready,
|
|
2813
|
+
timeout,
|
|
2814
|
+
metadata,
|
|
2815
|
+
_registered_method=True)
|
|
2816
|
+
|
|
2817
|
+
@staticmethod
|
|
2818
|
+
def BrowserNetworkStats(request,
|
|
2819
|
+
target,
|
|
2820
|
+
options=(),
|
|
2821
|
+
channel_credentials=None,
|
|
2822
|
+
call_credentials=None,
|
|
2823
|
+
insecure=False,
|
|
2824
|
+
compression=None,
|
|
2825
|
+
wait_for_ready=None,
|
|
2826
|
+
timeout=None,
|
|
2827
|
+
metadata=None):
|
|
2828
|
+
return grpc.experimental.unary_unary(
|
|
2829
|
+
request,
|
|
2830
|
+
target,
|
|
2831
|
+
'/terminal.TerminalStreamingService/BrowserNetworkStats',
|
|
2832
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkStatsRequest.SerializeToString,
|
|
2833
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkStatsResponse.FromString,
|
|
2834
|
+
options,
|
|
2835
|
+
channel_credentials,
|
|
2836
|
+
insecure,
|
|
2837
|
+
call_credentials,
|
|
2838
|
+
compression,
|
|
2839
|
+
wait_for_ready,
|
|
2840
|
+
timeout,
|
|
2841
|
+
metadata,
|
|
2842
|
+
_registered_method=True)
|
|
2843
|
+
|
|
2844
|
+
@staticmethod
|
|
2845
|
+
def BrowserNetworkExportHAR(request,
|
|
2846
|
+
target,
|
|
2847
|
+
options=(),
|
|
2848
|
+
channel_credentials=None,
|
|
2849
|
+
call_credentials=None,
|
|
2850
|
+
insecure=False,
|
|
2851
|
+
compression=None,
|
|
2852
|
+
wait_for_ready=None,
|
|
2853
|
+
timeout=None,
|
|
2854
|
+
metadata=None):
|
|
2855
|
+
return grpc.experimental.unary_unary(
|
|
2856
|
+
request,
|
|
2857
|
+
target,
|
|
2858
|
+
'/terminal.TerminalStreamingService/BrowserNetworkExportHAR',
|
|
2859
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkExportHARRequest.SerializeToString,
|
|
2860
|
+
rpc__messages_dot_browser__pb2.BrowserNetworkExportHARResponse.FromString,
|
|
2861
|
+
options,
|
|
2862
|
+
channel_credentials,
|
|
2863
|
+
insecure,
|
|
2864
|
+
call_credentials,
|
|
2865
|
+
compression,
|
|
2866
|
+
wait_for_ready,
|
|
2867
|
+
timeout,
|
|
2868
|
+
metadata,
|
|
2869
|
+
_registered_method=True)
|
|
@@ -5,6 +5,7 @@ from .input import InputCapability
|
|
|
5
5
|
from .timing import TimingCapability
|
|
6
6
|
from .dom import DOMCapability
|
|
7
7
|
from .fetch import FetchCapability
|
|
8
|
+
from .network import NetworkCapability
|
|
8
9
|
|
|
9
10
|
__all__ = [
|
|
10
11
|
"ScrollCapability",
|
|
@@ -12,4 +13,5 @@ __all__ = [
|
|
|
12
13
|
"TimingCapability",
|
|
13
14
|
"DOMCapability",
|
|
14
15
|
"FetchCapability",
|
|
16
|
+
"NetworkCapability",
|
|
15
17
|
]
|
|
@@ -35,8 +35,7 @@ class FetchCapability(BaseCapability):
|
|
|
35
35
|
if not urls:
|
|
36
36
|
return {}
|
|
37
37
|
js = build_fetch_all_js(urls, headers, credentials)
|
|
38
|
-
|
|
39
|
-
wrapped = build_async_js(js.replace("return ", ""))
|
|
38
|
+
wrapped = build_async_js(js)
|
|
40
39
|
result = parse_json_result(self._js(wrapped))
|
|
41
40
|
return result if isinstance(result, dict) else {}
|
|
42
41
|
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""Network capture capability (v2.19.0)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from cmdop.services.browser.models import (
|
|
7
|
+
NetworkExchange,
|
|
8
|
+
NetworkRequest,
|
|
9
|
+
NetworkResponse,
|
|
10
|
+
NetworkTiming,
|
|
11
|
+
NetworkStats,
|
|
12
|
+
NetworkFilter,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from ._base import BaseCapability
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NetworkCapability(BaseCapability):
|
|
19
|
+
"""Network capture operations.
|
|
20
|
+
|
|
21
|
+
Captures HTTP requests/responses made by the browser.
|
|
22
|
+
Useful for:
|
|
23
|
+
- Intercepting API responses
|
|
24
|
+
- Debugging network issues
|
|
25
|
+
- Extracting data from XHR/Fetch calls
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
# Enable capture
|
|
29
|
+
session.network.enable()
|
|
30
|
+
|
|
31
|
+
# Navigate and trigger requests
|
|
32
|
+
session.navigate("https://example.com")
|
|
33
|
+
|
|
34
|
+
# Get all captured exchanges
|
|
35
|
+
exchanges = session.network.get_all()
|
|
36
|
+
|
|
37
|
+
# Get last API response
|
|
38
|
+
api = session.network.last("/api/data")
|
|
39
|
+
data = api.json_body()
|
|
40
|
+
|
|
41
|
+
# Filter by criteria
|
|
42
|
+
xhr = session.network.filter(
|
|
43
|
+
url_pattern="/api/",
|
|
44
|
+
methods=["POST"],
|
|
45
|
+
status_codes=[200],
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Disable capture
|
|
49
|
+
session.network.disable()
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def enable(self, max_exchanges: int = 1000, max_response_size: int = 10_000_000) -> None:
|
|
53
|
+
"""Enable network capture.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
max_exchanges: Max exchanges to keep in memory (FIFO eviction)
|
|
57
|
+
max_response_size: Max response body size in bytes
|
|
58
|
+
"""
|
|
59
|
+
self._call("network_enable", max_exchanges, max_response_size)
|
|
60
|
+
|
|
61
|
+
def disable(self) -> None:
|
|
62
|
+
"""Disable network capture."""
|
|
63
|
+
self._call("network_disable")
|
|
64
|
+
|
|
65
|
+
def get_all(self, limit: int = 0) -> list[NetworkExchange]:
|
|
66
|
+
"""Get all captured exchanges.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
limit: Max results (0 = unlimited)
|
|
70
|
+
"""
|
|
71
|
+
return self.filter(limit=limit)
|
|
72
|
+
|
|
73
|
+
def filter(
|
|
74
|
+
self,
|
|
75
|
+
url_pattern: str = "",
|
|
76
|
+
methods: list[str] | None = None,
|
|
77
|
+
status_codes: list[int] | None = None,
|
|
78
|
+
resource_types: list[str] | None = None,
|
|
79
|
+
limit: int = 0,
|
|
80
|
+
) -> list[NetworkExchange]:
|
|
81
|
+
"""Get exchanges matching filter criteria.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
url_pattern: Regex pattern for URL matching
|
|
85
|
+
methods: HTTP methods (GET, POST, etc.)
|
|
86
|
+
status_codes: HTTP status codes (200, 404, etc.)
|
|
87
|
+
resource_types: xhr, fetch, document, script, image, etc.
|
|
88
|
+
limit: Max results (0 = unlimited)
|
|
89
|
+
"""
|
|
90
|
+
data = self._call(
|
|
91
|
+
"network_get_exchanges",
|
|
92
|
+
url_pattern,
|
|
93
|
+
methods or [],
|
|
94
|
+
status_codes or [],
|
|
95
|
+
resource_types or [],
|
|
96
|
+
limit,
|
|
97
|
+
)
|
|
98
|
+
return [self._parse_exchange(e) for e in data.get("exchanges", [])]
|
|
99
|
+
|
|
100
|
+
def get(self, exchange_id: str) -> NetworkExchange | None:
|
|
101
|
+
"""Get specific exchange by ID."""
|
|
102
|
+
data = self._call("network_get_exchange", exchange_id)
|
|
103
|
+
exchange = data.get("exchange")
|
|
104
|
+
if exchange:
|
|
105
|
+
return self._parse_exchange(exchange)
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
def last(self, url_pattern: str = "") -> NetworkExchange | None:
|
|
109
|
+
"""Get most recent exchange matching URL pattern.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
url_pattern: Regex pattern for URL (empty = any)
|
|
113
|
+
"""
|
|
114
|
+
data = self._call("network_get_last", url_pattern)
|
|
115
|
+
exchange = data.get("exchange")
|
|
116
|
+
if exchange:
|
|
117
|
+
return self._parse_exchange(exchange)
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
def clear(self) -> None:
|
|
121
|
+
"""Clear all captured exchanges."""
|
|
122
|
+
self._call("network_clear")
|
|
123
|
+
|
|
124
|
+
def stats(self) -> NetworkStats:
|
|
125
|
+
"""Get capture statistics."""
|
|
126
|
+
data = self._call("network_stats")
|
|
127
|
+
return NetworkStats(
|
|
128
|
+
enabled=data.get("enabled", False),
|
|
129
|
+
total_captured=data.get("total_captured", 0),
|
|
130
|
+
total_errors=data.get("total_errors", 0),
|
|
131
|
+
total_bytes=data.get("total_bytes", 0),
|
|
132
|
+
average_duration_ms=data.get("average_duration_ms", 0),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def export_har(
|
|
136
|
+
self,
|
|
137
|
+
url_pattern: str = "",
|
|
138
|
+
methods: list[str] | None = None,
|
|
139
|
+
status_codes: list[int] | None = None,
|
|
140
|
+
resource_types: list[str] | None = None,
|
|
141
|
+
) -> bytes:
|
|
142
|
+
"""Export captured exchanges to HAR format.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
url_pattern: Regex pattern for URL matching
|
|
146
|
+
methods: HTTP methods filter
|
|
147
|
+
status_codes: HTTP status codes filter
|
|
148
|
+
resource_types: Resource types filter
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
HAR JSON as bytes
|
|
152
|
+
"""
|
|
153
|
+
data = self._call(
|
|
154
|
+
"network_export_har",
|
|
155
|
+
url_pattern,
|
|
156
|
+
methods or [],
|
|
157
|
+
status_codes or [],
|
|
158
|
+
resource_types or [],
|
|
159
|
+
)
|
|
160
|
+
return data.get("har_data", b"")
|
|
161
|
+
|
|
162
|
+
# === Convenience Methods ===
|
|
163
|
+
|
|
164
|
+
def api_calls(self, url_pattern: str = "/api/") -> list[NetworkExchange]:
|
|
165
|
+
"""Get XHR/Fetch API calls matching pattern."""
|
|
166
|
+
return self.filter(
|
|
167
|
+
url_pattern=url_pattern,
|
|
168
|
+
resource_types=["xhr", "fetch"],
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def last_json(self, url_pattern: str = "") -> Any:
|
|
172
|
+
"""Get JSON body from most recent matching response."""
|
|
173
|
+
exchange = self.last(url_pattern)
|
|
174
|
+
if exchange:
|
|
175
|
+
return exchange.json_body()
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
def wait_for(self, url_pattern: str, timeout_ms: int = 30000) -> NetworkExchange | None:
|
|
179
|
+
"""Wait for a matching request to be captured.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
url_pattern: Regex pattern for URL
|
|
183
|
+
timeout_ms: Timeout in milliseconds
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Matching exchange or None if timeout
|
|
187
|
+
"""
|
|
188
|
+
import time
|
|
189
|
+
start = time.time()
|
|
190
|
+
timeout_sec = timeout_ms / 1000
|
|
191
|
+
|
|
192
|
+
while time.time() - start < timeout_sec:
|
|
193
|
+
exchange = self.last(url_pattern)
|
|
194
|
+
if exchange:
|
|
195
|
+
return exchange
|
|
196
|
+
time.sleep(0.1)
|
|
197
|
+
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
# === Internal ===
|
|
201
|
+
|
|
202
|
+
def _parse_exchange(self, data: dict[str, Any]) -> NetworkExchange:
|
|
203
|
+
"""Parse exchange from dict."""
|
|
204
|
+
request_data = data.get("request", {})
|
|
205
|
+
response_data = data.get("response")
|
|
206
|
+
timing_data = data.get("timing", {})
|
|
207
|
+
|
|
208
|
+
request = NetworkRequest(
|
|
209
|
+
url=request_data.get("url", ""),
|
|
210
|
+
method=request_data.get("method", "GET"),
|
|
211
|
+
headers=request_data.get("headers", {}),
|
|
212
|
+
body=request_data.get("body", b""),
|
|
213
|
+
content_type=request_data.get("content_type", ""),
|
|
214
|
+
resource_type=request_data.get("resource_type", ""),
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
response = None
|
|
218
|
+
if response_data:
|
|
219
|
+
response = NetworkResponse(
|
|
220
|
+
status=response_data.get("status", 0),
|
|
221
|
+
status_text=response_data.get("status_text", ""),
|
|
222
|
+
headers=response_data.get("headers", {}),
|
|
223
|
+
body=response_data.get("body", b""),
|
|
224
|
+
content_type=response_data.get("content_type", ""),
|
|
225
|
+
size=response_data.get("size", 0),
|
|
226
|
+
from_cache=response_data.get("from_cache", False),
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
timing = NetworkTiming(
|
|
230
|
+
started_at_ms=timing_data.get("started_at_ms", 0),
|
|
231
|
+
ended_at_ms=timing_data.get("ended_at_ms", 0),
|
|
232
|
+
duration_ms=timing_data.get("duration_ms", 0),
|
|
233
|
+
wait_time_ms=timing_data.get("wait_time_ms", 0),
|
|
234
|
+
receive_time_ms=timing_data.get("receive_time_ms", 0),
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return NetworkExchange(
|
|
238
|
+
id=data.get("id", ""),
|
|
239
|
+
request=request,
|
|
240
|
+
response=response,
|
|
241
|
+
timing=timing,
|
|
242
|
+
error=data.get("error", ""),
|
|
243
|
+
frame_id=data.get("frame_id", ""),
|
|
244
|
+
initiator=data.get("initiator", ""),
|
|
245
|
+
)
|