Documentation
¶
Overview ¶
Package api is the typed HTTP client for the positronick.com read API. It owns the wire types, the error envelope, auth-header injection, and the retry policy; callers receive decoded structs or a typed *APIError.
Index ¶
- Variables
- func IsAuthError(err error) bool
- func IsNotFound(err error) bool
- type APIError
- type APIKey
- type AdminListing
- type AdminSoul
- type Anonymous
- type Client
- func (c *Client) AdminListing(ctx context.Context, id string) (*AdminListing, error)
- func (c *Client) AdminSoul(ctx context.Context, id string) (*AdminSoul, error)
- func (c *Client) CreateAPIKey(ctx context.Context, name string, expiresIn int) (*APIKey, error)
- func (c *Client) CreateListing(ctx context.Context, fields map[string]any) (*AdminListing, error)
- func (c *Client) CreateSoul(ctx context.Context, fields map[string]any) (*AdminSoul, error)
- func (c *Client) Listing(ctx context.Context, slug string) (*Listing, error)
- func (c *Client) Listings(ctx context.Context, listingType string) ([]Listing, error)
- func (c *Client) Me(ctx context.Context) (*Me, error)
- func (c *Client) Soul(ctx context.Context, slug string) (*Soul, error)
- func (c *Client) SoulMarkdown(ctx context.Context, slug string) (string, error)
- func (c *Client) Souls(ctx context.Context) ([]SoulCard, error)
- func (c *Client) UpdateListing(ctx context.Context, id string, patch map[string]any) (*AdminListing, bool, error)
- func (c *Client) UpdateSoul(ctx context.Context, id string, patch map[string]any) (*AdminSoul, bool, error)
- type Credentials
- type CredentialsProvider
- type EnvCredentials
- type Listing
- type LoopData
- type Me
- type Option
- type Soul
- type SoulCard
- type User
Constants ¶
This section is empty.
Variables ¶
var ListingTypes = []string{"harness", "cli", "mcp", "agent", "skill", "plugin", "loop"}
ListingTypes are the kinds of official tooling the registry catalogs. Mirrors LISTING_TYPES in src/lib/types.ts.
Functions ¶
func IsAuthError ¶
IsAuthError reports whether err is an APIError for missing or insufficient authentication (401 or 403).
func IsNotFound ¶
IsNotFound reports whether err is an APIError for a missing resource (404).
Types ¶
type APIError ¶
type APIError struct {
// Status is the HTTP status code.
Status int
// Code is the machine-readable code from the envelope ("" when absent).
Code string
// Message is human-readable; never empty.
Message string
}
APIError is a non-2xx response from the API, decoded from the server's error envelope {"error":{"code":"...","message":"..."}} when present, or synthesized from the HTTP status text when the body is not the envelope (e.g. a proxy answering with HTML).
type APIKey ¶
type APIKey struct {
ID string `json:"id"`
Name string `json:"name"`
Start *string `json:"start"`
Prefix *string `json:"prefix"`
Key string `json:"key"`
ExpiresAt *string `json:"expiresAt"`
}
APIKey is the POST /api/auth/api-key/create response. Key is the raw secret — the server stores only a hash, so this is the one and only time it is visible. ExpiresAt stays an ISO-8601 string (or null for no expiry).
type AdminListing ¶
AdminListing is a listing as the admin API returns it, plus Source (same semantics as AdminSoul.Source).
type AdminSoul ¶
AdminSoul is a soul as the admin API returns it: the public shape plus Source, the ownership marker ("seed" = owned by the git content pipeline, "api" = owned by the write API; the seed skips api-owned rows).
type Anonymous ¶
type Anonymous struct{}
Anonymous is a CredentialsProvider that never authenticates.
func (Anonymous) Get ¶
func (Anonymous) Get() (Credentials, error)
Get implements CredentialsProvider.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a positronick.com API client. Construct it with New; the zero value is not usable.
func New ¶
func New(baseURL string, creds CredentialsProvider, opts ...Option) (*Client, error)
New returns a Client for the API at baseURL. The scheme must be http or https; a trailing slash is stripped. A nil creds means anonymous. Redirects are followed (the server 301s historical soul slugs to current ones).
func (*Client) AdminListing ¶
AdminListing fetches one listing by id, any status: GET /api/admin/listings/{id}.
func (*Client) CreateAPIKey ¶
CreateAPIKey mints a new API key: POST /api/auth/api-key/create. The server only allows session (bearer) credentials to mint keys — an API key cannot create more API keys. expiresIn is in seconds; 0 means the server default.
func (*Client) CreateListing ¶
CreateListing creates a registry listing: POST /api/admin/listings. The authoring profile is referenced by handle (profileHandle) and must already exist — profiles stay git-curated; the server answers 422 unknown_profile otherwise.
func (*Client) CreateSoul ¶
CreateSoul creates a soul: POST /api/admin/souls. fields is sent verbatim as the JSON body — the server validates with the seed's own validator and answers 422 with the validator message, 409 on a slug conflict. The id is server-assigned; the server rejects a client-supplied one.
func (*Client) Listings ¶
Listings fetches registry listings: GET /api/listings. An empty listingType returns all listings; otherwise ?type= is sent and the server rejects unknown types with an invalid_type error.
func (*Client) Me ¶
Me fetches the authenticated identity: GET /api/me. Without valid credentials the server answers 401, surfaced as an *APIError for which IsAuthError is true.
func (*Client) Soul ¶
Soul fetches one soul with its markdown body: GET /api/souls/{slug}. A renamed slug is followed via the server's 301; a missing one surfaces as an *APIError for which IsNotFound is true.
func (*Client) SoulMarkdown ¶
SoulMarkdown fetches the raw SOUL.md body verbatim: GET /api/souls/{slug}.md. This is the install contract — the returned string is byte-identical to what `curl .../api/souls/{slug}.md` writes, so installs are lossless.
func (*Client) UpdateListing ¶
func (c *Client) UpdateListing(ctx context.Context, id string, patch map[string]any) (*AdminListing, bool, error)
UpdateListing patches a listing: PATCH /api/admin/listings/{id}. A profileHandle in the patch is re-resolved server-side. tookOwnership has the same semantics as UpdateSoul.
func (*Client) UpdateSoul ¶
func (c *Client) UpdateSoul(ctx context.Context, id string, patch map[string]any) (*AdminSoul, bool, error)
UpdateSoul patches a soul: PATCH /api/admin/souls/{id}. patch carries only the fields to change. tookOwnership is true when the row was seed-owned and this update flipped it to api-owned — the caller must warn that the git copy is now inert and will be skipped by deploys.
type Credentials ¶
type Credentials struct {
// APIKey is sent as the x-api-key header.
APIKey string
// Bearer is sent as "Authorization: Bearer ..." when APIKey is empty.
Bearer string
}
Credentials carries the secrets a request can authenticate with. APIKey always wins: when set, the bearer token is not sent at all.
type CredentialsProvider ¶
type CredentialsProvider interface {
Get() (Credentials, error)
}
CredentialsProvider supplies credentials per request, so tokens refreshed or exported mid-process are picked up. The future auth package implements this; the client only depends on the interface.
type EnvCredentials ¶
type EnvCredentials struct{}
EnvCredentials reads POSITRONICK_API_KEY from the environment at call time (not at construction), so a key exported after client setup still applies.
func (EnvCredentials) Get ¶
func (EnvCredentials) Get() (Credentials, error)
Get implements CredentialsProvider.
type Listing ¶
type Listing struct {
// ID is the stable, immutable id (ULID).
ID string `json:"id"`
// Slug is the human-facing url segment: /listings/[slug].
Slug string `json:"slug"`
// ProfileHandle/ProfileName denormalize the authoring profile for cards.
ProfileHandle string `json:"profileHandle"`
ProfileName string `json:"profileName"`
Name string `json:"name"`
// Type is one of ListingTypes.
Type string `json:"type"`
// Tagline is the short one-liner shown on cards.
Tagline string `json:"tagline"`
Description *string `json:"description"`
Category string `json:"category"`
Tags []string `json:"tags"`
// Official is true when published by the owner's verified official account.
Official bool `json:"official"`
// SourceURL is the official source that was verified: repo, docs, or registry.
SourceURL string `json:"sourceUrl"`
RepoURL *string `json:"repoUrl"`
// InstallCmd is the canonical official install/run command, if any.
InstallCmd *string `json:"installCmd"`
// Data holds type-specific extras (e.g. LoopData for loops); {} when none.
Data map[string]any `json:"data"`
Confidence string `json:"confidence"`
Status string `json:"status"`
DownloadCount int `json:"downloadCount"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
Listing is a catalog entry for one official tool. Mirrors Listing in src/lib/types.ts.
type LoopData ¶
type LoopData struct {
// Goal is what "done" looks like for the loop.
Goal string `json:"goal,omitempty"`
// CheckCommand is the command run between iterations to gauge progress.
CheckCommand string `json:"checkCommand,omitempty"`
// ExitCondition is the condition that ends the loop.
ExitCondition string `json:"exitCondition,omitempty"`
// MaxIterations is the safety cap on iterations.
MaxIterations int `json:"maxIterations,omitempty"`
// CompatibleTools lists agents/harnesses the loop is known to work with.
CompatibleTools []string `json:"compatibleTools,omitempty"`
// Kickoff is the prompt a user copies to start the loop.
Kickoff string `json:"kickoff,omitempty"`
}
LoopData is the type-specific extras for a `loop` listing, stored in Listing.Data. Mirrors LoopData in src/lib/types.ts.
type Me ¶
Me is the GET /api/me response. IsAdmin is computed server-side; the CLI caches it but never decides it.
type Option ¶
type Option func(*Client)
Option customizes a Client.
func WithHTTPClient ¶
WithHTTPClient replaces the underlying *http.Client (e.g. for a custom transport). The caller owns the timeout configuration.
type Soul ¶
Soul is a full soul, including the raw SOUL.md markdown body. Mirrors Soul in src/lib/types.ts (SoulMeta + content).
type SoulCard ¶
type SoulCard struct {
// ID is the stable, immutable id (ULID). Never changes, even if the slug does.
ID string `json:"id"`
// Slug is the human-facing url segment: /souls/[slug].
Slug string `json:"slug"`
// SlugHistory holds previous slugs; the server 301s them to the current slug.
SlugHistory []string `json:"slugHistory"`
Name string `json:"name"`
// AuthorHandle is the author handle — the join key for user accounts.
AuthorHandle string `json:"authorHandle"`
AuthorName *string `json:"authorName"`
AuthorURL *string `json:"authorUrl"`
// Tagline is the short one-liner shown on cards.
Tagline string `json:"tagline"`
Description *string `json:"description"`
Category string `json:"category"`
Tags []string `json:"tags"`
Frameworks []string `json:"frameworks"`
Models []string `json:"models"`
// Version is semver.
Version string `json:"version"`
// License is an SPDX license id, e.g. "MIT".
License string `json:"license"`
RepoURL *string `json:"repoUrl"`
// ContentHash is the sha256 of the normalized SOUL.md body.
ContentHash string `json:"contentHash"`
// Status is draft | pending | published.
Status string `json:"status"`
DownloadCount int `json:"downloadCount"`
RatingAvg *float64 `json:"ratingAvg"`
RatingCount int `json:"ratingCount"`
ArenaRank *int `json:"arenaRank"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
SoulCard is the lightweight list/gallery view of a soul — everything needed to render a card or detail header, deliberately excluding the heavy markdown body. Mirrors SoulCard (= SoulMeta) in src/lib/types.ts.
Nullable fields in the TS contract (string | null, number | null) are pointers so that null round-trips as null in --json output.
type User ¶
type User struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Image *string `json:"image"`
GithubLogin *string `json:"githubLogin"`
}
User is the authenticated identity returned by GET /api/me. Image and GithubLogin are nullable (e.g. a Google-only account has no GitHub login), so they are pointers and null round-trips as null in --json output.