feat: project registry + per-project filters (stage 1 wire surface)
Some checks are pending
Security / Security check (push) Waiting to run

- listProjects() returns the shared hub's registry (general first;
  sealed areas never appear — they are their own hub instance).
- eventLog, streamEvents and listApprovals take an optional project:
  filter; LoggedEvent, PendingApprovalEntry and FlowSummary carry
  project. Stubs regenerated from the platform's hub.proto.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-07-12 13:33:08 +02:00
parent 408f0eb850
commit dd909b58f4
5 changed files with 383 additions and 11 deletions

View file

@ -4,6 +4,16 @@ The SDK version tracks the platform release that shipped the
matching wire surface; see the platform repo's `CHANGELOG.md` matching wire surface; see the platform repo's `CHANGELOG.md`
for the authoritative per-release detail. for the authoritative per-release detail.
## Unreleased
- Projects on the wire (Studio multi-project stage ①):
`listProjects()` returns the shared hub's registry (`general`
first; sealed areas never appear — they are their own hub
instance); `eventLog`, `streamEvents` and `listApprovals` take
an optional `project:` filter; `LoggedEvent`,
`PendingApprovalEntry` and `FlowSummary` carry `project`.
Regenerated stubs from the platform's `hub.proto`.
## 0.18.x (current) ## 0.18.x (current)
- Full Hub + HubAdmin coverage: `submit` (incl. `detach:`), - Full Hub + HubAdmin coverage: `submit` (incl. `detach:`),

View file

