diff --git a/crates/agent-gateway/internal/proto/v2/gateway.pb.go b/crates/agent-gateway/internal/proto/v2/gateway.pb.go index 87351a5ae..6d6711efd 100644 --- a/crates/agent-gateway/internal/proto/v2/gateway.pb.go +++ b/crates/agent-gateway/internal/proto/v2/gateway.pb.go @@ -337,7 +337,7 @@ func (x ChatIngressAck_Action) Number() protoreflect.EnumNumber { // Deprecated: Use ChatIngressAck_Action.Descriptor instead. func (ChatIngressAck_Action) EnumDescriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{163, 0} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{165, 0} } type GatewayEnvelope struct { @@ -12858,6 +12858,105 @@ func (x *ErrorResponse) GetMessage() string { return "" } +type ProviderCustomHeader struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProviderCustomHeader) Reset() { + *x = ProviderCustomHeader{} + mi := &file_proto_v2_gateway_proto_msgTypes[150] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProviderCustomHeader) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProviderCustomHeader) ProtoMessage() {} + +func (x *ProviderCustomHeader) ProtoReflect() protoreflect.Message { + mi := &file_proto_v2_gateway_proto_msgTypes[150] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProviderCustomHeader.ProtoReflect.Descriptor instead. +func (*ProviderCustomHeader) Descriptor() ([]byte, []int) { + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{150} +} + +func (x *ProviderCustomHeader) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ProviderCustomHeader) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +// 包一层 message 只为拿到字段存在性:repeated 无法区分「草稿没带」与「草稿把头 +// 清空了」,而这两种情况在已保存供应商分支上的处理相反(前者沿用落库的头, +// 后者按空集发请求)。与 is_full_url 的 optional 同一套三态语义。 +type ProviderCustomHeaders struct { + state protoimpl.MessageState `protogen:"open.v1"` + Headers []*ProviderCustomHeader `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProviderCustomHeaders) Reset() { + *x = ProviderCustomHeaders{} + mi := &file_proto_v2_gateway_proto_msgTypes[151] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProviderCustomHeaders) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProviderCustomHeaders) ProtoMessage() {} + +func (x *ProviderCustomHeaders) ProtoReflect() protoreflect.Message { + mi := &file_proto_v2_gateway_proto_msgTypes[151] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProviderCustomHeaders.ProtoReflect.Descriptor instead. +func (*ProviderCustomHeaders) Descriptor() ([]byte, []int) { + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{151} +} + +func (x *ProviderCustomHeaders) GetHeaders() []*ProviderCustomHeader { + if x != nil { + return x.Headers + } + return nil +} + type ProviderModelsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` ProviderType string `protobuf:"bytes,1,opt,name=provider_type,json=providerType,proto3" json:"provider_type,omitempty"` @@ -12869,14 +12968,17 @@ type ProviderModelsRequest struct { // WebUI 编辑已保存供应商时用于让桌面端复用本地密钥;密钥不返回浏览器。 ProviderId string `protobuf:"bytes,6,opt,name=provider_id,json=providerId,proto3" json:"provider_id,omitempty"` // 当前草稿是否把 base_url 作为完整聊天端点解释;未提供时沿用已保存配置。 - IsFullUrl *bool `protobuf:"varint,7,opt,name=is_full_url,json=isFullUrl,proto3,oneof" json:"is_full_url,omitempty"` + IsFullUrl *bool `protobuf:"varint,7,opt,name=is_full_url,json=isFullUrl,proto3,oneof" json:"is_full_url,omitempty"` + // 用户在供应商设置里显式配置的自定义请求头;未提供时沿用已保存配置。 + // 鉴权头与 host/content-length 等仍由落地侧的保留头名单兜底,不可被覆盖。 + CustomHeaders *ProviderCustomHeaders `protobuf:"bytes,8,opt,name=custom_headers,json=customHeaders,proto3" json:"custom_headers,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ProviderModelsRequest) Reset() { *x = ProviderModelsRequest{} - mi := &file_proto_v2_gateway_proto_msgTypes[150] + mi := &file_proto_v2_gateway_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12888,7 +12990,7 @@ func (x *ProviderModelsRequest) String() string { func (*ProviderModelsRequest) ProtoMessage() {} func (x *ProviderModelsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[150] + mi := &file_proto_v2_gateway_proto_msgTypes[152] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12901,7 +13003,7 @@ func (x *ProviderModelsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderModelsRequest.ProtoReflect.Descriptor instead. func (*ProviderModelsRequest) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{150} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{152} } func (x *ProviderModelsRequest) GetProviderType() string { @@ -12953,6 +13055,13 @@ func (x *ProviderModelsRequest) GetIsFullUrl() bool { return false } +func (x *ProviderModelsRequest) GetCustomHeaders() *ProviderCustomHeaders { + if x != nil { + return x.CustomHeaders + } + return nil +} + type ProviderModelsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` ModelsJson string `protobuf:"bytes,1,opt,name=models_json,json=modelsJson,proto3" json:"models_json,omitempty"` @@ -12962,7 +13071,7 @@ type ProviderModelsResponse struct { func (x *ProviderModelsResponse) Reset() { *x = ProviderModelsResponse{} - mi := &file_proto_v2_gateway_proto_msgTypes[151] + mi := &file_proto_v2_gateway_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12974,7 +13083,7 @@ func (x *ProviderModelsResponse) String() string { func (*ProviderModelsResponse) ProtoMessage() {} func (x *ProviderModelsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[151] + mi := &file_proto_v2_gateway_proto_msgTypes[153] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12987,7 +13096,7 @@ func (x *ProviderModelsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderModelsResponse.ProtoReflect.Descriptor instead. func (*ProviderModelsResponse) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{151} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{153} } func (x *ProviderModelsResponse) GetModelsJson() string { @@ -13010,7 +13119,7 @@ type ProviderUsageRequest struct { func (x *ProviderUsageRequest) Reset() { *x = ProviderUsageRequest{} - mi := &file_proto_v2_gateway_proto_msgTypes[152] + mi := &file_proto_v2_gateway_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13022,7 +13131,7 @@ func (x *ProviderUsageRequest) String() string { func (*ProviderUsageRequest) ProtoMessage() {} func (x *ProviderUsageRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[152] + mi := &file_proto_v2_gateway_proto_msgTypes[154] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13035,7 +13144,7 @@ func (x *ProviderUsageRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderUsageRequest.ProtoReflect.Descriptor instead. func (*ProviderUsageRequest) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{152} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{154} } func (x *ProviderUsageRequest) GetProviderId() string { @@ -13068,7 +13177,7 @@ type ProviderUsageResponse struct { func (x *ProviderUsageResponse) Reset() { *x = ProviderUsageResponse{} - mi := &file_proto_v2_gateway_proto_msgTypes[153] + mi := &file_proto_v2_gateway_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13080,7 +13189,7 @@ func (x *ProviderUsageResponse) String() string { func (*ProviderUsageResponse) ProtoMessage() {} func (x *ProviderUsageResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[153] + mi := &file_proto_v2_gateway_proto_msgTypes[155] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13093,7 +13202,7 @@ func (x *ProviderUsageResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderUsageResponse.ProtoReflect.Descriptor instead. func (*ProviderUsageResponse) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{153} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{155} } func (x *ProviderUsageResponse) GetResultJson() string { @@ -13117,7 +13226,7 @@ type ChatIngressBatch struct { func (x *ChatIngressBatch) Reset() { *x = ChatIngressBatch{} - mi := &file_proto_v2_gateway_proto_msgTypes[154] + mi := &file_proto_v2_gateway_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13129,7 +13238,7 @@ func (x *ChatIngressBatch) String() string { func (*ChatIngressBatch) ProtoMessage() {} func (x *ChatIngressBatch) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[154] + mi := &file_proto_v2_gateway_proto_msgTypes[156] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13142,7 +13251,7 @@ func (x *ChatIngressBatch) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatIngressBatch.ProtoReflect.Descriptor instead. func (*ChatIngressBatch) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{154} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{156} } func (x *ChatIngressBatch) GetRunId() string { @@ -13188,7 +13297,7 @@ type ChatIngressRecord struct { func (x *ChatIngressRecord) Reset() { *x = ChatIngressRecord{} - mi := &file_proto_v2_gateway_proto_msgTypes[155] + mi := &file_proto_v2_gateway_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13200,7 +13309,7 @@ func (x *ChatIngressRecord) String() string { func (*ChatIngressRecord) ProtoMessage() {} func (x *ChatIngressRecord) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[155] + mi := &file_proto_v2_gateway_proto_msgTypes[157] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13213,7 +13322,7 @@ func (x *ChatIngressRecord) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatIngressRecord.ProtoReflect.Descriptor instead. func (*ChatIngressRecord) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{155} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{157} } func (x *ChatIngressRecord) GetPayload() isChatIngressRecord_Payload { @@ -13297,7 +13406,7 @@ type ChatIngressDelta struct { func (x *ChatIngressDelta) Reset() { *x = ChatIngressDelta{} - mi := &file_proto_v2_gateway_proto_msgTypes[156] + mi := &file_proto_v2_gateway_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13309,7 +13418,7 @@ func (x *ChatIngressDelta) String() string { func (*ChatIngressDelta) ProtoMessage() {} func (x *ChatIngressDelta) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[156] + mi := &file_proto_v2_gateway_proto_msgTypes[158] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13322,7 +13431,7 @@ func (x *ChatIngressDelta) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatIngressDelta.ProtoReflect.Descriptor instead. func (*ChatIngressDelta) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{156} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{158} } func (x *ChatIngressDelta) GetEventJson() string { @@ -13349,7 +13458,7 @@ type ChatIngressHeartbeat struct { func (x *ChatIngressHeartbeat) Reset() { *x = ChatIngressHeartbeat{} - mi := &file_proto_v2_gateway_proto_msgTypes[157] + mi := &file_proto_v2_gateway_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13361,7 +13470,7 @@ func (x *ChatIngressHeartbeat) String() string { func (*ChatIngressHeartbeat) ProtoMessage() {} func (x *ChatIngressHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[157] + mi := &file_proto_v2_gateway_proto_msgTypes[159] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13374,7 +13483,7 @@ func (x *ChatIngressHeartbeat) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatIngressHeartbeat.ProtoReflect.Descriptor instead. func (*ChatIngressHeartbeat) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{157} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{159} } func (x *ChatIngressHeartbeat) GetUpdatedAt() int64 { @@ -13399,7 +13508,7 @@ type ChatIngressCheckpoint struct { func (x *ChatIngressCheckpoint) Reset() { *x = ChatIngressCheckpoint{} - mi := &file_proto_v2_gateway_proto_msgTypes[158] + mi := &file_proto_v2_gateway_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13411,7 +13520,7 @@ func (x *ChatIngressCheckpoint) String() string { func (*ChatIngressCheckpoint) ProtoMessage() {} func (x *ChatIngressCheckpoint) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[158] + mi := &file_proto_v2_gateway_proto_msgTypes[160] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13424,7 +13533,7 @@ func (x *ChatIngressCheckpoint) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatIngressCheckpoint.ProtoReflect.Descriptor instead. func (*ChatIngressCheckpoint) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{158} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{160} } func (x *ChatIngressCheckpoint) GetCoversThroughSeq() uint64 { @@ -13494,7 +13603,7 @@ type ChatIngressTerminal struct { func (x *ChatIngressTerminal) Reset() { *x = ChatIngressTerminal{} - mi := &file_proto_v2_gateway_proto_msgTypes[159] + mi := &file_proto_v2_gateway_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13506,7 +13615,7 @@ func (x *ChatIngressTerminal) String() string { func (*ChatIngressTerminal) ProtoMessage() {} func (x *ChatIngressTerminal) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[159] + mi := &file_proto_v2_gateway_proto_msgTypes[161] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13519,7 +13628,7 @@ func (x *ChatIngressTerminal) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatIngressTerminal.ProtoReflect.Descriptor instead. func (*ChatIngressTerminal) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{159} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{161} } func (x *ChatIngressTerminal) GetCoversThroughSeq() uint64 { @@ -13603,7 +13712,7 @@ type ChatIngressResume struct { func (x *ChatIngressResume) Reset() { *x = ChatIngressResume{} - mi := &file_proto_v2_gateway_proto_msgTypes[160] + mi := &file_proto_v2_gateway_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13615,7 +13724,7 @@ func (x *ChatIngressResume) String() string { func (*ChatIngressResume) ProtoMessage() {} func (x *ChatIngressResume) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[160] + mi := &file_proto_v2_gateway_proto_msgTypes[162] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13628,7 +13737,7 @@ func (x *ChatIngressResume) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatIngressResume.ProtoReflect.Descriptor instead. func (*ChatIngressResume) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{160} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{162} } func (x *ChatIngressResume) GetRuns() []*ChatIngressRunResume { @@ -13654,7 +13763,7 @@ type ChatIngressRunResume struct { func (x *ChatIngressRunResume) Reset() { *x = ChatIngressRunResume{} - mi := &file_proto_v2_gateway_proto_msgTypes[161] + mi := &file_proto_v2_gateway_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13666,7 +13775,7 @@ func (x *ChatIngressRunResume) String() string { func (*ChatIngressRunResume) ProtoMessage() {} func (x *ChatIngressRunResume) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[161] + mi := &file_proto_v2_gateway_proto_msgTypes[163] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13679,7 +13788,7 @@ func (x *ChatIngressRunResume) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatIngressRunResume.ProtoReflect.Descriptor instead. func (*ChatIngressRunResume) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{161} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{163} } func (x *ChatIngressRunResume) GetRunId() string { @@ -13757,7 +13866,7 @@ type ChatIngressFragment struct { func (x *ChatIngressFragment) Reset() { *x = ChatIngressFragment{} - mi := &file_proto_v2_gateway_proto_msgTypes[162] + mi := &file_proto_v2_gateway_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13769,7 +13878,7 @@ func (x *ChatIngressFragment) String() string { func (*ChatIngressFragment) ProtoMessage() {} func (x *ChatIngressFragment) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[162] + mi := &file_proto_v2_gateway_proto_msgTypes[164] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13782,7 +13891,7 @@ func (x *ChatIngressFragment) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatIngressFragment.ProtoReflect.Descriptor instead. func (*ChatIngressFragment) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{162} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{164} } func (x *ChatIngressFragment) GetRunId() string { @@ -13857,7 +13966,7 @@ type ChatIngressAck struct { func (x *ChatIngressAck) Reset() { *x = ChatIngressAck{} - mi := &file_proto_v2_gateway_proto_msgTypes[163] + mi := &file_proto_v2_gateway_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13869,7 +13978,7 @@ func (x *ChatIngressAck) String() string { func (*ChatIngressAck) ProtoMessage() {} func (x *ChatIngressAck) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[163] + mi := &file_proto_v2_gateway_proto_msgTypes[165] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13882,7 +13991,7 @@ func (x *ChatIngressAck) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatIngressAck.ProtoReflect.Descriptor instead. func (*ChatIngressAck) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{163} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{165} } func (x *ChatIngressAck) GetRunId() string { @@ -13951,7 +14060,7 @@ type HistorySetCwdRequest struct { func (x *HistorySetCwdRequest) Reset() { *x = HistorySetCwdRequest{} - mi := &file_proto_v2_gateway_proto_msgTypes[164] + mi := &file_proto_v2_gateway_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13963,7 +14072,7 @@ func (x *HistorySetCwdRequest) String() string { func (*HistorySetCwdRequest) ProtoMessage() {} func (x *HistorySetCwdRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[164] + mi := &file_proto_v2_gateway_proto_msgTypes[166] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13976,7 +14085,7 @@ func (x *HistorySetCwdRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use HistorySetCwdRequest.ProtoReflect.Descriptor instead. func (*HistorySetCwdRequest) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{164} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{166} } func (x *HistorySetCwdRequest) GetConversationId() string { @@ -14002,7 +14111,7 @@ type HistorySetCwdResponse struct { func (x *HistorySetCwdResponse) Reset() { *x = HistorySetCwdResponse{} - mi := &file_proto_v2_gateway_proto_msgTypes[165] + mi := &file_proto_v2_gateway_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14014,7 +14123,7 @@ func (x *HistorySetCwdResponse) String() string { func (*HistorySetCwdResponse) ProtoMessage() {} func (x *HistorySetCwdResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[165] + mi := &file_proto_v2_gateway_proto_msgTypes[167] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14027,7 +14136,7 @@ func (x *HistorySetCwdResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use HistorySetCwdResponse.ProtoReflect.Descriptor instead. func (*HistorySetCwdResponse) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{165} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{167} } func (x *HistorySetCwdResponse) GetConversation() *ConversationSummary { @@ -14049,7 +14158,7 @@ type WorkspaceRootGrantDraft struct { func (x *WorkspaceRootGrantDraft) Reset() { *x = WorkspaceRootGrantDraft{} - mi := &file_proto_v2_gateway_proto_msgTypes[166] + mi := &file_proto_v2_gateway_proto_msgTypes[168] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14061,7 +14170,7 @@ func (x *WorkspaceRootGrantDraft) String() string { func (*WorkspaceRootGrantDraft) ProtoMessage() {} func (x *WorkspaceRootGrantDraft) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[166] + mi := &file_proto_v2_gateway_proto_msgTypes[168] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14074,7 +14183,7 @@ func (x *WorkspaceRootGrantDraft) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceRootGrantDraft.ProtoReflect.Descriptor instead. func (*WorkspaceRootGrantDraft) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{166} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{168} } func (x *WorkspaceRootGrantDraft) GetId() string { @@ -14123,7 +14232,7 @@ type WorkspaceRootGrant struct { func (x *WorkspaceRootGrant) Reset() { *x = WorkspaceRootGrant{} - mi := &file_proto_v2_gateway_proto_msgTypes[167] + mi := &file_proto_v2_gateway_proto_msgTypes[169] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14135,7 +14244,7 @@ func (x *WorkspaceRootGrant) String() string { func (*WorkspaceRootGrant) ProtoMessage() {} func (x *WorkspaceRootGrant) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[167] + mi := &file_proto_v2_gateway_proto_msgTypes[169] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14148,7 +14257,7 @@ func (x *WorkspaceRootGrant) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceRootGrant.ProtoReflect.Descriptor instead. func (*WorkspaceRootGrant) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{167} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{169} } func (x *WorkspaceRootGrant) GetId() string { @@ -14233,7 +14342,7 @@ type WorkspaceRootGrantsRequest struct { func (x *WorkspaceRootGrantsRequest) Reset() { *x = WorkspaceRootGrantsRequest{} - mi := &file_proto_v2_gateway_proto_msgTypes[168] + mi := &file_proto_v2_gateway_proto_msgTypes[170] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14245,7 +14354,7 @@ func (x *WorkspaceRootGrantsRequest) String() string { func (*WorkspaceRootGrantsRequest) ProtoMessage() {} func (x *WorkspaceRootGrantsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[168] + mi := &file_proto_v2_gateway_proto_msgTypes[170] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14258,7 +14367,7 @@ func (x *WorkspaceRootGrantsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceRootGrantsRequest.ProtoReflect.Descriptor instead. func (*WorkspaceRootGrantsRequest) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{168} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{170} } func (x *WorkspaceRootGrantsRequest) GetAction() string { @@ -14298,7 +14407,7 @@ type WorkspaceRootGrantsResponse struct { func (x *WorkspaceRootGrantsResponse) Reset() { *x = WorkspaceRootGrantsResponse{} - mi := &file_proto_v2_gateway_proto_msgTypes[169] + mi := &file_proto_v2_gateway_proto_msgTypes[171] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14310,7 +14419,7 @@ func (x *WorkspaceRootGrantsResponse) String() string { func (*WorkspaceRootGrantsResponse) ProtoMessage() {} func (x *WorkspaceRootGrantsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[169] + mi := &file_proto_v2_gateway_proto_msgTypes[171] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14323,7 +14432,7 @@ func (x *WorkspaceRootGrantsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkspaceRootGrantsResponse.ProtoReflect.Descriptor instead. func (*WorkspaceRootGrantsResponse) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{169} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{171} } func (x *WorkspaceRootGrantsResponse) GetGrants() []*WorkspaceRootGrant { @@ -14343,7 +14452,7 @@ type CheckpointExpectedEntry struct { func (x *CheckpointExpectedEntry) Reset() { *x = CheckpointExpectedEntry{} - mi := &file_proto_v2_gateway_proto_msgTypes[170] + mi := &file_proto_v2_gateway_proto_msgTypes[172] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14355,7 +14464,7 @@ func (x *CheckpointExpectedEntry) String() string { func (*CheckpointExpectedEntry) ProtoMessage() {} func (x *CheckpointExpectedEntry) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[170] + mi := &file_proto_v2_gateway_proto_msgTypes[172] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14368,7 +14477,7 @@ func (x *CheckpointExpectedEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointExpectedEntry.ProtoReflect.Descriptor instead. func (*CheckpointExpectedEntry) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{170} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{172} } func (x *CheckpointExpectedEntry) GetKey() string { @@ -14398,7 +14507,7 @@ type CheckpointRequest struct { func (x *CheckpointRequest) Reset() { *x = CheckpointRequest{} - mi := &file_proto_v2_gateway_proto_msgTypes[171] + mi := &file_proto_v2_gateway_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14410,7 +14519,7 @@ func (x *CheckpointRequest) String() string { func (*CheckpointRequest) ProtoMessage() {} func (x *CheckpointRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[171] + mi := &file_proto_v2_gateway_proto_msgTypes[173] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14423,7 +14532,7 @@ func (x *CheckpointRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointRequest.ProtoReflect.Descriptor instead. func (*CheckpointRequest) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{171} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{173} } func (x *CheckpointRequest) GetAction() string { @@ -14471,7 +14580,7 @@ type CheckpointResponse struct { func (x *CheckpointResponse) Reset() { *x = CheckpointResponse{} - mi := &file_proto_v2_gateway_proto_msgTypes[172] + mi := &file_proto_v2_gateway_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14483,7 +14592,7 @@ func (x *CheckpointResponse) String() string { func (*CheckpointResponse) ProtoMessage() {} func (x *CheckpointResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[172] + mi := &file_proto_v2_gateway_proto_msgTypes[174] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14496,7 +14605,7 @@ func (x *CheckpointResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointResponse.ProtoReflect.Descriptor instead. func (*CheckpointResponse) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{172} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{174} } func (x *CheckpointResponse) GetAction() string { @@ -14525,7 +14634,7 @@ type ChatConversationReference struct { func (x *ChatConversationReference) Reset() { *x = ChatConversationReference{} - mi := &file_proto_v2_gateway_proto_msgTypes[173] + mi := &file_proto_v2_gateway_proto_msgTypes[175] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14537,7 +14646,7 @@ func (x *ChatConversationReference) String() string { func (*ChatConversationReference) ProtoMessage() {} func (x *ChatConversationReference) ProtoReflect() protoreflect.Message { - mi := &file_proto_v2_gateway_proto_msgTypes[173] + mi := &file_proto_v2_gateway_proto_msgTypes[175] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14550,7 +14659,7 @@ func (x *ChatConversationReference) ProtoReflect() protoreflect.Message { // Deprecated: Use ChatConversationReference.ProtoReflect.Descriptor instead. func (*ChatConversationReference) Descriptor() ([]byte, []int) { - return file_proto_v2_gateway_proto_rawDescGZIP(), []int{173} + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{175} } func (x *ChatConversationReference) GetId() string { @@ -15653,7 +15762,12 @@ const file_proto_v2_gateway_proto_rawDesc = "" + "\ttimestamp\x18\x01 \x01(\x03R\ttimestamp\"=\n" + "\rErrorResponse\x12\x12\n" + "\x04code\x18\x01 \x01(\x05R\x04code\x12\x18\n" + - "\amessage\x18\x02 \x01(\tR\amessage\"\x8f\x02\n" + + "\amessage\x18\x02 \x01(\tR\amessage\"@\n" + + "\x14ProviderCustomHeader\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value\"]\n" + + "\x15ProviderCustomHeaders\x12D\n" + + "\aheaders\x18\x01 \x03(\v2*.liveagent.gateway.v2.ProviderCustomHeaderR\aheaders\"\xe3\x02\n" + "\x15ProviderModelsRequest\x12#\n" + "\rprovider_type\x18\x01 \x01(\tR\fproviderType\x12\x19\n" + "\bbase_url\x18\x02 \x01(\tR\abaseUrl\x12\x17\n" + @@ -15663,7 +15777,8 @@ const file_proto_v2_gateway_proto_rawDesc = "" + "models_url\x18\x05 \x01(\tR\tmodelsUrl\x12\x1f\n" + "\vprovider_id\x18\x06 \x01(\tR\n" + "providerId\x12#\n" + - "\vis_full_url\x18\a \x01(\bH\x00R\tisFullUrl\x88\x01\x01B\x0e\n" + + "\vis_full_url\x18\a \x01(\bH\x00R\tisFullUrl\x88\x01\x01\x12R\n" + + "\x0ecustom_headers\x18\b \x01(\v2+.liveagent.gateway.v2.ProviderCustomHeadersR\rcustomHeadersB\x0e\n" + "\f_is_full_url\"9\n" + "\x16ProviderModelsResponse\x12\x1f\n" + "\vmodels_json\x18\x01 \x01(\tR\n" + @@ -15850,7 +15965,7 @@ func file_proto_v2_gateway_proto_rawDescGZIP() []byte { } var file_proto_v2_gateway_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_proto_v2_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 174) +var file_proto_v2_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 176) var file_proto_v2_gateway_proto_goTypes = []any{ (ImportDirectoryOperation)(0), // 0: liveagent.gateway.v2.ImportDirectoryOperation (TunnelFrameKind)(0), // 1: liveagent.gateway.v2.TunnelFrameKind @@ -16007,30 +16122,32 @@ var file_proto_v2_gateway_proto_goTypes = []any{ (*PingRequest)(nil), // 152: liveagent.gateway.v2.PingRequest (*PongResponse)(nil), // 153: liveagent.gateway.v2.PongResponse (*ErrorResponse)(nil), // 154: liveagent.gateway.v2.ErrorResponse - (*ProviderModelsRequest)(nil), // 155: liveagent.gateway.v2.ProviderModelsRequest - (*ProviderModelsResponse)(nil), // 156: liveagent.gateway.v2.ProviderModelsResponse - (*ProviderUsageRequest)(nil), // 157: liveagent.gateway.v2.ProviderUsageRequest - (*ProviderUsageResponse)(nil), // 158: liveagent.gateway.v2.ProviderUsageResponse - (*ChatIngressBatch)(nil), // 159: liveagent.gateway.v2.ChatIngressBatch - (*ChatIngressRecord)(nil), // 160: liveagent.gateway.v2.ChatIngressRecord - (*ChatIngressDelta)(nil), // 161: liveagent.gateway.v2.ChatIngressDelta - (*ChatIngressHeartbeat)(nil), // 162: liveagent.gateway.v2.ChatIngressHeartbeat - (*ChatIngressCheckpoint)(nil), // 163: liveagent.gateway.v2.ChatIngressCheckpoint - (*ChatIngressTerminal)(nil), // 164: liveagent.gateway.v2.ChatIngressTerminal - (*ChatIngressResume)(nil), // 165: liveagent.gateway.v2.ChatIngressResume - (*ChatIngressRunResume)(nil), // 166: liveagent.gateway.v2.ChatIngressRunResume - (*ChatIngressFragment)(nil), // 167: liveagent.gateway.v2.ChatIngressFragment - (*ChatIngressAck)(nil), // 168: liveagent.gateway.v2.ChatIngressAck - (*HistorySetCwdRequest)(nil), // 169: liveagent.gateway.v2.HistorySetCwdRequest - (*HistorySetCwdResponse)(nil), // 170: liveagent.gateway.v2.HistorySetCwdResponse - (*WorkspaceRootGrantDraft)(nil), // 171: liveagent.gateway.v2.WorkspaceRootGrantDraft - (*WorkspaceRootGrant)(nil), // 172: liveagent.gateway.v2.WorkspaceRootGrant - (*WorkspaceRootGrantsRequest)(nil), // 173: liveagent.gateway.v2.WorkspaceRootGrantsRequest - (*WorkspaceRootGrantsResponse)(nil), // 174: liveagent.gateway.v2.WorkspaceRootGrantsResponse - (*CheckpointExpectedEntry)(nil), // 175: liveagent.gateway.v2.CheckpointExpectedEntry - (*CheckpointRequest)(nil), // 176: liveagent.gateway.v2.CheckpointRequest - (*CheckpointResponse)(nil), // 177: liveagent.gateway.v2.CheckpointResponse - (*ChatConversationReference)(nil), // 178: liveagent.gateway.v2.ChatConversationReference + (*ProviderCustomHeader)(nil), // 155: liveagent.gateway.v2.ProviderCustomHeader + (*ProviderCustomHeaders)(nil), // 156: liveagent.gateway.v2.ProviderCustomHeaders + (*ProviderModelsRequest)(nil), // 157: liveagent.gateway.v2.ProviderModelsRequest + (*ProviderModelsResponse)(nil), // 158: liveagent.gateway.v2.ProviderModelsResponse + (*ProviderUsageRequest)(nil), // 159: liveagent.gateway.v2.ProviderUsageRequest + (*ProviderUsageResponse)(nil), // 160: liveagent.gateway.v2.ProviderUsageResponse + (*ChatIngressBatch)(nil), // 161: liveagent.gateway.v2.ChatIngressBatch + (*ChatIngressRecord)(nil), // 162: liveagent.gateway.v2.ChatIngressRecord + (*ChatIngressDelta)(nil), // 163: liveagent.gateway.v2.ChatIngressDelta + (*ChatIngressHeartbeat)(nil), // 164: liveagent.gateway.v2.ChatIngressHeartbeat + (*ChatIngressCheckpoint)(nil), // 165: liveagent.gateway.v2.ChatIngressCheckpoint + (*ChatIngressTerminal)(nil), // 166: liveagent.gateway.v2.ChatIngressTerminal + (*ChatIngressResume)(nil), // 167: liveagent.gateway.v2.ChatIngressResume + (*ChatIngressRunResume)(nil), // 168: liveagent.gateway.v2.ChatIngressRunResume + (*ChatIngressFragment)(nil), // 169: liveagent.gateway.v2.ChatIngressFragment + (*ChatIngressAck)(nil), // 170: liveagent.gateway.v2.ChatIngressAck + (*HistorySetCwdRequest)(nil), // 171: liveagent.gateway.v2.HistorySetCwdRequest + (*HistorySetCwdResponse)(nil), // 172: liveagent.gateway.v2.HistorySetCwdResponse + (*WorkspaceRootGrantDraft)(nil), // 173: liveagent.gateway.v2.WorkspaceRootGrantDraft + (*WorkspaceRootGrant)(nil), // 174: liveagent.gateway.v2.WorkspaceRootGrant + (*WorkspaceRootGrantsRequest)(nil), // 175: liveagent.gateway.v2.WorkspaceRootGrantsRequest + (*WorkspaceRootGrantsResponse)(nil), // 176: liveagent.gateway.v2.WorkspaceRootGrantsResponse + (*CheckpointExpectedEntry)(nil), // 177: liveagent.gateway.v2.CheckpointExpectedEntry + (*CheckpointRequest)(nil), // 178: liveagent.gateway.v2.CheckpointRequest + (*CheckpointResponse)(nil), // 179: liveagent.gateway.v2.CheckpointResponse + (*ChatConversationReference)(nil), // 180: liveagent.gateway.v2.ChatConversationReference } var file_proto_v2_gateway_proto_depIdxs = []int32{ 62, // 0: liveagent.gateway.v2.GatewayEnvelope.chat_command:type_name -> liveagent.gateway.v2.ChatCommandRequest @@ -16070,22 +16187,22 @@ var file_proto_v2_gateway_proto_depIdxs = []int32{ 138, // 34: liveagent.gateway.v2.GatewayEnvelope.fs_read_editable_text:type_name -> liveagent.gateway.v2.FsReadEditableTextRequest 140, // 35: liveagent.gateway.v2.GatewayEnvelope.fs_read_workspace_image:type_name -> liveagent.gateway.v2.FsReadWorkspaceImageRequest 42, // 36: liveagent.gateway.v2.GatewayEnvelope.sftp_request:type_name -> liveagent.gateway.v2.SftpRequest - 155, // 37: liveagent.gateway.v2.GatewayEnvelope.provider_models:type_name -> liveagent.gateway.v2.ProviderModelsRequest + 157, // 37: liveagent.gateway.v2.GatewayEnvelope.provider_models:type_name -> liveagent.gateway.v2.ProviderModelsRequest 108, // 38: liveagent.gateway.v2.GatewayEnvelope.settings_reset_ssh_known_host:type_name -> liveagent.gateway.v2.SettingsResetSshKnownHostRequest 63, // 39: liveagent.gateway.v2.GatewayEnvelope.chat_queue:type_name -> liveagent.gateway.v2.ChatQueueRequest - 168, // 40: liveagent.gateway.v2.GatewayEnvelope.chat_ingress_ack:type_name -> liveagent.gateway.v2.ChatIngressAck + 170, // 40: liveagent.gateway.v2.GatewayEnvelope.chat_ingress_ack:type_name -> liveagent.gateway.v2.ChatIngressAck 24, // 41: liveagent.gateway.v2.GatewayEnvelope.tunnel_state:type_name -> liveagent.gateway.v2.TunnelStateSnapshot 25, // 42: liveagent.gateway.v2.GatewayEnvelope.tunnel_mutation:type_name -> liveagent.gateway.v2.TunnelMutation 30, // 43: liveagent.gateway.v2.GatewayEnvelope.tunnel_frame:type_name -> liveagent.gateway.v2.TunnelFrame 31, // 44: liveagent.gateway.v2.GatewayEnvelope.workspace_watch:type_name -> liveagent.gateway.v2.WorkspaceWatchRequest 35, // 45: liveagent.gateway.v2.GatewayEnvelope.managed_process_request:type_name -> liveagent.gateway.v2.ManagedProcessRequest 85, // 46: liveagent.gateway.v2.GatewayEnvelope.history_branch:type_name -> liveagent.gateway.v2.HistoryBranchRequest - 157, // 47: liveagent.gateway.v2.GatewayEnvelope.provider_usage:type_name -> liveagent.gateway.v2.ProviderUsageRequest + 159, // 47: liveagent.gateway.v2.GatewayEnvelope.provider_usage:type_name -> liveagent.gateway.v2.ProviderUsageRequest 142, // 48: liveagent.gateway.v2.GatewayEnvelope.chat_file_open:type_name -> liveagent.gateway.v2.ChatFileOpenRequest - 169, // 49: liveagent.gateway.v2.GatewayEnvelope.history_set_cwd:type_name -> liveagent.gateway.v2.HistorySetCwdRequest - 173, // 50: liveagent.gateway.v2.GatewayEnvelope.workspace_root_grants:type_name -> liveagent.gateway.v2.WorkspaceRootGrantsRequest + 171, // 49: liveagent.gateway.v2.GatewayEnvelope.history_set_cwd:type_name -> liveagent.gateway.v2.HistorySetCwdRequest + 175, // 50: liveagent.gateway.v2.GatewayEnvelope.workspace_root_grants:type_name -> liveagent.gateway.v2.WorkspaceRootGrantsRequest 16, // 51: liveagent.gateway.v2.GatewayEnvelope.import_directory:type_name -> liveagent.gateway.v2.ImportDirectoryRequest - 176, // 52: liveagent.gateway.v2.GatewayEnvelope.checkpoint:type_name -> liveagent.gateway.v2.CheckpointRequest + 178, // 52: liveagent.gateway.v2.GatewayEnvelope.checkpoint:type_name -> liveagent.gateway.v2.CheckpointRequest 78, // 53: liveagent.gateway.v2.GatewayEnvelope.trajectory_fetch:type_name -> liveagent.gateway.v2.TrajectoryFetchRequest 122, // 54: liveagent.gateway.v2.GatewayEnvelope.installed_apps_list:type_name -> liveagent.gateway.v2.InstalledAppsListRequest 9, // 55: liveagent.gateway.v2.GatewayEnvelope.clarify_turn:type_name -> liveagent.gateway.v2.ClarifyTurnRequest @@ -16137,7 +16254,7 @@ var file_proto_v2_gateway_proto_depIdxs = []int32{ 69, // 101: liveagent.gateway.v2.AgentEnvelope.runtime_status:type_name -> liveagent.gateway.v2.RuntimeStatusEvent 109, // 102: liveagent.gateway.v2.AgentEnvelope.settings_reset_ssh_known_host_resp:type_name -> liveagent.gateway.v2.SettingsResetSshKnownHostResponse 68, // 103: liveagent.gateway.v2.AgentEnvelope.chat_runtime_snapshot:type_name -> liveagent.gateway.v2.ChatRuntimeSnapshot - 156, // 104: liveagent.gateway.v2.AgentEnvelope.provider_models_resp:type_name -> liveagent.gateway.v2.ProviderModelsResponse + 158, // 104: liveagent.gateway.v2.AgentEnvelope.provider_models_resp:type_name -> liveagent.gateway.v2.ProviderModelsResponse 21, // 105: liveagent.gateway.v2.AgentEnvelope.tunnel_desired:type_name -> liveagent.gateway.v2.TunnelDesiredState 26, // 106: liveagent.gateway.v2.AgentEnvelope.tunnel_mutation_result:type_name -> liveagent.gateway.v2.TunnelMutationResult 30, // 107: liveagent.gateway.v2.AgentEnvelope.tunnel_frame:type_name -> liveagent.gateway.v2.TunnelFrame @@ -16146,16 +16263,16 @@ var file_proto_v2_gateway_proto_depIdxs = []int32{ 36, // 110: liveagent.gateway.v2.AgentEnvelope.managed_process_response:type_name -> liveagent.gateway.v2.ManagedProcessResponse 34, // 111: liveagent.gateway.v2.AgentEnvelope.managed_process_snapshot:type_name -> liveagent.gateway.v2.ManagedProcessSnapshot 86, // 112: liveagent.gateway.v2.AgentEnvelope.history_branch_resp:type_name -> liveagent.gateway.v2.HistoryBranchResponse - 158, // 113: liveagent.gateway.v2.AgentEnvelope.provider_usage_resp:type_name -> liveagent.gateway.v2.ProviderUsageResponse - 159, // 114: liveagent.gateway.v2.AgentEnvelope.chat_ingress_batch:type_name -> liveagent.gateway.v2.ChatIngressBatch - 165, // 115: liveagent.gateway.v2.AgentEnvelope.chat_ingress_resume:type_name -> liveagent.gateway.v2.ChatIngressResume - 167, // 116: liveagent.gateway.v2.AgentEnvelope.chat_ingress_fragment:type_name -> liveagent.gateway.v2.ChatIngressFragment + 160, // 113: liveagent.gateway.v2.AgentEnvelope.provider_usage_resp:type_name -> liveagent.gateway.v2.ProviderUsageResponse + 161, // 114: liveagent.gateway.v2.AgentEnvelope.chat_ingress_batch:type_name -> liveagent.gateway.v2.ChatIngressBatch + 167, // 115: liveagent.gateway.v2.AgentEnvelope.chat_ingress_resume:type_name -> liveagent.gateway.v2.ChatIngressResume + 169, // 116: liveagent.gateway.v2.AgentEnvelope.chat_ingress_fragment:type_name -> liveagent.gateway.v2.ChatIngressFragment 143, // 117: liveagent.gateway.v2.AgentEnvelope.chat_file_open_resp:type_name -> liveagent.gateway.v2.ChatFileOpenResponse 154, // 118: liveagent.gateway.v2.AgentEnvelope.error:type_name -> liveagent.gateway.v2.ErrorResponse - 170, // 119: liveagent.gateway.v2.AgentEnvelope.history_set_cwd_resp:type_name -> liveagent.gateway.v2.HistorySetCwdResponse - 174, // 120: liveagent.gateway.v2.AgentEnvelope.workspace_root_grants_resp:type_name -> liveagent.gateway.v2.WorkspaceRootGrantsResponse + 172, // 119: liveagent.gateway.v2.AgentEnvelope.history_set_cwd_resp:type_name -> liveagent.gateway.v2.HistorySetCwdResponse + 176, // 120: liveagent.gateway.v2.AgentEnvelope.workspace_root_grants_resp:type_name -> liveagent.gateway.v2.WorkspaceRootGrantsResponse 17, // 121: liveagent.gateway.v2.AgentEnvelope.import_directory_resp:type_name -> liveagent.gateway.v2.ImportDirectoryResponse - 177, // 122: liveagent.gateway.v2.AgentEnvelope.checkpoint_resp:type_name -> liveagent.gateway.v2.CheckpointResponse + 179, // 122: liveagent.gateway.v2.AgentEnvelope.checkpoint_resp:type_name -> liveagent.gateway.v2.CheckpointResponse 80, // 123: liveagent.gateway.v2.AgentEnvelope.trajectory_fetch_resp:type_name -> liveagent.gateway.v2.TrajectoryFetchResponse 124, // 124: liveagent.gateway.v2.AgentEnvelope.installed_apps_list_resp:type_name -> liveagent.gateway.v2.InstalledAppsListResponse 10, // 125: liveagent.gateway.v2.AgentEnvelope.clarify_turn_resp:type_name -> liveagent.gateway.v2.ClarifyTurnResponse @@ -16198,7 +16315,7 @@ var file_proto_v2_gateway_proto_depIdxs = []int32{ 7, // 162: liveagent.gateway.v2.ChatRequest.selected_model:type_name -> liveagent.gateway.v2.ChatSelectedModel 11, // 163: liveagent.gateway.v2.ChatRequest.uploaded_files:type_name -> liveagent.gateway.v2.ChatUploadedFile 8, // 164: liveagent.gateway.v2.ChatRequest.runtime_controls:type_name -> liveagent.gateway.v2.ChatRuntimeControls - 178, // 165: liveagent.gateway.v2.ChatRequest.referenced_conversations:type_name -> liveagent.gateway.v2.ChatConversationReference + 180, // 165: liveagent.gateway.v2.ChatRequest.referenced_conversations:type_name -> liveagent.gateway.v2.ChatConversationReference 59, // 166: liveagent.gateway.v2.ChatCommandRequest.request:type_name -> liveagent.gateway.v2.ChatRequest 60, // 167: liveagent.gateway.v2.ChatCommandRequest.base_message_ref:type_name -> liveagent.gateway.v2.ChatMessageRef 61, // 168: liveagent.gateway.v2.ChatCommandRequest.cancel:type_name -> liveagent.gateway.v2.CancelChatRequest @@ -16224,22 +16341,24 @@ var file_proto_v2_gateway_proto_depIdxs = []int32{ 127, // 188: liveagent.gateway.v2.FsRootsResponse.roots:type_name -> liveagent.gateway.v2.FsRoot 131, // 189: liveagent.gateway.v2.FsListDirsResponse.entries:type_name -> liveagent.gateway.v2.FsDirEntry 136, // 190: liveagent.gateway.v2.FsListResponse.entries:type_name -> liveagent.gateway.v2.FsListEntry - 160, // 191: liveagent.gateway.v2.ChatIngressBatch.records:type_name -> liveagent.gateway.v2.ChatIngressRecord - 161, // 192: liveagent.gateway.v2.ChatIngressRecord.delta:type_name -> liveagent.gateway.v2.ChatIngressDelta - 163, // 193: liveagent.gateway.v2.ChatIngressRecord.checkpoint:type_name -> liveagent.gateway.v2.ChatIngressCheckpoint - 164, // 194: liveagent.gateway.v2.ChatIngressRecord.terminal:type_name -> liveagent.gateway.v2.ChatIngressTerminal - 162, // 195: liveagent.gateway.v2.ChatIngressRecord.heartbeat:type_name -> liveagent.gateway.v2.ChatIngressHeartbeat - 166, // 196: liveagent.gateway.v2.ChatIngressResume.runs:type_name -> liveagent.gateway.v2.ChatIngressRunResume - 4, // 197: liveagent.gateway.v2.ChatIngressAck.action:type_name -> liveagent.gateway.v2.ChatIngressAck.Action - 75, // 198: liveagent.gateway.v2.HistorySetCwdResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary - 171, // 199: liveagent.gateway.v2.WorkspaceRootGrantsRequest.grants:type_name -> liveagent.gateway.v2.WorkspaceRootGrantDraft - 172, // 200: liveagent.gateway.v2.WorkspaceRootGrantsResponse.grants:type_name -> liveagent.gateway.v2.WorkspaceRootGrant - 175, // 201: liveagent.gateway.v2.CheckpointRequest.expected:type_name -> liveagent.gateway.v2.CheckpointExpectedEntry - 202, // [202:202] is the sub-list for method output_type - 202, // [202:202] is the sub-list for method input_type - 202, // [202:202] is the sub-list for extension type_name - 202, // [202:202] is the sub-list for extension extendee - 0, // [0:202] is the sub-list for field type_name + 155, // 191: liveagent.gateway.v2.ProviderCustomHeaders.headers:type_name -> liveagent.gateway.v2.ProviderCustomHeader + 156, // 192: liveagent.gateway.v2.ProviderModelsRequest.custom_headers:type_name -> liveagent.gateway.v2.ProviderCustomHeaders + 162, // 193: liveagent.gateway.v2.ChatIngressBatch.records:type_name -> liveagent.gateway.v2.ChatIngressRecord + 163, // 194: liveagent.gateway.v2.ChatIngressRecord.delta:type_name -> liveagent.gateway.v2.ChatIngressDelta + 165, // 195: liveagent.gateway.v2.ChatIngressRecord.checkpoint:type_name -> liveagent.gateway.v2.ChatIngressCheckpoint + 166, // 196: liveagent.gateway.v2.ChatIngressRecord.terminal:type_name -> liveagent.gateway.v2.ChatIngressTerminal + 164, // 197: liveagent.gateway.v2.ChatIngressRecord.heartbeat:type_name -> liveagent.gateway.v2.ChatIngressHeartbeat + 168, // 198: liveagent.gateway.v2.ChatIngressResume.runs:type_name -> liveagent.gateway.v2.ChatIngressRunResume + 4, // 199: liveagent.gateway.v2.ChatIngressAck.action:type_name -> liveagent.gateway.v2.ChatIngressAck.Action + 75, // 200: liveagent.gateway.v2.HistorySetCwdResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary + 173, // 201: liveagent.gateway.v2.WorkspaceRootGrantsRequest.grants:type_name -> liveagent.gateway.v2.WorkspaceRootGrantDraft + 174, // 202: liveagent.gateway.v2.WorkspaceRootGrantsResponse.grants:type_name -> liveagent.gateway.v2.WorkspaceRootGrant + 177, // 203: liveagent.gateway.v2.CheckpointRequest.expected:type_name -> liveagent.gateway.v2.CheckpointExpectedEntry + 204, // [204:204] is the sub-list for method output_type + 204, // [204:204] is the sub-list for method input_type + 204, // [204:204] is the sub-list for extension type_name + 204, // [204:204] is the sub-list for extension extendee + 0, // [0:204] is the sub-list for field type_name } func init() { file_proto_v2_gateway_proto_init() } @@ -16386,21 +16505,21 @@ func file_proto_v2_gateway_proto_init() { file_proto_v2_gateway_proto_msgTypes[130].OneofWrappers = []any{} file_proto_v2_gateway_proto_msgTypes[137].OneofWrappers = []any{} file_proto_v2_gateway_proto_msgTypes[138].OneofWrappers = []any{} - file_proto_v2_gateway_proto_msgTypes[150].OneofWrappers = []any{} - file_proto_v2_gateway_proto_msgTypes[155].OneofWrappers = []any{ + file_proto_v2_gateway_proto_msgTypes[152].OneofWrappers = []any{} + file_proto_v2_gateway_proto_msgTypes[157].OneofWrappers = []any{ (*ChatIngressRecord_Delta)(nil), (*ChatIngressRecord_Checkpoint)(nil), (*ChatIngressRecord_Terminal)(nil), (*ChatIngressRecord_Heartbeat)(nil), } - file_proto_v2_gateway_proto_msgTypes[166].OneofWrappers = []any{} + file_proto_v2_gateway_proto_msgTypes[168].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_v2_gateway_proto_rawDesc), len(file_proto_v2_gateway_proto_rawDesc)), NumEnums: 5, - NumMessages: 174, + NumMessages: 176, NumExtensions: 0, NumServices: 0, }, diff --git a/crates/agent-gateway/proto/v2/gateway.proto b/crates/agent-gateway/proto/v2/gateway.proto index 343c1ed79..848d4c21a 100644 --- a/crates/agent-gateway/proto/v2/gateway.proto +++ b/crates/agent-gateway/proto/v2/gateway.proto @@ -1336,6 +1336,18 @@ message ErrorResponse { string message = 2; } +message ProviderCustomHeader { + string name = 1; + string value = 2; +} + +// 包一层 message 只为拿到字段存在性:repeated 无法区分「草稿没带」与「草稿把头 +// 清空了」,而这两种情况在已保存供应商分支上的处理相反(前者沿用落库的头, +// 后者按空集发请求)。与 is_full_url 的 optional 同一套三态语义。 +message ProviderCustomHeaders { + repeated ProviderCustomHeader headers = 1; +} + message ProviderModelsRequest { string provider_type = 1; string base_url = 2; @@ -1347,6 +1359,9 @@ message ProviderModelsRequest { string provider_id = 6; // 当前草稿是否把 base_url 作为完整聊天端点解释;未提供时沿用已保存配置。 optional bool is_full_url = 7; + // 用户在供应商设置里显式配置的自定义请求头;未提供时沿用已保存配置。 + // 鉴权头与 host/content-length 等仍由落地侧的保留头名单兜底,不可被覆盖。 + ProviderCustomHeaders custom_headers = 8; } message ProviderModelsResponse { diff --git a/crates/agent-gateway/web/src/lib/gatewaySocket.ts b/crates/agent-gateway/web/src/lib/gatewaySocket.ts index 98a2033f9..e9161b89d 100644 --- a/crates/agent-gateway/web/src/lib/gatewaySocket.ts +++ b/crates/agent-gateway/web/src/lib/gatewaySocket.ts @@ -414,6 +414,7 @@ export type GatewayWebSocketClientLike = { modelsUrl?: string, providerId?: string, isFullUrl?: boolean, + customHeaders?: readonly { key: string; value: string }[], ): Promise; providerUsageQuery(providerId: string, refresh: boolean): Promise; providerUsageTest(providerId: string, configJson: string): Promise; diff --git a/crates/agent-gateway/web/src/lib/gatewaySocketRpc.ts b/crates/agent-gateway/web/src/lib/gatewaySocketRpc.ts index c2bf19427..503fb87e6 100644 --- a/crates/agent-gateway/web/src/lib/gatewaySocketRpc.ts +++ b/crates/agent-gateway/web/src/lib/gatewaySocketRpc.ts @@ -1379,6 +1379,7 @@ export class GatewayWebSocketRpcClient extends GatewayWebSocketTransport { modelsUrl = "", providerId = "", isFullUrl?: boolean, + customHeaders?: readonly { key: string; value: string }[], ): Promise { return this.requestWithRecovery("provider.models", { type, @@ -1388,6 +1389,7 @@ export class GatewayWebSocketRpcClient extends GatewayWebSocketTransport { models_url: modelsUrl, provider_id: providerId, is_full_url: isFullUrl, + custom_headers: customHeaders, }); } diff --git a/crates/agent-gateway/web/src/lib/gatewaySocketV2/adapters.ts b/crates/agent-gateway/web/src/lib/gatewaySocketV2/adapters.ts index 58add445b..4286af7ec 100644 --- a/crates/agent-gateway/web/src/lib/gatewaySocketV2/adapters.ts +++ b/crates/agent-gateway/web/src/lib/gatewaySocketV2/adapters.ts @@ -72,6 +72,8 @@ import { InstalledAppsListRequestSchema, ManagedProcessRequestSchema, MemoryManageRequestSchema, + ProviderCustomHeaderSchema, + ProviderCustomHeadersSchema, ProviderListRequestSchema, ProviderModelsRequestSchema, ProviderUsageRequestSchema, @@ -621,6 +623,18 @@ function agentRequestPayload(type: string, body: J): GatewayEnvelope["payload"] modelsUrl: trimStr(body.models_url), providerId: trimStr(body.provider_id), isFullUrl: typeof body.is_full_url === "boolean" ? body.is_full_url : undefined, + // 字段存在性即语义:调用方没带 custom_headers 才回落到落库配置,带了空 + // 数组表示草稿把头清空了,桌面端必须按空集发。 + customHeaders: Array.isArray(body.custom_headers) + ? create(ProviderCustomHeadersSchema, { + headers: body.custom_headers.map((header) => + create(ProviderCustomHeaderSchema, { + name: trimStr((header as { key?: unknown } | null)?.key), + value: str((header as { value?: unknown } | null)?.value), + }), + ), + }) + : undefined, }), }; case "provider.usage.query": diff --git a/crates/agent-gateway/web/src/lib/proto/gen/proto/v2/gateway_pb.ts b/crates/agent-gateway/web/src/lib/proto/gen/proto/v2/gateway_pb.ts index acfd423df..af1fb6f6c 100644 --- a/crates/agent-gateway/web/src/lib/proto/gen/proto/v2/gateway_pb.ts +++ b/crates/agent-gateway/web/src/lib/proto/gen/proto/v2/gateway_pb.ts @@ -10,7 +10,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file proto/v2/gateway.proto. */ export const file_proto_v2_gateway: GenFile = /*@__PURE__*/ - fileDesc("ChZwcm90by92Mi9nYXRld2F5LnByb3RvEhRsaXZlYWdlbnQuZ2F0ZXdheS52MiLyHwoPR2F0ZXdheUVudmVsb3BlEhIKCnJlcXVlc3RfaWQYASABKAkSEQoJdGltZXN0YW1wGAIgASgDEkAKDGNoYXRfY29tbWFuZBgKIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRDb21tYW5kUmVxdWVzdEgAEj4KC2Nyb25fbWFuYWdlGBQgASgLMicubGl2ZWFnZW50LmdhdGV3YXkudjIuQ3Jvbk1hbmFnZVJlcXVlc3RIABJACgxoaXN0b3J5X2xpc3QYHiABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5TGlzdFJlcXVlc3RIABI+CgtoaXN0b3J5X2dldBgfIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlHZXRSZXF1ZXN0SAASRAoOaGlzdG9yeV9yZW5hbWUYICABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UmVuYW1lUmVxdWVzdEgAEkQKDmhpc3RvcnlfZGVsZXRlGCEgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeURlbGV0ZVJlcXVlc3RIABJECg5oaXN0b3J5X3ByZWZpeBgiIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlQcmVmaXhSZXF1ZXN0SAASPgoLaGlzdG9yeV9waW4YIyABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UGluUmVxdWVzdEgAEkkKEWhpc3Rvcnlfc2hhcmVfZ2V0GCQgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlR2V0UmVxdWVzdEgAEkkKEWhpc3Rvcnlfc2hhcmVfc2V0GCUgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlU2V0UmVxdWVzdEgAElEKFWhpc3Rvcnlfc2hhcmVfcmVzb2x2ZRgmIAEoCzIwLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZVJlc29sdmVSZXF1ZXN0SAASSAoQaGlzdG9yeV93b3JrZGlycxgnIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlXb3JrZGlyc1JlcXVlc3RIABJCCg1wcm92aWRlcl9saXN0GCggASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuUHJvdmlkZXJMaXN0UmVxdWVzdEgAEkAKDHNldHRpbmdzX2dldBgpIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzR2V0UmVxdWVzdEgAEkYKD3NldHRpbmdzX3VwZGF0ZRgqIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzVXBkYXRlUmVxdWVzdEgAEkcKEHNraWxsX2ZpbGVzX2xpc3QYKyABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Ta2lsbEZpbGVzTGlzdFJlcXVlc3RIABJNChNza2lsbF9tZXRhZGF0YV9yZWFkGCwgASgLMi4ubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxNZXRhZGF0YVJlYWRSZXF1ZXN0SAASRQoPc2tpbGxfdGV4dF9yZWFkGC0gASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxUZXh0UmVhZFJlcXVlc3RIABJJChFmaWxlX21lbnRpb25fbGlzdBguIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZpbGVNZW50aW9uTGlzdFJlcXVlc3RIABJRChV1cGxvYWRfcmVhZGFibGVfZmlsZXMYLyABKAsyMC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5VcGxvYWRSZWFkYWJsZUZpbGVzUmVxdWVzdEgAEjgKCGZzX3Jvb3RzGDAgASgLMiQubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNSb290c1JlcXVlc3RIABI/Cgxmc19saXN0X2RpcnMYMSABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0xpc3REaXJzUmVxdWVzdEgAEjEKBHBpbmcYMiABKAsyIS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5QaW5nUmVxdWVzdEgAElMKFnVwbG9hZGVkX2ltYWdlX3ByZXZpZXcYMyABKAsyMS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5VcGxvYWRlZEltYWdlUHJldmlld1JlcXVlc3RIABJCCg1tZW1vcnlfbWFuYWdlGDQgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuTWVtb3J5TWFuYWdlUmVxdWVzdEgAEkAKDHNraWxsX21hbmFnZRg1IAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsTWFuYWdlUmVxdWVzdEgAElYKGGZzX2NyZWF0ZV9wcm9qZWN0X2ZvbGRlchg2IAEoCzIyLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzQ3JlYXRlUHJvamVjdEZvbGRlclJlcXVlc3RIABJBChB0ZXJtaW5hbF9yZXF1ZXN0GDcgASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxSZXF1ZXN0SAASNgoHZnNfbGlzdBg4IAEoCzIjLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzTGlzdFJlcXVlc3RIABJBCg1mc193cml0ZV90ZXh0GDkgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNXcml0ZVRleHRSZXF1ZXN0SAASQQoNZnNfY3JlYXRlX2Rpchg6IAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzQ3JlYXRlRGlyUmVxdWVzdEgAEjoKCWZzX3JlbmFtZRg7IAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVuYW1lUmVxdWVzdEgAEjoKCWZzX2RlbGV0ZRg8IAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzRGVsZXRlUmVxdWVzdEgAEjcKC2dpdF9yZXF1ZXN0GD0gASgLMiAubGl2ZWFnZW50LmdhdGV3YXkudjIuR2l0UmVxdWVzdEgAElAKFWZzX3JlYWRfZWRpdGFibGVfdGV4dBg+IAEoCzIvLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZEVkaXRhYmxlVGV4dFJlcXVlc3RIABJUChdmc19yZWFkX3dvcmtzcGFjZV9pbWFnZRg/IAEoCzIxLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZFdvcmtzcGFjZUltYWdlUmVxdWVzdEgAEjkKDHNmdHBfcmVxdWVzdBhAIAEoCzIhLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNmdHBSZXF1ZXN0SAASRgoPcHJvdmlkZXJfbW9kZWxzGEEgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuUHJvdmlkZXJNb2RlbHNSZXF1ZXN0SAASXwodc2V0dGluZ3NfcmVzZXRfc3NoX2tub3duX2hvc3QYSCABKAsyNi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc1Jlc2V0U3NoS25vd25Ib3N0UmVxdWVzdEgAEjwKCmNoYXRfcXVldWUYSSABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UXVldWVSZXF1ZXN0SAASQAoQY2hhdF9pbmdyZXNzX2FjaxhLIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzQWNrSAASQQoMdHVubmVsX3N0YXRlGFAgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsU3RhdGVTbmFwc2hvdEgAEj8KD3R1bm5lbF9tdXRhdGlvbhhRIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbE11dGF0aW9uSAASOQoMdHVubmVsX2ZyYW1lGFIgASgLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsRnJhbWVIABJGCg93b3Jrc3BhY2Vfd2F0Y2gYWiABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Xb3Jrc3BhY2VXYXRjaFJlcXVlc3RIABJOChdtYW5hZ2VkX3Byb2Nlc3NfcmVxdWVzdBhbIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLk1hbmFnZWRQcm9jZXNzUmVxdWVzdEgAEkQKDmhpc3RvcnlfYnJhbmNoGFwgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeUJyYW5jaFJlcXVlc3RIABJECg5wcm92aWRlcl91c2FnZRhdIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyVXNhZ2VSZXF1ZXN0SAASQwoOY2hhdF9maWxlX29wZW4YXiABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0RmlsZU9wZW5SZXF1ZXN0SAASRQoPaGlzdG9yeV9zZXRfY3dkGF8gASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNldEN3ZFJlcXVlc3RIABJRChV3b3Jrc3BhY2Vfcm9vdF9ncmFudHMYYCABKAsyMC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Xb3Jrc3BhY2VSb290R3JhbnRzUmVxdWVzdEgAEkgKEGltcG9ydF9kaXJlY3RvcnkYYSABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5JbXBvcnREaXJlY3RvcnlSZXF1ZXN0SAASPQoKY2hlY2twb2ludBhiIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoZWNrcG9pbnRSZXF1ZXN0SAASSAoQdHJhamVjdG9yeV9mZXRjaBhjIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRyYWplY3RvcnlGZXRjaFJlcXVlc3RIABJNChNpbnN0YWxsZWRfYXBwc19saXN0GGQgASgLMi4ubGl2ZWFnZW50LmdhdGV3YXkudjIuSW5zdGFsbGVkQXBwc0xpc3RSZXF1ZXN0SAASQAoMY2xhcmlmeV90dXJuGGUgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2xhcmlmeVR1cm5SZXF1ZXN0SAASPAoKY3VhX2RyaXZlchhmIAEoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLkN1YURyaXZlclJlcXVlc3RIAEIJCgdwYXlsb2FkSgQIQxBESgQIRBBFSgQIRRBGSgQIShBLIoYpCg1BZ2VudEVudmVsb3BlEhIKCnJlcXVlc3RfaWQYASABKAkSEQoJdGltZXN0YW1wGAIgASgDEjUKCmNoYXRfZXZlbnQYCiABKAsyHy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0RXZlbnRIABJEChBjcm9uX21hbmFnZV9yZXNwGBQgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuQ3Jvbk1hbmFnZVJlc3BvbnNlSAASRgoRaGlzdG9yeV9saXN0X3Jlc3AYHiABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5TGlzdFJlc3BvbnNlSAASRAoQaGlzdG9yeV9nZXRfcmVzcBgfIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlHZXRSZXNwb25zZUgAEkoKE2hpc3RvcnlfcmVuYW1lX3Jlc3AYICABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UmVuYW1lUmVzcG9uc2VIABJKChNoaXN0b3J5X2RlbGV0ZV9yZXNwGCEgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeURlbGV0ZVJlc3BvbnNlSAASPgoMaGlzdG9yeV9zeW5jGCIgASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVN5bmNFdmVudEgAEkoKE2hpc3RvcnlfcHJlZml4X3Jlc3AYIyABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UHJlZml4UmVzcG9uc2VIABJEChBoaXN0b3J5X3Bpbl9yZXNwGCQgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVBpblJlc3BvbnNlSAASTwoWaGlzdG9yeV9zaGFyZV9nZXRfcmVzcBglIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZUdldFJlc3BvbnNlSAASTwoWaGlzdG9yeV9zaGFyZV9zZXRfcmVzcBgmIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZVNldFJlc3BvbnNlSAASVwoaaGlzdG9yeV9zaGFyZV9yZXNvbHZlX3Jlc3AYJyABKAsyMS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5U2hhcmVSZXNvbHZlUmVzcG9uc2VIABJOChVoaXN0b3J5X3dvcmtkaXJzX3Jlc3AYOCABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5V29ya2RpcnNSZXNwb25zZUgAEkgKEnByb3ZpZGVyX2xpc3RfcmVzcBgoIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyTGlzdFJlc3BvbnNlSAASRgoRc2V0dGluZ3NfZ2V0X3Jlc3AYKSABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc0dldFJlc3BvbnNlSAASTAoUc2V0dGluZ3NfdXBkYXRlX3Jlc3AYKiABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc1VwZGF0ZVJlc3BvbnNlSAASQAoNc2V0dGluZ3Nfc3luYxgrIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzU3luY0V2ZW50SAASTQoVc2tpbGxfZmlsZXNfbGlzdF9yZXNwGCwgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxGaWxlc0xpc3RSZXNwb25zZUgAElMKGHNraWxsX21ldGFkYXRhX3JlYWRfcmVzcBgtIAEoCzIvLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsTWV0YWRhdGFSZWFkUmVzcG9uc2VIABJLChRza2lsbF90ZXh0X3JlYWRfcmVzcBguIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsVGV4dFJlYWRSZXNwb25zZUgAEk8KFmZpbGVfbWVudGlvbl9saXN0X3Jlc3AYLyABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5GaWxlTWVudGlvbkxpc3RSZXNwb25zZUgAElcKGnVwbG9hZF9yZWFkYWJsZV9maWxlc19yZXNwGDAgASgLMjEubGl2ZWFnZW50LmdhdGV3YXkudjIuVXBsb2FkUmVhZGFibGVGaWxlc1Jlc3BvbnNlSAASPgoNZnNfcm9vdHNfcmVzcBgxIAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUm9vdHNSZXNwb25zZUgAEjIKBHBvbmcYMiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Qb25nUmVzcG9uc2VIABJFChFmc19saXN0X2RpcnNfcmVzcBgzIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzTGlzdERpcnNSZXNwb25zZUgAElkKG3VwbG9hZGVkX2ltYWdlX3ByZXZpZXdfcmVzcBg0IAEoCzIyLmxpdmVhZ2VudC5nYXRld2F5LnYyLlVwbG9hZGVkSW1hZ2VQcmV2aWV3UmVzcG9uc2VIABJIChJtZW1vcnlfbWFuYWdlX3Jlc3AYNSABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5NZW1vcnlNYW5hZ2VSZXNwb25zZUgAEkYKEXNraWxsX21hbmFnZV9yZXNwGDYgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxNYW5hZ2VSZXNwb25zZUgAElwKHWZzX2NyZWF0ZV9wcm9qZWN0X2ZvbGRlcl9yZXNwGDcgASgLMjMubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNDcmVhdGVQcm9qZWN0Rm9sZGVyUmVzcG9uc2VIABJDChF0ZXJtaW5hbF9yZXNwb25zZRg5IAEoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsUmVzcG9uc2VIABI9Cg50ZXJtaW5hbF9ldmVudBg6IAEoCzIjLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsRXZlbnRIABI8Cgxmc19saXN0X3Jlc3AYOyABKAsyJC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0xpc3RSZXNwb25zZUgAEkcKEmZzX3dyaXRlX3RleHRfcmVzcBg8IAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzV3JpdGVUZXh0UmVzcG9uc2VIABJHChJmc19jcmVhdGVfZGlyX3Jlc3AYPSABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0NyZWF0ZURpclJlc3BvbnNlSAASQAoOZnNfcmVuYW1lX3Jlc3AYPiABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc1JlbmFtZVJlc3BvbnNlSAASQAoOZnNfZGVsZXRlX3Jlc3AYPyABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0RlbGV0ZVJlc3BvbnNlSAASOQoMZ2l0X3Jlc3BvbnNlGEAgASgLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuR2l0UmVzcG9uc2VIABJWChpmc19yZWFkX2VkaXRhYmxlX3RleHRfcmVzcBhBIAEoCzIwLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZEVkaXRhYmxlVGV4dFJlc3BvbnNlSAASWgocZnNfcmVhZF93b3Jrc3BhY2VfaW1hZ2VfcmVzcBhCIAEoCzIyLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZFdvcmtzcGFjZUltYWdlUmVzcG9uc2VIABI7Cg1zZnRwX3Jlc3BvbnNlGEkgASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuU2Z0cFJlc3BvbnNlSAASNQoKc2Z0cF9ldmVudBhKIAEoCzIfLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNmdHBFdmVudEgAEkIKD2NoYXRfcXVldWVfcmVzcBhLIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRRdWV1ZVJlc3BvbnNlSAASQAoQY2hhdF9xdWV1ZV9ldmVudBhMIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRRdWV1ZUV2ZW50SAASPgoMY2hhdF9jb250cm9sGEYgASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdENvbnRyb2xFdmVudEgAEkIKDnJ1bnRpbWVfc3RhdHVzGEcgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuUnVudGltZVN0YXR1c0V2ZW50SAASZQoic2V0dGluZ3NfcmVzZXRfc3NoX2tub3duX2hvc3RfcmVzcBhIIAEoCzI3LmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzUmVzZXRTc2hLbm93bkhvc3RSZXNwb25zZUgAEkoKFWNoYXRfcnVudGltZV9zbmFwc2hvdBhNIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRSdW50aW1lU25hcHNob3RIABJMChRwcm92aWRlcl9tb2RlbHNfcmVzcBhPIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyTW9kZWxzUmVzcG9uc2VIABJCCg50dW5uZWxfZGVzaXJlZBhQIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbERlc2lyZWRTdGF0ZUgAEkwKFnR1bm5lbF9tdXRhdGlvbl9yZXN1bHQYUSABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxNdXRhdGlvblJlc3VsdEgAEjkKDHR1bm5lbF9mcmFtZRhSIAEoCzIhLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbEZyYW1lSAASRgoTdHVubmVsX3Byb2JlX3JlcG9ydBhTIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbFByb2JlUmVwb3J0SAASSgoSd29ya3NwYWNlX2FjdGl2aXR5GFogASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuV29ya3NwYWNlQWN0aXZpdHlFdmVudEgAElAKGG1hbmFnZWRfcHJvY2Vzc19yZXNwb25zZRhbIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLk1hbmFnZWRQcm9jZXNzUmVzcG9uc2VIABJQChhtYW5hZ2VkX3Byb2Nlc3Nfc25hcHNob3QYXCABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5NYW5hZ2VkUHJvY2Vzc1NuYXBzaG90SAASSgoTaGlzdG9yeV9icmFuY2hfcmVzcBhdIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlCcmFuY2hSZXNwb25zZUgAEkoKE3Byb3ZpZGVyX3VzYWdlX3Jlc3AYXiABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Qcm92aWRlclVzYWdlUmVzcG9uc2VIABJEChJjaGF0X2luZ3Jlc3NfYmF0Y2gYXyABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc0JhdGNoSAASRgoTY2hhdF9pbmdyZXNzX3Jlc3VtZRhgIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzUmVzdW1lSAASSgoVY2hhdF9pbmdyZXNzX2ZyYWdtZW50GGEgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NGcmFnbWVudEgAEkkKE2NoYXRfZmlsZV9vcGVuX3Jlc3AYYiABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0RmlsZU9wZW5SZXNwb25zZUgAEjQKBWVycm9yGGMgASgLMiMubGl2ZWFnZW50LmdhdGV3YXkudjIuRXJyb3JSZXNwb25zZUgAEksKFGhpc3Rvcnlfc2V0X2N3ZF9yZXNwGGQgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNldEN3ZFJlc3BvbnNlSAASVwoad29ya3NwYWNlX3Jvb3RfZ3JhbnRzX3Jlc3AYZSABKAsyMS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Xb3Jrc3BhY2VSb290R3JhbnRzUmVzcG9uc2VIABJOChVpbXBvcnRfZGlyZWN0b3J5X3Jlc3AYZiABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5JbXBvcnREaXJlY3RvcnlSZXNwb25zZUgAEkMKD2NoZWNrcG9pbnRfcmVzcBhnIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoZWNrcG9pbnRSZXNwb25zZUgAEk4KFXRyYWplY3RvcnlfZmV0Y2hfcmVzcBhoIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRyYWplY3RvcnlGZXRjaFJlc3BvbnNlSAASUwoYaW5zdGFsbGVkX2FwcHNfbGlzdF9yZXNwGGkgASgLMi8ubGl2ZWFnZW50LmdhdGV3YXkudjIuSW5zdGFsbGVkQXBwc0xpc3RSZXNwb25zZUgAEkYKEWNsYXJpZnlfdHVybl9yZXNwGGogASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2xhcmlmeVR1cm5SZXNwb25zZUgAEkIKD2N1YV9kcml2ZXJfcmVzcBhrIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkN1YURyaXZlclJlc3BvbnNlSABCCQoHcGF5bG9hZEoECEMQREoECEQQRUoECEUQRkoECE4QTyJVChFDaGF0U2VsZWN0ZWRNb2RlbBIaChJjdXN0b21fcHJvdmlkZXJfaWQYASABKAkSDQoFbW9kZWwYAiABKAkSFQoNcHJvdmlkZXJfdHlwZRgDIAEoCSKAAQoTQ2hhdFJ1bnRpbWVDb250cm9scxIYChB0aGlua2luZ19lbmFibGVkGAEgASgIEiEKGW5hdGl2ZV93ZWJfc2VhcmNoX2VuYWJsZWQYAiABKAgSEQoJcmVhc29uaW5nGAMgASgJEhkKEXBsYW5fbW9kZV9lbmFibGVkGAQgASgIIpQBChJDbGFyaWZ5VHVyblJlcXVlc3QSFQoNbWVzc2FnZXNfanNvbhgBIAEoCRITCgtwcm92aWRlcl9pZBgCIAEoCRINCgVtb2RlbBgDIAEoCRJDChBydW50aW1lX2NvbnRyb2xzGAQgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdFJ1bnRpbWVDb250cm9scyJUChNDbGFyaWZ5VHVyblJlc3BvbnNlEhIKCmZpbmFsX3RleHQYASABKAkSEgoKZXJyb3JfY29kZRgCIAEoCRIVCg1lcnJvcl9tZXNzYWdlGAMgASgJIokBChBDaGF0VXBsb2FkZWRGaWxlEhUKDXJlbGF0aXZlX3BhdGgYASABKAkSFQoNYWJzb2x1dGVfcGF0aBgCIAEoCRIRCglmaWxlX25hbWUYAyABKAkSDAoEa2luZBgEIAEoCRISCgpzaXplX2J5dGVzGAUgASgDEhIKCmRlZHVwZV9rZXkYBiABKAkiSwoSVXBsb2FkUmVhZGFibGVGaWxlEhEKCWZpbGVfbmFtZRgBIAEoCRIRCgltaW1lX3R5cGUYAiABKAkSDwoHY29udGVudBgDIAEoDCJmChpVcGxvYWRSZWFkYWJsZUZpbGVzUmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEjcKBWZpbGVzGAIgAygLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuVXBsb2FkUmVhZGFibGVGaWxlImUKG1VwbG9hZFJlYWRhYmxlRmlsZXNSZXNwb25zZRI1CgVmaWxlcxgBIAMoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRVcGxvYWRlZEZpbGUSDwoHc2tpcHBlZBgCIAMoCSI9ChNJbXBvcnREaXJlY3RvcnlGaWxlEhUKDXJlbGF0aXZlX3BhdGgYASABKAkSDwoHY29udGVudBgCIAEoDCLDAgoWSW1wb3J0RGlyZWN0b3J5UmVxdWVzdBIMCgRuYW1lGAEgASgJEg4KBnRhcmdldBgCIAEoCRI8CgVmaWxlcxgDIAMoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkltcG9ydERpcmVjdG9yeUZpbGVCAhgBEhMKC3RyYW5zZmVyX2lkGAQgASgJEkEKCW9wZXJhdGlvbhgFIAEoDjIuLmxpdmVhZ2VudC5nYXRld2F5LnYyLkltcG9ydERpcmVjdG9yeU9wZXJhdGlvbhITCgt0b3RhbF9maWxlcxgGIAEoDRITCgt0b3RhbF9ieXRlcxgHIAEoBBIVCg1yZWxhdGl2ZV9wYXRoGAggASgJEg4KBm9mZnNldBgJIAEoBBINCgVjaHVuaxgKIAEoDBIVCg1maWxlX2NvbXBsZXRlGAsgASgIIn4KF0ltcG9ydERpcmVjdG9yeVJlc3BvbnNlEhEKCXJvb3RfcGF0aBgBIAEoCRISCgpmaWxlX2NvdW50GAIgASgFEg8KB3NraXBwZWQYAyADKAkSEwoLdHJhbnNmZXJfaWQYBCABKAkSFgoOcmVjZWl2ZWRfYnl0ZXMYBSABKAQiRQobVXBsb2FkZWRJbWFnZVByZXZpZXdSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSFQoNYWJzb2x1dGVfcGF0aBgCIAEoCSI/ChxVcGxvYWRlZEltYWdlUHJldmlld1Jlc3BvbnNlEhEKCW1pbWVfdHlwZRgBIAEoCRIMCgRkYXRhGAIgASgJInsKClR1bm5lbFNwZWMSCgoCaWQYASABKAkSEQoJc2x1Z19oaW50GAIgASgJEgwKBG5hbWUYAyABKAkSEgoKdGFyZ2V0X3VybBgEIAEoCRISCgpleHBpcmVzX2F0GAUgASgDEhgKEHByb2plY3RfcGF0aF9rZXkYBiABKAkiWQoSVHVubmVsRGVzaXJlZFN0YXRlEjEKB3R1bm5lbHMYASADKAsyIC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxTcGVjEhAKCHJldmlzaW9uGAIgASgEImYKDFR1bm5lbEhlYWx0aBIOCgZzdGF0dXMYASABKAkSEwoLaHR0cF9zdGF0dXMYAiABKA0SDQoFZXJyb3IYAyABKAkSEgoKY2hlY2tlZF9hdBgEIAEoAxIOCgZydHRfbXMYBSABKA0i8AEKDFR1bm5lbFN0YXR1cxIKCgJpZBgBIAEoCRIMCgRzbHVnGAIgASgJEgwKBG5hbWUYAyABKAkSEgoKdGFyZ2V0X3VybBgEIAEoCRITCgtwdWJsaWNfcGF0aBgFIAEoCRISCgpjcmVhdGVkX2F0GAYgASgDEhIKCmV4cGlyZXNfYXQYByABKAMSGgoSYWN0aXZlX2Nvbm5lY3Rpb25zGAggASgNEhgKEHByb2plY3RfcGF0aF9rZXkYCSABKAkSMQoFbG9jYWwYCiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFsdGgipQEKE1R1bm5lbFN0YXRlU25hcHNob3QSMwoHdHVubmVscxgBIAMoCzIiLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbFN0YXR1cxIQCghyZXZpc2lvbhgCIAEoBBIUCgxhZ2VudF9vbmxpbmUYAyABKAgSMQoFcmVsYXkYBCABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFsdGgimQEKDlR1bm5lbE11dGF0aW9uEg4KBmFjdGlvbhgBIAEoCRIRCgl0dW5uZWxfaWQYAiABKAkSEgoKdGFyZ2V0X3VybBgDIAEoCRIMCgRuYW1lGAQgASgJEhgKC3R0bF9zZWNvbmRzGAUgASgNSACIAQESGAoQcHJvamVjdF9wYXRoX2tleRgGIAEoCUIOCgxfdHRsX3NlY29uZHMiVAoUVHVubmVsTXV0YXRpb25SZXN1bHQSEQoJdHVubmVsX2lkGAEgASgJEhIKCmVycm9yX2NvZGUYAiABKAkSFQoNZXJyb3JfbWVzc2FnZRgDIAEoCSJZChFUdW5uZWxQcm9iZVJlc3VsdBIRCgl0dW5uZWxfaWQYASABKAkSMQoFbG9jYWwYAiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFsdGgiTQoRVHVubmVsUHJvYmVSZXBvcnQSOAoHcmVzdWx0cxgBIAMoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbFByb2JlUmVzdWx0IisKDFR1bm5lbEhlYWRlchIMCgRuYW1lGAEgASgJEg0KBXZhbHVlGAIgASgJIvUCCgtUdW5uZWxGcmFtZRIRCglzdHJlYW1faWQYASABKAkSMwoEa2luZBgCIAEoDjIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbEZyYW1lS2luZBISCgp0YXJnZXRfdXJsGAMgASgJEg4KBm1ldGhvZBgEIAEoCRIMCgRwYXRoGAUgASgJEjMKB2hlYWRlcnMYBiADKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFkZXISDgoGc3RhdHVzGAcgASgNEgwKBGJvZHkYCCABKAwSDQoFZXJyb3IYCSABKAkSQgoPd3NfbWVzc2FnZV90eXBlGAogASgOMikubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsV3NNZXNzYWdlVHlwZRIWCg53c19zdWJwcm90b2NvbBgLIAEoCRIVCg13c19jbG9zZV9jb2RlGAwgASgNEhcKD3dzX2Nsb3NlX3JlYXNvbhgNIAEoCSIpChVXb3Jrc3BhY2VXYXRjaFJlcXVlc3QSEAoId29ya2RpcnMYASADKAkifgoWV29ya3NwYWNlQWN0aXZpdHlFdmVudBIPCgd3b3JrZGlyGAEgASgJEhAKCHJldmlzaW9uGAIgASgEEgoKAmZzGAMgASgIEgsKA2dpdBgEIAEoCBIVCg1jaGFuZ2VkX3BhdGhzGAUgAygJEhEKCXRydW5jYXRlZBgGIAEoCCKWAgoUTWFuYWdlZFByb2Nlc3NSZWNvcmQSCgoCaWQYASABKAkSDQoFbGFiZWwYAiABKAkSDwoHY29tbWFuZBgDIAEoCRILCgNjd2QYBCABKAkSDQoFc2hlbGwYBSABKAkSCwoDcGlkGAYgASgNEhAKCGxvZ19wYXRoGAcgASgJEhIKCnN0YXJ0ZWRfYXQYCCABKAMSGAoLZmluaXNoZWRfYXQYCSABKANIAIgBARIWCglleGl0X2NvZGUYCiABKAVIAYgBARIPCgdydW5uaW5nGAsgASgIEhAKCGlzb2xhdGVkGAwgASgIEhAKCHJlc3RvcmVkGA0gASgIQg4KDF9maW5pc2hlZF9hdEIMCgpfZXhpdF9jb2RlImkKFk1hbmFnZWRQcm9jZXNzU25hcHNob3QSPQoJcHJvY2Vzc2VzGAEgAygLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuTWFuYWdlZFByb2Nlc3NSZWNvcmQSEAoIcmV2aXNpb24YAiABKAQiTgoVTWFuYWdlZFByb2Nlc3NSZXF1ZXN0Eg4KBmFjdGlvbhgBIAEoCRISCgpwcm9jZXNzX2lkGAIgASgJEhEKCW1heF9ieXRlcxgDIAEoDSK3AQoWTWFuYWdlZFByb2Nlc3NSZXNwb25zZRIOCgZhY3Rpb24YASABKAkSPgoIc25hcHNob3QYAiABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5NYW5hZ2VkUHJvY2Vzc1NuYXBzaG90EhMKC2xvZ19jb250ZW50GAMgASgJEhAKCGxvZ19wYXRoGAQgASgJEhUKDWxvZ190cnVuY2F0ZWQYBSABKAgSDwoHc3RvcHBlZBgGIAEoCCI5ChNNZW1vcnlNYW5hZ2VSZXF1ZXN0Eg8KB2NvbW1hbmQYASABKAkSEQoJYXJnc19qc29uGAIgASgJIisKFE1lbW9yeU1hbmFnZVJlc3BvbnNlEhMKC3Jlc3VsdF9qc29uGAEgASgJIpgDCg9UZXJtaW5hbFJlcXVlc3QSDgoGYWN0aW9uGAEgASgJEhIKCnNlc3Npb25faWQYAiABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgDIAEoCRILCgNjd2QYBCABKAkSDQoFc2hlbGwYBSABKAkSDQoFdGl0bGUYBiABKAkSDAoEZGF0YRgHIAEoCRIMCgRjb2xzGAggASgNEgwKBHJvd3MYCSABKA0SEQoJbWF4X2J5dGVzGAogASgNEhMKC3NzaF9ob3N0X2lkGAsgASgJEhEKCXByb21wdF9pZBgMIAEoCRIVCg1wcm9tcHRfYW5zd2VyGA0gASgJEhYKDnRydXN0X2hvc3Rfa2V5GA4gASgIEhQKDHNmdHBfZW5hYmxlZBgPIAEoCBIOCgZ0YWJfaWQYECABKAkSEAoIdGFiX2tpbmQYESABKAkSEwoLcmVtb3RlX2hvc3QYEiABKAkSEwoLcmVtb3RlX3BvcnQYEyABKA0SEgoKbG9jYWxfcG9ydBgUIAEoDRISCgpmb3J3YXJkX2lkGBUgASgJIrICCg9UZXJtaW5hbFNlc3Npb24SCgoCaWQYASABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgCIAEoCRILCgNjd2QYAyABKAkSDQoFc2hlbGwYBCABKAkSDQoFdGl0bGUYBSABKAkSCwoDcGlkGAYgASgNEgwKBGNvbHMYByABKA0SDAoEcm93cxgIIAEoDRISCgpjcmVhdGVkX2F0GAkgASgEEhIKCnVwZGF0ZWRfYXQYCiABKAQSEwoLZmluaXNoZWRfYXQYCyABKAQSEQoJZXhpdF9jb2RlGAwgASgFEg8KB3J1bm5pbmcYDSABKAgSDAoEa2luZBgOIAEoCRI2CgNzc2gYDyABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaE1ldGFkYXRhItsBChNUZXJtaW5hbFNzaE1ldGFkYXRhEg8KB2hvc3RfaWQYASABKAkSEQoJaG9zdF9uYW1lGAIgASgJEhAKCHVzZXJuYW1lGAMgASgJEgwKBGhvc3QYBCABKAkSDAoEcG9ydBgFIAEoDRIRCglhdXRoX3R5cGUYBiABKAkSDgoGc3RhdHVzGAcgASgJEhkKEXJlY29ubmVjdF9hdHRlbXB0GAggASgNEh4KFnJlY29ubmVjdF9tYXhfYXR0ZW1wdHMYCSABKA0SFAoMc2Z0cF9lbmFibGVkGAogASgIIpEDCgtTZnRwUmVxdWVzdBIOCgZhY3Rpb24YASABKAkSEgoKc2Vzc2lvbl9pZBgCIAEoCRIYChBwcm9qZWN0X3BhdGhfa2V5GAMgASgJEg8KB3dvcmtkaXIYBCABKAkSEgoKbG9jYWxfcGF0aBgFIAEoCRITCgtyZW1vdGVfcGF0aBgGIAEoCRIRCglmcm9tX3BhdGgYByABKAkSDwoHdG9fcGF0aBgIIAEoCRIRCglkaXJlY3Rpb24YCSABKAkSEwoLdGFyZ2V0X3BhdGgYCiABKAkSEQoJcmVjdXJzaXZlGAsgASgIEhEKCW92ZXJ3cml0ZRgMIAEoCBIPCgdjb250ZW50GA0gASgJEg4KBm9mZnNldBgOIAEoBBIRCgltYXhfYnl0ZXMYDyABKAQSEwoLc3RyaWN0X3V0ZjgYECABKAgSFgoOZXhwZWN0ZWRfbXRpbWUYESABKAQSGwoTZXhwZWN0ZWRfc2l6ZV9ieXRlcxgSIAEoBBIaChJjcmVhdGVfcGFyZW50X2RpcnMYEyABKAgiWAoJU2Z0cEVudHJ5EgwKBHBhdGgYASABKAkSDAoEbmFtZRgCIAEoCRIMCgRraW5kGAMgASgJEhIKCnNpemVfYnl0ZXMYBCABKAQSDQoFbXRpbWUYBSABKAQi8gEKDFNmdHBUcmFuc2ZlchIKCgJpZBgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJEhEKCWRpcmVjdGlvbhgDIAEoCRIOCgZzdGF0dXMYBCABKAkSEwoLc291cmNlX3BhdGgYBSABKAkSEwoLdGFyZ2V0X3BhdGgYBiABKAkSFAoMY3VycmVudF9wYXRoGAcgASgJEhIKCmJ5dGVzX2RvbmUYCCABKAQSEwoLYnl0ZXNfdG90YWwYCSABKAQSEgoKZmlsZXNfZG9uZRgKIAEoDRITCgtmaWxlc190b3RhbBgLIAEoDRINCgVlcnJvchgMIAEoCSKwAgoMU2Z0cFJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRIMCgRwYXRoGAIgASgJEjAKB2VudHJpZXMYAyADKAsyHy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwRW50cnkSLgoFZW50cnkYBCABKAsyHy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwRW50cnkSDgoGZXhpc3RzGAUgASgIEjQKCHRyYW5zZmVyGAYgASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuU2Z0cFRyYW5zZmVyEg8KB2NvbnRlbnQYByABKAkSDgoGb2Zmc2V0GAggASgEEhIKCmJ5dGVzX3JlYWQYCSABKAQSEgoKc2l6ZV9ieXRlcxgKIAEoBBIRCgl0cnVuY2F0ZWQYCyABKAgiTwoJU2Z0cEV2ZW50EgwKBGtpbmQYASABKAkSNAoIdHJhbnNmZXIYAiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwVHJhbnNmZXIiwQEKEVRlcm1pbmFsU3NoUHJvbXB0EgoKAmlkGAEgASgJEgwKBGtpbmQYAiABKAkSDwoHaG9zdF9pZBgDIAEoCRIRCglob3N0X25hbWUYBCABKAkSDAoEaG9zdBgFIAEoCRIMCgRwb3J0GAYgASgNEg8KB21lc3NhZ2UYByABKAkSGgoSZmluZ2VycHJpbnRfc2hhMjU2GAggASgJEhAKCGtleV90eXBlGAkgASgJEhMKC2Fuc3dlcl9lY2hvGAogASgIIkEKE1Rlcm1pbmFsU2hlbGxPcHRpb24SCgoCaWQYASABKAkSDQoFbGFiZWwYAiABKAkSDwoHY29tbWFuZBgDIAEoCSKAAQoOVGVybWluYWxTc2hUYWISCgoCaWQYASABKAkSEgoKc2Vzc2lvbl9pZBgCIAEoCRIYChBwcm9qZWN0X3BhdGhfa2V5GAMgASgJEgwKBGtpbmQYBCABKAkSEgoKY3JlYXRlZF9hdBgFIAEoBBISCgp1cGRhdGVkX2F0GAYgASgEIn8KF1Rlcm1pbmFsU3NoVGFic1NuYXBzaG90EhgKEHByb2plY3RfcGF0aF9rZXkYASABKAkSMgoEdGFicxgCIAMoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoVGFiEhAKCHJldmlzaW9uGAQgASgESgQIAxAEIv0BChdUZXJtaW5hbFNzaExvY2FsRm9yd2FyZBIKCgJpZBgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJEhgKEHByb2plY3RfcGF0aF9rZXkYAyABKAkSEgoKbG9jYWxfaG9zdBgEIAEoCRISCgpsb2NhbF9wb3J0GAUgASgNEg8KB2FkZHJlc3MYBiABKAkSEwoLcmVtb3RlX2hvc3QYByABKAkSEwoLcmVtb3RlX3BvcnQYCCABKA0SDgoGc3RhdHVzGAkgASgJEhIKCmNyZWF0ZWRfYXQYCiABKAQSEgoKdXBkYXRlZF9hdBgLIAEoBBINCgVlcnJvchgMIAEoCSJ1CiBUZXJtaW5hbFNzaExvY2FsRm9yd2FyZHNTbmFwc2hvdBI/Cghmb3J3YXJkcxgBIAMoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoTG9jYWxGb3J3YXJkEhAKCHJldmlzaW9uGAIgASgEIn8KHVRlcm1pbmFsU3NoTG9jYWxGb3J3YXJkQWN0aW9uEj4KB2ZvcndhcmQYASABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaExvY2FsRm9yd2FyZBIQCghyZXZpc2lvbhgCIAEoBBIMCgRraW5kGAMgASgJIqcFChBUZXJtaW5hbFJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRI3CghzZXNzaW9ucxgCIAMoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU2Vzc2lvbhI2CgdzZXNzaW9uGAMgASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTZXNzaW9uEg4KBm91dHB1dBgEIAEoDBIRCgl0cnVuY2F0ZWQYBSABKAgSQAoNc2hlbGxfb3B0aW9ucxgGIAMoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU2hlbGxPcHRpb24SFQoNZGVmYXVsdF9zaGVsbBgHIAEoCRIbChNvdXRwdXRfc3RhcnRfb2Zmc2V0GAggASgEEhkKEW91dHB1dF9lbmRfb2Zmc2V0GAkgASgEEjsKCnNzaF9wcm9tcHQYCiABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaFByb21wdBISCgpsYXRlbmN5X21zGAsgASgNEj8KCHNzaF90YWJzGAwgASgLMi0ubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hUYWJzU25hcHNob3QSUgoSc3NoX2xvY2FsX2ZvcndhcmRzGA0gASgLMjYubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hMb2NhbEZvcndhcmRzU25hcHNob3QSTgoRc3NoX2xvY2FsX2ZvcndhcmQYDiABKAsyMy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaExvY2FsRm9yd2FyZEFjdGlvbhIoCiBzc2hfbG9jYWxfZm9yd2FyZF9wb3J0X2F2YWlsYWJsZRgPIAEoCCLaAgoNVGVybWluYWxFdmVudBIMCgRraW5kGAEgASgJEhIKCnNlc3Npb25faWQYAiABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgDIAEoCRI2CgdzZXNzaW9uGAQgASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTZXNzaW9uEgwKBGRhdGEYBSABKAwSGwoTb3V0cHV0X3N0YXJ0X29mZnNldBgGIAEoBBIZChFvdXRwdXRfZW5kX29mZnNldBgHIAEoBBI/Cghzc2hfdGFicxgIIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoVGFic1NuYXBzaG90Ek4KEXNzaF9sb2NhbF9mb3J3YXJkGAkgASgLMjMubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hMb2NhbEZvcndhcmRBY3Rpb24isgIKE1Rlcm1pbmFsU3RyZWFtRnJhbWUSDAoEa2luZBgBIAEoCRIRCglzdHJlYW1faWQYAiABKAkSEgoKc2Vzc2lvbl9pZBgDIAEoCRIYChBwcm9qZWN0X3BhdGhfa2V5GAQgASgJEgsKA3NlcRgFIAEoBBIUCgxzdGFydF9vZmZzZXQYBiABKAQSEgoKZW5kX29mZnNldBgHIAEoBBIMCgRjb2xzGAggASgNEgwKBHJvd3MYCSABKA0SEQoJbWF4X2J5dGVzGAogASgNEhEKCXRydW5jYXRlZBgLIAEoCBINCgVlcnJvchgMIAEoCRI2CgdzZXNzaW9uGA0gASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTZXNzaW9uEgwKBGRhdGEYDiABKAwiQAoKR2l0UmVxdWVzdBIOCgZhY3Rpb24YASABKAkSDwoHd29ya2RpchgCIAEoCRIRCglhcmdzX2pzb24YAyABKAkiMgoLR2l0UmVzcG9uc2USDgoGYWN0aW9uGAEgASgJEhMKC3Jlc3VsdF9qc29uGAIgASgJIuQDCgtDaGF0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDwoHbWVzc2FnZRgCIAEoCRI/Cg5zZWxlY3RlZF9tb2RlbBgDIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRTZWxlY3RlZE1vZGVsEhYKDmV4ZWN1dGlvbl9tb2RlGAQgASgJEg8KB3dvcmtkaXIYBSABKAkSPgoOdXBsb2FkZWRfZmlsZXMYByADKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0VXBsb2FkZWRGaWxlEhkKEWNsaWVudF9yZXF1ZXN0X2lkGAggASgJEkMKEHJ1bnRpbWVfY29udHJvbHMYCSABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UnVudGltZUNvbnRyb2xzEhQKDHF1ZXVlX3BvbGljeRgKIAEoCRIbChNjb21tYW5kX3NhZmV0eV9tb2RlGAsgASgJElEKGHJlZmVyZW5jZWRfY29udmVyc2F0aW9ucxgMIAMoCzIvLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRDb252ZXJzYXRpb25SZWZlcmVuY2VKBAgGEAdSFXNlbGVjdGVkX3N5c3RlbV90b29scyKKAQoOQ2hhdE1lc3NhZ2VSZWYSFQoNc2VnbWVudF9pbmRleBgBIAEoBRIVCg1tZXNzYWdlX2luZGV4GAIgASgFEhIKCnNlZ21lbnRfaWQYAyABKAkSEgoKbWVzc2FnZV9pZBgEIAEoCRIMCgRyb2xlGAUgASgJEhQKDGNvbnRlbnRfaGFzaBgGIAEoCSI8ChFDYW5jZWxDaGF0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDgoGcnVuX2lkGAIgASgJIs8BChJDaGF0Q29tbWFuZFJlcXVlc3QSDAoEdHlwZRgBIAEoCRIyCgdyZXF1ZXN0GAIgASgLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdFJlcXVlc3QSPgoQYmFzZV9tZXNzYWdlX3JlZhgDIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRNZXNzYWdlUmVmEjcKBmNhbmNlbBgEIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNhbmNlbENoYXRSZXF1ZXN0IrgBChBDaGF0UXVldWVSZXF1ZXN0Eg4KBmFjdGlvbhgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSDwoHaXRlbV9pZBgDIAEoCRIRCglkaXJlY3Rpb24YBCABKAkSEAoIcmV2aXNpb24YBSABKAQSEgoKZHJhZnRfanNvbhgGIAEoCRIbChN1cGxvYWRlZF9maWxlc19qc29uGAcgASgJEhQKDHJlcXVlc3RfanNvbhgIIAEoCSKGAQoRQ2hhdFF1ZXVlUmVzcG9uc2USEAoIYWNjZXB0ZWQYASABKAgSDwoHbWVzc2FnZRgCIAEoCRIVCg1zbmFwc2hvdF9qc29uGAMgASgJEhEKCWl0ZW1fanNvbhgEIAEoCRISCgplcnJvcl9jb2RlGAUgASgJEhAKCHJldmlzaW9uGAYgASgEIlIKDkNoYXRRdWV1ZUV2ZW50EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIVCg1zbmFwc2hvdF9qc29uGAIgASgJEhAKCHJldmlzaW9uGAMgASgEIpUCCglDaGF0RXZlbnQSOwoEdHlwZRgBIAEoDjItLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRFdmVudC5DaGF0RXZlbnRUeXBlEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRIMCgRkYXRhGAMgASgJIqMBCg1DaGF0RXZlbnRUeXBlEgkKBVRPS0VOEAASDAoIVEhJTktJTkcQARINCglUT09MX0NBTEwQAhIPCgtUT09MX1JFU1VMVBADEggKBERPTkUQBBIJCgVFUlJPUhAFEg8KC1RPT0xfU1RBVFVTEAYSEQoNSE9TVEVEX1NFQVJDSBAHEhAKDFVTRVJfTUVTU0FHRRAIEg4KClRSQUpFQ1RPUlkQCSK8AQoQQ2hhdENvbnRyb2xFdmVudBISCgpyZXF1ZXN0X2lkGAEgASgJEhkKEWNsaWVudF9yZXF1ZXN0X2lkGAIgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgDIAEoCRIRCglydW5fZXBvY2gYBCABKAMSDAoEdHlwZRgFIAEoCRINCgVzdGF0ZRgGIAEoCRISCgplcnJvcl9jb2RlGAcgASgJEg8KB21lc3NhZ2UYCCABKAkSCwoDc2VxGAkgASgDIvwBChNDaGF0UnVudGltZVNuYXBzaG90EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIOCgZydW5faWQYAiABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAyABKAkSEQoJd29ya2VyX2lkGAQgASgJEg0KBXN0YXRlGAUgASgJEgsKA2N3ZBgGIAEoCRISCgp1cGRhdGVkX2F0GAcgASgDEhAKCHJldmlzaW9uGAggASgDEhQKDGVudHJpZXNfanNvbhgJIAEoCRITCgt0b29sX3N0YXR1cxgKIAEoCRIhChl0b29sX3N0YXR1c19pc19jb21wYWN0aW9uGAsgASgIIuoBChJSdW50aW1lU3RhdHVzRXZlbnQSEQoJd29ya2VyX2lkGAEgASgJEg0KBXN0YXRlGAIgASgJEg8KB3Zpc2libGUYAyABKAgSGAoQYWN0aXZlX3J1bl9jb3VudBgEIAEoDRIRCgl0aW1lc3RhbXAYBSABKAMSOAoLYWN0aXZlX3J1bnMYBiADKAsyIy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UnVuUmVwb3J0EjoKDWZpbmlzaGVkX3J1bnMYByADKAsyIy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UnVuUmVwb3J0IoABCg1DaGF0UnVuUmVwb3J0Eg4KBnJ1bl9pZBgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSDQoFc3RhdGUYAyABKAkSEgoKZXJyb3JfY29kZRgEIAEoCRIPCgdtZXNzYWdlGAUgASgJEhIKCnVwZGF0ZWRfYXQYBiABKAMiRwoRQ3Jvbk1hbmFnZVJlcXVlc3QSDgoGYWN0aW9uGAEgASgJEg8KB3Rhc2tfaWQYAiABKAkSEQoJdGFza19qc29uGAMgASgJIjkKEkNyb25NYW5hZ2VSZXNwb25zZRIOCgZhY3Rpb24YASABKAkSEwoLcmVzdWx0X2pzb24YAiABKAkiVQoSSGlzdG9yeUxpc3RSZXF1ZXN0EgwKBHBhZ2UYASABKAUSEQoJcGFnZV9zaXplGAIgASgFEgsKA2N3ZBgDIAEoCRIRCgljd2RfZW1wdHkYBCABKAgibAoTSGlzdG9yeUxpc3RSZXNwb25zZRJACg1jb252ZXJzYXRpb25zGAEgAygLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeRITCgt0b3RhbF9jb3VudBgCIAEoBSKKAgoTQ29udmVyc2F0aW9uU3VtbWFyeRIKCgJpZBgBIAEoCRINCgV0aXRsZRgCIAEoCRISCgpjcmVhdGVkX2F0GAMgASgDEhIKCnVwZGF0ZWRfYXQYBCABKAMSFQoNbWVzc2FnZV9jb3VudBgFIAEoBRITCgtwcm92aWRlcl9pZBgGIAEoCRINCgVtb2RlbBgHIAEoCRISCgpzZXNzaW9uX2lkGAggASgJEgsKA2N3ZBgJIAEoCRIRCglpc19waW5uZWQYCiABKAgSEQoJcGlubmVkX2F0GAsgASgDEhEKCWlzX3NoYXJlZBgMIAEoCBIbChNzZWxlY3RlZF9tb2RlbF9qc29uGA0gASgJIkIKEUhpc3RvcnlHZXRSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIUCgxtYXhfbWVzc2FnZXMYAiABKAUi1AEKEkhpc3RvcnlHZXRSZXNwb25zZRIXCg9jb252ZXJzYXRpb25faWQYASABKAkSFQoNbWVzc2FnZXNfanNvbhgCIAEoCRIbChN0b3RhbF9tZXNzYWdlX2NvdW50GAMgASgFEh4KFnJldHVybmVkX21lc3NhZ2VfY291bnQYBCABKAUSEAoIaGFzX21vcmUYBSABKAgSPwoMY29udmVyc2F0aW9uGAYgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeSLRAQoWVHJhamVjdG9yeUZldGNoUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSEwoLc2VjdGlvbl9pZHMYAiADKAkSFAoMbWF4X3NlZ21lbnRzGAMgASgFEiEKFGJlZm9yZV9zZWdtZW50X2luZGV4GAQgASgFSACIAQESHQoVaW5jbHVkZV9zdWJhZ2VudF9ydW5zGAUgASgIEhgKEHN1YmFnZW50X3J1bl9pZHMYBiADKAlCFwoVX2JlZm9yZV9zZWdtZW50X2luZGV4IlwKGFRyYWplY3RvcnlTZWN0aW9uUGF5bG9hZBISCgpzZWN0aW9uX2lkGAEgASgJEgwKBHNsb3QYAiABKAkSDwoHY29udGVudBgDIAEoCRINCgVieXRlcxgEIAEoAyKsAgoXVHJhamVjdG9yeUZldGNoUmVzcG9uc2USFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhMKC2V2ZW50c19qc29uGAIgASgJEhEKCXRydW5jYXRlZBgDIAEoCBJACghzZWN0aW9ucxgEIAMoCzIuLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRyYWplY3RvcnlTZWN0aW9uUGF5bG9hZBIcChRvbGRlc3Rfc2VnbWVudF9pbmRleBgFIAEoBRIeChZyZXR1cm5lZF9zZWdtZW50X2NvdW50GAYgASgFEhsKE3RvdGFsX3NlZ21lbnRfY291bnQYByABKAUSFwoPaGFzX21vcmVfYmVmb3JlGAggASgIEhoKEnN1YmFnZW50X3J1bnNfanNvbhgJIAEoCSKFAQoUSGlzdG9yeVByZWZpeFJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhQKDG1heF9tZXNzYWdlcxgCIAEoBRI+ChBiYXNlX21lc3NhZ2VfcmVmGAMgASgLMiQubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdE1lc3NhZ2VSZWYi1wEKFUhpc3RvcnlQcmVmaXhSZXNwb25zZRIXCg9jb252ZXJzYXRpb25faWQYASABKAkSFQoNbWVzc2FnZXNfanNvbhgCIAEoCRIbChN0b3RhbF9tZXNzYWdlX2NvdW50GAMgASgFEh4KFnJldHVybmVkX21lc3NhZ2VfY291bnQYBCABKAUSEAoIaGFzX21vcmUYBSABKAgSPwoMY29udmVyc2F0aW9uGAYgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeSI+ChRIaXN0b3J5UmVuYW1lUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDQoFdGl0bGUYAiABKAkiWAoVSGlzdG9yeVJlbmFtZVJlc3BvbnNlEj8KDGNvbnZlcnNhdGlvbhgBIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkibwoUSGlzdG9yeUJyYW5jaFJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEj4KEGJhc2VfbWVzc2FnZV9yZWYYAiABKAsyJC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0TWVzc2FnZVJlZiJYChVIaXN0b3J5QnJhbmNoUmVzcG9uc2USPwoMY29udmVyc2F0aW9uGAEgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeSI/ChFIaXN0b3J5UGluUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSEQoJaXNfcGlubmVkGAIgASgIIlUKEkhpc3RvcnlQaW5SZXNwb25zZRI/Cgxjb252ZXJzYXRpb24YASABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Db252ZXJzYXRpb25TdW1tYXJ5IpIBChJIaXN0b3J5U2hhcmVTdGF0dXMSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEg8KB2VuYWJsZWQYAiABKAgSDQoFdG9rZW4YAyABKAkSEgoKY3JlYXRlZF9hdBgEIAEoAxISCgp1cGRhdGVkX2F0GAUgASgDEhsKE3JlZGFjdF90b29sX2NvbnRlbnQYBiABKAgiMQoWSGlzdG9yeVNoYXJlR2V0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkiUgoXSGlzdG9yeVNoYXJlR2V0UmVzcG9uc2USNwoFc2hhcmUYASABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5U2hhcmVTdGF0dXMifAoWSGlzdG9yeVNoYXJlU2V0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDwoHZW5hYmxlZBgCIAEoCBIgChNyZWRhY3RfdG9vbF9jb250ZW50GAMgASgISACIAQFCFgoUX3JlZGFjdF90b29sX2NvbnRlbnQiUgoXSGlzdG9yeVNoYXJlU2V0UmVzcG9uc2USNwoFc2hhcmUYASABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5U2hhcmVTdGF0dXMiKwoaSGlzdG9yeVNoYXJlUmVzb2x2ZVJlcXVlc3QSDQoFdG9rZW4YASABKAkiyAEKG0hpc3RvcnlTaGFyZVJlc29sdmVSZXNwb25zZRIXCg9jb252ZXJzYXRpb25faWQYASABKAkSFQoNbWVzc2FnZXNfanNvbhgCIAEoCRIbChN0b3RhbF9tZXNzYWdlX2NvdW50GAMgASgFEj8KDGNvbnZlcnNhdGlvbhgEIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkSGwoTcmVkYWN0X3Rvb2xfY29udGVudBgFIAEoCCIYChZIaXN0b3J5V29ya2RpcnNSZXF1ZXN0IlUKFUhpc3RvcnlXb3JrZGlyU3VtbWFyeRIMCgRwYXRoGAEgASgJEhoKEmNvbnZlcnNhdGlvbl9jb3VudBgCIAEoBRISCgp1cGRhdGVkX2F0GAMgASgDIlgKF0hpc3RvcnlXb3JrZGlyc1Jlc3BvbnNlEj0KCHdvcmtkaXJzGAEgAygLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVdvcmtkaXJTdW1tYXJ5Ii8KFEhpc3RvcnlEZWxldGVSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCSIXChVIaXN0b3J5RGVsZXRlUmVzcG9uc2UiegoQSGlzdG9yeVN5bmNFdmVudBIMCgRraW5kGAEgASgJEj8KDGNvbnZlcnNhdGlvbhgCIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkSFwoPY29udmVyc2F0aW9uX2lkGAMgASgJIhUKE1Byb3ZpZGVyTGlzdFJlcXVlc3QiLgoUUHJvdmlkZXJMaXN0UmVzcG9uc2USFgoOcHJvdmlkZXJzX2pzb24YASABKAkiFAoSU2V0dGluZ3NHZXRSZXF1ZXN0IiwKE1NldHRpbmdzR2V0UmVzcG9uc2USFQoNc2V0dGluZ3NfanNvbhgBIAEoCSIuChVTZXR0aW5nc1VwZGF0ZVJlcXVlc3QSFQoNc2V0dGluZ3NfanNvbhgBIAEoCSI7ChZTZXR0aW5nc1VwZGF0ZVJlc3BvbnNlEhAKCGFjY2VwdGVkGAEgASgIEg8KB21lc3NhZ2UYAiABKAkiPgogU2V0dGluZ3NSZXNldFNzaEtub3duSG9zdFJlcXVlc3QSDAoEaG9zdBgBIAEoCRIMCgRwb3J0GAIgASgNIjQKIVNldHRpbmdzUmVzZXRTc2hLbm93bkhvc3RSZXNwb25zZRIPCgdkZWxldGVkGAEgASgNIioKEVNldHRpbmdzU3luY0V2ZW50EhUKDXNldHRpbmdzX2pzb24YASABKAkiFwoVU2tpbGxGaWxlc0xpc3RSZXF1ZXN0IkwKFlNraWxsRmlsZXNMaXN0UmVzcG9uc2USEAoIcm9vdF9kaXIYASABKAkSDQoFcGF0aHMYAiADKAkSEQoJdHJ1bmNhdGVkGAMgASgIIigKGFNraWxsTWV0YWRhdGFSZWFkUmVxdWVzdBIMCgRwYXRoGAEgASgJIj4KGVNraWxsTWV0YWRhdGFSZWFkUmVzcG9uc2USDAoEbmFtZRgBIAEoCRITCgtkZXNjcmlwdGlvbhgCIAEoCSJEChRTa2lsbFRleHRSZWFkUmVxdWVzdBIMCgRwYXRoGAEgASgJEg4KBm9mZnNldBgCIAEoDRIOCgZsZW5ndGgYAyABKA0iOwoVU2tpbGxUZXh0UmVhZFJlc3BvbnNlEg8KB2NvbnRlbnQYASABKAkSEQoJdHJ1bmNhdGVkGAIgASgIIioKElNraWxsTWFuYWdlUmVxdWVzdBIUCgxwYXlsb2FkX2pzb24YASABKAkiKgoTU2tpbGxNYW5hZ2VSZXNwb25zZRITCgtyZXN1bHRfanNvbhgBIAEoCSJ3ChZGaWxlTWVudGlvbkxpc3RSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSEwoLbWF4X3Jlc3VsdHMYAiABKA0SDQoFcXVlcnkYAyABKAkSGAoLc2hvd19oaWRkZW4YBCABKAhIAIgBAUIOCgxfc2hvd19oaWRkZW4iPgoQRmlsZU1lbnRpb25FbnRyeRIMCgRwYXRoGAEgASgJEgwKBGtpbmQYAiABKAkSDgoGaGlkZGVuGAMgASgIImUKF0ZpbGVNZW50aW9uTGlzdFJlc3BvbnNlEjcKB2VudHJpZXMYASADKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5GaWxlTWVudGlvbkVudHJ5EhEKCXRydW5jYXRlZBgCIAEoCCIaChhJbnN0YWxsZWRBcHBzTGlzdFJlcXVlc3QiWQoRSW5zdGFsbGVkQXBwRW50cnkSDAoEbmFtZRgBIAEoCRIRCglidW5kbGVfaWQYAiABKAkSDAoEcGF0aBgDIAEoCRIVCg1pY29uX2RhdGFfdXJsGAQgASgJIlIKGUluc3RhbGxlZEFwcHNMaXN0UmVzcG9uc2USNQoEYXBwcxgBIAMoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkluc3RhbGxlZEFwcEVudHJ5IiIKEEN1YURyaXZlclJlcXVlc3QSDgoGYWN0aW9uGAEgASgJIjgKEUN1YURyaXZlclJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRITCgtyZXN1bHRfanNvbhgCIAEoCSI/CgZGc1Jvb3QSCgoCaWQYASABKAkSDAoEcGF0aBgCIAEoCRIMCgRraW5kGAMgASgJEg0KBWxhYmVsGAQgASgJIhAKDkZzUm9vdHNSZXF1ZXN0Ij4KD0ZzUm9vdHNSZXNwb25zZRIrCgVyb290cxgBIAMoCzIcLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUm9vdCI2ChFGc0xpc3REaXJzUmVxdWVzdBIMCgRwYXRoGAEgASgJEhMKC21heF9yZXN1bHRzGAIgASgNIigKCkZzRGlyRW50cnkSDAoEcGF0aBgBIAEoCRIMCgRuYW1lGAIgASgJImgKEkZzTGlzdERpcnNSZXNwb25zZRIMCgRwYXRoGAEgASgJEjEKB2VudHJpZXMYAiADKAsyIC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0RpckVudHJ5EhEKCXRydW5jYXRlZBgDIAEoCCI8ChxGc0NyZWF0ZVByb2plY3RGb2xkZXJSZXF1ZXN0Eg4KBnBhcmVudBgBIAEoCRIMCgRuYW1lGAIgASgJIi0KHUZzQ3JlYXRlUHJvamVjdEZvbGRlclJlc3BvbnNlEgwKBHBhdGgYASABKAkijAEKDUZzTGlzdFJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIMCgRwYXRoGAIgASgJEg0KBWRlcHRoGAMgASgNEg4KBm9mZnNldBgEIAEoDRITCgttYXhfcmVzdWx0cxgFIAEoDRIYCgtzaG93X2hpZGRlbhgGIAEoCEgAiAEBQg4KDF9zaG93X2hpZGRlbiI5CgtGc0xpc3RFbnRyeRIMCgRwYXRoGAEgASgJEgwKBGtpbmQYAiABKAkSDgoGaGlkZGVuGAMgASgIIrkBCg5Gc0xpc3RSZXNwb25zZRIMCgRwYXRoGAEgASgJEhAKCGhhc19wYXRoGAIgASgIEg0KBWRlcHRoGAMgASgNEg4KBm9mZnNldBgEIAEoDRITCgttYXhfcmVzdWx0cxgFIAEoDRINCgV0b3RhbBgGIAEoDRIQCghoYXNfbW9yZRgHIAEoCBIyCgdlbnRyaWVzGAggAygLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNMaXN0RW50cnkiOgoZRnNSZWFkRWRpdGFibGVUZXh0UmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEgwKBHBhdGgYAiABKAkijAEKGkZzUmVhZEVkaXRhYmxlVGV4dFJlc3BvbnNlEgwKBHBhdGgYASABKAkSDwoHY29udGVudBgCIAEoCRIQCghtdGltZV9tcxgDIAEoBBIUCgxjb250ZW50X2hhc2gYBCABKAkSEgoKc2l6ZV9ieXRlcxgFIAEoBBITCgt0b3RhbF9saW5lcxgGIAEoBCI8ChtGc1JlYWRXb3Jrc3BhY2VJbWFnZVJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIMCgRwYXRoGAIgASgJIokBChxGc1JlYWRXb3Jrc3BhY2VJbWFnZVJlc3BvbnNlEgwKBHBhdGgYASABKAkSEQoJbWltZV90eXBlGAIgASgJEgwKBGRhdGEYAyABKAkSEgoKc2l6ZV9ieXRlcxgEIAEoBBIQCghtdGltZV9tcxgFIAEoBBIUCgxjb250ZW50X2hhc2gYBiABKAki2wEKE0NoYXRGaWxlT3BlblJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEg8KB3dvcmtkaXIYAiABKAkSDAoEcGF0aBgDIAEoCRIOCgZzb3VyY2UYBCABKAkSEQoEbGluZRgFIAEoDUgAiAEBEhUKCGVuZF9saW5lGAYgASgNSAGIAQESEwoGY29sdW1uGAcgASgNSAKIAQESHAoUb3Blbl9pbl9maWxlX21hbmFnZXIYCCABKAhCBwoFX2xpbmVCCwoJX2VuZF9saW5lQgkKB19jb2x1bW4izgEKFENoYXRGaWxlT3BlblJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRIMCgRraW5kGAIgASgJEg8KB3dvcmtkaXIYAyABKAkSDAoEcGF0aBgEIAEoCRIRCgRsaW5lGAUgASgNSACIAQESFQoIZW5kX2xpbmUYBiABKA1IAYgBARITCgZjb2x1bW4YByABKA1IAogBARIZChFvdXRzaWRlX3dvcmtzcGFjZRgIIAEoCEIHCgVfbGluZUILCglfZW5kX2xpbmVCCQoHX2NvbHVtbiLOAQoSRnNXcml0ZVRleHRSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSDAoEcGF0aBgCIAEoCRIPCgdjb250ZW50GAMgASgJEgwKBG1vZGUYBCABKAkSGQoRZXhwZWN0ZWRfbXRpbWVfbXMYBSABKAQSHQoVZXhwZWN0ZWRfY29udGVudF9oYXNoGAYgASgJEh0KFWhhc19leHBlY3RlZF9tdGltZV9tcxgHIAEoCBIhChloYXNfZXhwZWN0ZWRfY29udGVudF9oYXNoGAggASgIIp0BChNGc1dyaXRlVGV4dFJlc3BvbnNlEgwKBHBhdGgYASABKAkSDAoEbW9kZRgCIAEoCRIWCg5leGlzdGVkX2JlZm9yZRgDIAEoCBIVCg1ieXRlc193cml0dGVuGAQgASgEEhAKCG10aW1lX21zGAUgASgEEhQKDGNvbnRlbnRfaGFzaBgGIAEoCRITCgt0b3RhbF9saW5lcxgHIAEoBCIzChJGc0NyZWF0ZURpclJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIMCgRwYXRoGAIgASgJIjEKE0ZzQ3JlYXRlRGlyUmVzcG9uc2USDAoEcGF0aBgBIAEoCRIMCgRraW5kGAIgASgJIkYKD0ZzUmVuYW1lUmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEhEKCWZyb21fcGF0aBgCIAEoCRIPCgd0b19wYXRoGAMgASgJIkEKEEZzUmVuYW1lUmVzcG9uc2USEQoJZnJvbV9wYXRoGAEgASgJEgwKBHBhdGgYAiABKAkSDAoEa2luZBgDIAEoCSIwCg9Gc0RlbGV0ZVJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIMCgRwYXRoGAIgASgJIi4KEEZzRGVsZXRlUmVzcG9uc2USDAoEcGF0aBgBIAEoCRIMCgRraW5kGAIgASgJIiAKC1BpbmdSZXF1ZXN0EhEKCXRpbWVzdGFtcBgBIAEoAyIhCgxQb25nUmVzcG9uc2USEQoJdGltZXN0YW1wGAEgASgDIi4KDUVycm9yUmVzcG9uc2USDAoEY29kZRgBIAEoBRIPCgdtZXNzYWdlGAIgASgJIr4BChVQcm92aWRlck1vZGVsc1JlcXVlc3QSFQoNcHJvdmlkZXJfdHlwZRgBIAEoCRIQCghiYXNlX3VybBgCIAEoCRIPCgdhcGlfa2V5GAMgASgJEhgKEHVzZV9zeXN0ZW1fcHJveHkYBCABKAgSEgoKbW9kZWxzX3VybBgFIAEoCRITCgtwcm92aWRlcl9pZBgGIAEoCRIYCgtpc19mdWxsX3VybBgHIAEoCEgAiAEBQg4KDF9pc19mdWxsX3VybCItChZQcm92aWRlck1vZGVsc1Jlc3BvbnNlEhMKC21vZGVsc19qc29uGAEgASgJIlEKFFByb3ZpZGVyVXNhZ2VSZXF1ZXN0EhMKC3Byb3ZpZGVyX2lkGAEgASgJEg8KB3JlZnJlc2gYAiABKAgSEwoLY29uZmlnX2pzb24YAyABKAkiLAoVUHJvdmlkZXJVc2FnZVJlc3BvbnNlEhMKC3Jlc3VsdF9qc29uGAEgASgJIogBChBDaGF0SW5ncmVzc0JhdGNoEg4KBnJ1bl9pZBgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSEQoJZmlyc3Rfc2VxGAMgASgEEjgKB3JlY29yZHMYBCADKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc1JlY29yZCKaAgoRQ2hhdEluZ3Jlc3NSZWNvcmQSNwoFZGVsdGEYASABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc0RlbHRhSAASQQoKY2hlY2twb2ludBgCIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzQ2hlY2twb2ludEgAEj0KCHRlcm1pbmFsGAMgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NUZXJtaW5hbEgAEj8KCWhlYXJ0YmVhdBgEIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzSGVhcnRiZWF0SABCCQoHcGF5bG9hZCI5ChBDaGF0SW5ncmVzc0RlbHRhEhIKCmV2ZW50X2pzb24YASABKAkSEQoJd29ya2VyX2lkGAIgASgJIioKFENoYXRJbmdyZXNzSGVhcnRiZWF0EhIKCnVwZGF0ZWRfYXQYASABKAMixAEKFUNoYXRJbmdyZXNzQ2hlY2twb2ludBIaChJjb3ZlcnNfdGhyb3VnaF9zZXEYASABKAQSEAoIcmV2aXNpb24YAiABKAQSHQoVY29tcHJlc3NlZF9wcm9qZWN0aW9uGAMgASgMEhoKEnVuY29tcHJlc3NlZF9ieXRlcxgEIAEoBBIOCgZzaGEyNTYYBSABKAkSGAoQY29udGVudF9jb21wbGV0ZRgGIAEoCBIYChBoaXN0b3J5X3JlcXVpcmVkGAcgASgIIvwBChNDaGF0SW5ncmVzc1Rlcm1pbmFsEhoKEmNvdmVyc190aHJvdWdoX3NlcRgBIAEoBBIQCghyZXZpc2lvbhgCIAEoBBIdChVjb21wcmVzc2VkX3Byb2plY3Rpb24YAyABKAwSGgoSdW5jb21wcmVzc2VkX2J5dGVzGAQgASgEEg4KBnNoYTI1NhgFIAEoCRIYChBjb250ZW50X2NvbXBsZXRlGAYgASgIEhgKEGhpc3RvcnlfcmVxdWlyZWQYByABKAgSDQoFc3RhdGUYCCABKAkSEgoKZXJyb3JfY29kZRgJIAEoCRIVCg1lcnJvcl9tZXNzYWdlGAogASgJIk0KEUNoYXRJbmdyZXNzUmVzdW1lEjgKBHJ1bnMYASADKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc1J1blJlc3VtZSLVAQoUQ2hhdEluZ3Jlc3NSdW5SZXN1bWUSDgoGcnVuX2lkGAEgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRIXCg9yZXBsYXlfZnJvbV9zZXEYAyABKAQSGgoScmVwbGF5X3Rocm91Z2hfc2VxGAQgASgEEhAKCG5leHRfc2VxGAUgASgEEh0KFWxhdGVzdF9jaGVja3BvaW50X3NlcRgGIAEoBBIUCgx0ZXJtaW5hbF9zZXEYByABKAQSGAoQdGVybWluYWxfcGVuZGluZxgIIAEoCCLOAQoTQ2hhdEluZ3Jlc3NGcmFnbWVudBIOCgZydW5faWQYASABKAkSFwoPY29udmVyc2F0aW9uX2lkGAIgASgJEhIKCnNvdXJjZV9zZXEYAyABKAQSFgoOZnJhZ21lbnRfaW5kZXgYBCABKA0SFgoOZnJhZ21lbnRfY291bnQYBSABKA0SHAoUZW5jb2RlZF9yZWNvcmRfY2h1bmsYBiABKAwSHAoUZW5jb2RlZF9yZWNvcmRfYnl0ZXMYByABKAQSDgoGc2hhMjU2GAggASgJItwCCg5DaGF0SW5ncmVzc0FjaxIOCgZydW5faWQYASABKAkSFwoPY29udmVyc2F0aW9uX2lkGAIgASgJEhkKEWNvbW1pdHRlZF90aHJvdWdoGAMgASgEEhUKDWV4cGVjdGVkX25leHQYBCABKAQSOwoGYWN0aW9uGAUgASgOMisubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NBY2suQWN0aW9uEhoKEnRlcm1pbmFsX2NvbW1pdHRlZBgGIAEoCBISCgplcnJvcl9jb2RlGAcgASgJEhUKDWVycm9yX21lc3NhZ2UYCCABKAkiawoGQWN0aW9uEhYKEkFDVElPTl9VTlNQRUNJRklFRBAAEgwKCENPTlRJTlVFEAESGAoUUkVQTEFZX0ZST01fRVhQRUNURUQQAhITCg9TRU5EX0NIRUNLUE9JTlQQAxIMCghSRUpFQ1RFRBAEIjwKFEhpc3RvcnlTZXRDd2RSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRILCgNjd2QYAiABKAkiWAoVSGlzdG9yeVNldEN3ZFJlc3BvbnNlEj8KDGNvbnZlcnNhdGlvbhgBIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkiZgoXV29ya3NwYWNlUm9vdEdyYW50RHJhZnQSDwoCaWQYASABKAlIAIgBARINCgVhbGlhcxgCIAEoCRIUCgxkaXNwbGF5X3BhdGgYAyABKAkSDgoGYWNjZXNzGAQgASgJQgUKA19pZCLSAQoSV29ya3NwYWNlUm9vdEdyYW50EgoKAmlkGAEgASgJEhIKCnByb2plY3RfaWQYAiABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgDIAEoCRINCgVhbGlhcxgEIAEoCRIUCgxkaXNwbGF5X3BhdGgYBSABKAkSFgoOY2Fub25pY2FsX3BhdGgYBiABKAkSDgoGYWNjZXNzGAcgASgJEg0KBXN0YXRlGAggASgJEhIKCmNyZWF0ZWRfYXQYCSABKAMSEgoKdXBkYXRlZF9hdBgKIAEoAyKVAQoaV29ya3NwYWNlUm9vdEdyYW50c1JlcXVlc3QSDgoGYWN0aW9uGAEgASgJEhIKCnByb2plY3RfaWQYAiABKAkSFAoMcHJvamVjdF9wYXRoGAMgASgJEj0KBmdyYW50cxgEIAMoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLldvcmtzcGFjZVJvb3RHcmFudERyYWZ0IlcKG1dvcmtzcGFjZVJvb3RHcmFudHNSZXNwb25zZRI4CgZncmFudHMYASADKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Xb3Jrc3BhY2VSb290R3JhbnQiPAoXQ2hlY2twb2ludEV4cGVjdGVkRW50cnkSCwoDa2V5GAEgASgJEhQKDGN1cnJlbnRfaGFzaBgCIAEoCSKpAQoRQ2hlY2twb2ludFJlcXVlc3QSDgoGYWN0aW9uGAEgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRIQCgh0dXJuX3NlcRgDIAEoBBIYChBhdXRob3JpemVkX3Jvb3RzGAQgAygJEj8KCGV4cGVjdGVkGAUgAygLMi0ubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hlY2twb2ludEV4cGVjdGVkRW50cnkiOQoSQ2hlY2twb2ludFJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRITCgtyZXN1bHRfanNvbhgCIAEoCSJXChlDaGF0Q29udmVyc2F0aW9uUmVmZXJlbmNlEgoKAmlkGAEgASgJEg0KBXRpdGxlGAIgASgJEgsKA2N3ZBgDIAEoCRISCgp1cGRhdGVkX2F0GAQgASgDKuUBChhJbXBvcnREaXJlY3RvcnlPcGVyYXRpb24SKgomSU1QT1JUX0RJUkVDVE9SWV9PUEVSQVRJT05fVU5TUEVDSUZJRUQQABIkCiBJTVBPUlRfRElSRUNUT1JZX09QRVJBVElPTl9TVEFSVBABEioKJklNUE9SVF9ESVJFQ1RPUllfT1BFUkFUSU9OX1dSSVRFX0NIVU5LEAISJQohSU1QT1JUX0RJUkVDVE9SWV9PUEVSQVRJT05fQ09NTUlUEAMSJAogSU1QT1JUX0RJUkVDVE9SWV9PUEVSQVRJT05fQUJPUlQQBCrGBAoPVHVubmVsRnJhbWVLaW5kEiEKHVRVTk5FTF9GUkFNRV9LSU5EX1VOU1BFQ0lGSUVEEAASKAokVFVOTkVMX0ZSQU1FX0tJTkRfSFRUUF9SRVFVRVNUX1NUQVJUEAESJwojVFVOTkVMX0ZSQU1FX0tJTkRfSFRUUF9SRVFVRVNUX0JPRFkQAhImCiJUVU5ORUxfRlJBTUVfS0lORF9IVFRQX1JFUVVFU1RfRU5EEAMSKQolVFVOTkVMX0ZSQU1FX0tJTkRfSFRUUF9SRVNQT05TRV9TVEFSVBAEEigKJFRVTk5FTF9GUkFNRV9LSU5EX0hUVFBfUkVTUE9OU0VfQk9EWRAFEicKI1RVTk5FTF9GUkFNRV9LSU5EX0hUVFBfUkVTUE9OU0VfRU5EEAYSHQoZVFVOTkVMX0ZSQU1FX0tJTkRfV1NfRElBTBAHEiAKHFRVTk5FTF9GUkFNRV9LSU5EX1dTX0RJQUxfT0sQCBIjCh9UVU5ORUxfRlJBTUVfS0lORF9XU19ESUFMX0VSUk9SEAkSHgoaVFVOTkVMX0ZSQU1FX0tJTkRfV1NfRlJBTUUQChIeChpUVU5ORUxfRlJBTUVfS0lORF9XU19DTE9TRRALEhsKF1RVTk5FTF9GUkFNRV9LSU5EX0VSUk9SEAwSHAoYVFVOTkVMX0ZSQU1FX0tJTkRfQ0FOQ0VMEA0SGgoWVFVOTkVMX0ZSQU1FX0tJTkRfUElORxAOEhoKFlRVTk5FTF9GUkFNRV9LSU5EX1BPTkcQDyqBAQoTVHVubmVsV3NNZXNzYWdlVHlwZRImCiJUVU5ORUxfV1NfTUVTU0FHRV9UWVBFX1VOU1BFQ0lGSUVEEAASHwobVFVOTkVMX1dTX01FU1NBR0VfVFlQRV9URVhUEAESIQodVFVOTkVMX1dTX01FU1NBR0VfVFlQRV9CSU5BUlkQAkJAWj5naXRodWIuY29tL2xpdmVhZ2VudC9hZ2VudC1nYXRld2F5L2ludGVybmFsL3Byb3RvL3YyO2dhdGV3YXl2MmIGcHJvdG8z"); + fileDesc("ChZwcm90by92Mi9nYXRld2F5LnByb3RvEhRsaXZlYWdlbnQuZ2F0ZXdheS52MiLyHwoPR2F0ZXdheUVudmVsb3BlEhIKCnJlcXVlc3RfaWQYASABKAkSEQoJdGltZXN0YW1wGAIgASgDEkAKDGNoYXRfY29tbWFuZBgKIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRDb21tYW5kUmVxdWVzdEgAEj4KC2Nyb25fbWFuYWdlGBQgASgLMicubGl2ZWFnZW50LmdhdGV3YXkudjIuQ3Jvbk1hbmFnZVJlcXVlc3RIABJACgxoaXN0b3J5X2xpc3QYHiABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5TGlzdFJlcXVlc3RIABI+CgtoaXN0b3J5X2dldBgfIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlHZXRSZXF1ZXN0SAASRAoOaGlzdG9yeV9yZW5hbWUYICABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UmVuYW1lUmVxdWVzdEgAEkQKDmhpc3RvcnlfZGVsZXRlGCEgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeURlbGV0ZVJlcXVlc3RIABJECg5oaXN0b3J5X3ByZWZpeBgiIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlQcmVmaXhSZXF1ZXN0SAASPgoLaGlzdG9yeV9waW4YIyABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UGluUmVxdWVzdEgAEkkKEWhpc3Rvcnlfc2hhcmVfZ2V0GCQgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlR2V0UmVxdWVzdEgAEkkKEWhpc3Rvcnlfc2hhcmVfc2V0GCUgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlU2V0UmVxdWVzdEgAElEKFWhpc3Rvcnlfc2hhcmVfcmVzb2x2ZRgmIAEoCzIwLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZVJlc29sdmVSZXF1ZXN0SAASSAoQaGlzdG9yeV93b3JrZGlycxgnIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlXb3JrZGlyc1JlcXVlc3RIABJCCg1wcm92aWRlcl9saXN0GCggASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuUHJvdmlkZXJMaXN0UmVxdWVzdEgAEkAKDHNldHRpbmdzX2dldBgpIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzR2V0UmVxdWVzdEgAEkYKD3NldHRpbmdzX3VwZGF0ZRgqIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzVXBkYXRlUmVxdWVzdEgAEkcKEHNraWxsX2ZpbGVzX2xpc3QYKyABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Ta2lsbEZpbGVzTGlzdFJlcXVlc3RIABJNChNza2lsbF9tZXRhZGF0YV9yZWFkGCwgASgLMi4ubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxNZXRhZGF0YVJlYWRSZXF1ZXN0SAASRQoPc2tpbGxfdGV4dF9yZWFkGC0gASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxUZXh0UmVhZFJlcXVlc3RIABJJChFmaWxlX21lbnRpb25fbGlzdBguIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZpbGVNZW50aW9uTGlzdFJlcXVlc3RIABJRChV1cGxvYWRfcmVhZGFibGVfZmlsZXMYLyABKAsyMC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5VcGxvYWRSZWFkYWJsZUZpbGVzUmVxdWVzdEgAEjgKCGZzX3Jvb3RzGDAgASgLMiQubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNSb290c1JlcXVlc3RIABI/Cgxmc19saXN0X2RpcnMYMSABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0xpc3REaXJzUmVxdWVzdEgAEjEKBHBpbmcYMiABKAsyIS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5QaW5nUmVxdWVzdEgAElMKFnVwbG9hZGVkX2ltYWdlX3ByZXZpZXcYMyABKAsyMS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5VcGxvYWRlZEltYWdlUHJldmlld1JlcXVlc3RIABJCCg1tZW1vcnlfbWFuYWdlGDQgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuTWVtb3J5TWFuYWdlUmVxdWVzdEgAEkAKDHNraWxsX21hbmFnZRg1IAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsTWFuYWdlUmVxdWVzdEgAElYKGGZzX2NyZWF0ZV9wcm9qZWN0X2ZvbGRlchg2IAEoCzIyLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzQ3JlYXRlUHJvamVjdEZvbGRlclJlcXVlc3RIABJBChB0ZXJtaW5hbF9yZXF1ZXN0GDcgASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxSZXF1ZXN0SAASNgoHZnNfbGlzdBg4IAEoCzIjLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzTGlzdFJlcXVlc3RIABJBCg1mc193cml0ZV90ZXh0GDkgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNXcml0ZVRleHRSZXF1ZXN0SAASQQoNZnNfY3JlYXRlX2Rpchg6IAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzQ3JlYXRlRGlyUmVxdWVzdEgAEjoKCWZzX3JlbmFtZRg7IAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVuYW1lUmVxdWVzdEgAEjoKCWZzX2RlbGV0ZRg8IAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzRGVsZXRlUmVxdWVzdEgAEjcKC2dpdF9yZXF1ZXN0GD0gASgLMiAubGl2ZWFnZW50LmdhdGV3YXkudjIuR2l0UmVxdWVzdEgAElAKFWZzX3JlYWRfZWRpdGFibGVfdGV4dBg+IAEoCzIvLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZEVkaXRhYmxlVGV4dFJlcXVlc3RIABJUChdmc19yZWFkX3dvcmtzcGFjZV9pbWFnZRg/IAEoCzIxLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZFdvcmtzcGFjZUltYWdlUmVxdWVzdEgAEjkKDHNmdHBfcmVxdWVzdBhAIAEoCzIhLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNmdHBSZXF1ZXN0SAASRgoPcHJvdmlkZXJfbW9kZWxzGEEgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuUHJvdmlkZXJNb2RlbHNSZXF1ZXN0SAASXwodc2V0dGluZ3NfcmVzZXRfc3NoX2tub3duX2hvc3QYSCABKAsyNi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc1Jlc2V0U3NoS25vd25Ib3N0UmVxdWVzdEgAEjwKCmNoYXRfcXVldWUYSSABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UXVldWVSZXF1ZXN0SAASQAoQY2hhdF9pbmdyZXNzX2FjaxhLIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzQWNrSAASQQoMdHVubmVsX3N0YXRlGFAgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsU3RhdGVTbmFwc2hvdEgAEj8KD3R1bm5lbF9tdXRhdGlvbhhRIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbE11dGF0aW9uSAASOQoMdHVubmVsX2ZyYW1lGFIgASgLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsRnJhbWVIABJGCg93b3Jrc3BhY2Vfd2F0Y2gYWiABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Xb3Jrc3BhY2VXYXRjaFJlcXVlc3RIABJOChdtYW5hZ2VkX3Byb2Nlc3NfcmVxdWVzdBhbIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLk1hbmFnZWRQcm9jZXNzUmVxdWVzdEgAEkQKDmhpc3RvcnlfYnJhbmNoGFwgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeUJyYW5jaFJlcXVlc3RIABJECg5wcm92aWRlcl91c2FnZRhdIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyVXNhZ2VSZXF1ZXN0SAASQwoOY2hhdF9maWxlX29wZW4YXiABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0RmlsZU9wZW5SZXF1ZXN0SAASRQoPaGlzdG9yeV9zZXRfY3dkGF8gASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNldEN3ZFJlcXVlc3RIABJRChV3b3Jrc3BhY2Vfcm9vdF9ncmFudHMYYCABKAsyMC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Xb3Jrc3BhY2VSb290R3JhbnRzUmVxdWVzdEgAEkgKEGltcG9ydF9kaXJlY3RvcnkYYSABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5JbXBvcnREaXJlY3RvcnlSZXF1ZXN0SAASPQoKY2hlY2twb2ludBhiIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoZWNrcG9pbnRSZXF1ZXN0SAASSAoQdHJhamVjdG9yeV9mZXRjaBhjIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRyYWplY3RvcnlGZXRjaFJlcXVlc3RIABJNChNpbnN0YWxsZWRfYXBwc19saXN0GGQgASgLMi4ubGl2ZWFnZW50LmdhdGV3YXkudjIuSW5zdGFsbGVkQXBwc0xpc3RSZXF1ZXN0SAASQAoMY2xhcmlmeV90dXJuGGUgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2xhcmlmeVR1cm5SZXF1ZXN0SAASPAoKY3VhX2RyaXZlchhmIAEoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLkN1YURyaXZlclJlcXVlc3RIAEIJCgdwYXlsb2FkSgQIQxBESgQIRBBFSgQIRRBGSgQIShBLIoYpCg1BZ2VudEVudmVsb3BlEhIKCnJlcXVlc3RfaWQYASABKAkSEQoJdGltZXN0YW1wGAIgASgDEjUKCmNoYXRfZXZlbnQYCiABKAsyHy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0RXZlbnRIABJEChBjcm9uX21hbmFnZV9yZXNwGBQgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuQ3Jvbk1hbmFnZVJlc3BvbnNlSAASRgoRaGlzdG9yeV9saXN0X3Jlc3AYHiABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5TGlzdFJlc3BvbnNlSAASRAoQaGlzdG9yeV9nZXRfcmVzcBgfIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlHZXRSZXNwb25zZUgAEkoKE2hpc3RvcnlfcmVuYW1lX3Jlc3AYICABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UmVuYW1lUmVzcG9uc2VIABJKChNoaXN0b3J5X2RlbGV0ZV9yZXNwGCEgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeURlbGV0ZVJlc3BvbnNlSAASPgoMaGlzdG9yeV9zeW5jGCIgASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVN5bmNFdmVudEgAEkoKE2hpc3RvcnlfcHJlZml4X3Jlc3AYIyABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UHJlZml4UmVzcG9uc2VIABJEChBoaXN0b3J5X3Bpbl9yZXNwGCQgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVBpblJlc3BvbnNlSAASTwoWaGlzdG9yeV9zaGFyZV9nZXRfcmVzcBglIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZUdldFJlc3BvbnNlSAASTwoWaGlzdG9yeV9zaGFyZV9zZXRfcmVzcBgmIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZVNldFJlc3BvbnNlSAASVwoaaGlzdG9yeV9zaGFyZV9yZXNvbHZlX3Jlc3AYJyABKAsyMS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5U2hhcmVSZXNvbHZlUmVzcG9uc2VIABJOChVoaXN0b3J5X3dvcmtkaXJzX3Jlc3AYOCABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5V29ya2RpcnNSZXNwb25zZUgAEkgKEnByb3ZpZGVyX2xpc3RfcmVzcBgoIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyTGlzdFJlc3BvbnNlSAASRgoRc2V0dGluZ3NfZ2V0X3Jlc3AYKSABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc0dldFJlc3BvbnNlSAASTAoUc2V0dGluZ3NfdXBkYXRlX3Jlc3AYKiABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc1VwZGF0ZVJlc3BvbnNlSAASQAoNc2V0dGluZ3Nfc3luYxgrIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzU3luY0V2ZW50SAASTQoVc2tpbGxfZmlsZXNfbGlzdF9yZXNwGCwgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxGaWxlc0xpc3RSZXNwb25zZUgAElMKGHNraWxsX21ldGFkYXRhX3JlYWRfcmVzcBgtIAEoCzIvLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsTWV0YWRhdGFSZWFkUmVzcG9uc2VIABJLChRza2lsbF90ZXh0X3JlYWRfcmVzcBguIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsVGV4dFJlYWRSZXNwb25zZUgAEk8KFmZpbGVfbWVudGlvbl9saXN0X3Jlc3AYLyABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5GaWxlTWVudGlvbkxpc3RSZXNwb25zZUgAElcKGnVwbG9hZF9yZWFkYWJsZV9maWxlc19yZXNwGDAgASgLMjEubGl2ZWFnZW50LmdhdGV3YXkudjIuVXBsb2FkUmVhZGFibGVGaWxlc1Jlc3BvbnNlSAASPgoNZnNfcm9vdHNfcmVzcBgxIAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUm9vdHNSZXNwb25zZUgAEjIKBHBvbmcYMiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Qb25nUmVzcG9uc2VIABJFChFmc19saXN0X2RpcnNfcmVzcBgzIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzTGlzdERpcnNSZXNwb25zZUgAElkKG3VwbG9hZGVkX2ltYWdlX3ByZXZpZXdfcmVzcBg0IAEoCzIyLmxpdmVhZ2VudC5nYXRld2F5LnYyLlVwbG9hZGVkSW1hZ2VQcmV2aWV3UmVzcG9uc2VIABJIChJtZW1vcnlfbWFuYWdlX3Jlc3AYNSABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5NZW1vcnlNYW5hZ2VSZXNwb25zZUgAEkYKEXNraWxsX21hbmFnZV9yZXNwGDYgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxNYW5hZ2VSZXNwb25zZUgAElwKHWZzX2NyZWF0ZV9wcm9qZWN0X2ZvbGRlcl9yZXNwGDcgASgLMjMubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNDcmVhdGVQcm9qZWN0Rm9sZGVyUmVzcG9uc2VIABJDChF0ZXJtaW5hbF9yZXNwb25zZRg5IAEoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsUmVzcG9uc2VIABI9Cg50ZXJtaW5hbF9ldmVudBg6IAEoCzIjLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsRXZlbnRIABI8Cgxmc19saXN0X3Jlc3AYOyABKAsyJC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0xpc3RSZXNwb25zZUgAEkcKEmZzX3dyaXRlX3RleHRfcmVzcBg8IAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzV3JpdGVUZXh0UmVzcG9uc2VIABJHChJmc19jcmVhdGVfZGlyX3Jlc3AYPSABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0NyZWF0ZURpclJlc3BvbnNlSAASQAoOZnNfcmVuYW1lX3Jlc3AYPiABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc1JlbmFtZVJlc3BvbnNlSAASQAoOZnNfZGVsZXRlX3Jlc3AYPyABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0RlbGV0ZVJlc3BvbnNlSAASOQoMZ2l0X3Jlc3BvbnNlGEAgASgLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuR2l0UmVzcG9uc2VIABJWChpmc19yZWFkX2VkaXRhYmxlX3RleHRfcmVzcBhBIAEoCzIwLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZEVkaXRhYmxlVGV4dFJlc3BvbnNlSAASWgocZnNfcmVhZF93b3Jrc3BhY2VfaW1hZ2VfcmVzcBhCIAEoCzIyLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZFdvcmtzcGFjZUltYWdlUmVzcG9uc2VIABI7Cg1zZnRwX3Jlc3BvbnNlGEkgASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuU2Z0cFJlc3BvbnNlSAASNQoKc2Z0cF9ldmVudBhKIAEoCzIfLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNmdHBFdmVudEgAEkIKD2NoYXRfcXVldWVfcmVzcBhLIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRRdWV1ZVJlc3BvbnNlSAASQAoQY2hhdF9xdWV1ZV9ldmVudBhMIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRRdWV1ZUV2ZW50SAASPgoMY2hhdF9jb250cm9sGEYgASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdENvbnRyb2xFdmVudEgAEkIKDnJ1bnRpbWVfc3RhdHVzGEcgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuUnVudGltZVN0YXR1c0V2ZW50SAASZQoic2V0dGluZ3NfcmVzZXRfc3NoX2tub3duX2hvc3RfcmVzcBhIIAEoCzI3LmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzUmVzZXRTc2hLbm93bkhvc3RSZXNwb25zZUgAEkoKFWNoYXRfcnVudGltZV9zbmFwc2hvdBhNIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRSdW50aW1lU25hcHNob3RIABJMChRwcm92aWRlcl9tb2RlbHNfcmVzcBhPIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyTW9kZWxzUmVzcG9uc2VIABJCCg50dW5uZWxfZGVzaXJlZBhQIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbERlc2lyZWRTdGF0ZUgAEkwKFnR1bm5lbF9tdXRhdGlvbl9yZXN1bHQYUSABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxNdXRhdGlvblJlc3VsdEgAEjkKDHR1bm5lbF9mcmFtZRhSIAEoCzIhLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbEZyYW1lSAASRgoTdHVubmVsX3Byb2JlX3JlcG9ydBhTIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbFByb2JlUmVwb3J0SAASSgoSd29ya3NwYWNlX2FjdGl2aXR5GFogASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuV29ya3NwYWNlQWN0aXZpdHlFdmVudEgAElAKGG1hbmFnZWRfcHJvY2Vzc19yZXNwb25zZRhbIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLk1hbmFnZWRQcm9jZXNzUmVzcG9uc2VIABJQChhtYW5hZ2VkX3Byb2Nlc3Nfc25hcHNob3QYXCABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5NYW5hZ2VkUHJvY2Vzc1NuYXBzaG90SAASSgoTaGlzdG9yeV9icmFuY2hfcmVzcBhdIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlCcmFuY2hSZXNwb25zZUgAEkoKE3Byb3ZpZGVyX3VzYWdlX3Jlc3AYXiABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Qcm92aWRlclVzYWdlUmVzcG9uc2VIABJEChJjaGF0X2luZ3Jlc3NfYmF0Y2gYXyABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc0JhdGNoSAASRgoTY2hhdF9pbmdyZXNzX3Jlc3VtZRhgIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzUmVzdW1lSAASSgoVY2hhdF9pbmdyZXNzX2ZyYWdtZW50GGEgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NGcmFnbWVudEgAEkkKE2NoYXRfZmlsZV9vcGVuX3Jlc3AYYiABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0RmlsZU9wZW5SZXNwb25zZUgAEjQKBWVycm9yGGMgASgLMiMubGl2ZWFnZW50LmdhdGV3YXkudjIuRXJyb3JSZXNwb25zZUgAEksKFGhpc3Rvcnlfc2V0X2N3ZF9yZXNwGGQgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNldEN3ZFJlc3BvbnNlSAASVwoad29ya3NwYWNlX3Jvb3RfZ3JhbnRzX3Jlc3AYZSABKAsyMS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Xb3Jrc3BhY2VSb290R3JhbnRzUmVzcG9uc2VIABJOChVpbXBvcnRfZGlyZWN0b3J5X3Jlc3AYZiABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5JbXBvcnREaXJlY3RvcnlSZXNwb25zZUgAEkMKD2NoZWNrcG9pbnRfcmVzcBhnIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoZWNrcG9pbnRSZXNwb25zZUgAEk4KFXRyYWplY3RvcnlfZmV0Y2hfcmVzcBhoIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRyYWplY3RvcnlGZXRjaFJlc3BvbnNlSAASUwoYaW5zdGFsbGVkX2FwcHNfbGlzdF9yZXNwGGkgASgLMi8ubGl2ZWFnZW50LmdhdGV3YXkudjIuSW5zdGFsbGVkQXBwc0xpc3RSZXNwb25zZUgAEkYKEWNsYXJpZnlfdHVybl9yZXNwGGogASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2xhcmlmeVR1cm5SZXNwb25zZUgAEkIKD2N1YV9kcml2ZXJfcmVzcBhrIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkN1YURyaXZlclJlc3BvbnNlSABCCQoHcGF5bG9hZEoECEMQREoECEQQRUoECEUQRkoECE4QTyJVChFDaGF0U2VsZWN0ZWRNb2RlbBIaChJjdXN0b21fcHJvdmlkZXJfaWQYASABKAkSDQoFbW9kZWwYAiABKAkSFQoNcHJvdmlkZXJfdHlwZRgDIAEoCSKAAQoTQ2hhdFJ1bnRpbWVDb250cm9scxIYChB0aGlua2luZ19lbmFibGVkGAEgASgIEiEKGW5hdGl2ZV93ZWJfc2VhcmNoX2VuYWJsZWQYAiABKAgSEQoJcmVhc29uaW5nGAMgASgJEhkKEXBsYW5fbW9kZV9lbmFibGVkGAQgASgIIpQBChJDbGFyaWZ5VHVyblJlcXVlc3QSFQoNbWVzc2FnZXNfanNvbhgBIAEoCRITCgtwcm92aWRlcl9pZBgCIAEoCRINCgVtb2RlbBgDIAEoCRJDChBydW50aW1lX2NvbnRyb2xzGAQgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdFJ1bnRpbWVDb250cm9scyJUChNDbGFyaWZ5VHVyblJlc3BvbnNlEhIKCmZpbmFsX3RleHQYASABKAkSEgoKZXJyb3JfY29kZRgCIAEoCRIVCg1lcnJvcl9tZXNzYWdlGAMgASgJIokBChBDaGF0VXBsb2FkZWRGaWxlEhUKDXJlbGF0aXZlX3BhdGgYASABKAkSFQoNYWJzb2x1dGVfcGF0aBgCIAEoCRIRCglmaWxlX25hbWUYAyABKAkSDAoEa2luZBgEIAEoCRISCgpzaXplX2J5dGVzGAUgASgDEhIKCmRlZHVwZV9rZXkYBiABKAkiSwoSVXBsb2FkUmVhZGFibGVGaWxlEhEKCWZpbGVfbmFtZRgBIAEoCRIRCgltaW1lX3R5cGUYAiABKAkSDwoHY29udGVudBgDIAEoDCJmChpVcGxvYWRSZWFkYWJsZUZpbGVzUmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEjcKBWZpbGVzGAIgAygLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuVXBsb2FkUmVhZGFibGVGaWxlImUKG1VwbG9hZFJlYWRhYmxlRmlsZXNSZXNwb25zZRI1CgVmaWxlcxgBIAMoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRVcGxvYWRlZEZpbGUSDwoHc2tpcHBlZBgCIAMoCSI9ChNJbXBvcnREaXJlY3RvcnlGaWxlEhUKDXJlbGF0aXZlX3BhdGgYASABKAkSDwoHY29udGVudBgCIAEoDCLDAgoWSW1wb3J0RGlyZWN0b3J5UmVxdWVzdBIMCgRuYW1lGAEgASgJEg4KBnRhcmdldBgCIAEoCRI8CgVmaWxlcxgDIAMoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkltcG9ydERpcmVjdG9yeUZpbGVCAhgBEhMKC3RyYW5zZmVyX2lkGAQgASgJEkEKCW9wZXJhdGlvbhgFIAEoDjIuLmxpdmVhZ2VudC5nYXRld2F5LnYyLkltcG9ydERpcmVjdG9yeU9wZXJhdGlvbhITCgt0b3RhbF9maWxlcxgGIAEoDRITCgt0b3RhbF9ieXRlcxgHIAEoBBIVCg1yZWxhdGl2ZV9wYXRoGAggASgJEg4KBm9mZnNldBgJIAEoBBINCgVjaHVuaxgKIAEoDBIVCg1maWxlX2NvbXBsZXRlGAsgASgIIn4KF0ltcG9ydERpcmVjdG9yeVJlc3BvbnNlEhEKCXJvb3RfcGF0aBgBIAEoCRISCgpmaWxlX2NvdW50GAIgASgFEg8KB3NraXBwZWQYAyADKAkSEwoLdHJhbnNmZXJfaWQYBCABKAkSFgoOcmVjZWl2ZWRfYnl0ZXMYBSABKAQiRQobVXBsb2FkZWRJbWFnZVByZXZpZXdSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSFQoNYWJzb2x1dGVfcGF0aBgCIAEoCSI/ChxVcGxvYWRlZEltYWdlUHJldmlld1Jlc3BvbnNlEhEKCW1pbWVfdHlwZRgBIAEoCRIMCgRkYXRhGAIgASgJInsKClR1bm5lbFNwZWMSCgoCaWQYASABKAkSEQoJc2x1Z19oaW50GAIgASgJEgwKBG5hbWUYAyABKAkSEgoKdGFyZ2V0X3VybBgEIAEoCRISCgpleHBpcmVzX2F0GAUgASgDEhgKEHByb2plY3RfcGF0aF9rZXkYBiABKAkiWQoSVHVubmVsRGVzaXJlZFN0YXRlEjEKB3R1bm5lbHMYASADKAsyIC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxTcGVjEhAKCHJldmlzaW9uGAIgASgEImYKDFR1bm5lbEhlYWx0aBIOCgZzdGF0dXMYASABKAkSEwoLaHR0cF9zdGF0dXMYAiABKA0SDQoFZXJyb3IYAyABKAkSEgoKY2hlY2tlZF9hdBgEIAEoAxIOCgZydHRfbXMYBSABKA0i8AEKDFR1bm5lbFN0YXR1cxIKCgJpZBgBIAEoCRIMCgRzbHVnGAIgASgJEgwKBG5hbWUYAyABKAkSEgoKdGFyZ2V0X3VybBgEIAEoCRITCgtwdWJsaWNfcGF0aBgFIAEoCRISCgpjcmVhdGVkX2F0GAYgASgDEhIKCmV4cGlyZXNfYXQYByABKAMSGgoSYWN0aXZlX2Nvbm5lY3Rpb25zGAggASgNEhgKEHByb2plY3RfcGF0aF9rZXkYCSABKAkSMQoFbG9jYWwYCiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFsdGgipQEKE1R1bm5lbFN0YXRlU25hcHNob3QSMwoHdHVubmVscxgBIAMoCzIiLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbFN0YXR1cxIQCghyZXZpc2lvbhgCIAEoBBIUCgxhZ2VudF9vbmxpbmUYAyABKAgSMQoFcmVsYXkYBCABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFsdGgimQEKDlR1bm5lbE11dGF0aW9uEg4KBmFjdGlvbhgBIAEoCRIRCgl0dW5uZWxfaWQYAiABKAkSEgoKdGFyZ2V0X3VybBgDIAEoCRIMCgRuYW1lGAQgASgJEhgKC3R0bF9zZWNvbmRzGAUgASgNSACIAQESGAoQcHJvamVjdF9wYXRoX2tleRgGIAEoCUIOCgxfdHRsX3NlY29uZHMiVAoUVHVubmVsTXV0YXRpb25SZXN1bHQSEQoJdHVubmVsX2lkGAEgASgJEhIKCmVycm9yX2NvZGUYAiABKAkSFQoNZXJyb3JfbWVzc2FnZRgDIAEoCSJZChFUdW5uZWxQcm9iZVJlc3VsdBIRCgl0dW5uZWxfaWQYASABKAkSMQoFbG9jYWwYAiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFsdGgiTQoRVHVubmVsUHJvYmVSZXBvcnQSOAoHcmVzdWx0cxgBIAMoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbFByb2JlUmVzdWx0IisKDFR1bm5lbEhlYWRlchIMCgRuYW1lGAEgASgJEg0KBXZhbHVlGAIgASgJIvUCCgtUdW5uZWxGcmFtZRIRCglzdHJlYW1faWQYASABKAkSMwoEa2luZBgCIAEoDjIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbEZyYW1lS2luZBISCgp0YXJnZXRfdXJsGAMgASgJEg4KBm1ldGhvZBgEIAEoCRIMCgRwYXRoGAUgASgJEjMKB2hlYWRlcnMYBiADKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFkZXISDgoGc3RhdHVzGAcgASgNEgwKBGJvZHkYCCABKAwSDQoFZXJyb3IYCSABKAkSQgoPd3NfbWVzc2FnZV90eXBlGAogASgOMikubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsV3NNZXNzYWdlVHlwZRIWCg53c19zdWJwcm90b2NvbBgLIAEoCRIVCg13c19jbG9zZV9jb2RlGAwgASgNEhcKD3dzX2Nsb3NlX3JlYXNvbhgNIAEoCSIpChVXb3Jrc3BhY2VXYXRjaFJlcXVlc3QSEAoId29ya2RpcnMYASADKAkifgoWV29ya3NwYWNlQWN0aXZpdHlFdmVudBIPCgd3b3JrZGlyGAEgASgJEhAKCHJldmlzaW9uGAIgASgEEgoKAmZzGAMgASgIEgsKA2dpdBgEIAEoCBIVCg1jaGFuZ2VkX3BhdGhzGAUgAygJEhEKCXRydW5jYXRlZBgGIAEoCCKWAgoUTWFuYWdlZFByb2Nlc3NSZWNvcmQSCgoCaWQYASABKAkSDQoFbGFiZWwYAiABKAkSDwoHY29tbWFuZBgDIAEoCRILCgNjd2QYBCABKAkSDQoFc2hlbGwYBSABKAkSCwoDcGlkGAYgASgNEhAKCGxvZ19wYXRoGAcgASgJEhIKCnN0YXJ0ZWRfYXQYCCABKAMSGAoLZmluaXNoZWRfYXQYCSABKANIAIgBARIWCglleGl0X2NvZGUYCiABKAVIAYgBARIPCgdydW5uaW5nGAsgASgIEhAKCGlzb2xhdGVkGAwgASgIEhAKCHJlc3RvcmVkGA0gASgIQg4KDF9maW5pc2hlZF9hdEIMCgpfZXhpdF9jb2RlImkKFk1hbmFnZWRQcm9jZXNzU25hcHNob3QSPQoJcHJvY2Vzc2VzGAEgAygLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuTWFuYWdlZFByb2Nlc3NSZWNvcmQSEAoIcmV2aXNpb24YAiABKAQiTgoVTWFuYWdlZFByb2Nlc3NSZXF1ZXN0Eg4KBmFjdGlvbhgBIAEoCRISCgpwcm9jZXNzX2lkGAIgASgJEhEKCW1heF9ieXRlcxgDIAEoDSK3AQoWTWFuYWdlZFByb2Nlc3NSZXNwb25zZRIOCgZhY3Rpb24YASABKAkSPgoIc25hcHNob3QYAiABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5NYW5hZ2VkUHJvY2Vzc1NuYXBzaG90EhMKC2xvZ19jb250ZW50GAMgASgJEhAKCGxvZ19wYXRoGAQgASgJEhUKDWxvZ190cnVuY2F0ZWQYBSABKAgSDwoHc3RvcHBlZBgGIAEoCCI5ChNNZW1vcnlNYW5hZ2VSZXF1ZXN0Eg8KB2NvbW1hbmQYASABKAkSEQoJYXJnc19qc29uGAIgASgJIisKFE1lbW9yeU1hbmFnZVJlc3BvbnNlEhMKC3Jlc3VsdF9qc29uGAEgASgJIpgDCg9UZXJtaW5hbFJlcXVlc3QSDgoGYWN0aW9uGAEgASgJEhIKCnNlc3Npb25faWQYAiABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgDIAEoCRILCgNjd2QYBCABKAkSDQoFc2hlbGwYBSABKAkSDQoFdGl0bGUYBiABKAkSDAoEZGF0YRgHIAEoCRIMCgRjb2xzGAggASgNEgwKBHJvd3MYCSABKA0SEQoJbWF4X2J5dGVzGAogASgNEhMKC3NzaF9ob3N0X2lkGAsgASgJEhEKCXByb21wdF9pZBgMIAEoCRIVCg1wcm9tcHRfYW5zd2VyGA0gASgJEhYKDnRydXN0X2hvc3Rfa2V5GA4gASgIEhQKDHNmdHBfZW5hYmxlZBgPIAEoCBIOCgZ0YWJfaWQYECABKAkSEAoIdGFiX2tpbmQYESABKAkSEwoLcmVtb3RlX2hvc3QYEiABKAkSEwoLcmVtb3RlX3BvcnQYEyABKA0SEgoKbG9jYWxfcG9ydBgUIAEoDRISCgpmb3J3YXJkX2lkGBUgASgJIrICCg9UZXJtaW5hbFNlc3Npb24SCgoCaWQYASABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgCIAEoCRILCgNjd2QYAyABKAkSDQoFc2hlbGwYBCABKAkSDQoFdGl0bGUYBSABKAkSCwoDcGlkGAYgASgNEgwKBGNvbHMYByABKA0SDAoEcm93cxgIIAEoDRISCgpjcmVhdGVkX2F0GAkgASgEEhIKCnVwZGF0ZWRfYXQYCiABKAQSEwoLZmluaXNoZWRfYXQYCyABKAQSEQoJZXhpdF9jb2RlGAwgASgFEg8KB3J1bm5pbmcYDSABKAgSDAoEa2luZBgOIAEoCRI2CgNzc2gYDyABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaE1ldGFkYXRhItsBChNUZXJtaW5hbFNzaE1ldGFkYXRhEg8KB2hvc3RfaWQYASABKAkSEQoJaG9zdF9uYW1lGAIgASgJEhAKCHVzZXJuYW1lGAMgASgJEgwKBGhvc3QYBCABKAkSDAoEcG9ydBgFIAEoDRIRCglhdXRoX3R5cGUYBiABKAkSDgoGc3RhdHVzGAcgASgJEhkKEXJlY29ubmVjdF9hdHRlbXB0GAggASgNEh4KFnJlY29ubmVjdF9tYXhfYXR0ZW1wdHMYCSABKA0SFAoMc2Z0cF9lbmFibGVkGAogASgIIpEDCgtTZnRwUmVxdWVzdBIOCgZhY3Rpb24YASABKAkSEgoKc2Vzc2lvbl9pZBgCIAEoCRIYChBwcm9qZWN0X3BhdGhfa2V5GAMgASgJEg8KB3dvcmtkaXIYBCABKAkSEgoKbG9jYWxfcGF0aBgFIAEoCRITCgtyZW1vdGVfcGF0aBgGIAEoCRIRCglmcm9tX3BhdGgYByABKAkSDwoHdG9fcGF0aBgIIAEoCRIRCglkaXJlY3Rpb24YCSABKAkSEwoLdGFyZ2V0X3BhdGgYCiABKAkSEQoJcmVjdXJzaXZlGAsgASgIEhEKCW92ZXJ3cml0ZRgMIAEoCBIPCgdjb250ZW50GA0gASgJEg4KBm9mZnNldBgOIAEoBBIRCgltYXhfYnl0ZXMYDyABKAQSEwoLc3RyaWN0X3V0ZjgYECABKAgSFgoOZXhwZWN0ZWRfbXRpbWUYESABKAQSGwoTZXhwZWN0ZWRfc2l6ZV9ieXRlcxgSIAEoBBIaChJjcmVhdGVfcGFyZW50X2RpcnMYEyABKAgiWAoJU2Z0cEVudHJ5EgwKBHBhdGgYASABKAkSDAoEbmFtZRgCIAEoCRIMCgRraW5kGAMgASgJEhIKCnNpemVfYnl0ZXMYBCABKAQSDQoFbXRpbWUYBSABKAQi8gEKDFNmdHBUcmFuc2ZlchIKCgJpZBgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJEhEKCWRpcmVjdGlvbhgDIAEoCRIOCgZzdGF0dXMYBCABKAkSEwoLc291cmNlX3BhdGgYBSABKAkSEwoLdGFyZ2V0X3BhdGgYBiABKAkSFAoMY3VycmVudF9wYXRoGAcgASgJEhIKCmJ5dGVzX2RvbmUYCCABKAQSEwoLYnl0ZXNfdG90YWwYCSABKAQSEgoKZmlsZXNfZG9uZRgKIAEoDRITCgtmaWxlc190b3RhbBgLIAEoDRINCgVlcnJvchgMIAEoCSKwAgoMU2Z0cFJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRIMCgRwYXRoGAIgASgJEjAKB2VudHJpZXMYAyADKAsyHy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwRW50cnkSLgoFZW50cnkYBCABKAsyHy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwRW50cnkSDgoGZXhpc3RzGAUgASgIEjQKCHRyYW5zZmVyGAYgASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuU2Z0cFRyYW5zZmVyEg8KB2NvbnRlbnQYByABKAkSDgoGb2Zmc2V0GAggASgEEhIKCmJ5dGVzX3JlYWQYCSABKAQSEgoKc2l6ZV9ieXRlcxgKIAEoBBIRCgl0cnVuY2F0ZWQYCyABKAgiTwoJU2Z0cEV2ZW50EgwKBGtpbmQYASABKAkSNAoIdHJhbnNmZXIYAiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwVHJhbnNmZXIiwQEKEVRlcm1pbmFsU3NoUHJvbXB0EgoKAmlkGAEgASgJEgwKBGtpbmQYAiABKAkSDwoHaG9zdF9pZBgDIAEoCRIRCglob3N0X25hbWUYBCABKAkSDAoEaG9zdBgFIAEoCRIMCgRwb3J0GAYgASgNEg8KB21lc3NhZ2UYByABKAkSGgoSZmluZ2VycHJpbnRfc2hhMjU2GAggASgJEhAKCGtleV90eXBlGAkgASgJEhMKC2Fuc3dlcl9lY2hvGAogASgIIkEKE1Rlcm1pbmFsU2hlbGxPcHRpb24SCgoCaWQYASABKAkSDQoFbGFiZWwYAiABKAkSDwoHY29tbWFuZBgDIAEoCSKAAQoOVGVybWluYWxTc2hUYWISCgoCaWQYASABKAkSEgoKc2Vzc2lvbl9pZBgCIAEoCRIYChBwcm9qZWN0X3BhdGhfa2V5GAMgASgJEgwKBGtpbmQYBCABKAkSEgoKY3JlYXRlZF9hdBgFIAEoBBISCgp1cGRhdGVkX2F0GAYgASgEIn8KF1Rlcm1pbmFsU3NoVGFic1NuYXBzaG90EhgKEHByb2plY3RfcGF0aF9rZXkYASABKAkSMgoEdGFicxgCIAMoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoVGFiEhAKCHJldmlzaW9uGAQgASgESgQIAxAEIv0BChdUZXJtaW5hbFNzaExvY2FsRm9yd2FyZBIKCgJpZBgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJEhgKEHByb2plY3RfcGF0aF9rZXkYAyABKAkSEgoKbG9jYWxfaG9zdBgEIAEoCRISCgpsb2NhbF9wb3J0GAUgASgNEg8KB2FkZHJlc3MYBiABKAkSEwoLcmVtb3RlX2hvc3QYByABKAkSEwoLcmVtb3RlX3BvcnQYCCABKA0SDgoGc3RhdHVzGAkgASgJEhIKCmNyZWF0ZWRfYXQYCiABKAQSEgoKdXBkYXRlZF9hdBgLIAEoBBINCgVlcnJvchgMIAEoCSJ1CiBUZXJtaW5hbFNzaExvY2FsRm9yd2FyZHNTbmFwc2hvdBI/Cghmb3J3YXJkcxgBIAMoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoTG9jYWxGb3J3YXJkEhAKCHJldmlzaW9uGAIgASgEIn8KHVRlcm1pbmFsU3NoTG9jYWxGb3J3YXJkQWN0aW9uEj4KB2ZvcndhcmQYASABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaExvY2FsRm9yd2FyZBIQCghyZXZpc2lvbhgCIAEoBBIMCgRraW5kGAMgASgJIqcFChBUZXJtaW5hbFJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRI3CghzZXNzaW9ucxgCIAMoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU2Vzc2lvbhI2CgdzZXNzaW9uGAMgASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTZXNzaW9uEg4KBm91dHB1dBgEIAEoDBIRCgl0cnVuY2F0ZWQYBSABKAgSQAoNc2hlbGxfb3B0aW9ucxgGIAMoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU2hlbGxPcHRpb24SFQoNZGVmYXVsdF9zaGVsbBgHIAEoCRIbChNvdXRwdXRfc3RhcnRfb2Zmc2V0GAggASgEEhkKEW91dHB1dF9lbmRfb2Zmc2V0GAkgASgEEjsKCnNzaF9wcm9tcHQYCiABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaFByb21wdBISCgpsYXRlbmN5X21zGAsgASgNEj8KCHNzaF90YWJzGAwgASgLMi0ubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hUYWJzU25hcHNob3QSUgoSc3NoX2xvY2FsX2ZvcndhcmRzGA0gASgLMjYubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hMb2NhbEZvcndhcmRzU25hcHNob3QSTgoRc3NoX2xvY2FsX2ZvcndhcmQYDiABKAsyMy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaExvY2FsRm9yd2FyZEFjdGlvbhIoCiBzc2hfbG9jYWxfZm9yd2FyZF9wb3J0X2F2YWlsYWJsZRgPIAEoCCLaAgoNVGVybWluYWxFdmVudBIMCgRraW5kGAEgASgJEhIKCnNlc3Npb25faWQYAiABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgDIAEoCRI2CgdzZXNzaW9uGAQgASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTZXNzaW9uEgwKBGRhdGEYBSABKAwSGwoTb3V0cHV0X3N0YXJ0X29mZnNldBgGIAEoBBIZChFvdXRwdXRfZW5kX29mZnNldBgHIAEoBBI/Cghzc2hfdGFicxgIIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoVGFic1NuYXBzaG90Ek4KEXNzaF9sb2NhbF9mb3J3YXJkGAkgASgLMjMubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hMb2NhbEZvcndhcmRBY3Rpb24isgIKE1Rlcm1pbmFsU3RyZWFtRnJhbWUSDAoEa2luZBgBIAEoCRIRCglzdHJlYW1faWQYAiABKAkSEgoKc2Vzc2lvbl9pZBgDIAEoCRIYChBwcm9qZWN0X3BhdGhfa2V5GAQgASgJEgsKA3NlcRgFIAEoBBIUCgxzdGFydF9vZmZzZXQYBiABKAQSEgoKZW5kX29mZnNldBgHIAEoBBIMCgRjb2xzGAggASgNEgwKBHJvd3MYCSABKA0SEQoJbWF4X2J5dGVzGAogASgNEhEKCXRydW5jYXRlZBgLIAEoCBINCgVlcnJvchgMIAEoCRI2CgdzZXNzaW9uGA0gASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTZXNzaW9uEgwKBGRhdGEYDiABKAwiQAoKR2l0UmVxdWVzdBIOCgZhY3Rpb24YASABKAkSDwoHd29ya2RpchgCIAEoCRIRCglhcmdzX2pzb24YAyABKAkiMgoLR2l0UmVzcG9uc2USDgoGYWN0aW9uGAEgASgJEhMKC3Jlc3VsdF9qc29uGAIgASgJIuQDCgtDaGF0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDwoHbWVzc2FnZRgCIAEoCRI/Cg5zZWxlY3RlZF9tb2RlbBgDIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRTZWxlY3RlZE1vZGVsEhYKDmV4ZWN1dGlvbl9tb2RlGAQgASgJEg8KB3dvcmtkaXIYBSABKAkSPgoOdXBsb2FkZWRfZmlsZXMYByADKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0VXBsb2FkZWRGaWxlEhkKEWNsaWVudF9yZXF1ZXN0X2lkGAggASgJEkMKEHJ1bnRpbWVfY29udHJvbHMYCSABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UnVudGltZUNvbnRyb2xzEhQKDHF1ZXVlX3BvbGljeRgKIAEoCRIbChNjb21tYW5kX3NhZmV0eV9tb2RlGAsgASgJElEKGHJlZmVyZW5jZWRfY29udmVyc2F0aW9ucxgMIAMoCzIvLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRDb252ZXJzYXRpb25SZWZlcmVuY2VKBAgGEAdSFXNlbGVjdGVkX3N5c3RlbV90b29scyKKAQoOQ2hhdE1lc3NhZ2VSZWYSFQoNc2VnbWVudF9pbmRleBgBIAEoBRIVCg1tZXNzYWdlX2luZGV4GAIgASgFEhIKCnNlZ21lbnRfaWQYAyABKAkSEgoKbWVzc2FnZV9pZBgEIAEoCRIMCgRyb2xlGAUgASgJEhQKDGNvbnRlbnRfaGFzaBgGIAEoCSI8ChFDYW5jZWxDaGF0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDgoGcnVuX2lkGAIgASgJIs8BChJDaGF0Q29tbWFuZFJlcXVlc3QSDAoEdHlwZRgBIAEoCRIyCgdyZXF1ZXN0GAIgASgLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdFJlcXVlc3QSPgoQYmFzZV9tZXNzYWdlX3JlZhgDIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRNZXNzYWdlUmVmEjcKBmNhbmNlbBgEIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNhbmNlbENoYXRSZXF1ZXN0IrgBChBDaGF0UXVldWVSZXF1ZXN0Eg4KBmFjdGlvbhgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSDwoHaXRlbV9pZBgDIAEoCRIRCglkaXJlY3Rpb24YBCABKAkSEAoIcmV2aXNpb24YBSABKAQSEgoKZHJhZnRfanNvbhgGIAEoCRIbChN1cGxvYWRlZF9maWxlc19qc29uGAcgASgJEhQKDHJlcXVlc3RfanNvbhgIIAEoCSKGAQoRQ2hhdFF1ZXVlUmVzcG9uc2USEAoIYWNjZXB0ZWQYASABKAgSDwoHbWVzc2FnZRgCIAEoCRIVCg1zbmFwc2hvdF9qc29uGAMgASgJEhEKCWl0ZW1fanNvbhgEIAEoCRISCgplcnJvcl9jb2RlGAUgASgJEhAKCHJldmlzaW9uGAYgASgEIlIKDkNoYXRRdWV1ZUV2ZW50EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIVCg1zbmFwc2hvdF9qc29uGAIgASgJEhAKCHJldmlzaW9uGAMgASgEIpUCCglDaGF0RXZlbnQSOwoEdHlwZRgBIAEoDjItLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRFdmVudC5DaGF0RXZlbnRUeXBlEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRIMCgRkYXRhGAMgASgJIqMBCg1DaGF0RXZlbnRUeXBlEgkKBVRPS0VOEAASDAoIVEhJTktJTkcQARINCglUT09MX0NBTEwQAhIPCgtUT09MX1JFU1VMVBADEggKBERPTkUQBBIJCgVFUlJPUhAFEg8KC1RPT0xfU1RBVFVTEAYSEQoNSE9TVEVEX1NFQVJDSBAHEhAKDFVTRVJfTUVTU0FHRRAIEg4KClRSQUpFQ1RPUlkQCSK8AQoQQ2hhdENvbnRyb2xFdmVudBISCgpyZXF1ZXN0X2lkGAEgASgJEhkKEWNsaWVudF9yZXF1ZXN0X2lkGAIgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgDIAEoCRIRCglydW5fZXBvY2gYBCABKAMSDAoEdHlwZRgFIAEoCRINCgVzdGF0ZRgGIAEoCRISCgplcnJvcl9jb2RlGAcgASgJEg8KB21lc3NhZ2UYCCABKAkSCwoDc2VxGAkgASgDIvwBChNDaGF0UnVudGltZVNuYXBzaG90EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIOCgZydW5faWQYAiABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAyABKAkSEQoJd29ya2VyX2lkGAQgASgJEg0KBXN0YXRlGAUgASgJEgsKA2N3ZBgGIAEoCRISCgp1cGRhdGVkX2F0GAcgASgDEhAKCHJldmlzaW9uGAggASgDEhQKDGVudHJpZXNfanNvbhgJIAEoCRITCgt0b29sX3N0YXR1cxgKIAEoCRIhChl0b29sX3N0YXR1c19pc19jb21wYWN0aW9uGAsgASgIIuoBChJSdW50aW1lU3RhdHVzRXZlbnQSEQoJd29ya2VyX2lkGAEgASgJEg0KBXN0YXRlGAIgASgJEg8KB3Zpc2libGUYAyABKAgSGAoQYWN0aXZlX3J1bl9jb3VudBgEIAEoDRIRCgl0aW1lc3RhbXAYBSABKAMSOAoLYWN0aXZlX3J1bnMYBiADKAsyIy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UnVuUmVwb3J0EjoKDWZpbmlzaGVkX3J1bnMYByADKAsyIy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UnVuUmVwb3J0IoABCg1DaGF0UnVuUmVwb3J0Eg4KBnJ1bl9pZBgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSDQoFc3RhdGUYAyABKAkSEgoKZXJyb3JfY29kZRgEIAEoCRIPCgdtZXNzYWdlGAUgASgJEhIKCnVwZGF0ZWRfYXQYBiABKAMiRwoRQ3Jvbk1hbmFnZVJlcXVlc3QSDgoGYWN0aW9uGAEgASgJEg8KB3Rhc2tfaWQYAiABKAkSEQoJdGFza19qc29uGAMgASgJIjkKEkNyb25NYW5hZ2VSZXNwb25zZRIOCgZhY3Rpb24YASABKAkSEwoLcmVzdWx0X2pzb24YAiABKAkiVQoSSGlzdG9yeUxpc3RSZXF1ZXN0EgwKBHBhZ2UYASABKAUSEQoJcGFnZV9zaXplGAIgASgFEgsKA2N3ZBgDIAEoCRIRCgljd2RfZW1wdHkYBCABKAgibAoTSGlzdG9yeUxpc3RSZXNwb25zZRJACg1jb252ZXJzYXRpb25zGAEgAygLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeRITCgt0b3RhbF9jb3VudBgCIAEoBSKKAgoTQ29udmVyc2F0aW9uU3VtbWFyeRIKCgJpZBgBIAEoCRINCgV0aXRsZRgCIAEoCRISCgpjcmVhdGVkX2F0GAMgASgDEhIKCnVwZGF0ZWRfYXQYBCABKAMSFQoNbWVzc2FnZV9jb3VudBgFIAEoBRITCgtwcm92aWRlcl9pZBgGIAEoCRINCgVtb2RlbBgHIAEoCRISCgpzZXNzaW9uX2lkGAggASgJEgsKA2N3ZBgJIAEoCRIRCglpc19waW5uZWQYCiABKAgSEQoJcGlubmVkX2F0GAsgASgDEhEKCWlzX3NoYXJlZBgMIAEoCBIbChNzZWxlY3RlZF9tb2RlbF9qc29uGA0gASgJIkIKEUhpc3RvcnlHZXRSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIUCgxtYXhfbWVzc2FnZXMYAiABKAUi1AEKEkhpc3RvcnlHZXRSZXNwb25zZRIXCg9jb252ZXJzYXRpb25faWQYASABKAkSFQoNbWVzc2FnZXNfanNvbhgCIAEoCRIbChN0b3RhbF9tZXNzYWdlX2NvdW50GAMgASgFEh4KFnJldHVybmVkX21lc3NhZ2VfY291bnQYBCABKAUSEAoIaGFzX21vcmUYBSABKAgSPwoMY29udmVyc2F0aW9uGAYgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeSLRAQoWVHJhamVjdG9yeUZldGNoUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSEwoLc2VjdGlvbl9pZHMYAiADKAkSFAoMbWF4X3NlZ21lbnRzGAMgASgFEiEKFGJlZm9yZV9zZWdtZW50X2luZGV4GAQgASgFSACIAQESHQoVaW5jbHVkZV9zdWJhZ2VudF9ydW5zGAUgASgIEhgKEHN1YmFnZW50X3J1bl9pZHMYBiADKAlCFwoVX2JlZm9yZV9zZWdtZW50X2luZGV4IlwKGFRyYWplY3RvcnlTZWN0aW9uUGF5bG9hZBISCgpzZWN0aW9uX2lkGAEgASgJEgwKBHNsb3QYAiABKAkSDwoHY29udGVudBgDIAEoCRINCgVieXRlcxgEIAEoAyKsAgoXVHJhamVjdG9yeUZldGNoUmVzcG9uc2USFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhMKC2V2ZW50c19qc29uGAIgASgJEhEKCXRydW5jYXRlZBgDIAEoCBJACghzZWN0aW9ucxgEIAMoCzIuLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRyYWplY3RvcnlTZWN0aW9uUGF5bG9hZBIcChRvbGRlc3Rfc2VnbWVudF9pbmRleBgFIAEoBRIeChZyZXR1cm5lZF9zZWdtZW50X2NvdW50GAYgASgFEhsKE3RvdGFsX3NlZ21lbnRfY291bnQYByABKAUSFwoPaGFzX21vcmVfYmVmb3JlGAggASgIEhoKEnN1YmFnZW50X3J1bnNfanNvbhgJIAEoCSKFAQoUSGlzdG9yeVByZWZpeFJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhQKDG1heF9tZXNzYWdlcxgCIAEoBRI+ChBiYXNlX21lc3NhZ2VfcmVmGAMgASgLMiQubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdE1lc3NhZ2VSZWYi1wEKFUhpc3RvcnlQcmVmaXhSZXNwb25zZRIXCg9jb252ZXJzYXRpb25faWQYASABKAkSFQoNbWVzc2FnZXNfanNvbhgCIAEoCRIbChN0b3RhbF9tZXNzYWdlX2NvdW50GAMgASgFEh4KFnJldHVybmVkX21lc3NhZ2VfY291bnQYBCABKAUSEAoIaGFzX21vcmUYBSABKAgSPwoMY29udmVyc2F0aW9uGAYgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeSI+ChRIaXN0b3J5UmVuYW1lUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDQoFdGl0bGUYAiABKAkiWAoVSGlzdG9yeVJlbmFtZVJlc3BvbnNlEj8KDGNvbnZlcnNhdGlvbhgBIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkibwoUSGlzdG9yeUJyYW5jaFJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEj4KEGJhc2VfbWVzc2FnZV9yZWYYAiABKAsyJC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0TWVzc2FnZVJlZiJYChVIaXN0b3J5QnJhbmNoUmVzcG9uc2USPwoMY29udmVyc2F0aW9uGAEgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeSI/ChFIaXN0b3J5UGluUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSEQoJaXNfcGlubmVkGAIgASgIIlUKEkhpc3RvcnlQaW5SZXNwb25zZRI/Cgxjb252ZXJzYXRpb24YASABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Db252ZXJzYXRpb25TdW1tYXJ5IpIBChJIaXN0b3J5U2hhcmVTdGF0dXMSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEg8KB2VuYWJsZWQYAiABKAgSDQoFdG9rZW4YAyABKAkSEgoKY3JlYXRlZF9hdBgEIAEoAxISCgp1cGRhdGVkX2F0GAUgASgDEhsKE3JlZGFjdF90b29sX2NvbnRlbnQYBiABKAgiMQoWSGlzdG9yeVNoYXJlR2V0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkiUgoXSGlzdG9yeVNoYXJlR2V0UmVzcG9uc2USNwoFc2hhcmUYASABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5U2hhcmVTdGF0dXMifAoWSGlzdG9yeVNoYXJlU2V0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDwoHZW5hYmxlZBgCIAEoCBIgChNyZWRhY3RfdG9vbF9jb250ZW50GAMgASgISACIAQFCFgoUX3JlZGFjdF90b29sX2NvbnRlbnQiUgoXSGlzdG9yeVNoYXJlU2V0UmVzcG9uc2USNwoFc2hhcmUYASABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5U2hhcmVTdGF0dXMiKwoaSGlzdG9yeVNoYXJlUmVzb2x2ZVJlcXVlc3QSDQoFdG9rZW4YASABKAkiyAEKG0hpc3RvcnlTaGFyZVJlc29sdmVSZXNwb25zZRIXCg9jb252ZXJzYXRpb25faWQYASABKAkSFQoNbWVzc2FnZXNfanNvbhgCIAEoCRIbChN0b3RhbF9tZXNzYWdlX2NvdW50GAMgASgFEj8KDGNvbnZlcnNhdGlvbhgEIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkSGwoTcmVkYWN0X3Rvb2xfY29udGVudBgFIAEoCCIYChZIaXN0b3J5V29ya2RpcnNSZXF1ZXN0IlUKFUhpc3RvcnlXb3JrZGlyU3VtbWFyeRIMCgRwYXRoGAEgASgJEhoKEmNvbnZlcnNhdGlvbl9jb3VudBgCIAEoBRISCgp1cGRhdGVkX2F0GAMgASgDIlgKF0hpc3RvcnlXb3JrZGlyc1Jlc3BvbnNlEj0KCHdvcmtkaXJzGAEgAygLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVdvcmtkaXJTdW1tYXJ5Ii8KFEhpc3RvcnlEZWxldGVSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCSIXChVIaXN0b3J5RGVsZXRlUmVzcG9uc2UiegoQSGlzdG9yeVN5bmNFdmVudBIMCgRraW5kGAEgASgJEj8KDGNvbnZlcnNhdGlvbhgCIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkSFwoPY29udmVyc2F0aW9uX2lkGAMgASgJIhUKE1Byb3ZpZGVyTGlzdFJlcXVlc3QiLgoUUHJvdmlkZXJMaXN0UmVzcG9uc2USFgoOcHJvdmlkZXJzX2pzb24YASABKAkiFAoSU2V0dGluZ3NHZXRSZXF1ZXN0IiwKE1NldHRpbmdzR2V0UmVzcG9uc2USFQoNc2V0dGluZ3NfanNvbhgBIAEoCSIuChVTZXR0aW5nc1VwZGF0ZVJlcXVlc3QSFQoNc2V0dGluZ3NfanNvbhgBIAEoCSI7ChZTZXR0aW5nc1VwZGF0ZVJlc3BvbnNlEhAKCGFjY2VwdGVkGAEgASgIEg8KB21lc3NhZ2UYAiABKAkiPgogU2V0dGluZ3NSZXNldFNzaEtub3duSG9zdFJlcXVlc3QSDAoEaG9zdBgBIAEoCRIMCgRwb3J0GAIgASgNIjQKIVNldHRpbmdzUmVzZXRTc2hLbm93bkhvc3RSZXNwb25zZRIPCgdkZWxldGVkGAEgASgNIioKEVNldHRpbmdzU3luY0V2ZW50EhUKDXNldHRpbmdzX2pzb24YASABKAkiFwoVU2tpbGxGaWxlc0xpc3RSZXF1ZXN0IkwKFlNraWxsRmlsZXNMaXN0UmVzcG9uc2USEAoIcm9vdF9kaXIYASABKAkSDQoFcGF0aHMYAiADKAkSEQoJdHJ1bmNhdGVkGAMgASgIIigKGFNraWxsTWV0YWRhdGFSZWFkUmVxdWVzdBIMCgRwYXRoGAEgASgJIj4KGVNraWxsTWV0YWRhdGFSZWFkUmVzcG9uc2USDAoEbmFtZRgBIAEoCRITCgtkZXNjcmlwdGlvbhgCIAEoCSJEChRTa2lsbFRleHRSZWFkUmVxdWVzdBIMCgRwYXRoGAEgASgJEg4KBm9mZnNldBgCIAEoDRIOCgZsZW5ndGgYAyABKA0iOwoVU2tpbGxUZXh0UmVhZFJlc3BvbnNlEg8KB2NvbnRlbnQYASABKAkSEQoJdHJ1bmNhdGVkGAIgASgIIioKElNraWxsTWFuYWdlUmVxdWVzdBIUCgxwYXlsb2FkX2pzb24YASABKAkiKgoTU2tpbGxNYW5hZ2VSZXNwb25zZRITCgtyZXN1bHRfanNvbhgBIAEoCSJ3ChZGaWxlTWVudGlvbkxpc3RSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSEwoLbWF4X3Jlc3VsdHMYAiABKA0SDQoFcXVlcnkYAyABKAkSGAoLc2hvd19oaWRkZW4YBCABKAhIAIgBAUIOCgxfc2hvd19oaWRkZW4iPgoQRmlsZU1lbnRpb25FbnRyeRIMCgRwYXRoGAEgASgJEgwKBGtpbmQYAiABKAkSDgoGaGlkZGVuGAMgASgIImUKF0ZpbGVNZW50aW9uTGlzdFJlc3BvbnNlEjcKB2VudHJpZXMYASADKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5GaWxlTWVudGlvbkVudHJ5EhEKCXRydW5jYXRlZBgCIAEoCCIaChhJbnN0YWxsZWRBcHBzTGlzdFJlcXVlc3QiWQoRSW5zdGFsbGVkQXBwRW50cnkSDAoEbmFtZRgBIAEoCRIRCglidW5kbGVfaWQYAiABKAkSDAoEcGF0aBgDIAEoCRIVCg1pY29uX2RhdGFfdXJsGAQgASgJIlIKGUluc3RhbGxlZEFwcHNMaXN0UmVzcG9uc2USNQoEYXBwcxgBIAMoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkluc3RhbGxlZEFwcEVudHJ5IiIKEEN1YURyaXZlclJlcXVlc3QSDgoGYWN0aW9uGAEgASgJIjgKEUN1YURyaXZlclJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRITCgtyZXN1bHRfanNvbhgCIAEoCSI/CgZGc1Jvb3QSCgoCaWQYASABKAkSDAoEcGF0aBgCIAEoCRIMCgRraW5kGAMgASgJEg0KBWxhYmVsGAQgASgJIhAKDkZzUm9vdHNSZXF1ZXN0Ij4KD0ZzUm9vdHNSZXNwb25zZRIrCgVyb290cxgBIAMoCzIcLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUm9vdCI2ChFGc0xpc3REaXJzUmVxdWVzdBIMCgRwYXRoGAEgASgJEhMKC21heF9yZXN1bHRzGAIgASgNIigKCkZzRGlyRW50cnkSDAoEcGF0aBgBIAEoCRIMCgRuYW1lGAIgASgJImgKEkZzTGlzdERpcnNSZXNwb25zZRIMCgRwYXRoGAEgASgJEjEKB2VudHJpZXMYAiADKAsyIC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0RpckVudHJ5EhEKCXRydW5jYXRlZBgDIAEoCCI8ChxGc0NyZWF0ZVByb2plY3RGb2xkZXJSZXF1ZXN0Eg4KBnBhcmVudBgBIAEoCRIMCgRuYW1lGAIgASgJIi0KHUZzQ3JlYXRlUHJvamVjdEZvbGRlclJlc3BvbnNlEgwKBHBhdGgYASABKAkijAEKDUZzTGlzdFJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIMCgRwYXRoGAIgASgJEg0KBWRlcHRoGAMgASgNEg4KBm9mZnNldBgEIAEoDRITCgttYXhfcmVzdWx0cxgFIAEoDRIYCgtzaG93X2hpZGRlbhgGIAEoCEgAiAEBQg4KDF9zaG93X2hpZGRlbiI5CgtGc0xpc3RFbnRyeRIMCgRwYXRoGAEgASgJEgwKBGtpbmQYAiABKAkSDgoGaGlkZGVuGAMgASgIIrkBCg5Gc0xpc3RSZXNwb25zZRIMCgRwYXRoGAEgASgJEhAKCGhhc19wYXRoGAIgASgIEg0KBWRlcHRoGAMgASgNEg4KBm9mZnNldBgEIAEoDRITCgttYXhfcmVzdWx0cxgFIAEoDRINCgV0b3RhbBgGIAEoDRIQCghoYXNfbW9yZRgHIAEoCBIyCgdlbnRyaWVzGAggAygLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNMaXN0RW50cnkiOgoZRnNSZWFkRWRpdGFibGVUZXh0UmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEgwKBHBhdGgYAiABKAkijAEKGkZzUmVhZEVkaXRhYmxlVGV4dFJlc3BvbnNlEgwKBHBhdGgYASABKAkSDwoHY29udGVudBgCIAEoCRIQCghtdGltZV9tcxgDIAEoBBIUCgxjb250ZW50X2hhc2gYBCABKAkSEgoKc2l6ZV9ieXRlcxgFIAEoBBITCgt0b3RhbF9saW5lcxgGIAEoBCI8ChtGc1JlYWRXb3Jrc3BhY2VJbWFnZVJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIMCgRwYXRoGAIgASgJIokBChxGc1JlYWRXb3Jrc3BhY2VJbWFnZVJlc3BvbnNlEgwKBHBhdGgYASABKAkSEQoJbWltZV90eXBlGAIgASgJEgwKBGRhdGEYAyABKAkSEgoKc2l6ZV9ieXRlcxgEIAEoBBIQCghtdGltZV9tcxgFIAEoBBIUCgxjb250ZW50X2hhc2gYBiABKAki2wEKE0NoYXRGaWxlT3BlblJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEg8KB3dvcmtkaXIYAiABKAkSDAoEcGF0aBgDIAEoCRIOCgZzb3VyY2UYBCABKAkSEQoEbGluZRgFIAEoDUgAiAEBEhUKCGVuZF9saW5lGAYgASgNSAGIAQESEwoGY29sdW1uGAcgASgNSAKIAQESHAoUb3Blbl9pbl9maWxlX21hbmFnZXIYCCABKAhCBwoFX2xpbmVCCwoJX2VuZF9saW5lQgkKB19jb2x1bW4izgEKFENoYXRGaWxlT3BlblJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRIMCgRraW5kGAIgASgJEg8KB3dvcmtkaXIYAyABKAkSDAoEcGF0aBgEIAEoCRIRCgRsaW5lGAUgASgNSACIAQESFQoIZW5kX2xpbmUYBiABKA1IAYgBARITCgZjb2x1bW4YByABKA1IAogBARIZChFvdXRzaWRlX3dvcmtzcGFjZRgIIAEoCEIHCgVfbGluZUILCglfZW5kX2xpbmVCCQoHX2NvbHVtbiLOAQoSRnNXcml0ZVRleHRSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSDAoEcGF0aBgCIAEoCRIPCgdjb250ZW50GAMgASgJEgwKBG1vZGUYBCABKAkSGQoRZXhwZWN0ZWRfbXRpbWVfbXMYBSABKAQSHQoVZXhwZWN0ZWRfY29udGVudF9oYXNoGAYgASgJEh0KFWhhc19leHBlY3RlZF9tdGltZV9tcxgHIAEoCBIhChloYXNfZXhwZWN0ZWRfY29udGVudF9oYXNoGAggASgIIp0BChNGc1dyaXRlVGV4dFJlc3BvbnNlEgwKBHBhdGgYASABKAkSDAoEbW9kZRgCIAEoCRIWCg5leGlzdGVkX2JlZm9yZRgDIAEoCBIVCg1ieXRlc193cml0dGVuGAQgASgEEhAKCG10aW1lX21zGAUgASgEEhQKDGNvbnRlbnRfaGFzaBgGIAEoCRITCgt0b3RhbF9saW5lcxgHIAEoBCIzChJGc0NyZWF0ZURpclJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIMCgRwYXRoGAIgASgJIjEKE0ZzQ3JlYXRlRGlyUmVzcG9uc2USDAoEcGF0aBgBIAEoCRIMCgRraW5kGAIgASgJIkYKD0ZzUmVuYW1lUmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEhEKCWZyb21fcGF0aBgCIAEoCRIPCgd0b19wYXRoGAMgASgJIkEKEEZzUmVuYW1lUmVzcG9uc2USEQoJZnJvbV9wYXRoGAEgASgJEgwKBHBhdGgYAiABKAkSDAoEa2luZBgDIAEoCSIwCg9Gc0RlbGV0ZVJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIMCgRwYXRoGAIgASgJIi4KEEZzRGVsZXRlUmVzcG9uc2USDAoEcGF0aBgBIAEoCRIMCgRraW5kGAIgASgJIiAKC1BpbmdSZXF1ZXN0EhEKCXRpbWVzdGFtcBgBIAEoAyIhCgxQb25nUmVzcG9uc2USEQoJdGltZXN0YW1wGAEgASgDIi4KDUVycm9yUmVzcG9uc2USDAoEY29kZRgBIAEoBRIPCgdtZXNzYWdlGAIgASgJIjMKFFByb3ZpZGVyQ3VzdG9tSGVhZGVyEgwKBG5hbWUYASABKAkSDQoFdmFsdWUYAiABKAkiVAoVUHJvdmlkZXJDdXN0b21IZWFkZXJzEjsKB2hlYWRlcnMYASADKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Qcm92aWRlckN1c3RvbUhlYWRlciKDAgoVUHJvdmlkZXJNb2RlbHNSZXF1ZXN0EhUKDXByb3ZpZGVyX3R5cGUYASABKAkSEAoIYmFzZV91cmwYAiABKAkSDwoHYXBpX2tleRgDIAEoCRIYChB1c2Vfc3lzdGVtX3Byb3h5GAQgASgIEhIKCm1vZGVsc191cmwYBSABKAkSEwoLcHJvdmlkZXJfaWQYBiABKAkSGAoLaXNfZnVsbF91cmwYByABKAhIAIgBARJDCg5jdXN0b21faGVhZGVycxgIIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyQ3VzdG9tSGVhZGVyc0IOCgxfaXNfZnVsbF91cmwiLQoWUHJvdmlkZXJNb2RlbHNSZXNwb25zZRITCgttb2RlbHNfanNvbhgBIAEoCSJRChRQcm92aWRlclVzYWdlUmVxdWVzdBITCgtwcm92aWRlcl9pZBgBIAEoCRIPCgdyZWZyZXNoGAIgASgIEhMKC2NvbmZpZ19qc29uGAMgASgJIiwKFVByb3ZpZGVyVXNhZ2VSZXNwb25zZRITCgtyZXN1bHRfanNvbhgBIAEoCSKIAQoQQ2hhdEluZ3Jlc3NCYXRjaBIOCgZydW5faWQYASABKAkSFwoPY29udmVyc2F0aW9uX2lkGAIgASgJEhEKCWZpcnN0X3NlcRgDIAEoBBI4CgdyZWNvcmRzGAQgAygLMicubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NSZWNvcmQimgIKEUNoYXRJbmdyZXNzUmVjb3JkEjcKBWRlbHRhGAEgASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NEZWx0YUgAEkEKCmNoZWNrcG9pbnQYAiABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc0NoZWNrcG9pbnRIABI9Cgh0ZXJtaW5hbBgDIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzVGVybWluYWxIABI/CgloZWFydGJlYXQYBCABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc0hlYXJ0YmVhdEgAQgkKB3BheWxvYWQiOQoQQ2hhdEluZ3Jlc3NEZWx0YRISCgpldmVudF9qc29uGAEgASgJEhEKCXdvcmtlcl9pZBgCIAEoCSIqChRDaGF0SW5ncmVzc0hlYXJ0YmVhdBISCgp1cGRhdGVkX2F0GAEgASgDIsQBChVDaGF0SW5ncmVzc0NoZWNrcG9pbnQSGgoSY292ZXJzX3Rocm91Z2hfc2VxGAEgASgEEhAKCHJldmlzaW9uGAIgASgEEh0KFWNvbXByZXNzZWRfcHJvamVjdGlvbhgDIAEoDBIaChJ1bmNvbXByZXNzZWRfYnl0ZXMYBCABKAQSDgoGc2hhMjU2GAUgASgJEhgKEGNvbnRlbnRfY29tcGxldGUYBiABKAgSGAoQaGlzdG9yeV9yZXF1aXJlZBgHIAEoCCL8AQoTQ2hhdEluZ3Jlc3NUZXJtaW5hbBIaChJjb3ZlcnNfdGhyb3VnaF9zZXEYASABKAQSEAoIcmV2aXNpb24YAiABKAQSHQoVY29tcHJlc3NlZF9wcm9qZWN0aW9uGAMgASgMEhoKEnVuY29tcHJlc3NlZF9ieXRlcxgEIAEoBBIOCgZzaGEyNTYYBSABKAkSGAoQY29udGVudF9jb21wbGV0ZRgGIAEoCBIYChBoaXN0b3J5X3JlcXVpcmVkGAcgASgIEg0KBXN0YXRlGAggASgJEhIKCmVycm9yX2NvZGUYCSABKAkSFQoNZXJyb3JfbWVzc2FnZRgKIAEoCSJNChFDaGF0SW5ncmVzc1Jlc3VtZRI4CgRydW5zGAEgAygLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NSdW5SZXN1bWUi1QEKFENoYXRJbmdyZXNzUnVuUmVzdW1lEg4KBnJ1bl9pZBgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSFwoPcmVwbGF5X2Zyb21fc2VxGAMgASgEEhoKEnJlcGxheV90aHJvdWdoX3NlcRgEIAEoBBIQCghuZXh0X3NlcRgFIAEoBBIdChVsYXRlc3RfY2hlY2twb2ludF9zZXEYBiABKAQSFAoMdGVybWluYWxfc2VxGAcgASgEEhgKEHRlcm1pbmFsX3BlbmRpbmcYCCABKAgizgEKE0NoYXRJbmdyZXNzRnJhZ21lbnQSDgoGcnVuX2lkGAEgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRISCgpzb3VyY2Vfc2VxGAMgASgEEhYKDmZyYWdtZW50X2luZGV4GAQgASgNEhYKDmZyYWdtZW50X2NvdW50GAUgASgNEhwKFGVuY29kZWRfcmVjb3JkX2NodW5rGAYgASgMEhwKFGVuY29kZWRfcmVjb3JkX2J5dGVzGAcgASgEEg4KBnNoYTI1NhgIIAEoCSLcAgoOQ2hhdEluZ3Jlc3NBY2sSDgoGcnVuX2lkGAEgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRIZChFjb21taXR0ZWRfdGhyb3VnaBgDIAEoBBIVCg1leHBlY3RlZF9uZXh0GAQgASgEEjsKBmFjdGlvbhgFIAEoDjIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzQWNrLkFjdGlvbhIaChJ0ZXJtaW5hbF9jb21taXR0ZWQYBiABKAgSEgoKZXJyb3JfY29kZRgHIAEoCRIVCg1lcnJvcl9tZXNzYWdlGAggASgJImsKBkFjdGlvbhIWChJBQ1RJT05fVU5TUEVDSUZJRUQQABIMCghDT05USU5VRRABEhgKFFJFUExBWV9GUk9NX0VYUEVDVEVEEAISEwoPU0VORF9DSEVDS1BPSU5UEAMSDAoIUkVKRUNURUQQBCI8ChRIaXN0b3J5U2V0Q3dkUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSCwoDY3dkGAIgASgJIlgKFUhpc3RvcnlTZXRDd2RSZXNwb25zZRI/Cgxjb252ZXJzYXRpb24YASABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Db252ZXJzYXRpb25TdW1tYXJ5ImYKF1dvcmtzcGFjZVJvb3RHcmFudERyYWZ0Eg8KAmlkGAEgASgJSACIAQESDQoFYWxpYXMYAiABKAkSFAoMZGlzcGxheV9wYXRoGAMgASgJEg4KBmFjY2VzcxgEIAEoCUIFCgNfaWQi0gEKEldvcmtzcGFjZVJvb3RHcmFudBIKCgJpZBgBIAEoCRISCgpwcm9qZWN0X2lkGAIgASgJEhgKEHByb2plY3RfcGF0aF9rZXkYAyABKAkSDQoFYWxpYXMYBCABKAkSFAoMZGlzcGxheV9wYXRoGAUgASgJEhYKDmNhbm9uaWNhbF9wYXRoGAYgASgJEg4KBmFjY2VzcxgHIAEoCRINCgVzdGF0ZRgIIAEoCRISCgpjcmVhdGVkX2F0GAkgASgDEhIKCnVwZGF0ZWRfYXQYCiABKAMilQEKGldvcmtzcGFjZVJvb3RHcmFudHNSZXF1ZXN0Eg4KBmFjdGlvbhgBIAEoCRISCgpwcm9qZWN0X2lkGAIgASgJEhQKDHByb2plY3RfcGF0aBgDIAEoCRI9CgZncmFudHMYBCADKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Xb3Jrc3BhY2VSb290R3JhbnREcmFmdCJXChtXb3Jrc3BhY2VSb290R3JhbnRzUmVzcG9uc2USOAoGZ3JhbnRzGAEgAygLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuV29ya3NwYWNlUm9vdEdyYW50IjwKF0NoZWNrcG9pbnRFeHBlY3RlZEVudHJ5EgsKA2tleRgBIAEoCRIUCgxjdXJyZW50X2hhc2gYAiABKAkiqQEKEUNoZWNrcG9pbnRSZXF1ZXN0Eg4KBmFjdGlvbhgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSEAoIdHVybl9zZXEYAyABKAQSGAoQYXV0aG9yaXplZF9yb290cxgEIAMoCRI/CghleHBlY3RlZBgFIAMoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoZWNrcG9pbnRFeHBlY3RlZEVudHJ5IjkKEkNoZWNrcG9pbnRSZXNwb25zZRIOCgZhY3Rpb24YASABKAkSEwoLcmVzdWx0X2pzb24YAiABKAkiVwoZQ2hhdENvbnZlcnNhdGlvblJlZmVyZW5jZRIKCgJpZBgBIAEoCRINCgV0aXRsZRgCIAEoCRILCgNjd2QYAyABKAkSEgoKdXBkYXRlZF9hdBgEIAEoAyrlAQoYSW1wb3J0RGlyZWN0b3J5T3BlcmF0aW9uEioKJklNUE9SVF9ESVJFQ1RPUllfT1BFUkFUSU9OX1VOU1BFQ0lGSUVEEAASJAogSU1QT1JUX0RJUkVDVE9SWV9PUEVSQVRJT05fU1RBUlQQARIqCiZJTVBPUlRfRElSRUNUT1JZX09QRVJBVElPTl9XUklURV9DSFVOSxACEiUKIUlNUE9SVF9ESVJFQ1RPUllfT1BFUkFUSU9OX0NPTU1JVBADEiQKIElNUE9SVF9ESVJFQ1RPUllfT1BFUkFUSU9OX0FCT1JUEAQqxgQKD1R1bm5lbEZyYW1lS2luZBIhCh1UVU5ORUxfRlJBTUVfS0lORF9VTlNQRUNJRklFRBAAEigKJFRVTk5FTF9GUkFNRV9LSU5EX0hUVFBfUkVRVUVTVF9TVEFSVBABEicKI1RVTk5FTF9GUkFNRV9LSU5EX0hUVFBfUkVRVUVTVF9CT0RZEAISJgoiVFVOTkVMX0ZSQU1FX0tJTkRfSFRUUF9SRVFVRVNUX0VORBADEikKJVRVTk5FTF9GUkFNRV9LSU5EX0hUVFBfUkVTUE9OU0VfU1RBUlQQBBIoCiRUVU5ORUxfRlJBTUVfS0lORF9IVFRQX1JFU1BPTlNFX0JPRFkQBRInCiNUVU5ORUxfRlJBTUVfS0lORF9IVFRQX1JFU1BPTlNFX0VORBAGEh0KGVRVTk5FTF9GUkFNRV9LSU5EX1dTX0RJQUwQBxIgChxUVU5ORUxfRlJBTUVfS0lORF9XU19ESUFMX09LEAgSIwofVFVOTkVMX0ZSQU1FX0tJTkRfV1NfRElBTF9FUlJPUhAJEh4KGlRVTk5FTF9GUkFNRV9LSU5EX1dTX0ZSQU1FEAoSHgoaVFVOTkVMX0ZSQU1FX0tJTkRfV1NfQ0xPU0UQCxIbChdUVU5ORUxfRlJBTUVfS0lORF9FUlJPUhAMEhwKGFRVTk5FTF9GUkFNRV9LSU5EX0NBTkNFTBANEhoKFlRVTk5FTF9GUkFNRV9LSU5EX1BJTkcQDhIaChZUVU5ORUxfRlJBTUVfS0lORF9QT05HEA8qgQEKE1R1bm5lbFdzTWVzc2FnZVR5cGUSJgoiVFVOTkVMX1dTX01FU1NBR0VfVFlQRV9VTlNQRUNJRklFRBAAEh8KG1RVTk5FTF9XU19NRVNTQUdFX1RZUEVfVEVYVBABEiEKHVRVTk5FTF9XU19NRVNTQUdFX1RZUEVfQklOQVJZEAJCQFo+Z2l0aHViLmNvbS9saXZlYWdlbnQvYWdlbnQtZ2F0ZXdheS9pbnRlcm5hbC9wcm90by92MjtnYXRld2F5djJiBnByb3RvMw"); /** * @generated from message liveagent.gateway.v2.GatewayEnvelope @@ -5952,6 +5952,49 @@ export type ErrorResponse = Message<"liveagent.gateway.v2.ErrorResponse"> & { export const ErrorResponseSchema: GenMessage = /*@__PURE__*/ messageDesc(file_proto_v2_gateway, 149); +/** + * @generated from message liveagent.gateway.v2.ProviderCustomHeader + */ +export type ProviderCustomHeader = Message<"liveagent.gateway.v2.ProviderCustomHeader"> & { + /** + * @generated from field: string name = 1; + */ + name: string; + + /** + * @generated from field: string value = 2; + */ + value: string; +}; + +/** + * Describes the message liveagent.gateway.v2.ProviderCustomHeader. + * Use `create(ProviderCustomHeaderSchema)` to create a new message. + */ +export const ProviderCustomHeaderSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_proto_v2_gateway, 150); + +/** + * 包一层 message 只为拿到字段存在性:repeated 无法区分「草稿没带」与「草稿把头 + * 清空了」,而这两种情况在已保存供应商分支上的处理相反(前者沿用落库的头, + * 后者按空集发请求)。与 is_full_url 的 optional 同一套三态语义。 + * + * @generated from message liveagent.gateway.v2.ProviderCustomHeaders + */ +export type ProviderCustomHeaders = Message<"liveagent.gateway.v2.ProviderCustomHeaders"> & { + /** + * @generated from field: repeated liveagent.gateway.v2.ProviderCustomHeader headers = 1; + */ + headers: ProviderCustomHeader[]; +}; + +/** + * Describes the message liveagent.gateway.v2.ProviderCustomHeaders. + * Use `create(ProviderCustomHeadersSchema)` to create a new message. + */ +export const ProviderCustomHeadersSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_proto_v2_gateway, 151); + /** * @generated from message liveagent.gateway.v2.ProviderModelsRequest */ @@ -5996,6 +6039,14 @@ export type ProviderModelsRequest = Message<"liveagent.gateway.v2.ProviderModels * @generated from field: optional bool is_full_url = 7; */ isFullUrl?: boolean | undefined; + + /** + * 用户在供应商设置里显式配置的自定义请求头;未提供时沿用已保存配置。 + * 鉴权头与 host/content-length 等仍由落地侧的保留头名单兜底,不可被覆盖。 + * + * @generated from field: liveagent.gateway.v2.ProviderCustomHeaders custom_headers = 8; + */ + customHeaders?: ProviderCustomHeaders | undefined; }; /** @@ -6003,7 +6054,7 @@ export type ProviderModelsRequest = Message<"liveagent.gateway.v2.ProviderModels * Use `create(ProviderModelsRequestSchema)` to create a new message. */ export const ProviderModelsRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 150); + messageDesc(file_proto_v2_gateway, 152); /** * @generated from message liveagent.gateway.v2.ProviderModelsResponse @@ -6020,7 +6071,7 @@ export type ProviderModelsResponse = Message<"liveagent.gateway.v2.ProviderModel * Use `create(ProviderModelsResponseSchema)` to create a new message. */ export const ProviderModelsResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 151); + messageDesc(file_proto_v2_gateway, 153); /** * @generated from message liveagent.gateway.v2.ProviderUsageRequest @@ -6050,7 +6101,7 @@ export type ProviderUsageRequest = Message<"liveagent.gateway.v2.ProviderUsageRe * Use `create(ProviderUsageRequestSchema)` to create a new message. */ export const ProviderUsageRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 152); + messageDesc(file_proto_v2_gateway, 154); /** * @generated from message liveagent.gateway.v2.ProviderUsageResponse @@ -6067,7 +6118,7 @@ export type ProviderUsageResponse = Message<"liveagent.gateway.v2.ProviderUsageR * Use `create(ProviderUsageResponseSchema)` to create a new message. */ export const ProviderUsageResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 153); + messageDesc(file_proto_v2_gateway, 155); /** * ChatIngressBatch carries contiguous logical records for one run. Each record @@ -6102,7 +6153,7 @@ export type ChatIngressBatch = Message<"liveagent.gateway.v2.ChatIngressBatch"> * Use `create(ChatIngressBatchSchema)` to create a new message. */ export const ChatIngressBatchSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 154); + messageDesc(file_proto_v2_gateway, 156); /** * @generated from message liveagent.gateway.v2.ChatIngressRecord @@ -6143,7 +6194,7 @@ export type ChatIngressRecord = Message<"liveagent.gateway.v2.ChatIngressRecord" * Use `create(ChatIngressRecordSchema)` to create a new message. */ export const ChatIngressRecordSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 155); + messageDesc(file_proto_v2_gateway, 157); /** * @generated from message liveagent.gateway.v2.ChatIngressDelta @@ -6165,7 +6216,7 @@ export type ChatIngressDelta = Message<"liveagent.gateway.v2.ChatIngressDelta"> * Use `create(ChatIngressDeltaSchema)` to create a new message. */ export const ChatIngressDeltaSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 156); + messageDesc(file_proto_v2_gateway, 158); /** * @generated from message liveagent.gateway.v2.ChatIngressHeartbeat @@ -6184,7 +6235,7 @@ export type ChatIngressHeartbeat = Message<"liveagent.gateway.v2.ChatIngressHear * Use `create(ChatIngressHeartbeatSchema)` to create a new message. */ export const ChatIngressHeartbeatSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 157); + messageDesc(file_proto_v2_gateway, 159); /** * @generated from message liveagent.gateway.v2.ChatIngressCheckpoint @@ -6231,7 +6282,7 @@ export type ChatIngressCheckpoint = Message<"liveagent.gateway.v2.ChatIngressChe * Use `create(ChatIngressCheckpointSchema)` to create a new message. */ export const ChatIngressCheckpointSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 158); + messageDesc(file_proto_v2_gateway, 160); /** * @generated from message liveagent.gateway.v2.ChatIngressTerminal @@ -6293,7 +6344,7 @@ export type ChatIngressTerminal = Message<"liveagent.gateway.v2.ChatIngressTermi * Use `create(ChatIngressTerminalSchema)` to create a new message. */ export const ChatIngressTerminalSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 159); + messageDesc(file_proto_v2_gateway, 161); /** * ChatIngressResume declares the replay window retained by the desktop after a @@ -6313,7 +6364,7 @@ export type ChatIngressResume = Message<"liveagent.gateway.v2.ChatIngressResume" * Use `create(ChatIngressResumeSchema)` to create a new message. */ export const ChatIngressResumeSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 160); + messageDesc(file_proto_v2_gateway, 162); /** * @generated from message liveagent.gateway.v2.ChatIngressRunResume @@ -6365,7 +6416,7 @@ export type ChatIngressRunResume = Message<"liveagent.gateway.v2.ChatIngressRunR * Use `create(ChatIngressRunResumeSchema)` to create a new message. */ export const ChatIngressRunResumeSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 161); + messageDesc(file_proto_v2_gateway, 163); /** * ChatIngressFragment transports one encoded ChatIngressRecord that exceeds a @@ -6421,7 +6472,7 @@ export type ChatIngressFragment = Message<"liveagent.gateway.v2.ChatIngressFragm * Use `create(ChatIngressFragmentSchema)` to create a new message. */ export const ChatIngressFragmentSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 162); + messageDesc(file_proto_v2_gateway, 164); /** * @generated from message liveagent.gateway.v2.ChatIngressAck @@ -6473,7 +6524,7 @@ export type ChatIngressAck = Message<"liveagent.gateway.v2.ChatIngressAck"> & { * Use `create(ChatIngressAckSchema)` to create a new message. */ export const ChatIngressAckSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 163); + messageDesc(file_proto_v2_gateway, 165); /** * @generated from enum liveagent.gateway.v2.ChatIngressAck.Action @@ -6509,7 +6560,7 @@ export enum ChatIngressAck_Action { * Describes the enum liveagent.gateway.v2.ChatIngressAck.Action. */ export const ChatIngressAck_ActionSchema: GenEnum = /*@__PURE__*/ - enumDesc(file_proto_v2_gateway, 163, 0); + enumDesc(file_proto_v2_gateway, 165, 0); /** * @generated from message liveagent.gateway.v2.HistorySetCwdRequest @@ -6531,7 +6582,7 @@ export type HistorySetCwdRequest = Message<"liveagent.gateway.v2.HistorySetCwdRe * Use `create(HistorySetCwdRequestSchema)` to create a new message. */ export const HistorySetCwdRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 164); + messageDesc(file_proto_v2_gateway, 166); /** * @generated from message liveagent.gateway.v2.HistorySetCwdResponse @@ -6548,7 +6599,7 @@ export type HistorySetCwdResponse = Message<"liveagent.gateway.v2.HistorySetCwdR * Use `create(HistorySetCwdResponseSchema)` to create a new message. */ export const HistorySetCwdResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 165); + messageDesc(file_proto_v2_gateway, 167); /** * @generated from message liveagent.gateway.v2.WorkspaceRootGrantDraft @@ -6580,7 +6631,7 @@ export type WorkspaceRootGrantDraft = Message<"liveagent.gateway.v2.WorkspaceRoo * Use `create(WorkspaceRootGrantDraftSchema)` to create a new message. */ export const WorkspaceRootGrantDraftSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 166); + messageDesc(file_proto_v2_gateway, 168); /** * @generated from message liveagent.gateway.v2.WorkspaceRootGrant @@ -6642,7 +6693,7 @@ export type WorkspaceRootGrant = Message<"liveagent.gateway.v2.WorkspaceRootGran * Use `create(WorkspaceRootGrantSchema)` to create a new message. */ export const WorkspaceRootGrantSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 167); + messageDesc(file_proto_v2_gateway, 169); /** * @generated from message liveagent.gateway.v2.WorkspaceRootGrantsRequest @@ -6674,7 +6725,7 @@ export type WorkspaceRootGrantsRequest = Message<"liveagent.gateway.v2.Workspace * Use `create(WorkspaceRootGrantsRequestSchema)` to create a new message. */ export const WorkspaceRootGrantsRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 168); + messageDesc(file_proto_v2_gateway, 170); /** * @generated from message liveagent.gateway.v2.WorkspaceRootGrantsResponse @@ -6691,7 +6742,7 @@ export type WorkspaceRootGrantsResponse = Message<"liveagent.gateway.v2.Workspac * Use `create(WorkspaceRootGrantsResponseSchema)` to create a new message. */ export const WorkspaceRootGrantsResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 169); + messageDesc(file_proto_v2_gateway, 171); /** * @generated from message liveagent.gateway.v2.CheckpointExpectedEntry @@ -6713,7 +6764,7 @@ export type CheckpointExpectedEntry = Message<"liveagent.gateway.v2.CheckpointEx * Use `create(CheckpointExpectedEntrySchema)` to create a new message. */ export const CheckpointExpectedEntrySchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 170); + messageDesc(file_proto_v2_gateway, 172); /** * @generated from message liveagent.gateway.v2.CheckpointRequest @@ -6752,7 +6803,7 @@ export type CheckpointRequest = Message<"liveagent.gateway.v2.CheckpointRequest" * Use `create(CheckpointRequestSchema)` to create a new message. */ export const CheckpointRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 171); + messageDesc(file_proto_v2_gateway, 173); /** * @generated from message liveagent.gateway.v2.CheckpointResponse @@ -6774,7 +6825,7 @@ export type CheckpointResponse = Message<"liveagent.gateway.v2.CheckpointRespons * Use `create(CheckpointResponseSchema)` to create a new message. */ export const CheckpointResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 172); + messageDesc(file_proto_v2_gateway, 174); /** * @generated from message liveagent.gateway.v2.ChatConversationReference @@ -6806,7 +6857,7 @@ export type ChatConversationReference = Message<"liveagent.gateway.v2.ChatConver * Use `create(ChatConversationReferenceSchema)` to create a new message. */ export const ChatConversationReferenceSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_proto_v2_gateway, 173); + messageDesc(file_proto_v2_gateway, 175); /** * @generated from enum liveagent.gateway.v2.ImportDirectoryOperation diff --git a/crates/agent-gateway/web/src/shims/tauriCore.ts b/crates/agent-gateway/web/src/shims/tauriCore.ts index 6ee38ab44..a483706a2 100644 --- a/crates/agent-gateway/web/src/shims/tauriCore.ts +++ b/crates/agent-gateway/web/src/shims/tauriCore.ts @@ -285,6 +285,7 @@ export async function invoke(command: string, args?: Record) String(args?.models_url ?? ""), String(args?.provider_id ?? ""), typeof args?.is_full_url === "boolean" ? args.is_full_url : undefined, + Array.isArray(args?.custom_headers) ? args.custom_headers : undefined, )) as T; case "settings_reset_ssh_known_host": { const host = String(args?.host ?? "").trim(); diff --git a/crates/agent-gui/src-tauri/src/services/gateway_bridge.rs b/crates/agent-gui/src-tauri/src/services/gateway_bridge.rs index a8ee7c772..36c1c0100 100644 --- a/crates/agent-gui/src-tauri/src/services/gateway_bridge.rs +++ b/crates/agent-gui/src-tauri/src/services/gateway_bridge.rs @@ -511,16 +511,27 @@ pub async fn handle_provider_models( ) -> Result { let provider_type = request.provider_type.trim().to_string(); let request_api_key = request.api_key.trim().to_string(); + // message 字段带存在性:未设置=草稿没带头,沿用落库配置;设置了(哪怕是空 + // 列表)=草稿的头就是权威值。 + let request_custom_headers = request.custom_headers.as_ref().map(|headers| { + headers + .headers + .iter() + .map(|header| (header.name.clone(), header.value.clone())) + .collect::>() + }); let config = if request_api_key.is_empty() { let provider_id = request.provider_id.trim().to_string(); let expected_provider_type = provider_type.clone(); let is_full_url = request.is_full_url; + let custom_headers = request_custom_headers.clone(); tauri::async_runtime::spawn_blocking(move || { let conn = open_db()?; resolve_stored_provider_models_config( &provider_id, &expected_provider_type, is_full_url, + custom_headers, load_providers(&conn)?, ) }) @@ -535,6 +546,7 @@ pub async fn handle_provider_models( models_url: Some(request.models_url.trim().to_string()) .filter(|value| !value.is_empty()), is_full_url: request.is_full_url.unwrap_or(false), + custom_headers: request_custom_headers.unwrap_or_default(), } }; let models_json = crate::services::provider_models::fetch_provider_models( @@ -544,6 +556,7 @@ pub async fn handle_provider_models( config.use_system_proxy, config.models_url.as_deref(), config.is_full_url, + &config.custom_headers, ) .await?; Ok(proto::ProviderModelsResponse { models_json }) @@ -557,12 +570,14 @@ struct ProviderModelsRequestConfig { use_system_proxy: bool, models_url: Option, is_full_url: bool, + custom_headers: Vec<(String, String)>, } fn resolve_stored_provider_models_config( provider_id: &str, expected_provider_type: &str, is_full_url: Option, + custom_headers: Option>, providers: Option, ) -> Result { let provider_id = provider_id.trim(); @@ -618,6 +633,22 @@ fn resolve_stored_provider_models_config( .and_then(Value::as_bool) .unwrap_or(false) }), + custom_headers: custom_headers.unwrap_or_else(|| { + provider + .get("customHeaders") + .and_then(Value::as_array) + .map(|headers| { + headers + .iter() + .filter_map(|header| { + let key = header.get("key").and_then(Value::as_str)?.trim(); + let value = header.get("value").and_then(Value::as_str)?; + (!key.is_empty()).then(|| (key.to_string(), value.to_string())) + }) + .collect() + }) + .unwrap_or_default() + }), }) } @@ -1915,8 +1946,14 @@ mod tests { "useSystemProxy": true }]); assert_eq!( - resolve_stored_provider_models_config("provider-a", "codex", None, Some(providers)) - .expect("stored provider config"), + resolve_stored_provider_models_config( + "provider-a", + "codex", + None, + None, + Some(providers), + ) + .expect("stored provider config"), super::ProviderModelsRequestConfig { provider_type: "codex".to_string(), base_url: "https://stored.example.com/v1/responses".to_string(), @@ -1924,10 +1961,61 @@ mod tests { use_system_proxy: true, models_url: Some("https://stored.example.com/models".to_string()), is_full_url: true, + custom_headers: Vec::new(), } ); } + #[test] + fn provider_models_custom_headers_fall_back_to_stored_only_when_draft_omits_them() { + let providers = json!([{ + "id": "provider-a", + "type": "codex", + "baseUrl": "https://stored.example.com", + "apiKey": "stored-secret", + "customHeaders": [{ "key": "User-Agent", "value": "stored-cli/1.0" }] + }]); + + // 草稿没带请求头(proto 的 custom_headers 缺省)→ 沿用落库配置。 + let inherited = resolve_stored_provider_models_config( + "provider-a", + "codex", + None, + None, + Some(providers.clone()), + ) + .expect("stored provider config"); + assert_eq!( + inherited.custom_headers, + vec![("User-Agent".to_string(), "stored-cli/1.0".to_string())] + ); + + // 草稿把请求头清空了 → 按空集发,绝不回落到落库配置(否则用户删不掉伪装头)。 + let cleared = resolve_stored_provider_models_config( + "provider-a", + "codex", + None, + Some(Vec::new()), + Some(providers.clone()), + ) + .expect("stored provider config"); + assert!(cleared.custom_headers.is_empty()); + + // 草稿显式给了头 → 覆盖落库配置。 + let overridden = resolve_stored_provider_models_config( + "provider-a", + "codex", + None, + Some(vec![("User-Agent".to_string(), "draft-cli/2.0".to_string())]), + Some(providers), + ) + .expect("stored provider config"); + assert_eq!( + overridden.custom_headers, + vec![("User-Agent".to_string(), "draft-cli/2.0".to_string())] + ); + } + #[test] fn provider_models_applies_webui_full_url_mode_to_stored_endpoint() { let providers = json!([{ @@ -1941,6 +2029,7 @@ mod tests { "provider-a", "codex", Some(true), + None, Some(providers), ) .expect("stored provider config with draft full URL mode"); @@ -1958,8 +2047,14 @@ mod tests { "apiKey": "stored-secret" }]); assert_eq!( - resolve_stored_provider_models_config("provider-a", "codex", None, Some(providers)) - .expect_err("provider type mismatch"), + resolve_stored_provider_models_config( + "provider-a", + "codex", + None, + None, + Some(providers), + ) + .expect_err("provider type mismatch"), "供应商类型与已保存配置不匹配" ); } diff --git a/crates/agent-gui/src-tauri/src/services/provider_models.rs b/crates/agent-gui/src-tauri/src/services/provider_models.rs index 09b3835aa..fd4103816 100644 --- a/crates/agent-gui/src-tauri/src/services/provider_models.rs +++ b/crates/agent-gui/src-tauri/src/services/provider_models.rs @@ -13,7 +13,7 @@ const CODEX_MODELS_SUFFIXES: [&str; 3] = ["/chat/completions", "/responses", "/r #[derive(Clone, Debug)] struct ProviderModelsAttempt { url: Url, - headers: Vec<(&'static str, String)>, + headers: Vec<(String, String)>, } #[derive(Debug)] @@ -29,6 +29,7 @@ pub async fn fetch_provider_models( use_system_proxy: bool, models_url: Option<&str>, is_full_url: bool, + custom_headers: &[(String, String)], ) -> Result { // 与本地反代的 x-liveagent-use-system-proxy 语义一致:勾选时代理配置异常 // fail fast,绝不静默降级;未勾选一律直连(忽略环境代理)。 @@ -47,6 +48,7 @@ pub async fn fetch_provider_models( api_key, models_url, is_full_url, + custom_headers, ), ) .await @@ -71,6 +73,7 @@ async fn fetch_provider_models_with_client( api_key: &str, models_url: Option<&str>, is_full_url: bool, + custom_headers: &[(String, String)], ) -> Result { let attempts = build_provider_models_attempts_with_override( provider_type, @@ -78,6 +81,7 @@ async fn fetch_provider_models_with_client( api_key, models_url, is_full_url, + custom_headers, )?; let mut failures = Vec::new(); let mut empty_result = None; @@ -271,7 +275,7 @@ fn build_provider_models_attempts( base_url: &str, api_key: &str, ) -> Result, String> { - build_provider_models_attempts_with_override(provider_type, base_url, api_key, None, false) + build_provider_models_attempts_with_override(provider_type, base_url, api_key, None, false, &[]) } fn build_provider_models_attempts_with_override( @@ -280,6 +284,7 @@ fn build_provider_models_attempts_with_override( api_key: &str, models_url: Option<&str>, is_full_url: bool, + custom_headers: &[(String, String)], ) -> Result, String> { validate_provider_type(provider_type)?; let explicit_url = if provider_type == "gemini" { @@ -301,7 +306,10 @@ fn build_provider_models_attempts_with_override( .as_ref() .cloned() .unwrap_or_else(|| build_provider_models_url(provider_type, &base_url, official)), - headers: build_provider_models_headers(provider_type, api_key, official), + headers: merge_custom_headers( + build_provider_models_headers(provider_type, api_key, official), + custom_headers, + ), }); // codex/xai/deepseek 的官方形式与统一首次尝试完全一致,重复请求同一端点没有意义,收敛为一次。 let mut attempts = vec![default_attempt]; @@ -361,6 +369,66 @@ fn build_provider_models_headers( headers } +// KEEP IN SYNC: crates/agent-ui/src/lib/providers/customHeaders.ts 的 +// RESERVED_CUSTOM_HEADER_KEYS / RESERVED_CUSTOM_HEADER_KEY_PREFIX。鉴权头与 +// host/content-length 属保留头:用户改不了,自定义头也不得顶掉它们。 +const RESERVED_CUSTOM_HEADER_KEYS: [&str; 6] = [ + "authorization", + "x-api-key", + "x-goog-api-key", + "anthropic-beta", + "host", + "content-length", +]; +const RESERVED_CUSTOM_HEADER_KEY_PREFIX: &str = "x-liveagent-"; + +fn is_valid_custom_header_key(key: &str) -> bool { + !key.is_empty() + && key.chars().all(|character| { + character.is_ascii_alphanumeric() + || "!#$%&'*+-.^_`|~".contains(character) + }) +} + +// 取值只允许可见 ASCII 与水平制表符:CR/LF 会造成 header 注入。 +fn is_valid_custom_header_value(value: &str) -> bool { + value + .chars() + .all(|character| character == '\t' || ('\x20'..='\x7e').contains(&character)) +} + +fn is_reserved_custom_header_key(key: &str) -> bool { + let normalized = key.to_ascii_lowercase(); + RESERVED_CUSTOM_HEADER_KEYS.contains(&normalized.as_str()) + || normalized.starts_with(RESERVED_CUSTOM_HEADER_KEY_PREFIX) +} + +/// 把用户显式配置的自定义请求头并入自动装配的头集合。非法键名/取值与保留头 +/// 直接丢弃(与前端 mergeCustomHeaders 同语义),同名头大小写不敏感地覆盖。 +fn merge_custom_headers( + base: Vec<(&'static str, String)>, + custom_headers: &[(String, String)], +) -> Vec<(String, String)> { + let mut merged: Vec<(String, String)> = base + .into_iter() + .map(|(name, value)| (name.to_string(), value)) + .collect(); + + for (key, value) in custom_headers { + if !is_valid_custom_header_key(key) + || !is_valid_custom_header_value(value) + || is_reserved_custom_header_key(key) + { + continue; + } + let normalized = key.to_ascii_lowercase(); + merged.retain(|(name, _)| name.to_ascii_lowercase() != normalized); + merged.push((key.clone(), value.clone())); + } + + merged +} + fn provider_models_payload_has_entries(payload: &Value) -> bool { match payload { Value::Array(items) => !items.is_empty(), @@ -472,6 +540,7 @@ mod tests { "key", None, true, + &[], ) .expect("full URL attempts"); @@ -504,6 +573,7 @@ mod tests { "key", Some("https://models.example.com/catalog?api-version=2026-01"), false, + &[], ) .expect("models URL override attempts"); @@ -517,6 +587,7 @@ mod tests { "key", Some("https://user:pass@example.com/models"), false, + &[], ) .is_err()); @@ -526,6 +597,7 @@ mod tests { "key", Some("https://ignored.example.com/custom/models"), false, + &[], ) .expect("gemini keeps automatic model discovery"); assert_eq!( @@ -535,7 +607,7 @@ mod tests { } #[test] - fn provider_model_headers_exclude_inference_identity() { + fn provider_model_headers_never_forge_a_client_identity() { for provider_type in ["claude_code", "codex", "gemini", "xai", "deepseek"] { for official in [false, true] { let headers = build_provider_models_headers(provider_type, "key", official); @@ -544,21 +616,70 @@ mod tests { .map(|(name, _)| name.to_ascii_lowercase()) .collect::>(); + // 不开启伪装就照实发:既不带 UA,也不带 SDK 指纹头。伪装只能由用户 + // 在设置里显式写进自定义请求头,经 merge_custom_headers 落到请求上。 + assert!( + !names.iter().any(|name| name == "user-agent"), + "{provider_type}" + ); assert!(!names.iter().any(|name| name.starts_with("x-stainless-"))); for forbidden in [ "x-app", - "user-agent", "anthropic-beta", "anthropic-dangerous-direct-browser-access", "session_id", "conversation_id", ] { - assert!(!names.iter().any(|name| name == forbidden)); + assert!(!names.iter().any(|name| name == forbidden), "{forbidden}"); } } } } + #[test] + fn provider_model_custom_headers_override_identity_but_not_auth() { + let custom = [ + ("User-Agent".to_string(), "my-relay-client/9.9".to_string()), + ("X-Request-ID".to_string(), "abc123".to_string()), + // 保留头:鉴权与 host/content-length 不可被顶掉。 + ("authorization".to_string(), "Bearer stolen".to_string()), + ("Host".to_string(), "evil.example.com".to_string()), + ("x-liveagent-proxy-token".to_string(), "leak".to_string()), + // 非法键名/取值直接丢弃(CR/LF 会造成 header 注入)。 + ("Bad Key".to_string(), "value".to_string()), + ("X-Inject".to_string(), "a\r\nX-Evil: 1".to_string()), + ]; + let attempts = build_provider_models_attempts_with_override( + "claude_code", + "https://relay.example.com", + "key", + None, + false, + &custom, + ) + .expect("attempts with custom headers"); + + for attempt in &attempts { + let get = |wanted: &str| { + attempt + .headers + .iter() + .find(|(name, _)| name.eq_ignore_ascii_case(wanted)) + .map(|(_, value)| value.clone()) + }; + // UA 只可能来自用户显式配置——这里就是它落到请求上的唯一路径。 + assert_eq!(get("user-agent").as_deref(), Some("my-relay-client/9.9")); + assert_eq!(get("x-request-id").as_deref(), Some("abc123")); + assert_eq!(get("host"), None); + assert_eq!(get("x-liveagent-proxy-token"), None); + assert_eq!(get("bad key"), None); + assert_eq!(get("x-inject"), None); + assert_ne!(get("authorization").as_deref(), Some("Bearer stolen")); + } + // 鉴权头仍恰好一条,自定义头不得让 attempts 分裂成重复请求。 + assert_eq!(attempts.len(), 2); + } + #[test] fn provider_model_headers_use_authorization_then_official_auth() { for (provider_type, official_expected) in [ @@ -627,6 +748,7 @@ mod tests { "test-key", None, false, + &[], ) .await .expect("fetch provider models"); diff --git a/crates/agent-gui/src/agent-ui-adapters/providerSettings.tsx b/crates/agent-gui/src/agent-ui-adapters/providerSettings.tsx index 523e94aae..da7ddb494 100644 --- a/crates/agent-gui/src/agent-ui-adapters/providerSettings.tsx +++ b/crates/agent-gui/src/agent-ui-adapters/providerSettings.tsx @@ -442,6 +442,7 @@ export function ProviderSettingsExtension(props: { useSystemProxy: provider.useSystemProxy, isFullUrl: provider.isFullUrl, modelsUrl: provider.modelsUrl, + customHeaders: provider.customHeaders, }, ); return { id: provider.id, models, ok: true }; diff --git a/crates/agent-gui/test/providers/request-options.test.mjs b/crates/agent-gui/test/providers/request-options.test.mjs index 3c8ee509a..73e2ab3d5 100644 --- a/crates/agent-gui/test/providers/request-options.test.mjs +++ b/crates/agent-gui/test/providers/request-options.test.mjs @@ -139,8 +139,8 @@ test("provider request helpers normalize auth, metadata, errors, and model value "X-Stainless-Runtime": "node", "X-Stainless-Timeout": "600", "x-stainless-retry-count": "0", - "X-Stainless-Package-Version": "0.74.0", - "X-Stainless-Runtime-Version": "v22.19.0", + "X-Stainless-Package-Version": "0.94.0", + "X-Stainless-Runtime-Version": "v26.3.0", "anthropic-dangerous-direct-browser-access": "true", }, ); diff --git a/crates/agent-gui/test/settings/provider-models-fetch.test.mjs b/crates/agent-gui/test/settings/provider-models-fetch.test.mjs index 82232fb7c..b050c407a 100644 --- a/crates/agent-gui/test/settings/provider-models-fetch.test.mjs +++ b/crates/agent-gui/test/settings/provider-models-fetch.test.mjs @@ -130,23 +130,45 @@ test("buildProviderModelsAttempts uses Authorization first and official auth sec assert.equal(attemptsFor.claude_code[1].headers["anthropic-version"], "2023-06-01"); assert.equal(attemptsFor.gemini[1].headers["x-goog-api-key"], "test-key"); - const inferenceOnlyHeaders = [ - "x-app", + // 拉模型列表不带任何客户端身份:不开启伪装就照实发。SDK 指纹头更是推理请求才有 + // 的东西,带上它们等于谎报了一次没发生的 SDK 调用,上游据此做限流/统计会被污染。 + const neverAutoInjected = [ "user-agent", + "x-app", "anthropic-beta", "anthropic-dangerous-direct-browser-access", "session_id", "conversation_id", ]; - for (const [, attempts] of attemptsByProvider) { + for (const [type, attempts] of attemptsByProvider) { for (const attempt of attempts) { const headerNames = Object.keys(attempt.headers).map((name) => name.toLowerCase()); assert.ok(!headerNames.some((name) => name.startsWith("x-stainless-"))); - for (const name of inferenceOnlyHeaders) assert.ok(!headerNames.includes(name), name); + for (const name of neverAutoInjected) { + assert.ok(!headerNames.includes(name), `${type}:${name}`); + } } } }); +test("custom headers reach the model-list request, and never override auth", () => { + // 不点「模拟 CLI」就没有任何身份头——伪装只能来自用户显式配置。 + const [bare] = providerUtils.buildProviderModelsAttempts("claude_code", "test-key"); + assert.ok(!Object.keys(bare.headers).some((name) => name.toLowerCase() === "user-agent")); + + const [overridden] = providerUtils.buildProviderModelsAttempts("claude_code", "test-key", [ + { key: "user-agent", value: "my-relay/1.0" }, + { key: "X-Trace", value: "abc" }, + // 保留头:鉴权头顶不掉,非法取值(CR/LF 注入)整条丢弃。 + { key: "Authorization", value: "Bearer stolen" }, + { key: "X-Bad", value: "line\r\nInjected: 1" }, + ]); + assert.equal(overridden.headers["user-agent"], "my-relay/1.0"); + assert.equal(overridden.headers["X-Trace"], "abc"); + assert.equal(overridden.headers.Authorization, "Bearer test-key"); + assert.equal(overridden.headers["X-Bad"], undefined); +}); + test("provider model fetch identity changes when system proxy routing changes", () => { const direct = providerUtils.buildProviderModelsFetchKey( " https://relay.example.com/v1 ", @@ -439,6 +461,7 @@ test("gateway WebUI forwards proxy and models URL choices to desktop model fetch isFullUrl: true, modelsUrl: "https://catalog.example.com/models?api-version=2026-01", providerId: "provider-codex", + customHeaders: [{ key: "User-Agent", value: "claude-cli/2.1.186 (external, cli)" }], }, ); assert.deepEqual( @@ -454,6 +477,9 @@ test("gateway WebUI forwards proxy and models URL choices to desktop model fetch models_url: "https://catalog.example.com/models?api-version=2026-01", provider_id: "provider-codex", is_full_url: true, + // WebView 的 fetch() 会静默丢掉 User-Agent,Gateway 路径必须把用户配的头 + // 原样交给桌面端去落地,否则改了头也到不了上游。 + custom_headers: [{ key: "User-Agent", value: "claude-cli/2.1.186 (external, cli)" }], }, ]); } finally { diff --git a/crates/agent-ui/src/components/IconSet.tsx b/crates/agent-ui/src/components/IconSet.tsx index db0602875..9c739284e 100644 --- a/crates/agent-ui/src/components/IconSet.tsx +++ b/crates/agent-ui/src/components/IconSet.tsx @@ -48,6 +48,7 @@ import EyeOffSource from "~icons/lucide/eye-off"; import FileSource from "~icons/lucide/file"; import FilePenLineSource from "~icons/lucide/file-pen-line"; import FileTextSource from "~icons/lucide/file-text"; +import FingerprintSource from "~icons/lucide/fingerprint"; import FlameSource from "~icons/lucide/flame"; import FolderSource from "~icons/lucide/folder"; import FolderClosedSource from "~icons/lucide/folder-closed"; @@ -524,6 +525,7 @@ export const EyeOff = createIcon(EyeOffSource); export const File = createIcon(FileSource); export const FilePenLine = createIcon(FilePenLineSource); export const FileText = createIcon(FileTextSource); +export const Fingerprint = createIcon(FingerprintSource); export const Flame = createIcon(FlameSource); export const Folder = createIcon(FolderSource); export const FolderClosed = createIcon(FolderClosedSource); diff --git a/crates/agent-ui/src/i18n/translations/enUSSettings.ts b/crates/agent-ui/src/i18n/translations/enUSSettings.ts index e4b6acdd2..abe341a5d 100644 --- a/crates/agent-ui/src/i18n/translations/enUSSettings.ts +++ b/crates/agent-ui/src/i18n/translations/enUSSettings.ts @@ -463,6 +463,11 @@ export const EN_US_SETTINGS_TRANSLATIONS = { "settings.customHeaderKeyPlaceholder": "Header name", "settings.addCustomHeader": "Add", "settings.importCustomHeaders": "Import", + "settings.cliIdentityHeaders": "Mimic CLI", + "settings.cliIdentityHeadersHint": "Fill in official CLI identity headers", + "settings.cliIdentity.claude_code": "Claude Code", + "settings.cliIdentity.codex": "Codex", + "settings.cliIdentity.xai": "Grok", "settings.customHeaderImportLabel": "JSON / cURL", "settings.customHeaderImportPlaceholder": "Paste JSON or cURL", "settings.cancelCustomHeaderImport": "Cancel", diff --git a/crates/agent-ui/src/i18n/translations/zhCNSettings.ts b/crates/agent-ui/src/i18n/translations/zhCNSettings.ts index ce5d4bb90..fe6999d44 100644 --- a/crates/agent-ui/src/i18n/translations/zhCNSettings.ts +++ b/crates/agent-ui/src/i18n/translations/zhCNSettings.ts @@ -443,6 +443,11 @@ export const ZH_CN_SETTINGS_TRANSLATIONS = { "settings.customHeaderKeyPlaceholder": "请求头名称", "settings.addCustomHeader": "添加", "settings.importCustomHeaders": "导入", + "settings.cliIdentityHeaders": "模拟 CLI", + "settings.cliIdentityHeadersHint": "一键写入官方 CLI 身份请求头", + "settings.cliIdentity.claude_code": "Claude Code", + "settings.cliIdentity.codex": "Codex", + "settings.cliIdentity.xai": "Grok", "settings.customHeaderImportLabel": "JSON / cURL", "settings.customHeaderImportPlaceholder": "粘贴 JSON 或 cURL", "settings.cancelCustomHeaderImport": "取消", diff --git a/crates/agent-ui/src/lib/providers/customHeaders.ts b/crates/agent-ui/src/lib/providers/customHeaders.ts index 356e8000c..33e9acfe5 100644 --- a/crates/agent-ui/src/lib/providers/customHeaders.ts +++ b/crates/agent-ui/src/lib/providers/customHeaders.ts @@ -49,6 +49,12 @@ export class CustomHeaderImportError extends Error { } } +// Claude Code CLI 的 SDK 指纹头,取值与 CPA/CLIProxyAPI 的 claudeDeviceProfile +// 默认档(helps/claude_device_profile.go:22-31)逐字对齐,配套的 UA 见 +// CLI_IDENTITY_USER_AGENTS.claude_code——版本号必须成套,SDK 版本和 UA 里的 CLI +// 版本对不上本身就是破绽。 +// anthropic-beta 不在这里:它按请求内容逐次计算,写死一个值反而失真,因此列进 +// RESERVED_CUSTOM_HEADER_KEYS 由发请求那侧生成。 export const ANTHROPIC_DEFAULT_REQUEST_HEADERS = { "x-app": "cli", "Content-Type": "application/json", @@ -59,8 +65,8 @@ export const ANTHROPIC_DEFAULT_REQUEST_HEADERS = { "X-Stainless-Runtime": "node", "X-Stainless-Timeout": "600", "x-stainless-retry-count": "0", - "X-Stainless-Package-Version": "0.74.0", - "X-Stainless-Runtime-Version": "v22.19.0", + "X-Stainless-Package-Version": "0.94.0", + "X-Stainless-Runtime-Version": "v26.3.0", "anthropic-dangerous-direct-browser-access": "true", } as const; @@ -104,6 +110,42 @@ export function isAnthropicOAuthApiKey(apiKey: string | undefined): boolean { return Boolean(apiKey?.includes("sk-ant-oat")); } +// 「模拟 CLI」按钮写入的身份 UA。claude_code 一项与 CPA/CLIProxyAPI 的 +// defaultClaudeFingerprintUserAgent(helps/claude_device_profile.go:23)逐字一致; +// codex / xai 用当前发行版本号。这些值只在用户点按钮时写进自定义请求头, +// 发请求那侧不含任何内置伪装。 +export const CLI_IDENTITY_USER_AGENTS = { + claude_code: "claude-cli/2.1.186 (external, cli)", + codex: "codex_cli_rs/0.151.0 (Ubuntu 24.4.0; x86_64) WindowsTerminal", + xai: "grok-shell/1.0.13 (linux; x86_64)", +} as const; + +export type CliIdentityProviderId = keyof typeof CLI_IDENTITY_USER_AGENTS; + +export const CLI_IDENTITY_PROVIDER_IDS = Object.keys( + CLI_IDENTITY_USER_AGENTS, +) as CliIdentityProviderId[]; + +export function isCliIdentityProviderId(value: string): value is CliIdentityProviderId { + return Object.hasOwn(CLI_IDENTITY_USER_AGENTS, value); +} + +// 一键模拟按钮写入的整套 CLI 身份头。Content-Type 由发请求那一侧按 body 决定, +// 不进用户可编辑列表;session_id/conversation_id 是每会话随机值,冒充成固定串反而 +// 比不带更可疑,留给用户自己填。 +export function buildCliIdentityHeaders(type: CliIdentityProviderId): CustomHeader[] { + const headers: CustomHeader[] = [ + { key: "User-Agent", value: CLI_IDENTITY_USER_AGENTS[type] }, + ]; + if (type === "claude_code") { + for (const [key, value] of Object.entries(ANTHROPIC_DEFAULT_REQUEST_HEADERS)) { + if (key.toLowerCase() === "content-type") continue; + headers.push({ key, value }); + } + } + return headers; +} + function findHeaderKey( headers: Record, name: string, @@ -337,7 +379,7 @@ export function mergeImportedCustomHeaders( } export function mergeCustomHeaders( base: Record, - customHeaders?: CustomProvider["customHeaders"], + customHeaders?: readonly CustomHeader[], ): Record { const merged = { ...base }; diff --git a/crates/agent-ui/src/pages/settings/ProviderModal.tsx b/crates/agent-ui/src/pages/settings/ProviderModal.tsx index 053d648ab..fb67c59cf 100644 --- a/crates/agent-ui/src/pages/settings/ProviderModal.tsx +++ b/crates/agent-ui/src/pages/settings/ProviderModal.tsx @@ -15,10 +15,15 @@ import { useConfirmDialog } from "@liveagent/ui/components/ui/confirm-dialog"; import { useVerticalListReorder } from "@liveagent/ui/components/ui/useVerticalListReorder"; import { useLocale } from "@liveagent/ui/i18n/index"; import { + buildCliIdentityHeaders, + type CliIdentityProviderId, CustomHeaderImportError, type CustomHeaderImportErrorCode, type CustomHeaderImportIssue, getCustomHeaderKeyPresets, + isReservedCustomHeaderKey, + isValidCustomHeaderKey, + isValidCustomHeaderValue, mergeImportedCustomHeaders, parseCustomHeadersImport, } from "@liveagent/ui/lib/providers/customHeaders"; @@ -138,6 +143,18 @@ function useProviderModalController({ providerType, initialData, onSave, onClose const [customHeaders, setCustomHeaders] = useState(() => (initialData?.customHeaders ?? []).map((header) => ({ ...header })), ); + // 只有真会发出去的头才参与请求与去重 key:半截键名/保留头在 mergeCustomHeaders + // 里本就会被丢掉,让它们触发重新拉取只是白等 900ms 换回同一份结果。 + const effectiveCustomHeaders = useMemo( + () => + customHeaders.filter( + (header) => + isValidCustomHeaderKey(header.key) && + isValidCustomHeaderValue(header.value) && + !isReservedCustomHeaderKey(header.key), + ), + [customHeaders], + ); const [headerImportOpen, setHeaderImportOpen] = useState(false); const [headerImportText, setHeaderImportText] = useState(""); const [headerImportError, setHeaderImportError] = useState(null); @@ -309,6 +326,7 @@ function useProviderModalController({ providerType, initialData, onSave, onClose isFullUrl, modelsUrl, providerId: initialData?.id, + customHeaders: effectiveCustomHeaders, }); const mergedModels = mergeFetchedModels(list, modelsRef.current); commitModelsWithNewRowsRef.current(mergedModels); @@ -318,7 +336,7 @@ function useProviderModalController({ providerType, initialData, onSave, onClose setFetchingModels(false); } }, - [initialData?.id, isFullUrl, modelsUrl, providerType, useSystemProxy], + [effectiveCustomHeaders, initialData?.id, isFullUrl, modelsUrl, providerType, useSystemProxy], ); useEffect(() => { @@ -331,6 +349,7 @@ function useProviderModalController({ providerType, initialData, onSave, onClose useSystemProxy, isFullUrl, trimModelsUrl, + effectiveCustomHeaders, ); if ((!trimUrl && !trimModelsUrl) || !trimKey) return; if (key === prevFetchKey.current) return; @@ -344,7 +363,15 @@ function useProviderModalController({ providerType, initialData, onSave, onClose return () => { if (debounceRef.current) clearTimeout(debounceRef.current); }; - }, [apiKeyForRequest, baseUrl, doFetch, isFullUrl, modelsUrl, useSystemProxy]); + }, [ + apiKeyForRequest, + baseUrl, + doFetch, + effectiveCustomHeaders, + isFullUrl, + modelsUrl, + useSystemProxy, + ]); useEffect(() => { if (!modelOrder) return; @@ -659,6 +686,22 @@ function useProviderModalController({ providerType, initialData, onSave, onClose setHeaderImportError(null); } + // 一键模拟:把选中 CLI 的身份头并入现有列表。走与「导入」同一条合并路径, + // 同名头覆盖、其余保留,用户手工加的业务头不会被这一下清掉。 + function applyCliIdentityHeaders(identity: CliIdentityProviderId) { + const merged = mergeImportedCustomHeaders(customHeaders, buildCliIdentityHeaders(identity)); + setCustomHeaders(merged.headers); + setHeaderSuggest(null); + setHeaderValidationSubmitted(false); + setHeaderImportOpen(false); + setHeaderImportError(null); + setHeaderImportSummary({ + importedCount: merged.importedCount, + overwrittenCount: merged.overwrittenCount, + issues: [], + }); + } + function handleImportCustomHeaders() { setHeaderImportError(null); setHeaderImportSummary(null); @@ -928,6 +971,7 @@ function useProviderModalController({ providerType, initialData, onSave, onClose apiKeyForRequest, apiKeyIsRedactedDisplay, applyHeaderSuggestion, + applyCliIdentityHeaders, applyModelBulkState, baseUrl, canSaveEditingModel, diff --git a/crates/agent-ui/src/pages/settings/ProviderModalView.tsx b/crates/agent-ui/src/pages/settings/ProviderModalView.tsx index ce950971d..bad77f461 100644 --- a/crates/agent-ui/src/pages/settings/ProviderModalView.tsx +++ b/crates/agent-ui/src/pages/settings/ProviderModalView.tsx @@ -12,6 +12,7 @@ import { ExternalLink, Eye, EyeOff, + Fingerprint, Globe, Key, Link2, @@ -36,6 +37,13 @@ import { DialogHeader, DialogTitle, } from "@liveagent/ui/components/ui/dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuTrigger, +} from "@liveagent/ui/components/ui/dropdown-menu"; import { Input } from "@liveagent/ui/components/ui/input"; import { Label } from "@liveagent/ui/components/ui/label"; import { @@ -48,6 +56,10 @@ import { import { Switch } from "@liveagent/ui/components/ui/switch"; import { Textarea } from "@liveagent/ui/components/ui/textarea"; import { cn } from "@liveagent/ui/lib/shared/utils"; +import { + CLI_IDENTITY_PROVIDER_IDS, + CLI_IDENTITY_USER_AGENTS, +} from "@liveagent/ui/lib/providers/customHeaders"; import { applyUsageQueryModePreset, formatTokenCount, @@ -78,6 +90,7 @@ export function ProviderModalView({ viewModel }: { viewModel: ProviderModalViewM apiKeyForRequest, apiKeyIsRedactedDisplay, applyHeaderSuggestion, + applyCliIdentityHeaders, applyModelBulkState, baseUrl, canOverrideModelInputModalities, @@ -1057,6 +1070,40 @@ export function ProviderModalView({ viewModel }: { viewModel: ProviderModalViewM ) : null}
+ + + } + > + + {t("settings.cliIdentityHeaders")} + + + + {t("settings.cliIdentityHeadersHint")} + + {CLI_IDENTITY_PROVIDER_IDS.map((identity) => ( + applyCliIdentityHeaders(identity)} + > + + {t(`settings.cliIdentity.${identity}`)} + + + {CLI_IDENTITY_USER_AGENTS[identity].split(" ")[0]} + + + ))} + +