api

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
var BlogCategories = []string{"Releases", "Announcements", "Tutorials", "Guides", "Engineering", "Community"}

BlogCategories are the categories a mirrored post (and a feed source's defaultCategory) may use. Mirrors BLOG_CATEGORIES in src/lib/types.ts.

View Source
var FeedKinds = []string{"github_release", "rss"}

FeedKinds are the kinds of blog feed source the ingestor mirrors. Mirrors FEED_KINDS in src/lib/server/feedFields.ts.

View Source
var ListingCategories = []string{
	"AI/ML", "Memory", "Observability", "DevOps", "Cloud", "Web",
	"Data", "Security", "Technical", "Productivity",
}

ListingCategories are the broad subject labels a listing may use. Mirrors LISTING_CATEGORIES in src/lib/types.ts.

View Source
var ListingTypes = []string{"harness", "cli", "mcp", "memory", "agent", "skill", "plugin", "loop", "bot"}

ListingTypes are the kinds of official tooling the registry catalogs. Mirrors LISTING_TYPES in src/lib/types.ts.

View Source
var PostKinds = []string{"article", "release", "link"}

PostKinds are the kinds of blog post the registry serves: an editorial article, a mirrored GitHub release, or a mirrored RSS/news link. Mirrors POST_KINDS in src/lib/types.ts.

Functions

func IsAuthError

func IsAuthError(err error) bool

IsAuthError reports whether err is an APIError for missing or insufficient authentication (401 or 403).

func IsNotFound

func IsNotFound(err error) bool

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
	// Body is the raw response body, retained so a caller can read a
	// non-envelope payload the generic decoder ignores — e.g. the feed-sync
	// 502 answers with {"summary":{...}} (carrying the failure reason) rather
	// than the error envelope. Nil when the response had no body.
	Body []byte
}

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).

func (*APIError) Error

func (e *APIError) Error() string

Error implements the error interface.

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

type AdminListing struct {
	Listing
	Source string `json:"source"`
}

AdminListing is a listing as the admin API returns it, plus Source (same semantics as AdminSoul.Source).

type AdminPost added in v0.2.0

type AdminPost struct {
	Post
	Source string `json:"source"`
}

AdminPost is a blog post as the admin API returns it: the full public Post (markdown body included) plus Source, the ownership marker. For posts the values are "feed" (mirrored from a feed source by the ingestor, which keeps it fresh) and "api" (authored or edited through the write API, which the ingestor leaves alone). Editing a feed-owned post flips it to api, reported as tookOwnership.

type AdminProfile added in v0.1.2

type AdminProfile struct {
	Profile
	Source string `json:"source"`
}

AdminProfile is a profile as the admin API returns it, plus Source (same semantics as AdminSoul.Source: "seed" = git-curated, "api" = admin-created).

type AdminSoul

type AdminSoul struct {
	Soul
	Source string `json:"source"`
}

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 BotData added in v0.2.0

type BotData struct {
	// Prompt is the paste-ready instruction the bot runs on each fire.
	Prompt string `json:"prompt"`
	// Integrations are services the bot is wired to (e.g. github, slack).
	Integrations []string `json:"integrations,omitempty"`
	// Schedule is the cron expression the bot fires on, if any.
	Schedule string `json:"schedule,omitempty"`
	// Platforms are harnesses the bot is known to run on.
	Platforms []string `json:"platforms,omitempty"`
	// SoulSlug is the soul this bot is paired with, if any.
	SoulSlug string `json:"soulSlug,omitempty"`
}

BotData is the type-specific extras for a `bot` listing, stored in Listing.Data. Mirrors BotData in src/lib/types.ts.

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) AdminFeed added in v0.2.0

func (c *Client) AdminFeed(ctx context.Context, id string) (*FeedSource, error)

AdminFeed fetches one feed source by id: GET /api/admin/feeds/{id}.

func (*Client) AdminListing

func (c *Client) AdminListing(ctx context.Context, id string) (*AdminListing, error)

AdminListing fetches one listing by id, any status: GET /api/admin/listings/{id}.

func (*Client) AdminPost added in v0.2.0

func (c *Client) AdminPost(ctx context.Context, id string) (*AdminPost, error)

AdminPost fetches one post by id, any status: GET /api/admin/posts/{id}.

func (*Client) AdminPosts added in v0.2.0

func (c *Client) AdminPosts(ctx context.Context) ([]AdminPost, error)

AdminPosts lists every post (any status, source included): GET /api/admin/posts. Admin only — the server answers 401/403 for non-admins. Named AdminPosts (not Posts) to leave the public, published-only blog gallery reader Posts(ctx, kind) untouched.