@ -1762,10 +1762,12 @@ class EventLogRequest extends $pb.GeneratedMessage {
factory EventLogRequest({ factory EventLogRequest({
$core.int? limit, $core.int? limit,
$core.Iterable<$core.String>? eventTypes, $core.Iterable<$core.String>? eventTypes,
$core.String? project,
}) { }) {
final result = create(); final result = create();
if (limit != null) result.limit = limit; if (limit != null) result.limit = limit;
if (eventTypes != null) result.eventTypes.addAll(eventTypes); if (eventTypes != null) result.eventTypes.addAll(eventTypes);
if (project != null) result.project = project;
return result; return result;
} }
@ -1784,6 +1786,7 @@ class EventLogRequest extends $pb.GeneratedMessage {
createEmptyInstance: create) createEmptyInstance: create)
..aI(1, _omitFieldNames ? '' : 'limit') ..aI(1, _omitFieldNames ? '' : 'limit')
..pPS(2, _omitFieldNames ? '' : 'eventTypes') ..pPS(2, _omitFieldNames ? '' : 'eventTypes')
..aOS(3, _omitFieldNames ? '' : 'project')
..hasRequiredFields = false; ..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@ -1818,6 +1821,18 @@ class EventLogRequest extends $pb.GeneratedMessage {
/// Optional event_type filter; when empty, all types are returned. /// Optional event_type filter; when empty, all types are returned.
@$pb.TagNumber(2) @$pb.TagNumber(2)
$pb.PbList<$core.String> get eventTypes => $_getList(1); $pb.PbList<$core.String> get eventTypes => $_getList(1);
/// Optional project filter (slug). Empty = all projects. Scopes
/// the audit view to one project the per-project compliance
/// export use case (`chain admin events --project`).
@$pb.TagNumber(3)
$core.String get project => $_getSZ(2);
@$pb.TagNumber(3)
set project($core.String value) => $_setString(2, value);
@$pb.TagNumber(3)
$core.bool hasProject() => $_has(2);
@$pb.TagNumber(3)
void clearProject() => $_clearField(3);
} }
class EventLogResponse extends $pb.GeneratedMessage { class EventLogResponse extends $pb.GeneratedMessage {
@ -1873,10 +1888,12 @@ class StreamEventsRequest extends $pb.GeneratedMessage {
factory StreamEventsRequest({ factory StreamEventsRequest({
$core.int? backfill, $core.int? backfill,
$core.Iterable<$core.String>? eventTypes, $core.Iterable<$core.String>? eventTypes,
$core.String? project,
}) { }) {
final result = create(); final result = create();
if (backfill != null) result.backfill = backfill; if (backfill != null) result.backfill = backfill;
if (eventTypes != null) result.eventTypes.addAll(eventTypes); if (eventTypes != null) result.eventTypes.addAll(eventTypes);
if (project != null) result.project = project;
return result; return result;
} }
@ -1895,6 +1912,7 @@ class StreamEventsRequest extends $pb.GeneratedMessage {
createEmptyInstance: create) createEmptyInstance: create)
..aI(1, _omitFieldNames ? '' : 'backfill') ..aI(1, _omitFieldNames ? '' : 'backfill')
..pPS(2, _omitFieldNames ? '' : 'eventTypes') ..pPS(2, _omitFieldNames ? '' : 'eventTypes')
..aOS(3, _omitFieldNames ? '' : 'project')
..hasRequiredFields = false; ..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@ -1933,6 +1951,17 @@ class StreamEventsRequest extends $pb.GeneratedMessage {
/// tail. /// tail.
@$pb.TagNumber(2) @$pb.TagNumber(2)
$pb.PbList<$core.String> get eventTypes => $_getList(1); $pb.PbList<$core.String> get eventTypes => $_getList(1);
/// Optional project filter (slug). Empty = all projects. Applies
/// to both the backfill prefix and the live tail.
@$pb.TagNumber(3)
$core.String get project => $_getSZ(2);
@$pb.TagNumber(3)
set project($core.String value) => $_setString(2, value);
@$pb.TagNumber(3)
$core.bool hasProject() => $_has(2);
@$pb.TagNumber(3)
void clearProject() => $_clearField(3);
} }
class LoggedEvent extends $pb.GeneratedMessage { class LoggedEvent extends $pb.GeneratedMessage {
@ -1950,6 +1979,7 @@ class LoggedEvent extends $pb.GeneratedMessage {
$core.String? error, $core.String? error,
$core.String? detail, $core.String? detail,
$core.String? callerName, $core.String? callerName,
$core.String? project,
}) { }) {
final result = create(); final result = create();
if (eventId != null) result.eventId = eventId; if (eventId != null) result.eventId = eventId;
@ -1965,6 +1995,7 @@ class LoggedEvent extends $pb.GeneratedMessage {
if (error != null) result.error = error; if (error != null) result.error = error;
if (detail != null) result.detail = detail; if (detail != null) result.detail = detail;
if (callerName != null) result.callerName = callerName; if (callerName != null) result.callerName = callerName;
if (project != null) result.project = project;
return result; return result;
} }
@ -1994,6 +2025,7 @@ class LoggedEvent extends $pb.GeneratedMessage {
..aOS(11, _omitFieldNames ? '' : 'error') ..aOS(11, _omitFieldNames ? '' : 'error')
..aOS(12, _omitFieldNames ? '' : 'detail') ..aOS(12, _omitFieldNames ? '' : 'detail')
..aOS(13, _omitFieldNames ? '' : 'callerName') ..aOS(13, _omitFieldNames ? '' : 'callerName')
..aOS(14, _omitFieldNames ? '' : 'project')
..hasRequiredFields = false; ..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@ -2141,6 +2173,19 @@ class LoggedEvent extends $pb.GeneratedMessage {
$core.bool hasCallerName() => $_has(12); $core.bool hasCallerName() => $_has(12);
@$pb.TagNumber(13) @$pb.TagNumber(13)
void clearCallerName() => $_clearField(13); void clearCallerName() => $_clearField(13);
/// Project slug the run was stamped with (`general` when the
/// run carried no explicit project). Bound into the V4 audit
/// hash, so the attribution is tamper-evident. Empty on rows
/// pre-dating the project migration (0008).
@$pb.TagNumber(14)
$core.String get project => $_getSZ(13);
@$pb.TagNumber(14)
set project($core.String value) => $_setString(13, value);
@$pb.TagNumber(14)
$core.bool hasProject() => $_has(13);
@$pb.TagNumber(14)
void clearProject() => $_clearField(14);
} }
class SaveFlowRequest extends $pb.GeneratedMessage { class SaveFlowRequest extends $pb.GeneratedMessage {
@ -2453,6 +2498,7 @@ class FlowSummary extends $pb.GeneratedMessage {
$core.String? path, $core.String? path,
$fixnum.Int64? sizeBytes, $fixnum.Int64? sizeBytes,
$core.Iterable<$core.String>? requiredCapabilities, $core.Iterable<$core.String>? requiredCapabilities,
$core.String? project,
}) { }) {
final result = create(); final result = create();
if (name != null) result.name = name; if (name != null) result.name = name;
@ -2460,6 +2506,7 @@ class FlowSummary extends $pb.GeneratedMessage {
if (sizeBytes != null) result.sizeBytes = sizeBytes; if (sizeBytes != null) result.sizeBytes = sizeBytes;
if (requiredCapabilities != null) if (requiredCapabilities != null)
result.requiredCapabilities.addAll(requiredCapabilities); result.requiredCapabilities.addAll(requiredCapabilities);
if (project != null) result.project = project;
return result; return result;
} }
@ -2480,6 +2527,7 @@ class FlowSummary extends $pb.GeneratedMessage {
..aOS(2, _omitFieldNames ? '' : 'path') ..aOS(2, _omitFieldNames ? '' : 'path')
..aInt64(3, _omitFieldNames ? '' : 'sizeBytes') ..aInt64(3, _omitFieldNames ? '' : 'sizeBytes')
..pPS(4, _omitFieldNames ? '' : 'requiredCapabilities') ..pPS(4, _omitFieldNames ? '' : 'requiredCapabilities')
..aOS(5, _omitFieldNames ? '' : 'project')
..hasRequiredFields = false; ..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@ -2536,6 +2584,195 @@ class FlowSummary extends $pb.GeneratedMessage {
/// installed module) without parsing the YAML itself. /// installed module) without parsing the YAML itself.
@$pb.TagNumber(4) @$pb.TagNumber(4)
$pb.PbList<$core.String> get requiredCapabilities => $_getList(3); $pb.PbList<$core.String> get requiredCapabilities => $_getList(3);
/// The flow's own top-level `project:` slug; empty when the
/// YAML has none (a run then lands in the caller's project,
/// then `general`). The file wins on mismatch same semantics
/// as the CLI (docs/architecture/projects.md).
@$pb.TagNumber(5)
$core.String get project => $_getSZ(4);
@$pb.TagNumber(5)
set project($core.String value) => $_setString(4, value);
@$pb.TagNumber(5)
$core.bool hasProject() => $_has(4);
@$pb.TagNumber(5)
void clearProject() => $_clearField(5);
}
/// One registered project (shared-hub registry row). Sealed
/// projects never appear here they are their own hub instance.
class ProjectInfo extends $pb.GeneratedMessage {
factory ProjectInfo({
$core.String? slug,
$core.String? name,
$core.String? color,
$core.String? description,
$core.String? isolation,
$core.String? createdAt,
}) {
final result = create();
if (slug != null) result.slug = slug;
if (name != null) result.name = name;
if (color != null) result.color = color;
if (description != null) result.description = description;
if (isolation != null) result.isolation = isolation;
if (createdAt != null) result.createdAt = createdAt;
return result;
}
ProjectInfo._();
factory ProjectInfo.fromBuffer($core.List<$core.int> data,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(data, registry);
factory ProjectInfo.fromJson($core.String json,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(json, registry);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
_omitMessageNames ? '' : 'ProjectInfo',
package: const $pb.PackageName(_omitMessageNames ? '' : 'chain.v1'),
createEmptyInstance: create)
..aOS(1, _omitFieldNames ? '' : 'slug')
..aOS(2, _omitFieldNames ? '' : 'name')
..aOS(3, _omitFieldNames ? '' : 'color')
..aOS(4, _omitFieldNames ? '' : 'description')
..aOS(5, _omitFieldNames ? '' : 'isolation')
..aOS(6, _omitFieldNames ? '' : 'createdAt')
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ProjectInfo clone() => deepCopy();
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ProjectInfo copyWith(void Function(ProjectInfo) updates) =>
super.copyWith((message) => updates(message as ProjectInfo))
as ProjectInfo;
@$core.override
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ProjectInfo create() => ProjectInfo._();
@$core.override
ProjectInfo createEmptyInstance() => create();
@$core.pragma('dart2js:noInline')
static ProjectInfo getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<ProjectInfo>(create);
static ProjectInfo? _defaultInstance;
/// Stable lowercase-kebab identifier stamped onto every run.
@$pb.TagNumber(1)
$core.String get slug => $_getSZ(0);
@$pb.TagNumber(1)
set slug($core.String value) => $_setString(0, value);
@$pb.TagNumber(1)
$core.bool hasSlug() => $_has(0);
@$pb.TagNumber(1)
void clearSlug() => $_clearField(1);
/// Human display name (renamable without touching history).
@$pb.TagNumber(2)
$core.String get name => $_getSZ(1);
@$pb.TagNumber(2)
set name($core.String value) => $_setString(1, value);
@$pb.TagNumber(2)
$core.bool hasName() => $_has(1);
@$pb.TagNumber(2)
void clearName() => $_clearField(2);
/// Accent colour (hex, e.g. "#8b7cf6") for UI marking. Empty
/// when unset.
@$pb.TagNumber(3)
$core.String get color => $_getSZ(2);
@$pb.TagNumber(3)
set color($core.String value) => $_setString(2, value);
@$pb.TagNumber(3)
$core.bool hasColor() => $_has(2);
@$pb.TagNumber(3)
void clearColor() => $_clearField(3);
/// Free-form description. Empty when unset.
@$pb.TagNumber(4)
$core.String get description => $_getSZ(3);
@$pb.TagNumber(4)
set description($core.String value) => $_setString(3, value);
@$pb.TagNumber(4)
$core.bool hasDescription() => $_has(3);
@$pb.TagNumber(4)
void clearDescription() => $_clearField(4);
/// Protection level: "open" (pure label) or "protected"
/// (logically separated no hard process barrier; the honest
/// wording every UI must keep).
@$pb.TagNumber(5)
$core.String get isolation => $_getSZ(4);
@$pb.TagNumber(5)
set isolation($core.String value) => $_setString(4, value);
@$pb.TagNumber(5)
$core.bool hasIsolation() => $_has(4);
@$pb.TagNumber(5)
void clearIsolation() => $_clearField(5);
/// ISO-8601 UTC creation timestamp.
@$pb.TagNumber(6)
$core.String get createdAt => $_getSZ(5);
@$pb.TagNumber(6)
set createdAt($core.String value) => $_setString(5, value);
@$pb.TagNumber(6)
$core.bool hasCreatedAt() => $_has(5);
@$pb.TagNumber(6)
void clearCreatedAt() => $_clearField(6);
}
class ProjectList extends $pb.GeneratedMessage {
factory ProjectList({
$core.Iterable<ProjectInfo>? projects,
}) {
final result = create();
if (projects != null) result.projects.addAll(projects);
return result;
}
ProjectList._();
factory ProjectList.fromBuffer($core.List<$core.int> data,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(data, registry);
factory ProjectList.fromJson($core.String json,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(json, registry);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
_omitMessageNames ? '' : 'ProjectList',
package: const $pb.PackageName(_omitMessageNames ? '' : 'chain.v1'),
createEmptyInstance: create)
..pPM<ProjectInfo>(1, _omitFieldNames ? '' : 'projects',
subBuilder: ProjectInfo.create)
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ProjectList clone() => deepCopy();
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ProjectList copyWith(void Function(ProjectList) updates) =>
super.copyWith((message) => updates(message as ProjectList))
as ProjectList;
@$core.override
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ProjectList create() => ProjectList._();
@$core.override
ProjectList createEmptyInstance() => create();
@$core.pragma('dart2js:noInline')
static ProjectList getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<ProjectList>(create);
static ProjectList? _defaultInstance;
/// All registered projects, `general` first, then by display
/// name (case-insensitive).
@$pb.TagNumber(1)
$pb.PbList<ProjectInfo> get projects => $_getList(0);
} }
class RunSavedFlowRequest extends $pb.GeneratedMessage { class RunSavedFlowRequest extends $pb.GeneratedMessage {
@ -3936,10 +4173,12 @@ class ListApprovalsRequest extends $pb.GeneratedMessage {
factory ListApprovalsRequest({ factory ListApprovalsRequest({
$core.Iterable<$core.String>? statuses, $core.Iterable<$core.String>? statuses,
$core.int? limit, $core.int? limit,
$core.String? project,
}) { }) {
final result = create(); final result = create();
if (statuses != null) result.statuses.addAll(statuses); if (statuses != null) result.statuses.addAll(statuses);
if (limit != null) result.limit = limit; if (limit != null) result.limit = limit;
if (project != null) result.project = project;
return result; return result;
} }
@ -3958,6 +4197,7 @@ class ListApprovalsRequest extends $pb.GeneratedMessage {
createEmptyInstance: create) createEmptyInstance: create)
..pPS(1, _omitFieldNames ? '' : 'statuses') ..pPS(1, _omitFieldNames ? '' : 'statuses')
..aI(2, _omitFieldNames ? '' : 'limit') ..aI(2, _omitFieldNames ? '' : 'limit')
..aOS(3, _omitFieldNames ? '' : 'project')
..hasRequiredFields = false; ..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@ -3993,6 +4233,16 @@ class ListApprovalsRequest extends $pb.GeneratedMessage {
$core.bool hasLimit() => $_has(1); $core.bool hasLimit() => $_has(1);
@$pb.TagNumber(2) @$pb.TagNumber(2)
void clearLimit() => $_clearField(2); void clearLimit() => $_clearField(2);
/// Optional project filter (slug). Empty = all projects.
@$pb.TagNumber(3)
$core.String get project => $_getSZ(2);
@$pb.TagNumber(3)
set project($core.String value) => $_setString(2, value);
@$pb.TagNumber(3)
$core.bool hasProject() => $_has(2);
@$pb.TagNumber(3)
void clearProject() => $_clearField(3);
} }
class ApprovalList extends $pb.GeneratedMessage { class ApprovalList extends $pb.GeneratedMessage {
@ -4058,6 +4308,7 @@ class PendingApprovalEntry extends $pb.GeneratedMessage {
$core.String? decidedAt, $core.String? decidedAt,
$core.String? decidedBy, $core.String? decidedBy,
$core.String? reason, $core.String? reason,
$core.String? project,
}) { }) {
final result = create(); final result = create();
if (approvalId != null) result.approvalId = approvalId; if (approvalId != null) result.approvalId = approvalId;
@ -4072,6 +4323,7 @@ class PendingApprovalEntry extends $pb.GeneratedMessage {
if (decidedAt != null) result.decidedAt = decidedAt; if (decidedAt != null) result.decidedAt = decidedAt;
if (decidedBy != null) result.decidedBy = decidedBy; if (decidedBy != null) result.decidedBy = decidedBy;
if (reason != null) result.reason = reason; if (reason != null) result.reason = reason;
if (project != null) result.project = project;
return result; return result;
} }
@ -4100,6 +4352,7 @@ class PendingApprovalEntry extends $pb.GeneratedMessage {
..aOS(10, _omitFieldNames ? '' : 'decidedAt') ..aOS(10, _omitFieldNames ? '' : 'decidedAt')
..aOS(11, _omitFieldNames ? '' : 'decidedBy') ..aOS(11, _omitFieldNames ? '' : 'decidedBy')
..aOS(12, _omitFieldNames ? '' : 'reason') ..aOS(12, _omitFieldNames ? '' : 'reason')
..aOS(13, _omitFieldNames ? '' : 'project')
..hasRequiredFields = false; ..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@ -4233,6 +4486,17 @@ class PendingApprovalEntry extends $pb.GeneratedMessage {
$core.bool hasReason() => $_has(11); $core.bool hasReason() => $_has(11);
@$pb.TagNumber(12) @$pb.TagNumber(12)
void clearReason() => $_clearField(12); void clearReason() => $_clearField(12);
/// Project slug the requesting run was stamped with
/// (`general` when the run carried no explicit project).
@$pb.TagNumber(13)
$core.String get project => $_getSZ(12);
@$pb.TagNumber(13)
set project($core.String value) => $_setString(12, value);
@$pb.TagNumber(13)
$core.bool hasProject() => $_has(12);
@$pb.TagNumber(13)
void clearProject() => $_clearField(13);
} }
class DecideApprovalRequest extends $pb.GeneratedMessage { class DecideApprovalRequest extends $pb.GeneratedMessage {

View file

@ -355,6 +355,19 @@ class HubAdminClient extends $grpc.Client {
return $createUnaryCall(_$getFlowDefinition, request, options: options); return $createUnaryCall(_$getFlowDefinition, request, options: options);
} }
/// List the shared hub's project registry (`general` first) —
/// the lightweight labels that group flows, runs, approvals and
/// audit events. Only `open` and `protected` projects appear:
/// sealed projects are their own hub instance and are never
/// known to a shared hub (docs/architecture/projects.md).
/// Studio's workspace switcher reads this list.
$grpc.ResponseFuture<$0.ProjectList> listProjects(
$1.Empty request, {
$grpc.CallOptions? options,
}) {
return $createUnaryCall(_$listProjects, request, options: options);
}
/// Search the bundled (and Phase 1+: federated) store index for /// Search the bundled (and Phase 1+: federated) store index for
/// modules matching a query. /// modules matching a query.
$grpc.ResponseFuture<$0.StoreSearchResponse> searchStore( $grpc.ResponseFuture<$0.StoreSearchResponse> searchStore(
@ -846,6 +859,10 @@ class HubAdminClient extends $grpc.Client {
'/chain.v1.HubAdmin/GetFlowDefinition', '/chain.v1.HubAdmin/GetFlowDefinition',
($0.GetFlowDefinitionRequest value) => value.writeToBuffer(), ($0.GetFlowDefinitionRequest value) => value.writeToBuffer(),
$0.FlowDefinition.fromBuffer); $0.FlowDefinition.fromBuffer);
static final _$listProjects = $grpc.ClientMethod<$1.Empty, $0.ProjectList>(
'/chain.v1.HubAdmin/ListProjects',
($1.Empty value) => value.writeToBuffer(),
$0.ProjectList.fromBuffer);
static final _$searchStore = static final _$searchStore =
$grpc.ClientMethod<$0.SearchStoreRequest, $0.StoreSearchResponse>( $grpc.ClientMethod<$0.SearchStoreRequest, $0.StoreSearchResponse>(
'/chain.v1.HubAdmin/SearchStore', '/chain.v1.HubAdmin/SearchStore',
@ -1137,6 +1154,13 @@ abstract class HubAdminServiceBase extends $grpc.Service {
($core.List<$core.int> value) => ($core.List<$core.int> value) =>
$0.GetFlowDefinitionRequest.fromBuffer(value), $0.GetFlowDefinitionRequest.fromBuffer(value),
($0.FlowDefinition value) => value.writeToBuffer())); ($0.FlowDefinition value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$1.Empty, $0.ProjectList>(
'ListProjects',
listProjects_Pre,
false,
false,
($core.List<$core.int> value) => $1.Empty.fromBuffer(value),
($0.ProjectList value) => value.writeToBuffer()));
$addMethod( $addMethod(
$grpc.ServiceMethod<$0.SearchStoreRequest, $0.StoreSearchResponse>( $grpc.ServiceMethod<$0.SearchStoreRequest, $0.StoreSearchResponse>(
'SearchStore', 'SearchStore',
@ -1558,6 +1582,14 @@ abstract class HubAdminServiceBase extends $grpc.Service {
$async.Future<$0.FlowDefinition> getFlowDefinition( $async.Future<$0.FlowDefinition> getFlowDefinition(
$grpc.ServiceCall call, $0.GetFlowDefinitionRequest request); $grpc.ServiceCall call, $0.GetFlowDefinitionRequest request);
$async.Future<$0.ProjectList> listProjects_Pre(
$grpc.ServiceCall $call, $async.Future<$1.Empty> $request) async {
return listProjects($call, await $request);
}
$async.Future<$0.ProjectList> listProjects(
$grpc.ServiceCall call, $1.Empty request);
$async.Future<$0.StoreSearchResponse> searchStore_Pre($grpc.ServiceCall $call, $async.Future<$0.StoreSearchResponse> searchStore_Pre($grpc.ServiceCall $call,
$async.Future<$0.SearchStoreRequest> $request) async { $async.Future<$0.SearchStoreRequest> $request) async {
return searchStore($call, await $request); return searchStore($call, await $request);

View file

@ -574,13 +574,14 @@ const EventLogRequest$json = {
'2': [ '2': [
{'1': 'limit', '3': 1, '4': 1, '5': 5, '10': 'limit'}, {'1': 'limit', '3': 1, '4': 1, '5': 5, '10': 'limit'},
{'1': 'event_types', '3': 2, '4': 3, '5': 9, '10': 'eventTypes'}, {'1': 'event_types', '3': 2, '4': 3, '5': 9, '10': 'eventTypes'},
{'1': 'project', '3': 3, '4': 1, '5': 9, '10': 'project'},
], ],
}; };
/// Descriptor for `EventLogRequest`. Decode as a `google.protobuf.DescriptorProto`. /// Descriptor for `EventLogRequest`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List eventLogRequestDescriptor = $convert.base64Decode( final $typed_data.Uint8List eventLogRequestDescriptor = $convert.base64Decode(
'Cg9FdmVudExvZ1JlcXVlc3QSFAoFbGltaXQYASABKAVSBWxpbWl0Eh8KC2V2ZW50X3R5cGVzGA' 'Cg9FdmVudExvZ1JlcXVlc3QSFAoFbGltaXQYASABKAVSBWxpbWl0Eh8KC2V2ZW50X3R5cGVzGA'
'IgAygJUgpldmVudFR5cGVz'); 'IgAygJUgpldmVudFR5cGVzEhgKB3Byb2plY3QYAyABKAlSB3Byb2plY3Q=');
@$core.Deprecated('Use eventLogResponseDescriptor instead') @$core.Deprecated('Use eventLogResponseDescriptor instead')
const EventLogResponse$json = { const EventLogResponse$json = {
@ -608,13 +609,14 @@ const StreamEventsRequest$json = {
'2': [ '2': [
{'1': 'backfill', '3': 1, '4': 1, '5': 5, '10': 'backfill'}, {'1': 'backfill', '3': 1, '4': 1, '5': 5, '10': 'backfill'},
{'1': 'event_types', '3': 2, '4': 3, '5': 9, '10': 'eventTypes'}, {'1': 'event_types', '3': 2, '4': 3, '5': 9, '10': 'eventTypes'},
{'1': 'project', '3': 3, '4': 1, '5': 9, '10': 'project'},
], ],
}; };
/// Descriptor for `StreamEventsRequest`. Decode as a `google.protobuf.DescriptorProto`. /// Descriptor for `StreamEventsRequest`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List streamEventsRequestDescriptor = $convert.base64Decode( final $typed_data.Uint8List streamEventsRequestDescriptor = $convert.base64Decode(
'ChNTdHJlYW1FdmVudHNSZXF1ZXN0EhoKCGJhY2tmaWxsGAEgASgFUghiYWNrZmlsbBIfCgtldm' 'ChNTdHJlYW1FdmVudHNSZXF1ZXN0EhoKCGJhY2tmaWxsGAEgASgFUghiYWNrZmlsbBIfCgtldm'
'VudF90eXBlcxgCIAMoCVIKZXZlbnRUeXBlcw=='); 'VudF90eXBlcxgCIAMoCVIKZXZlbnRUeXBlcxIYCgdwcm9qZWN0GAMgASgJUgdwcm9qZWN0');
@$core.Deprecated('Use loggedEventDescriptor instead') @$core.Deprecated('Use loggedEventDescriptor instead')
const LoggedEvent$json = { const LoggedEvent$json = {
@ -633,6 +635,7 @@ const LoggedEvent$json = {
{'1': 'error', '3': 11, '4': 1, '5': 9, '10': 'error'}, {'1': 'error', '3': 11, '4': 1, '5': 9, '10': 'error'},
{'1': 'detail', '3': 12, '4': 1, '5': 9, '10': 'detail'}, {'1': 'detail', '3': 12, '4': 1, '5': 9, '10': 'detail'},
{'1': 'caller_name', '3': 13, '4': 1, '5': 9, '10': 'callerName'}, {'1': 'caller_name', '3': 13, '4': 1, '5': 9, '10': 'callerName'},
{'1': 'project', '3': 14, '4': 1, '5': 9, '10': 'project'},
], ],
}; };
@ -645,7 +648,7 @@ final $typed_data.Uint8List loggedEventDescriptor = $convert.base64Decode(
'CVIGc3RlcElkEh8KC21vZHVsZV9uYW1lGAggASgJUgptb2R1bGVOYW1lEiUKDm1vZHVsZV92ZX' 'CVIGc3RlcElkEh8KC21vZHVsZV9uYW1lGAggASgJUgptb2R1bGVOYW1lEiUKDm1vZHVsZV92ZX'
'JzaW9uGAkgASgJUg1tb2R1bGVWZXJzaW9uEh8KC2R1cmF0aW9uX21zGAogASgDUgpkdXJhdGlv' 'JzaW9uGAkgASgJUg1tb2R1bGVWZXJzaW9uEh8KC2R1cmF0aW9uX21zGAogASgDUgpkdXJhdGlv'
'bk1zEhQKBWVycm9yGAsgASgJUgVlcnJvchIWCgZkZXRhaWwYDCABKAlSBmRldGFpbBIfCgtjYW' 'bk1zEhQKBWVycm9yGAsgASgJUgVlcnJvchIWCgZkZXRhaWwYDCABKAlSBmRldGFpbBIfCgtjYW'
'xsZXJfbmFtZRgNIAEoCVIKY2FsbGVyTmFtZQ=='); 'xsZXJfbmFtZRgNIAEoCVIKY2FsbGVyTmFtZRIYCgdwcm9qZWN0GA4gASgJUgdwcm9qZWN0');
@$core.Deprecated('Use saveFlowRequestDescriptor instead') @$core.Deprecated('Use saveFlowRequestDescriptor instead')
const SaveFlowRequest$json = { const SaveFlowRequest$json = {
@ -734,6 +737,7 @@ const FlowSummary$json = {
'5': 9, '5': 9,
'10': 'requiredCapabilities' '10': 'requiredCapabilities'
}, },
{'1': 'project', '3': 5, '4': 1, '5': 9, '10': 'project'},
], ],
}; };
@ -741,7 +745,47 @@ const FlowSummary$json = {
final $typed_data.Uint8List flowSummaryDescriptor = $convert.base64Decode( final $typed_data.Uint8List flowSummaryDescriptor = $convert.base64Decode(
'CgtGbG93U3VtbWFyeRISCgRuYW1lGAEgASgJUgRuYW1lEhIKBHBhdGgYAiABKAlSBHBhdGgSHQ' 'CgtGbG93U3VtbWFyeRISCgRuYW1lGAEgASgJUgRuYW1lEhIKBHBhdGgYAiABKAlSBHBhdGgSHQ'
'oKc2l6ZV9ieXRlcxgDIAEoA1IJc2l6ZUJ5dGVzEjMKFXJlcXVpcmVkX2NhcGFiaWxpdGllcxgE' 'oKc2l6ZV9ieXRlcxgDIAEoA1IJc2l6ZUJ5dGVzEjMKFXJlcXVpcmVkX2NhcGFiaWxpdGllcxgE'
'IAMoCVIUcmVxdWlyZWRDYXBhYmlsaXRpZXM='); 'IAMoCVIUcmVxdWlyZWRDYXBhYmlsaXRpZXMSGAoHcHJvamVjdBgFIAEoCVIHcHJvamVjdA==');
@$core.Deprecated('Use projectInfoDescriptor instead')
const ProjectInfo$json = {
'1': 'ProjectInfo',
'2': [
{'1': 'slug', '3': 1, '4': 1, '5': 9, '10': 'slug'},
{'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
{'1': 'color', '3': 3, '4': 1, '5': 9, '10': 'color'},
{'1': 'description', '3': 4, '4': 1, '5': 9, '10': 'description'},
{'1': 'isolation', '3': 5, '4': 1, '5': 9, '10': 'isolation'},
{'1': 'created_at', '3': 6, '4': 1, '5': 9, '10': 'createdAt'},
],
};
/// Descriptor for `ProjectInfo`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List projectInfoDescriptor = $convert.base64Decode(
'CgtQcm9qZWN0SW5mbxISCgRzbHVnGAEgASgJUgRzbHVnEhIKBG5hbWUYAiABKAlSBG5hbWUSFA'
'oFY29sb3IYAyABKAlSBWNvbG9yEiAKC2Rlc2NyaXB0aW9uGAQgASgJUgtkZXNjcmlwdGlvbhIc'
'Cglpc29sYXRpb24YBSABKAlSCWlzb2xhdGlvbhIdCgpjcmVhdGVkX2F0GAYgASgJUgljcmVhdG'
'VkQXQ=');
@$core.Deprecated('Use projectListDescriptor instead')
const ProjectList$json = {
'1': 'ProjectList',
'2': [
{
'1': 'projects',
'3': 1,
'4': 3,
'5': 11,
'6': '.chain.v1.ProjectInfo',
'10': 'projects'
},
],
};
/// Descriptor for `ProjectList`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List projectListDescriptor = $convert.base64Decode(
'CgtQcm9qZWN0TGlzdBIxCghwcm9qZWN0cxgBIAMoCzIVLmNoYWluLnYxLlByb2plY3RJbmZvUg'
'hwcm9qZWN0cw==');
@$core.Deprecated('Use runSavedFlowRequestDescriptor instead') @$core.Deprecated('Use runSavedFlowRequestDescriptor instead')
const RunSavedFlowRequest$json = { const RunSavedFlowRequest$json = {
@ -1066,13 +1110,14 @@ const ListApprovalsRequest$json = {
'2': [ '2': [
{'1': 'statuses', '3': 1, '4': 3, '5': 9, '10': 'statuses'}, {'1': 'statuses', '3': 1, '4': 3, '5': 9, '10': 'statuses'},
{'1': 'limit', '3': 2, '4': 1, '5': 5, '10': 'limit'}, {'1': 'limit', '3': 2, '4': 1, '5': 5, '10': 'limit'},
{'1': 'project', '3': 3, '4': 1, '5': 9, '10': 'project'},
], ],
}; };
/// Descriptor for `ListApprovalsRequest`. Decode as a `google.protobuf.DescriptorProto`. /// Descriptor for `ListApprovalsRequest`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List listApprovalsRequestDescriptor = $convert.base64Decode( final $typed_data.Uint8List listApprovalsRequestDescriptor = $convert.base64Decode(
'ChRMaXN0QXBwcm92YWxzUmVxdWVzdBIaCghzdGF0dXNlcxgBIAMoCVIIc3RhdHVzZXMSFAoFbG' 'ChRMaXN0QXBwcm92YWxzUmVxdWVzdBIaCghzdGF0dXNlcxgBIAMoCVIIc3RhdHVzZXMSFAoFbG'
'ltaXQYAiABKAVSBWxpbWl0'); 'ltaXQYAiABKAVSBWxpbWl0EhgKB3Byb2plY3QYAyABKAlSB3Byb2plY3Q=');
@$core.Deprecated('Use approvalListDescriptor instead') @$core.Deprecated('Use approvalListDescriptor instead')
const ApprovalList$json = { const ApprovalList$json = {
@ -1110,6 +1155,7 @@ const PendingApprovalEntry$json = {
{'1': 'decided_at', '3': 10, '4': 1, '5': 9, '10': 'decidedAt'}, {'1': 'decided_at', '3': 10, '4': 1, '5': 9, '10': 'decidedAt'},
{'1': 'decided_by', '3': 11, '4': 1, '5': 9, '10': 'decidedBy'}, {'1': 'decided_by', '3': 11, '4': 1, '5': 9, '10': 'decidedBy'},
{'1': 'reason', '3': 12, '4': 1, '5': 9, '10': 'reason'}, {'1': 'reason', '3': 12, '4': 1, '5': 9, '10': 'reason'},
{'1': 'project', '3': 13, '4': 1, '5': 9, '10': 'project'},
], ],
}; };
@ -1121,7 +1167,8 @@ final $typed_data.Uint8List pendingApprovalEntryDescriptor = $convert.base64Deco
'Byb21wdBInCg9wYXlsb2FkX3ByZXZpZXcYBiABKAlSDnBheWxvYWRQcmV2aWV3Eh0KCmNyZWF0' 'Byb21wdBInCg9wYXlsb2FkX3ByZXZpZXcYBiABKAlSDnBheWxvYWRQcmV2aWV3Eh0KCmNyZWF0'
'ZWRfYXQYByABKAlSCWNyZWF0ZWRBdBIdCgpleHBpcmVzX2F0GAggASgJUglleHBpcmVzQXQSFg' 'ZWRfYXQYByABKAlSCWNyZWF0ZWRBdBIdCgpleHBpcmVzX2F0GAggASgJUglleHBpcmVzQXQSFg'
'oGc3RhdHVzGAkgASgJUgZzdGF0dXMSHQoKZGVjaWRlZF9hdBgKIAEoCVIJZGVjaWRlZEF0Eh0K' 'oGc3RhdHVzGAkgASgJUgZzdGF0dXMSHQoKZGVjaWRlZF9hdBgKIAEoCVIJZGVjaWRlZEF0Eh0K'
'CmRlY2lkZWRfYnkYCyABKAlSCWRlY2lkZWRCeRIWCgZyZWFzb24YDCABKAlSBnJlYXNvbg=='); 'CmRlY2lkZWRfYnkYCyABKAlSCWRlY2lkZWRCeRIWCgZyZWFzb24YDCABKAlSBnJlYXNvbhIYCg'
'dwcm9qZWN0GA0gASgJUgdwcm9qZWN0');
@$core.Deprecated('Use decideApprovalRequestDescriptor instead') @$core.Deprecated('Use decideApprovalRequestDescriptor instead')
const DecideApprovalRequest$json = { const DecideApprovalRequest$json = {

View file

@ -81,6 +81,7 @@ typedef N8nEndpointStatus = pb.N8nEndpointStatus;
typedef N8nEndpointConfigEntry = pb.N8nEndpointConfigEntry; typedef N8nEndpointConfigEntry = pb.N8nEndpointConfigEntry;
typedef ModuleInfoResponse = pb.ModuleInfoResponse; typedef ModuleInfoResponse = pb.ModuleInfoResponse;
typedef FlowSummary = pb.FlowSummary; typedef FlowSummary = pb.FlowSummary;
typedef ProjectInfo = pb.ProjectInfo;
typedef StoreEntry = pb.StoreEntry; typedef StoreEntry = pb.StoreEntry;
typedef StoreSearchResponse = pb.StoreSearchResponse; typedef StoreSearchResponse = pb.StoreSearchResponse;
typedef InstallModuleResponse = pb.InstallModuleResponse; typedef InstallModuleResponse = pb.InstallModuleResponse;
@ -183,18 +184,31 @@ class HubClient {
} }
/// Recent events from the audit log, newest-first. Pass an /// Recent events from the audit log, newest-first. Pass an
/// empty `types` list for all event types. /// empty `types` list for all event types. A non-empty
/// [project] scopes the view to that project's runs (the slug
/// is normalized hub-side, so a display name works too).
Future<List<LoggedEvent>> eventLog({ Future<List<LoggedEvent>> eventLog({
int limit = 100, int limit = 100,
List<String> types = const [], List<String> types = const [],
String project = '',
}) async { }) async {
final req = pb.EventLogRequest() final req = pb.EventLogRequest()
..limit = limit ..limit = limit
..eventTypes.addAll(types); ..eventTypes.addAll(types)
..project = project;
final r = await _admin.eventLog(req); final r = await _admin.eventLog(req);
return r.events; return r.events;
} }
/// The shared hub's project registry (`general` first): the
/// lightweight labels grouping flows, runs, approvals and audit
/// events. Sealed areas never appear here they are their own
/// hub instance a client connects to directly.
Future<List<ProjectInfo>> listProjects() async {
final r = await _admin.listProjects(Empty());
return r.projects;
}
/// Read the operator's `default_scope:` — the ordered list of /// Read the operator's `default_scope:` — the ordered list of
/// provider segments the bare-form capability resolver /// provider segments the bare-form capability resolver
/// consults when a flow names a capability without an explicit /// consults when a flow names a capability without an explicit
@ -248,21 +262,26 @@ class HubClient {
Stream<LoggedEvent> streamEvents({ Stream<LoggedEvent> streamEvents({
int backfill = 50, int backfill = 50,
List<String> types = const [], List<String> types = const [],
String project = '',
}) { }) {
final req = pb.StreamEventsRequest() final req = pb.StreamEventsRequest()
..backfill = backfill ..backfill = backfill
..eventTypes.addAll(types); ..eventTypes.addAll(types)
..project = project;
return _admin.streamEvents(req); return _admin.streamEvents(req);
} }
/// Pending approvals filtered by status. Defaults to all. /// Pending approvals filtered by status and/or project.
/// Defaults to all.
Future<List<PendingApprovalEntry>> listApprovals({ Future<List<PendingApprovalEntry>> listApprovals({
List<String> statuses = const [], List<String> statuses = const [],
int limit = 100, int limit = 100,
String project = '',
}) async { }) async {
final req = pb.ListApprovalsRequest() final req = pb.ListApprovalsRequest()
..limit = limit ..limit = limit
..statuses.addAll(statuses); ..statuses.addAll(statuses)
..project = project;
final r = await _admin.listApprovals(req); final r = await _admin.listApprovals(req);
return r.entries; return r.entries;
} }