commit 2f065e3ed4a82fec2806279886aab8b2038c434c Author: Alex Dunmow Date: Sat Jul 4 22:39:32 2026 +0800 feat: ninja CLI in its own repo (block/cli, WO-WZ-023) The ninja developer CLI moves out of block/core into its own module git.dev.alexdunmow.com/block/cli. Pins block/core@v0.18.2 for abi/v1, the plugin.mod parser, and DESCRIBE guest builds; vendors its own orchestrator registry client (internal/api, generated from a vendored plugin_registry.proto) since it cannot import block/core/internal. Produces byte-identical v1 artifacts (verified: art-deco codeless SHA256-identical to the pre-move build). Co-Authored-By: Claude Opus 4.8 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e4dd382 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +.PHONY: build install test proto tidy safety-check + +# Build the ninja binary into ./bin/ninja. +build: + go build -o bin/ninja ./cmd/ninja + +# Install ninja onto $GOBIN/$GOPATH/bin (yields `ninja` on PATH). +install: + go install ./cmd/ninja + +# Run the full test suite (compiles guest wasm fixtures; needs the Go +# toolchain with GOOS=wasip1 support and network access to the block/core +# module proxy for the pinned guest SDK). +test: + go test ./... + +# Regenerate the vendored orchestrator client from proto/. +# Requires buf + protoc-gen-go + protoc-gen-connect-go on PATH. +proto: + buf generate --path proto/orchestrator/v1/plugin_registry.proto + +tidy: + go mod tidy + +# Static safety gate (shared workspace tool). +safety-check: + cd ../check-safety && go run . ../cli diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..a825372 --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,18 @@ +version: v2 +# Managed mode overrides the vendored proto's go_package so the generated Go +# lands in this module (git.dev.alexdunmow.com/block/cli/internal/api/...), +# not the CMS/orchestrator package the proto file nominally declares. +managed: + enabled: true + override: + - file_option: go_package_prefix + value: git.dev.alexdunmow.com/block/cli/internal/api +plugins: + - local: protoc-gen-go + out: internal/api + opt: + - paths=source_relative + - local: protoc-gen-connect-go + out: internal/api + opt: + - paths=source_relative diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..2da2a4a --- /dev/null +++ b/buf.yaml @@ -0,0 +1,17 @@ +version: v2 +# Minimal buf module for the ninja CLI. We vendor a single proto — +# orchestrator/v1/plugin_registry.proto — because the CLI only needs the +# plugin registry/publish/auth/scope client to talk to the orchestrator. The +# rest of orchestrator/v1 is generated by the orchestrator and CMS from their +# own copies; the CLI never registers those descriptors. +modules: + - path: proto +lint: + use: + - STANDARD + except: + - PACKAGE_SAME_GO_PACKAGE + - RPC_REQUEST_RESPONSE_UNIQUE +breaking: + use: + - FILE diff --git a/cmd/ninja/cmd/abi.go b/cmd/ninja/cmd/abi.go new file mode 100644 index 0000000..bd47c72 --- /dev/null +++ b/cmd/ninja/cmd/abi.go @@ -0,0 +1,137 @@ +package cmd + +import ( + "archive/tar" + "compress/gzip" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strings" + + "git.dev.alexdunmow.com/block/cli/internal/creds" + "github.com/spf13/cobra" +) + +// newAbiCmd is the `ninja abi` group: fetch the versioned wasm-plugin ABI +// contract (proto schema + calling-convention docs) the registry publishes, so +// any language can codegen against it (WO-WZ-023). +func newAbiCmd() *cobra.Command { + c := &cobra.Command{ + Use: "abi", + Short: "Work with the wasm-plugin ABI contract", + Long: "Fetch the versioned ABI contract (proto schema + docs) published by the registry.", + } + c.AddCommand(newAbiPullCmd()) + return c +} + +func newAbiPullCmd() *cobra.Command { + var version string + var outDir string + cmd := &cobra.Command{ + Use: "pull", + Short: "Download and extract the ABI contract tarball from the registry", + Long: "Fetches /registry/abi//contract.tar.gz (public, no login\n" + + "required) and extracts the proto schema + docs into a directory.", + RunE: func(c *cobra.Command, _ []string) error { + hostFlag, _ := c.Flags().GetString("host") + host := resolveContractHost(hostFlag) + + if !strings.HasPrefix(version, "v") { + version = "v" + version + } + url := strings.TrimRight(host, "/") + "/registry/abi/" + version + "/contract.tar.gz" + + req, err := http.NewRequestWithContext(c.Context(), http.MethodGet, url, nil) + if err != nil { + return err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("fetch contract: %w", err) + } + defer func() { _ = resp.Body.Close() }() + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(io.LimitReader(resp.Body, 512)) + return fmt.Errorf("registry returned %s for %s: %s", resp.Status, url, strings.TrimSpace(string(body))) + } + + if outDir == "" { + outDir = filepath.Join("abi-contract", version) + } + n, err := extractContract(resp.Body, outDir) + if err != nil { + return err + } + fmt.Printf("Pulled ABI contract %s → %s (%d files)\n", version, outDir, n) + return nil + }, + } + cmd.Flags().StringVar(&version, "version", "v1", "ABI contract version to pull (e.g. v1)") + cmd.Flags().StringVarP(&outDir, "output", "o", "", "output directory (default: abi-contract/)") + return cmd +} + +// resolveContractHost resolves the registry host for a public (unauthenticated) +// download: the --host flag wins, else the logged-in default host, else the +// production registry. It never errors on a missing token — the contract route +// is public. +func resolveContractHost(hostFlag string) string { + if hostFlag != "" { + return hostFlag + } + if cr, err := creds.Load(); err == nil && cr.DefaultHost != "" { + return cr.DefaultHost + } + return "https://my.blockninjacms.com" +} + +// extractContract untars a gzip stream into dir, refusing any entry that would +// escape the destination root. Returns the number of files written. +func extractContract(r io.Reader, dir string) (int, error) { + if err := os.MkdirAll(dir, 0o755); err != nil { + return 0, err + } + gz, err := gzip.NewReader(r) + if err != nil { + return 0, fmt.Errorf("open gzip: %w", err) + } + defer func() { _ = gz.Close() }() + tr := tar.NewReader(gz) + var count int + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return count, fmt.Errorf("read tar: %w", err) + } + if hdr.Typeflag != tar.TypeReg { + continue + } + clean := filepath.Clean(hdr.Name) + if !filepath.IsLocal(clean) { + return count, fmt.Errorf("refusing unsafe tar path %q", hdr.Name) + } + dest := filepath.Join(dir, clean) + if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { + return count, err + } + f, err := os.OpenFile(dest, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644) + if err != nil { + return count, err + } + if _, err := io.Copy(f, tr); err != nil { //nolint:gosec // size-bounded contract tarball from our own registry + _ = f.Close() + return count, err + } + if err := f.Close(); err != nil { + return count, err + } + count++ + } + return count, nil +} diff --git a/cmd/ninja/cmd/account.go b/cmd/ninja/cmd/account.go new file mode 100644 index 0000000..5b5a119 --- /dev/null +++ b/cmd/ninja/cmd/account.go @@ -0,0 +1,150 @@ +package cmd + +import ( + "bufio" + "context" + "fmt" + "strconv" + "strings" + + "connectrpc.com/connect" + "github.com/spf13/cobra" + + "git.dev.alexdunmow.com/block/cli/internal/creds" + "git.dev.alexdunmow.com/block/cli/internal/orchclient" + v1 "git.dev.alexdunmow.com/block/cli/internal/api/orchestrator/v1" +) + +func newAccountCmd() *cobra.Command { + c := &cobra.Command{ + Use: "account", + Short: "Manage which account ninja acts as", + Long: `Account-scoped commands like ` + "`ninja plugins publish --private`" + ` act +against an "active account" — the orchestrator-side account whose members +can see and install the plugin. The active account is selected at +` + "`ninja login`" + ` time and persisted in your credentials file.`, + } + c.AddCommand(newAccountListCmd(), newAccountSetCmd(), newAccountShowCmd()) + return c +} + +func newAccountListCmd() *cobra.Command { + return &cobra.Command{ + Use: "list", + Short: "List the accounts the authenticated user belongs to", + RunE: func(c *cobra.Command, _ []string) error { + cli, _, _, hc, err := resolveClient(c) + if err != nil { + return err + } + accts, err := cli.Auth.ListMyAccountsForCLI(context.Background(), + connect.NewRequest(&v1.ListMyAccountsForCLIRequest{})) + if err != nil { + return fmt.Errorf("list accounts: %w", err) + } + if len(accts.Msg.Accounts) == 0 { + fmt.Println("No accounts.") + return nil + } + for _, a := range accts.Msg.Accounts { + marker := " " + if a.Id == hc.ActiveAccountID { + marker = "* " + } + fmt.Printf("%s%s — %s\n", marker, a.Slug, a.Name) + } + return nil + }, + } +} + +func newAccountSetCmd() *cobra.Command { + return &cobra.Command{ + Use: "set ", + Short: "Change the active account", + Args: cobra.ExactArgs(1), + RunE: func(c *cobra.Command, args []string) error { + cli, cr, host, hc, err := resolveClient(c) + if err != nil { + return err + } + slug := strings.TrimPrefix(args[0], "@") + accts, err := cli.Auth.ListMyAccountsForCLI(context.Background(), + connect.NewRequest(&v1.ListMyAccountsForCLIRequest{})) + if err != nil { + return fmt.Errorf("list accounts: %w", err) + } + for _, a := range accts.Msg.Accounts { + if a.Slug == slug { + hc.ActiveAccountID = a.Id + hc.ActiveAccountSlug = a.Slug + cr.Hosts[host] = hc + if err := cr.Save(); err != nil { + return err + } + fmt.Printf("Active account: %s (%s)\n", a.Slug, a.Name) + return nil + } + } + return fmt.Errorf("account %q not found among your memberships; try `ninja account list`", slug) + }, + } +} + +func newAccountShowCmd() *cobra.Command { + return &cobra.Command{ + Use: "show", + Short: "Show the currently active account", + RunE: func(c *cobra.Command, _ []string) error { + _, _, _, hc, err := resolveClient(c) + if err != nil { + return err + } + if hc.ActiveAccountSlug == "" { + fmt.Println("(no active account set; run `ninja login` or `ninja account set `)") + return nil + } + fmt.Printf("Active account: %s (id=%s)\n", hc.ActiveAccountSlug, hc.ActiveAccountID) + return nil + }, + } +} + +// resolveClient is a small helper used by every `ninja account` subcommand: +// it loads creds, resolves the host, and returns an authed client plus the +// loaded credentials so the caller can persist changes. +func resolveClient(c *cobra.Command) (*orchclient.Client, *creds.Credentials, string, creds.HostCreds, error) { + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return nil, nil, "", creds.HostCreds{}, err + } + resolvedHost, hc, err := cr.Resolve(host) + if err != nil { + return nil, nil, "", creds.HostCreds{}, err + } + return orchclient.New(resolvedHost, hc.Token), cr, resolvedHost, hc, nil +} + +// pickAccountInteractive prompts the user to select an account by number from +// the given list and returns the chosen account. Used by `ninja login` when +// the user belongs to more than one account. +func pickAccountInteractive(scanner *bufio.Scanner, accounts []*v1.MyAccount) (*v1.MyAccount, error) { + if len(accounts) == 0 { + return nil, fmt.Errorf("no accounts available") + } + fmt.Println("Select an account:") + for i, a := range accounts { + fmt.Printf(" %d) %s — %s\n", i+1, a.Slug, a.Name) + } + fmt.Print("> ") + if !scanner.Scan() { + return nil, fmt.Errorf("cancelled") + } + v := strings.TrimSpace(scanner.Text()) + n, err := strconv.Atoi(v) + if err != nil || n < 1 || n > len(accounts) { + return nil, fmt.Errorf("invalid selection: %s", v) + } + return accounts[n-1], nil +} diff --git a/cmd/ninja/cmd/login.go b/cmd/ninja/cmd/login.go new file mode 100644 index 0000000..35d22d1 --- /dev/null +++ b/cmd/ninja/cmd/login.go @@ -0,0 +1,150 @@ +package cmd + +import ( + "bufio" + "context" + "fmt" + "os" + "time" + + "connectrpc.com/connect" + "github.com/spf13/cobra" + + "git.dev.alexdunmow.com/block/cli/internal/creds" + "git.dev.alexdunmow.com/block/cli/internal/orchclient" + v1 "git.dev.alexdunmow.com/block/cli/internal/api/orchestrator/v1" +) + +func newLoginCmd() *cobra.Command { + var host string + cmd := &cobra.Command{ + Use: "login", + Short: "Authenticate against the orchestrator using device flow", + RunE: func(c *cobra.Command, _ []string) error { + if host == "" { + host, _ = c.Flags().GetString("host") + } + if host == "" { + host = "https://my.blockninjacms.com" + } + cli := orchclient.New(host, "") + ctx := context.Background() + start, err := cli.Auth.StartDevice(ctx, connect.NewRequest(&v1.StartDeviceRequest{ + Scopes: []string{"plugin:read", "plugin:publish", "scope:admin"}, + })) + if err != nil { + return fmt.Errorf("start device: %w", err) + } + fmt.Printf("Visit %s?user_code=%s to authorize.\n", start.Msg.VerificationUri, start.Msg.UserCode) + interval := time.Duration(start.Msg.IntervalSeconds) * time.Second + deadline := time.Now().Add(time.Duration(start.Msg.ExpiresInSeconds) * time.Second) + for time.Now().Before(deadline) { + time.Sleep(interval) + poll, err := cli.Auth.PollDevice(ctx, connect.NewRequest(&v1.PollDeviceRequest{DeviceCode: start.Msg.DeviceCode})) + if err != nil { + return err + } + switch poll.Msg.Status { + case "pending": + continue + case "approved": + cr, err := creds.Load() + if err != nil { + return err + } + cr.DefaultHost = host + if cr.Hosts == nil { + cr.Hosts = map[string]creds.HostCreds{} + } + hc := creds.HostCreds{Token: poll.Msg.AccessToken} + authed := orchclient.New(host, hc.Token) + if err := selectActiveAccount(ctx, authed, &hc); err != nil { + return err + } + cr.Hosts[host] = hc + if err := cr.Save(); err != nil { + return err + } + fmt.Println("Logged in.") + return nil + case "expired": + return fmt.Errorf("device code expired; try again") + } + } + return fmt.Errorf("login timed out") + }, + } + cmd.Flags().StringVar(&host, "host", "", "Orchestrator base URL") + return cmd +} + +func newWhoamiCmd() *cobra.Command { + return &cobra.Command{ + Use: "whoami", + Short: "Show the currently logged-in user", + RunE: func(c *cobra.Command, _ []string) error { + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return err + } + resolvedHost, hc, err := cr.Resolve(host) + if err != nil { + return err + } + cli := orchclient.New(resolvedHost, hc.Token) + r, err := cli.Auth.Whoami(context.Background(), connect.NewRequest(&v1.WhoamiRequest{})) + if err != nil { + return err + } + fmt.Printf("%s <%s> at %s\n", r.Msg.DisplayName, r.Msg.Email, resolvedHost) + return nil + }, + } +} + +// selectActiveAccount fetches the user's accounts and writes the active one +// into hc. With 0 accounts it errors (the server contract guarantees every +// user has at least one). With 1 it auto-selects silently. With ≥2 it +// prompts interactively on stdin. +func selectActiveAccount(ctx context.Context, cli *orchclient.Client, hc *creds.HostCreds) error { + resp, err := cli.Auth.ListMyAccountsForCLI(ctx, connect.NewRequest(&v1.ListMyAccountsForCLIRequest{})) + if err != nil { + return fmt.Errorf("list accounts: %w", err) + } + accts := resp.Msg.Accounts + switch len(accts) { + case 0: + return fmt.Errorf("no accounts found for this user; contact support") + case 1: + hc.ActiveAccountID = accts[0].Id + hc.ActiveAccountSlug = accts[0].Slug + fmt.Printf("Active account: %s (%s)\n", accts[0].Slug, accts[0].Name) + return nil + } + chosen, err := pickAccountInteractive(bufio.NewScanner(os.Stdin), accts) + if err != nil { + return err + } + hc.ActiveAccountID = chosen.Id + hc.ActiveAccountSlug = chosen.Slug + fmt.Printf("Active account: %s (%s)\n", chosen.Slug, chosen.Name) + return nil +} + +func newLogoutCmd() *cobra.Command { + return &cobra.Command{ + Use: "logout", + Short: "Remove stored credentials", + RunE: func(c *cobra.Command, _ []string) error { + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return err + } + resolvedHost, _, _ := cr.Resolve(host) + delete(cr.Hosts, resolvedHost) + return cr.Save() + }, + } +} diff --git a/cmd/ninja/cmd/plugin.go b/cmd/ninja/cmd/plugin.go new file mode 100644 index 0000000..17ca513 --- /dev/null +++ b/cmd/ninja/cmd/plugin.go @@ -0,0 +1,1238 @@ +package cmd + +import ( + "bufio" + "context" + "fmt" + "io" + "os" + "os/exec" + "regexp" + "sort" + "strconv" + "strings" + + "connectrpc.com/connect" + "github.com/spf13/cobra" + + core "git.dev.alexdunmow.com/block/core/plugin" + + "git.dev.alexdunmow.com/block/cli/internal/archive" + "git.dev.alexdunmow.com/block/cli/internal/creds" + "git.dev.alexdunmow.com/block/cli/internal/orchclient" + v1 "git.dev.alexdunmow.com/block/cli/internal/api/orchestrator/v1" +) + +func newPluginCmd() *cobra.Command { + c := &cobra.Command{Use: "plugin", Short: "Manage BlockNinja plugins"} + c.AddCommand( + newPluginInitCmd(), + newPluginBuildCmd(), + newPluginVerifyCmd(), + newPluginPublishCmd(), + newPluginStatusCmd(), + newPluginListCmd(), + newPluginDeleteCmd(), + newPluginDeleteVersionCmd(), + newPluginBumpCmd(), + newPluginVersionCmd(), + newPluginTagsCmd(), + ) + return c +} + +func newPluginListCmd() *cobra.Command { + var publicOnly, privateOnly bool + cmd := &cobra.Command{ + Use: "list", + Short: "List your plugins, sectioned by public scope and active-account private namespace", + RunE: func(c *cobra.Command, _ []string) error { + cli, _, _, hc, err := resolveClient(c) + if err != nil { + return err + } + ctx := context.Background() + if !privateOnly { + printPublicSection(ctx, cli) + } + if !publicOnly { + printPrivateSection(ctx, cli, hc) + } + return nil + }, + } + cmd.Flags().BoolVar(&publicOnly, "public-only", false, "Only show plugins under public scopes") + cmd.Flags().BoolVar(&privateOnly, "private-only", false, "Only show plugins under the active account's @private namespace") + return cmd +} + +func printPublicSection(ctx context.Context, cli *orchclient.Client) { + fmt.Println("Public") + scopes, err := cli.Scope.ListMyScopes(ctx, connect.NewRequest(&v1.ListMyScopesRequest{})) + if err != nil { + fmt.Printf(" (error: %v)\n", err) + return + } + if len(scopes.Msg.Scopes) == 0 { + fmt.Println(" (no scopes — create one with `ninja scope create`)") + return + } + for _, s := range scopes.Msg.Scopes { + gs, err := cli.Scope.GetScope(ctx, connect.NewRequest(&v1.GetScopeRequest{Slug: s.Slug})) + if err != nil { + fmt.Printf(" @%s — (error: %v)\n", s.Slug, err) + continue + } + if len(gs.Msg.Plugins) == 0 { + fmt.Printf(" @%s — (no plugins)\n", s.Slug) + continue + } + for _, p := range gs.Msg.Plugins { + fmt.Printf(" @%s/%s [%s]\n", s.Slug, p.Name, visibilityLabel(p.Visibility)) + } + } +} + +func newPluginDeleteCmd() *cobra.Command { + var assumeYes bool + cmd := &cobra.Command{ + Use: "delete <@private/name|name>", + Short: "Delete a private plugin from your active account (all versions)", + Long: `Delete a private plugin owned by the active account, along with all its +versions. The server refuses to delete a plugin while any site in the +account has it installed; uninstall those first. + +Public plugins cannot be deleted via this command.`, + Args: cobra.ExactArgs(1), + RunE: func(c *cobra.Command, args []string) error { + cli, _, _, hc, err := resolveClient(c) + if err != nil { + return err + } + if hc.ActiveAccountID == "" { + return fmt.Errorf("no active account; run `ninja account set `") + } + name, err := parsePrivateCoord(args[0]) + if err != nil { + return err + } + if !assumeYes { + return fmt.Errorf("refusing to delete without --yes") + } + _, err = cli.Reg.DeletePrivatePlugin(context.Background(), + connect.NewRequest(&v1.DeletePrivatePluginRequest{ + AccountId: hc.ActiveAccountID, + PluginName: name, + })) + if err != nil { + return fmt.Errorf("delete: %w", err) + } + fmt.Printf("Deleted @%s/%s\n", core.PrivateScopeSlug, name) + return nil + }, + } + cmd.Flags().BoolVar(&assumeYes, "yes", false, "Confirm the destructive action") + return cmd +} + +func newPluginDeleteVersionCmd() *cobra.Command { + var assumeYes bool + var version string + cmd := &cobra.Command{ + Use: "delete-version <@private/name|name>", + Short: "Delete one version of a private plugin", + Args: cobra.ExactArgs(1), + RunE: func(c *cobra.Command, args []string) error { + cli, _, _, hc, err := resolveClient(c) + if err != nil { + return err + } + if hc.ActiveAccountID == "" { + return fmt.Errorf("no active account; run `ninja account set `") + } + if version == "" { + return fmt.Errorf("--version is required") + } + name, err := parsePrivateCoord(args[0]) + if err != nil { + return err + } + if !assumeYes { + return fmt.Errorf("refusing to delete without --yes") + } + _, err = cli.Reg.DeletePrivatePluginVersion(context.Background(), + connect.NewRequest(&v1.DeletePrivatePluginVersionRequest{ + AccountId: hc.ActiveAccountID, + PluginName: name, + Version: version, + })) + if err != nil { + return fmt.Errorf("delete-version: %w", err) + } + fmt.Printf("Deleted @%s/%s@%s\n", core.PrivateScopeSlug, name, version) + return nil + }, + } + cmd.Flags().BoolVar(&assumeYes, "yes", false, "Confirm the destructive action") + cmd.Flags().StringVar(&version, "version", "", "Version to delete (required)") + return cmd +} + +// visibilityLabel converts the proto PluginVisibility enum into the +// lowercase, user-facing form ("private", "public", ...). Kept here in the +// CLI rather than in core/plugin so that external consumers of core/plugin +// don't transitively pull in core/internal/api/... — every such pull-in +// races with the orchestrator's own generated bindings of the same proto +// file at proto-registration time. +func visibilityLabel(v v1.PluginVisibility) string { + switch v { + case v1.PluginVisibility_PLUGIN_VISIBILITY_PRIVATE: + return "private" + case v1.PluginVisibility_PLUGIN_VISIBILITY_UNDER_REVIEW: + return "under_review" + case v1.PluginVisibility_PLUGIN_VISIBILITY_PUBLIC, + v1.PluginVisibility_PLUGIN_VISIBILITY_UNSPECIFIED: + return "public" + case v1.PluginVisibility_PLUGIN_VISIBILITY_REJECTED: + return "rejected" + case v1.PluginVisibility_PLUGIN_VISIBILITY_TAKEN_DOWN: + return "taken_down" + } + return "unknown" +} + +// parsePrivateCoord accepts either a bare plugin name ("myplugin") or the +// canonical "@private/name" form and returns just the plugin name. Any other +// scope is rejected to make accidental deletion of public plugins impossible. +func parsePrivateCoord(s string) (string, error) { + s = strings.TrimSpace(s) + if !strings.HasPrefix(s, "@") { + return s, nil + } + rest := strings.TrimPrefix(s, "@") + before, after, ok := strings.Cut(rest, "/") + if !ok { + return "", fmt.Errorf("expected @%s/, got %q", core.PrivateScopeSlug, s) + } + if before != core.PrivateScopeSlug { + return "", fmt.Errorf("only @%s scope is supported for delete; got @%s", core.PrivateScopeSlug, before) + } + return after, nil +} + +func printPrivateSection(ctx context.Context, cli *orchclient.Client, hc creds.HostCreds) { + if hc.ActiveAccountID == "" { + fmt.Println("\nPrivate — (no active account; set one with `ninja account set `)") + return + } + fmt.Printf("\nPrivate — account: %s\n", hc.ActiveAccountSlug) + resp, err := cli.Reg.ListPrivatePlugins(ctx, connect.NewRequest(&v1.ListPrivatePluginsRequest{ + AccountId: hc.ActiveAccountID, + })) + if err != nil { + fmt.Printf(" (error: %v)\n", err) + return + } + if len(resp.Msg.Plugins) == 0 { + fmt.Println(" (none — publish one with `ninja plugin publish --private`)") + return + } + for _, p := range resp.Msg.Plugins { + latest := p.ChannelVersions["latest"] + if latest == "" { + latest = "(no versions)" + } + fmt.Printf(" @%s/%s %s\n", core.PrivateScopeSlug, p.Plugin.Name, latest) + } +} + +func newPluginInitCmd() *cobra.Command { + var scope, name string + var private bool + cmd := &cobra.Command{ + Use: "init", + Short: "Create a plugin in the registry and add a git remote", + RunE: func(c *cobra.Command, _ []string) error { + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return err + } + resolvedHost, hc, err := cr.Resolve(host) + if err != nil { + return err + } + cli := orchclient.New(resolvedHost, hc.Token) + ctx := context.Background() + scanner := bufio.NewScanner(os.Stdin) + + // Read existing plugin.mod (if any) so prompts can default to its + // values. Missing or unparseable file is fine — we just start from + // empty defaults. + existing := &core.ModFile{} + if data, err := os.ReadFile("plugin.mod"); err == nil { + if m, err := core.ParseModFull(data); err == nil && m != nil { + existing = m + } + } + + // --private forces the plugin into the account-scoped @private + // namespace and skips scope selection entirely. Inherit a previous + // `private = true` from the mod file so re-running init in a + // private plugin's directory doesn't silently flip it public. + if existing.Plugin.Private { + private = true + } + + switch { + case private: + if hc.ActiveAccountID == "" { + return fmt.Errorf("--private requires an active account; run `ninja login` or `ninja account set `") + } + scope = "@" + core.PrivateScopeSlug + case scope != "": + scope, err = parseScope(scope) + if err != nil { + return err + } + case existing.Plugin.Scope != "": + scope = "@" + strings.TrimPrefix(existing.Plugin.Scope, "@") + default: + // Scope precedence: --scope flag > plugin.mod scope > interactive prompt. + scope, err = promptScope(ctx, cli, cr, resolvedHost, hc, scanner) + if err != nil { + return err + } + } + + // Name precedence: --name flag > plugin.mod name > prompt. + if name == "" { + if existing.Plugin.Name != "" { + fmt.Printf("Plugin name [%s]: ", existing.Plugin.Name) + } else { + fmt.Print("Plugin name: ") + } + if !scanner.Scan() { + return fmt.Errorf("cancelled") + } + input := strings.TrimSpace(scanner.Text()) + if input != "" { + name = input + } else { + name = existing.Plugin.Name + } + if name == "" { + return fmt.Errorf("plugin name is required") + } + } + rawName := name + name = strings.ToLower(name) + + displayDefault := existing.Plugin.DisplayName + if displayDefault == "" { + displayDefault = rawName + } + fmt.Printf("Display name [%s]: ", displayDefault) + displayName := displayDefault + if scanner.Scan() { + if v := strings.TrimSpace(scanner.Text()); v != "" { + displayName = v + } + } + + descLabel := "Description (one-line summary, optional)" + if existing.Plugin.Description != "" { + descLabel = fmt.Sprintf("Description [%s]", truncate(existing.Plugin.Description, 60)) + } + fmt.Printf("%s: ", descLabel) + description := existing.Plugin.Description + if scanner.Scan() { + if v := strings.TrimSpace(scanner.Text()); v != "" { + description = v + } + } + + kind, err := promptKindWithDefault(scanner, existing.Plugin.Kind) + if err != nil { + return err + } + var cats []string + if kind == "plugin" { + cats, err = promptCategoriesWithDefault(ctx, cli, scanner, existing.Plugin.Categories) + if err != nil { + return err + } + } + tags, err := promptTagsWithDefault(ctx, cli, scanner, kind, existing.Plugin.Tags) + if err != nil { + return err + } + + createReq := &v1.CreatePluginRequest{ + ScopeSlug: scopeAPISlug(scope), + Name: name, + DisplayName: displayName, + Description: description, + Kind: kind, + Categories: cats, + } + if private { + createReq.Visibility = v1.PluginVisibility_PLUGIN_VISIBILITY_PRIVATE + createReq.ActiveAccountId = hc.ActiveAccountID + } + if _, err := cli.Reg.CreatePlugin(ctx, connect.NewRequest(createReq)); err != nil { + return err + } + fmt.Printf("\nCreated %s/%s\n", scope, name) + + // For private plugins the scope line in plugin.mod is informational + // only — coord resolution always rewrites to @private. Persist an + // empty scope so future re-runs don't see a misleading scope value. + modScope := scope + if private { + modScope = "" + } + if err := upsertPluginMod(modScope, name, displayName, description, kind, cats, tags, private); err != nil { + return err + } + fmt.Println("plugin.mod updated") + + if _, err := os.Stat(".git"); err == nil { + if err := autoCommitPluginMod("Add plugin.mod"); err != nil { + fmt.Fprintf(os.Stderr, "warning: could not commit plugin.mod: %v\n", err) + } + } else { + fmt.Println("Not in a git repo - run `git init && git commit` before `ninja plugin publish`") + } + return nil + }, + } + cmd.Flags().StringVar(&scope, "scope", "", "Scope (e.g. @acme)") + cmd.Flags().StringVar(&name, "name", "", "Plugin name") + cmd.Flags().BoolVar(&private, "private", false, "Create a private plugin under your active account's @private namespace") + return cmd +} + +func promptScope(ctx context.Context, cli *orchclient.Client, cr *creds.Credentials, host string, hc creds.HostCreds, scanner *bufio.Scanner) (string, error) { + if hc.DefaultScope != "" { + fmt.Printf("Using default scope: %s (override with --scope)\n", hc.DefaultScope) + return hc.DefaultScope, nil + } + + scopes, err := cli.Scope.ListMyScopes(ctx, connect.NewRequest(&v1.ListMyScopesRequest{})) + if err != nil { + return "", fmt.Errorf("listing scopes: %w", err) + } + + fmt.Println() + fmt.Println("A scope is your organisation or personal namespace for plugins (like @mycompany).") + fmt.Println() + + var scope string + if len(scopes.Msg.Scopes) == 0 { + fmt.Println("You don't have any scopes yet. Let's create one.") + fmt.Println() + s, err := createScopeInline(ctx, cli, scanner) + if err != nil { + return "", err + } + scope = s + } else { + fmt.Println("Your scopes:") + for i, s := range scopes.Msg.Scopes { + fmt.Printf(" %d. @%s — %s\n", i+1, s.Slug, s.DisplayName) + } + fmt.Println() + fmt.Print("Select a scope [1]: ") + if !scanner.Scan() { + return "", fmt.Errorf("cancelled") + } + input := strings.TrimSpace(scanner.Text()) + + if input == "" { + scope = "@" + scopes.Msg.Scopes[0].Slug + } else if n, err := strconv.Atoi(input); err == nil && n >= 1 && n <= len(scopes.Msg.Scopes) { + scope = "@" + scopes.Msg.Scopes[n-1].Slug + } else { + return "", fmt.Errorf("invalid selection: %s", input) + } + } + + fmt.Printf("\nSave %s as your default scope? [Y/n]: ", scope) + if scanner.Scan() { + ans := strings.ToLower(strings.TrimSpace(scanner.Text())) + if ans == "" || ans == "y" || ans == "yes" { + hc.DefaultScope = scope + cr.Hosts[host] = hc + if err := cr.Save(); err != nil { + fmt.Fprintf(os.Stderr, "warning: could not save default scope: %v\n", err) + } else { + fmt.Println("Default scope saved.") + } + } + } + + return scope, nil +} + +func createScopeInline(ctx context.Context, cli *orchclient.Client, scanner *bufio.Scanner) (string, error) { + fmt.Println("A scope is an organisation namespace for your plugins (e.g. @acme).") + fmt.Println("It appears in plugin names like @acme/my-plugin.") + fmt.Println() + fmt.Print("Scope slug (lowercase letters, numbers, dashes): ") + if !scanner.Scan() { + return "", fmt.Errorf("cancelled") + } + slug, err := parseScope(scanner.Text()) + if err != nil { + return "", err + } + + fmt.Printf("Display name [%s]: ", scopeAPISlug(slug)) + if !scanner.Scan() { + return "", fmt.Errorf("cancelled") + } + displayName := strings.TrimSpace(scanner.Text()) + if displayName == "" { + displayName = scopeAPISlug(slug) + } + + _, err = cli.Scope.CreateScope(ctx, connect.NewRequest(&v1.CreateScopeRequest{ + Slug: scopeAPISlug(slug), + DisplayName: displayName, + })) + if err != nil { + return "", err + } + fmt.Printf("Created scope %s\n\n", slug) + return slug, nil +} + +// promptKindWithDefault prompts for plugin kind. When current is "plugin" or +// "theme", that becomes the empty-input default (and the [n] hint reflects it). +func promptKindWithDefault(scanner *bufio.Scanner, current string) (string, error) { + defaultIdx := "1" + switch current { + case "theme": + defaultIdx = "2" + case "plugin", "": + defaultIdx = "1" + } + fmt.Println("Kind: 1) plugin 2) theme") + fmt.Printf("Select [%s]: ", defaultIdx) + if !scanner.Scan() { + return "", fmt.Errorf("cancelled") + } + v := strings.TrimSpace(scanner.Text()) + if v == "" { + v = defaultIdx + } + switch v { + case "1", "plugin": + return "plugin", nil + case "2", "theme": + return "theme", nil + default: + return "", fmt.Errorf("invalid kind: %s", v) + } +} + +// truncate clips a string to at most max runes, appending "…" if it was cut. +// Used to keep prompt labels readable when the existing description is long. +func truncate(s string, max int) string { + if len([]rune(s)) <= max { + return s + } + r := []rune(s) + return string(r[:max-1]) + "…" +} + +// promptCategoriesWithDefault lists the canonical categories and lets the user +// pick by number. When current is non-empty, those slugs are pre-selected: +// the prompt shows their numbers as the default, and empty input keeps them. +func promptCategoriesWithDefault(ctx context.Context, cli *orchclient.Client, scanner *bufio.Scanner, current []string) ([]string, error) { + resp, err := cli.Reg.ListCategories(ctx, connect.NewRequest(&v1.ListCategoriesRequest{})) + if err != nil { + return nil, fmt.Errorf("list categories: %w", err) + } + cats := resp.Msg.Categories + if len(cats) == 0 { + return nil, nil + } + // Build the default indices string from `current` so the prompt shows e.g. + // "Select [9]: " when current is ["templates"] and templates is the 9th + // entry. + slugToIdx := make(map[string]int, len(cats)) + for i, c := range cats { + slugToIdx[c.Slug] = i + 1 + } + var defaultNums []string + for _, slug := range current { + if i, ok := slugToIdx[slug]; ok { + defaultNums = append(defaultNums, strconv.Itoa(i)) + } + } + defaultLabel := strings.Join(defaultNums, ",") + + fmt.Println("Categories (comma-separated numbers, or blank to keep current):") + for i, c := range cats { + marker := " " + if _, picked := pickedSet(current)[c.Slug]; picked { + marker = "*" + } + fmt.Printf(" %d.%s%s — %s\n", i+1, marker, c.Slug, c.DisplayName) + } + if defaultLabel != "" { + fmt.Printf("Select [%s]: ", defaultLabel) + } else { + fmt.Print("Select: ") + } + if !scanner.Scan() { + return nil, fmt.Errorf("cancelled") + } + raw := strings.TrimSpace(scanner.Text()) + if raw == "" { + // Empty input → keep current (or none, if there's no current). + return current, nil + } + parts := strings.Split(raw, ",") + out := make([]string, 0, len(parts)) + for _, p := range parts { + n, err := strconv.Atoi(strings.TrimSpace(p)) + if err != nil || n < 1 || n > len(cats) { + return nil, fmt.Errorf("invalid category selection: %s", p) + } + out = append(out, cats[n-1].Slug) + } + return out, nil +} + +// fetchPopularTagsForPrompt returns a comma-joined "tag (count)" string of the +// top tags for the given kind, or "" if the orchestrator lookup fails (e.g. +// offline, unauthenticated, RPC error). Callers must tolerate an empty return +// and surface their own user-facing fallback message. +func fetchPopularTagsForPrompt(ctx context.Context, cli *orchclient.Client, kind string) string { + resp, err := cli.Reg.ListTags(ctx, connect.NewRequest(&v1.ListTagsRequest{Kind: kind, Limit: 20})) + if err != nil { + return "" + } + if len(resp.Msg.Tags) == 0 { + return "" + } + parts := make([]string, 0, len(resp.Msg.Tags)) + for _, t := range resp.Msg.Tags { + parts = append(parts, fmt.Sprintf("%s (%d)", t.Tag, t.Count)) + } + return strings.Join(parts, ", ") +} + +// promptTagsWithDefault prompts the user for free-form tags. Best-effort fetches +// the top-20 most-used tags via ListTags(kind) to surface popular suggestions; +// if that call fails (offline, etc.) it falls back to a plain prompt with a +// one-line warning. Empty input keeps `current`. Validates with NormalizeTags; +// on error, prints the issue and reprompts (one re-try, then bails). +func promptTagsWithDefault(ctx context.Context, cli *orchclient.Client, scanner *bufio.Scanner, kind string, current []string) ([]string, error) { + popular := fetchPopularTagsForPrompt(ctx, cli, kind) + + for attempt := range 2 { + if len(current) > 0 { + fmt.Printf("Tags (current: %s)\n", strings.Join(current, ", ")) + } else { + fmt.Println("Tags (current: none)") + } + if popular != "" { + fmt.Printf("Popular: %s\n", popular) + } else if attempt == 0 { + fmt.Println("(could not fetch popular tags — offline or unauthenticated)") + } + fmt.Print("Enter comma-separated tags (blank to keep current): ") + if !scanner.Scan() { + return nil, fmt.Errorf("cancelled") + } + raw := strings.TrimSpace(scanner.Text()) + if raw == "" { + return current, nil + } + parts := strings.Split(raw, ",") + out, err := core.NormalizeTags(parts) + if err == nil { + return out, nil + } + fmt.Printf(" %s\n", err.Error()) + if attempt == 0 { + fmt.Println(" Try again:") + } + } + return nil, fmt.Errorf("tag input failed validation after 2 attempts") +} + +// pickedSet returns a set of the given slugs for O(1) membership checks. +func pickedSet(slugs []string) map[string]struct{} { + s := make(map[string]struct{}, len(slugs)) + for _, x := range slugs { + s[x] = struct{}{} + } + return s +} + +func newPluginPublishCmd() *cobra.Command { + var channel string + var strict bool + var privateFlag bool + var bnpFile string + cmd := &cobra.Command{ + Use: "publish", + Short: "Publish a new version to the registry (source archive, or a prebuilt .bnp via --bnp)", + RunE: func(c *cobra.Command, _ []string) error { + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return err + } + resolvedHost, hc, err := cr.Resolve(host) + if err != nil { + return err + } + cli := orchclient.New(resolvedHost, hc.Token) + + modBytes, err := os.ReadFile("plugin.mod") + if err != nil { + return fmt.Errorf("read plugin.mod: %w", err) + } + mod, err := core.ParseModFull(modBytes) + if err != nil { + return err + } + + // --private on the command line is sticky: persist it back to + // plugin.mod so the next publish keeps the visibility. The mod + // file is the source of truth at publish time; the flag is just + // the on-ramp. + private := mod.Plugin.Private || privateFlag + if private && !mod.Plugin.Private { + mod.Plugin.Private = true + if err := writeMod("plugin.mod", mod); err != nil { + return fmt.Errorf("persist private=true to plugin.mod: %w", err) + } + fmt.Fprintln(os.Stderr, "plugin.mod updated: private = true") + } + + switch { + case private: + if hc.ActiveAccountID == "" { + return fmt.Errorf("--private requires an active account; run `ninja login` or `ninja account set `") + } + if mod.Plugin.Scope != "" { + fmt.Fprintf(os.Stderr, + "warning: scope %q in plugin.mod is ignored because private = true; publishing as %s\n", + mod.Plugin.Scope, mod.Coords()) + } + if mod.Plugin.Name == "" || mod.Plugin.Version == "" { + return fmt.Errorf("plugin.mod must have name and version") + } + default: + if mod.Plugin.Scope == "" || mod.Plugin.Name == "" || mod.Plugin.Version == "" { + return fmt.Errorf("plugin.mod must have scope, name, and version") + } + } + + if err := checkRepoHasHEAD("."); err != nil { + return err + } + + // Backstop for the bump-then-publish flow when bump's auto-commit + // was skipped (--no-commit) or the version was hand-edited. Commits + // only plugin.mod; any other dirty paths are left for + // emitPublishWarnings (and ship via stash-create unless --strict). + if err := autoCommitPluginMod(fmt.Sprintf("bump to %s", mod.Plugin.Version)); err != nil { + return fmt.Errorf("auto-commit plugin.mod: %w", err) + } + + if err := emitPublishWarnings(".", !strict, os.Stderr); err != nil { + return err + } + + // WO-WZ-011: --bnp uploads a prebuilt wasm artifact (from + // `ninja plugin build`) instead of a source archive. The orchestrator + // verifies the .bnp layout/ABI/name/version server-side and records + // its abi_version. Without --bnp, publish keeps shipping a source + // archive (compiled in-container by the CMS reconciler) for + // backwards compatibility until the CMS install path is wasm-native. + var ( + archiveBytes []byte + artifactKind = "source archive" + ) + if bnpFile != "" { + archiveBytes, err = os.ReadFile(bnpFile) + if err != nil { + return fmt.Errorf("read --bnp file: %w", err) + } + artifactKind = ".bnp artifact" + } else { + archiveBytes, err = archive.BuildSourceArchive(".") + if err != nil { + return fmt.Errorf("build archive: %w", err) + } + } + + ctx := context.Background() + getReq := &v1.GetPluginRequest{ + ScopeSlug: mod.Plugin.Scope, + Name: mod.Plugin.Name, + } + if private { + getReq.ScopeSlug = core.PrivateScopeSlug + getReq.ActiveAccountId = hc.ActiveAccountID + } + pr, err := cli.Reg.GetPlugin(ctx, connect.NewRequest(getReq)) + if err != nil { + return fmt.Errorf("get plugin: %w", err) + } + + readme, _ := os.ReadFile("README.md") + changelog, _ := os.ReadFile("CHANGELOG.md") + + pubResp, err := cli.Pub.PublishVersion(ctx, connect.NewRequest(&v1.PublishVersionRequest{ + PluginId: pr.Msg.Plugin.Id, + Version: mod.Plugin.Version, + Channel: channel, + Archive: archiveBytes, + ReadmeMd: string(readme), + ChangelogMd: string(changelog), + })) + if err != nil { + return fmt.Errorf("publish: %w", err) + } + fmt.Printf("Published %s (%s, %d bytes)\n", mod.Coords(), artifactKind, len(archiveBytes)) + for _, w := range pubResp.Msg.Warnings { + fmt.Printf(" warning: %s\n", w) + } + return nil + }, + } + cmd.Flags().StringVar(&channel, "channel", "latest", "Channel to point at this version") + cmd.Flags().BoolVar(&strict, "strict", false, "Fail if the working tree is dirty (default: ship dirty trees via stash-create)") + cmd.Flags().BoolVar(&privateFlag, "private", false, "Publish as a private plugin under your active account's @private namespace") + cmd.Flags().StringVar(&bnpFile, "bnp", "", "Upload a prebuilt .bnp wasm artifact (from `ninja plugin build`) instead of a source archive") + return cmd +} + +func newPluginStatusCmd() *cobra.Command { + return &cobra.Command{ + Use: "status", + Short: "List owned scopes and their plugins", + RunE: func(c *cobra.Command, _ []string) error { + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return err + } + resolvedHost, hc, err := cr.Resolve(host) + if err != nil { + return err + } + cli := orchclient.New(resolvedHost, hc.Token) + ctx := context.Background() + scopes, err := cli.Scope.ListMyScopes(ctx, connect.NewRequest(&v1.ListMyScopesRequest{})) + if err != nil { + return err + } + if len(scopes.Msg.Scopes) == 0 { + fmt.Println("No scopes yet. Create one with: ninja scope create") + return nil + } + for _, s := range scopes.Msg.Scopes { + fmt.Printf("@%s - %s\n", s.Slug, s.DisplayName) + gs, err := cli.Scope.GetScope(ctx, connect.NewRequest(&v1.GetScopeRequest{Slug: s.Slug})) + if err != nil { + fmt.Printf(" (error: %v)\n", err) + continue + } + for _, p := range gs.Msg.Plugins { + fmt.Printf(" @%s/%s [%s]\n", s.Slug, p.Name, visibilityLabel(p.Visibility)) + } + } + return nil + }, + } +} + +func newPluginBumpCmd() *cobra.Command { + var setVersion string + var noCommit bool + cmd := &cobra.Command{ + Use: "bump [major|minor|patch]", + Short: "Bump the version in plugin.mod (default: patch)", + Long: "Updates plugin.mod with a new version and auto-commits it. Pass --no-commit to skip the commit.", + Args: cobra.MaximumNArgs(1), + RunE: func(c *cobra.Command, args []string) error { + modBytes, err := os.ReadFile("plugin.mod") + if err != nil { + return fmt.Errorf("read plugin.mod: %w", err) + } + mod, err := core.ParseModFull(modBytes) + if err != nil { + return err + } + if mod.Plugin.Version == "" { + return fmt.Errorf("plugin.mod has no version") + } + old := mod.Plugin.Version + + var next string + if setVersion != "" { + if len(args) > 0 { + return fmt.Errorf("cannot combine --set with bump argument") + } + if _, _, _, err := core.ParseBaseSemver(setVersion); err != nil { + return err + } + next = setVersion + } else { + level := "patch" + if len(args) > 0 { + level = args[0] + } + next, err = core.BumpVersion(old, level) + if err != nil { + return err + } + } + + mod.Plugin.Version = next + if err := writeMod("plugin.mod", mod); err != nil { + return err + } + fmt.Printf("%s -> %s\n", old, next) + + if !noCommit { + if _, err := os.Stat(".git"); err == nil { + if err := autoCommitPluginMod(fmt.Sprintf("bump to %s", next)); err != nil { + fmt.Fprintf(os.Stderr, "warning: could not commit plugin.mod: %v\n", err) + } + } + } + return nil + }, + } + cmd.Flags().StringVar(&setVersion, "set", "", "Set explicit version (e.g. 0.5.0)") + cmd.Flags().BoolVar(&noCommit, "no-commit", false, "Don't auto-commit plugin.mod after bumping") + return cmd +} + +func newPluginVersionCmd() *cobra.Command { + var short bool + cmd := &cobra.Command{ + Use: "version", + Short: "Show local plugin version and registry channels", + RunE: func(c *cobra.Command, _ []string) error { + modBytes, err := os.ReadFile("plugin.mod") + if err != nil { + return fmt.Errorf("read plugin.mod: %w", err) + } + mod, err := core.ParseModFull(modBytes) + if err != nil { + return err + } + if short { + fmt.Println(mod.Plugin.Version) + return nil + } + + local := mod.Plugin.Version + if local == "" { + local = "(unset)" + } + fmt.Printf("local: %s\n", local) + + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + fmt.Printf("(registry: %v)\n", err) + return nil + } + resolvedHost, hc, err := cr.Resolve(host) + if err != nil { + fmt.Printf("(registry: %v)\n", err) + return nil + } + if mod.Plugin.Scope == "" || mod.Plugin.Name == "" { + fmt.Println("(registry: plugin.mod missing scope or name)") + return nil + } + + cli := orchclient.New(resolvedHost, hc.Token) + pr, err := cli.Reg.GetPlugin(context.Background(), connect.NewRequest(&v1.GetPluginRequest{ + ScopeSlug: mod.Plugin.Scope, Name: mod.Plugin.Name, + })) + if err != nil { + fmt.Printf("(registry: %v)\n", err) + return nil + } + + verByString := make(map[string]*v1.Version, len(pr.Msg.Versions)) + for _, v := range pr.Msg.Versions { + verByString[v.Version] = v + } + + names := make([]string, 0, len(pr.Msg.Channels)) + for ch := range pr.Msg.Channels { + names = append(names, ch) + } + sort.Slice(names, func(i, j int) bool { + if names[i] == "latest" { + return true + } + if names[j] == "latest" { + return false + } + return names[i] < names[j] + }) + for _, ch := range names { + ver := pr.Msg.Channels[ch] + date := "" + if v, ok := verByString[ver]; ok && v.PublishedAt != nil { + date = " (published " + v.PublishedAt.AsTime().Format("2006-01-02") + ")" + } + fmt.Printf("%-8s %s%s\n", ch+":", ver, date) + } + return nil + }, + } + cmd.Flags().BoolVar(&short, "short", false, "Print only the local version") + return cmd +} + +var scopeSlugRe = regexp.MustCompile(`^[a-z][a-z0-9-]{2,}$`) + +func parseScope(input string) (string, error) { + raw := strings.TrimPrefix(strings.TrimSpace(input), "@") + if raw == "" { + return "", fmt.Errorf("scope is required") + } + if !scopeSlugRe.MatchString(raw) { + return "", fmt.Errorf("invalid scope %q: must be at least 3 characters, lowercase letters, numbers, and dashes", raw) + } + return "@" + raw, nil +} + +func scopeAPISlug(scope string) string { + return strings.TrimPrefix(scope, "@") +} + +func runCmd(name string, args ...string) error { + c := exec.Command(name, args...) + c.Stderr = os.Stderr + return c.Run() +} + +// checkRepoHasHEAD returns a friendlier error than the raw git failure when +// the publish flow is invoked in a freshly-initialised repository that has +// no commits yet. Without this, `git stash create` reports "You do not have +// the initial commit yet" via exit status 128, which surfaces as an internal +// failure to the user. +func checkRepoHasHEAD(repoDir string) error { + cmd := exec.Command("git", "rev-parse", "--verify", "HEAD") + cmd.Dir = repoDir + if err := cmd.Run(); err != nil { + return fmt.Errorf("no commits in repository; run `git add . && git commit` before publishing") + } + return nil +} + +// submodulePaths returns the configured submodule paths from .gitmodules. +// Reading the file directly (rather than running `git submodule status`) means +// we detect submodules that have been declared but not yet initialised. +func submodulePaths(repoDir string) []string { + cmd := exec.Command("git", "config", "--file", ".gitmodules", "--get-regexp", `submodule\..*\.path`) + cmd.Dir = repoDir + out, err := cmd.Output() + if err != nil { + return nil + } + var paths []string + for line := range strings.SplitSeq(strings.TrimSpace(string(out)), "\n") { + // each line is "submodule..path " + fields := strings.Fields(line) + if len(fields) >= 2 { + paths = append(paths, fields[len(fields)-1]) + } + } + return paths +} + +func upsertPluginMod(scope, name, displayName, description, kind string, categories, tags []string, private bool) error { + const file = "plugin.mod" + existing, _ := os.ReadFile(file) + mod, _ := core.ParseModFull(existing) + if mod == nil { + mod = &core.ModFile{} + } + if mod.Plugin.Version == "" { + mod.Plugin.Version = "0.1.0" + } + mod.Plugin.Scope = scope + mod.Plugin.Name = name + mod.Plugin.DisplayName = displayName + mod.Plugin.Description = description + mod.Plugin.Kind = kind + mod.Plugin.Categories = categories + mod.Plugin.Tags = tags + mod.Plugin.Private = private + return writeMod(file, mod) +} + +func writeMod(path string, m *core.ModFile) error { + var b strings.Builder + b.WriteString("[plugin]\n") + fmt.Fprintf(&b, "name = %q\n", m.Plugin.Name) + if m.Plugin.DisplayName != "" { + fmt.Fprintf(&b, "display_name = %q\n", m.Plugin.DisplayName) + } + fmt.Fprintf(&b, "scope = %q\n", m.Plugin.Scope) + fmt.Fprintf(&b, "version = %q\n", m.Plugin.Version) + if m.Plugin.Description != "" { + fmt.Fprintf(&b, "description = %q\n", m.Plugin.Description) + } + if m.Plugin.Kind != "" { + fmt.Fprintf(&b, "kind = %q\n", m.Plugin.Kind) + } + if len(m.Plugin.Categories) > 0 { + quoted := make([]string, len(m.Plugin.Categories)) + for i, c := range m.Plugin.Categories { + quoted[i] = fmt.Sprintf("%q", c) + } + fmt.Fprintf(&b, "categories = [%s]\n", strings.Join(quoted, ", ")) + } + if len(m.Plugin.Tags) > 0 { + quoted := make([]string, len(m.Plugin.Tags)) + for i, t := range m.Plugin.Tags { + quoted[i] = fmt.Sprintf("%q", t) + } + fmt.Fprintf(&b, "tags = [%s]\n", strings.Join(quoted, ", ")) + } + if m.Plugin.Private { + b.WriteString("private = true\n") + } + if m.Plugin.DataDir { + b.WriteString("data_dir = true\n") + } + if m.Compatibility != nil { + b.WriteString("\n[compatibility]\n") + fmt.Fprintf(&b, "block_core = %q\n", m.Compatibility.BlockCore) + } + for _, r := range m.Requires { + b.WriteString("\n[[requires]]\n") + fmt.Fprintf(&b, "name = %q\n", r.Name) + fmt.Fprintf(&b, "version = %q\n", r.Version) + } + return os.WriteFile(path, []byte(b.String()), 0o644) +} + +// gitignoredTrackedWarning writes a warning to w if any tracked files +// in repoDir match the active .gitignore — those files still ship in the +// archive, which is almost always not what the author wants. +func gitignoredTrackedWarning(repoDir string, w io.Writer) { + cmd := exec.Command("git", "ls-files", "--cached", "--ignored", "--exclude-standard") + cmd.Dir = repoDir + out, _ := cmd.Output() + names := strings.TrimSpace(string(out)) + if names == "" { + return + } + _, _ = fmt.Fprintln(w, "warning: these tracked files match .gitignore and will still be shipped:") + for n := range strings.SplitSeq(names, "\n") { + _, _ = fmt.Fprintln(w, " "+n) + } + _, _ = fmt.Fprintln(w, " (run `git rm --cached ` to drop)") +} + +// untrackedFilesWarning writes a warning to w listing untracked files in +// repoDir. Fires on the dirty-tree publish path because `git stash create` +// only captures tracked content, so untracked files silently vanish from the +// archive. +func untrackedFilesWarning(repoDir string, w io.Writer) { + cmd := exec.Command("git", "ls-files", "--others", "--exclude-standard") + cmd.Dir = repoDir + out, _ := cmd.Output() + names := strings.TrimSpace(string(out)) + if names == "" { + return + } + _, _ = fmt.Fprintln(w, "warning: these untracked files will NOT be in the archive:") + for n := range strings.SplitSeq(names, "\n") { + _, _ = fmt.Fprintln(w, " "+n) + } + _, _ = fmt.Fprintln(w, " (run `git add ` if they should be shipped)") +} + +// emitPublishWarnings runs the publish-time warning helpers in the order the +// CLI expects: +// +// 1. gitignoredTrackedWarning is unconditional so the developer sees it even +// when the publish is about to abort under --strict. +// 2. If allowDirty is false (i.e. --strict) the working tree must be clean — +// a dirty tree returns an error and the remaining warnings (which are +// only useful on the proceeding-publish path) are skipped. +// 3. If allowDirty is true (the default) the untracked-files warning fires +// so the user knows those files won't be in the archive. +// 4. submoduleWarning is unconditional on the proceeding path because +// `git archive` does not recurse into submodules. +func emitPublishWarnings(repoDir string, allowDirty bool, w io.Writer) error { + gitignoredTrackedWarning(repoDir, w) + + if !allowDirty { + cmd := exec.Command("git", "status", "--porcelain") + cmd.Dir = repoDir + out, _ := cmd.Output() + if len(strings.TrimSpace(string(out))) > 0 { + return fmt.Errorf("working tree dirty (--strict); commit your changes or drop --strict") + } + } else { + // `git stash create` only captures tracked content, so untracked + // files would be silently dropped from the archive. Warn loudly. + untrackedFilesWarning(repoDir, w) + } + + submoduleWarning(repoDir, w) + return nil +} + +// submoduleWarning writes a warning to w if repoDir contains submodules. +// `git archive` doesn't recurse into them so they ship as empty directories; +// detection is via .gitmodules so it fires even for uninitialised submodules. +func submoduleWarning(repoDir string, w io.Writer) { + paths := submodulePaths(repoDir) + if len(paths) == 0 { + return + } + _, _ = fmt.Fprintln(w, "warning: this repo has submodules; git archive will ship them as empty directories:") + for _, p := range paths { + _, _ = fmt.Fprintln(w, " "+p) + } + _, _ = fmt.Fprintln(w, " (vendor the contents or pack them separately if the plugin depends on them)") +} + +// autoCommitPluginMod stages and commits plugin.mod with the given message, +// if plugin.mod differs from HEAD. No-op when there's nothing to commit. +// +// The commit is path-scoped (`git commit -- plugin.mod`) so any other staged +// paths in the index are left alone — important now that publish calls this +// automatically. +func autoCommitPluginMod(message string) error { + out, err := exec.Command("git", "status", "--porcelain", "plugin.mod").Output() + if err != nil { + return fmt.Errorf("git status: %w", err) + } + if strings.TrimSpace(string(out)) == "" { + return nil + } + if err := runCmd("git", "add", "plugin.mod"); err != nil { + return err + } + if err := runCmd("git", "commit", "-m", message, "--", "plugin.mod"); err != nil { + return err + } + fmt.Println("Committed plugin.mod") + return nil +} diff --git a/cmd/ninja/cmd/plugin_build.go b/cmd/ninja/cmd/plugin_build.go new file mode 100644 index 0000000..b92b7f7 --- /dev/null +++ b/cmd/ninja/cmd/plugin_build.go @@ -0,0 +1,140 @@ +package cmd + +import ( + "context" + "fmt" + "strings" + + "github.com/spf13/cobra" + + "git.dev.alexdunmow.com/block/cli/internal/bnp" +) + +func newPluginBuildCmd() *cobra.Command { + var dir, output string + var codeless bool + cmd := &cobra.Command{ + Use: "build", + Short: "Compile a plugin to wasm and pack a .bnp artifact", + Long: `Compile the plugin in --dir to reactor-mode wasip1 wasm, extract its +static manifest by instantiating the module once (HOOK_DESCRIBE), and pack a +.bnp (tar.zst of plugin.wasm, plugin.mod, manifest.pb, plus migrations/, +schemas/, assets/, web/dist when present). + +No Docker or podman: the whole pipeline is the local Go toolchain (>= 1.24) +plus wazero. This replaces the in-container .so compile.`, + RunE: func(c *cobra.Command, _ []string) error { + // Classify by repo shape (WO-WZ-020): no Go source → codeless + // (declarative, no wasm); Go source → wasm. --codeless asserts + // the expectation and fails loudly on a mismatch. + isCodeless, err := bnp.IsCodelessRepo(dir) + if err != nil { + return err + } + if codeless && !isCodeless { + return fmt.Errorf("--codeless asserted but %s contains Go source — a codeless plugin has no code (delete the Go or drop the flag)", dir) + } + var res *bnp.BuildResult + if isCodeless { + res, err = bnp.BuildCodeless(context.Background(), bnp.BuildOptions{Dir: dir, Output: output}) + } else { + res, err = bnp.Build(context.Background(), bnp.BuildOptions{Dir: dir, Output: output}) + } + if err != nil { + return err + } + printBuildSummary(res) + return nil + }, + } + cmd.Flags().StringVar(&dir, "dir", ".", "Plugin repo directory") + cmd.Flags().StringVarP(&output, "output", "o", "", "Output .bnp path (default -.bnp)") + cmd.Flags().BoolVar(&codeless, "codeless", false, "Assert the repo builds a codeless (no-wasm) artifact; fail if it contains Go source") + return cmd +} + +func newPluginVerifyCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "verify ", + Short: "Validate a .bnp against the loader's layout/name/abi/path/size checks", + Long: `Re-run the CMS reader's checks on a .bnp standalone so CI and the registry +can gate uploads: required members present, path-safety and size caps on +extraction, manifest decodes, abi_version supported, and manifest name matches +plugin.mod. Prints a named reason for each malformed class.`, + Args: cobra.ExactArgs(1), + RunE: func(c *cobra.Command, args []string) error { + res, err := bnp.Verify(args[0]) + if err != nil { + return err + } + printVerifySummary(args[0], res) + return nil + }, + } + return cmd +} + +func printBuildSummary(r *bnp.BuildResult) { + hooks := "(none)" + if len(r.Hooks) > 0 { + hooks = strings.Join(r.Hooks, ", ") + } + dirs := "(none)" + if len(r.IncludedDirs) > 0 { + dirs = strings.Join(r.IncludedDirs, ", ") + } + kind := "wasm" + if r.Codeless { + kind = "codeless" + } + fmt.Printf("Built %s@%s (%s) → %s\n", r.Name, r.Version, kind, r.OutputPath) + fmt.Println(" ┌───────────────────────────────────────────") + fmt.Printf(" │ artifact size %s (%s uncompressed)\n", humanBytes(r.ArtifactBytes), humanBytes(r.UncompBytes)) + fmt.Printf(" │ plugin.wasm %s\n", humanBytes(r.WasmBytes)) + fmt.Printf(" │ manifest.pb %s\n", humanBytes(r.ManifestBytes)) + fmt.Printf(" │ blocks %d\n", r.BlockCount) + fmt.Printf(" │ templates %d\n", r.TemplateCount) + fmt.Printf(" │ admin pages %d\n", r.AdminPages) + fmt.Printf(" │ job types %d\n", r.JobTypes) + fmt.Printf(" │ hooks %s\n", hooks) + fmt.Printf(" │ bundled dirs %s\n", dirs) + fmt.Printf(" │ data_dir grant %t\n", r.DataDir) + fmt.Println(" └───────────────────────────────────────────") +} + +func printVerifySummary(path string, r *bnp.VerifyResult) { + var dirs []string + if r.HasMigrations { + dirs = append(dirs, "migrations") + } + if r.HasSchemas { + dirs = append(dirs, "schemas") + } + if r.HasAssets { + dirs = append(dirs, "assets") + } + if r.HasWeb { + dirs = append(dirs, "web") + } + joined := "(none)" + if len(dirs) > 0 { + joined = strings.Join(dirs, ", ") + } + fmt.Printf("OK: %s\n", path) + fmt.Printf(" name=%s version=%s abi_version=%d blocks=%d data_dir=%t dirs=[%s]\n", + r.Name, r.Version, r.ABIVersion, r.BlockCount, r.DataDir, joined) +} + +// humanBytes formats a byte count with a binary unit suffix. +func humanBytes(n int64) string { + const unit = 1024 + if n < unit { + return fmt.Sprintf("%d B", n) + } + div, exp := int64(unit), 0 + for x := n / unit; x >= unit; x /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f %ciB", float64(n)/float64(div), "KMGTPE"[exp]) +} diff --git a/cmd/ninja/cmd/plugin_build_test.go b/cmd/ninja/cmd/plugin_build_test.go new file mode 100644 index 0000000..a2cd70e --- /dev/null +++ b/cmd/ninja/cmd/plugin_build_test.go @@ -0,0 +1,281 @@ +package cmd + +import ( + "archive/tar" + "context" + "os" + "path/filepath" + "slices" + "strings" + "testing" + + abiv1 "git.dev.alexdunmow.com/block/core/abi/v1" + "git.dev.alexdunmow.com/block/cli/internal/bnp" + "github.com/klauspost/compress/zstd" + "google.golang.org/protobuf/proto" +) + +// fixtureDir resolves a guest-plugin fixture under this package's testdata/. +// The fixtures compile against the cli module's pinned block/core@v0.18.x +// (guest SDK), which is exactly what `ninja plugin build` drives in the wild. +func fixtureDir(t *testing.T, name string) string { + t.Helper() + dir, err := filepath.Abs(filepath.Join("testdata", name)) + if err != nil { + t.Fatalf("resolve fixture %s: %v", name, err) + } + if _, err := os.Stat(filepath.Join(dir, "plugin.mod")); err != nil { + t.Fatalf("fixture %s missing plugin.mod: %v", name, err) + } + return dir +} + +// TestPluginBuildAndVerify is the WO-WZ-009 acceptance e2e: build the WZ-002 +// fixture repo → a .bnp exists, `verify` passes, and its manifest decodes with +// the expected block keys and the data_dir grant from plugin.mod. +func TestPluginBuildAndVerify(t *testing.T) { + if testing.Short() { + t.Skip("compiles a wasm module; skipped in -short") + } + out := filepath.Join(t.TempDir(), "wasmfixture-0.0.1.bnp") + + res, err := bnp.Build(context.Background(), bnp.BuildOptions{ + Dir: fixtureDir(t, "fixture"), + Output: out, + }) + if err != nil { + t.Fatalf("build: %v", err) + } + if res.Name != "wasmfixture" || res.Version != "0.0.1" { + t.Errorf("identity = %q/%q", res.Name, res.Version) + } + if res.BlockCount != 1 || res.TemplateCount != 1 || res.AdminPages != 1 { + t.Errorf("counts blocks=%d templates=%d admin=%d", res.BlockCount, res.TemplateCount, res.AdminPages) + } + if !res.DataDir { + t.Errorf("data_dir grant not carried from plugin.mod into the manifest") + } + if !slices.Contains(res.Hooks, "load") { + t.Errorf("hooks = %v, want load present", res.Hooks) + } + if _, err := os.Stat(out); err != nil { + t.Fatalf("artifact not written: %v", err) + } + + // verify passes on the produced artifact. + vr, err := bnp.Verify(out) + if err != nil { + t.Fatalf("verify: %v", err) + } + if vr.Name != "wasmfixture" || vr.ABIVersion != 1 || !vr.DataDir { + t.Errorf("verify result = %+v", vr) + } + + // manifest decodes with expected block keys. + m, err := bnp.ReadManifest(out) + if err != nil { + t.Fatalf("read manifest: %v", err) + } + var keys []string + for _, b := range m.GetBlocks() { + keys = append(keys, b.GetKey()) + } + if !slices.Contains(keys, "wasmfixture:greeting") { + t.Errorf("block keys = %v, want wasmfixture:greeting", keys) + } + if !slices.Contains(m.GetTemplateKeys(), "wasmfixture-page") { + t.Errorf("template keys = %v, want wasmfixture-page", m.GetTemplateKeys()) + } + if !m.GetDataDir() { + t.Errorf("manifest.data_dir = false, want true") + } +} + +// TestPluginBuildDefaultOutputName confirms the default artifact name is +// -.bnp in the working directory. +func TestPluginBuildDefaultOutputName(t *testing.T) { + if testing.Short() { + t.Skip("compiles a wasm module; skipped in -short") + } + dir := fixtureDir(t, "fixture") // resolve to an absolute path before chdir + wd, _ := os.Getwd() + t.Cleanup(func() { _ = os.Chdir(wd) }) + tmp := t.TempDir() + if err := os.Chdir(tmp); err != nil { + t.Fatalf("chdir: %v", err) + } + res, err := bnp.Build(context.Background(), bnp.BuildOptions{Dir: dir}) + if err != nil { + t.Fatalf("build: %v", err) + } + if res.OutputPath != "wasmfixture-0.0.1.bnp" { + t.Errorf("default output = %q", res.OutputPath) + } + if _, err := os.Stat(filepath.Join(tmp, "wasmfixture-0.0.1.bnp")); err != nil { + t.Errorf("default artifact not in cwd: %v", err) + } +} + +// TestPluginBuildCapabilityInRegisterFails proves a plugin that reaches a host +// capability at describe time fails with an actionable error naming the call. +func TestPluginBuildCapabilityInRegisterFails(t *testing.T) { + if testing.Short() { + t.Skip("compiles a wasm module; skipped in -short") + } + out := filepath.Join(t.TempDir(), "capfixture.bnp") + _, err := bnp.Build(context.Background(), bnp.BuildOptions{ + Dir: fixtureDir(t, "capfixture"), + Output: out, + }) + if err == nil { + t.Fatal("expected build to fail on a describe-time capability call") + } + msg := err.Error() + if !strings.Contains(msg, "db.query") || !strings.Contains(msg, "manifest extraction") { + t.Errorf("error not actionable: %q (want it to name db.query + manifest extraction)", msg) + } + if _, statErr := os.Stat(out); statErr == nil { + t.Errorf("a .bnp must not be written when the build fails") + } +} + +// --- verify rejection classes (fast; no wasm compile) --- + +type fakeEntry struct { + name string + data []byte + typeflag byte +} + +// writeTarZst crafts an arbitrary tar.zst for the malformed-artifact tests. +func writeTarZst(t *testing.T, entries []fakeEntry) string { + t.Helper() + path := filepath.Join(t.TempDir(), "artifact.bnp") + f, err := os.Create(path) + if err != nil { + t.Fatalf("create: %v", err) + } + defer func() { _ = f.Close() }() + enc, _ := zstd.NewWriter(f) + tw := tar.NewWriter(enc) + for _, e := range entries { + typ := e.typeflag + if typ == 0 { + typ = tar.TypeReg + } + hdr := &tar.Header{Name: e.name, Typeflag: typ, Mode: 0o644, Size: int64(len(e.data))} + if typ == tar.TypeSymlink { + hdr.Linkname = string(e.data) + hdr.Size = 0 + } + if err := tw.WriteHeader(hdr); err != nil { + t.Fatalf("hdr %q: %v", e.name, err) + } + if typ == tar.TypeReg { + if _, err := tw.Write(e.data); err != nil { + t.Fatalf("write %q: %v", e.name, err) + } + } + } + _ = tw.Close() + _ = enc.Close() + return path +} + +func manifestBytes(t *testing.T, name string, abi uint32) []byte { + t.Helper() + b, err := proto.Marshal(&abiv1.PluginManifest{Name: name, Version: "1.0.0", AbiVersion: abi}) + if err != nil { + t.Fatalf("marshal manifest: %v", err) + } + return b +} + +func TestVerifyRejectsMalformed(t *testing.T) { + validMod := []byte("[plugin]\nname = \"demo\"\nversion = \"1.0.0\"\n") + + cases := []struct { + name string + entries []fakeEntry + wantMsg string + }{ + { + name: "missing manifest.pb", + entries: []fakeEntry{ + {name: "plugin.wasm", data: []byte("\x00asm")}, + {name: "plugin.mod", data: validMod}, + }, + wantMsg: "missing required manifest.pb", + }, + { + name: "undecodable manifest", + entries: []fakeEntry{ + {name: "plugin.wasm", data: []byte("\x00asm")}, + {name: "plugin.mod", data: validMod}, + {name: "manifest.pb", data: []byte("not-a-proto\xff\xff")}, + }, + wantMsg: "decode manifest.pb", + }, + { + name: "unsupported abi_version", + entries: []fakeEntry{ + {name: "plugin.wasm", data: []byte("\x00asm")}, + {name: "plugin.mod", data: validMod}, + {name: "manifest.pb", data: manifestBytes(t, "demo", 2)}, + }, + wantMsg: "unsupported abi_version 2", + }, + { + name: "empty manifest name", + entries: []fakeEntry{ + {name: "plugin.wasm", data: []byte("\x00asm")}, + {name: "plugin.mod", data: validMod}, + {name: "manifest.pb", data: manifestBytes(t, "", 1)}, + }, + wantMsg: "empty name", + }, + { + name: "name mismatch", + entries: []fakeEntry{ + {name: "plugin.wasm", data: []byte("\x00asm")}, + {name: "plugin.mod", data: validMod}, + {name: "manifest.pb", data: manifestBytes(t, "other", 1)}, + }, + wantMsg: "!= plugin.mod name", + }, + { + name: "absolute path entry", + entries: []fakeEntry{ + {name: "/etc/passwd", data: []byte("x")}, + }, + wantMsg: "absolute entry path", + }, + { + name: "traversal entry", + entries: []fakeEntry{ + {name: "../escape.txt", data: []byte("x")}, + }, + wantMsg: "traversal segment", + }, + { + name: "symlink entry", + entries: []fakeEntry{ + {name: "link", data: []byte("/etc/passwd"), typeflag: tar.TypeSymlink}, + }, + wantMsg: "non-regular entry", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + path := writeTarZst(t, tc.entries) + _, err := bnp.Verify(path) + if err == nil { + t.Fatalf("expected rejection, got nil") + } + if !strings.Contains(err.Error(), tc.wantMsg) { + t.Errorf("error %q does not mention %q", err.Error(), tc.wantMsg) + } + }) + } +} diff --git a/cmd/ninja/cmd/plugin_tags.go b/cmd/ninja/cmd/plugin_tags.go new file mode 100644 index 0000000..009d020 --- /dev/null +++ b/cmd/ninja/cmd/plugin_tags.go @@ -0,0 +1,175 @@ +package cmd + +import ( + "context" + "fmt" + "os" + "slices" + "sort" + "strings" + + "connectrpc.com/connect" + "github.com/spf13/cobra" + + core "git.dev.alexdunmow.com/block/core/plugin" + "git.dev.alexdunmow.com/block/cli/internal/creds" + "git.dev.alexdunmow.com/block/cli/internal/orchclient" + v1 "git.dev.alexdunmow.com/block/cli/internal/api/orchestrator/v1" +) + +func newPluginTagsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "tags", + Short: "Show current tags and popular tags from the registry", + RunE: func(c *cobra.Command, _ []string) error { + mod, err := readLocalMod() + if err != nil { + return err + } + if len(mod.Plugin.Tags) == 0 { + fmt.Println("Current tags: (none)") + } else { + fmt.Printf("Current tags: %s\n", strings.Join(mod.Plugin.Tags, ", ")) + } + + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return nil // not signed in is fine — silent best-effort + } + resolvedHost, hc, err := cr.Resolve(host) + if err != nil { + return nil + } + cli := orchclient.New(resolvedHost, hc.Token) + line := fetchPopularTagsForList(cli, mod.Plugin.Kind) + fmt.Println(line) + return nil + }, + } + + cmd.AddCommand(&cobra.Command{ + Use: "add ...", + Short: "Add tags to the local plugin.mod (union with current)", + Args: cobra.MinimumNArgs(1), + RunE: func(_ *cobra.Command, args []string) error { return mutateTags("add", args) }, + }) + cmd.AddCommand(&cobra.Command{ + Use: "rm ...", + Short: "Remove tags from the local plugin.mod", + Args: cobra.MinimumNArgs(1), + RunE: func(_ *cobra.Command, args []string) error { return mutateTags("rm", args) }, + }) + cmd.AddCommand(&cobra.Command{ + Use: "set ...", + Short: "Replace all tags in the local plugin.mod", + Args: cobra.MinimumNArgs(1), + RunE: func(_ *cobra.Command, args []string) error { return mutateTags("set", args) }, + }) + cmd.AddCommand(&cobra.Command{ + Use: "clear", + Short: "Remove all tags from the local plugin.mod", + Args: cobra.NoArgs, + RunE: func(_ *cobra.Command, _ []string) error { return mutateTags("clear", nil) }, + }) + + return cmd +} + +// mutateTags reads plugin.mod, computes the new tag set, normalises, and writes +// it back. Prints the before→after diff and a reminder to publish. +func mutateTags(op string, args []string) error { + mod, err := readLocalMod() + if err != nil { + return err + } + before := append([]string(nil), mod.Plugin.Tags...) + + var next []string + switch op { + case "add": + next = append(append([]string(nil), before...), args...) + case "rm": + drop := map[string]struct{}{} + for _, a := range args { + drop[strings.ToLower(strings.TrimSpace(a))] = struct{}{} + } + for _, t := range before { + if _, gone := drop[t]; !gone { + next = append(next, t) + } + } + case "set": + next = append([]string(nil), args...) + case "clear": + next = nil + default: + return fmt.Errorf("unknown tag op: %s", op) + } + + normalised, err := core.NormalizeTags(next) + if err != nil { + return err + } + + if err := writeLocalModTags(mod, normalised); err != nil { + return err + } + + sortedBefore := append([]string(nil), before...) + sortedAfter := append([]string(nil), normalised...) + sort.Strings(sortedBefore) + sort.Strings(sortedAfter) + fmt.Printf("Tags: [%s] → [%s]\n", strings.Join(sortedBefore, ", "), strings.Join(sortedAfter, ", ")) + if !slices.Equal(sortedBefore, sortedAfter) { + fmt.Println("Run 'ninja plugin publish' to push to the registry.") + } + return nil +} + +func readLocalMod() (*core.ModFile, error) { + b, err := os.ReadFile("plugin.mod") + if err != nil { + return nil, fmt.Errorf("read plugin.mod: %w", err) + } + mod, err := core.ParseModFull(b) + if err != nil { + return nil, fmt.Errorf("parse plugin.mod: %w", err) + } + return mod, nil +} + +// writeLocalModTags rewrites plugin.mod with the new tag set, preserving all +// other fields by reusing upsertPluginMod. +func writeLocalModTags(mod *core.ModFile, tags []string) error { + return upsertPluginMod( + mod.Plugin.Scope, + mod.Plugin.Name, + mod.Plugin.DisplayName, + mod.Plugin.Description, + mod.Plugin.Kind, + mod.Plugin.Categories, + tags, + mod.Plugin.Private, + ) +} + +// fetchPopularTagsForList returns a single user-facing line listing the most-used +// tags for the given kind. Renders "Popular: tag (count), ...", "Popular tags: +// (none yet)" when no tags exist on public plugins yet, or an "(unreachable)" +// notice if the RPC fails. +func fetchPopularTagsForList(cli *orchclient.Client, kind string) string { + resp, err := cli.Reg.ListTags(context.Background(), connect.NewRequest(&v1.ListTagsRequest{Kind: kind, Limit: 20})) + if err != nil { + return "(could not fetch popular tags — orchestrator unreachable)" + } + if len(resp.Msg.Tags) == 0 { + return "Popular tags: (none yet)" + } + parts := make([]string, len(resp.Msg.Tags)) + for i, t := range resp.Msg.Tags { + parts[i] = fmt.Sprintf("%s (%d)", t.Tag, t.Count) + } + return "Popular: " + strings.Join(parts, ", ") +} + diff --git a/cmd/ninja/cmd/plugin_test.go b/cmd/ninja/cmd/plugin_test.go new file mode 100644 index 0000000..e4870fc --- /dev/null +++ b/cmd/ninja/cmd/plugin_test.go @@ -0,0 +1,637 @@ +package cmd + +import ( + "bytes" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + core "git.dev.alexdunmow.com/block/core/plugin" +) + +func TestCheckRepoHasHEAD_NoCommitsReturnsFriendlyError(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + + err := checkRepoHasHEAD(dir) + if err == nil { + t.Fatal("expected error for repo with no commits, got nil") + } + if !strings.Contains(err.Error(), "no commits in repository") { + t.Errorf("error %q should mention 'no commits in repository'", err.Error()) + } + if !strings.Contains(err.Error(), "git commit") { + t.Errorf("error %q should suggest `git commit`", err.Error()) + } +} + +func TestCheckRepoHasHEAD_WithCommitReturnsNil(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "f"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "f") + runGit(t, dir, "commit", "-qm", "init") + + if err := checkRepoHasHEAD(dir); err != nil { + t.Errorf("expected nil for repo with a commit, got %v", err) + } +} + +func TestAutoCommitPluginMod_CommitsWhenDirty(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "README.md") + runGit(t, dir, "commit", "-qm", "init") + + if err := os.WriteFile(filepath.Join(dir, "plugin.mod"), + []byte("[plugin]\nname = \"x\"\nscope = \"@s\"\nversion = \"0.1.0\"\n"), 0o644); err != nil { + t.Fatal(err) + } + + t.Chdir(dir) + if err := autoCommitPluginMod("Add plugin.mod"); err != nil { + t.Fatalf("autoCommitPluginMod: %v", err) + } + + subject := gitLogSubject(t, dir) + if subject != "Add plugin.mod" { + t.Errorf("expected latest commit subject 'Add plugin.mod', got %q", subject) + } +} + +func TestAutoCommitPluginMod_NoopWhenClean(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "plugin.mod"), + []byte("[plugin]\nname = \"x\"\nscope = \"@s\"\nversion = \"0.1.0\"\n"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "plugin.mod") + runGit(t, dir, "commit", "-qm", "seed") + + beforeSHA := gitHeadSHA(t, dir) + + t.Chdir(dir) + if err := autoCommitPluginMod("Add plugin.mod"); err != nil { + t.Fatalf("autoCommitPluginMod: %v", err) + } + + afterSHA := gitHeadSHA(t, dir) + if afterSHA != beforeSHA { + t.Errorf("expected no new commit, HEAD moved %s -> %s", beforeSHA, afterSHA) + } +} + +func TestAutoCommitPluginMod_WorksOnDetachedHEAD(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "README.md") + runGit(t, dir, "commit", "-qm", "init") + + initialSHA := gitHeadSHA(t, dir) + runGit(t, dir, "checkout", "-q", initialSHA) + + if err := os.WriteFile(filepath.Join(dir, "plugin.mod"), + []byte("[plugin]\nname = \"x\"\nscope = \"@s\"\nversion = \"0.1.0\"\n"), 0o644); err != nil { + t.Fatal(err) + } + + t.Chdir(dir) + if err := autoCommitPluginMod("Add plugin.mod"); err != nil { + t.Fatalf("autoCommitPluginMod on detached HEAD: %v", err) + } + + afterSHA := gitHeadSHA(t, dir) + if afterSHA == initialSHA { + t.Fatalf("expected new commit on detached HEAD, HEAD still at %s", afterSHA) + } + + subject := gitLogSubject(t, dir) + if subject != "Add plugin.mod" { + t.Errorf("expected latest commit subject 'Add plugin.mod', got %q", subject) + } + + parentCmd := exec.Command("git", "rev-parse", "HEAD^") + parentCmd.Dir = dir + parentOut, err := parentCmd.CombinedOutput() + if err != nil { + t.Fatalf("git rev-parse HEAD^: %v\n%s", err, parentOut) + } + parentSHA := strings.TrimSpace(string(parentOut)) + if parentSHA != initialSHA { + t.Errorf("expected new commit parent to be %s, got %s", initialSHA, parentSHA) + } +} + +func TestAutoCommitPluginMod_UsesProvidedMessage(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "README.md") + runGit(t, dir, "commit", "-qm", "init") + + if err := os.WriteFile(filepath.Join(dir, "plugin.mod"), + []byte("[plugin]\nname = \"x\"\nscope = \"@s\"\nversion = \"0.3.0\"\n"), 0o644); err != nil { + t.Fatal(err) + } + + t.Chdir(dir) + if err := autoCommitPluginMod("bump to 0.3.0"); err != nil { + t.Fatalf("autoCommitPluginMod: %v", err) + } + + if got := gitLogSubject(t, dir); got != "bump to 0.3.0" { + t.Errorf("expected commit subject 'bump to 0.3.0', got %q", got) + } +} + +func TestAutoCommitPluginMod_LeavesOtherStagedPathsAlone(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "README.md") + runGit(t, dir, "commit", "-qm", "init") + + // Stage an unrelated change that publish should NOT sweep up into the + // plugin.mod auto-commit. + if err := os.WriteFile(filepath.Join(dir, "other.txt"), []byte("scratch"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "other.txt") + + if err := os.WriteFile(filepath.Join(dir, "plugin.mod"), + []byte("[plugin]\nname = \"x\"\nscope = \"@s\"\nversion = \"0.3.0\"\n"), 0o644); err != nil { + t.Fatal(err) + } + + t.Chdir(dir) + if err := autoCommitPluginMod("bump to 0.3.0"); err != nil { + t.Fatalf("autoCommitPluginMod: %v", err) + } + + // The new commit should touch plugin.mod only. + filesCmd := exec.Command("git", "show", "--name-only", "--pretty=", "HEAD") + filesCmd.Dir = dir + filesOut, err := filesCmd.CombinedOutput() + if err != nil { + t.Fatalf("git show: %v\n%s", err, filesOut) + } + files := strings.Fields(strings.TrimSpace(string(filesOut))) + if len(files) != 1 || files[0] != "plugin.mod" { + t.Errorf("expected commit to touch only plugin.mod, got %v", files) + } + + // other.txt should still be staged (waiting for the developer to deal with). + statusCmd := exec.Command("git", "status", "--porcelain", "other.txt") + statusCmd.Dir = dir + statusOut, _ := statusCmd.Output() + if !strings.HasPrefix(strings.TrimSpace(string(statusOut)), "A ") { + t.Errorf("expected other.txt to remain staged ('A '), got %q", string(statusOut)) + } +} + +func TestAutoCommitPluginMod_ErrorsWhenGitMissing(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "README.md") + runGit(t, dir, "commit", "-qm", "init") + + if err := os.WriteFile(filepath.Join(dir, "plugin.mod"), + []byte("[plugin]\nname = \"x\"\nscope = \"@s\"\nversion = \"0.1.0\"\n"), 0o644); err != nil { + t.Fatal(err) + } + + t.Chdir(dir) + t.Setenv("PATH", "") + + err := autoCommitPluginMod("Add plugin.mod") + if err == nil { + t.Fatal("expected error when git is missing from PATH, got nil") + } + if !strings.Contains(err.Error(), "git") { + t.Errorf("error %q should mention 'git'", err.Error()) + } +} + +func gitLogSubject(t *testing.T, dir string) string { + t.Helper() + cmd := exec.Command("git", "log", "-1", "--pretty=%s") + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("git log: %v\n%s", err, out) + } + return strings.TrimSpace(string(out)) +} + +func gitHeadSHA(t *testing.T, dir string) string { + t.Helper() + cmd := exec.Command("git", "rev-parse", "HEAD") + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("git rev-parse: %v\n%s", err, out) + } + return strings.TrimSpace(string(out)) +} + +func TestGitignoredTrackedWarning_FiresWhenTrackedFileMatchesGitignore(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "secret.env"), []byte("token=abc"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "secret.env") + runGit(t, dir, "commit", "-qm", "init") + if err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("*.env\n"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", ".gitignore") + runGit(t, dir, "commit", "-qm", "ignore") + + var buf bytes.Buffer + gitignoredTrackedWarning(dir, &buf) + + out := buf.String() + if !strings.Contains(out, "secret.env") { + t.Errorf("warning should list secret.env, got: %q", out) + } + if !strings.Contains(out, "git rm --cached") { + t.Errorf("warning should suggest `git rm --cached`, got: %q", out) + } + if !strings.HasSuffix(out, "\n") { + t.Errorf("warning should end with a newline (Fprintln), got: %q", out) + } +} + +func TestGitignoredTrackedWarning_NoopWhenNothingMatches(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "README.md") + runGit(t, dir, "commit", "-qm", "init") + + var buf bytes.Buffer + gitignoredTrackedWarning(dir, &buf) + if buf.Len() != 0 { + t.Errorf("expected empty output for clean repo, got: %q", buf.String()) + } +} + +func TestUntrackedFilesWarning_FiresWithUntrackedFile(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "README.md") + runGit(t, dir, "commit", "-qm", "init") + + if err := os.WriteFile(filepath.Join(dir, "notes.txt"), []byte("scratch"), 0o644); err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + untrackedFilesWarning(dir, &buf) + + out := buf.String() + if !strings.Contains(out, "notes.txt") { + t.Errorf("warning should list notes.txt, got: %q", out) + } + if !strings.Contains(out, "git add") { + t.Errorf("warning should suggest `git add`, got: %q", out) + } +} + +func TestUntrackedFilesWarning_NoopWithNoUntracked(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "README.md") + runGit(t, dir, "commit", "-qm", "init") + + var buf bytes.Buffer + untrackedFilesWarning(dir, &buf) + if buf.Len() != 0 { + t.Errorf("expected empty output, got: %q", buf.String()) + } +} + +func TestSubmoduleWarning_FiresWithGitmodules(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + gitmodules := `[submodule "vendor/foo"] + path = vendor/foo + url = https://example.com/foo.git +` + if err := os.WriteFile(filepath.Join(dir, ".gitmodules"), []byte(gitmodules), 0o644); err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + submoduleWarning(dir, &buf) + + out := buf.String() + if !strings.Contains(out, "vendor/foo") { + t.Errorf("warning should mention vendor/foo, got: %q", out) + } + if !strings.Contains(out, "submodules") { + t.Errorf("warning should mention submodules, got: %q", out) + } +} + +func TestSubmoduleWarning_NoopWithoutGitmodules(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + + var buf bytes.Buffer + submoduleWarning(dir, &buf) + if buf.Len() != 0 { + t.Errorf("expected empty output, got: %q", buf.String()) + } +} + +func TestEmitPublishWarnings_WarnsAboutGitignoreTracked(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "secret.env"), []byte("token=abc"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "secret.env") + runGit(t, dir, "commit", "-qm", "init") + if err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("*.env\n"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", ".gitignore") + runGit(t, dir, "commit", "-qm", "ignore") + + var buf bytes.Buffer + if err := emitPublishWarnings(dir, false, &buf); err != nil { + t.Fatalf("emitPublishWarnings: %v", err) + } + + out := buf.String() + if !strings.Contains(out, "secret.env") { + t.Errorf("expected gitignored-tracked warning to mention secret.env, got: %q", out) + } + if !strings.Contains(out, "match .gitignore") { + t.Errorf("expected gitignored-tracked warning fragment, got: %q", out) + } +} + +func TestEmitPublishWarnings_WarnsAboutSubmodules(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "README.md") + runGit(t, dir, "commit", "-qm", "init") + + gitmodules := `[submodule "vendor/foo"] + path = vendor/foo + url = https://example.com/foo.git +` + if err := os.WriteFile(filepath.Join(dir, ".gitmodules"), []byte(gitmodules), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", ".gitmodules") + runGit(t, dir, "commit", "-qm", "add submodule decl") + + var buf bytes.Buffer + if err := emitPublishWarnings(dir, false, &buf); err != nil { + t.Fatalf("emitPublishWarnings: %v", err) + } + + out := buf.String() + if !strings.Contains(out, "vendor/foo") { + t.Errorf("expected submodule warning to mention vendor/foo, got: %q", out) + } + if !strings.Contains(out, "submodules") { + t.Errorf("expected submodule warning fragment, got: %q", out) + } +} + +func TestEmitPublishWarnings_WarnsAboutUntrackedWithAllowDirty(t *testing.T) { + dir := t.TempDir() + runGit(t, dir, "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hi"), 0o644); err != nil { + t.Fatal(err) + } + runGit(t, dir, "add", "README.md") + runGit(t, dir, "commit", "-qm", "init") + if err := os.WriteFile(filepath.Join(dir, "scratch.txt"), []byte("notes"), 0o644); err != nil { + t.Fatal(err) + } + + t.Run("allowDirty=true surfaces untracked warning", func(t *testing.T) { + var buf bytes.Buffer + if err := emitPublishWarnings(dir, true, &buf); err != nil { + t.Fatalf("emitPublishWarnings: %v", err) + } + out := buf.String() + if !strings.Contains(out, "scratch.txt") { + t.Errorf("expected untracked-files warning to mention scratch.txt, got: %q", out) + } + if !strings.Contains(out, "NOT be in the archive") { + t.Errorf("expected untracked-files warning fragment, got: %q", out) + } + }) + + t.Run("allowDirty=false aborts before untracked warning", func(t *testing.T) { + var buf bytes.Buffer + err := emitPublishWarnings(dir, false, &buf) + if err == nil { + t.Fatal("expected dirty-tree error, got nil") + } + if !strings.Contains(err.Error(), "working tree dirty") { + t.Errorf("expected dirty-tree error, got: %v", err) + } + if !strings.Contains(err.Error(), "--strict") { + t.Errorf("expected dirty-tree error to reference --strict, got: %v", err) + } + if strings.Contains(buf.String(), "untracked files will NOT be in the archive") { + t.Errorf("untracked-files warning should not fire on dirty-abort path, got: %q", buf.String()) + } + }) +} + +func TestWriteMod_PrivateTrueSerializes(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "plugin.mod") + m := &core.ModFile{Plugin: core.ModPlugin{ + Name: "myplugin", + Scope: "themes", + Version: "0.1.0", + Private: true, + }} + if err := writeMod(path, m); err != nil { + t.Fatalf("writeMod: %v", err) + } + got, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read back: %v", err) + } + if !strings.Contains(string(got), "private = true") { + t.Errorf("expected `private = true` line in plugin.mod, got:\n%s", got) + } +} + +func TestWriteMod_PrivateFalseOmitted(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "plugin.mod") + m := &core.ModFile{Plugin: core.ModPlugin{ + Name: "publicthing", + Scope: "themes", + Version: "0.1.0", + }} + if err := writeMod(path, m); err != nil { + t.Fatalf("writeMod: %v", err) + } + got, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read back: %v", err) + } + if strings.Contains(string(got), "private") { + t.Errorf("expected no `private` line, got:\n%s", got) + } +} + +func TestParsePrivateCoord(t *testing.T) { + cases := []struct { + in string + want string + wantErr bool + }{ + {in: "myplugin", want: "myplugin"}, + {in: "@private/myplugin", want: "myplugin"}, + {in: " myplugin ", want: "myplugin"}, + {in: "@themes/myplugin", wantErr: true}, + {in: "@private", wantErr: true}, + } + for _, c := range cases { + got, err := parsePrivateCoord(c.in) + if c.wantErr { + if err == nil { + t.Errorf("parsePrivateCoord(%q) = %q, want error", c.in, got) + } + continue + } + if err != nil { + t.Errorf("parsePrivateCoord(%q) err: %v", c.in, err) + continue + } + if got != c.want { + t.Errorf("parsePrivateCoord(%q) = %q, want %q", c.in, got, c.want) + } + } +} + +func TestMutateTags_AddRmSetClear(t *testing.T) { + dir := t.TempDir() + t.Chdir(dir) + + must := func(err error) { + t.Helper() + if err != nil { + t.Fatal(err) + } + } + + // Seed plugin.mod with no tags. + must(upsertPluginMod("themes", "darkpro", "Dark Pro", "Sleek dark theme", "theme", []string{}, nil, false)) + + // add + must(mutateTags("add", []string{"dark", "agency"})) + mod, err := readLocalMod() + must(err) + if len(mod.Plugin.Tags) != 2 || mod.Plugin.Tags[0] != "dark" || mod.Plugin.Tags[1] != "agency" { + t.Errorf("after add: %v", mod.Plugin.Tags) + } + + // add (dedupe + normalise) + must(mutateTags("add", []string{"Agency", "Serif"})) + mod, _ = readLocalMod() + if len(mod.Plugin.Tags) != 3 { + t.Errorf("after dedupe add: %v", mod.Plugin.Tags) + } + + // rm + must(mutateTags("rm", []string{"dark"})) + mod, _ = readLocalMod() + for _, tag := range mod.Plugin.Tags { + if tag == "dark" { + t.Errorf("after rm: dark still present: %v", mod.Plugin.Tags) + } + } + + // set + must(mutateTags("set", []string{"editorial"})) + mod, _ = readLocalMod() + if len(mod.Plugin.Tags) != 1 || mod.Plugin.Tags[0] != "editorial" { + t.Errorf("after set: %v", mod.Plugin.Tags) + } + + // clear + must(mutateTags("clear", nil)) + mod, _ = readLocalMod() + if len(mod.Plugin.Tags) != 0 { + t.Errorf("after clear: %v", mod.Plugin.Tags) + } +} + +func TestMutateTags_RejectsInvalidNoWrite(t *testing.T) { + dir := t.TempDir() + t.Chdir(dir) + + if err := upsertPluginMod("themes", "x", "X", "", "theme", nil, []string{"dark"}, false); err != nil { + t.Fatal(err) + } + if err := mutateTags("add", []string{"BAD SPACE"}); err == nil { + t.Fatal("expected validation error") + } + mod, err := readLocalMod() + if err != nil { + t.Fatal(err) + } + if len(mod.Plugin.Tags) != 1 || mod.Plugin.Tags[0] != "dark" { + t.Errorf("tags mutated despite error: %v", mod.Plugin.Tags) + } +} + +func runGit(t *testing.T, dir string, args ...string) { + t.Helper() + cmd := exec.Command("git", args...) + cmd.Dir = dir + cmd.Env = append(os.Environ(), + "GIT_AUTHOR_NAME=t", + "GIT_AUTHOR_EMAIL=t@t", + "GIT_COMMITTER_NAME=t", + "GIT_COMMITTER_EMAIL=t@t", + ) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("git %v: %v\n%s", args, err, out) + } +} diff --git a/cmd/ninja/cmd/root.go b/cmd/ninja/cmd/root.go new file mode 100644 index 0000000..3c8d6be --- /dev/null +++ b/cmd/ninja/cmd/root.go @@ -0,0 +1,22 @@ +package cmd + +import "github.com/spf13/cobra" + +func NewRoot() *cobra.Command { + root := &cobra.Command{ + Use: "ninja", + Short: "BlockNinja developer CLI", + Long: "ninja is the developer-facing CLI for BlockNinja. First subcommand group: plugin.", + } + root.PersistentFlags().String("host", "", "Orchestrator base URL (default: from credentials or https://my.blockninjacms.com)") + root.AddCommand(newVersionCmd()) + root.AddCommand(newLoginCmd()) + root.AddCommand(newLogoutCmd()) + root.AddCommand(newWhoamiCmd()) + root.AddCommand(newPluginCmd()) + root.AddCommand(newThemeCmd()) + root.AddCommand(newScopeCmd()) + root.AddCommand(newAccountCmd()) + root.AddCommand(newAbiCmd()) + return root +} diff --git a/cmd/ninja/cmd/scope.go b/cmd/ninja/cmd/scope.go new file mode 100644 index 0000000..8f8e2bd --- /dev/null +++ b/cmd/ninja/cmd/scope.go @@ -0,0 +1,230 @@ +package cmd + +import ( + "bufio" + "context" + "fmt" + "os" + "strconv" + "strings" + + "connectrpc.com/connect" + "github.com/spf13/cobra" + + "git.dev.alexdunmow.com/block/cli/internal/creds" + "git.dev.alexdunmow.com/block/cli/internal/orchclient" + v1 "git.dev.alexdunmow.com/block/cli/internal/api/orchestrator/v1" +) + +func newScopeCmd() *cobra.Command { + c := &cobra.Command{Use: "scope", Short: "Manage plugin scopes"} + c.AddCommand(newScopeCreateCmd(), newScopeListCmd(), newScopeDefaultCmd()) + return c +} + +func newScopeCreateCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create [scope]", + Short: "Create a new scope (organisation namespace for plugins)", + Args: cobra.MaximumNArgs(1), + RunE: func(c *cobra.Command, args []string) error { + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return err + } + resolvedHost, hc, err := cr.Resolve(host) + if err != nil { + return err + } + cli := orchclient.New(resolvedHost, hc.Token) + ctx := context.Background() + scanner := bufio.NewScanner(os.Stdin) + + var slug string + if len(args) > 0 { + slug, err = parseScope(args[0]) + if err != nil { + return err + } + } else { + slug, err = promptScopeSlug(scanner) + if err != nil { + return err + } + } + + fmt.Printf("Display name [%s]: ", scopeAPISlug(slug)) + displayName := scopeAPISlug(slug) + if scanner.Scan() { + if v := strings.TrimSpace(scanner.Text()); v != "" { + displayName = v + } + } + + _, err = cli.Scope.CreateScope(ctx, connect.NewRequest(&v1.CreateScopeRequest{ + Slug: scopeAPISlug(slug), + DisplayName: displayName, + })) + if err != nil { + return err + } + fmt.Printf("Created scope %s\n", slug) + + fmt.Printf("Set %s as your default scope? [Y/n]: ", slug) + if scanner.Scan() { + ans := strings.ToLower(strings.TrimSpace(scanner.Text())) + if ans == "" || ans == "y" || ans == "yes" { + hc.DefaultScope = slug + cr.Hosts[resolvedHost] = hc + if err := cr.Save(); err != nil { + fmt.Fprintf(os.Stderr, "warning: could not save default scope: %v\n", err) + } else { + fmt.Println("Default scope saved.") + } + } + } + + return nil + }, + } + return cmd +} + +func promptScopeSlug(scanner *bufio.Scanner) (string, error) { + fmt.Println("A scope is an organisation namespace for your plugins (e.g. @acme).") + fmt.Println("It appears in plugin names like @acme/my-plugin.") + fmt.Println() + fmt.Print("Scope slug (lowercase letters, numbers, dashes): ") + if !scanner.Scan() { + return "", fmt.Errorf("cancelled") + } + return parseScope(scanner.Text()) +} + +func newScopeDefaultCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "default", + Short: "Show or change the default scope", + RunE: func(c *cobra.Command, _ []string) error { + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return err + } + _, hc, err := cr.Resolve(host) + if err != nil { + return err + } + if hc.DefaultScope == "" { + fmt.Println("No default scope set. Run: ninja scope default set") + } else { + fmt.Println(hc.DefaultScope) + } + return nil + }, + } + cmd.AddCommand(newScopeDefaultSetCmd()) + return cmd +} + +func newScopeDefaultSetCmd() *cobra.Command { + return &cobra.Command{ + Use: "set", + Short: "Pick a default scope from your scopes", + RunE: func(c *cobra.Command, _ []string) error { + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return err + } + resolvedHost, hc, err := cr.Resolve(host) + if err != nil { + return err + } + cli := orchclient.New(resolvedHost, hc.Token) + ctx := context.Background() + + scopes, err := cli.Scope.ListMyScopes(ctx, connect.NewRequest(&v1.ListMyScopesRequest{})) + if err != nil { + return err + } + if len(scopes.Msg.Scopes) == 0 { + fmt.Println("No scopes yet. Create one with: ninja scope create") + return nil + } + + fmt.Println("Your scopes:") + for i, s := range scopes.Msg.Scopes { + marker := "" + if "@"+s.Slug == hc.DefaultScope { + marker = " (current)" + } + fmt.Printf(" %d. @%s — %s%s\n", i+1, s.Slug, s.DisplayName, marker) + } + fmt.Println() + + scanner := bufio.NewScanner(os.Stdin) + fmt.Print("Select a scope: ") + if !scanner.Scan() { + return fmt.Errorf("cancelled") + } + input := strings.TrimSpace(scanner.Text()) + if input == "" { + return fmt.Errorf("cancelled") + } + + var scope string + if n, err := strconv.Atoi(input); err == nil && n >= 1 && n <= len(scopes.Msg.Scopes) { + scope = "@" + scopes.Msg.Scopes[n-1].Slug + } else { + return fmt.Errorf("invalid selection: %s", input) + } + + hc.DefaultScope = scope + cr.Hosts[resolvedHost] = hc + if err := cr.Save(); err != nil { + return err + } + fmt.Printf("Default scope set to %s\n", scope) + return nil + }, + } +} + +func newScopeListCmd() *cobra.Command { + return &cobra.Command{ + Use: "list", + Short: "List your scopes", + RunE: func(c *cobra.Command, _ []string) error { + host, _ := c.Flags().GetString("host") + cr, err := creds.Load() + if err != nil { + return err + } + resolvedHost, hc, err := cr.Resolve(host) + if err != nil { + return err + } + cli := orchclient.New(resolvedHost, hc.Token) + ctx := context.Background() + + scopes, err := cli.Scope.ListMyScopes(ctx, connect.NewRequest(&v1.ListMyScopesRequest{})) + if err != nil { + return err + } + if len(scopes.Msg.Scopes) == 0 { + fmt.Println("No scopes yet. Create one with: ninja scope create") + return nil + } + for _, s := range scopes.Msg.Scopes { + marker := "" + if "@"+s.Slug == hc.DefaultScope { + marker = " (default)" + } + fmt.Printf("@%s — %s%s\n", s.Slug, s.DisplayName, marker) + } + return nil + }, + } +} diff --git a/cmd/ninja/cmd/testdata/capfixture/main.go b/cmd/ninja/cmd/testdata/capfixture/main.go new file mode 100644 index 0000000..c1cf042 --- /dev/null +++ b/cmd/ninja/cmd/testdata/capfixture/main.go @@ -0,0 +1,8 @@ +//go:build wasip1 + +package main + +import "git.dev.alexdunmow.com/block/core/plugin/wasmguest" + +func init() { wasmguest.Serve(Registration) } +func main() {} // never called — reactor mode diff --git a/cmd/ninja/cmd/testdata/capfixture/plugin.mod b/cmd/ninja/cmd/testdata/capfixture/plugin.mod new file mode 100644 index 0000000..2bb3a67 --- /dev/null +++ b/cmd/ninja/cmd/testdata/capfixture/plugin.mod @@ -0,0 +1,6 @@ +[plugin] +name = "capfixture" +scope = "@blockninja" +version = "0.0.1" +description = "Negative fixture: Register hits a host capability at describe time" +kind = "plugin" diff --git a/cmd/ninja/cmd/testdata/capfixture/registration.go b/cmd/ninja/cmd/testdata/capfixture/registration.go new file mode 100644 index 0000000..d680db3 --- /dev/null +++ b/cmd/ninja/cmd/testdata/capfixture/registration.go @@ -0,0 +1,38 @@ +// Package main is a NEGATIVE build fixture: its Register hook reaches a host +// capability (a db.* call) at describe time, which is illegal — DESCRIBE runs +// with no live host. `ninja plugin build` must surface an actionable error +// naming the offending call rather than hanging or crashing. +package main + +import ( + "context" + "database/sql" + "fmt" + + // Registers the "bnwasm" database/sql driver (its init runs sql.Register). + _ "git.dev.alexdunmow.com/block/core/plugin/wasmguest/bnwasm" + + "git.dev.alexdunmow.com/block/core/blocks" + "git.dev.alexdunmow.com/block/core/plugin" + "git.dev.alexdunmow.com/block/core/templates" +) + +var Registration = plugin.PluginRegistration{ + Name: "capfixture", + Version: "0.0.1", + Register: func(_ templates.TemplateRegistry, _ blocks.BlockRegistry) error { + // Illegal describe-time capability use: query the host DB from + // Register. Over the packer's failing host stub this returns an error + // naming "db.query". + db, err := sql.Open("bnwasm", "") + if err != nil { + return err + } + defer func() { _ = db.Close() }() + var n int + if err := db.QueryRowContext(context.Background(), "SELECT 1").Scan(&n); err != nil { + return fmt.Errorf("describe-time db probe: %w", err) + } + return nil + }, +} diff --git a/cmd/ninja/cmd/testdata/fixture/main.go b/cmd/ninja/cmd/testdata/fixture/main.go new file mode 100644 index 0000000..c1cf042 --- /dev/null +++ b/cmd/ninja/cmd/testdata/fixture/main.go @@ -0,0 +1,8 @@ +//go:build wasip1 + +package main + +import "git.dev.alexdunmow.com/block/core/plugin/wasmguest" + +func init() { wasmguest.Serve(Registration) } +func main() {} // never called — reactor mode diff --git a/cmd/ninja/cmd/testdata/fixture/plugin.mod b/cmd/ninja/cmd/testdata/fixture/plugin.mod new file mode 100644 index 0000000..4c8f7cd --- /dev/null +++ b/cmd/ninja/cmd/testdata/fixture/plugin.mod @@ -0,0 +1,8 @@ +[plugin] +name = "wasmfixture" +display_name = "Wasm Fixture" +scope = "@blockninja" +version = "0.0.1" +description = "WO-WZ-002 acceptance fixture: one block, one template, one admin page" +kind = "plugin" +data_dir = true diff --git a/cmd/ninja/cmd/testdata/fixture/registration.go b/cmd/ninja/cmd/testdata/fixture/registration.go new file mode 100644 index 0000000..a4561d8 --- /dev/null +++ b/cmd/ninja/cmd/testdata/fixture/registration.go @@ -0,0 +1,97 @@ +// Package main is the WO-WZ-002 acceptance fixture: a minimal plugin with +// one block, one template, and one admin page, compiled to wasm with only +// the wasmguest boilerplate (main.go). +package main + +import ( + "context" + "fmt" + "io" + "strings" + + "git.dev.alexdunmow.com/block/core/blocks" + "git.dev.alexdunmow.com/block/core/plugin" + "git.dev.alexdunmow.com/block/core/templates" +) + +// capReport is populated by the Load hook from capability calls, then +// surfaced through the greeting block ({"report": true}). It is the runtime +// proof that deps.Content / deps.Settings / deps.Bridge cross the wasm ABI +// end-to-end through the guest capability stubs (WO-WZ-003 acceptance). +var capReport string + +// Registration is the fixture's plugin registration — the same shape a .so +// plugin exports, untouched by the wasm migration. +var Registration = plugin.PluginRegistration{ + Name: "wasmfixture", + Version: "0.0.1", + Register: func(tr templates.TemplateRegistry, br blocks.BlockRegistry) error { + br.Register(blocks.BlockMeta{ + Key: "greeting", + Title: "Greeting", + Description: "Renders a greeting", + Category: blocks.CategoryContent, + }, func(ctx context.Context, content map[string]any) string { + if boom, _ := content["boom"].(bool); boom { + panic("fixture kaboom") + } + if report, _ := content["report"].(bool); report { + return "