func (*Client) AdminSoul

func (c *Client) AdminSoul(ctx context.Context, id string) (*AdminSoul, error)

AdminSoul fetches one soul by id, any status: GET /api/admin/souls/{id}.

func (*Client) CreateAPIKey

func (c *Client) CreateAPIKey(ctx context.Context, name string, expiresIn int) (*APIKey, error)

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) CreateFeed added in v0.2.0

func (c *Client) CreateFeed(ctx context.Context, fields map[string]any) (*FeedSource, error)

CreateFeed creates a blog feed source: POST /api/admin/feeds. fields is sent verbatim as the JSON body — the server validates it and answers 422 with the validator message, 422 unknown_profile when authorHandle resolves to no profile, or 422 invalid_input when listingSlug resolves to no listing. The id and source are server-assigned (source is always "api").

func (*Client) CreateListing

func (c *Client) CreateListing(ctx context.Context, fields map[string]any) (*AdminListing, error)

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) CreatePost added in v0.2.0

func (c *Client) CreatePost(ctx context.Context, fields map[string]any) (*AdminPost, error)

CreatePost creates a blog post: POST /api/admin/posts. fields is sent verbatim as the JSON body — the server validates it and answers 422 with the validator message, 409 on a slug conflict. The id is server-assigned (the server rejects a client-supplied one) and status defaults to "draft": an agent-authored post cannot self-publish, it must be promoted deliberately.

func (*Client) CreateProfile added in v0.1.2

func (c *Client) CreateProfile(ctx context.Context, fields map[string]any) (*AdminProfile, error)

CreateProfile creates a registry profile: POST /api/admin/profiles. fields is sent verbatim as the JSON body — the server validates it and answers 422 on a bad field, 409 when the handle is already taken. The id and source are server-assigned (source becomes "api"); verified/official default false. Profiles were historically git-curated only; this is the admin write path.

func (*Client) CreateSoul

func (c *Client) CreateSoul(ctx context.Context, fields map[string]any) (*AdminSoul, error)

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) Feeds added in v0.2.0

func (c *Client) Feeds(ctx context.Context) ([]FeedSource, error)

Feeds lists every blog feed source, newest first: GET /api/admin/feeds. Admin only — the server answers 401/403 for non-admins.

func (*Client) Listing

func (c *Client) Listing(ctx context.Context, slug string) (*Listing, error)

Listing fetches one registry listing: GET /api/listings/{slug}.

func (*Client) Listings

func (c *Client) Listings(ctx context.Context, listingType string) ([]Listing, error)

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

func (c *Client) Me(ctx context.Context) (*Me, error)

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) Post added in v0.2.0

func (c *Client) Post(ctx context.Context, slug string) (*Post, error)

Post fetches one post with its markdown body: GET /api/blog/{slug}. A renamed slug is followed via the server's 301; a missing one surfaces as an *APIError for which IsNotFound is true. This endpoint never bumps the post's viewCount.

func (*Client) PostMarkdown added in v0.2.0

func (c *Client) PostMarkdown(ctx context.Context, slug string) (string, error)

PostMarkdown fetches the raw post markdown verbatim: GET /api/blog/{slug}.md. Unlike the soul .md endpoint (which bumps the install counter), the blog .md endpoint never bumps viewCount — counting stays exclusive to the HTML page view — so `blog show --raw` reads it directly. A renamed slug is followed via the server's 301; a missing one is an IsNotFound *APIError.

func (*Client) Posts added in v0.2.0

func (c *Client) Posts(ctx context.Context, kind string) ([]PostCard, error)

Posts fetches the published blog gallery, newest first: GET /api/blog. A non-empty kind narrows to one of PostKinds (article, release, link) via the server's ?kind= filter; "" fetches every kind.

func (*Client) Profiles added in v0.1.2

func (c *Client) Profiles(ctx context.Context) ([]AdminProfile, error)

Profiles lists every profile (any source): GET /api/admin/profiles. Admin only — the server answers 401/403 for non-admins. Useful for discovering which handles already exist before authoring a listing.

func (*Client) Research added in v0.2.0

func (c *Client) Research(ctx context.Context, q ResearchQuery) (*ResearchResult, error)

Research fetches the "what's new" feed: GET /api/research. The server validates the params and answers 400 invalid_input (surfaced verbatim) on a bad kind, limit, or since value.

func (*Client) SkillMarkdown added in v0.2.0

func (c *Client) SkillMarkdown(ctx context.Context, slug string) (string, error)

SkillMarkdown fetches a hosted skill's raw SKILL.md body verbatim: GET /api/skills/{slug}.md. This is the install contract for skill listings whose HasAsset is true — mirrors SoulMarkdown, and bumps the same public download counter.

