diff --git a/lib/src/generated/fai/v1/hub.pb.dart b/lib/src/generated/fai/v1/hub.pb.dart index c1f6164..4adc934 100644 --- a/lib/src/generated/fai/v1/hub.pb.dart +++ b/lib/src/generated/fai/v1/hub.pb.dart @@ -777,6 +777,7 @@ class LoggedEvent extends $pb.GeneratedMessage { $core.String? moduleVersion, $fixnum.Int64? durationMs, $core.String? error, + $core.String? detail, }) { final $result = create(); if (eventId != null) { @@ -812,6 +813,9 @@ class LoggedEvent extends $pb.GeneratedMessage { if (error != null) { $result.error = error; } + if (detail != null) { + $result.detail = detail; + } return $result; } LoggedEvent._() : super(); @@ -830,6 +834,7 @@ class LoggedEvent extends $pb.GeneratedMessage { ..aOS(9, _omitFieldNames ? '' : 'moduleVersion') ..aInt64(10, _omitFieldNames ? '' : 'durationMs') ..aOS(11, _omitFieldNames ? '' : 'error') + ..aOS(12, _omitFieldNames ? '' : 'detail') ..hasRequiredFields = false ; @@ -946,6 +951,19 @@ class LoggedEvent extends $pb.GeneratedMessage { $core.bool hasError() => $_has(10); @$pb.TagNumber(11) void clearError() => $_clearField(11); + + /// Free-form JSON-string carrying event-type-specific fields: + /// input/output sizes, model digest, sha256, etc. Empty when + /// the event type has no structured detail. Studio renders + /// this as a code block in the audit drill-down. + @$pb.TagNumber(12) + $core.String get detail => $_getSZ(11); + @$pb.TagNumber(12) + set detail($core.String v) { $_setString(11, v); } + @$pb.TagNumber(12) + $core.bool hasDetail() => $_has(11); + @$pb.TagNumber(12) + void clearDetail() => $_clearField(12); } class SaveFlowRequest extends $pb.GeneratedMessage { @@ -2457,6 +2475,149 @@ class StoreEntry extends $pb.GeneratedMessage { void clearInstalled() => $_clearField(14); } +class ChannelStatusResponse extends $pb.GeneratedMessage { + factory ChannelStatusResponse({ + $core.String? active, + $core.Iterable? channels, + }) { + final $result = create(); + if (active != null) { + $result.active = active; + } + if (channels != null) { + $result.channels.addAll(channels); + } + return $result; + } + ChannelStatusResponse._() : super(); + factory ChannelStatusResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ChannelStatusResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ChannelStatusResponse', package: const $pb.PackageName(_omitMessageNames ? '' : 'fai.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'active') + ..pc(2, _omitFieldNames ? '' : 'channels', $pb.PbFieldType.PM, subBuilder: ChannelEntry.create) + ..hasRequiredFields = false + ; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + ChannelStatusResponse clone() => ChannelStatusResponse()..mergeFromMessage(this); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + ChannelStatusResponse copyWith(void Function(ChannelStatusResponse) updates) => super.copyWith((message) => updates(message as ChannelStatusResponse)) as ChannelStatusResponse; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ChannelStatusResponse create() => ChannelStatusResponse._(); + ChannelStatusResponse createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ChannelStatusResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ChannelStatusResponse? _defaultInstance; + + /// Active channel pointer from `~/.fai/current-channel`. + @$pb.TagNumber(1) + $core.String get active => $_getSZ(0); + @$pb.TagNumber(1) + set active($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasActive() => $_has(0); + @$pb.TagNumber(1) + void clearActive() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList get channels => $_getList(1); +} + +class ChannelEntry extends $pb.GeneratedMessage { + factory ChannelEntry({ + $core.String? name, + $core.int? port, + $core.bool? running, + $core.String? endpoint, + }) { + final $result = create(); + if (name != null) { + $result.name = name; + } + if (port != null) { + $result.port = port; + } + if (running != null) { + $result.running = running; + } + if (endpoint != null) { + $result.endpoint = endpoint; + } + return $result; + } + ChannelEntry._() : super(); + factory ChannelEntry.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ChannelEntry.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ChannelEntry', package: const $pb.PackageName(_omitMessageNames ? '' : 'fai.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'name') + ..a<$core.int>(2, _omitFieldNames ? '' : 'port', $pb.PbFieldType.OU3) + ..aOB(3, _omitFieldNames ? '' : 'running') + ..aOS(4, _omitFieldNames ? '' : 'endpoint') + ..hasRequiredFields = false + ; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + ChannelEntry clone() => ChannelEntry()..mergeFromMessage(this); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + ChannelEntry copyWith(void Function(ChannelEntry) updates) => super.copyWith((message) => updates(message as ChannelEntry)) as ChannelEntry; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ChannelEntry create() => ChannelEntry._(); + ChannelEntry createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ChannelEntry getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ChannelEntry? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasName() => $_has(0); + @$pb.TagNumber(1) + void clearName() => $_clearField(1); + + @$pb.TagNumber(2) + $core.int get port => $_getIZ(1); + @$pb.TagNumber(2) + set port($core.int v) { $_setUnsignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasPort() => $_has(1); + @$pb.TagNumber(2) + void clearPort() => $_clearField(2); + + /// True iff a daemon is currently bound to that channel's port + /// (PID file present + process alive). + @$pb.TagNumber(3) + $core.bool get running => $_getBF(2); + @$pb.TagNumber(3) + set running($core.bool v) { $_setBool(2, v); } + @$pb.TagNumber(3) + $core.bool hasRunning() => $_has(2); + @$pb.TagNumber(3) + void clearRunning() => $_clearField(3); + + /// Endpoint string from `~/.fai/run/.endpoint` when + /// the daemon is up, otherwise empty. + @$pb.TagNumber(4) + $core.String get endpoint => $_getSZ(3); + @$pb.TagNumber(4) + set endpoint($core.String v) { $_setString(3, v); } + @$pb.TagNumber(4) + $core.bool hasEndpoint() => $_has(3); + @$pb.TagNumber(4) + void clearEndpoint() => $_clearField(4); +} + class CheckUpdateResponse extends $pb.GeneratedMessage { factory CheckUpdateResponse({ $core.String? channel, diff --git a/lib/src/generated/fai/v1/hub.pbgrpc.dart b/lib/src/generated/fai/v1/hub.pbgrpc.dart index 6ef0375..a5e541e 100644 --- a/lib/src/generated/fai/v1/hub.pbgrpc.dart +++ b/lib/src/generated/fai/v1/hub.pbgrpc.dart @@ -183,6 +183,10 @@ class HubAdminClient extends $grpc.Client { '/fai.v1.HubAdmin/CheckUpdate', ($1.Empty value) => value.writeToBuffer(), ($core.List<$core.int> value) => $0.CheckUpdateResponse.fromBuffer(value)); + static final _$channelStatus = $grpc.ClientMethod<$1.Empty, $0.ChannelStatusResponse>( + '/fai.v1.HubAdmin/ChannelStatus', + ($1.Empty value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.ChannelStatusResponse.fromBuffer(value)); HubAdminClient(super.channel, {super.options, super.interceptors}); @@ -272,6 +276,13 @@ class HubAdminClient extends $grpc.Client { $grpc.ResponseFuture<$0.CheckUpdateResponse> checkUpdate($1.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$checkUpdate, request, options: options); } + + /// Active channel + per-channel daemon status. Lets Studio's + /// settings page render which channel is current and whether + /// each daemon is up. Mirrors the CLI's `fai daemon status`. + $grpc.ResponseFuture<$0.ChannelStatusResponse> channelStatus($1.Empty request, {$grpc.CallOptions? options}) { + return $createUnaryCall(_$channelStatus, request, options: options); + } } @$pb.GrpcServiceName('fai.v1.HubAdmin') @@ -384,6 +395,13 @@ abstract class HubAdminServiceBase extends $grpc.Service { false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($0.CheckUpdateResponse value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$1.Empty, $0.ChannelStatusResponse>( + 'ChannelStatus', + channelStatus_Pre, + false, + false, + ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), + ($0.ChannelStatusResponse value) => value.writeToBuffer())); } $async.Future<$0.CapabilityList> listCapabilities_Pre($grpc.ServiceCall $call, $async.Future<$1.Empty> $request) async { @@ -446,6 +464,10 @@ abstract class HubAdminServiceBase extends $grpc.Service { return checkUpdate($call, await $request); } + $async.Future<$0.ChannelStatusResponse> channelStatus_Pre($grpc.ServiceCall $call, $async.Future<$1.Empty> $request) async { + return channelStatus($call, await $request); + } + $async.Future<$0.CapabilityList> listCapabilities($grpc.ServiceCall call, $1.Empty request); $async.Future<$0.ModuleInfoResponse> moduleInfo($grpc.ServiceCall call, $0.ModuleInfoRequest request); $async.Future<$0.CheckPermissionResponse> checkPermission($grpc.ServiceCall call, $0.CheckPermissionRequest request); @@ -461,4 +483,5 @@ abstract class HubAdminServiceBase extends $grpc.Service { $async.Future<$0.VerifyEventChainResponse> verifyEventChain($grpc.ServiceCall call, $1.Empty request); $async.Future<$0.ServiceList> listServices($grpc.ServiceCall call, $1.Empty request); $async.Future<$0.CheckUpdateResponse> checkUpdate($grpc.ServiceCall call, $1.Empty request); + $async.Future<$0.ChannelStatusResponse> channelStatus($grpc.ServiceCall call, $1.Empty request); } diff --git a/lib/src/generated/fai/v1/hub.pbjson.dart b/lib/src/generated/fai/v1/hub.pbjson.dart index a822670..e203e2e 100644 --- a/lib/src/generated/fai/v1/hub.pbjson.dart +++ b/lib/src/generated/fai/v1/hub.pbjson.dart @@ -253,6 +253,7 @@ const LoggedEvent$json = { {'1': 'module_version', '3': 9, '4': 1, '5': 9, '10': 'moduleVersion'}, {'1': 'duration_ms', '3': 10, '4': 1, '5': 3, '10': 'durationMs'}, {'1': 'error', '3': 11, '4': 1, '5': 9, '10': 'error'}, + {'1': 'detail', '3': 12, '4': 1, '5': 9, '10': 'detail'}, ], }; @@ -264,7 +265,7 @@ final $typed_data.Uint8List loggedEventDescriptor = $convert.base64Decode( '52b2NhdGlvbklkEhsKCWZsb3dfbmFtZRgGIAEoCVIIZmxvd05hbWUSFwoHc3RlcF9pZBgHIAEo' 'CVIGc3RlcElkEh8KC21vZHVsZV9uYW1lGAggASgJUgptb2R1bGVOYW1lEiUKDm1vZHVsZV92ZX' 'JzaW9uGAkgASgJUg1tb2R1bGVWZXJzaW9uEh8KC2R1cmF0aW9uX21zGAogASgDUgpkdXJhdGlv' - 'bk1zEhQKBWVycm9yGAsgASgJUgVlcnJvcg=='); + 'bk1zEhQKBWVycm9yGAsgASgJUgVlcnJvchIWCgZkZXRhaWwYDCABKAlSBmRldGFpbA=='); @$core.Deprecated('Use saveFlowRequestDescriptor instead') const SaveFlowRequest$json = { @@ -612,6 +613,36 @@ final $typed_data.Uint8List storeEntryDescriptor = $convert.base64Decode( 'Npb24YDCABKAlSC2Jlc3RWZXJzaW9uEhYKBnN0YXR1cxgNIAEoCVIGc3RhdHVzEhwKCWluc3Rh' 'bGxlZBgOIAEoCFIJaW5zdGFsbGVk'); +@$core.Deprecated('Use channelStatusResponseDescriptor instead') +const ChannelStatusResponse$json = { + '1': 'ChannelStatusResponse', + '2': [ + {'1': 'active', '3': 1, '4': 1, '5': 9, '10': 'active'}, + {'1': 'channels', '3': 2, '4': 3, '5': 11, '6': '.fai.v1.ChannelEntry', '10': 'channels'}, + ], +}; + +/// Descriptor for `ChannelStatusResponse`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List channelStatusResponseDescriptor = $convert.base64Decode( + 'ChVDaGFubmVsU3RhdHVzUmVzcG9uc2USFgoGYWN0aXZlGAEgASgJUgZhY3RpdmUSMAoIY2hhbm' + '5lbHMYAiADKAsyFC5mYWkudjEuQ2hhbm5lbEVudHJ5UghjaGFubmVscw=='); + +@$core.Deprecated('Use channelEntryDescriptor instead') +const ChannelEntry$json = { + '1': 'ChannelEntry', + '2': [ + {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, + {'1': 'port', '3': 2, '4': 1, '5': 13, '10': 'port'}, + {'1': 'running', '3': 3, '4': 1, '5': 8, '10': 'running'}, + {'1': 'endpoint', '3': 4, '4': 1, '5': 9, '10': 'endpoint'}, + ], +}; + +/// Descriptor for `ChannelEntry`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List channelEntryDescriptor = $convert.base64Decode( + 'CgxDaGFubmVsRW50cnkSEgoEbmFtZRgBIAEoCVIEbmFtZRISCgRwb3J0GAIgASgNUgRwb3J0Eh' + 'gKB3J1bm5pbmcYAyABKAhSB3J1bm5pbmcSGgoIZW5kcG9pbnQYBCABKAlSCGVuZHBvaW50'); + @$core.Deprecated('Use checkUpdateResponseDescriptor instead') const CheckUpdateResponse$json = { '1': 'CheckUpdateResponse', diff --git a/lib/src/hub_client.dart b/lib/src/hub_client.dart index aeaaeff..13b0ef4 100644 --- a/lib/src/hub_client.dart +++ b/lib/src/hub_client.dart @@ -42,8 +42,13 @@ typedef PendingApprovalEntry = pb.PendingApprovalEntry; typedef DeclaredService = pb.DeclaredService; typedef VerifyEventChainResponse = pb.VerifyEventChainResponse; typedef CheckUpdateResponse = pb.CheckUpdateResponse; +typedef ChannelStatusResponse = pb.ChannelStatusResponse; +typedef ChannelEntry = pb.ChannelEntry; typedef ModuleInfoResponse = pb.ModuleInfoResponse; typedef FlowSummary = pb.FlowSummary; +typedef StoreEntry = pb.StoreEntry; +typedef StoreSearchResponse = pb.StoreSearchResponse; +typedef InstallModuleResponse = pb.InstallModuleResponse; typedef Payload = pb_common.Payload; typedef SubmitResponse = pb.SubmitResponse; @@ -172,6 +177,46 @@ class HubClient { return _admin.checkUpdate(Empty()); } + /// Active channel name + per-channel daemon status. Studio's + /// settings page calls this to render which channel is current + /// and whether each daemon is up. + Future channelStatus() { + return _admin.channelStatus(Empty()); + } + + /// Search the hub's bundled store index. All filters are + /// optional; an empty query returns the first [limit] entries + /// the index ships with. + Future> searchStore({ + String query = '', + String category = '', + String tag = '', + String status = '', + int limit = 50, + }) async { + final r = await _admin.searchStore(pb.SearchStoreRequest( + query: query, + category: category, + tag: tag, + status: status, + limit: limit, + )); + return r.entries; + } + + /// Install a module from a `.fai` bundle. [source] is either a + /// URL or a local filesystem path; [expectedSha256] is an + /// optional hex digest the hub verifies before unpacking. + Future installModule({ + required String source, + String expectedSha256 = '', + }) { + return _admin.installModule(pb.InstallModuleRequest( + source: source, + expectedSha256: expectedSha256, + )); + } + /// All saved flows known to the hub. Each entry carries the /// flow name, on-disk path and byte size. Future> listFlows() async { diff --git a/pubspec.yaml b/pubspec.yaml index 0dac438..1eb1b03 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fai_dart_sdk description: gRPC client SDK for the F∆I Platform hub. Used by F∆I Studio and Tier-3 domain apps. -version: 0.2.0 +version: 0.3.0 publish_to: 'none' repository: https://git.flemming.ws/fai/dart-sdk