capreport:" + capReport + "

" + } + name, _ := content["name"].(string) + if name == "" { + name = "world" + } + if powered, _ := content["powered"].(bool); powered { + // Powered path: hand the host a template + data instead of final + // HTML, so the host renders it (pongo2) after this call returns. + return blocks.PoweredBlock("

hello {{ name }}

", map[string]any{"name": name}) + } + return fmt.Sprintf("

hello %s

", name) + }) + // Custom template tag + filter the host wires from manifest.declared_tags + // / declared_filters, invoked via HOOK_RENDER_TAG / HOOK_APPLY_FILTER. + blocks.RegisterTag("shout", func(args map[string]any, rctx blocks.RenderContext) (string, error) { + msg, _ := args["msg"].(string) + return "" + strings.ToUpper(msg) + "", nil + }) + blocks.RegisterFilter("exclaim", func(input string, args map[string]any) (string, error) { + return input + "!", nil + }) + return tr.Register("wasmfixture-page", func(ctx context.Context, doc map[string]any) templates.HTMLComponent { + title, _ := doc["title"].(string) + return htmlString("" + title + "") + }) + }, + // Load exercises the guest capability stubs exactly as a real plugin's + // service code does — unchanged calls against deps.Content / deps.Settings + // / deps.Bridge — proving they compile and cross the ABI for wasip1. + Load: func(deps plugin.CoreServices) error { + page, err := deps.Content.GetPage(context.Background(), "about") + if err != nil { + capReport = "content-err:" + err.Error() + return nil + } + site, err := deps.Settings.GetSiteSettings(context.Background()) + if err != nil { + capReport = "settings-err:" + err.Error() + return nil + } + svc := deps.Bridge.GetService("other", "search") // cannot cross; expected nil + capReport = fmt.Sprintf("page=%s site=%v bridge_nil=%t", + page.Title, site["site_name"], svc == nil) + return nil + }, + AdminPages: func() []plugin.AdminPage { + return []plugin.AdminPage{{ + Key: "wasmfixture", Title: "Wasm Fixture", Icon: "flask", Route: "/admin/wasmfixture", + }} + }, +} + +type htmlString string + +func (s htmlString) Render(_ context.Context, w io.Writer) error { + _, err := io.WriteString(w, string(s)) + return err +} diff --git a/cmd/ninja/cmd/theme.go b/cmd/ninja/cmd/theme.go new file mode 100644 index 0000000..2ca8e15 --- /dev/null +++ b/cmd/ninja/cmd/theme.go @@ -0,0 +1,108 @@ +package cmd + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/spf13/cobra" + + core "git.dev.alexdunmow.com/block/core/plugin" + + "git.dev.alexdunmow.com/block/cli/internal/shot" +) + +func newThemeCmd() *cobra.Command { + c := &cobra.Command{Use: "theme", Short: "Theme authoring helpers (preview screenshots)"} + c.AddCommand(newThemeScreenshotCmd()) + return c +} + +func newThemeScreenshotCmd() *cobra.Command { + var gallery, slug, out, mobileOut, themeName, waitSelector string + var mobile bool + var width, height int + cmd := &cobra.Command{ + Use: "screenshot", + Short: "Render this theme's showcase page and write preview.png into the repo", + Long: `screenshot builds the gallery URL for this theme's showcase page +(rendered via the CMS render-only ?preview_template override), drives a headless +Chromium against it, and writes the captured PNG into the theme repo (preview.png, +git-tracked — the source of truth a human may later replace). + +The theme name defaults to plugin.mod's name in the current directory. Point +--gallery at the gallery CMS site that has this theme's .so loaded and the +showcase content seeded. --host selects the orchestrator (defaults to PROD); the +gallery URL is independent of --host but the flag is accepted for parity with +the rest of the CLI.`, + RunE: func(c *cobra.Command, _ []string) error { + if themeName == "" { + modBytes, err := os.ReadFile("plugin.mod") + if err != nil { + return fmt.Errorf("read plugin.mod (run from the theme repo, or pass --theme): %w", err) + } + mod, err := core.ParseModFull(modBytes) + if err != nil { + return err + } + if mod.Plugin.Kind != "theme" { + return fmt.Errorf("plugin.mod kind = %q, want theme", mod.Plugin.Kind) + } + themeName = mod.Plugin.Name + } + if gallery == "" { + return fmt.Errorf("--gallery is required (the gallery CMS site base, e.g. https://showcase.localdev.blockninjacms.com)") + } + + ctx := context.Background() + + desktopURL := shot.PreviewURL(gallery, slug, themeName) + fmt.Fprintf(os.Stderr, "capturing desktop: %s\n", desktopURL) + png, err := shot.Capture(ctx, shot.Options{ + URL: desktopURL, + Width: width, + Height: height, + WaitSelector: waitSelector, + Timeout: 45 * time.Second, + }) + if err != nil { + return err + } + if err := os.WriteFile(out, png, 0o644); err != nil { + return fmt.Errorf("write %s: %w", out, err) + } + fmt.Printf("wrote %s (%d bytes)\n", out, len(png)) + + if mobile { + mURL := shot.PreviewURL(gallery, slug, themeName) + fmt.Fprintf(os.Stderr, "capturing mobile: %s\n", mURL) + mpng, err := shot.Capture(ctx, shot.Options{ + URL: mURL, + Width: 390, + Height: 844, + WaitSelector: waitSelector, + Timeout: 45 * time.Second, + }) + if err != nil { + return err + } + if err := os.WriteFile(mobileOut, mpng, 0o644); err != nil { + return fmt.Errorf("write %s: %w", mobileOut, err) + } + fmt.Printf("wrote %s (%d bytes)\n", mobileOut, len(mpng)) + } + return nil + }, + } + cmd.Flags().StringVar(&gallery, "gallery", "", "Gallery CMS site base URL (has this theme loaded + showcase seeded)") + cmd.Flags().StringVar(&slug, "slug", "/", "Showcase page slug to capture") + cmd.Flags().StringVar(&out, "out", "preview.png", "Output path for the desktop screenshot (git-tracked in the theme repo)") + cmd.Flags().StringVar(&mobileOut, "mobile-out", "preview-mobile.png", "Output path for the mobile screenshot") + cmd.Flags().StringVar(&themeName, "theme", "", "Theme key to preview (default: plugin.mod name)") + cmd.Flags().StringVar(&waitSelector, "wait", "section", "CSS selector to wait for before capturing") + cmd.Flags().BoolVar(&mobile, "mobile", false, "Also capture preview-mobile.png at a phone viewport") + cmd.Flags().IntVar(&width, "width", 1440, "Desktop viewport width") + cmd.Flags().IntVar(&height, "height", 900, "Desktop viewport height") + return cmd +} diff --git a/cmd/ninja/cmd/theme_test.go b/cmd/ninja/cmd/theme_test.go new file mode 100644 index 0000000..8859451 --- /dev/null +++ b/cmd/ninja/cmd/theme_test.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "strings" + "testing" +) + +func TestThemeScreenshotCommandRegistered(t *testing.T) { + root := NewRoot() + theme, _, err := root.Find([]string{"theme", "screenshot"}) + if err != nil { + t.Fatalf("find theme screenshot: %v", err) + } + if theme.Name() != "screenshot" { + t.Fatalf("resolved command = %q, want screenshot", theme.Name()) + } +} + +func TestThemeScreenshotHasGalleryAndMobileFlags(t *testing.T) { + root := NewRoot() + cmd, _, _ := root.Find([]string{"theme", "screenshot"}) + for _, name := range []string{"gallery", "mobile", "out", "slug"} { + if cmd.Flags().Lookup(name) == nil { + t.Errorf("missing --%s flag", name) + } + } +} + +func TestThemeScreenshotDefaultOutIsPreviewPng(t *testing.T) { + root := NewRoot() + cmd, _, _ := root.Find([]string{"theme", "screenshot"}) + out, _ := cmd.Flags().GetString("out") + if !strings.HasSuffix(out, "preview.png") { + t.Fatalf("default --out = %q, want it to end with preview.png", out) + } +} diff --git a/cmd/ninja/cmd/version.go b/cmd/ninja/cmd/version.go new file mode 100644 index 0000000..a5501c9 --- /dev/null +++ b/cmd/ninja/cmd/version.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var Version = "dev" + +func newVersionCmd() *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Print ninja version", + Run: func(_ *cobra.Command, _ []string) { + fmt.Println("ninja", Version) + }, + } +} diff --git a/cmd/ninja/main.go b/cmd/ninja/main.go new file mode 100644 index 0000000..0f38582 --- /dev/null +++ b/cmd/ninja/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "os" + + "git.dev.alexdunmow.com/block/cli/cmd/ninja/cmd" +) + +func main() { + if err := cmd.NewRoot().Execute(); err != nil { + fmt.Fprintln(os.Stderr, "Error:", err) + os.Exit(1) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a34deb0 --- /dev/null +++ b/go.mod @@ -0,0 +1,34 @@ +module git.dev.alexdunmow.com/block/cli + +go 1.26.4 + +require ( + connectrpc.com/connect v1.20.0 + git.dev.alexdunmow.com/block/core v0.18.2 + github.com/chromedp/chromedp v0.15.1 + github.com/klauspost/compress v1.18.6 + github.com/spf13/cobra v1.10.2 + github.com/tetratelabs/wazero v1.12.0 + google.golang.org/protobuf v1.36.11 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/BurntSushi/toml v1.6.0 // indirect + github.com/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc // indirect + github.com/chromedp/sysutil v1.1.0 // indirect + github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect + github.com/gobwas/ws v1.4.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.9.2 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/spf13/pflag v1.0.9 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/sys v0.44.0 // indirect + golang.org/x/text v0.36.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..261e5c6 --- /dev/null +++ b/go.sum @@ -0,0 +1,83 @@ +connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ= +connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= +git.dev.alexdunmow.com/block/core v0.18.2 h1:+3OfZ424yoc1k1CucXYARk8TBkh8Z693HRkhf5Ci2wU= +git.dev.alexdunmow.com/block/core v0.18.2/go.mod h1:GGuUu826AoJepC/hKLGJ7BX3PQaDss9ueCT0se6Ao2w= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc h1:wkN/LMi5vc60pBRWx6qpbk/aEvq3/ZVNpnMvsw8PVVU= +github.com/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc/go.mod h1:cbyjALe67vDvlvdiG9369P8w5U2w6IshwtyD2f2Tvag= +github.com/chromedp/chromedp v0.15.1 h1:EJWiPm7BNqDqjYy6U0lTSL5wNH+iNt9GjC3a4gfjNyQ= +github.com/chromedp/chromedp v0.15.1/go.mod h1:CdTHtUqD/dqaFw/cvFWtTydoEQS44wLBuwbMR9EkOY4= +github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM= +github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 h1:vymEbVwYFP/L05h5TKQxvkXoKxNvTpjxYKdF1Nlwuao= +github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs= +github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.15.0 h1:D0RCU5rMAp+SpgkiNdrjfJ+LX4J1M32V2NeCY7EJ6hc= +github.com/rogpeppe/go-internal v1.15.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU= +github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/api/orchestrator/v1/orchestratorv1connect/plugin_registry.connect.go b/internal/api/orchestrator/v1/orchestratorv1connect/plugin_registry.connect.go new file mode 100644 index 0000000..4ce19a2 --- /dev/null +++ b/internal/api/orchestrator/v1/orchestratorv1connect/plugin_registry.connect.go @@ -0,0 +1,1167 @@ +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: orchestrator/v1/plugin_registry.proto + +package orchestratorv1connect + +import ( + connect "connectrpc.com/connect" + context "context" + errors "errors" + v1 "git.dev.alexdunmow.com/block/cli/internal/api/orchestrator/v1" + http "net/http" + strings "strings" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_13_0 + +const ( + // PluginScopeServiceName is the fully-qualified name of the PluginScopeService service. + PluginScopeServiceName = "orchestrator.v1.PluginScopeService" + // PluginRegistryServiceName is the fully-qualified name of the PluginRegistryService service. + PluginRegistryServiceName = "orchestrator.v1.PluginRegistryService" + // PluginModerationServiceName is the fully-qualified name of the PluginModerationService service. + PluginModerationServiceName = "orchestrator.v1.PluginModerationService" + // PluginPublishServiceName is the fully-qualified name of the PluginPublishService service. + PluginPublishServiceName = "orchestrator.v1.PluginPublishService" + // PluginAuthServiceName is the fully-qualified name of the PluginAuthService service. + PluginAuthServiceName = "orchestrator.v1.PluginAuthService" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // PluginScopeServiceCreateScopeProcedure is the fully-qualified name of the PluginScopeService's + // CreateScope RPC. + PluginScopeServiceCreateScopeProcedure = "/orchestrator.v1.PluginScopeService/CreateScope" + // PluginScopeServiceListMyScopesProcedure is the fully-qualified name of the PluginScopeService's + // ListMyScopes RPC. + PluginScopeServiceListMyScopesProcedure = "/orchestrator.v1.PluginScopeService/ListMyScopes" + // PluginScopeServiceGetScopeProcedure is the fully-qualified name of the PluginScopeService's + // GetScope RPC. + PluginScopeServiceGetScopeProcedure = "/orchestrator.v1.PluginScopeService/GetScope" + // PluginScopeServiceListMyPluginsProcedure is the fully-qualified name of the PluginScopeService's + // ListMyPlugins RPC. + PluginScopeServiceListMyPluginsProcedure = "/orchestrator.v1.PluginScopeService/ListMyPlugins" + // PluginRegistryServiceCreatePluginProcedure is the fully-qualified name of the + // PluginRegistryService's CreatePlugin RPC. + PluginRegistryServiceCreatePluginProcedure = "/orchestrator.v1.PluginRegistryService/CreatePlugin" + // PluginRegistryServiceGetPluginProcedure is the fully-qualified name of the + // PluginRegistryService's GetPlugin RPC. + PluginRegistryServiceGetPluginProcedure = "/orchestrator.v1.PluginRegistryService/GetPlugin" + // PluginRegistryServiceListPluginsProcedure is the fully-qualified name of the + // PluginRegistryService's ListPlugins RPC. + PluginRegistryServiceListPluginsProcedure = "/orchestrator.v1.PluginRegistryService/ListPlugins" + // PluginRegistryServiceGetVersionProcedure is the fully-qualified name of the + // PluginRegistryService's GetVersion RPC. + PluginRegistryServiceGetVersionProcedure = "/orchestrator.v1.PluginRegistryService/GetVersion" + // PluginRegistryServiceResolveInstallProcedure is the fully-qualified name of the + // PluginRegistryService's ResolveInstall RPC. + PluginRegistryServiceResolveInstallProcedure = "/orchestrator.v1.PluginRegistryService/ResolveInstall" + // PluginRegistryServiceListCategoriesProcedure is the fully-qualified name of the + // PluginRegistryService's ListCategories RPC. + PluginRegistryServiceListCategoriesProcedure = "/orchestrator.v1.PluginRegistryService/ListCategories" + // PluginRegistryServiceListTagsProcedure is the fully-qualified name of the PluginRegistryService's + // ListTags RPC. + PluginRegistryServiceListTagsProcedure = "/orchestrator.v1.PluginRegistryService/ListTags" + // PluginRegistryServiceSubmitForReviewProcedure is the fully-qualified name of the + // PluginRegistryService's SubmitForReview RPC. + PluginRegistryServiceSubmitForReviewProcedure = "/orchestrator.v1.PluginRegistryService/SubmitForReview" + // PluginRegistryServiceListPrivatePluginsProcedure is the fully-qualified name of the + // PluginRegistryService's ListPrivatePlugins RPC. + PluginRegistryServiceListPrivatePluginsProcedure = "/orchestrator.v1.PluginRegistryService/ListPrivatePlugins" + // PluginRegistryServiceDeletePrivatePluginProcedure is the fully-qualified name of the + // PluginRegistryService's DeletePrivatePlugin RPC. + PluginRegistryServiceDeletePrivatePluginProcedure = "/orchestrator.v1.PluginRegistryService/DeletePrivatePlugin" + // PluginRegistryServiceDeletePrivatePluginVersionProcedure is the fully-qualified name of the + // PluginRegistryService's DeletePrivatePluginVersion RPC. + PluginRegistryServiceDeletePrivatePluginVersionProcedure = "/orchestrator.v1.PluginRegistryService/DeletePrivatePluginVersion" + // PluginRegistryServiceListPrivatePluginInstallSitesProcedure is the fully-qualified name of the + // PluginRegistryService's ListPrivatePluginInstallSites RPC. + PluginRegistryServiceListPrivatePluginInstallSitesProcedure = "/orchestrator.v1.PluginRegistryService/ListPrivatePluginInstallSites" + // PluginRegistryServiceListPrivatePluginsForInstanceProcedure is the fully-qualified name of the + // PluginRegistryService's ListPrivatePluginsForInstance RPC. + PluginRegistryServiceListPrivatePluginsForInstanceProcedure = "/orchestrator.v1.PluginRegistryService/ListPrivatePluginsForInstance" + // PluginRegistryServiceResolveInstallForInstanceProcedure is the fully-qualified name of the + // PluginRegistryService's ResolveInstallForInstance RPC. + PluginRegistryServiceResolveInstallForInstanceProcedure = "/orchestrator.v1.PluginRegistryService/ResolveInstallForInstance" + // PluginModerationServiceListPendingReviewsProcedure is the fully-qualified name of the + // PluginModerationService's ListPendingReviews RPC. + PluginModerationServiceListPendingReviewsProcedure = "/orchestrator.v1.PluginModerationService/ListPendingReviews" + // PluginModerationServiceApproveSubmissionProcedure is the fully-qualified name of the + // PluginModerationService's ApproveSubmission RPC. + PluginModerationServiceApproveSubmissionProcedure = "/orchestrator.v1.PluginModerationService/ApproveSubmission" + // PluginModerationServiceRejectSubmissionProcedure is the fully-qualified name of the + // PluginModerationService's RejectSubmission RPC. + PluginModerationServiceRejectSubmissionProcedure = "/orchestrator.v1.PluginModerationService/RejectSubmission" + // PluginModerationServiceRequestChangesProcedure is the fully-qualified name of the + // PluginModerationService's RequestChanges RPC. + PluginModerationServiceRequestChangesProcedure = "/orchestrator.v1.PluginModerationService/RequestChanges" + // PluginPublishServicePublishVersionProcedure is the fully-qualified name of the + // PluginPublishService's PublishVersion RPC. + PluginPublishServicePublishVersionProcedure = "/orchestrator.v1.PluginPublishService/PublishVersion" + // PluginAuthServiceStartDeviceProcedure is the fully-qualified name of the PluginAuthService's + // StartDevice RPC. + PluginAuthServiceStartDeviceProcedure = "/orchestrator.v1.PluginAuthService/StartDevice" + // PluginAuthServicePollDeviceProcedure is the fully-qualified name of the PluginAuthService's + // PollDevice RPC. + PluginAuthServicePollDeviceProcedure = "/orchestrator.v1.PluginAuthService/PollDevice" + // PluginAuthServiceApproveDeviceProcedure is the fully-qualified name of the PluginAuthService's + // ApproveDevice RPC. + PluginAuthServiceApproveDeviceProcedure = "/orchestrator.v1.PluginAuthService/ApproveDevice" + // PluginAuthServiceDenyDeviceProcedure is the fully-qualified name of the PluginAuthService's + // DenyDevice RPC. + PluginAuthServiceDenyDeviceProcedure = "/orchestrator.v1.PluginAuthService/DenyDevice" + // PluginAuthServiceGetDeviceStatusProcedure is the fully-qualified name of the PluginAuthService's + // GetDeviceStatus RPC. + PluginAuthServiceGetDeviceStatusProcedure = "/orchestrator.v1.PluginAuthService/GetDeviceStatus" + // PluginAuthServiceWhoamiProcedure is the fully-qualified name of the PluginAuthService's Whoami + // RPC. + PluginAuthServiceWhoamiProcedure = "/orchestrator.v1.PluginAuthService/Whoami" + // PluginAuthServiceListMyAccountsForCLIProcedure is the fully-qualified name of the + // PluginAuthService's ListMyAccountsForCLI RPC. + PluginAuthServiceListMyAccountsForCLIProcedure = "/orchestrator.v1.PluginAuthService/ListMyAccountsForCLI" +) + +// PluginScopeServiceClient is a client for the orchestrator.v1.PluginScopeService service. +type PluginScopeServiceClient interface { + CreateScope(context.Context, *connect.Request[v1.CreateScopeRequest]) (*connect.Response[v1.CreateScopeResponse], error) + ListMyScopes(context.Context, *connect.Request[v1.ListMyScopesRequest]) (*connect.Response[v1.ListMyScopesResponse], error) + GetScope(context.Context, *connect.Request[v1.GetScopeRequest]) (*connect.Response[v1.GetScopeResponse], error) + ListMyPlugins(context.Context, *connect.Request[v1.ListMyPluginsRequest]) (*connect.Response[v1.ListMyPluginsResponse], error) +} + +// NewPluginScopeServiceClient constructs a client for the orchestrator.v1.PluginScopeService +// service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for +// gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply +// the connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewPluginScopeServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) PluginScopeServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + pluginScopeServiceMethods := v1.File_orchestrator_v1_plugin_registry_proto.Services().ByName("PluginScopeService").Methods() + return &pluginScopeServiceClient{ + createScope: connect.NewClient[v1.CreateScopeRequest, v1.CreateScopeResponse]( + httpClient, + baseURL+PluginScopeServiceCreateScopeProcedure, + connect.WithSchema(pluginScopeServiceMethods.ByName("CreateScope")), + connect.WithClientOptions(opts...), + ), + listMyScopes: connect.NewClient[v1.ListMyScopesRequest, v1.ListMyScopesResponse]( + httpClient, + baseURL+PluginScopeServiceListMyScopesProcedure, + connect.WithSchema(pluginScopeServiceMethods.ByName("ListMyScopes")), + connect.WithClientOptions(opts...), + ), + getScope: connect.NewClient[v1.GetScopeRequest, v1.GetScopeResponse]( + httpClient, + baseURL+PluginScopeServiceGetScopeProcedure, + connect.WithSchema(pluginScopeServiceMethods.ByName("GetScope")), + connect.WithClientOptions(opts...), + ), + listMyPlugins: connect.NewClient[v1.ListMyPluginsRequest, v1.ListMyPluginsResponse]( + httpClient, + baseURL+PluginScopeServiceListMyPluginsProcedure, + connect.WithSchema(pluginScopeServiceMethods.ByName("ListMyPlugins")), + connect.WithClientOptions(opts...), + ), + } +} + +// pluginScopeServiceClient implements PluginScopeServiceClient. +type pluginScopeServiceClient struct { + createScope *connect.Client[v1.CreateScopeRequest, v1.CreateScopeResponse] + listMyScopes *connect.Client[v1.ListMyScopesRequest, v1.ListMyScopesResponse] + getScope *connect.Client[v1.GetScopeRequest, v1.GetScopeResponse] + listMyPlugins *connect.Client[v1.ListMyPluginsRequest, v1.ListMyPluginsResponse] +} + +// CreateScope calls orchestrator.v1.PluginScopeService.CreateScope. +func (c *pluginScopeServiceClient) CreateScope(ctx context.Context, req *connect.Request[v1.CreateScopeRequest]) (*connect.Response[v1.CreateScopeResponse], error) { + return c.createScope.CallUnary(ctx, req) +} + +// ListMyScopes calls orchestrator.v1.PluginScopeService.ListMyScopes. +func (c *pluginScopeServiceClient) ListMyScopes(ctx context.Context, req *connect.Request[v1.ListMyScopesRequest]) (*connect.Response[v1.ListMyScopesResponse], error) { + return c.listMyScopes.CallUnary(ctx, req) +} + +// GetScope calls orchestrator.v1.PluginScopeService.GetScope. +func (c *pluginScopeServiceClient) GetScope(ctx context.Context, req *connect.Request[v1.GetScopeRequest]) (*connect.Response[v1.GetScopeResponse], error) { + return c.getScope.CallUnary(ctx, req) +} + +// ListMyPlugins calls orchestrator.v1.PluginScopeService.ListMyPlugins. +func (c *pluginScopeServiceClient) ListMyPlugins(ctx context.Context, req *connect.Request[v1.ListMyPluginsRequest]) (*connect.Response[v1.ListMyPluginsResponse], error) { + return c.listMyPlugins.CallUnary(ctx, req) +} + +// PluginScopeServiceHandler is an implementation of the orchestrator.v1.PluginScopeService service. +type PluginScopeServiceHandler interface { + CreateScope(context.Context, *connect.Request[v1.CreateScopeRequest]) (*connect.Response[v1.CreateScopeResponse], error) + ListMyScopes(context.Context, *connect.Request[v1.ListMyScopesRequest]) (*connect.Response[v1.ListMyScopesResponse], error) + GetScope(context.Context, *connect.Request[v1.GetScopeRequest]) (*connect.Response[v1.GetScopeResponse], error) + ListMyPlugins(context.Context, *connect.Request[v1.ListMyPluginsRequest]) (*connect.Response[v1.ListMyPluginsResponse], error) +} + +// NewPluginScopeServiceHandler builds an HTTP handler from the service implementation. It returns +// the path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewPluginScopeServiceHandler(svc PluginScopeServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + pluginScopeServiceMethods := v1.File_orchestrator_v1_plugin_registry_proto.Services().ByName("PluginScopeService").Methods() + pluginScopeServiceCreateScopeHandler := connect.NewUnaryHandler( + PluginScopeServiceCreateScopeProcedure, + svc.CreateScope, + connect.WithSchema(pluginScopeServiceMethods.ByName("CreateScope")), + connect.WithHandlerOptions(opts...), + ) + pluginScopeServiceListMyScopesHandler := connect.NewUnaryHandler( + PluginScopeServiceListMyScopesProcedure, + svc.ListMyScopes, + connect.WithSchema(pluginScopeServiceMethods.ByName("ListMyScopes")), + connect.WithHandlerOptions(opts...), + ) + pluginScopeServiceGetScopeHandler := connect.NewUnaryHandler( + PluginScopeServiceGetScopeProcedure, + svc.GetScope, + connect.WithSchema(pluginScopeServiceMethods.ByName("GetScope")), + connect.WithHandlerOptions(opts...), + ) + pluginScopeServiceListMyPluginsHandler := connect.NewUnaryHandler( + PluginScopeServiceListMyPluginsProcedure, + svc.ListMyPlugins, + connect.WithSchema(pluginScopeServiceMethods.ByName("ListMyPlugins")), + connect.WithHandlerOptions(opts...), + ) + return "/orchestrator.v1.PluginScopeService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case PluginScopeServiceCreateScopeProcedure: + pluginScopeServiceCreateScopeHandler.ServeHTTP(w, r) + case PluginScopeServiceListMyScopesProcedure: + pluginScopeServiceListMyScopesHandler.ServeHTTP(w, r) + case PluginScopeServiceGetScopeProcedure: + pluginScopeServiceGetScopeHandler.ServeHTTP(w, r) + case PluginScopeServiceListMyPluginsProcedure: + pluginScopeServiceListMyPluginsHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedPluginScopeServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedPluginScopeServiceHandler struct{} + +func (UnimplementedPluginScopeServiceHandler) CreateScope(context.Context, *connect.Request[v1.CreateScopeRequest]) (*connect.Response[v1.CreateScopeResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginScopeService.CreateScope is not implemented")) +} + +func (UnimplementedPluginScopeServiceHandler) ListMyScopes(context.Context, *connect.Request[v1.ListMyScopesRequest]) (*connect.Response[v1.ListMyScopesResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginScopeService.ListMyScopes is not implemented")) +} + +func (UnimplementedPluginScopeServiceHandler) GetScope(context.Context, *connect.Request[v1.GetScopeRequest]) (*connect.Response[v1.GetScopeResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginScopeService.GetScope is not implemented")) +} + +func (UnimplementedPluginScopeServiceHandler) ListMyPlugins(context.Context, *connect.Request[v1.ListMyPluginsRequest]) (*connect.Response[v1.ListMyPluginsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginScopeService.ListMyPlugins is not implemented")) +} + +// PluginRegistryServiceClient is a client for the orchestrator.v1.PluginRegistryService service. +type PluginRegistryServiceClient interface { + CreatePlugin(context.Context, *connect.Request[v1.CreatePluginRequest]) (*connect.Response[v1.CreatePluginResponse], error) + GetPlugin(context.Context, *connect.Request[v1.GetPluginRequest]) (*connect.Response[v1.GetPluginResponse], error) + ListPlugins(context.Context, *connect.Request[v1.ListPluginsRequest]) (*connect.Response[v1.ListPluginsResponse], error) + GetVersion(context.Context, *connect.Request[v1.GetVersionRequest]) (*connect.Response[v1.GetVersionResponse], error) + ResolveInstall(context.Context, *connect.Request[v1.ResolveInstallRequest]) (*connect.Response[v1.ResolveInstallResponse], error) + ListCategories(context.Context, *connect.Request[v1.ListCategoriesRequest]) (*connect.Response[v1.ListCategoriesResponse], error) + ListTags(context.Context, *connect.Request[v1.ListTagsRequest]) (*connect.Response[v1.ListTagsResponse], error) + SubmitForReview(context.Context, *connect.Request[v1.SubmitForReviewRequest]) (*connect.Response[v1.SubmitForReviewResponse], error) + // Private-plugin RPCs. All require the caller to be a member of the target + // account; the server resolves account membership from the bearer token. + ListPrivatePlugins(context.Context, *connect.Request[v1.ListPrivatePluginsRequest]) (*connect.Response[v1.ListPrivatePluginsResponse], error) + DeletePrivatePlugin(context.Context, *connect.Request[v1.DeletePrivatePluginRequest]) (*connect.Response[v1.DeletePrivatePluginResponse], error) + DeletePrivatePluginVersion(context.Context, *connect.Request[v1.DeletePrivatePluginVersionRequest]) (*connect.Response[v1.DeletePrivatePluginVersionResponse], error) + ListPrivatePluginInstallSites(context.Context, *connect.Request[v1.ListPrivatePluginInstallSitesRequest]) (*connect.Response[v1.ListPrivatePluginInstallSitesResponse], error) + // 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). + ListPrivatePluginsForInstance(context.Context, *connect.Request[v1.ListPrivatePluginsForInstanceRequest]) (*connect.Response[v1.ListPrivatePluginsResponse], error) + ResolveInstallForInstance(context.Context, *connect.Request[v1.ResolveInstallForInstanceRequest]) (*connect.Response[v1.ResolveInstallResponse], error) +} + +// NewPluginRegistryServiceClient constructs a client for the orchestrator.v1.PluginRegistryService +// service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for +// gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply +// the connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewPluginRegistryServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) PluginRegistryServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + pluginRegistryServiceMethods := v1.File_orchestrator_v1_plugin_registry_proto.Services().ByName("PluginRegistryService").Methods() + return &pluginRegistryServiceClient{ + createPlugin: connect.NewClient[v1.CreatePluginRequest, v1.CreatePluginResponse]( + httpClient, + baseURL+PluginRegistryServiceCreatePluginProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("CreatePlugin")), + connect.WithClientOptions(opts...), + ), + getPlugin: connect.NewClient[v1.GetPluginRequest, v1.GetPluginResponse]( + httpClient, + baseURL+PluginRegistryServiceGetPluginProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("GetPlugin")), + connect.WithClientOptions(opts...), + ), + listPlugins: connect.NewClient[v1.ListPluginsRequest, v1.ListPluginsResponse]( + httpClient, + baseURL+PluginRegistryServiceListPluginsProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListPlugins")), + connect.WithClientOptions(opts...), + ), + getVersion: connect.NewClient[v1.GetVersionRequest, v1.GetVersionResponse]( + httpClient, + baseURL+PluginRegistryServiceGetVersionProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("GetVersion")), + connect.WithClientOptions(opts...), + ), + resolveInstall: connect.NewClient[v1.ResolveInstallRequest, v1.ResolveInstallResponse]( + httpClient, + baseURL+PluginRegistryServiceResolveInstallProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ResolveInstall")), + connect.WithClientOptions(opts...), + ), + listCategories: connect.NewClient[v1.ListCategoriesRequest, v1.ListCategoriesResponse]( + httpClient, + baseURL+PluginRegistryServiceListCategoriesProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListCategories")), + connect.WithClientOptions(opts...), + ), + listTags: connect.NewClient[v1.ListTagsRequest, v1.ListTagsResponse]( + httpClient, + baseURL+PluginRegistryServiceListTagsProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListTags")), + connect.WithClientOptions(opts...), + ), + submitForReview: connect.NewClient[v1.SubmitForReviewRequest, v1.SubmitForReviewResponse]( + httpClient, + baseURL+PluginRegistryServiceSubmitForReviewProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("SubmitForReview")), + connect.WithClientOptions(opts...), + ), + listPrivatePlugins: connect.NewClient[v1.ListPrivatePluginsRequest, v1.ListPrivatePluginsResponse]( + httpClient, + baseURL+PluginRegistryServiceListPrivatePluginsProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListPrivatePlugins")), + connect.WithClientOptions(opts...), + ), + deletePrivatePlugin: connect.NewClient[v1.DeletePrivatePluginRequest, v1.DeletePrivatePluginResponse]( + httpClient, + baseURL+PluginRegistryServiceDeletePrivatePluginProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("DeletePrivatePlugin")), + connect.WithClientOptions(opts...), + ), + deletePrivatePluginVersion: connect.NewClient[v1.DeletePrivatePluginVersionRequest, v1.DeletePrivatePluginVersionResponse]( + httpClient, + baseURL+PluginRegistryServiceDeletePrivatePluginVersionProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("DeletePrivatePluginVersion")), + connect.WithClientOptions(opts...), + ), + listPrivatePluginInstallSites: connect.NewClient[v1.ListPrivatePluginInstallSitesRequest, v1.ListPrivatePluginInstallSitesResponse]( + httpClient, + baseURL+PluginRegistryServiceListPrivatePluginInstallSitesProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListPrivatePluginInstallSites")), + connect.WithClientOptions(opts...), + ), + listPrivatePluginsForInstance: connect.NewClient[v1.ListPrivatePluginsForInstanceRequest, v1.ListPrivatePluginsResponse]( + httpClient, + baseURL+PluginRegistryServiceListPrivatePluginsForInstanceProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListPrivatePluginsForInstance")), + connect.WithClientOptions(opts...), + ), + resolveInstallForInstance: connect.NewClient[v1.ResolveInstallForInstanceRequest, v1.ResolveInstallResponse]( + httpClient, + baseURL+PluginRegistryServiceResolveInstallForInstanceProcedure, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ResolveInstallForInstance")), + connect.WithClientOptions(opts...), + ), + } +} + +// pluginRegistryServiceClient implements PluginRegistryServiceClient. +type pluginRegistryServiceClient struct { + createPlugin *connect.Client[v1.CreatePluginRequest, v1.CreatePluginResponse] + getPlugin *connect.Client[v1.GetPluginRequest, v1.GetPluginResponse] + listPlugins *connect.Client[v1.ListPluginsRequest, v1.ListPluginsResponse] + getVersion *connect.Client[v1.GetVersionRequest, v1.GetVersionResponse] + resolveInstall *connect.Client[v1.ResolveInstallRequest, v1.ResolveInstallResponse] + listCategories *connect.Client[v1.ListCategoriesRequest, v1.ListCategoriesResponse] + listTags *connect.Client[v1.ListTagsRequest, v1.ListTagsResponse] + submitForReview *connect.Client[v1.SubmitForReviewRequest, v1.SubmitForReviewResponse] + listPrivatePlugins *connect.Client[v1.ListPrivatePluginsRequest, v1.ListPrivatePluginsResponse] + deletePrivatePlugin *connect.Client[v1.DeletePrivatePluginRequest, v1.DeletePrivatePluginResponse] + deletePrivatePluginVersion *connect.Client[v1.DeletePrivatePluginVersionRequest, v1.DeletePrivatePluginVersionResponse] + listPrivatePluginInstallSites *connect.Client[v1.ListPrivatePluginInstallSitesRequest, v1.ListPrivatePluginInstallSitesResponse] + listPrivatePluginsForInstance *connect.Client[v1.ListPrivatePluginsForInstanceRequest, v1.ListPrivatePluginsResponse] + resolveInstallForInstance *connect.Client[v1.ResolveInstallForInstanceRequest, v1.ResolveInstallResponse] +} + +// CreatePlugin calls orchestrator.v1.PluginRegistryService.CreatePlugin. +func (c *pluginRegistryServiceClient) CreatePlugin(ctx context.Context, req *connect.Request[v1.CreatePluginRequest]) (*connect.Response[v1.CreatePluginResponse], error) { + return c.createPlugin.CallUnary(ctx, req) +} + +// GetPlugin calls orchestrator.v1.PluginRegistryService.GetPlugin. +func (c *pluginRegistryServiceClient) GetPlugin(ctx context.Context, req *connect.Request[v1.GetPluginRequest]) (*connect.Response[v1.GetPluginResponse], error) { + return c.getPlugin.CallUnary(ctx, req) +} + +// ListPlugins calls orchestrator.v1.PluginRegistryService.ListPlugins. +func (c *pluginRegistryServiceClient) ListPlugins(ctx context.Context, req *connect.Request[v1.ListPluginsRequest]) (*connect.Response[v1.ListPluginsResponse], error) { + return c.listPlugins.CallUnary(ctx, req) +} + +// GetVersion calls orchestrator.v1.PluginRegistryService.GetVersion. +func (c *pluginRegistryServiceClient) GetVersion(ctx context.Context, req *connect.Request[v1.GetVersionRequest]) (*connect.Response[v1.GetVersionResponse], error) { + return c.getVersion.CallUnary(ctx, req) +} + +// ResolveInstall calls orchestrator.v1.PluginRegistryService.ResolveInstall. +func (c *pluginRegistryServiceClient) ResolveInstall(ctx context.Context, req *connect.Request[v1.ResolveInstallRequest]) (*connect.Response[v1.ResolveInstallResponse], error) { + return c.resolveInstall.CallUnary(ctx, req) +} + +// ListCategories calls orchestrator.v1.PluginRegistryService.ListCategories. +func (c *pluginRegistryServiceClient) ListCategories(ctx context.Context, req *connect.Request[v1.ListCategoriesRequest]) (*connect.Response[v1.ListCategoriesResponse], error) { + return c.listCategories.CallUnary(ctx, req) +} + +// ListTags calls orchestrator.v1.PluginRegistryService.ListTags. +func (c *pluginRegistryServiceClient) ListTags(ctx context.Context, req *connect.Request[v1.ListTagsRequest]) (*connect.Response[v1.ListTagsResponse], error) { + return c.listTags.CallUnary(ctx, req) +} + +// SubmitForReview calls orchestrator.v1.PluginRegistryService.SubmitForReview. +func (c *pluginRegistryServiceClient) SubmitForReview(ctx context.Context, req *connect.Request[v1.SubmitForReviewRequest]) (*connect.Response[v1.SubmitForReviewResponse], error) { + return c.submitForReview.CallUnary(ctx, req) +} + +// ListPrivatePlugins calls orchestrator.v1.PluginRegistryService.ListPrivatePlugins. +func (c *pluginRegistryServiceClient) ListPrivatePlugins(ctx context.Context, req *connect.Request[v1.ListPrivatePluginsRequest]) (*connect.Response[v1.ListPrivatePluginsResponse], error) { + return c.listPrivatePlugins.CallUnary(ctx, req) +} + +// DeletePrivatePlugin calls orchestrator.v1.PluginRegistryService.DeletePrivatePlugin. +func (c *pluginRegistryServiceClient) DeletePrivatePlugin(ctx context.Context, req *connect.Request[v1.DeletePrivatePluginRequest]) (*connect.Response[v1.DeletePrivatePluginResponse], error) { + return c.deletePrivatePlugin.CallUnary(ctx, req) +} + +// DeletePrivatePluginVersion calls +// orchestrator.v1.PluginRegistryService.DeletePrivatePluginVersion. +func (c *pluginRegistryServiceClient) DeletePrivatePluginVersion(ctx context.Context, req *connect.Request[v1.DeletePrivatePluginVersionRequest]) (*connect.Response[v1.DeletePrivatePluginVersionResponse], error) { + return c.deletePrivatePluginVersion.CallUnary(ctx, req) +} + +// ListPrivatePluginInstallSites calls +// orchestrator.v1.PluginRegistryService.ListPrivatePluginInstallSites. +func (c *pluginRegistryServiceClient) ListPrivatePluginInstallSites(ctx context.Context, req *connect.Request[v1.ListPrivatePluginInstallSitesRequest]) (*connect.Response[v1.ListPrivatePluginInstallSitesResponse], error) { + return c.listPrivatePluginInstallSites.CallUnary(ctx, req) +} + +// ListPrivatePluginsForInstance calls +// orchestrator.v1.PluginRegistryService.ListPrivatePluginsForInstance. +func (c *pluginRegistryServiceClient) ListPrivatePluginsForInstance(ctx context.Context, req *connect.Request[v1.ListPrivatePluginsForInstanceRequest]) (*connect.Response[v1.ListPrivatePluginsResponse], error) { + return c.listPrivatePluginsForInstance.CallUnary(ctx, req) +} + +// ResolveInstallForInstance calls orchestrator.v1.PluginRegistryService.ResolveInstallForInstance. +func (c *pluginRegistryServiceClient) ResolveInstallForInstance(ctx context.Context, req *connect.Request[v1.ResolveInstallForInstanceRequest]) (*connect.Response[v1.ResolveInstallResponse], error) { + return c.resolveInstallForInstance.CallUnary(ctx, req) +} + +// PluginRegistryServiceHandler is an implementation of the orchestrator.v1.PluginRegistryService +// service. +type PluginRegistryServiceHandler interface { + CreatePlugin(context.Context, *connect.Request[v1.CreatePluginRequest]) (*connect.Response[v1.CreatePluginResponse], error) + GetPlugin(context.Context, *connect.Request[v1.GetPluginRequest]) (*connect.Response[v1.GetPluginResponse], error) + ListPlugins(context.Context, *connect.Request[v1.ListPluginsRequest]) (*connect.Response[v1.ListPluginsResponse], error) + GetVersion(context.Context, *connect.Request[v1.GetVersionRequest]) (*connect.Response[v1.GetVersionResponse], error) + ResolveInstall(context.Context, *connect.Request[v1.ResolveInstallRequest]) (*connect.Response[v1.ResolveInstallResponse], error) + ListCategories(context.Context, *connect.Request[v1.ListCategoriesRequest]) (*connect.Response[v1.ListCategoriesResponse], error) + ListTags(context.Context, *connect.Request[v1.ListTagsRequest]) (*connect.Response[v1.ListTagsResponse], error) + SubmitForReview(context.Context, *connect.Request[v1.SubmitForReviewRequest]) (*connect.Response[v1.SubmitForReviewResponse], error) + // Private-plugin RPCs. All require the caller to be a member of the target + // account; the server resolves account membership from the bearer token. + ListPrivatePlugins(context.Context, *connect.Request[v1.ListPrivatePluginsRequest]) (*connect.Response[v1.ListPrivatePluginsResponse], error) + DeletePrivatePlugin(context.Context, *connect.Request[v1.DeletePrivatePluginRequest]) (*connect.Response[v1.DeletePrivatePluginResponse], error) + DeletePrivatePluginVersion(context.Context, *connect.Request[v1.DeletePrivatePluginVersionRequest]) (*connect.Response[v1.DeletePrivatePluginVersionResponse], error) + ListPrivatePluginInstallSites(context.Context, *connect.Request[v1.ListPrivatePluginInstallSitesRequest]) (*connect.Response[v1.ListPrivatePluginInstallSitesResponse], error) + // 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). + ListPrivatePluginsForInstance(context.Context, *connect.Request[v1.ListPrivatePluginsForInstanceRequest]) (*connect.Response[v1.ListPrivatePluginsResponse], error) + ResolveInstallForInstance(context.Context, *connect.Request[v1.ResolveInstallForInstanceRequest]) (*connect.Response[v1.ResolveInstallResponse], error) +} + +// NewPluginRegistryServiceHandler builds an HTTP handler from the service implementation. It +// returns the path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewPluginRegistryServiceHandler(svc PluginRegistryServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + pluginRegistryServiceMethods := v1.File_orchestrator_v1_plugin_registry_proto.Services().ByName("PluginRegistryService").Methods() + pluginRegistryServiceCreatePluginHandler := connect.NewUnaryHandler( + PluginRegistryServiceCreatePluginProcedure, + svc.CreatePlugin, + connect.WithSchema(pluginRegistryServiceMethods.ByName("CreatePlugin")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceGetPluginHandler := connect.NewUnaryHandler( + PluginRegistryServiceGetPluginProcedure, + svc.GetPlugin, + connect.WithSchema(pluginRegistryServiceMethods.ByName("GetPlugin")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceListPluginsHandler := connect.NewUnaryHandler( + PluginRegistryServiceListPluginsProcedure, + svc.ListPlugins, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListPlugins")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceGetVersionHandler := connect.NewUnaryHandler( + PluginRegistryServiceGetVersionProcedure, + svc.GetVersion, + connect.WithSchema(pluginRegistryServiceMethods.ByName("GetVersion")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceResolveInstallHandler := connect.NewUnaryHandler( + PluginRegistryServiceResolveInstallProcedure, + svc.ResolveInstall, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ResolveInstall")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceListCategoriesHandler := connect.NewUnaryHandler( + PluginRegistryServiceListCategoriesProcedure, + svc.ListCategories, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListCategories")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceListTagsHandler := connect.NewUnaryHandler( + PluginRegistryServiceListTagsProcedure, + svc.ListTags, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListTags")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceSubmitForReviewHandler := connect.NewUnaryHandler( + PluginRegistryServiceSubmitForReviewProcedure, + svc.SubmitForReview, + connect.WithSchema(pluginRegistryServiceMethods.ByName("SubmitForReview")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceListPrivatePluginsHandler := connect.NewUnaryHandler( + PluginRegistryServiceListPrivatePluginsProcedure, + svc.ListPrivatePlugins, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListPrivatePlugins")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceDeletePrivatePluginHandler := connect.NewUnaryHandler( + PluginRegistryServiceDeletePrivatePluginProcedure, + svc.DeletePrivatePlugin, + connect.WithSchema(pluginRegistryServiceMethods.ByName("DeletePrivatePlugin")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceDeletePrivatePluginVersionHandler := connect.NewUnaryHandler( + PluginRegistryServiceDeletePrivatePluginVersionProcedure, + svc.DeletePrivatePluginVersion, + connect.WithSchema(pluginRegistryServiceMethods.ByName("DeletePrivatePluginVersion")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceListPrivatePluginInstallSitesHandler := connect.NewUnaryHandler( + PluginRegistryServiceListPrivatePluginInstallSitesProcedure, + svc.ListPrivatePluginInstallSites, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListPrivatePluginInstallSites")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceListPrivatePluginsForInstanceHandler := connect.NewUnaryHandler( + PluginRegistryServiceListPrivatePluginsForInstanceProcedure, + svc.ListPrivatePluginsForInstance, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ListPrivatePluginsForInstance")), + connect.WithHandlerOptions(opts...), + ) + pluginRegistryServiceResolveInstallForInstanceHandler := connect.NewUnaryHandler( + PluginRegistryServiceResolveInstallForInstanceProcedure, + svc.ResolveInstallForInstance, + connect.WithSchema(pluginRegistryServiceMethods.ByName("ResolveInstallForInstance")), + connect.WithHandlerOptions(opts...), + ) + return "/orchestrator.v1.PluginRegistryService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case PluginRegistryServiceCreatePluginProcedure: + pluginRegistryServiceCreatePluginHandler.ServeHTTP(w, r) + case PluginRegistryServiceGetPluginProcedure: + pluginRegistryServiceGetPluginHandler.ServeHTTP(w, r) + case PluginRegistryServiceListPluginsProcedure: + pluginRegistryServiceListPluginsHandler.ServeHTTP(w, r) + case PluginRegistryServiceGetVersionProcedure: + pluginRegistryServiceGetVersionHandler.ServeHTTP(w, r) + case PluginRegistryServiceResolveInstallProcedure: + pluginRegistryServiceResolveInstallHandler.ServeHTTP(w, r) + case PluginRegistryServiceListCategoriesProcedure: + pluginRegistryServiceListCategoriesHandler.ServeHTTP(w, r) + case PluginRegistryServiceListTagsProcedure: + pluginRegistryServiceListTagsHandler.ServeHTTP(w, r) + case PluginRegistryServiceSubmitForReviewProcedure: + pluginRegistryServiceSubmitForReviewHandler.ServeHTTP(w, r) + case PluginRegistryServiceListPrivatePluginsProcedure: + pluginRegistryServiceListPrivatePluginsHandler.ServeHTTP(w, r) + case PluginRegistryServiceDeletePrivatePluginProcedure: + pluginRegistryServiceDeletePrivatePluginHandler.ServeHTTP(w, r) + case PluginRegistryServiceDeletePrivatePluginVersionProcedure: + pluginRegistryServiceDeletePrivatePluginVersionHandler.ServeHTTP(w, r) + case PluginRegistryServiceListPrivatePluginInstallSitesProcedure: + pluginRegistryServiceListPrivatePluginInstallSitesHandler.ServeHTTP(w, r) + case PluginRegistryServiceListPrivatePluginsForInstanceProcedure: + pluginRegistryServiceListPrivatePluginsForInstanceHandler.ServeHTTP(w, r) + case PluginRegistryServiceResolveInstallForInstanceProcedure: + pluginRegistryServiceResolveInstallForInstanceHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedPluginRegistryServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedPluginRegistryServiceHandler struct{} + +func (UnimplementedPluginRegistryServiceHandler) CreatePlugin(context.Context, *connect.Request[v1.CreatePluginRequest]) (*connect.Response[v1.CreatePluginResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.CreatePlugin is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) GetPlugin(context.Context, *connect.Request[v1.GetPluginRequest]) (*connect.Response[v1.GetPluginResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.GetPlugin is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) ListPlugins(context.Context, *connect.Request[v1.ListPluginsRequest]) (*connect.Response[v1.ListPluginsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.ListPlugins is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) GetVersion(context.Context, *connect.Request[v1.GetVersionRequest]) (*connect.Response[v1.GetVersionResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.GetVersion is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) ResolveInstall(context.Context, *connect.Request[v1.ResolveInstallRequest]) (*connect.Response[v1.ResolveInstallResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.ResolveInstall is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) ListCategories(context.Context, *connect.Request[v1.ListCategoriesRequest]) (*connect.Response[v1.ListCategoriesResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.ListCategories is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) ListTags(context.Context, *connect.Request[v1.ListTagsRequest]) (*connect.Response[v1.ListTagsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.ListTags is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) SubmitForReview(context.Context, *connect.Request[v1.SubmitForReviewRequest]) (*connect.Response[v1.SubmitForReviewResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.SubmitForReview is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) ListPrivatePlugins(context.Context, *connect.Request[v1.ListPrivatePluginsRequest]) (*connect.Response[v1.ListPrivatePluginsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.ListPrivatePlugins is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) DeletePrivatePlugin(context.Context, *connect.Request[v1.DeletePrivatePluginRequest]) (*connect.Response[v1.DeletePrivatePluginResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.DeletePrivatePlugin is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) DeletePrivatePluginVersion(context.Context, *connect.Request[v1.DeletePrivatePluginVersionRequest]) (*connect.Response[v1.DeletePrivatePluginVersionResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.DeletePrivatePluginVersion is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) ListPrivatePluginInstallSites(context.Context, *connect.Request[v1.ListPrivatePluginInstallSitesRequest]) (*connect.Response[v1.ListPrivatePluginInstallSitesResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.ListPrivatePluginInstallSites is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) ListPrivatePluginsForInstance(context.Context, *connect.Request[v1.ListPrivatePluginsForInstanceRequest]) (*connect.Response[v1.ListPrivatePluginsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.ListPrivatePluginsForInstance is not implemented")) +} + +func (UnimplementedPluginRegistryServiceHandler) ResolveInstallForInstance(context.Context, *connect.Request[v1.ResolveInstallForInstanceRequest]) (*connect.Response[v1.ResolveInstallResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginRegistryService.ResolveInstallForInstance is not implemented")) +} + +// PluginModerationServiceClient is a client for the orchestrator.v1.PluginModerationService +// service. +type PluginModerationServiceClient interface { + ListPendingReviews(context.Context, *connect.Request[v1.ListPendingReviewsRequest]) (*connect.Response[v1.ListPendingReviewsResponse], error) + ApproveSubmission(context.Context, *connect.Request[v1.ApproveSubmissionRequest]) (*connect.Response[v1.ApproveSubmissionResponse], error) + RejectSubmission(context.Context, *connect.Request[v1.RejectSubmissionRequest]) (*connect.Response[v1.RejectSubmissionResponse], error) + RequestChanges(context.Context, *connect.Request[v1.RequestChangesRequest]) (*connect.Response[v1.RequestChangesResponse], error) +} + +// NewPluginModerationServiceClient constructs a client for the +// orchestrator.v1.PluginModerationService service. By default, it uses the Connect protocol with +// the binary Protobuf Codec, asks for gzipped responses, and sends uncompressed requests. To use +// the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewPluginModerationServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) PluginModerationServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + pluginModerationServiceMethods := v1.File_orchestrator_v1_plugin_registry_proto.Services().ByName("PluginModerationService").Methods() + return &pluginModerationServiceClient{ + listPendingReviews: connect.NewClient[v1.ListPendingReviewsRequest, v1.ListPendingReviewsResponse]( + httpClient, + baseURL+PluginModerationServiceListPendingReviewsProcedure, + connect.WithSchema(pluginModerationServiceMethods.ByName("ListPendingReviews")), + connect.WithClientOptions(opts...), + ), + approveSubmission: connect.NewClient[v1.ApproveSubmissionRequest, v1.ApproveSubmissionResponse]( + httpClient, + baseURL+PluginModerationServiceApproveSubmissionProcedure, + connect.WithSchema(pluginModerationServiceMethods.ByName("ApproveSubmission")), + connect.WithClientOptions(opts...), + ), + rejectSubmission: connect.NewClient[v1.RejectSubmissionRequest, v1.RejectSubmissionResponse]( + httpClient, + baseURL+PluginModerationServiceRejectSubmissionProcedure, + connect.WithSchema(pluginModerationServiceMethods.ByName("RejectSubmission")), + connect.WithClientOptions(opts...), + ), + requestChanges: connect.NewClient[v1.RequestChangesRequest, v1.RequestChangesResponse]( + httpClient, + baseURL+PluginModerationServiceRequestChangesProcedure, + connect.WithSchema(pluginModerationServiceMethods.ByName("RequestChanges")), + connect.WithClientOptions(opts...), + ), + } +} + +// pluginModerationServiceClient implements PluginModerationServiceClient. +type pluginModerationServiceClient struct { + listPendingReviews *connect.Client[v1.ListPendingReviewsRequest, v1.ListPendingReviewsResponse] + approveSubmission *connect.Client[v1.ApproveSubmissionRequest, v1.ApproveSubmissionResponse] + rejectSubmission *connect.Client[v1.RejectSubmissionRequest, v1.RejectSubmissionResponse] + requestChanges *connect.Client[v1.RequestChangesRequest, v1.RequestChangesResponse] +} + +// ListPendingReviews calls orchestrator.v1.PluginModerationService.ListPendingReviews. +func (c *pluginModerationServiceClient) ListPendingReviews(ctx context.Context, req *connect.Request[v1.ListPendingReviewsRequest]) (*connect.Response[v1.ListPendingReviewsResponse], error) { + return c.listPendingReviews.CallUnary(ctx, req) +} + +// ApproveSubmission calls orchestrator.v1.PluginModerationService.ApproveSubmission. +func (c *pluginModerationServiceClient) ApproveSubmission(ctx context.Context, req *connect.Request[v1.ApproveSubmissionRequest]) (*connect.Response[v1.ApproveSubmissionResponse], error) { + return c.approveSubmission.CallUnary(ctx, req) +} + +// RejectSubmission calls orchestrator.v1.PluginModerationService.RejectSubmission. +func (c *pluginModerationServiceClient) RejectSubmission(ctx context.Context, req *connect.Request[v1.RejectSubmissionRequest]) (*connect.Response[v1.RejectSubmissionResponse], error) { + return c.rejectSubmission.CallUnary(ctx, req) +} + +// RequestChanges calls orchestrator.v1.PluginModerationService.RequestChanges. +func (c *pluginModerationServiceClient) RequestChanges(ctx context.Context, req *connect.Request[v1.RequestChangesRequest]) (*connect.Response[v1.RequestChangesResponse], error) { + return c.requestChanges.CallUnary(ctx, req) +} + +// PluginModerationServiceHandler is an implementation of the +// orchestrator.v1.PluginModerationService service. +type PluginModerationServiceHandler interface { + ListPendingReviews(context.Context, *connect.Request[v1.ListPendingReviewsRequest]) (*connect.Response[v1.ListPendingReviewsResponse], error) + ApproveSubmission(context.Context, *connect.Request[v1.ApproveSubmissionRequest]) (*connect.Response[v1.ApproveSubmissionResponse], error) + RejectSubmission(context.Context, *connect.Request[v1.RejectSubmissionRequest]) (*connect.Response[v1.RejectSubmissionResponse], error) + RequestChanges(context.Context, *connect.Request[v1.RequestChangesRequest]) (*connect.Response[v1.RequestChangesResponse], error) +} + +// NewPluginModerationServiceHandler builds an HTTP handler from the service implementation. It +// returns the path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewPluginModerationServiceHandler(svc PluginModerationServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + pluginModerationServiceMethods := v1.File_orchestrator_v1_plugin_registry_proto.Services().ByName("PluginModerationService").Methods() + pluginModerationServiceListPendingReviewsHandler := connect.NewUnaryHandler( + PluginModerationServiceListPendingReviewsProcedure, + svc.ListPendingReviews, + connect.WithSchema(pluginModerationServiceMethods.ByName("ListPendingReviews")), + connect.WithHandlerOptions(opts...), + ) + pluginModerationServiceApproveSubmissionHandler := connect.NewUnaryHandler( + PluginModerationServiceApproveSubmissionProcedure, + svc.ApproveSubmission, + connect.WithSchema(pluginModerationServiceMethods.ByName("ApproveSubmission")), + connect.WithHandlerOptions(opts...), + ) + pluginModerationServiceRejectSubmissionHandler := connect.NewUnaryHandler( + PluginModerationServiceRejectSubmissionProcedure, + svc.RejectSubmission, + connect.WithSchema(pluginModerationServiceMethods.ByName("RejectSubmission")), + connect.WithHandlerOptions(opts...), + ) + pluginModerationServiceRequestChangesHandler := connect.NewUnaryHandler( + PluginModerationServiceRequestChangesProcedure, + svc.RequestChanges, + connect.WithSchema(pluginModerationServiceMethods.ByName("RequestChanges")), + connect.WithHandlerOptions(opts...), + ) + return "/orchestrator.v1.PluginModerationService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case PluginModerationServiceListPendingReviewsProcedure: + pluginModerationServiceListPendingReviewsHandler.ServeHTTP(w, r) + case PluginModerationServiceApproveSubmissionProcedure: + pluginModerationServiceApproveSubmissionHandler.ServeHTTP(w, r) + case PluginModerationServiceRejectSubmissionProcedure: + pluginModerationServiceRejectSubmissionHandler.ServeHTTP(w, r) + case PluginModerationServiceRequestChangesProcedure: + pluginModerationServiceRequestChangesHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedPluginModerationServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedPluginModerationServiceHandler struct{} + +func (UnimplementedPluginModerationServiceHandler) ListPendingReviews(context.Context, *connect.Request[v1.ListPendingReviewsRequest]) (*connect.Response[v1.ListPendingReviewsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginModerationService.ListPendingReviews is not implemented")) +} + +func (UnimplementedPluginModerationServiceHandler) ApproveSubmission(context.Context, *connect.Request[v1.ApproveSubmissionRequest]) (*connect.Response[v1.ApproveSubmissionResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginModerationService.ApproveSubmission is not implemented")) +} + +func (UnimplementedPluginModerationServiceHandler) RejectSubmission(context.Context, *connect.Request[v1.RejectSubmissionRequest]) (*connect.Response[v1.RejectSubmissionResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginModerationService.RejectSubmission is not implemented")) +} + +func (UnimplementedPluginModerationServiceHandler) RequestChanges(context.Context, *connect.Request[v1.RequestChangesRequest]) (*connect.Response[v1.RequestChangesResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginModerationService.RequestChanges is not implemented")) +} + +// PluginPublishServiceClient is a client for the orchestrator.v1.PluginPublishService service. +type PluginPublishServiceClient interface { + PublishVersion(context.Context, *connect.Request[v1.PublishVersionRequest]) (*connect.Response[v1.PublishVersionResponse], error) +} + +// NewPluginPublishServiceClient constructs a client for the orchestrator.v1.PluginPublishService +// service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for +// gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply +// the connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewPluginPublishServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) PluginPublishServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + pluginPublishServiceMethods := v1.File_orchestrator_v1_plugin_registry_proto.Services().ByName("PluginPublishService").Methods() + return &pluginPublishServiceClient{ + publishVersion: connect.NewClient[v1.PublishVersionRequest, v1.PublishVersionResponse]( + httpClient, + baseURL+PluginPublishServicePublishVersionProcedure, + connect.WithSchema(pluginPublishServiceMethods.ByName("PublishVersion")), + connect.WithClientOptions(opts...), + ), + } +} + +// pluginPublishServiceClient implements PluginPublishServiceClient. +type pluginPublishServiceClient struct { + publishVersion *connect.Client[v1.PublishVersionRequest, v1.PublishVersionResponse] +} + +// PublishVersion calls orchestrator.v1.PluginPublishService.PublishVersion. +func (c *pluginPublishServiceClient) PublishVersion(ctx context.Context, req *connect.Request[v1.PublishVersionRequest]) (*connect.Response[v1.PublishVersionResponse], error) { + return c.publishVersion.CallUnary(ctx, req) +} + +// PluginPublishServiceHandler is an implementation of the orchestrator.v1.PluginPublishService +// service. +type PluginPublishServiceHandler interface { + PublishVersion(context.Context, *connect.Request[v1.PublishVersionRequest]) (*connect.Response[v1.PublishVersionResponse], error) +} + +// NewPluginPublishServiceHandler builds an HTTP handler from the service implementation. It returns +// the path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewPluginPublishServiceHandler(svc PluginPublishServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + pluginPublishServiceMethods := v1.File_orchestrator_v1_plugin_registry_proto.Services().ByName("PluginPublishService").Methods() + pluginPublishServicePublishVersionHandler := connect.NewUnaryHandler( + PluginPublishServicePublishVersionProcedure, + svc.PublishVersion, + connect.WithSchema(pluginPublishServiceMethods.ByName("PublishVersion")), + connect.WithHandlerOptions(opts...), + ) + return "/orchestrator.v1.PluginPublishService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case PluginPublishServicePublishVersionProcedure: + pluginPublishServicePublishVersionHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedPluginPublishServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedPluginPublishServiceHandler struct{} + +func (UnimplementedPluginPublishServiceHandler) PublishVersion(context.Context, *connect.Request[v1.PublishVersionRequest]) (*connect.Response[v1.PublishVersionResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginPublishService.PublishVersion is not implemented")) +} + +// PluginAuthServiceClient is a client for the orchestrator.v1.PluginAuthService service. +type PluginAuthServiceClient interface { + StartDevice(context.Context, *connect.Request[v1.StartDeviceRequest]) (*connect.Response[v1.StartDeviceResponse], error) + PollDevice(context.Context, *connect.Request[v1.PollDeviceRequest]) (*connect.Response[v1.PollDeviceResponse], error) + ApproveDevice(context.Context, *connect.Request[v1.ApproveDeviceRequest]) (*connect.Response[v1.ApproveDeviceResponse], error) + DenyDevice(context.Context, *connect.Request[v1.DenyDeviceRequest]) (*connect.Response[v1.DenyDeviceResponse], error) + GetDeviceStatus(context.Context, *connect.Request[v1.GetDeviceStatusRequest]) (*connect.Response[v1.GetDeviceStatusResponse], error) + Whoami(context.Context, *connect.Request[v1.WhoamiRequest]) (*connect.Response[v1.WhoamiResponse], error) + // 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. + ListMyAccountsForCLI(context.Context, *connect.Request[v1.ListMyAccountsForCLIRequest]) (*connect.Response[v1.ListMyAccountsForCLIResponse], error) +} + +// NewPluginAuthServiceClient constructs a client for the orchestrator.v1.PluginAuthService service. +// By default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped +// responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the +// connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewPluginAuthServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) PluginAuthServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + pluginAuthServiceMethods := v1.File_orchestrator_v1_plugin_registry_proto.Services().ByName("PluginAuthService").Methods() + return &pluginAuthServiceClient{ + startDevice: connect.NewClient[v1.StartDeviceRequest, v1.StartDeviceResponse]( + httpClient, + baseURL+PluginAuthServiceStartDeviceProcedure, + connect.WithSchema(pluginAuthServiceMethods.ByName("StartDevice")), + connect.WithClientOptions(opts...), + ), + pollDevice: connect.NewClient[v1.PollDeviceRequest, v1.PollDeviceResponse]( + httpClient, + baseURL+PluginAuthServicePollDeviceProcedure, + connect.WithSchema(pluginAuthServiceMethods.ByName("PollDevice")), + connect.WithClientOptions(opts...), + ), + approveDevice: connect.NewClient[v1.ApproveDeviceRequest, v1.ApproveDeviceResponse]( + httpClient, + baseURL+PluginAuthServiceApproveDeviceProcedure, + connect.WithSchema(pluginAuthServiceMethods.ByName("ApproveDevice")), + connect.WithClientOptions(opts...), + ), + denyDevice: connect.NewClient[v1.DenyDeviceRequest, v1.DenyDeviceResponse]( + httpClient, + baseURL+PluginAuthServiceDenyDeviceProcedure, + connect.WithSchema(pluginAuthServiceMethods.ByName("DenyDevice")), + connect.WithClientOptions(opts...), + ), + getDeviceStatus: connect.NewClient[v1.GetDeviceStatusRequest, v1.GetDeviceStatusResponse]( + httpClient, + baseURL+PluginAuthServiceGetDeviceStatusProcedure, + connect.WithSchema(pluginAuthServiceMethods.ByName("GetDeviceStatus")), + connect.WithClientOptions(opts...), + ), + whoami: connect.NewClient[v1.WhoamiRequest, v1.WhoamiResponse]( + httpClient, + baseURL+PluginAuthServiceWhoamiProcedure, + connect.WithSchema(pluginAuthServiceMethods.ByName("Whoami")), + connect.WithClientOptions(opts...), + ), + listMyAccountsForCLI: connect.NewClient[v1.ListMyAccountsForCLIRequest, v1.ListMyAccountsForCLIResponse]( + httpClient, + baseURL+PluginAuthServiceListMyAccountsForCLIProcedure, + connect.WithSchema(pluginAuthServiceMethods.ByName("ListMyAccountsForCLI")), + connect.WithClientOptions(opts...), + ), + } +} + +// pluginAuthServiceClient implements PluginAuthServiceClient. +type pluginAuthServiceClient struct { + startDevice *connect.Client[v1.StartDeviceRequest, v1.StartDeviceResponse] + pollDevice *connect.Client[v1.PollDeviceRequest, v1.PollDeviceResponse] + approveDevice *connect.Client[v1.ApproveDeviceRequest, v1.ApproveDeviceResponse] + denyDevice *connect.Client[v1.DenyDeviceRequest, v1.DenyDeviceResponse] + getDeviceStatus *connect.Client[v1.GetDeviceStatusRequest, v1.GetDeviceStatusResponse] + whoami *connect.Client[v1.WhoamiRequest, v1.WhoamiResponse] + listMyAccountsForCLI *connect.Client[v1.ListMyAccountsForCLIRequest, v1.ListMyAccountsForCLIResponse] +} + +// StartDevice calls orchestrator.v1.PluginAuthService.StartDevice. +func (c *pluginAuthServiceClient) StartDevice(ctx context.Context, req *connect.Request[v1.StartDeviceRequest]) (*connect.Response[v1.StartDeviceResponse], error) { + return c.startDevice.CallUnary(ctx, req) +} + +// PollDevice calls orchestrator.v1.PluginAuthService.PollDevice. +func (c *pluginAuthServiceClient) PollDevice(ctx context.Context, req *connect.Request[v1.PollDeviceRequest]) (*connect.Response[v1.PollDeviceResponse], error) { + return c.pollDevice.CallUnary(ctx, req) +} + +// ApproveDevice calls orchestrator.v1.PluginAuthService.ApproveDevice. +func (c *pluginAuthServiceClient) ApproveDevice(ctx context.Context, req *connect.Request[v1.ApproveDeviceRequest]) (*connect.Response[v1.ApproveDeviceResponse], error) { + return c.approveDevice.CallUnary(ctx, req) +} + +// DenyDevice calls orchestrator.v1.PluginAuthService.DenyDevice. +func (c *pluginAuthServiceClient) DenyDevice(ctx context.Context, req *connect.Request[v1.DenyDeviceRequest]) (*connect.Response[v1.DenyDeviceResponse], error) { + return c.denyDevice.CallUnary(ctx, req) +} + +// GetDeviceStatus calls orchestrator.v1.PluginAuthService.GetDeviceStatus. +func (c *pluginAuthServiceClient) GetDeviceStatus(ctx context.Context, req *connect.Request[v1.GetDeviceStatusRequest]) (*connect.Response[v1.GetDeviceStatusResponse], error) { + return c.getDeviceStatus.CallUnary(ctx, req) +} + +// Whoami calls orchestrator.v1.PluginAuthService.Whoami. +func (c *pluginAuthServiceClient) Whoami(ctx context.Context, req *connect.Request[v1.WhoamiRequest]) (*connect.Response[v1.WhoamiResponse], error) { + return c.whoami.CallUnary(ctx, req) +} + +// ListMyAccountsForCLI calls orchestrator.v1.PluginAuthService.ListMyAccountsForCLI. +func (c *pluginAuthServiceClient) ListMyAccountsForCLI(ctx context.Context, req *connect.Request[v1.ListMyAccountsForCLIRequest]) (*connect.Response[v1.ListMyAccountsForCLIResponse], error) { + return c.listMyAccountsForCLI.CallUnary(ctx, req) +} + +// PluginAuthServiceHandler is an implementation of the orchestrator.v1.PluginAuthService service. +type PluginAuthServiceHandler interface { + StartDevice(context.Context, *connect.Request[v1.StartDeviceRequest]) (*connect.Response[v1.StartDeviceResponse], error) + PollDevice(context.Context, *connect.Request[v1.PollDeviceRequest]) (*connect.Response[v1.PollDeviceResponse], error) + ApproveDevice(context.Context, *connect.Request[v1.ApproveDeviceRequest]) (*connect.Response[v1.ApproveDeviceResponse], error) + DenyDevice(context.Context, *connect.Request[v1.DenyDeviceRequest]) (*connect.Response[v1.DenyDeviceResponse], error) + GetDeviceStatus(context.Context, *connect.Request[v1.GetDeviceStatusRequest]) (*connect.Response[v1.GetDeviceStatusResponse], error) + Whoami(context.Context, *connect.Request[v1.WhoamiRequest]) (*connect.Response[v1.WhoamiResponse], error) + // 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. + ListMyAccountsForCLI(context.Context, *connect.Request[v1.ListMyAccountsForCLIRequest]) (*connect.Response[v1.ListMyAccountsForCLIResponse], error) +} + +// NewPluginAuthServiceHandler builds an HTTP handler from the service implementation. It returns +// the path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewPluginAuthServiceHandler(svc PluginAuthServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + pluginAuthServiceMethods := v1.File_orchestrator_v1_plugin_registry_proto.Services().ByName("PluginAuthService").Methods() + pluginAuthServiceStartDeviceHandler := connect.NewUnaryHandler( + PluginAuthServiceStartDeviceProcedure, + svc.StartDevice, + connect.WithSchema(pluginAuthServiceMethods.ByName("StartDevice")), + connect.WithHandlerOptions(opts...), + ) + pluginAuthServicePollDeviceHandler := connect.NewUnaryHandler( + PluginAuthServicePollDeviceProcedure, + svc.PollDevice, + connect.WithSchema(pluginAuthServiceMethods.ByName("PollDevice")), + connect.WithHandlerOptions(opts...), + ) + pluginAuthServiceApproveDeviceHandler := connect.NewUnaryHandler( + PluginAuthServiceApproveDeviceProcedure, + svc.ApproveDevice, + connect.WithSchema(pluginAuthServiceMethods.ByName("ApproveDevice")), + connect.WithHandlerOptions(opts...), + ) + pluginAuthServiceDenyDeviceHandler := connect.NewUnaryHandler( + PluginAuthServiceDenyDeviceProcedure, + svc.DenyDevice, + connect.WithSchema(pluginAuthServiceMethods.ByName("DenyDevice")), + connect.WithHandlerOptions(opts...), + ) + pluginAuthServiceGetDeviceStatusHandler := connect.NewUnaryHandler( + PluginAuthServiceGetDeviceStatusProcedure, + svc.GetDeviceStatus, + connect.WithSchema(pluginAuthServiceMethods.ByName("GetDeviceStatus")), + connect.WithHandlerOptions(opts...), + ) + pluginAuthServiceWhoamiHandler := connect.NewUnaryHandler( + PluginAuthServiceWhoamiProcedure, + svc.Whoami, + connect.WithSchema(pluginAuthServiceMethods.ByName("Whoami")), + connect.WithHandlerOptions(opts...), + ) + pluginAuthServiceListMyAccountsForCLIHandler := connect.NewUnaryHandler( + PluginAuthServiceListMyAccountsForCLIProcedure, + svc.ListMyAccountsForCLI, + connect.WithSchema(pluginAuthServiceMethods.ByName("ListMyAccountsForCLI")), + connect.WithHandlerOptions(opts...), + ) + return "/orchestrator.v1.PluginAuthService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case PluginAuthServiceStartDeviceProcedure: + pluginAuthServiceStartDeviceHandler.ServeHTTP(w, r) + case PluginAuthServicePollDeviceProcedure: + pluginAuthServicePollDeviceHandler.ServeHTTP(w, r) + case PluginAuthServiceApproveDeviceProcedure: + pluginAuthServiceApproveDeviceHandler.ServeHTTP(w, r) + case PluginAuthServiceDenyDeviceProcedure: + pluginAuthServiceDenyDeviceHandler.ServeHTTP(w, r) + case PluginAuthServiceGetDeviceStatusProcedure: + pluginAuthServiceGetDeviceStatusHandler.ServeHTTP(w, r) + case PluginAuthServiceWhoamiProcedure: + pluginAuthServiceWhoamiHandler.ServeHTTP(w, r) + case PluginAuthServiceListMyAccountsForCLIProcedure: + pluginAuthServiceListMyAccountsForCLIHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedPluginAuthServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedPluginAuthServiceHandler struct{} + +func (UnimplementedPluginAuthServiceHandler) StartDevice(context.Context, *connect.Request[v1.StartDeviceRequest]) (*connect.Response[v1.StartDeviceResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginAuthService.StartDevice is not implemented")) +} + +func (UnimplementedPluginAuthServiceHandler) PollDevice(context.Context, *connect.Request[v1.PollDeviceRequest]) (*connect.Response[v1.PollDeviceResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginAuthService.PollDevice is not implemented")) +} + +func (UnimplementedPluginAuthServiceHandler) ApproveDevice(context.Context, *connect.Request[v1.ApproveDeviceRequest]) (*connect.Response[v1.ApproveDeviceResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginAuthService.ApproveDevice is not implemented")) +} + +func (UnimplementedPluginAuthServiceHandler) DenyDevice(context.Context, *connect.Request[v1.DenyDeviceRequest]) (*connect.Response[v1.DenyDeviceResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginAuthService.DenyDevice is not implemented")) +} + +func (UnimplementedPluginAuthServiceHandler) GetDeviceStatus(context.Context, *connect.Request[v1.GetDeviceStatusRequest]) (*connect.Response[v1.GetDeviceStatusResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginAuthService.GetDeviceStatus is not implemented")) +} + +func (UnimplementedPluginAuthServiceHandler) Whoami(context.Context, *connect.Request[v1.WhoamiRequest]) (*connect.Response[v1.WhoamiResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginAuthService.Whoami is not implemented")) +} + +func (UnimplementedPluginAuthServiceHandler) ListMyAccountsForCLI(context.Context, *connect.Request[v1.ListMyAccountsForCLIRequest]) (*connect.Response[v1.ListMyAccountsForCLIResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("orchestrator.v1.PluginAuthService.ListMyAccountsForCLI is not implemented")) +} diff --git a/internal/api/orchestrator/v1/plugin_registry.pb.go b/internal/api/orchestrator/v1/plugin_registry.pb.go new file mode 100644 index 0000000..ad8c83b --- /dev/null +++ b/internal/api/orchestrator/v1/plugin_registry.pb.go @@ -0,0 +1,4466 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: orchestrator/v1/plugin_registry.proto + +package orchestratorv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// 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. +type PluginVisibility int32 + +const ( + PluginVisibility_PLUGIN_VISIBILITY_UNSPECIFIED PluginVisibility = 0 + PluginVisibility_PLUGIN_VISIBILITY_PRIVATE PluginVisibility = 1 + PluginVisibility_PLUGIN_VISIBILITY_UNDER_REVIEW PluginVisibility = 2 + PluginVisibility_PLUGIN_VISIBILITY_PUBLIC PluginVisibility = 3 + PluginVisibility_PLUGIN_VISIBILITY_REJECTED PluginVisibility = 4 + PluginVisibility_PLUGIN_VISIBILITY_TAKEN_DOWN PluginVisibility = 5 +) + +// Enum value maps for PluginVisibility. +var ( + PluginVisibility_name = map[int32]string{ + 0: "PLUGIN_VISIBILITY_UNSPECIFIED", + 1: "PLUGIN_VISIBILITY_PRIVATE", + 2: "PLUGIN_VISIBILITY_UNDER_REVIEW", + 3: "PLUGIN_VISIBILITY_PUBLIC", + 4: "PLUGIN_VISIBILITY_REJECTED", + 5: "PLUGIN_VISIBILITY_TAKEN_DOWN", + } + PluginVisibility_value = map[string]int32{ + "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, + } +) + +func (x PluginVisibility) Enum() *PluginVisibility { + p := new(PluginVisibility) + *p = x + return p +} + +func (x PluginVisibility) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (PluginVisibility) Descriptor() protoreflect.EnumDescriptor { + return file_orchestrator_v1_plugin_registry_proto_enumTypes[0].Descriptor() +} + +func (PluginVisibility) Type() protoreflect.EnumType { + return &file_orchestrator_v1_plugin_registry_proto_enumTypes[0] +} + +func (x PluginVisibility) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use PluginVisibility.Descriptor instead. +func (PluginVisibility) EnumDescriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{0} +} + +type Scope struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Slug string `protobuf:"bytes,2,opt,name=slug,proto3" json:"slug,omitempty"` + DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Scope) Reset() { + *x = Scope{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Scope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Scope) ProtoMessage() {} + +func (x *Scope) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[0] + 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 Scope.ProtoReflect.Descriptor instead. +func (*Scope) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{0} +} + +func (x *Scope) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Scope) GetSlug() string { + if x != nil { + return x.Slug + } + return "" +} + +func (x *Scope) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *Scope) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +type Plugin struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ScopeSlug string `protobuf:"bytes,2,opt,name=scope_slug,json=scopeSlug,proto3" json:"scope_slug,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Visibility PluginVisibility `protobuf:"varint,4,opt,name=visibility,proto3,enum=orchestrator.v1.PluginVisibility" json:"visibility,omitempty"` + Premium bool `protobuf:"varint,5,opt,name=premium,proto3" json:"premium,omitempty"` + Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"` + HomepageUrl string `protobuf:"bytes,7,opt,name=homepage_url,json=homepageUrl,proto3" json:"homepage_url,omitempty"` + Categories []string `protobuf:"bytes,8,rep,name=categories,proto3" json:"categories,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + Kind string `protobuf:"bytes,10,opt,name=kind,proto3" json:"kind,omitempty"` + DisplayName string `protobuf:"bytes,11,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + // owner_account_id is set for private plugins and identifies the account + // that owns the plugin. Empty for public plugins. + OwnerAccountId string `protobuf:"bytes,12,opt,name=owner_account_id,json=ownerAccountId,proto3" json:"owner_account_id,omitempty"` + // 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. + LatestVersion string `protobuf:"bytes,13,opt,name=latest_version,json=latestVersion,proto3" json:"latest_version,omitempty"` + Tags []string `protobuf:"bytes,14,rep,name=tags,proto3" json:"tags,omitempty"` + // preview_image_url is the PUBLIC, unsigned, long-lived image URL for this + // plugin's latest non-yanked version preview (a PNG screenshot), usable + // directly as an . Empty when no preview has been published yet — + // the wizard card then degrades to display_name + tags (spec §5.2). + PreviewImageUrl string `protobuf:"bytes,15,opt,name=preview_image_url,json=previewImageUrl,proto3" json:"preview_image_url,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Plugin) Reset() { + *x = Plugin{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Plugin) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Plugin) ProtoMessage() {} + +func (x *Plugin) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[1] + 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 Plugin.ProtoReflect.Descriptor instead. +func (*Plugin) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{1} +} + +func (x *Plugin) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Plugin) GetScopeSlug() string { + if x != nil { + return x.ScopeSlug + } + return "" +} + +func (x *Plugin) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Plugin) GetVisibility() PluginVisibility { + if x != nil { + return x.Visibility + } + return PluginVisibility_PLUGIN_VISIBILITY_UNSPECIFIED +} + +func (x *Plugin) GetPremium() bool { + if x != nil { + return x.Premium + } + return false +} + +func (x *Plugin) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Plugin) GetHomepageUrl() string { + if x != nil { + return x.HomepageUrl + } + return "" +} + +func (x *Plugin) GetCategories() []string { + if x != nil { + return x.Categories + } + return nil +} + +func (x *Plugin) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +func (x *Plugin) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *Plugin) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *Plugin) GetOwnerAccountId() string { + if x != nil { + return x.OwnerAccountId + } + return "" +} + +func (x *Plugin) GetLatestVersion() string { + if x != nil { + return x.LatestVersion + } + return "" +} + +func (x *Plugin) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + +func (x *Plugin) GetPreviewImageUrl() string { + if x != nil { + return x.PreviewImageUrl + } + return "" +} + +type Category struct { + state protoimpl.MessageState `protogen:"open.v1"` + Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"` + DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + SortOrder int32 `protobuf:"varint,4,opt,name=sort_order,json=sortOrder,proto3" json:"sort_order,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Category) Reset() { + *x = Category{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Category) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Category) ProtoMessage() {} + +func (x *Category) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[2] + 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 Category.ProtoReflect.Descriptor instead. +func (*Category) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{2} +} + +func (x *Category) GetSlug() string { + if x != nil { + return x.Slug + } + return "" +} + +func (x *Category) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *Category) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Category) GetSortOrder() int32 { + if x != nil { + return x.SortOrder + } + return 0 +} + +type Version struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + PluginId string `protobuf:"bytes,2,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + PublishedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=published_at,json=publishedAt,proto3" json:"published_at,omitempty"` + Yanked bool `protobuf:"varint,7,opt,name=yanked,proto3" json:"yanked,omitempty"` + SdkConstraint string `protobuf:"bytes,8,opt,name=sdk_constraint,json=sdkConstraint,proto3" json:"sdk_constraint,omitempty"` + SourceArchiveSha256 string `protobuf:"bytes,9,opt,name=source_archive_sha256,json=sourceArchiveSha256,proto3" json:"source_archive_sha256,omitempty"` + SourceArchiveSize int64 `protobuf:"varint,10,opt,name=source_archive_size,json=sourceArchiveSize,proto3" json:"source_archive_size,omitempty"` + // preview_image_url is the public unsigned URL of this specific version's + // preview PNG, or empty when this version shipped no preview.png. + PreviewImageUrl string `protobuf:"bytes,11,opt,name=preview_image_url,json=previewImageUrl,proto3" json:"preview_image_url,omitempty"` + // 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. + AbiVersion uint32 `protobuf:"varint,12,opt,name=abi_version,json=abiVersion,proto3" json:"abi_version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Version) Reset() { + *x = Version{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Version) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Version) ProtoMessage() {} + +func (x *Version) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[3] + 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 Version.ProtoReflect.Descriptor instead. +func (*Version) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{3} +} + +func (x *Version) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Version) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *Version) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *Version) GetPublishedAt() *timestamppb.Timestamp { + if x != nil { + return x.PublishedAt + } + return nil +} + +func (x *Version) GetYanked() bool { + if x != nil { + return x.Yanked + } + return false +} + +func (x *Version) GetSdkConstraint() string { + if x != nil { + return x.SdkConstraint + } + return "" +} + +func (x *Version) GetSourceArchiveSha256() string { + if x != nil { + return x.SourceArchiveSha256 + } + return "" +} + +func (x *Version) GetSourceArchiveSize() int64 { + if x != nil { + return x.SourceArchiveSize + } + return 0 +} + +func (x *Version) GetPreviewImageUrl() string { + if x != nil { + return x.PreviewImageUrl + } + return "" +} + +func (x *Version) GetAbiVersion() uint32 { + if x != nil { + return x.AbiVersion + } + return 0 +} + +type Requirement struct { + state protoimpl.MessageState `protogen:"open.v1"` + ScopeSlug string `protobuf:"bytes,1,opt,name=scope_slug,json=scopeSlug,proto3" json:"scope_slug,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + ConstraintExpr string `protobuf:"bytes,3,opt,name=constraint_expr,json=constraintExpr,proto3" json:"constraint_expr,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Requirement) Reset() { + *x = Requirement{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Requirement) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Requirement) ProtoMessage() {} + +func (x *Requirement) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[4] + 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 Requirement.ProtoReflect.Descriptor instead. +func (*Requirement) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{4} +} + +func (x *Requirement) GetScopeSlug() string { + if x != nil { + return x.ScopeSlug + } + return "" +} + +func (x *Requirement) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Requirement) GetConstraintExpr() string { + if x != nil { + return x.ConstraintExpr + } + return "" +} + +type CreateScopeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"` + DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateScopeRequest) Reset() { + *x = CreateScopeRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateScopeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateScopeRequest) ProtoMessage() {} + +func (x *CreateScopeRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[5] + 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 CreateScopeRequest.ProtoReflect.Descriptor instead. +func (*CreateScopeRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateScopeRequest) GetSlug() string { + if x != nil { + return x.Slug + } + return "" +} + +func (x *CreateScopeRequest) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +type CreateScopeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Scope *Scope `protobuf:"bytes,1,opt,name=scope,proto3" json:"scope,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateScopeResponse) Reset() { + *x = CreateScopeResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateScopeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateScopeResponse) ProtoMessage() {} + +func (x *CreateScopeResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[6] + 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 CreateScopeResponse.ProtoReflect.Descriptor instead. +func (*CreateScopeResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{6} +} + +func (x *CreateScopeResponse) GetScope() *Scope { + if x != nil { + return x.Scope + } + return nil +} + +type ListMyScopesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListMyScopesRequest) Reset() { + *x = ListMyScopesRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListMyScopesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListMyScopesRequest) ProtoMessage() {} + +func (x *ListMyScopesRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[7] + 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 ListMyScopesRequest.ProtoReflect.Descriptor instead. +func (*ListMyScopesRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{7} +} + +type ListMyScopesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Scopes []*Scope `protobuf:"bytes,1,rep,name=scopes,proto3" json:"scopes,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListMyScopesResponse) Reset() { + *x = ListMyScopesResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListMyScopesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListMyScopesResponse) ProtoMessage() {} + +func (x *ListMyScopesResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[8] + 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 ListMyScopesResponse.ProtoReflect.Descriptor instead. +func (*ListMyScopesResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{8} +} + +func (x *ListMyScopesResponse) GetScopes() []*Scope { + if x != nil { + return x.Scopes + } + return nil +} + +type GetScopeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetScopeRequest) Reset() { + *x = GetScopeRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetScopeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetScopeRequest) ProtoMessage() {} + +func (x *GetScopeRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[9] + 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 GetScopeRequest.ProtoReflect.Descriptor instead. +func (*GetScopeRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{9} +} + +func (x *GetScopeRequest) GetSlug() string { + if x != nil { + return x.Slug + } + return "" +} + +type GetScopeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Scope *Scope `protobuf:"bytes,1,opt,name=scope,proto3" json:"scope,omitempty"` + Plugins []*Plugin `protobuf:"bytes,2,rep,name=plugins,proto3" json:"plugins,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetScopeResponse) Reset() { + *x = GetScopeResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetScopeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetScopeResponse) ProtoMessage() {} + +func (x *GetScopeResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[10] + 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 GetScopeResponse.ProtoReflect.Descriptor instead. +func (*GetScopeResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{10} +} + +func (x *GetScopeResponse) GetScope() *Scope { + if x != nil { + return x.Scope + } + return nil +} + +func (x *GetScopeResponse) GetPlugins() []*Plugin { + if x != nil { + return x.Plugins + } + return nil +} + +type ListMyPluginsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListMyPluginsRequest) Reset() { + *x = ListMyPluginsRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListMyPluginsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListMyPluginsRequest) ProtoMessage() {} + +func (x *ListMyPluginsRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[11] + 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 ListMyPluginsRequest.ProtoReflect.Descriptor instead. +func (*ListMyPluginsRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{11} +} + +type ListMyPluginsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plugins []*Plugin `protobuf:"bytes,1,rep,name=plugins,proto3" json:"plugins,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListMyPluginsResponse) Reset() { + *x = ListMyPluginsResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListMyPluginsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListMyPluginsResponse) ProtoMessage() {} + +func (x *ListMyPluginsResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[12] + 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 ListMyPluginsResponse.ProtoReflect.Descriptor instead. +func (*ListMyPluginsResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{12} +} + +func (x *ListMyPluginsResponse) GetPlugins() []*Plugin { + if x != nil { + return x.Plugins + } + return nil +} + +type CreatePluginRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ScopeSlug string `protobuf:"bytes,1,opt,name=scope_slug,json=scopeSlug,proto3" json:"scope_slug,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Kind string `protobuf:"bytes,4,opt,name=kind,proto3" json:"kind,omitempty"` + Categories []string `protobuf:"bytes,5,rep,name=categories,proto3" json:"categories,omitempty"` + DisplayName string `protobuf:"bytes,6,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + // visibility is the requested visibility of the plugin. UNSPECIFIED defers + // to the registry's default (public for explicit scopes, private for the + // "@private" sentinel). + Visibility PluginVisibility `protobuf:"varint,7,opt,name=visibility,proto3,enum=orchestrator.v1.PluginVisibility" json:"visibility,omitempty"` + // 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. + ActiveAccountId string `protobuf:"bytes,8,opt,name=active_account_id,json=activeAccountId,proto3" json:"active_account_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreatePluginRequest) Reset() { + *x = CreatePluginRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreatePluginRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreatePluginRequest) ProtoMessage() {} + +func (x *CreatePluginRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[13] + 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 CreatePluginRequest.ProtoReflect.Descriptor instead. +func (*CreatePluginRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{13} +} + +func (x *CreatePluginRequest) GetScopeSlug() string { + if x != nil { + return x.ScopeSlug + } + return "" +} + +func (x *CreatePluginRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CreatePluginRequest) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *CreatePluginRequest) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *CreatePluginRequest) GetCategories() []string { + if x != nil { + return x.Categories + } + return nil +} + +func (x *CreatePluginRequest) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *CreatePluginRequest) GetVisibility() PluginVisibility { + if x != nil { + return x.Visibility + } + return PluginVisibility_PLUGIN_VISIBILITY_UNSPECIFIED +} + +func (x *CreatePluginRequest) GetActiveAccountId() string { + if x != nil { + return x.ActiveAccountId + } + return "" +} + +type CreatePluginResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plugin *Plugin `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreatePluginResponse) Reset() { + *x = CreatePluginResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreatePluginResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreatePluginResponse) ProtoMessage() {} + +func (x *CreatePluginResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[14] + 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 CreatePluginResponse.ProtoReflect.Descriptor instead. +func (*CreatePluginResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{14} +} + +func (x *CreatePluginResponse) GetPlugin() *Plugin { + if x != nil { + return x.Plugin + } + return nil +} + +type GetPluginRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ScopeSlug string `protobuf:"bytes,1,opt,name=scope_slug,json=scopeSlug,proto3" json:"scope_slug,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // active_account_id is required when scope_slug = "private" so the server + // can resolve (account_id, name) instead of (scope_id, name). Ignored + // otherwise. + ActiveAccountId string `protobuf:"bytes,3,opt,name=active_account_id,json=activeAccountId,proto3" json:"active_account_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetPluginRequest) Reset() { + *x = GetPluginRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetPluginRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPluginRequest) ProtoMessage() {} + +func (x *GetPluginRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[15] + 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 GetPluginRequest.ProtoReflect.Descriptor instead. +func (*GetPluginRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{15} +} + +func (x *GetPluginRequest) GetScopeSlug() string { + if x != nil { + return x.ScopeSlug + } + return "" +} + +func (x *GetPluginRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetPluginRequest) GetActiveAccountId() string { + if x != nil { + return x.ActiveAccountId + } + return "" +} + +type GetPluginResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plugin *Plugin `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Versions []*Version `protobuf:"bytes,2,rep,name=versions,proto3" json:"versions,omitempty"` + Channels map[string]string `protobuf:"bytes,3,rep,name=channels,proto3" json:"channels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetPluginResponse) Reset() { + *x = GetPluginResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetPluginResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPluginResponse) ProtoMessage() {} + +func (x *GetPluginResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[16] + 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 GetPluginResponse.ProtoReflect.Descriptor instead. +func (*GetPluginResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{16} +} + +func (x *GetPluginResponse) GetPlugin() *Plugin { + if x != nil { + return x.Plugin + } + return nil +} + +func (x *GetPluginResponse) GetVersions() []*Version { + if x != nil { + return x.Versions + } + return nil +} + +func (x *GetPluginResponse) GetChannels() map[string]string { + if x != nil { + return x.Channels + } + return nil +} + +type ListPluginsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Limit int32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` + Offset int32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` + Query string `protobuf:"bytes,3,opt,name=query,proto3" json:"query,omitempty"` + Kind string `protobuf:"bytes,4,opt,name=kind,proto3" json:"kind,omitempty"` + Categories []string `protobuf:"bytes,5,rep,name=categories,proto3" json:"categories,omitempty"` + Tags []string `protobuf:"bytes,6,rep,name=tags,proto3" json:"tags,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPluginsRequest) Reset() { + *x = ListPluginsRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPluginsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPluginsRequest) ProtoMessage() {} + +func (x *ListPluginsRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[17] + 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 ListPluginsRequest.ProtoReflect.Descriptor instead. +func (*ListPluginsRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{17} +} + +func (x *ListPluginsRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ListPluginsRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *ListPluginsRequest) GetQuery() string { + if x != nil { + return x.Query + } + return "" +} + +func (x *ListPluginsRequest) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *ListPluginsRequest) GetCategories() []string { + if x != nil { + return x.Categories + } + return nil +} + +func (x *ListPluginsRequest) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + +type ListPluginsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plugins []*Plugin `protobuf:"bytes,1,rep,name=plugins,proto3" json:"plugins,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPluginsResponse) Reset() { + *x = ListPluginsResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPluginsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPluginsResponse) ProtoMessage() {} + +func (x *ListPluginsResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[18] + 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 ListPluginsResponse.ProtoReflect.Descriptor instead. +func (*ListPluginsResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{18} +} + +func (x *ListPluginsResponse) GetPlugins() []*Plugin { + if x != nil { + return x.Plugins + } + return nil +} + +type ListCategoriesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListCategoriesRequest) Reset() { + *x = ListCategoriesRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListCategoriesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListCategoriesRequest) ProtoMessage() {} + +func (x *ListCategoriesRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[19] + 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 ListCategoriesRequest.ProtoReflect.Descriptor instead. +func (*ListCategoriesRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{19} +} + +type ListCategoriesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Categories []*Category `protobuf:"bytes,1,rep,name=categories,proto3" json:"categories,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListCategoriesResponse) Reset() { + *x = ListCategoriesResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListCategoriesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListCategoriesResponse) ProtoMessage() {} + +func (x *ListCategoriesResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[20] + 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 ListCategoriesResponse.ProtoReflect.Descriptor instead. +func (*ListCategoriesResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{20} +} + +func (x *ListCategoriesResponse) GetCategories() []*Category { + if x != nil { + return x.Categories + } + return nil +} + +type ListTagsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Optional kind filter: "plugin", "theme", or "" for both. Lets the browse + // UI show theme-specific tags when the user filters by kind=theme. + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` + // Cap the response. 0 → server default (100). + Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListTagsRequest) Reset() { + *x = ListTagsRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListTagsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListTagsRequest) ProtoMessage() {} + +func (x *ListTagsRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[21] + 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 ListTagsRequest.ProtoReflect.Descriptor instead. +func (*ListTagsRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{21} +} + +func (x *ListTagsRequest) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *ListTagsRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type ListTagsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Tags []*TagCount `protobuf:"bytes,1,rep,name=tags,proto3" json:"tags,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListTagsResponse) Reset() { + *x = ListTagsResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListTagsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListTagsResponse) ProtoMessage() {} + +func (x *ListTagsResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[22] + 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 ListTagsResponse.ProtoReflect.Descriptor instead. +func (*ListTagsResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{22} +} + +func (x *ListTagsResponse) GetTags() []*TagCount { + if x != nil { + return x.Tags + } + return nil +} + +type TagCount struct { + state protoimpl.MessageState `protogen:"open.v1"` + Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` + Count int32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TagCount) Reset() { + *x = TagCount{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TagCount) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TagCount) ProtoMessage() {} + +func (x *TagCount) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[23] + 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 TagCount.ProtoReflect.Descriptor instead. +func (*TagCount) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{23} +} + +func (x *TagCount) GetTag() string { + if x != nil { + return x.Tag + } + return "" +} + +func (x *TagCount) GetCount() int32 { + if x != nil { + return x.Count + } + return 0 +} + +type ListPrivatePluginsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // account_id selects which account's private plugins to list. The caller + // must be a member of this account. + AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPrivatePluginsRequest) Reset() { + *x = ListPrivatePluginsRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPrivatePluginsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPrivatePluginsRequest) ProtoMessage() {} + +func (x *ListPrivatePluginsRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[24] + 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 ListPrivatePluginsRequest.ProtoReflect.Descriptor instead. +func (*ListPrivatePluginsRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{24} +} + +func (x *ListPrivatePluginsRequest) GetAccountId() string { + if x != nil { + return x.AccountId + } + return "" +} + +type ListPrivatePluginsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plugins []*PrivatePluginSummary `protobuf:"bytes,1,rep,name=plugins,proto3" json:"plugins,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPrivatePluginsResponse) Reset() { + *x = ListPrivatePluginsResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPrivatePluginsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPrivatePluginsResponse) ProtoMessage() {} + +func (x *ListPrivatePluginsResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[25] + 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 ListPrivatePluginsResponse.ProtoReflect.Descriptor instead. +func (*ListPrivatePluginsResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{25} +} + +func (x *ListPrivatePluginsResponse) GetPlugins() []*PrivatePluginSummary { + if x != nil { + return x.Plugins + } + return nil +} + +// 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. +type PrivatePluginSummary struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plugin *Plugin `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + // channel_versions maps channel name -> latest version string published on + // that channel (e.g. "latest" -> "0.3.0", "beta" -> "0.4.0-beta.1"). + ChannelVersions map[string]string `protobuf:"bytes,2,rep,name=channel_versions,json=channelVersions,proto3" json:"channel_versions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // 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. + InstalledSiteCount int32 `protobuf:"varint,3,opt,name=installed_site_count,json=installedSiteCount,proto3" json:"installed_site_count,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PrivatePluginSummary) Reset() { + *x = PrivatePluginSummary{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PrivatePluginSummary) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PrivatePluginSummary) ProtoMessage() {} + +func (x *PrivatePluginSummary) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[26] + 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 PrivatePluginSummary.ProtoReflect.Descriptor instead. +func (*PrivatePluginSummary) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{26} +} + +func (x *PrivatePluginSummary) GetPlugin() *Plugin { + if x != nil { + return x.Plugin + } + return nil +} + +func (x *PrivatePluginSummary) GetChannelVersions() map[string]string { + if x != nil { + return x.ChannelVersions + } + return nil +} + +func (x *PrivatePluginSummary) GetInstalledSiteCount() int32 { + if x != nil { + return x.InstalledSiteCount + } + return 0 +} + +type DeletePrivatePluginRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` + PluginName string `protobuf:"bytes,2,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeletePrivatePluginRequest) Reset() { + *x = DeletePrivatePluginRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeletePrivatePluginRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeletePrivatePluginRequest) ProtoMessage() {} + +func (x *DeletePrivatePluginRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[27] + 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 DeletePrivatePluginRequest.ProtoReflect.Descriptor instead. +func (*DeletePrivatePluginRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{27} +} + +func (x *DeletePrivatePluginRequest) GetAccountId() string { + if x != nil { + return x.AccountId + } + return "" +} + +func (x *DeletePrivatePluginRequest) GetPluginName() string { + if x != nil { + return x.PluginName + } + return "" +} + +type DeletePrivatePluginResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeletePrivatePluginResponse) Reset() { + *x = DeletePrivatePluginResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeletePrivatePluginResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeletePrivatePluginResponse) ProtoMessage() {} + +func (x *DeletePrivatePluginResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[28] + 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 DeletePrivatePluginResponse.ProtoReflect.Descriptor instead. +func (*DeletePrivatePluginResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{28} +} + +type DeletePrivatePluginVersionRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` + PluginName string `protobuf:"bytes,2,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeletePrivatePluginVersionRequest) Reset() { + *x = DeletePrivatePluginVersionRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeletePrivatePluginVersionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeletePrivatePluginVersionRequest) ProtoMessage() {} + +func (x *DeletePrivatePluginVersionRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[29] + 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 DeletePrivatePluginVersionRequest.ProtoReflect.Descriptor instead. +func (*DeletePrivatePluginVersionRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{29} +} + +func (x *DeletePrivatePluginVersionRequest) GetAccountId() string { + if x != nil { + return x.AccountId + } + return "" +} + +func (x *DeletePrivatePluginVersionRequest) GetPluginName() string { + if x != nil { + return x.PluginName + } + return "" +} + +func (x *DeletePrivatePluginVersionRequest) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +type DeletePrivatePluginVersionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeletePrivatePluginVersionResponse) Reset() { + *x = DeletePrivatePluginVersionResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeletePrivatePluginVersionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeletePrivatePluginVersionResponse) ProtoMessage() {} + +func (x *DeletePrivatePluginVersionResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[30] + 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 DeletePrivatePluginVersionResponse.ProtoReflect.Descriptor instead. +func (*DeletePrivatePluginVersionResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{30} +} + +type ListPrivatePluginInstallSitesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` + PluginName string `protobuf:"bytes,2,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPrivatePluginInstallSitesRequest) Reset() { + *x = ListPrivatePluginInstallSitesRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPrivatePluginInstallSitesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPrivatePluginInstallSitesRequest) ProtoMessage() {} + +func (x *ListPrivatePluginInstallSitesRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[31] + 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 ListPrivatePluginInstallSitesRequest.ProtoReflect.Descriptor instead. +func (*ListPrivatePluginInstallSitesRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{31} +} + +func (x *ListPrivatePluginInstallSitesRequest) GetAccountId() string { + if x != nil { + return x.AccountId + } + return "" +} + +func (x *ListPrivatePluginInstallSitesRequest) GetPluginName() string { + if x != nil { + return x.PluginName + } + return "" +} + +type ListPrivatePluginInstallSitesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Sites []*PrivatePluginInstallSite `protobuf:"bytes,1,rep,name=sites,proto3" json:"sites,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPrivatePluginInstallSitesResponse) Reset() { + *x = ListPrivatePluginInstallSitesResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPrivatePluginInstallSitesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPrivatePluginInstallSitesResponse) ProtoMessage() {} + +func (x *ListPrivatePluginInstallSitesResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[32] + 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 ListPrivatePluginInstallSitesResponse.ProtoReflect.Descriptor instead. +func (*ListPrivatePluginInstallSitesResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{32} +} + +func (x *ListPrivatePluginInstallSitesResponse) GetSites() []*PrivatePluginInstallSite { + if x != nil { + return x.Sites + } + return nil +} + +type PrivatePluginInstallSite struct { + state protoimpl.MessageState `protogen:"open.v1"` + InstanceId string `protobuf:"bytes,1,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` + SiteDisplayName string `protobuf:"bytes,2,opt,name=site_display_name,json=siteDisplayName,proto3" json:"site_display_name,omitempty"` + InstalledVersion string `protobuf:"bytes,3,opt,name=installed_version,json=installedVersion,proto3" json:"installed_version,omitempty"` + PinnedChannel string `protobuf:"bytes,4,opt,name=pinned_channel,json=pinnedChannel,proto3" json:"pinned_channel,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PrivatePluginInstallSite) Reset() { + *x = PrivatePluginInstallSite{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PrivatePluginInstallSite) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PrivatePluginInstallSite) ProtoMessage() {} + +func (x *PrivatePluginInstallSite) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[33] + 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 PrivatePluginInstallSite.ProtoReflect.Descriptor instead. +func (*PrivatePluginInstallSite) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{33} +} + +func (x *PrivatePluginInstallSite) GetInstanceId() string { + if x != nil { + return x.InstanceId + } + return "" +} + +func (x *PrivatePluginInstallSite) GetSiteDisplayName() string { + if x != nil { + return x.SiteDisplayName + } + return "" +} + +func (x *PrivatePluginInstallSite) GetInstalledVersion() string { + if x != nil { + return x.InstalledVersion + } + return "" +} + +func (x *PrivatePluginInstallSite) GetPinnedChannel() string { + if x != nil { + return x.PinnedChannel + } + return "" +} + +type GetVersionRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ScopeSlug string `protobuf:"bytes,1,opt,name=scope_slug,json=scopeSlug,proto3" json:"scope_slug,omitempty"` + PluginName string `protobuf:"bytes,2,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetVersionRequest) Reset() { + *x = GetVersionRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetVersionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetVersionRequest) ProtoMessage() {} + +func (x *GetVersionRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[34] + 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 GetVersionRequest.ProtoReflect.Descriptor instead. +func (*GetVersionRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{34} +} + +func (x *GetVersionRequest) GetScopeSlug() string { + if x != nil { + return x.ScopeSlug + } + return "" +} + +func (x *GetVersionRequest) GetPluginName() string { + if x != nil { + return x.PluginName + } + return "" +} + +func (x *GetVersionRequest) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +type GetVersionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Version *Version `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + Requires []*Requirement `protobuf:"bytes,2,rep,name=requires,proto3" json:"requires,omitempty"` + ReadmeMd string `protobuf:"bytes,3,opt,name=readme_md,json=readmeMd,proto3" json:"readme_md,omitempty"` + ChangelogMd string `protobuf:"bytes,4,opt,name=changelog_md,json=changelogMd,proto3" json:"changelog_md,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetVersionResponse) Reset() { + *x = GetVersionResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetVersionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetVersionResponse) ProtoMessage() {} + +func (x *GetVersionResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[35] + 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 GetVersionResponse.ProtoReflect.Descriptor instead. +func (*GetVersionResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{35} +} + +func (x *GetVersionResponse) GetVersion() *Version { + if x != nil { + return x.Version + } + return nil +} + +func (x *GetVersionResponse) GetRequires() []*Requirement { + if x != nil { + return x.Requires + } + return nil +} + +func (x *GetVersionResponse) GetReadmeMd() string { + if x != nil { + return x.ReadmeMd + } + return "" +} + +func (x *GetVersionResponse) GetChangelogMd() string { + if x != nil { + return x.ChangelogMd + } + return "" +} + +type ResolveInstallRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ScopeSlug string `protobuf:"bytes,1,opt,name=scope_slug,json=scopeSlug,proto3" json:"scope_slug,omitempty"` + PluginName string `protobuf:"bytes,2,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty"` + VersionOrChannel string `protobuf:"bytes,3,opt,name=version_or_channel,json=versionOrChannel,proto3" json:"version_or_channel,omitempty"` + // 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. + ActiveAccountId string `protobuf:"bytes,4,opt,name=active_account_id,json=activeAccountId,proto3" json:"active_account_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ResolveInstallRequest) Reset() { + *x = ResolveInstallRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ResolveInstallRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResolveInstallRequest) ProtoMessage() {} + +func (x *ResolveInstallRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[36] + 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 ResolveInstallRequest.ProtoReflect.Descriptor instead. +func (*ResolveInstallRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{36} +} + +func (x *ResolveInstallRequest) GetScopeSlug() string { + if x != nil { + return x.ScopeSlug + } + return "" +} + +func (x *ResolveInstallRequest) GetPluginName() string { + if x != nil { + return x.PluginName + } + return "" +} + +func (x *ResolveInstallRequest) GetVersionOrChannel() string { + if x != nil { + return x.VersionOrChannel + } + return "" +} + +func (x *ResolveInstallRequest) GetActiveAccountId() string { + if x != nil { + return x.ActiveAccountId + } + return "" +} + +type ResolveInstallResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + VersionId string `protobuf:"bytes,1,opt,name=version_id,json=versionId,proto3" json:"version_id,omitempty"` + ScopeSlug string `protobuf:"bytes,2,opt,name=scope_slug,json=scopeSlug,proto3" json:"scope_slug,omitempty"` + PluginName string `protobuf:"bytes,3,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty"` + Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + ChannelPinned string `protobuf:"bytes,5,opt,name=channel_pinned,json=channelPinned,proto3" json:"channel_pinned,omitempty"` + ArchiveUrl string `protobuf:"bytes,6,opt,name=archive_url,json=archiveUrl,proto3" json:"archive_url,omitempty"` + ArchiveSha256 string `protobuf:"bytes,7,opt,name=archive_sha256,json=archiveSha256,proto3" json:"archive_sha256,omitempty"` + SdkConstraint string `protobuf:"bytes,8,opt,name=sdk_constraint,json=sdkConstraint,proto3" json:"sdk_constraint,omitempty"` + Requires []*Requirement `protobuf:"bytes,9,rep,name=requires,proto3" json:"requires,omitempty"` + Yanked bool `protobuf:"varint,10,opt,name=yanked,proto3" json:"yanked,omitempty"` + Warnings []string `protobuf:"bytes,11,rep,name=warnings,proto3" json:"warnings,omitempty"` + // 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. + AbiVersion uint32 `protobuf:"varint,12,opt,name=abi_version,json=abiVersion,proto3" json:"abi_version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ResolveInstallResponse) Reset() { + *x = ResolveInstallResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ResolveInstallResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResolveInstallResponse) ProtoMessage() {} + +func (x *ResolveInstallResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[37] + 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 ResolveInstallResponse.ProtoReflect.Descriptor instead. +func (*ResolveInstallResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{37} +} + +func (x *ResolveInstallResponse) GetVersionId() string { + if x != nil { + return x.VersionId + } + return "" +} + +func (x *ResolveInstallResponse) GetScopeSlug() string { + if x != nil { + return x.ScopeSlug + } + return "" +} + +func (x *ResolveInstallResponse) GetPluginName() string { + if x != nil { + return x.PluginName + } + return "" +} + +func (x *ResolveInstallResponse) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *ResolveInstallResponse) GetChannelPinned() string { + if x != nil { + return x.ChannelPinned + } + return "" +} + +func (x *ResolveInstallResponse) GetArchiveUrl() string { + if x != nil { + return x.ArchiveUrl + } + return "" +} + +func (x *ResolveInstallResponse) GetArchiveSha256() string { + if x != nil { + return x.ArchiveSha256 + } + return "" +} + +func (x *ResolveInstallResponse) GetSdkConstraint() string { + if x != nil { + return x.SdkConstraint + } + return "" +} + +func (x *ResolveInstallResponse) GetRequires() []*Requirement { + if x != nil { + return x.Requires + } + return nil +} + +func (x *ResolveInstallResponse) GetYanked() bool { + if x != nil { + return x.Yanked + } + return false +} + +func (x *ResolveInstallResponse) GetWarnings() []string { + if x != nil { + return x.Warnings + } + return nil +} + +func (x *ResolveInstallResponse) GetAbiVersion() uint32 { + if x != nil { + return x.AbiVersion + } + return 0 +} + +type ListPrivatePluginsForInstanceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + InstanceId string `protobuf:"bytes,1,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPrivatePluginsForInstanceRequest) Reset() { + *x = ListPrivatePluginsForInstanceRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPrivatePluginsForInstanceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPrivatePluginsForInstanceRequest) ProtoMessage() {} + +func (x *ListPrivatePluginsForInstanceRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[38] + 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 ListPrivatePluginsForInstanceRequest.ProtoReflect.Descriptor instead. +func (*ListPrivatePluginsForInstanceRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{38} +} + +func (x *ListPrivatePluginsForInstanceRequest) GetInstanceId() string { + if x != nil { + return x.InstanceId + } + return "" +} + +type ResolveInstallForInstanceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + InstanceId string `protobuf:"bytes,1,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` + PluginName string `protobuf:"bytes,2,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty"` + VersionOrChannel string `protobuf:"bytes,3,opt,name=version_or_channel,json=versionOrChannel,proto3" json:"version_or_channel,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ResolveInstallForInstanceRequest) Reset() { + *x = ResolveInstallForInstanceRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ResolveInstallForInstanceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResolveInstallForInstanceRequest) ProtoMessage() {} + +func (x *ResolveInstallForInstanceRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[39] + 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 ResolveInstallForInstanceRequest.ProtoReflect.Descriptor instead. +func (*ResolveInstallForInstanceRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{39} +} + +func (x *ResolveInstallForInstanceRequest) GetInstanceId() string { + if x != nil { + return x.InstanceId + } + return "" +} + +func (x *ResolveInstallForInstanceRequest) GetPluginName() string { + if x != nil { + return x.PluginName + } + return "" +} + +func (x *ResolveInstallForInstanceRequest) GetVersionOrChannel() string { + if x != nil { + return x.VersionOrChannel + } + return "" +} + +type PendingReview struct { + state protoimpl.MessageState `protogen:"open.v1"` + ReviewId string `protobuf:"bytes,1,opt,name=review_id,json=reviewId,proto3" json:"review_id,omitempty"` + PluginId string `protobuf:"bytes,2,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + ScopeSlug string `protobuf:"bytes,3,opt,name=scope_slug,json=scopeSlug,proto3" json:"scope_slug,omitempty"` + PluginName string `protobuf:"bytes,4,opt,name=plugin_name,json=pluginName,proto3" json:"plugin_name,omitempty"` + PluginDisplayName string `protobuf:"bytes,5,opt,name=plugin_display_name,json=pluginDisplayName,proto3" json:"plugin_display_name,omitempty"` + SubmittedByUserId string `protobuf:"bytes,6,opt,name=submitted_by_user_id,json=submittedByUserId,proto3" json:"submitted_by_user_id,omitempty"` + SubmittedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=submitted_at,json=submittedAt,proto3" json:"submitted_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PendingReview) Reset() { + *x = PendingReview{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PendingReview) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PendingReview) ProtoMessage() {} + +func (x *PendingReview) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[40] + 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 PendingReview.ProtoReflect.Descriptor instead. +func (*PendingReview) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{40} +} + +func (x *PendingReview) GetReviewId() string { + if x != nil { + return x.ReviewId + } + return "" +} + +func (x *PendingReview) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *PendingReview) GetScopeSlug() string { + if x != nil { + return x.ScopeSlug + } + return "" +} + +func (x *PendingReview) GetPluginName() string { + if x != nil { + return x.PluginName + } + return "" +} + +func (x *PendingReview) GetPluginDisplayName() string { + if x != nil { + return x.PluginDisplayName + } + return "" +} + +func (x *PendingReview) GetSubmittedByUserId() string { + if x != nil { + return x.SubmittedByUserId + } + return "" +} + +func (x *PendingReview) GetSubmittedAt() *timestamppb.Timestamp { + if x != nil { + return x.SubmittedAt + } + return nil +} + +type SubmitForReviewRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitForReviewRequest) Reset() { + *x = SubmitForReviewRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitForReviewRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitForReviewRequest) ProtoMessage() {} + +func (x *SubmitForReviewRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[41] + 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 SubmitForReviewRequest.ProtoReflect.Descriptor instead. +func (*SubmitForReviewRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{41} +} + +func (x *SubmitForReviewRequest) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +type SubmitForReviewResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // 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. + Visibility string `protobuf:"bytes,1,opt,name=visibility,proto3" json:"visibility,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitForReviewResponse) Reset() { + *x = SubmitForReviewResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitForReviewResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitForReviewResponse) ProtoMessage() {} + +func (x *SubmitForReviewResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[42] + 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 SubmitForReviewResponse.ProtoReflect.Descriptor instead. +func (*SubmitForReviewResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{42} +} + +func (x *SubmitForReviewResponse) GetVisibility() string { + if x != nil { + return x.Visibility + } + return "" +} + +type ListPendingReviewsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Limit int32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` + Offset int32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPendingReviewsRequest) Reset() { + *x = ListPendingReviewsRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPendingReviewsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPendingReviewsRequest) ProtoMessage() {} + +func (x *ListPendingReviewsRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[43] + 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 ListPendingReviewsRequest.ProtoReflect.Descriptor instead. +func (*ListPendingReviewsRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{43} +} + +func (x *ListPendingReviewsRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ListPendingReviewsRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +type ListPendingReviewsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Reviews []*PendingReview `protobuf:"bytes,1,rep,name=reviews,proto3" json:"reviews,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPendingReviewsResponse) Reset() { + *x = ListPendingReviewsResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPendingReviewsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPendingReviewsResponse) ProtoMessage() {} + +func (x *ListPendingReviewsResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[44] + 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 ListPendingReviewsResponse.ProtoReflect.Descriptor instead. +func (*ListPendingReviewsResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{44} +} + +func (x *ListPendingReviewsResponse) GetReviews() []*PendingReview { + if x != nil { + return x.Reviews + } + return nil +} + +type ApproveSubmissionRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` // optional note for audit log + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ApproveSubmissionRequest) Reset() { + *x = ApproveSubmissionRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ApproveSubmissionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApproveSubmissionRequest) ProtoMessage() {} + +func (x *ApproveSubmissionRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[45] + 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 ApproveSubmissionRequest.ProtoReflect.Descriptor instead. +func (*ApproveSubmissionRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{45} +} + +func (x *ApproveSubmissionRequest) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *ApproveSubmissionRequest) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +type ApproveSubmissionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ApproveSubmissionResponse) Reset() { + *x = ApproveSubmissionResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ApproveSubmissionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApproveSubmissionResponse) ProtoMessage() {} + +func (x *ApproveSubmissionResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[46] + 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 ApproveSubmissionResponse.ProtoReflect.Descriptor instead. +func (*ApproveSubmissionResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{46} +} + +type RejectSubmissionRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` // required + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RejectSubmissionRequest) Reset() { + *x = RejectSubmissionRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RejectSubmissionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RejectSubmissionRequest) ProtoMessage() {} + +func (x *RejectSubmissionRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[47] + 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 RejectSubmissionRequest.ProtoReflect.Descriptor instead. +func (*RejectSubmissionRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{47} +} + +func (x *RejectSubmissionRequest) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *RejectSubmissionRequest) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +type RejectSubmissionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RejectSubmissionResponse) Reset() { + *x = RejectSubmissionResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RejectSubmissionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RejectSubmissionResponse) ProtoMessage() {} + +func (x *RejectSubmissionResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[48] + 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 RejectSubmissionResponse.ProtoReflect.Descriptor instead. +func (*RejectSubmissionResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{48} +} + +type RequestChangesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` // required — author sees this verbatim + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RequestChangesRequest) Reset() { + *x = RequestChangesRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RequestChangesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RequestChangesRequest) ProtoMessage() {} + +func (x *RequestChangesRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[49] + 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 RequestChangesRequest.ProtoReflect.Descriptor instead. +func (*RequestChangesRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{49} +} + +func (x *RequestChangesRequest) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *RequestChangesRequest) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +type RequestChangesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RequestChangesResponse) Reset() { + *x = RequestChangesResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RequestChangesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RequestChangesResponse) ProtoMessage() {} + +func (x *RequestChangesResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[50] + 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 RequestChangesResponse.ProtoReflect.Descriptor instead. +func (*RequestChangesResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{50} +} + +type PublishVersionRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + PluginId string `protobuf:"bytes,1,opt,name=plugin_id,json=pluginId,proto3" json:"plugin_id,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + Channel string `protobuf:"bytes,3,opt,name=channel,proto3" json:"channel,omitempty"` + Archive []byte `protobuf:"bytes,4,opt,name=archive,proto3" json:"archive,omitempty"` + ReadmeMd string `protobuf:"bytes,5,opt,name=readme_md,json=readmeMd,proto3" json:"readme_md,omitempty"` + ChangelogMd string `protobuf:"bytes,6,opt,name=changelog_md,json=changelogMd,proto3" json:"changelog_md,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PublishVersionRequest) Reset() { + *x = PublishVersionRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PublishVersionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PublishVersionRequest) ProtoMessage() {} + +func (x *PublishVersionRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[51] + 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 PublishVersionRequest.ProtoReflect.Descriptor instead. +func (*PublishVersionRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{51} +} + +func (x *PublishVersionRequest) GetPluginId() string { + if x != nil { + return x.PluginId + } + return "" +} + +func (x *PublishVersionRequest) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *PublishVersionRequest) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +func (x *PublishVersionRequest) GetArchive() []byte { + if x != nil { + return x.Archive + } + return nil +} + +func (x *PublishVersionRequest) GetReadmeMd() string { + if x != nil { + return x.ReadmeMd + } + return "" +} + +func (x *PublishVersionRequest) GetChangelogMd() string { + if x != nil { + return x.ChangelogMd + } + return "" +} + +type PublishVersionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Version *Version `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + ArchiveUrl string `protobuf:"bytes,2,opt,name=archive_url,json=archiveUrl,proto3" json:"archive_url,omitempty"` + Warnings []string `protobuf:"bytes,3,rep,name=warnings,proto3" json:"warnings,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PublishVersionResponse) Reset() { + *x = PublishVersionResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PublishVersionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PublishVersionResponse) ProtoMessage() {} + +func (x *PublishVersionResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[52] + 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 PublishVersionResponse.ProtoReflect.Descriptor instead. +func (*PublishVersionResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{52} +} + +func (x *PublishVersionResponse) GetVersion() *Version { + if x != nil { + return x.Version + } + return nil +} + +func (x *PublishVersionResponse) GetArchiveUrl() string { + if x != nil { + return x.ArchiveUrl + } + return "" +} + +func (x *PublishVersionResponse) GetWarnings() []string { + if x != nil { + return x.Warnings + } + return nil +} + +type StartDeviceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Scopes []string `protobuf:"bytes,1,rep,name=scopes,proto3" json:"scopes,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StartDeviceRequest) Reset() { + *x = StartDeviceRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StartDeviceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartDeviceRequest) ProtoMessage() {} + +func (x *StartDeviceRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[53] + 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 StartDeviceRequest.ProtoReflect.Descriptor instead. +func (*StartDeviceRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{53} +} + +func (x *StartDeviceRequest) GetScopes() []string { + if x != nil { + return x.Scopes + } + return nil +} + +type StartDeviceResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + DeviceCode string `protobuf:"bytes,1,opt,name=device_code,json=deviceCode,proto3" json:"device_code,omitempty"` + UserCode string `protobuf:"bytes,2,opt,name=user_code,json=userCode,proto3" json:"user_code,omitempty"` + VerificationUri string `protobuf:"bytes,3,opt,name=verification_uri,json=verificationUri,proto3" json:"verification_uri,omitempty"` + IntervalSeconds int32 `protobuf:"varint,4,opt,name=interval_seconds,json=intervalSeconds,proto3" json:"interval_seconds,omitempty"` + ExpiresInSeconds int32 `protobuf:"varint,5,opt,name=expires_in_seconds,json=expiresInSeconds,proto3" json:"expires_in_seconds,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StartDeviceResponse) Reset() { + *x = StartDeviceResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StartDeviceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartDeviceResponse) ProtoMessage() {} + +func (x *StartDeviceResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[54] + 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 StartDeviceResponse.ProtoReflect.Descriptor instead. +func (*StartDeviceResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{54} +} + +func (x *StartDeviceResponse) GetDeviceCode() string { + if x != nil { + return x.DeviceCode + } + return "" +} + +func (x *StartDeviceResponse) GetUserCode() string { + if x != nil { + return x.UserCode + } + return "" +} + +func (x *StartDeviceResponse) GetVerificationUri() string { + if x != nil { + return x.VerificationUri + } + return "" +} + +func (x *StartDeviceResponse) GetIntervalSeconds() int32 { + if x != nil { + return x.IntervalSeconds + } + return 0 +} + +func (x *StartDeviceResponse) GetExpiresInSeconds() int32 { + if x != nil { + return x.ExpiresInSeconds + } + return 0 +} + +type PollDeviceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + DeviceCode string `protobuf:"bytes,1,opt,name=device_code,json=deviceCode,proto3" json:"device_code,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PollDeviceRequest) Reset() { + *x = PollDeviceRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PollDeviceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollDeviceRequest) ProtoMessage() {} + +func (x *PollDeviceRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[55] + 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 PollDeviceRequest.ProtoReflect.Descriptor instead. +func (*PollDeviceRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{55} +} + +func (x *PollDeviceRequest) GetDeviceCode() string { + if x != nil { + return x.DeviceCode + } + return "" +} + +type PollDeviceResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PollDeviceResponse) Reset() { + *x = PollDeviceResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PollDeviceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PollDeviceResponse) ProtoMessage() {} + +func (x *PollDeviceResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[56] + 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 PollDeviceResponse.ProtoReflect.Descriptor instead. +func (*PollDeviceResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{56} +} + +func (x *PollDeviceResponse) GetAccessToken() string { + if x != nil { + return x.AccessToken + } + return "" +} + +func (x *PollDeviceResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +type ApproveDeviceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + UserCode string `protobuf:"bytes,1,opt,name=user_code,json=userCode,proto3" json:"user_code,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ApproveDeviceRequest) Reset() { + *x = ApproveDeviceRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ApproveDeviceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApproveDeviceRequest) ProtoMessage() {} + +func (x *ApproveDeviceRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[57] + 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 ApproveDeviceRequest.ProtoReflect.Descriptor instead. +func (*ApproveDeviceRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{57} +} + +func (x *ApproveDeviceRequest) GetUserCode() string { + if x != nil { + return x.UserCode + } + return "" +} + +type ApproveDeviceResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ApproveDeviceResponse) Reset() { + *x = ApproveDeviceResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ApproveDeviceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApproveDeviceResponse) ProtoMessage() {} + +func (x *ApproveDeviceResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[58] + 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 ApproveDeviceResponse.ProtoReflect.Descriptor instead. +func (*ApproveDeviceResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{58} +} + +type DenyDeviceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + UserCode string `protobuf:"bytes,1,opt,name=user_code,json=userCode,proto3" json:"user_code,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DenyDeviceRequest) Reset() { + *x = DenyDeviceRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[59] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DenyDeviceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DenyDeviceRequest) ProtoMessage() {} + +func (x *DenyDeviceRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[59] + 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 DenyDeviceRequest.ProtoReflect.Descriptor instead. +func (*DenyDeviceRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{59} +} + +func (x *DenyDeviceRequest) GetUserCode() string { + if x != nil { + return x.UserCode + } + return "" +} + +type DenyDeviceResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DenyDeviceResponse) Reset() { + *x = DenyDeviceResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[60] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DenyDeviceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DenyDeviceResponse) ProtoMessage() {} + +func (x *DenyDeviceResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[60] + 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 DenyDeviceResponse.ProtoReflect.Descriptor instead. +func (*DenyDeviceResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{60} +} + +type GetDeviceStatusRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + UserCode string `protobuf:"bytes,1,opt,name=user_code,json=userCode,proto3" json:"user_code,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetDeviceStatusRequest) Reset() { + *x = GetDeviceStatusRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[61] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetDeviceStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDeviceStatusRequest) ProtoMessage() {} + +func (x *GetDeviceStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[61] + 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 GetDeviceStatusRequest.ProtoReflect.Descriptor instead. +func (*GetDeviceStatusRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{61} +} + +func (x *GetDeviceStatusRequest) GetUserCode() string { + if x != nil { + return x.UserCode + } + return "" +} + +type GetDeviceStatusResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Valid bool `protobuf:"varint,1,opt,name=valid,proto3" json:"valid,omitempty"` + Authorized bool `protobuf:"varint,2,opt,name=authorized,proto3" json:"authorized,omitempty"` + Denied bool `protobuf:"varint,3,opt,name=denied,proto3" json:"denied,omitempty"` + ClientId string `protobuf:"bytes,4,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetDeviceStatusResponse) Reset() { + *x = GetDeviceStatusResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[62] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetDeviceStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDeviceStatusResponse) ProtoMessage() {} + +func (x *GetDeviceStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[62] + 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 GetDeviceStatusResponse.ProtoReflect.Descriptor instead. +func (*GetDeviceStatusResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{62} +} + +func (x *GetDeviceStatusResponse) GetValid() bool { + if x != nil { + return x.Valid + } + return false +} + +func (x *GetDeviceStatusResponse) GetAuthorized() bool { + if x != nil { + return x.Authorized + } + return false +} + +func (x *GetDeviceStatusResponse) GetDenied() bool { + if x != nil { + return x.Denied + } + return false +} + +func (x *GetDeviceStatusResponse) GetClientId() string { + if x != nil { + return x.ClientId + } + return "" +} + +type WhoamiRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WhoamiRequest) Reset() { + *x = WhoamiRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[63] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WhoamiRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WhoamiRequest) ProtoMessage() {} + +func (x *WhoamiRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[63] + 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 WhoamiRequest.ProtoReflect.Descriptor instead. +func (*WhoamiRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{63} +} + +type WhoamiResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` + DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WhoamiResponse) Reset() { + *x = WhoamiResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[64] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WhoamiResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WhoamiResponse) ProtoMessage() {} + +func (x *WhoamiResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[64] + 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 WhoamiResponse.ProtoReflect.Descriptor instead. +func (*WhoamiResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{64} +} + +func (x *WhoamiResponse) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *WhoamiResponse) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *WhoamiResponse) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +// 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. +type MyAccount struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Slug string `protobuf:"bytes,2,opt,name=slug,proto3" json:"slug,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MyAccount) Reset() { + *x = MyAccount{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[65] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MyAccount) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MyAccount) ProtoMessage() {} + +func (x *MyAccount) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[65] + 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 MyAccount.ProtoReflect.Descriptor instead. +func (*MyAccount) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{65} +} + +func (x *MyAccount) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *MyAccount) GetSlug() string { + if x != nil { + return x.Slug + } + return "" +} + +func (x *MyAccount) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type ListMyAccountsForCLIRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListMyAccountsForCLIRequest) Reset() { + *x = ListMyAccountsForCLIRequest{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[66] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListMyAccountsForCLIRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListMyAccountsForCLIRequest) ProtoMessage() {} + +func (x *ListMyAccountsForCLIRequest) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[66] + 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 ListMyAccountsForCLIRequest.ProtoReflect.Descriptor instead. +func (*ListMyAccountsForCLIRequest) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{66} +} + +type ListMyAccountsForCLIResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Accounts []*MyAccount `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListMyAccountsForCLIResponse) Reset() { + *x = ListMyAccountsForCLIResponse{} + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[67] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListMyAccountsForCLIResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListMyAccountsForCLIResponse) ProtoMessage() {} + +func (x *ListMyAccountsForCLIResponse) ProtoReflect() protoreflect.Message { + mi := &file_orchestrator_v1_plugin_registry_proto_msgTypes[67] + 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 ListMyAccountsForCLIResponse.ProtoReflect.Descriptor instead. +func (*ListMyAccountsForCLIResponse) Descriptor() ([]byte, []int) { + return file_orchestrator_v1_plugin_registry_proto_rawDescGZIP(), []int{67} +} + +func (x *ListMyAccountsForCLIResponse) GetAccounts() []*MyAccount { + if x != nil { + return x.Accounts + } + return nil +} + +var File_orchestrator_v1_plugin_registry_proto protoreflect.FileDescriptor + +const file_orchestrator_v1_plugin_registry_proto_rawDesc = "" + + "\n" + + "%orchestrator/v1/plugin_registry.proto\x12\x0forchestrator.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\x89\x01\n" + + "\x05Scope\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04slug\x18\x02 \x01(\tR\x04slug\x12!\n" + + "\fdisplay_name\x18\x03 \x01(\tR\vdisplayName\x129\n" + + "\n" + + "created_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"\x90\x04\n" + + "\x06Plugin\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n" + + "\n" + + "scope_slug\x18\x02 \x01(\tR\tscopeSlug\x12\x12\n" + + "\x04name\x18\x03 \x01(\tR\x04name\x12A\n" + + "\n" + + "visibility\x18\x04 \x01(\x0e2!.orchestrator.v1.PluginVisibilityR\n" + + "visibility\x12\x18\n" + + "\apremium\x18\x05 \x01(\bR\apremium\x12 \n" + + "\vdescription\x18\x06 \x01(\tR\vdescription\x12!\n" + + "\fhomepage_url\x18\a \x01(\tR\vhomepageUrl\x12\x1e\n" + + "\n" + + "categories\x18\b \x03(\tR\n" + + "categories\x129\n" + + "\n" + + "updated_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x12\x12\n" + + "\x04kind\x18\n" + + " \x01(\tR\x04kind\x12!\n" + + "\fdisplay_name\x18\v \x01(\tR\vdisplayName\x12(\n" + + "\x10owner_account_id\x18\f \x01(\tR\x0eownerAccountId\x12%\n" + + "\x0elatest_version\x18\r \x01(\tR\rlatestVersion\x12\x12\n" + + "\x04tags\x18\x0e \x03(\tR\x04tags\x12*\n" + + "\x11preview_image_url\x18\x0f \x01(\tR\x0fpreviewImageUrl\"\x82\x01\n" + + "\bCategory\x12\x12\n" + + "\x04slug\x18\x01 \x01(\tR\x04slug\x12!\n" + + "\fdisplay_name\x18\x02 \x01(\tR\vdisplayName\x12 \n" + + "\vdescription\x18\x03 \x01(\tR\vdescription\x12\x1d\n" + + "\n" + + "sort_order\x18\x04 \x01(\x05R\tsortOrder\"\xff\x02\n" + + "\aVersion\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x1b\n" + + "\tplugin_id\x18\x02 \x01(\tR\bpluginId\x12\x18\n" + + "\aversion\x18\x03 \x01(\tR\aversion\x12=\n" + + "\fpublished_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\vpublishedAt\x12\x16\n" + + "\x06yanked\x18\a \x01(\bR\x06yanked\x12%\n" + + "\x0esdk_constraint\x18\b \x01(\tR\rsdkConstraint\x122\n" + + "\x15source_archive_sha256\x18\t \x01(\tR\x13sourceArchiveSha256\x12.\n" + + "\x13source_archive_size\x18\n" + + " \x01(\x03R\x11sourceArchiveSize\x12*\n" + + "\x11preview_image_url\x18\v \x01(\tR\x0fpreviewImageUrl\x12\x1f\n" + + "\vabi_version\x18\f \x01(\rR\n" + + "abiVersion\"i\n" + + "\vRequirement\x12\x1d\n" + + "\n" + + "scope_slug\x18\x01 \x01(\tR\tscopeSlug\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12'\n" + + "\x0fconstraint_expr\x18\x03 \x01(\tR\x0econstraintExpr\"K\n" + + "\x12CreateScopeRequest\x12\x12\n" + + "\x04slug\x18\x01 \x01(\tR\x04slug\x12!\n" + + "\fdisplay_name\x18\x02 \x01(\tR\vdisplayName\"C\n" + + "\x13CreateScopeResponse\x12,\n" + + "\x05scope\x18\x01 \x01(\v2\x16.orchestrator.v1.ScopeR\x05scope\"\x15\n" + + "\x13ListMyScopesRequest\"F\n" + + "\x14ListMyScopesResponse\x12.\n" + + "\x06scopes\x18\x01 \x03(\v2\x16.orchestrator.v1.ScopeR\x06scopes\"%\n" + + "\x0fGetScopeRequest\x12\x12\n" + + "\x04slug\x18\x01 \x01(\tR\x04slug\"s\n" + + "\x10GetScopeResponse\x12,\n" + + "\x05scope\x18\x01 \x01(\v2\x16.orchestrator.v1.ScopeR\x05scope\x121\n" + + "\aplugins\x18\x02 \x03(\v2\x17.orchestrator.v1.PluginR\aplugins\"\x16\n" + + "\x14ListMyPluginsRequest\"J\n" + + "\x15ListMyPluginsResponse\x121\n" + + "\aplugins\x18\x01 \x03(\v2\x17.orchestrator.v1.PluginR\aplugins\"\xb0\x02\n" + + "\x13CreatePluginRequest\x12\x1d\n" + + "\n" + + "scope_slug\x18\x01 \x01(\tR\tscopeSlug\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12 \n" + + "\vdescription\x18\x03 \x01(\tR\vdescription\x12\x12\n" + + "\x04kind\x18\x04 \x01(\tR\x04kind\x12\x1e\n" + + "\n" + + "categories\x18\x05 \x03(\tR\n" + + "categories\x12!\n" + + "\fdisplay_name\x18\x06 \x01(\tR\vdisplayName\x12A\n" + + "\n" + + "visibility\x18\a \x01(\x0e2!.orchestrator.v1.PluginVisibilityR\n" + + "visibility\x12*\n" + + "\x11active_account_id\x18\b \x01(\tR\x0factiveAccountId\"G\n" + + "\x14CreatePluginResponse\x12/\n" + + "\x06plugin\x18\x01 \x01(\v2\x17.orchestrator.v1.PluginR\x06plugin\"q\n" + + "\x10GetPluginRequest\x12\x1d\n" + + "\n" + + "scope_slug\x18\x01 \x01(\tR\tscopeSlug\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12*\n" + + "\x11active_account_id\x18\x03 \x01(\tR\x0factiveAccountId\"\x85\x02\n" + + "\x11GetPluginResponse\x12/\n" + + "\x06plugin\x18\x01 \x01(\v2\x17.orchestrator.v1.PluginR\x06plugin\x124\n" + + "\bversions\x18\x02 \x03(\v2\x18.orchestrator.v1.VersionR\bversions\x12L\n" + + "\bchannels\x18\x03 \x03(\v20.orchestrator.v1.GetPluginResponse.ChannelsEntryR\bchannels\x1a;\n" + + "\rChannelsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xa0\x01\n" + + "\x12ListPluginsRequest\x12\x14\n" + + "\x05limit\x18\x01 \x01(\x05R\x05limit\x12\x16\n" + + "\x06offset\x18\x02 \x01(\x05R\x06offset\x12\x14\n" + + "\x05query\x18\x03 \x01(\tR\x05query\x12\x12\n" + + "\x04kind\x18\x04 \x01(\tR\x04kind\x12\x1e\n" + + "\n" + + "categories\x18\x05 \x03(\tR\n" + + "categories\x12\x12\n" + + "\x04tags\x18\x06 \x03(\tR\x04tags\"H\n" + + "\x13ListPluginsResponse\x121\n" + + "\aplugins\x18\x01 \x03(\v2\x17.orchestrator.v1.PluginR\aplugins\"\x17\n" + + "\x15ListCategoriesRequest\"S\n" + + "\x16ListCategoriesResponse\x129\n" + + "\n" + + "categories\x18\x01 \x03(\v2\x19.orchestrator.v1.CategoryR\n" + + "categories\";\n" + + "\x0fListTagsRequest\x12\x12\n" + + "\x04kind\x18\x01 \x01(\tR\x04kind\x12\x14\n" + + "\x05limit\x18\x02 \x01(\x05R\x05limit\"A\n" + + "\x10ListTagsResponse\x12-\n" + + "\x04tags\x18\x01 \x03(\v2\x19.orchestrator.v1.TagCountR\x04tags\"2\n" + + "\bTagCount\x12\x10\n" + + "\x03tag\x18\x01 \x01(\tR\x03tag\x12\x14\n" + + "\x05count\x18\x02 \x01(\x05R\x05count\":\n" + + "\x19ListPrivatePluginsRequest\x12\x1d\n" + + "\n" + + "account_id\x18\x01 \x01(\tR\taccountId\"]\n" + + "\x1aListPrivatePluginsResponse\x12?\n" + + "\aplugins\x18\x01 \x03(\v2%.orchestrator.v1.PrivatePluginSummaryR\aplugins\"\xa4\x02\n" + + "\x14PrivatePluginSummary\x12/\n" + + "\x06plugin\x18\x01 \x01(\v2\x17.orchestrator.v1.PluginR\x06plugin\x12e\n" + + "\x10channel_versions\x18\x02 \x03(\v2:.orchestrator.v1.PrivatePluginSummary.ChannelVersionsEntryR\x0fchannelVersions\x120\n" + + "\x14installed_site_count\x18\x03 \x01(\x05R\x12installedSiteCount\x1aB\n" + + "\x14ChannelVersionsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\\\n" + + "\x1aDeletePrivatePluginRequest\x12\x1d\n" + + "\n" + + "account_id\x18\x01 \x01(\tR\taccountId\x12\x1f\n" + + "\vplugin_name\x18\x02 \x01(\tR\n" + + "pluginName\"\x1d\n" + + "\x1bDeletePrivatePluginResponse\"}\n" + + "!DeletePrivatePluginVersionRequest\x12\x1d\n" + + "\n" + + "account_id\x18\x01 \x01(\tR\taccountId\x12\x1f\n" + + "\vplugin_name\x18\x02 \x01(\tR\n" + + "pluginName\x12\x18\n" + + "\aversion\x18\x03 \x01(\tR\aversion\"$\n" + + "\"DeletePrivatePluginVersionResponse\"f\n" + + "$ListPrivatePluginInstallSitesRequest\x12\x1d\n" + + "\n" + + "account_id\x18\x01 \x01(\tR\taccountId\x12\x1f\n" + + "\vplugin_name\x18\x02 \x01(\tR\n" + + "pluginName\"h\n" + + "%ListPrivatePluginInstallSitesResponse\x12?\n" + + "\x05sites\x18\x01 \x03(\v2).orchestrator.v1.PrivatePluginInstallSiteR\x05sites\"\xbb\x01\n" + + "\x18PrivatePluginInstallSite\x12\x1f\n" + + "\vinstance_id\x18\x01 \x01(\tR\n" + + "instanceId\x12*\n" + + "\x11site_display_name\x18\x02 \x01(\tR\x0fsiteDisplayName\x12+\n" + + "\x11installed_version\x18\x03 \x01(\tR\x10installedVersion\x12%\n" + + "\x0epinned_channel\x18\x04 \x01(\tR\rpinnedChannel\"m\n" + + "\x11GetVersionRequest\x12\x1d\n" + + "\n" + + "scope_slug\x18\x01 \x01(\tR\tscopeSlug\x12\x1f\n" + + "\vplugin_name\x18\x02 \x01(\tR\n" + + "pluginName\x12\x18\n" + + "\aversion\x18\x03 \x01(\tR\aversion\"\xc2\x01\n" + + "\x12GetVersionResponse\x122\n" + + "\aversion\x18\x01 \x01(\v2\x18.orchestrator.v1.VersionR\aversion\x128\n" + + "\brequires\x18\x02 \x03(\v2\x1c.orchestrator.v1.RequirementR\brequires\x12\x1b\n" + + "\treadme_md\x18\x03 \x01(\tR\breadmeMd\x12!\n" + + "\fchangelog_md\x18\x04 \x01(\tR\vchangelogMd\"\xb1\x01\n" + + "\x15ResolveInstallRequest\x12\x1d\n" + + "\n" + + "scope_slug\x18\x01 \x01(\tR\tscopeSlug\x12\x1f\n" + + "\vplugin_name\x18\x02 \x01(\tR\n" + + "pluginName\x12,\n" + + "\x12version_or_channel\x18\x03 \x01(\tR\x10versionOrChannel\x12*\n" + + "\x11active_account_id\x18\x04 \x01(\tR\x0factiveAccountId\"\xb6\x03\n" + + "\x16ResolveInstallResponse\x12\x1d\n" + + "\n" + + "version_id\x18\x01 \x01(\tR\tversionId\x12\x1d\n" + + "\n" + + "scope_slug\x18\x02 \x01(\tR\tscopeSlug\x12\x1f\n" + + "\vplugin_name\x18\x03 \x01(\tR\n" + + "pluginName\x12\x18\n" + + "\aversion\x18\x04 \x01(\tR\aversion\x12%\n" + + "\x0echannel_pinned\x18\x05 \x01(\tR\rchannelPinned\x12\x1f\n" + + "\varchive_url\x18\x06 \x01(\tR\n" + + "archiveUrl\x12%\n" + + "\x0earchive_sha256\x18\a \x01(\tR\rarchiveSha256\x12%\n" + + "\x0esdk_constraint\x18\b \x01(\tR\rsdkConstraint\x128\n" + + "\brequires\x18\t \x03(\v2\x1c.orchestrator.v1.RequirementR\brequires\x12\x16\n" + + "\x06yanked\x18\n" + + " \x01(\bR\x06yanked\x12\x1a\n" + + "\bwarnings\x18\v \x03(\tR\bwarnings\x12\x1f\n" + + "\vabi_version\x18\f \x01(\rR\n" + + "abiVersion\"G\n" + + "$ListPrivatePluginsForInstanceRequest\x12\x1f\n" + + "\vinstance_id\x18\x01 \x01(\tR\n" + + "instanceId\"\x92\x01\n" + + " ResolveInstallForInstanceRequest\x12\x1f\n" + + "\vinstance_id\x18\x01 \x01(\tR\n" + + "instanceId\x12\x1f\n" + + "\vplugin_name\x18\x02 \x01(\tR\n" + + "pluginName\x12,\n" + + "\x12version_or_channel\x18\x03 \x01(\tR\x10versionOrChannel\"\xa9\x02\n" + + "\rPendingReview\x12\x1b\n" + + "\treview_id\x18\x01 \x01(\tR\breviewId\x12\x1b\n" + + "\tplugin_id\x18\x02 \x01(\tR\bpluginId\x12\x1d\n" + + "\n" + + "scope_slug\x18\x03 \x01(\tR\tscopeSlug\x12\x1f\n" + + "\vplugin_name\x18\x04 \x01(\tR\n" + + "pluginName\x12.\n" + + "\x13plugin_display_name\x18\x05 \x01(\tR\x11pluginDisplayName\x12/\n" + + "\x14submitted_by_user_id\x18\x06 \x01(\tR\x11submittedByUserId\x12=\n" + + "\fsubmitted_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\vsubmittedAt\"5\n" + + "\x16SubmitForReviewRequest\x12\x1b\n" + + "\tplugin_id\x18\x01 \x01(\tR\bpluginId\"9\n" + + "\x17SubmitForReviewResponse\x12\x1e\n" + + "\n" + + "visibility\x18\x01 \x01(\tR\n" + + "visibility\"I\n" + + "\x19ListPendingReviewsRequest\x12\x14\n" + + "\x05limit\x18\x01 \x01(\x05R\x05limit\x12\x16\n" + + "\x06offset\x18\x02 \x01(\x05R\x06offset\"V\n" + + "\x1aListPendingReviewsResponse\x128\n" + + "\areviews\x18\x01 \x03(\v2\x1e.orchestrator.v1.PendingReviewR\areviews\"O\n" + + "\x18ApproveSubmissionRequest\x12\x1b\n" + + "\tplugin_id\x18\x01 \x01(\tR\bpluginId\x12\x16\n" + + "\x06reason\x18\x02 \x01(\tR\x06reason\"\x1b\n" + + "\x19ApproveSubmissionResponse\"N\n" + + "\x17RejectSubmissionRequest\x12\x1b\n" + + "\tplugin_id\x18\x01 \x01(\tR\bpluginId\x12\x16\n" + + "\x06reason\x18\x02 \x01(\tR\x06reason\"\x1a\n" + + "\x18RejectSubmissionResponse\"L\n" + + "\x15RequestChangesRequest\x12\x1b\n" + + "\tplugin_id\x18\x01 \x01(\tR\bpluginId\x12\x16\n" + + "\x06reason\x18\x02 \x01(\tR\x06reason\"\x18\n" + + "\x16RequestChangesResponse\"\xc2\x01\n" + + "\x15PublishVersionRequest\x12\x1b\n" + + "\tplugin_id\x18\x01 \x01(\tR\bpluginId\x12\x18\n" + + "\aversion\x18\x02 \x01(\tR\aversion\x12\x18\n" + + "\achannel\x18\x03 \x01(\tR\achannel\x12\x18\n" + + "\aarchive\x18\x04 \x01(\fR\aarchive\x12\x1b\n" + + "\treadme_md\x18\x05 \x01(\tR\breadmeMd\x12!\n" + + "\fchangelog_md\x18\x06 \x01(\tR\vchangelogMd\"\x89\x01\n" + + "\x16PublishVersionResponse\x122\n" + + "\aversion\x18\x01 \x01(\v2\x18.orchestrator.v1.VersionR\aversion\x12\x1f\n" + + "\varchive_url\x18\x02 \x01(\tR\n" + + "archiveUrl\x12\x1a\n" + + "\bwarnings\x18\x03 \x03(\tR\bwarnings\",\n" + + "\x12StartDeviceRequest\x12\x16\n" + + "\x06scopes\x18\x01 \x03(\tR\x06scopes\"\xd7\x01\n" + + "\x13StartDeviceResponse\x12\x1f\n" + + "\vdevice_code\x18\x01 \x01(\tR\n" + + "deviceCode\x12\x1b\n" + + "\tuser_code\x18\x02 \x01(\tR\buserCode\x12)\n" + + "\x10verification_uri\x18\x03 \x01(\tR\x0fverificationUri\x12)\n" + + "\x10interval_seconds\x18\x04 \x01(\x05R\x0fintervalSeconds\x12,\n" + + "\x12expires_in_seconds\x18\x05 \x01(\x05R\x10expiresInSeconds\"4\n" + + "\x11PollDeviceRequest\x12\x1f\n" + + "\vdevice_code\x18\x01 \x01(\tR\n" + + "deviceCode\"O\n" + + "\x12PollDeviceResponse\x12!\n" + + "\faccess_token\x18\x01 \x01(\tR\vaccessToken\x12\x16\n" + + "\x06status\x18\x02 \x01(\tR\x06status\"3\n" + + "\x14ApproveDeviceRequest\x12\x1b\n" + + "\tuser_code\x18\x01 \x01(\tR\buserCode\"\x17\n" + + "\x15ApproveDeviceResponse\"0\n" + + "\x11DenyDeviceRequest\x12\x1b\n" + + "\tuser_code\x18\x01 \x01(\tR\buserCode\"\x14\n" + + "\x12DenyDeviceResponse\"5\n" + + "\x16GetDeviceStatusRequest\x12\x1b\n" + + "\tuser_code\x18\x01 \x01(\tR\buserCode\"\x84\x01\n" + + "\x17GetDeviceStatusResponse\x12\x14\n" + + "\x05valid\x18\x01 \x01(\bR\x05valid\x12\x1e\n" + + "\n" + + "authorized\x18\x02 \x01(\bR\n" + + "authorized\x12\x16\n" + + "\x06denied\x18\x03 \x01(\bR\x06denied\x12\x1b\n" + + "\tclient_id\x18\x04 \x01(\tR\bclientId\"\x0f\n" + + "\rWhoamiRequest\"b\n" + + "\x0eWhoamiResponse\x12\x17\n" + + "\auser_id\x18\x01 \x01(\tR\x06userId\x12\x14\n" + + "\x05email\x18\x02 \x01(\tR\x05email\x12!\n" + + "\fdisplay_name\x18\x03 \x01(\tR\vdisplayName\"C\n" + + "\tMyAccount\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04slug\x18\x02 \x01(\tR\x04slug\x12\x12\n" + + "\x04name\x18\x03 \x01(\tR\x04name\"\x1d\n" + + "\x1bListMyAccountsForCLIRequest\"V\n" + + "\x1cListMyAccountsForCLIResponse\x126\n" + + "\baccounts\x18\x01 \x03(\v2\x1a.orchestrator.v1.MyAccountR\baccounts*\xd8\x01\n" + + "\x10PluginVisibility\x12!\n" + + "\x1dPLUGIN_VISIBILITY_UNSPECIFIED\x10\x00\x12\x1d\n" + + "\x19PLUGIN_VISIBILITY_PRIVATE\x10\x01\x12\"\n" + + "\x1ePLUGIN_VISIBILITY_UNDER_REVIEW\x10\x02\x12\x1c\n" + + "\x18PLUGIN_VISIBILITY_PUBLIC\x10\x03\x12\x1e\n" + + "\x1aPLUGIN_VISIBILITY_REJECTED\x10\x04\x12 \n" + + "\x1cPLUGIN_VISIBILITY_TAKEN_DOWN\x10\x052\xfc\x02\n" + + "\x12PluginScopeService\x12X\n" + + "\vCreateScope\x12#.orchestrator.v1.CreateScopeRequest\x1a$.orchestrator.v1.CreateScopeResponse\x12[\n" + + "\fListMyScopes\x12$.orchestrator.v1.ListMyScopesRequest\x1a%.orchestrator.v1.ListMyScopesResponse\x12O\n" + + "\bGetScope\x12 .orchestrator.v1.GetScopeRequest\x1a!.orchestrator.v1.GetScopeResponse\x12^\n" + + "\rListMyPlugins\x12%.orchestrator.v1.ListMyPluginsRequest\x1a&.orchestrator.v1.ListMyPluginsResponse2\xef\v\n" + + "\x15PluginRegistryService\x12[\n" + + "\fCreatePlugin\x12$.orchestrator.v1.CreatePluginRequest\x1a%.orchestrator.v1.CreatePluginResponse\x12R\n" + + "\tGetPlugin\x12!.orchestrator.v1.GetPluginRequest\x1a\".orchestrator.v1.GetPluginResponse\x12X\n" + + "\vListPlugins\x12#.orchestrator.v1.ListPluginsRequest\x1a$.orchestrator.v1.ListPluginsResponse\x12U\n" + + "\n" + + "GetVersion\x12\".orchestrator.v1.GetVersionRequest\x1a#.orchestrator.v1.GetVersionResponse\x12a\n" + + "\x0eResolveInstall\x12&.orchestrator.v1.ResolveInstallRequest\x1a'.orchestrator.v1.ResolveInstallResponse\x12a\n" + + "\x0eListCategories\x12&.orchestrator.v1.ListCategoriesRequest\x1a'.orchestrator.v1.ListCategoriesResponse\x12O\n" + + "\bListTags\x12 .orchestrator.v1.ListTagsRequest\x1a!.orchestrator.v1.ListTagsResponse\x12d\n" + + "\x0fSubmitForReview\x12'.orchestrator.v1.SubmitForReviewRequest\x1a(.orchestrator.v1.SubmitForReviewResponse\x12m\n" + + "\x12ListPrivatePlugins\x12*.orchestrator.v1.ListPrivatePluginsRequest\x1a+.orchestrator.v1.ListPrivatePluginsResponse\x12p\n" + + "\x13DeletePrivatePlugin\x12+.orchestrator.v1.DeletePrivatePluginRequest\x1a,.orchestrator.v1.DeletePrivatePluginResponse\x12\x85\x01\n" + + "\x1aDeletePrivatePluginVersion\x122.orchestrator.v1.DeletePrivatePluginVersionRequest\x1a3.orchestrator.v1.DeletePrivatePluginVersionResponse\x12\x8e\x01\n" + + "\x1dListPrivatePluginInstallSites\x125.orchestrator.v1.ListPrivatePluginInstallSitesRequest\x1a6.orchestrator.v1.ListPrivatePluginInstallSitesResponse\x12\x83\x01\n" + + "\x1dListPrivatePluginsForInstance\x125.orchestrator.v1.ListPrivatePluginsForInstanceRequest\x1a+.orchestrator.v1.ListPrivatePluginsResponse\x12w\n" + + "\x19ResolveInstallForInstance\x121.orchestrator.v1.ResolveInstallForInstanceRequest\x1a'.orchestrator.v1.ResolveInstallResponse2\xc0\x03\n" + + "\x17PluginModerationService\x12m\n" + + "\x12ListPendingReviews\x12*.orchestrator.v1.ListPendingReviewsRequest\x1a+.orchestrator.v1.ListPendingReviewsResponse\x12j\n" + + "\x11ApproveSubmission\x12).orchestrator.v1.ApproveSubmissionRequest\x1a*.orchestrator.v1.ApproveSubmissionResponse\x12g\n" + + "\x10RejectSubmission\x12(.orchestrator.v1.RejectSubmissionRequest\x1a).orchestrator.v1.RejectSubmissionResponse\x12a\n" + + "\x0eRequestChanges\x12&.orchestrator.v1.RequestChangesRequest\x1a'.orchestrator.v1.RequestChangesResponse2y\n" + + "\x14PluginPublishService\x12a\n" + + "\x0ePublishVersion\x12&.orchestrator.v1.PublishVersionRequest\x1a'.orchestrator.v1.PublishVersionResponse2\xa1\x05\n" + + "\x11PluginAuthService\x12X\n" + + "\vStartDevice\x12#.orchestrator.v1.StartDeviceRequest\x1a$.orchestrator.v1.StartDeviceResponse\x12U\n" + + "\n" + + "PollDevice\x12\".orchestrator.v1.PollDeviceRequest\x1a#.orchestrator.v1.PollDeviceResponse\x12^\n" + + "\rApproveDevice\x12%.orchestrator.v1.ApproveDeviceRequest\x1a&.orchestrator.v1.ApproveDeviceResponse\x12U\n" + + "\n" + + "DenyDevice\x12\".orchestrator.v1.DenyDeviceRequest\x1a#.orchestrator.v1.DenyDeviceResponse\x12d\n" + + "\x0fGetDeviceStatus\x12'.orchestrator.v1.GetDeviceStatusRequest\x1a(.orchestrator.v1.GetDeviceStatusResponse\x12I\n" + + "\x06Whoami\x12\x1e.orchestrator.v1.WhoamiRequest\x1a\x1f.orchestrator.v1.WhoamiResponse\x12s\n" + + "\x14ListMyAccountsForCLI\x12,.orchestrator.v1.ListMyAccountsForCLIRequest\x1a-.orchestrator.v1.ListMyAccountsForCLIResponseB\xd5\x01\n" + + "\x13com.orchestrator.v1B\x13PluginRegistryProtoP\x01ZLgit.dev.alexdunmow.com/block/cli/internal/api/orchestrator/v1;orchestratorv1\xa2\x02\x03OXX\xaa\x02\x0fOrchestrator.V1\xca\x02\x0fOrchestrator\\V1\xe2\x02\x1bOrchestrator\\V1\\GPBMetadata\xea\x02\x10Orchestrator::V1b\x06proto3" + +var ( + file_orchestrator_v1_plugin_registry_proto_rawDescOnce sync.Once + file_orchestrator_v1_plugin_registry_proto_rawDescData []byte +) + +func file_orchestrator_v1_plugin_registry_proto_rawDescGZIP() []byte { + file_orchestrator_v1_plugin_registry_proto_rawDescOnce.Do(func() { + file_orchestrator_v1_plugin_registry_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_orchestrator_v1_plugin_registry_proto_rawDesc), len(file_orchestrator_v1_plugin_registry_proto_rawDesc))) + }) + return file_orchestrator_v1_plugin_registry_proto_rawDescData +} + +var file_orchestrator_v1_plugin_registry_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_orchestrator_v1_plugin_registry_proto_msgTypes = make([]protoimpl.MessageInfo, 70) +var file_orchestrator_v1_plugin_registry_proto_goTypes = []any{ + (PluginVisibility)(0), // 0: orchestrator.v1.PluginVisibility + (*Scope)(nil), // 1: orchestrator.v1.Scope + (*Plugin)(nil), // 2: orchestrator.v1.Plugin + (*Category)(nil), // 3: orchestrator.v1.Category + (*Version)(nil), // 4: orchestrator.v1.Version + (*Requirement)(nil), // 5: orchestrator.v1.Requirement + (*CreateScopeRequest)(nil), // 6: orchestrator.v1.CreateScopeRequest + (*CreateScopeResponse)(nil), // 7: orchestrator.v1.CreateScopeResponse + (*ListMyScopesRequest)(nil), // 8: orchestrator.v1.ListMyScopesRequest + (*ListMyScopesResponse)(nil), // 9: orchestrator.v1.ListMyScopesResponse + (*GetScopeRequest)(nil), // 10: orchestrator.v1.GetScopeRequest + (*GetScopeResponse)(nil), // 11: orchestrator.v1.GetScopeResponse + (*ListMyPluginsRequest)(nil), // 12: orchestrator.v1.ListMyPluginsRequest + (*ListMyPluginsResponse)(nil), // 13: orchestrator.v1.ListMyPluginsResponse + (*CreatePluginRequest)(nil), // 14: orchestrator.v1.CreatePluginRequest + (*CreatePluginResponse)(nil), // 15: orchestrator.v1.CreatePluginResponse + (*GetPluginRequest)(nil), // 16: orchestrator.v1.GetPluginRequest + (*GetPluginResponse)(nil), // 17: orchestrator.v1.GetPluginResponse + (*ListPluginsRequest)(nil), // 18: orchestrator.v1.ListPluginsRequest + (*ListPluginsResponse)(nil), // 19: orchestrator.v1.ListPluginsResponse + (*ListCategoriesRequest)(nil), // 20: orchestrator.v1.ListCategoriesRequest + (*ListCategoriesResponse)(nil), // 21: orchestrator.v1.ListCategoriesResponse + (*ListTagsRequest)(nil), // 22: orchestrator.v1.ListTagsRequest + (*ListTagsResponse)(nil), // 23: orchestrator.v1.ListTagsResponse + (*TagCount)(nil), // 24: orchestrator.v1.TagCount + (*ListPrivatePluginsRequest)(nil), // 25: orchestrator.v1.ListPrivatePluginsRequest + (*ListPrivatePluginsResponse)(nil), // 26: orchestrator.v1.ListPrivatePluginsResponse + (*PrivatePluginSummary)(nil), // 27: orchestrator.v1.PrivatePluginSummary + (*DeletePrivatePluginRequest)(nil), // 28: orchestrator.v1.DeletePrivatePluginRequest + (*DeletePrivatePluginResponse)(nil), // 29: orchestrator.v1.DeletePrivatePluginResponse + (*DeletePrivatePluginVersionRequest)(nil), // 30: orchestrator.v1.DeletePrivatePluginVersionRequest + (*DeletePrivatePluginVersionResponse)(nil), // 31: orchestrator.v1.DeletePrivatePluginVersionResponse + (*ListPrivatePluginInstallSitesRequest)(nil), // 32: orchestrator.v1.ListPrivatePluginInstallSitesRequest + (*ListPrivatePluginInstallSitesResponse)(nil), // 33: orchestrator.v1.ListPrivatePluginInstallSitesResponse + (*PrivatePluginInstallSite)(nil), // 34: orchestrator.v1.PrivatePluginInstallSite + (*GetVersionRequest)(nil), // 35: orchestrator.v1.GetVersionRequest + (*GetVersionResponse)(nil), // 36: orchestrator.v1.GetVersionResponse + (*ResolveInstallRequest)(nil), // 37: orchestrator.v1.ResolveInstallRequest + (*ResolveInstallResponse)(nil), // 38: orchestrator.v1.ResolveInstallResponse + (*ListPrivatePluginsForInstanceRequest)(nil), // 39: orchestrator.v1.ListPrivatePluginsForInstanceRequest + (*ResolveInstallForInstanceRequest)(nil), // 40: orchestrator.v1.ResolveInstallForInstanceRequest + (*PendingReview)(nil), // 41: orchestrator.v1.PendingReview + (*SubmitForReviewRequest)(nil), // 42: orchestrator.v1.SubmitForReviewRequest + (*SubmitForReviewResponse)(nil), // 43: orchestrator.v1.SubmitForReviewResponse + (*ListPendingReviewsRequest)(nil), // 44: orchestrator.v1.ListPendingReviewsRequest + (*ListPendingReviewsResponse)(nil), // 45: orchestrator.v1.ListPendingReviewsResponse + (*ApproveSubmissionRequest)(nil), // 46: orchestrator.v1.ApproveSubmissionRequest + (*ApproveSubmissionResponse)(nil), // 47: orchestrator.v1.ApproveSubmissionResponse + (*RejectSubmissionRequest)(nil), // 48: orchestrator.v1.RejectSubmissionRequest + (*RejectSubmissionResponse)(nil), // 49: orchestrator.v1.RejectSubmissionResponse + (*RequestChangesRequest)(nil), // 50: orchestrator.v1.RequestChangesRequest + (*RequestChangesResponse)(nil), // 51: orchestrator.v1.RequestChangesResponse + (*PublishVersionRequest)(nil), // 52: orchestrator.v1.PublishVersionRequest + (*PublishVersionResponse)(nil), // 53: orchestrator.v1.PublishVersionResponse + (*StartDeviceRequest)(nil), // 54: orchestrator.v1.StartDeviceRequest + (*StartDeviceResponse)(nil), // 55: orchestrator.v1.StartDeviceResponse + (*PollDeviceRequest)(nil), // 56: orchestrator.v1.PollDeviceRequest + (*PollDeviceResponse)(nil), // 57: orchestrator.v1.PollDeviceResponse + (*ApproveDeviceRequest)(nil), // 58: orchestrator.v1.ApproveDeviceRequest + (*ApproveDeviceResponse)(nil), // 59: orchestrator.v1.ApproveDeviceResponse + (*DenyDeviceRequest)(nil), // 60: orchestrator.v1.DenyDeviceRequest + (*DenyDeviceResponse)(nil), // 61: orchestrator.v1.DenyDeviceResponse + (*GetDeviceStatusRequest)(nil), // 62: orchestrator.v1.GetDeviceStatusRequest + (*GetDeviceStatusResponse)(nil), // 63: orchestrator.v1.GetDeviceStatusResponse + (*WhoamiRequest)(nil), // 64: orchestrator.v1.WhoamiRequest + (*WhoamiResponse)(nil), // 65: orchestrator.v1.WhoamiResponse + (*MyAccount)(nil), // 66: orchestrator.v1.MyAccount + (*ListMyAccountsForCLIRequest)(nil), // 67: orchestrator.v1.ListMyAccountsForCLIRequest + (*ListMyAccountsForCLIResponse)(nil), // 68: orchestrator.v1.ListMyAccountsForCLIResponse + nil, // 69: orchestrator.v1.GetPluginResponse.ChannelsEntry + nil, // 70: orchestrator.v1.PrivatePluginSummary.ChannelVersionsEntry + (*timestamppb.Timestamp)(nil), // 71: google.protobuf.Timestamp +} +var file_orchestrator_v1_plugin_registry_proto_depIdxs = []int32{ + 71, // 0: orchestrator.v1.Scope.created_at:type_name -> google.protobuf.Timestamp + 0, // 1: orchestrator.v1.Plugin.visibility:type_name -> orchestrator.v1.PluginVisibility + 71, // 2: orchestrator.v1.Plugin.updated_at:type_name -> google.protobuf.Timestamp + 71, // 3: orchestrator.v1.Version.published_at:type_name -> google.protobuf.Timestamp + 1, // 4: orchestrator.v1.CreateScopeResponse.scope:type_name -> orchestrator.v1.Scope + 1, // 5: orchestrator.v1.ListMyScopesResponse.scopes:type_name -> orchestrator.v1.Scope + 1, // 6: orchestrator.v1.GetScopeResponse.scope:type_name -> orchestrator.v1.Scope + 2, // 7: orchestrator.v1.GetScopeResponse.plugins:type_name -> orchestrator.v1.Plugin + 2, // 8: orchestrator.v1.ListMyPluginsResponse.plugins:type_name -> orchestrator.v1.Plugin + 0, // 9: orchestrator.v1.CreatePluginRequest.visibility:type_name -> orchestrator.v1.PluginVisibility + 2, // 10: orchestrator.v1.CreatePluginResponse.plugin:type_name -> orchestrator.v1.Plugin + 2, // 11: orchestrator.v1.GetPluginResponse.plugin:type_name -> orchestrator.v1.Plugin + 4, // 12: orchestrator.v1.GetPluginResponse.versions:type_name -> orchestrator.v1.Version + 69, // 13: orchestrator.v1.GetPluginResponse.channels:type_name -> orchestrator.v1.GetPluginResponse.ChannelsEntry + 2, // 14: orchestrator.v1.ListPluginsResponse.plugins:type_name -> orchestrator.v1.Plugin + 3, // 15: orchestrator.v1.ListCategoriesResponse.categories:type_name -> orchestrator.v1.Category + 24, // 16: orchestrator.v1.ListTagsResponse.tags:type_name -> orchestrator.v1.TagCount + 27, // 17: orchestrator.v1.ListPrivatePluginsResponse.plugins:type_name -> orchestrator.v1.PrivatePluginSummary + 2, // 18: orchestrator.v1.PrivatePluginSummary.plugin:type_name -> orchestrator.v1.Plugin + 70, // 19: orchestrator.v1.PrivatePluginSummary.channel_versions:type_name -> orchestrator.v1.PrivatePluginSummary.ChannelVersionsEntry + 34, // 20: orchestrator.v1.ListPrivatePluginInstallSitesResponse.sites:type_name -> orchestrator.v1.PrivatePluginInstallSite + 4, // 21: orchestrator.v1.GetVersionResponse.version:type_name -> orchestrator.v1.Version + 5, // 22: orchestrator.v1.GetVersionResponse.requires:type_name -> orchestrator.v1.Requirement + 5, // 23: orchestrator.v1.ResolveInstallResponse.requires:type_name -> orchestrator.v1.Requirement + 71, // 24: orchestrator.v1.PendingReview.submitted_at:type_name -> google.protobuf.Timestamp + 41, // 25: orchestrator.v1.ListPendingReviewsResponse.reviews:type_name -> orchestrator.v1.PendingReview + 4, // 26: orchestrator.v1.PublishVersionResponse.version:type_name -> orchestrator.v1.Version + 66, // 27: orchestrator.v1.ListMyAccountsForCLIResponse.accounts:type_name -> orchestrator.v1.MyAccount + 6, // 28: orchestrator.v1.PluginScopeService.CreateScope:input_type -> orchestrator.v1.CreateScopeRequest + 8, // 29: orchestrator.v1.PluginScopeService.ListMyScopes:input_type -> orchestrator.v1.ListMyScopesRequest + 10, // 30: orchestrator.v1.PluginScopeService.GetScope:input_type -> orchestrator.v1.GetScopeRequest + 12, // 31: orchestrator.v1.PluginScopeService.ListMyPlugins:input_type -> orchestrator.v1.ListMyPluginsRequest + 14, // 32: orchestrator.v1.PluginRegistryService.CreatePlugin:input_type -> orchestrator.v1.CreatePluginRequest + 16, // 33: orchestrator.v1.PluginRegistryService.GetPlugin:input_type -> orchestrator.v1.GetPluginRequest + 18, // 34: orchestrator.v1.PluginRegistryService.ListPlugins:input_type -> orchestrator.v1.ListPluginsRequest + 35, // 35: orchestrator.v1.PluginRegistryService.GetVersion:input_type -> orchestrator.v1.GetVersionRequest + 37, // 36: orchestrator.v1.PluginRegistryService.ResolveInstall:input_type -> orchestrator.v1.ResolveInstallRequest + 20, // 37: orchestrator.v1.PluginRegistryService.ListCategories:input_type -> orchestrator.v1.ListCategoriesRequest + 22, // 38: orchestrator.v1.PluginRegistryService.ListTags:input_type -> orchestrator.v1.ListTagsRequest + 42, // 39: orchestrator.v1.PluginRegistryService.SubmitForReview:input_type -> orchestrator.v1.SubmitForReviewRequest + 25, // 40: orchestrator.v1.PluginRegistryService.ListPrivatePlugins:input_type -> orchestrator.v1.ListPrivatePluginsRequest + 28, // 41: orchestrator.v1.PluginRegistryService.DeletePrivatePlugin:input_type -> orchestrator.v1.DeletePrivatePluginRequest + 30, // 42: orchestrator.v1.PluginRegistryService.DeletePrivatePluginVersion:input_type -> orchestrator.v1.DeletePrivatePluginVersionRequest + 32, // 43: orchestrator.v1.PluginRegistryService.ListPrivatePluginInstallSites:input_type -> orchestrator.v1.ListPrivatePluginInstallSitesRequest + 39, // 44: orchestrator.v1.PluginRegistryService.ListPrivatePluginsForInstance:input_type -> orchestrator.v1.ListPrivatePluginsForInstanceRequest + 40, // 45: orchestrator.v1.PluginRegistryService.ResolveInstallForInstance:input_type -> orchestrator.v1.ResolveInstallForInstanceRequest + 44, // 46: orchestrator.v1.PluginModerationService.ListPendingReviews:input_type -> orchestrator.v1.ListPendingReviewsRequest + 46, // 47: orchestrator.v1.PluginModerationService.ApproveSubmission:input_type -> orchestrator.v1.ApproveSubmissionRequest + 48, // 48: orchestrator.v1.PluginModerationService.RejectSubmission:input_type -> orchestrator.v1.RejectSubmissionRequest + 50, // 49: orchestrator.v1.PluginModerationService.RequestChanges:input_type -> orchestrator.v1.RequestChangesRequest + 52, // 50: orchestrator.v1.PluginPublishService.PublishVersion:input_type -> orchestrator.v1.PublishVersionRequest + 54, // 51: orchestrator.v1.PluginAuthService.StartDevice:input_type -> orchestrator.v1.StartDeviceRequest + 56, // 52: orchestrator.v1.PluginAuthService.PollDevice:input_type -> orchestrator.v1.PollDeviceRequest + 58, // 53: orchestrator.v1.PluginAuthService.ApproveDevice:input_type -> orchestrator.v1.ApproveDeviceRequest + 60, // 54: orchestrator.v1.PluginAuthService.DenyDevice:input_type -> orchestrator.v1.DenyDeviceRequest + 62, // 55: orchestrator.v1.PluginAuthService.GetDeviceStatus:input_type -> orchestrator.v1.GetDeviceStatusRequest + 64, // 56: orchestrator.v1.PluginAuthService.Whoami:input_type -> orchestrator.v1.WhoamiRequest + 67, // 57: orchestrator.v1.PluginAuthService.ListMyAccountsForCLI:input_type -> orchestrator.v1.ListMyAccountsForCLIRequest + 7, // 58: orchestrator.v1.PluginScopeService.CreateScope:output_type -> orchestrator.v1.CreateScopeResponse + 9, // 59: orchestrator.v1.PluginScopeService.ListMyScopes:output_type -> orchestrator.v1.ListMyScopesResponse + 11, // 60: orchestrator.v1.PluginScopeService.GetScope:output_type -> orchestrator.v1.GetScopeResponse + 13, // 61: orchestrator.v1.PluginScopeService.ListMyPlugins:output_type -> orchestrator.v1.ListMyPluginsResponse + 15, // 62: orchestrator.v1.PluginRegistryService.CreatePlugin:output_type -> orchestrator.v1.CreatePluginResponse + 17, // 63: orchestrator.v1.PluginRegistryService.GetPlugin:output_type -> orchestrator.v1.GetPluginResponse + 19, // 64: orchestrator.v1.PluginRegistryService.ListPlugins:output_type -> orchestrator.v1.ListPluginsResponse + 36, // 65: orchestrator.v1.PluginRegistryService.GetVersion:output_type -> orchestrator.v1.GetVersionResponse + 38, // 66: orchestrator.v1.PluginRegistryService.ResolveInstall:output_type -> orchestrator.v1.ResolveInstallResponse + 21, // 67: orchestrator.v1.PluginRegistryService.ListCategories:output_type -> orchestrator.v1.ListCategoriesResponse + 23, // 68: orchestrator.v1.PluginRegistryService.ListTags:output_type -> orchestrator.v1.ListTagsResponse + 43, // 69: orchestrator.v1.PluginRegistryService.SubmitForReview:output_type -> orchestrator.v1.SubmitForReviewResponse + 26, // 70: orchestrator.v1.PluginRegistryService.ListPrivatePlugins:output_type -> orchestrator.v1.ListPrivatePluginsResponse + 29, // 71: orchestrator.v1.PluginRegistryService.DeletePrivatePlugin:output_type -> orchestrator.v1.DeletePrivatePluginResponse + 31, // 72: orchestrator.v1.PluginRegistryService.DeletePrivatePluginVersion:output_type -> orchestrator.v1.DeletePrivatePluginVersionResponse + 33, // 73: orchestrator.v1.PluginRegistryService.ListPrivatePluginInstallSites:output_type -> orchestrator.v1.ListPrivatePluginInstallSitesResponse + 26, // 74: orchestrator.v1.PluginRegistryService.ListPrivatePluginsForInstance:output_type -> orchestrator.v1.ListPrivatePluginsResponse + 38, // 75: orchestrator.v1.PluginRegistryService.ResolveInstallForInstance:output_type -> orchestrator.v1.ResolveInstallResponse + 45, // 76: orchestrator.v1.PluginModerationService.ListPendingReviews:output_type -> orchestrator.v1.ListPendingReviewsResponse + 47, // 77: orchestrator.v1.PluginModerationService.ApproveSubmission:output_type -> orchestrator.v1.ApproveSubmissionResponse + 49, // 78: orchestrator.v1.PluginModerationService.RejectSubmission:output_type -> orchestrator.v1.RejectSubmissionResponse + 51, // 79: orchestrator.v1.PluginModerationService.RequestChanges:output_type -> orchestrator.v1.RequestChangesResponse + 53, // 80: orchestrator.v1.PluginPublishService.PublishVersion:output_type -> orchestrator.v1.PublishVersionResponse + 55, // 81: orchestrator.v1.PluginAuthService.StartDevice:output_type -> orchestrator.v1.StartDeviceResponse + 57, // 82: orchestrator.v1.PluginAuthService.PollDevice:output_type -> orchestrator.v1.PollDeviceResponse + 59, // 83: orchestrator.v1.PluginAuthService.ApproveDevice:output_type -> orchestrator.v1.ApproveDeviceResponse + 61, // 84: orchestrator.v1.PluginAuthService.DenyDevice:output_type -> orchestrator.v1.DenyDeviceResponse + 63, // 85: orchestrator.v1.PluginAuthService.GetDeviceStatus:output_type -> orchestrator.v1.GetDeviceStatusResponse + 65, // 86: orchestrator.v1.PluginAuthService.Whoami:output_type -> orchestrator.v1.WhoamiResponse + 68, // 87: orchestrator.v1.PluginAuthService.ListMyAccountsForCLI:output_type -> orchestrator.v1.ListMyAccountsForCLIResponse + 58, // [58:88] is the sub-list for method output_type + 28, // [28:58] is the sub-list for method input_type + 28, // [28:28] is the sub-list for extension type_name + 28, // [28:28] is the sub-list for extension extendee + 0, // [0:28] is the sub-list for field type_name +} + +func init() { file_orchestrator_v1_plugin_registry_proto_init() } +func file_orchestrator_v1_plugin_registry_proto_init() { + if File_orchestrator_v1_plugin_registry_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_orchestrator_v1_plugin_registry_proto_rawDesc), len(file_orchestrator_v1_plugin_registry_proto_rawDesc)), + NumEnums: 1, + NumMessages: 70, + NumExtensions: 0, + NumServices: 5, + }, + GoTypes: file_orchestrator_v1_plugin_registry_proto_goTypes, + DependencyIndexes: file_orchestrator_v1_plugin_registry_proto_depIdxs, + EnumInfos: file_orchestrator_v1_plugin_registry_proto_enumTypes, + MessageInfos: file_orchestrator_v1_plugin_registry_proto_msgTypes, + }.Build() + File_orchestrator_v1_plugin_registry_proto = out.File + file_orchestrator_v1_plugin_registry_proto_goTypes = nil + file_orchestrator_v1_plugin_registry_proto_depIdxs = nil +} diff --git a/internal/archive/archive.go b/internal/archive/archive.go new file mode 100644 index 0000000..ceb5d45 --- /dev/null +++ b/internal/archive/archive.go @@ -0,0 +1,56 @@ +package archive + +import ( + "bytes" + "fmt" + "io" + "os/exec" + "strings" + + "github.com/klauspost/compress/zstd" +) + +// BuildSourceArchive captures the working tree as `tar.zst` bytes. +// +// When the working tree is clean it archives HEAD. When it's dirty +// (modified or staged tracked files), it archives a temporary stash +// object so the dirty state is what ships — callers that want +// HEAD-only behaviour should reject dirty trees before calling. +// Untracked files are never included regardless of state. +func BuildSourceArchive(repoDir string) ([]byte, error) { + stashCmd := exec.Command("git", "stash", "create") + stashCmd.Dir = repoDir + var stashOut, stashErr bytes.Buffer + stashCmd.Stdout = &stashOut + stashCmd.Stderr = &stashErr + if err := stashCmd.Run(); err != nil { + return nil, fmt.Errorf("git stash create: %v: %s", err, stashErr.String()) + } + treeish := "HEAD" + if sha := strings.TrimSpace(stashOut.String()); sha != "" { + treeish = sha + } + + cmd := exec.Command("git", "archive", "--format=tar", treeish) + cmd.Dir = repoDir + var tarOut, stderr bytes.Buffer + cmd.Stdout = &tarOut + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + return nil, fmt.Errorf("git archive: %v: %s", err, stderr.String()) + } + + var compressed bytes.Buffer + enc, err := zstd.NewWriter(&compressed, zstd.WithEncoderLevel(zstd.SpeedDefault)) + if err != nil { + return nil, err + } + if _, err := io.Copy(enc, &tarOut); err != nil { + _ = enc.Close() + return nil, err + } + if err := enc.Close(); err != nil { + return nil, err + } + return compressed.Bytes(), nil +} diff --git a/internal/archive/archive_test.go b/internal/archive/archive_test.go new file mode 100644 index 0000000..4454e78 --- /dev/null +++ b/internal/archive/archive_test.go @@ -0,0 +1,208 @@ +package archive + +import ( + "archive/tar" + "bytes" + "io" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/klauspost/compress/zstd" +) + +func TestBuildSourceArchive_RoundTrip(t *testing.T) { + dir := t.TempDir() + run := func(name string, args ...string) { + t.Helper() + cmd := exec.Command(name, args...) + cmd.Dir = dir + cmd.Env = append(os.Environ(), + "GIT_AUTHOR_NAME=t", + "GIT_AUTHOR_EMAIL=t@t", + "GIT_COMMITTER_NAME=t", + "GIT_COMMITTER_EMAIL=t@t", + ) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s %v: %v\n%s", name, args, err, out) + } + } + run("git", "init", "-q") + if err := os.WriteFile(filepath.Join(dir, "plugin.mod"), + []byte("[plugin]\nname=\"x\"\nscope=\"@s\"\nversion=\"0.1.0\"\n"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "ignored.log"), []byte("nope"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("ignored.log\n"), 0o644); err != nil { + t.Fatal(err) + } + run("git", "add", "plugin.mod", ".gitignore") + run("git", "commit", "-qm", "init") + + zstdBytes, err := BuildSourceArchive(dir) + if err != nil { + t.Fatalf("BuildSourceArchive: %v", err) + } + if len(zstdBytes) == 0 { + t.Fatal("empty archive") + } + + dec, err := zstd.NewReader(bytes.NewReader(zstdBytes)) + if err != nil { + t.Fatal(err) + } + defer dec.Close() + tr := tar.NewReader(dec) + got := map[string]string{} + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + buf, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + got[hdr.Name] = string(buf) + } + if _, ok := got["plugin.mod"]; !ok { + t.Errorf("expected plugin.mod in archive, got %v", keys(got)) + } + if _, ok := got["ignored.log"]; ok { + t.Errorf("ignored.log should not be in archive (gitignored + untracked)") + } +} + +func keys(m map[string]string) []string { + out := make([]string, 0, len(m)) + for k := range m { + out = append(out, k) + } + return out +} + +func TestBuildSourceArchive_DirtyTreeShipsWorkingCopy(t *testing.T) { + dir := t.TempDir() + runGitArchive(t, dir, "init", "-q") + modPath := filepath.Join(dir, "plugin.mod") + if err := os.WriteFile(modPath, + []byte("[plugin]\nname=\"x\"\nscope=\"@s\"\nversion=\"0.1.0\"\n"), 0o644); err != nil { + t.Fatal(err) + } + runGitArchive(t, dir, "add", "plugin.mod") + runGitArchive(t, dir, "commit", "-qm", "init") + + dirtyContents := []byte("[plugin]\nname=\"x\"\nscope=\"@s\"\nversion=\"0.1.1\"\n") + if err := os.WriteFile(modPath, dirtyContents, 0o644); err != nil { + t.Fatal(err) + } + + zstdBytes, err := BuildSourceArchive(dir) + if err != nil { + t.Fatalf("BuildSourceArchive: %v", err) + } + + got := readArchive(t, zstdBytes) + contents, ok := got["plugin.mod"] + if !ok { + t.Fatalf("expected plugin.mod in archive, got %v", keys(got)) + } + if !strings.Contains(contents, `version="0.1.1"`) { + t.Errorf("archived plugin.mod should have dirty version 0.1.1, got: %q", contents) + } + if strings.Contains(contents, `version="0.1.0"`) { + t.Errorf("archived plugin.mod should NOT have HEAD version 0.1.0, got: %q", contents) + } + + // Working tree should be unchanged after stash-create. + postContents, err := os.ReadFile(modPath) + if err != nil { + t.Fatal(err) + } + if string(postContents) != string(dirtyContents) { + t.Errorf("working tree mutated after BuildSourceArchive\nwant: %q\ngot: %q", + string(dirtyContents), string(postContents)) + } +} + +func TestBuildSourceArchive_DirtyTreeOmitsUntracked(t *testing.T) { + dir := t.TempDir() + runGitArchive(t, dir, "init", "-q") + modPath := filepath.Join(dir, "plugin.mod") + if err := os.WriteFile(modPath, + []byte("[plugin]\nname=\"x\"\nscope=\"@s\"\nversion=\"0.1.0\"\n"), 0o644); err != nil { + t.Fatal(err) + } + runGitArchive(t, dir, "add", "plugin.mod") + runGitArchive(t, dir, "commit", "-qm", "init") + + // Dirty the tracked file. + if err := os.WriteFile(modPath, + []byte("[plugin]\nname=\"x\"\nscope=\"@s\"\nversion=\"0.1.1\"\n"), 0o644); err != nil { + t.Fatal(err) + } + // Add an untracked file (no git add). + if err := os.WriteFile(filepath.Join(dir, "extra.txt"), []byte("not tracked"), 0o644); err != nil { + t.Fatal(err) + } + + zstdBytes, err := BuildSourceArchive(dir) + if err != nil { + t.Fatalf("BuildSourceArchive: %v", err) + } + + got := readArchive(t, zstdBytes) + if _, ok := got["extra.txt"]; ok { + t.Errorf("untracked extra.txt should not be in archive, got %v", keys(got)) + } +} + +func runGitArchive(t *testing.T, dir string, args ...string) { + t.Helper() + cmd := exec.Command("git", args...) + cmd.Dir = dir + cmd.Env = append(os.Environ(), + "GIT_AUTHOR_NAME=t", + "GIT_AUTHOR_EMAIL=t@t", + "GIT_COMMITTER_NAME=t", + "GIT_COMMITTER_EMAIL=t@t", + ) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("git %v: %v\n%s", args, err, out) + } +} + +func readArchive(t *testing.T, zstdBytes []byte) map[string]string { + t.Helper() + dec, err := zstd.NewReader(bytes.NewReader(zstdBytes)) + if err != nil { + t.Fatal(err) + } + defer dec.Close() + tr := tar.NewReader(dec) + got := map[string]string{} + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + buf, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + got[hdr.Name] = string(buf) + } + return got +} diff --git a/internal/bnp/build.go b/internal/bnp/build.go new file mode 100644 index 0000000..1db9b45 --- /dev/null +++ b/internal/bnp/build.go @@ -0,0 +1,279 @@ +package bnp + +import ( + "bytes" + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "sort" + "strconv" + + abiv1 "git.dev.alexdunmow.com/block/core/abi/v1" + core "git.dev.alexdunmow.com/block/core/plugin" + "google.golang.org/protobuf/proto" +) + +// Required top-level artifact members (mirrors the CMS reader). +const ( + fileWasm = "plugin.wasm" + fileMod = "plugin.mod" + fileManifest = "manifest.pb" +) + +// minGoMajor/minGoMinor is the lowest Go toolchain that can emit reactor-mode +// (`-buildmode=c-shared`) wasip1 modules the guest shim relies on. +const ( + minGoMajor = 1 + minGoMinor = 24 +) + +// BuildOptions configures Build. +type BuildOptions struct { + // Dir is the plugin repo root (holds plugin.mod and the main Go package). + Dir string + // Output is the destination .bnp path. Empty → "-.bnp" in + // the current working directory. + Output string +} + +// BuildResult summarizes a produced artifact for the CLI summary table. +type BuildResult struct { + OutputPath string + Name string + Version string + // Codeless marks a declarative artifact (no plugin.wasm, WO-WZ-020). + Codeless bool + WasmBytes int64 + ManifestBytes int64 + ArtifactBytes int64 // on-disk .bnp size + UncompBytes int64 // sum of packed file sizes + BlockCount int + TemplateCount int + AdminPages int + JobTypes int + Hooks []string + DataDir bool + IncludedDirs []string +} + +// Build compiles the plugin in opts.Dir to wasm, extracts its manifest, and +// packs a .bnp. No Docker/podman: the whole pipeline is the local Go +// toolchain + wazero + tar.zst. +func Build(ctx context.Context, opts BuildOptions) (*BuildResult, error) { + dir := opts.Dir + if dir == "" { + dir = "." + } + dir, err := filepath.Abs(dir) + if err != nil { + return nil, fmt.Errorf("resolve dir: %w", err) + } + + modPath := filepath.Join(dir, fileMod) + modBytes, err := os.ReadFile(modPath) + if err != nil { + return nil, fmt.Errorf("read plugin.mod (is %s a plugin repo?): %w", dir, err) + } + mod, err := core.ParseModFull(modBytes) + if err != nil { + return nil, fmt.Errorf("parse plugin.mod: %w", err) + } + if mod.Plugin.Name == "" || mod.Plugin.Version == "" { + return nil, fmt.Errorf("plugin.mod must set both name and version") + } + + if err := checkGoVersion(ctx); err != nil { + return nil, err + } + + // 1. Compile to reactor-mode wasip1 c-shared. + tmp, err := os.MkdirTemp("", "ninja-build-*") + if err != nil { + return nil, fmt.Errorf("temp dir: %w", err) + } + defer func() { _ = os.RemoveAll(tmp) }() + wasmPath := filepath.Join(tmp, fileWasm) + if err := buildWasm(ctx, dir, wasmPath); err != nil { + return nil, err + } + + // 2. Extract the manifest by driving DESCRIBE. + wasm, err := readWasm(wasmPath) + if err != nil { + return nil, err + } + manifest, err := ExtractManifest(ctx, wasm) + if err != nil { + return nil, fmt.Errorf("extract manifest: %w", err) + } + + // 3. Validate against plugin.mod before packing an incoherent artifact. + if manifest.GetName() != mod.Plugin.Name { + return nil, fmt.Errorf("manifest name %q != plugin.mod name %q", manifest.GetName(), mod.Plugin.Name) + } + if v := manifest.GetAbiVersion(); v != hostAbiVersion { + return nil, fmt.Errorf("manifest abi_version %d unsupported (packer speaks %d)", v, hostAbiVersion) + } + + // 4. Stamp the data_dir grant from plugin.mod. DESCRIBE cannot see + // plugin.mod (it lives outside guest code), so the packer is where the + // grant crosses from mod → manifest; the loader then reads one source. + manifest.DataDir = mod.Plugin.DataDir + + // 4b. Mixed-form artifacts (WO-WZ-021): a reduced wasm plugin may keep a + // minimal guest for genuine logic (a contact route, Stripe) while its + // declarative surfaces (theme presets, bundled fonts, master pages, + // system/page templates, template overrides, email wrappers, css, + // required icon packs) live in an optional root manifest.yaml — exactly + // the codeless declarative set. Fold it into the DESCRIBE-derived + // manifest just as BuildCodeless does: the declarative keys + // supplement/override the guest's, and the referenced root JSON + // (presets.json / master_pages.json / fonts.json) is embedded into + // manifest.pb (not packed separately, same as codeless). The templates/ + // dir carrying the .ninjatpl sources already rides the optional-dir + // pack below. A repo with NO manifest.yaml is a no-op here + // (applyManifestYAML returns nil on os.IsNotExist), so an existing + // pure-wasm plugin builds byte-for-byte as before. + if err := applyManifestYAML(dir, manifest); err != nil { + return nil, err + } + + manifestBytes, err := proto.Marshal(manifest) + if err != nil { + return nil, fmt.Errorf("marshal manifest.pb: %w", err) + } + + // 5. Assemble artifact entries. + entries := []packEntry{ + {ArtifactPath: fileWasm, Source: wasmPath}, + {ArtifactPath: fileMod, Source: modPath}, + {ArtifactPath: fileManifest, Data: manifestBytes}, + } + var includedDirs []string + // dir-name → source subpath. web ships the Module Federation build output + // (web/dist) flattened under web/ so the reader's dirExists("web") fires. + optional := []struct{ artifact, src string }{ + {dirBlocks, dirBlocks}, + {dirTemplates, dirTemplates}, + {dirSeed, dirSeed}, + {"migrations", "migrations"}, + {"schemas", "schemas"}, + {"assets", "assets"}, + {"web", filepath.Join("web", "dist")}, + } + // Declarative dirs ride along on wasm artifacts too (a reduced plugin may + // mix definition-backed blocks with logic); validate them identically. + if _, err := validateBlocksDir(dir); err != nil { + return nil, err + } + if err := validateSeedDir(dir); err != nil { + return nil, err + } + for _, o := range optional { + dirEntries, has, dErr := collectDir(filepath.Join(dir, o.src), o.artifact) + if dErr != nil { + return nil, dErr + } + if has { + entries = append(entries, dirEntries...) + includedDirs = append(includedDirs, o.artifact) + } + } + + // 6. Pack. + outPath := opts.Output + if outPath == "" { + outPath = fmt.Sprintf("%s-%s.bnp", mod.Plugin.Name, mod.Plugin.Version) + } + uncomp, err := packArtifact(outPath, entries) + if err != nil { + return nil, err + } + + artInfo, err := os.Stat(outPath) + if err != nil { + return nil, fmt.Errorf("stat artifact: %w", err) + } + wasmInfo, _ := os.Stat(wasmPath) + + return &BuildResult{ + OutputPath: outPath, + Name: manifest.GetName(), + Version: manifest.GetVersion(), + WasmBytes: wasmInfo.Size(), + ManifestBytes: int64(len(manifestBytes)), + ArtifactBytes: artInfo.Size(), + UncompBytes: uncomp, + BlockCount: len(manifest.GetBlocks()), + TemplateCount: len(manifest.GetTemplateKeys()), + AdminPages: len(manifest.GetAdminPages()), + JobTypes: len(manifest.GetJobTypes()), + Hooks: hooksPresent(manifest), + DataDir: manifest.GetDataDir(), + IncludedDirs: includedDirs, + }, nil +} + +// buildWasm runs the reactor-mode wasip1 c-shared build in dir. +func buildWasm(ctx context.Context, dir, outPath string) error { + cmd := exec.CommandContext(ctx, "go", "build", "-buildmode=c-shared", "-o", outPath, ".") + cmd.Dir = dir + cmd.Env = append(os.Environ(), "GOOS=wasip1", "GOARCH=wasm") + var stderr bytes.Buffer + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("go build (GOOS=wasip1 GOARCH=wasm -buildmode=c-shared) failed: %w\n%s", err, stderr.String()) + } + return nil +} + +var goVersionRe = regexp.MustCompile(`go(\d+)\.(\d+)(?:\.\d+)?`) + +// checkGoVersion enforces the minimum toolchain (Go 1.24) with a clear error. +func checkGoVersion(ctx context.Context) error { + out, err := exec.CommandContext(ctx, "go", "version").Output() + if err != nil { + return fmt.Errorf("`go version` failed (is the Go toolchain on PATH?): %w", err) + } + m := goVersionRe.FindStringSubmatch(string(out)) + if m == nil { + return fmt.Errorf("could not parse Go version from %q", string(out)) + } + major, _ := strconv.Atoi(m[1]) + minor, _ := strconv.Atoi(m[2]) + if major < minGoMajor || (major == minGoMajor && minor < minGoMinor) { + return fmt.Errorf( + "building reactor-mode wasip1 plugins requires Go %d.%d+; found %d.%d — upgrade your toolchain", + minGoMajor, minGoMinor, major, minor) + } + return nil +} + +// hooksPresent returns the sorted set of runtime hooks the manifest declares, +// for the summary table. +func hooksPresent(m *abiv1.PluginManifest) []string { + var hooks []string + if m.GetHasLoadHook() { + hooks = append(hooks, "load") + } + if m.GetHasUnloadHook() { + hooks = append(hooks, "unload") + } + if m.GetHasHttpHandler() { + hooks = append(hooks, "http") + } + if m.GetHasMediaHooks() { + hooks = append(hooks, "media") + } + if len(m.GetJobTypes()) > 0 { + hooks = append(hooks, "job") + } + if len(m.GetRagContentFetcherTypes()) > 0 { + hooks = append(hooks, "rag") + } + sort.Strings(hooks) + return hooks +} diff --git a/internal/bnp/codeless.go b/internal/bnp/codeless.go new file mode 100644 index 0000000..31ba90c --- /dev/null +++ b/internal/bnp/codeless.go @@ -0,0 +1,515 @@ +package bnp + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + abiv1 "git.dev.alexdunmow.com/block/core/abi/v1" + core "git.dev.alexdunmow.com/block/core/plugin" + "google.golang.org/protobuf/proto" + "gopkg.in/yaml.v3" +) + +// Codeless artifacts (WO-WZ-020): a .bnp with NO plugin.wasm — pure +// declaration the host runs. Classification is by repo shape: a plugin repo +// with no Go source builds codeless; a repo with Go builds wasm as always +// (a converted repo DELETES its Go — that's the point). Both artifact kinds +// may carry blocks/, templates/, and seed/; codeless just has nothing else. + +// Declarative artifact dirs shared by both build paths. +const ( + dirBlocks = "blocks" + dirTemplates = "templates" + dirSeed = "seed" +) + +// manifestYAMLName is the optional root-level declarative manifest source for +// codeless plugins (the counterpart of what DESCRIBE captures from Go code). +const manifestYAMLName = "manifest.yaml" + +// manifestYAML mirrors the declarative PluginManifest fields a codeless +// plugin can set. File-valued keys are paths relative to the repo root. +type manifestYAML struct { + ThemePresets string `yaml:"theme_presets"` // JSON file + BundledFonts string `yaml:"bundled_fonts"` // JSON file + SettingsSchema string `yaml:"settings_schema"` // JSON file + MasterPages string `yaml:"master_pages"` // JSON file ([]masterPageJSON) + RequiredIconPacks []string `yaml:"required_icon_packs"` + SystemTemplates []struct { + Key string `yaml:"key"` + Title string `yaml:"title"` + Description string `yaml:"description"` + } `yaml:"system_templates"` + PageTemplates []struct { + System string `yaml:"system"` + Key string `yaml:"key"` + Title string `yaml:"title"` + Description string `yaml:"description"` + Slots []string `yaml:"slots"` + } `yaml:"page_templates"` + // TemplateOverrides declare theme-scoped overrides of builtin block + // rendering. Source convention: templates/overrides/