func (*Client) Soul

func (c *Client) Soul(ctx context.Context, slug string) (*Soul, error)

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

func (c *Client) SoulMarkdown(ctx context.Context, slug string) (string, error)

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) Souls

func (c *Client) Souls(ctx context.Context) ([]SoulCard, error)

Souls fetches the full soul gallery: GET /api/souls.

func (*Client) SyncFeed added in v0.2.0

func (c *Client) SyncFeed(ctx context.Context, id string) (*FeedSyncSummary, error)

SyncFeed ingests one feed now: POST /api/admin/feeds/{id}/sync. It returns the ingest summary on success AND on a fetch/parse failure: the server answers 502 with the same {"summary":{...}} body (not the error envelope) when the fetch/parse failed, and that summary's Error field carries the reason. The caller surfaces a non-empty Error as a clear failure. A missing feed is the usual 404 *APIError.

func (*Client) UpdateFeed added in v0.2.0

func (c *Client) UpdateFeed(ctx context.Context, id string, patch map[string]any) (*FeedSource, error)

UpdateFeed patches a feed source: PATCH /api/admin/feeds/{id}. patch carries only the fields to change — {"enabled":false} pauses a feed (there is no delete verb). Feed sources carry no seed/api ownership flip, so unlike soul and listing updates there is no tookOwnership signal.

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) UpdatePost added in v0.2.0

func (c *Client) UpdatePost(ctx context.Context, id string, patch map[string]any) (*AdminPost, bool, error)

UpdatePost patches a post: PATCH /api/admin/posts/{id}. patch carries only the fields to change — {"status":"draft"} unpublishes (there is no delete verb). tookOwnership is true when the row was feed-owned and this edit flipped it to api-owned: the ingestor will no longer refresh it from its source.

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 FeedSource added in v0.2.0

type FeedSource struct {
	// ID is the stable, immutable id (ULID).
	ID    string `json:"id"`
	Label string `json:"label"`
	// FeedURL is the github_release repo URL or the rss feed URL.
	FeedURL string `json:"feedUrl"`
	// Kind is one of FeedKinds: github_release | rss.
	Kind string `json:"kind"`
	// AuthorProfileID/AuthorHandle are the default byline stamped on mirrored
	// posts; the handle is left-joined for display. Null when unattributed.
	AuthorProfileID *string `json:"authorProfileId"`
	AuthorHandle    *string `json:"authorHandle"`
	// ListingID/ListingSlug are the related tool stamped on mirrored posts; the
	// slug is left-joined for display. Null when none.
	ListingID   *string `json:"listingId"`
	ListingSlug *string `json:"listingSlug"`
	// DefaultCategory is one of BlogCategories.
	DefaultCategory string   `json:"defaultCategory"`
	DefaultTags     []string `json:"defaultTags"`
	// AutoPublish publishes mirrored posts immediately instead of as drafts.
	AutoPublish bool `json:"autoPublish"`
	// Enabled is the on/off switch; false pauses the feed (there is no delete).
	Enabled bool `json:"enabled"`
	// LastFetchedAt/LastStatus record the last ingest run — null until the
	// first run; LastStatus is "ok" or the last error message (fail-loud).
	LastFetchedAt *string `json:"lastFetchedAt"`
	LastStatus    *string `json:"lastStatus"`
	CreatedAt     string  `json:"createdAt"`
	UpdatedAt     string  `json:"updatedAt"`
}

FeedSource is a subscribed blog feed source the ingestor mirrors into posts (GitHub releases or RSS). Admin-only — never returned from a public route. Mirrors FeedSource in src/lib/server/feeds.ts. Nullable TS fields are pointers so null round-trips as null in --json output. There is deliberately no `source` field on the wire: feed sources are always api-owned (never git-seeded), so they carry no seed/api ownership marker.

type FeedSyncSummary added in v0.2.0

type FeedSyncSummary struct {
	FeedID  string `json:"feedId"`
	Label   string `json:"label"`
	Fetched int    `json:"fetched"`
	Created int    `json:"created"`
	Updated int    `json:"updated"`
	Skipped int    `json:"skipped"`
	// ItemErrors holds per-item failures on an otherwise-successful run; each
	// such item is counted in Skipped.
	ItemErrors []string `json:"itemErrors"`
	// Error is the fetch/parse failure reason; empty on success.
	Error string `json:"error,omitempty"`
}

