696 lines
23 KiB
Protocol Buffer
696 lines
23 KiB
Protocol Buffer
syntax = "proto3";
|
|
|
|
package orchestrator.v1;
|
|
|
|
import "google/protobuf/timestamp.proto";
|
|
|
|
option go_package = "git.dev.alexdunmow.com/block/cms/orchestrator/internal/api/orchestrator/v1;orchestratorv1";
|
|
|
|
// PluginScopeService manages @scope namespaces.
|
|
service PluginScopeService {
|
|
rpc CreateScope(CreateScopeRequest) returns (CreateScopeResponse);
|
|
rpc ListMyScopes(ListMyScopesRequest) returns (ListMyScopesResponse);
|
|
rpc GetScope(GetScopeRequest) returns (GetScopeResponse);
|
|
rpc ListMyPlugins(ListMyPluginsRequest) returns (ListMyPluginsResponse);
|
|
}
|
|
|
|
// PluginRegistryService is the read-side public API plus CreatePlugin.
|
|
service PluginRegistryService {
|
|
rpc CreatePlugin(CreatePluginRequest) returns (CreatePluginResponse);
|
|
rpc GetPlugin(GetPluginRequest) returns (GetPluginResponse);
|
|
rpc ListPlugins(ListPluginsRequest) returns (ListPluginsResponse);
|
|
rpc GetVersion(GetVersionRequest) returns (GetVersionResponse);
|
|
rpc ResolveInstall(ResolveInstallRequest) returns (ResolveInstallResponse);
|
|
rpc ListCategories(ListCategoriesRequest) returns (ListCategoriesResponse);
|
|
rpc ListTags(ListTagsRequest) returns (ListTagsResponse);
|
|
rpc SubmitForReview(SubmitForReviewRequest) returns (SubmitForReviewResponse);
|
|
|
|
// Private-plugin RPCs. All require the caller to be a member of the target
|
|
// account; the server resolves account membership from the bearer token.
|
|
rpc ListPrivatePlugins(ListPrivatePluginsRequest) returns (ListPrivatePluginsResponse);
|
|
rpc DeletePrivatePlugin(DeletePrivatePluginRequest) returns (DeletePrivatePluginResponse);
|
|
rpc DeletePrivatePluginVersion(DeletePrivatePluginVersionRequest) returns (DeletePrivatePluginVersionResponse);
|
|
rpc ListPrivatePluginInstallSites(ListPrivatePluginInstallSitesRequest) returns (ListPrivatePluginInstallSitesResponse);
|
|
|
|
// Instance-authenticated variants used by the CMS install/update flow. A CMS
|
|
// instance calls these with its per-account X-CMS-Secret header plus its
|
|
// instance_id; the server resolves the owning account from the instance and
|
|
// authorises private plugins owned by that account. No end-user JWT is
|
|
// required (the calling instance, not a user, is the trust unit).
|
|
rpc ListPrivatePluginsForInstance(ListPrivatePluginsForInstanceRequest) returns (ListPrivatePluginsResponse);
|
|
rpc ResolveInstallForInstance(ResolveInstallForInstanceRequest) returns (ResolveInstallResponse);
|
|
}
|
|
|
|
// PluginModerationService is the superadmin-only side: lists pending submissions
|
|
// and decides them. The lifecycle is: scope member CreatePlugin → PublishVersion(s)
|
|
// → SubmitForReview → admin Approve / Reject / RequestChanges.
|
|
service PluginModerationService {
|
|
rpc ListPendingReviews(ListPendingReviewsRequest) returns (ListPendingReviewsResponse);
|
|
rpc ApproveSubmission(ApproveSubmissionRequest) returns (ApproveSubmissionResponse);
|
|
rpc RejectSubmission(RejectSubmissionRequest) returns (RejectSubmissionResponse);
|
|
rpc RequestChanges(RequestChangesRequest) returns (RequestChangesResponse);
|
|
|
|
// Star-review moderation (ADR 0021). Unrelated to the submission queue
|
|
// above: these act on user reviews (PluginReviewService), not plugins.
|
|
rpc ListFlaggedReviews(ListFlaggedReviewsRequest) returns (ListFlaggedReviewsResponse);
|
|
rpc SoftDeleteReview(SoftDeleteReviewRequest) returns (SoftDeleteReviewResponse);
|
|
rpc SoftDeleteReply(SoftDeleteReplyRequest) returns (SoftDeleteReplyResponse);
|
|
}
|
|
|
|
// PluginGalleryService manages a plugin's screenshot gallery. Reads are
|
|
// public; writes require scope (or owning-account) membership, enforced
|
|
// in-handler.
|
|
service PluginGalleryService {
|
|
rpc ListScreenshots(ListScreenshotsRequest) returns (ListScreenshotsResponse);
|
|
rpc UploadScreenshot(UploadScreenshotRequest) returns (UploadScreenshotResponse);
|
|
rpc DeleteScreenshot(DeleteScreenshotRequest) returns (DeleteScreenshotResponse);
|
|
rpc ReorderScreenshots(ReorderScreenshotsRequest) returns (ReorderScreenshotsResponse);
|
|
rpc SetFeaturedScreenshot(SetFeaturedScreenshotRequest) returns (SetFeaturedScreenshotResponse);
|
|
}
|
|
|
|
// PluginReviewService is the star-review surface for PUBLIC plugins
|
|
// (ADR 0021): verified-install gating, one review per user, author replies,
|
|
// user flags. Reads are public; writes require a logged-in orchestrator user.
|
|
service PluginReviewService {
|
|
rpc ListReviews(ListReviewsRequest) returns (ListReviewsResponse);
|
|
rpc GetMyReview(GetMyReviewRequest) returns (GetMyReviewResponse);
|
|
rpc UpsertReview(UpsertReviewRequest) returns (UpsertReviewResponse);
|
|
rpc DeleteReview(DeleteReviewRequest) returns (DeleteReviewResponse);
|
|
rpc ReplyToReview(ReplyToReviewRequest) returns (ReplyToReviewResponse);
|
|
rpc DeleteReply(DeleteReplyRequest) returns (DeleteReplyResponse);
|
|
rpc FlagReview(FlagReviewRequest) returns (FlagReviewResponse);
|
|
}
|
|
|
|
// PluginPublishService is called by the ninja CLI to publish a version.
|
|
service PluginPublishService {
|
|
rpc PublishVersion(PublishVersionRequest) returns (PublishVersionResponse);
|
|
}
|
|
|
|
// PluginAuthService handles OAuth device flow used by ninja CLI.
|
|
service PluginAuthService {
|
|
rpc StartDevice(StartDeviceRequest) returns (StartDeviceResponse);
|
|
rpc PollDevice(PollDeviceRequest) returns (PollDeviceResponse);
|
|
rpc ApproveDevice(ApproveDeviceRequest) returns (ApproveDeviceResponse);
|
|
rpc DenyDevice(DenyDeviceRequest) returns (DenyDeviceResponse);
|
|
rpc GetDeviceStatus(GetDeviceStatusRequest) returns (GetDeviceStatusResponse);
|
|
rpc Whoami(WhoamiRequest) returns (WhoamiResponse);
|
|
// ListMyAccounts is the CLI-facing account picker, returning the bare
|
|
// minimum needed for `ninja login` / `ninja account list`. The CLI in
|
|
// core/cmd/ninja consumes only this proto file; pulling in accounts.proto
|
|
// and AccountService would force core to also generate bindings for the
|
|
// full Account message and collide with the orchestrator's own copy at
|
|
// proto-registration time.
|
|
rpc ListMyAccountsForCLI(ListMyAccountsForCLIRequest) returns (ListMyAccountsForCLIResponse);
|
|
}
|
|
|
|
// --- Shared messages ---
|
|
|
|
message Scope {
|
|
string id = 1;
|
|
string slug = 2;
|
|
string display_name = 3;
|
|
google.protobuf.Timestamp created_at = 4;
|
|
}
|
|
|
|
// PluginVisibility is the lifecycle/access state of a plugin in the registry.
|
|
// PRIVATE plugins are scoped to a single account; PUBLIC plugins are visible
|
|
// to all. UNDER_REVIEW / REJECTED / TAKEN_DOWN are public-registry moderation
|
|
// states and do not apply to private plugins.
|
|
enum PluginVisibility {
|
|
PLUGIN_VISIBILITY_UNSPECIFIED = 0;
|
|
PLUGIN_VISIBILITY_PRIVATE = 1;
|
|
PLUGIN_VISIBILITY_UNDER_REVIEW = 2;
|
|
PLUGIN_VISIBILITY_PUBLIC = 3;
|
|
PLUGIN_VISIBILITY_REJECTED = 4;
|
|
PLUGIN_VISIBILITY_TAKEN_DOWN = 5;
|
|
}
|
|
|
|
message Plugin {
|
|
string id = 1;
|
|
string scope_slug = 2;
|
|
string name = 3;
|
|
PluginVisibility visibility = 4;
|
|
bool premium = 5;
|
|
string description = 6;
|
|
string homepage_url = 7;
|
|
repeated string categories = 8;
|
|
google.protobuf.Timestamp updated_at = 9;
|
|
string kind = 10;
|
|
string display_name = 11;
|
|
// owner_account_id is set for private plugins and identifies the account
|
|
// that owns the plugin. Empty for public plugins.
|
|
string owner_account_id = 12;
|
|
// latest_version is the most recent non-yanked version string published
|
|
// for this plugin (e.g. "0.2.3"). Empty when the plugin has no versions
|
|
// yet. Computed server-side as a LATERAL lookup on registry_versions so
|
|
// listing endpoints don't N+1.
|
|
string latest_version = 13;
|
|
repeated string tags = 14;
|
|
// preview_image_url is the PUBLIC, unsigned, long-lived image URL for this
|
|
// plugin's card image, usable directly as an <img src>: the gallery's
|
|
// Featured screenshot when one exists, else the latest non-yanked version's
|
|
// preview.png. Empty when neither exists — the wizard card then degrades to
|
|
// display_name + tags (spec §5.2).
|
|
string preview_image_url = 15;
|
|
// rating_avg/rating_count are the denormalised star-review aggregate over
|
|
// non-deleted reviews (0/0 when unreviewed).
|
|
float rating_avg = 16;
|
|
int32 rating_count = 17;
|
|
// install_count is the number of distinct CMS instances currently recorded
|
|
// as installers of this plugin (registry_install_events grain).
|
|
int32 install_count = 18;
|
|
}
|
|
|
|
// PluginScreenshot is one image in a plugin's managed gallery. The gallery is
|
|
// per-plugin (not per-version); images arrive either from a published
|
|
// artifact's screenshots/ dir or from author uploads, and the author orders
|
|
// them and marks one Featured (the card image).
|
|
message PluginScreenshot {
|
|
string id = 1;
|
|
// url is the public unsigned image URL (only public plugins serve gallery
|
|
// images in v1).
|
|
string url = 2;
|
|
bool featured = 3;
|
|
int32 position = 4;
|
|
string content_type = 5;
|
|
}
|
|
|
|
message Category {
|
|
string slug = 1;
|
|
string display_name = 2;
|
|
string description = 3;
|
|
int32 sort_order = 4;
|
|
}
|
|
|
|
message Version {
|
|
string id = 1;
|
|
string plugin_id = 2;
|
|
string version = 3;
|
|
google.protobuf.Timestamp published_at = 6;
|
|
bool yanked = 7;
|
|
string sdk_constraint = 8;
|
|
string source_archive_sha256 = 9;
|
|
int64 source_archive_size = 10;
|
|
// preview_image_url is the public unsigned URL of this specific version's
|
|
// preview PNG, or empty when this version shipped no preview.png.
|
|
string preview_image_url = 11;
|
|
// abi_version is the wasm ABI major version this artifact was built against
|
|
// (WO-WZ-011). 0 means a legacy source archive (compiled in-container), not a
|
|
// .bnp. Instances filter incompatible artifacts on this before downloading.
|
|
uint32 abi_version = 12;
|
|
}
|
|
|
|
message Requirement {
|
|
string scope_slug = 1;
|
|
string name = 2;
|
|
string constraint_expr = 3;
|
|
}
|
|
|
|
// --- Scope service ---
|
|
|
|
message CreateScopeRequest { string slug = 1; string display_name = 2; }
|
|
message CreateScopeResponse { Scope scope = 1; }
|
|
message ListMyScopesRequest {}
|
|
message ListMyScopesResponse { repeated Scope scopes = 1; }
|
|
message GetScopeRequest { string slug = 1; }
|
|
message GetScopeResponse { Scope scope = 1; repeated Plugin plugins = 2; }
|
|
message ListMyPluginsRequest {}
|
|
message ListMyPluginsResponse { repeated Plugin plugins = 1; }
|
|
|
|
// --- Registry service ---
|
|
|
|
message CreatePluginRequest {
|
|
string scope_slug = 1;
|
|
string name = 2;
|
|
string description = 3;
|
|
string kind = 4;
|
|
repeated string categories = 5;
|
|
string display_name = 6;
|
|
// visibility is the requested visibility of the plugin. UNSPECIFIED defers
|
|
// to the registry's default (public for explicit scopes, private for the
|
|
// "@private" sentinel).
|
|
PluginVisibility visibility = 7;
|
|
// active_account_id is required when visibility = PRIVATE; the server
|
|
// verifies the caller is a member of this account before creating the
|
|
// private plugin under it.
|
|
string active_account_id = 8;
|
|
}
|
|
message CreatePluginResponse {
|
|
Plugin plugin = 1;
|
|
}
|
|
|
|
message GetPluginRequest {
|
|
string scope_slug = 1;
|
|
string name = 2;
|
|
// active_account_id is required when scope_slug = "private" so the server
|
|
// can resolve (account_id, name) instead of (scope_id, name). Ignored
|
|
// otherwise.
|
|
string active_account_id = 3;
|
|
}
|
|
message GetPluginResponse {
|
|
Plugin plugin = 1;
|
|
repeated Version versions = 2;
|
|
map<string,string> channels = 3;
|
|
// screenshots is the plugin's gallery in display order (Featured included).
|
|
repeated PluginScreenshot screenshots = 4;
|
|
}
|
|
|
|
message ListPluginsRequest {
|
|
int32 limit = 1;
|
|
int32 offset = 2;
|
|
string query = 3;
|
|
string kind = 4;
|
|
repeated string categories = 5;
|
|
repeated string tags = 6;
|
|
}
|
|
message ListPluginsResponse { repeated Plugin plugins = 1; }
|
|
|
|
message ListCategoriesRequest {}
|
|
message ListCategoriesResponse { repeated Category categories = 1; }
|
|
|
|
message ListTagsRequest {
|
|
// Optional kind filter: "plugin", "theme", or "" for both. Lets the browse
|
|
// UI show theme-specific tags when the user filters by kind=theme.
|
|
string kind = 1;
|
|
// Cap the response. 0 → server default (100).
|
|
int32 limit = 2;
|
|
}
|
|
message ListTagsResponse {
|
|
repeated TagCount tags = 1;
|
|
}
|
|
message TagCount {
|
|
string tag = 1;
|
|
int32 count = 2;
|
|
}
|
|
|
|
// --- Private plugin RPC messages ---
|
|
|
|
message ListPrivatePluginsRequest {
|
|
// account_id selects which account's private plugins to list. The caller
|
|
// must be a member of this account.
|
|
string account_id = 1;
|
|
}
|
|
message ListPrivatePluginsResponse {
|
|
repeated PrivatePluginSummary plugins = 1;
|
|
}
|
|
|
|
// PrivatePluginSummary is the row shape used by the publisher dashboard and
|
|
// by the CMS "Private" installer tab. It bundles a Plugin with the latest
|
|
// version per channel and an installation count.
|
|
message PrivatePluginSummary {
|
|
Plugin plugin = 1;
|
|
// channel_versions maps channel name -> latest version string published on
|
|
// that channel (e.g. "latest" -> "0.3.0", "beta" -> "0.4.0-beta.1").
|
|
map<string,string> channel_versions = 2;
|
|
// installed_site_count is the number of CMS sites in the owning account
|
|
// that currently have any version of this plugin installed. Used to gate
|
|
// delete-plugin in the dashboard.
|
|
int32 installed_site_count = 3;
|
|
}
|
|
|
|
message DeletePrivatePluginRequest {
|
|
string account_id = 1;
|
|
string plugin_name = 2;
|
|
}
|
|
message DeletePrivatePluginResponse {}
|
|
|
|
message DeletePrivatePluginVersionRequest {
|
|
string account_id = 1;
|
|
string plugin_name = 2;
|
|
string version = 3;
|
|
}
|
|
message DeletePrivatePluginVersionResponse {}
|
|
|
|
message ListPrivatePluginInstallSitesRequest {
|
|
string account_id = 1;
|
|
string plugin_name = 2;
|
|
}
|
|
message ListPrivatePluginInstallSitesResponse {
|
|
repeated PrivatePluginInstallSite sites = 1;
|
|
}
|
|
message PrivatePluginInstallSite {
|
|
string instance_id = 1;
|
|
string site_display_name = 2;
|
|
string installed_version = 3;
|
|
string pinned_channel = 4;
|
|
}
|
|
|
|
message GetVersionRequest {
|
|
string scope_slug = 1;
|
|
string plugin_name = 2;
|
|
string version = 3;
|
|
}
|
|
message GetVersionResponse {
|
|
Version version = 1;
|
|
repeated Requirement requires = 2;
|
|
string readme_md = 3;
|
|
string changelog_md = 4;
|
|
}
|
|
|
|
message ResolveInstallRequest {
|
|
string scope_slug = 1;
|
|
string plugin_name = 2;
|
|
string version_or_channel = 3;
|
|
// active_account_id is required when scope_slug = "private" so the server
|
|
// resolves (account_id, name) and verifies the caller's account
|
|
// membership. Ignored for public scopes.
|
|
string active_account_id = 4;
|
|
}
|
|
message ResolveInstallResponse {
|
|
string version_id = 1;
|
|
string scope_slug = 2;
|
|
string plugin_name = 3;
|
|
string version = 4;
|
|
string channel_pinned = 5;
|
|
string archive_url = 6;
|
|
string archive_sha256 = 7;
|
|
string sdk_constraint = 8;
|
|
repeated Requirement requires = 9;
|
|
bool yanked = 10;
|
|
repeated string warnings = 11;
|
|
// abi_version is the wasm ABI major of the resolved artifact (WO-WZ-011); 0
|
|
// for a legacy source archive. The installing instance rejects an artifact
|
|
// whose abi_version its loader cannot run.
|
|
uint32 abi_version = 12;
|
|
}
|
|
|
|
// --- Instance-authenticated request shapes ---
|
|
// The instance proves its identity with the per-account X-CMS-Secret header;
|
|
// instance_id selects the instance whose owning account scopes the call. No
|
|
// active_account_id / user JWT is needed. Listing covers only private plugins
|
|
// (public plugins use the anonymous ListPlugins); resolve accepts BOTH — the
|
|
// instance identity is what lets the registry record the install event, which
|
|
// backs public install counts and verified-install review gating (ADR 0021).
|
|
|
|
message ListPrivatePluginsForInstanceRequest {
|
|
string instance_id = 1;
|
|
}
|
|
|
|
message ResolveInstallForInstanceRequest {
|
|
string instance_id = 1;
|
|
string plugin_name = 2;
|
|
string version_or_channel = 3;
|
|
// scope_slug selects the plugin's scope. Empty or "private" resolves the
|
|
// calling account's private plugin (the original behavior); any other value
|
|
// resolves a PUBLIC plugin in that scope (ADR 0021).
|
|
string scope_slug = 4;
|
|
}
|
|
|
|
// --- Review / moderation ---
|
|
|
|
message PendingReview {
|
|
string review_id = 1;
|
|
string plugin_id = 2;
|
|
string scope_slug = 3;
|
|
string plugin_name = 4;
|
|
string plugin_display_name = 5;
|
|
string submitted_by_user_id = 6;
|
|
google.protobuf.Timestamp submitted_at = 7;
|
|
}
|
|
|
|
message SubmitForReviewRequest {
|
|
string plugin_id = 1;
|
|
}
|
|
message SubmitForReviewResponse {
|
|
// visibility is "under_review" for non-admin authors, "public" when a superadmin
|
|
// submits (they bypass the queue entirely). String form is preserved here for
|
|
// backwards compatibility with existing handlers; the canonical enum is
|
|
// PluginVisibility on the Plugin message.
|
|
string visibility = 1;
|
|
}
|
|
|
|
message ListPendingReviewsRequest {
|
|
int32 limit = 1;
|
|
int32 offset = 2;
|
|
}
|
|
message ListPendingReviewsResponse {
|
|
repeated PendingReview reviews = 1;
|
|
}
|
|
|
|
message ApproveSubmissionRequest {
|
|
string plugin_id = 1;
|
|
string reason = 2; // optional note for audit log
|
|
}
|
|
message ApproveSubmissionResponse {}
|
|
|
|
message RejectSubmissionRequest {
|
|
string plugin_id = 1;
|
|
string reason = 2; // required
|
|
}
|
|
message RejectSubmissionResponse {}
|
|
|
|
message RequestChangesRequest {
|
|
string plugin_id = 1;
|
|
string reason = 2; // required — author sees this verbatim
|
|
}
|
|
message RequestChangesResponse {}
|
|
|
|
// --- Gallery service ---
|
|
|
|
message ListScreenshotsRequest {
|
|
string scope_slug = 1;
|
|
string plugin_name = 2;
|
|
}
|
|
message ListScreenshotsResponse {
|
|
repeated PluginScreenshot screenshots = 1;
|
|
}
|
|
|
|
message UploadScreenshotRequest {
|
|
string plugin_id = 1;
|
|
// image is the raw file bytes (≤ 8 MiB; png/jpg/jpeg/webp).
|
|
bytes image = 2;
|
|
string filename = 3;
|
|
string content_type = 4;
|
|
}
|
|
message UploadScreenshotResponse {
|
|
PluginScreenshot screenshot = 1;
|
|
}
|
|
|
|
message DeleteScreenshotRequest {
|
|
string plugin_id = 1;
|
|
string screenshot_id = 2;
|
|
}
|
|
message DeleteScreenshotResponse {}
|
|
|
|
message ReorderScreenshotsRequest {
|
|
string plugin_id = 1;
|
|
// screenshot_ids_in_order must name every current gallery image exactly once.
|
|
repeated string screenshot_ids_in_order = 2;
|
|
}
|
|
message ReorderScreenshotsResponse {
|
|
repeated PluginScreenshot screenshots = 1;
|
|
}
|
|
|
|
message SetFeaturedScreenshotRequest {
|
|
string plugin_id = 1;
|
|
string screenshot_id = 2;
|
|
}
|
|
message SetFeaturedScreenshotResponse {}
|
|
|
|
// --- Star reviews (ADR 0021) ---
|
|
|
|
message PluginReview {
|
|
string id = 1;
|
|
string plugin_id = 2;
|
|
string user_id = 3;
|
|
string author_display_name = 4;
|
|
int32 rating = 5; // 1..5
|
|
string title = 6;
|
|
string body = 7;
|
|
// version_at_review is the plugin version the reviewer's account had
|
|
// installed when the review was written (best effort; may be empty).
|
|
string version_at_review = 8;
|
|
bool edited = 9;
|
|
google.protobuf.Timestamp created_at = 10;
|
|
google.protobuf.Timestamp updated_at = 11;
|
|
PluginReviewReply reply = 12; // unset when no (non-deleted) reply exists
|
|
// deleted/deleted_reason are populated only in moderation views; public
|
|
// listings exclude soft-deleted reviews entirely.
|
|
bool deleted = 13;
|
|
string deleted_reason = 14;
|
|
}
|
|
|
|
message PluginReviewReply {
|
|
string id = 1;
|
|
string author_display_name = 2;
|
|
string body = 3;
|
|
bool edited = 4;
|
|
google.protobuf.Timestamp created_at = 5;
|
|
}
|
|
|
|
message ReviewAggregate {
|
|
float rating_avg = 1;
|
|
int32 rating_count = 2;
|
|
// count1..count5 are the per-star histogram buckets.
|
|
int32 count1 = 3;
|
|
int32 count2 = 4;
|
|
int32 count3 = 5;
|
|
int32 count4 = 6;
|
|
int32 count5 = 7;
|
|
}
|
|
|
|
message ListReviewsRequest {
|
|
string scope_slug = 1;
|
|
string plugin_name = 2;
|
|
int32 limit = 3; // 0 → server default
|
|
int32 offset = 4;
|
|
}
|
|
message ListReviewsResponse {
|
|
repeated PluginReview reviews = 1;
|
|
ReviewAggregate aggregate = 2;
|
|
}
|
|
|
|
message GetMyReviewRequest {
|
|
string plugin_id = 1;
|
|
}
|
|
message GetMyReviewResponse {
|
|
PluginReview review = 1; // unset when the caller has no review
|
|
// eligible reports whether the caller may write a review (verified install
|
|
// on their account, not a scope member). The form UI keys off this.
|
|
bool eligible = 2;
|
|
// ineligible_reason is a human-readable explanation when eligible = false
|
|
// ("no install on your account", "authors cannot review their own plugin").
|
|
string ineligible_reason = 3;
|
|
}
|
|
|
|
message UpsertReviewRequest {
|
|
string plugin_id = 1;
|
|
int32 rating = 2; // 1..5, required
|
|
string title = 3;
|
|
string body = 4;
|
|
}
|
|
message UpsertReviewResponse {
|
|
PluginReview review = 1;
|
|
}
|
|
|
|
message DeleteReviewRequest {
|
|
string plugin_id = 1;
|
|
}
|
|
message DeleteReviewResponse {}
|
|
|
|
message ReplyToReviewRequest {
|
|
string review_id = 1;
|
|
string body = 2;
|
|
}
|
|
message ReplyToReviewResponse {
|
|
PluginReviewReply reply = 1;
|
|
}
|
|
|
|
message DeleteReplyRequest {
|
|
string review_id = 1;
|
|
}
|
|
message DeleteReplyResponse {}
|
|
|
|
message FlagReviewRequest {
|
|
string review_id = 1;
|
|
string reason = 2; // required
|
|
}
|
|
message FlagReviewResponse {}
|
|
|
|
// --- Review moderation (superadmin) ---
|
|
|
|
message FlaggedReview {
|
|
PluginReview review = 1;
|
|
string scope_slug = 2;
|
|
string plugin_name = 3;
|
|
int32 flag_count = 4;
|
|
// flag_reasons carries the open flags' reasons, newest first.
|
|
repeated string flag_reasons = 5;
|
|
google.protobuf.Timestamp first_flagged_at = 6;
|
|
}
|
|
|
|
message ListFlaggedReviewsRequest {
|
|
int32 limit = 1;
|
|
int32 offset = 2;
|
|
}
|
|
message ListFlaggedReviewsResponse {
|
|
repeated FlaggedReview reviews = 1;
|
|
}
|
|
|
|
message SoftDeleteReviewRequest {
|
|
string review_id = 1;
|
|
string reason = 2; // required — audit trail
|
|
}
|
|
message SoftDeleteReviewResponse {}
|
|
|
|
message SoftDeleteReplyRequest {
|
|
string review_id = 1;
|
|
string reason = 2; // required — audit trail
|
|
}
|
|
message SoftDeleteReplyResponse {}
|
|
|
|
// --- Publish service ---
|
|
|
|
message PublishVersionRequest {
|
|
string plugin_id = 1;
|
|
string version = 2;
|
|
string channel = 3;
|
|
bytes archive = 4;
|
|
string readme_md = 5;
|
|
string changelog_md = 6;
|
|
}
|
|
message PublishVersionResponse {
|
|
Version version = 1;
|
|
string archive_url = 2;
|
|
repeated string warnings = 3;
|
|
}
|
|
|
|
// --- Auth service (device flow) ---
|
|
|
|
message StartDeviceRequest { repeated string scopes = 1; }
|
|
message StartDeviceResponse {
|
|
string device_code = 1;
|
|
string user_code = 2;
|
|
string verification_uri = 3;
|
|
int32 interval_seconds = 4;
|
|
int32 expires_in_seconds = 5;
|
|
}
|
|
|
|
message PollDeviceRequest { string device_code = 1; }
|
|
message PollDeviceResponse {
|
|
string access_token = 1;
|
|
string status = 2;
|
|
string account_id = 3;
|
|
string account_slug = 4;
|
|
}
|
|
|
|
message ApproveDeviceRequest {
|
|
string user_code = 1;
|
|
string account_id = 2;
|
|
}
|
|
message ApproveDeviceResponse {}
|
|
|
|
message DenyDeviceRequest { string user_code = 1; }
|
|
message DenyDeviceResponse {}
|
|
|
|
message GetDeviceStatusRequest { string user_code = 1; }
|
|
message GetDeviceStatusResponse {
|
|
bool valid = 1;
|
|
bool authorized = 2;
|
|
bool denied = 3;
|
|
string client_id = 4;
|
|
}
|
|
|
|
message WhoamiRequest {}
|
|
message WhoamiResponse {
|
|
string user_id = 1;
|
|
string email = 2;
|
|
string display_name = 3;
|
|
}
|
|
|
|
// MyAccount is the slim account row returned to the CLI by
|
|
// PluginAuthService.ListMyAccounts. The orchestrator's full Account message
|
|
// lives in accounts.proto and carries billing, plan, status, etc.; CLI
|
|
// account-switching needs none of that, so this shape stays minimal and
|
|
// avoids forcing core to generate bindings for accounts.proto.
|
|
message MyAccount {
|
|
string id = 1;
|
|
string slug = 2;
|
|
string name = 3;
|
|
}
|
|
|
|
message ListMyAccountsForCLIRequest {}
|
|
message ListMyAccountsForCLIResponse {
|
|
repeated MyAccount accounts = 1;
|
|
}
|