FeedSyncSummary is the per-feed ingest outcome from a sync. Mirrors IngestSummary in src/lib/server/feedIngest.ts. A non-empty Error means the fetch/parse failed and no items were processed — the sync endpoint answers 502 in that case, with this same summary as the body.

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"`
	// ProfileTier is the author's seal — "official" | "verified" | null.
	ProfileTier *string `json:"profileTier"`
	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, SkillData for
	// skills, BotData for bots); {} when none.
	Data map[string]any `json:"data"`
	// HasAsset is true when a hosted SKILL.md asset exists for this (skill) listing.
	HasAsset bool `json:"hasAsset"`
	// AssetVersion is the hosted asset's semver, or null when HasAsset is false.
	AssetVersion *string `json:"assetVersion"`
	// AssetContentHash is the sha256 of the hosted asset body, or null — lets
	// clients skip identical re-downloads.
	AssetContentHash *string `json:"assetContentHash"`
	Confidence       string  `json:"confidence"`
	Status           string  `json:"status"`
	DownloadCount    int     `json:"downloadCount"`
	// ChargeCount is the running count of user "charges" (the energy boost).
	ChargeCount int    `json:"chargeCount"`
	CreatedAt   string `json:"createdAt"`
	UpdatedAt   string `json:"updatedAt"`
}

Listing is a catalog entry for one official tool. Mirrors Listing in src/lib/types.ts.

func (*Listing) BotData added in v0.2.0

func (l *Listing) BotData() (BotData, error)

BotData decodes the untyped Data payload into BotData. Empty or nil Data yields the zero value; a wrong-typed field is an error, never a silent zero.

func (*Listing) LoopData

func (l *Listing) LoopData() (LoopData, error)

LoopData decodes the untyped Data payload into LoopData. Empty or nil Data yields the zero value; a wrong-typed field is an error, never a silent zero.

func (*Listing) SkillData added in v0.2.0

func (l *Listing) SkillData() (SkillData, error)

SkillData decodes the untyped Data payload into SkillData. Empty or nil Data yields the zero value; a wrong-typed field is an error, never a silent zero.

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"`
	// Bundles are slugs of listings this loop depends on (a loop usually drives
	// several skills).
	Bundles []string `json:"bundles,omitempty"`
}

LoopData is the type-specific extras for a `loop` listing, stored in Listing.Data. Mirrors LoopData in src/lib/types.ts.

type Me

type Me struct {
	User    User `json:"user"`
	IsAdmin bool `json:"isAdmin"`
}

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 WithSleep

func WithSleep(fn func(time.Duration)) Option

WithSleep replaces the backoff sleep function — a test seam so retry tests assert waits instead of serving them.

type Post added in v0.2.0

type Post struct {
	PostCard
	Content string `json:"content"`
}

Post is a full blog post, including the raw markdown body. Mirrors Post in src/lib/types.ts (PostMeta + content).

type PostCard added in v0.2.0

type PostCard struct {
	// ID is the stable, immutable id (ULID).
	ID string `json:"id"`
	// Slug is the human-facing url segment: /blog/[slug].
	Slug string `json:"slug"`
	// SlugHistory holds previous slugs; the server 301s them to the current slug.
	SlugHistory []string `json:"slugHistory"`
	// Kind is one of PostKinds: article | release | link.
	Kind  string `json:"kind"`
	Title string `json:"title"`
	// Excerpt is the short summary shown on cards and in the RSS feed.
	Excerpt string `json:"excerpt"`
	// Description is an optional longer SEO description.
	Description *string `json:"description"`
	// ContentHash is the sha256 of the normalized markdown body — the citation/dedup anchor.
	ContentHash string `json:"contentHash"`
	// Version is semver.
	Version  string   `json:"version"`
	Category string   `json:"category"`
	Tags     []string `json:"tags"`
	// AuthorHandle/AuthorName denormalize the authoring profile for cards; null when authorless.
	AuthorHandle *string `json:"authorHandle"`
	AuthorName   *string `json:"authorName"`
	// AuthorAvatar is the author's avatar URL; null falls back to the brand mark.
	AuthorAvatar *string `json:"authorAvatar"`
	// AuthorTier is the author's seal — "official" | "verified" | null.
	AuthorTier *string `json:"authorTier"`
	// ListingSlug/ListingName link a post about a registry tool to that listing; null otherwise.
	ListingSlug *string `json:"listingSlug"`
	ListingName *string `json:"listingName"`
	// CanonicalURL backlinks to the original GitHub release / RSS item; null for native posts.
	CanonicalURL *string `json:"canonicalUrl"`
	// Status is draft | pending | published.
	Status string `json:"status"`
	// ViewCount is the running page-view count.
	ViewCount int `json:"viewCount"`
	// PublishedAt is the canonical publish instant; null while unpublished.
	PublishedAt *string `json:"publishedAt"`
	CreatedAt   string  `json:"createdAt"`
	UpdatedAt   string  `json:"updatedAt"`
}

PostCard is the lightweight list/gallery view of a blog post — the scalar metadata needed to render a card or detail header, deliberately excluding the heavy markdown body. Mirrors PostCard (= PostMeta) in src/lib/types.ts.

Nullable fields in the TS contract (string | null, Date | null) are pointers so that null round-trips as null in --json output. Dates stay ISO-8601 strings, like every other date in this file.

type Profile added in v0.1.2

type Profile struct {
	// ID is the stable, immutable id (ULID).
	ID string `json:"id"`
	// Handle is the human-facing handle: /profiles/[handle].
	Handle string `json:"handle"`
	Name   string `json:"name"`
	// Kind is person | org.
	Kind string `json:"kind"`
	// Verified is the white seal (team-curated or claimed); Official is the red seal.
	Verified  bool    `json:"verified"`
	Official  bool    `json:"official"`
	Website   *string `json:"website"`
	GithubURL *string `json:"githubUrl"`
	// GithubUserID is the immutable GitHub numeric id (person profiles).
	GithubUserID *string `json:"githubUserId"`
	AvatarURL    *string `json:"avatarUrl"`
	Bio          *string `json:"bio"`
	// Socials are public http(s) URLs.
	Socials   []string `json:"socials"`
	CreatedAt string   `json:"createdAt"`
	UpdatedAt string   `json:"updatedAt"`
}

Profile is a verified person or org that authors registry tooling. Mirrors Profile in src/lib/types.ts. Nullable TS fields (string | null) are pointers so null round-trips as null in --json output.

type ResearchItem added in v0.2.0

type ResearchItem struct {
	Slug     string   `json:"slug"`
	Title    string   `json:"title"`
	Excerpt  string   `json:"excerpt"`
	Kind     string   `json:"kind"`
	Category string   `json:"category"`
	Tags     []string `json:"tags"`
	// URL is the absolute permalink: /blog/[slug].
	URL string `json:"url"`
	// MdURL is the absolute raw-markdown endpoint: /api/blog/[slug].md.
	MdURL        string  `json:"mdUrl"`
	CanonicalURL *string `json:"canonicalUrl"`
	ContentHash  string  `json:"contentHash"`
	PublishedAt  *string `json:"publishedAt"`
}

ResearchItem is one compact "what's new" record returned by GET /api/research — the payload the CLI surfaces (positronick research) so agents avoid stale knowledge. Mirrors ResearchItem in src/lib/types.ts. ContentHash lets a caller dedup and detect silent edits; CanonicalURL traces a mirrored release/link back to its source; MdURL points at the raw markdown.

PublishedAt and CanonicalURL are nullable in the TS contract and so are pointers, so null round-trips as null in --json output.

type ResearchQuery added in v0.2.0

type ResearchQuery struct {
	// Q is a free-text query over title/excerpt.
	Q string
	// Kind narrows to one of PostKinds (article, release, link).
	Kind string
	// Category narrows to one blog category.
	Category string
	// Tag narrows to posts carrying this tag.
	Tag string
	// Since is an ISO-8601 instant; only posts published strictly after it are
	// returned (the delta since a previous poll).
	Since string
	// Limit caps the result count (server range 1–100). 0 omits the param and
	// takes the server default.
	Limit int
}

ResearchQuery carries the optional filters for GET /api/research. The zero value (all fields empty, Limit 0) fetches the default feed: every published post, newest first, up to the server's default page size.

type ResearchResult added in v0.2.0

type ResearchResult struct {
	Results []ResearchItem `json:"results"`
	Latest  *string        `json:"latest"`
}

ResearchResult is the GET /api/research response: the matched items plus Latest, the newest publishedAt within the same filter (kind/category/tag/q), ignoring Since. Feed Latest back as the next Since to poll only the delta; it is null when the filter matched nothing at all.

type SkillData added in v0.2.0

type SkillData struct {
	// Bundles are slugs of listings this skill bundles — its install-time deps.
	Bundles []string `json:"bundles,omitempty"`
}

SkillData is the type-specific extras for a `skill` listing, stored in Listing.Data. A skill with Bundles is a meta-skill: installing it pulls in every bundled listing. Mirrors SkillData in src/lib/types.ts.

type Soul

type Soul struct {
	SoulCard
	Content string `json:"content"`
}

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"`
	// ChargeCount is the running count of user "charges" (the energy boost).
	ChargeCount int      `json:"chargeCount"`
	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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL