api

package
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Mar 16, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// HeaderMinVersion is returned by the server to indicate the minimum CLI version required
	HeaderMinVersion = "X-SageOx-Min-Version"

	// HeaderDeprecated is returned by the server to warn of upcoming deprecation
	HeaderDeprecated = "X-SageOx-Deprecated"
)
View Source
const RedirectHeader = "X-SageOx-Merge"

RedirectHeader is the HTTP header name for merge/redirect information

Variables

View Source
var ErrForbidden = errors.New("access denied: you are not a member of this team — request an invite URL from a team admin")

ErrForbidden is returned when the API returns 403 Forbidden

View Source
var ErrLedgerNotFound = errors.New("ledger not found")

ErrLedgerNotFound is returned when the ledger doesn't exist for the given repo.

View Source
var ErrReadOnly = errors.New("read-only access: you are a viewer on this public repo")

ErrReadOnly is returned when the user has viewer (read-only) access to a public repo

View Source
var ErrUnauthorized = errors.New("authentication required: run 'ox login' first")

ErrUnauthorized is returned when the API returns 401 Unauthorized

View Source
var ErrVersionUnsupported = errors.New("CLI version no longer supported by server")

ErrVersionUnsupported is returned when the server indicates the CLI version is no longer supported

Functions

func CheckVersionResponse

func CheckVersionResponse(resp *http.Response) bool

CheckVersionResponse inspects HTTP response for version deprecation signals. Returns true if the CLI should abort due to unsupported version (HTTP 426). For successful responses, checks for soft deprecation warning header.

func DeriveSlug added in v0.3.0

func DeriveSlug(name string) string

DeriveSlug generates a kebab-case slug from a display name. Used as fallback when the server doesn't provide a slug.

func HandleRedirect

func HandleRedirect(projectRoot string, info *RedirectInfo) error

HandleRedirect processes redirect info and updates local config/markers This is best-effort: returns nil on success or if nothing to do, and logs but does not fail on non-critical errors

func PrintDeprecationWarning

func PrintDeprecationWarning(message string)

PrintDeprecationWarning displays a warning that the CLI version is deprecated Uses yellow color semantically indicating a non-blocking warning

func PrintUpgradeRequired

func PrintUpgradeRequired(minVersion string)

PrintUpgradeRequired displays a message indicating the CLI version is no longer supported Uses red color semantically indicating a blocking error

func RenameRepoMarker added in v0.3.0

func RenameRepoMarker(projectRoot, oldID, newID string) error

RenameRepoMarker renames the .repo_* marker file from old ID to new ID using VCS and updates the repo_id value inside the marker file

Types

type DistillObservation added in v0.3.0

type DistillObservation struct {
	Content    string `json:"content"`
	RecordedAt string `json:"recorded_at"` // RFC3339
}

DistillObservation is a single observation in a distill request.

type DistillRequest added in v0.3.0

type DistillRequest struct {
	Observations []DistillObservation `json:"observations"`
	DateRange    [2]string            `json:"date_range"` // [start, end] RFC3339
}

DistillRequest is the request body for POST /api/v1/teams/<teamID>/memory/distill.

type DistillResponse added in v0.3.0

type DistillResponse struct {
	Summary   string `json:"summary"`
	UpdatedAt string `json:"updated_at"`
}

DistillResponse is the response from POST /api/v1/teams/<teamID>/memory/distill.

type DoctorIssue

type DoctorIssue struct {
	Type        string `json:"type"`                   // e.g., "merge_pending", "team_invite_pending"
	Severity    string `json:"severity"`               // "error", "warning", "info"
	Title       string `json:"title"`                  // short display title
	Description string `json:"description"`            // detailed explanation, supports Markdown
	ActionURL   string `json:"action_url,omitempty"`   // URL to resolve the issue
	ActionLabel string `json:"action_label,omitempty"` // button text, e.g., "Resolve merge"
}

DoctorIssue represents a single diagnostic issue from the cloud Cloud doctor detects things the local CLI cannot: - Pending merge conflicts (same repo registered twice) - requires cross-repo knowledge - Team invites pending acceptance - lives in cloud DB - Guidance updates available - version comparison server-side - Billing/quota warnings - enterprise only - Team-wide health (X repos need updates) - aggregate view

type DoctorResponse

type DoctorResponse struct {
	Issues    []DoctorIssue `json:"issues"`
	CheckedAt string        `json:"checked_at"` // RFC3339 timestamp
}

DoctorResponse represents the GET /api/v1/repo/{repo_id}/doctor response

type ImportNotification added in v0.3.0

type ImportNotification struct {
	TeamID   string          `json:"team_id"`
	Metadata json.RawMessage `json:"metadata"`
}

ImportNotification is the POST /api/v1/teams/{team_id}/context/import request body. Imports target a team context (not a project repo), so team_id is the primary identifier. The Metadata field is passed as-is (json.RawMessage) to avoid coupling the API package to the docMeta struct in cmd/ox.

type LedgerStatusResponse

type LedgerStatusResponse struct {
	Status      string `json:"status"`                 // "ready", "pending", "error"
	RepoURL     string `json:"repo_url"`               // git clone URL (empty if not ready)
	RepoID      int    `json:"repo_id"`                // GitLab project ID (internal use)
	CreatedAt   string `json:"created_at"`             // RFC3339 timestamp
	Message     string `json:"message,omitempty"`      // optional status message
	Visibility  string `json:"visibility,omitempty"`   // "public" or "private" (new field, may be empty on older servers)
	AccessLevel string `json:"access_level,omitempty"` // "member" or "viewer" (new field, may be empty on older servers)
}

LedgerStatusResponse represents the GET /api/v1/repos/{repo_id}/ledger-status response.

func (*LedgerStatusResponse) IsReadOnly

func (r *LedgerStatusResponse) IsReadOnly() bool

IsReadOnly returns true if the user has viewer (read-only) access.

type MergeRepoRequest added in v0.3.0

type MergeRepoRequest struct {
	RepoMarkers map[string]json.RawMessage `json:"repo_markers"` // filename -> marker JSON
}

MergeRepoRequest represents POST /api/v1/repo/{repo_id}/merge

type MergeRepoResponse added in v0.3.0

type MergeRepoResponse struct {
	Canonical string        `json:"canonical_repo_id"`  // the winning repo_id
	Merged    []string      `json:"merged_repo_ids"`    // repo_ids that were marked as merged
	Redirect  *RedirectInfo `json:"redirect,omitempty"` // redirect info (also in header)
}

MergeRepoResponse represents the merge API response

type QueryLatency added in v0.3.0

type QueryLatency struct {
	Embed  int64 `json:"embed"`
	Search int64 `json:"search"`
	Total  int64 `json:"total"`
}

QueryLatency tracks latency of sub-operations.

type QueryRequest added in v0.3.0

type QueryRequest struct {
	Query     string   `json:"query"`
	Mode      string   `json:"mode,omitempty"`       // "hybrid", "knn", "bm25" (default: hybrid)
	K         int      `json:"k,omitempty"`          // number of results (default: 10, max: 100)
	Teams     []string `json:"teams"`                // team IDs to search team-context indexes
	Repos     []string `json:"repos"`                // repo IDs to search ledger indexes
	AgentID   string   `json:"agent_id,omitempty"`   // querying agent instance (e.g. "Oxa7b3")
	AgentType string   `json:"agent_type,omitempty"` // querying agent type (e.g. "claude-code")
}

QueryRequest represents the POST /api/v1/query request body.

type QueryResponse added in v0.3.0

type QueryResponse struct {
	Results   []QueryResult `json:"results"`
	LatencyMs QueryLatency  `json:"latency_ms"`
}

QueryResponse represents the POST /api/v1/query response.

type QueryResult added in v0.3.0

type QueryResult struct {
	Score      float64 `json:"score"`
	Text       string  `json:"text"`
	DocType    string  `json:"doc_type"`
	FilePath   string  `json:"file_path"`
	SourceType string  `json:"source_type"`
	SourceID   string  `json:"source_id"`
	CreatedAt  string  `json:"created_at,omitempty"`
}

QueryResult is a single search result.

type RedirectConfig

type RedirectConfig struct {
	RepoID string `json:"repo_id,omitempty"`
	TeamID string `json:"team_id,omitempty"`
}

RedirectConfig contains the new config values to apply

type RedirectInfo

type RedirectInfo struct {
	Repo   *RedirectMapping `json:"repo,omitempty"`
	Team   *RedirectMapping `json:"team,omitempty"`
	Config *RedirectConfig  `json:"config,omitempty"`
}

RedirectInfo contains redirect information from the server when repos or teams have been merged

func ParseRedirectHeader

func ParseRedirectHeader(header http.Header) *RedirectInfo

ParseRedirectHeader parses X-Sageox-Redirect header if present Returns nil if header is not present or cannot be parsed

type RedirectMapping

type RedirectMapping struct {
	From string `json:"from"`
	To   string `json:"to"`
}

RedirectMapping represents a from -> to ID mapping

type RepoClient

type RepoClient struct {
	// contains filtered or unexported fields
}

RepoClient handles API communication with the SageOx repo endpoints

func NewRepoClient

func NewRepoClient() *RepoClient

NewRepoClient creates a new repo API client using the global default endpoint.

CAUTION: This should RARELY be used. It uses endpoint.Get() which ignores project config, so it will use the wrong endpoint for repos configured with non-default endpoints (e.g., enterprise or test environments).

Use NewRepoClientForProject(gitRoot) instead for operations within a repo context. Use NewRepoClientWithEndpoint(endpoint) when you have the endpoint explicitly.

func NewRepoClientForProject

func NewRepoClientForProject(gitRoot string) *RepoClient

NewRepoClientForProject creates a new repo API client using the endpoint from project config. This is the recommended way to create a client for repo-bound operations. It checks: SAGEOX_ENDPOINT env var > project config > default endpoint.

func NewRepoClientWithEndpoint

func NewRepoClientWithEndpoint(baseURL string) *RepoClient

NewRepoClientWithEndpoint creates a new repo API client with a specific endpoint. Use this when you already have the endpoint URL (e.g., from auth flow or config).

func (*RepoClient) DistillMemory added in v0.3.0

func (c *RepoClient) DistillMemory(teamID string, req *DistillRequest) (*DistillResponse, error)

DistillMemory calls POST /api/v1/teams/<teamID>/memory/distill to run server-side LLM distillation of accumulated observations.

func (*RepoClient) Endpoint

func (c *RepoClient) Endpoint() string

Endpoint returns the base URL this client is configured for

func (*RepoClient) GetDoctorIssues

func (c *RepoClient) GetDoctorIssues(repoID string) (*DoctorResponse, error)

GetDoctorIssues calls GET /api/v1/repo/{repo_id}/doctor for cloud diagnostics Returns nil, nil if API unavailable (graceful degradation for offline mode)

func (*RepoClient) GetLedgerStatus

func (c *RepoClient) GetLedgerStatus(repoID string) (*LedgerStatusResponse, error)

GetLedgerStatus fetches ledger provisioning status from the cloud API.

Response status values:

  • "ready": Ledger is provisioned and RepoURL is available for cloning
  • "pending": Ledger is being provisioned, caller should retry later
  • "error": Provisioning failed, check Message for details

The repoID parameter is the SageOx repo identifier (UUID) from project config. The returned RepoID is the GitLab project ID (used internally by server).

Returns ErrLedgerNotFound if no ledger exists for this repo. Returns ErrUnauthorized if authentication fails.

func (*RepoClient) GetRepoDetail

func (c *RepoClient) GetRepoDetail(repoID string) (*RepoDetailResponse, error)

GetRepoDetail calls GET /api/v1/cli/repos/{repo_id} to fetch repo detail. Returns visibility, access level, ledger status, and accessible team contexts. Works for both members (full access) and non-members on public repos (viewer access).

Returns ErrForbidden if the user is not a member and the repo is private. Returns ErrUnauthorized if authentication fails. Returns nil, nil if the endpoint returns 404 (server hasn't implemented this endpoint yet).

func (*RepoClient) GetRepos

func (c *RepoClient) GetRepos() (*ReposResponse, error)

GetRepos calls GET /api/v1/cli/repos to fetch user's team context repos. This is user-scoped and returns all team contexts the user has access to. For ledger URLs, use GetLedgerStatus() which is project-scoped. Requires authentication. Returns PAT, repo URLs, and token expiration.

func (*RepoClient) GetTeamInfo

func (c *RepoClient) GetTeamInfo(teamID string) (*TeamInfoResponse, error)

GetTeamInfo calls GET /api/v1/teams/{id} to fetch team information including the team context repo URL and credentials. Requires authentication. Returns nil, nil if team not found (404).

func (*RepoClient) MergeRepo added in v0.3.0

func (c *RepoClient) MergeRepo(repoID string, markers map[string]json.RawMessage) (*MergeRepoResponse, *RedirectInfo, error)

MergeRepo calls POST /api/v1/repo/{repo_id}/merge to notify the server about duplicate registration resolution. This is best-effort for server-side visibility and bookkeeping — the local cleanup is authoritative. Gracefully handles 404 (endpoint not yet deployed) by returning nil, nil, nil.

func (*RepoClient) NotifyImport added in v0.3.0

func (c *RepoClient) NotifyImport(teamID string, metadata any) error

NotifyImport sends a fire-and-forget notification about a new document import. Imports target a team context, so teamID identifies where the document lives. The metadata argument should be JSON-marshalable (typically the docMeta struct). Returns nil on network error, 404, or any non-2xx — never fails the caller's operation.

func (*RepoClient) NotifyUninstall

func (c *RepoClient) NotifyUninstall(repoID, repoSalt string) error

NotifyUninstall calls POST /api/v1/repo/{repo_id}/uninstall to notify server of local uninstall. Requires authentication - the server validates the user is a team member with permission to trigger uninstallation workflows. Returns errors so callers can provide user feedback. The repo_salt (first commit hash) provides additional verification.

func (*RepoClient) Query added in v0.3.0

func (c *RepoClient) Query(req *QueryRequest) (*QueryResponse, error)

Query calls POST /api/v1/query to perform semantic search over team context and ledger data. Requires authentication. Returns search results with relevance scores.

func (*RepoClient) RegisterRepo

func (c *RepoClient) RegisterRepo(req *RepoInitRequest) (*RepoInitResponse, error)

RegisterRepo calls POST /api/v1/repo/init Returns (response, error) - error is nil if call succeeds (even for 4xx/5xx) Gracefully handles 404 (endpoint not yet deployed) by returning nil, nil

func (*RepoClient) WithAuthToken

func (c *RepoClient) WithAuthToken(token string) *RepoClient

WithAuthToken sets the auth token for authenticated requests

type RepoDetailLedger

type RepoDetailLedger struct {
	Status  string `json:"status"`            // "ready", "pending", "error"
	RepoURL string `json:"repo_url"`          // git clone URL (empty if not ready)
	Message string `json:"message,omitempty"` // status message for pending/error
}

RepoDetailLedger is the ledger section of the repo detail response.

type RepoDetailResponse

type RepoDetailResponse struct {
	Visibility   string                  `json:"visibility"`    // "public" or "private"
	AccessLevel  string                  `json:"access_level"`  // "member" or "viewer"
	Ledger       *RepoDetailLedger       `json:"ledger"`        // null if not provisioned
	TeamContexts []RepoDetailTeamContext `json:"team_contexts"` // empty if none accessible
}

RepoDetailResponse represents GET /api/v1/cli/repos/{repo_id}. Returns repo visibility, user access level, ledger status, and accessible team contexts. Works for both members and non-members on public repos.

func (*RepoDetailResponse) IsReadOnly

func (r *RepoDetailResponse) IsReadOnly() bool

IsReadOnly returns true if the user has viewer (read-only) access.

type RepoDetailTeamContext

type RepoDetailTeamContext struct {
	TeamID      string `json:"team_id"`        // team_xxx
	Name        string `json:"name"`           // display name
	Slug        string `json:"slug,omitempty"` // kebab-case team slug
	Visibility  string `json:"visibility"`     // "public" or "private"
	AccessLevel string `json:"access_level"`   // "member" or "viewer"
	RepoURL     string `json:"repo_url"`       // git clone URL
}

RepoDetailTeamContext is a team context in the repo detail response.

func (RepoDetailTeamContext) StableID

func (r RepoDetailTeamContext) StableID() string

StableID returns the stable team identifier (team_xxx) for path construction and lookups.

type RepoFingerprint

type RepoFingerprint struct {
	// FirstCommit is the hash of the initial commit (same as repo_salt).
	FirstCommit string `json:"first_commit"`

	// MonthlyCheckpoints maps "YYYY-MM" to the first commit hash of that month.
	MonthlyCheckpoints map[string]string `json:"monthly_checkpoints"`

	// AncestrySamples contains commit hashes at power-of-2 intervals.
	AncestrySamples []string `json:"ancestry_samples"`

	// RemoteHashes contains salted SHA256 hashes of normalized remote URLs.
	RemoteHashes []string `json:"remote_hashes,omitempty"`
}

RepoFingerprint holds repository identity fingerprint data for detecting identical or related repositories across teams. Enables the API to suggest team merges when multiple teams are working on the same codebase.

type RepoInfo

type RepoInfo struct {
	Name   string `json:"name"`              // e.g., "acme-corp-team-context"
	URL    string `json:"url"`               // git clone URL
	Type   string `json:"type"`              // "team-context" (ledgers use separate API)
	TeamID string `json:"team_id,omitempty"` // team_xxx (present for team-context repos)
	Slug   string `json:"slug,omitempty"`    // kebab-case team slug (server-provided)
}

RepoInfo represents a single git repository from the server. NOTE: This API only returns team context repos, not ledgers. Use GetLedgerStatus() for ledger URLs (project-scoped).

func (RepoInfo) StableID

func (r RepoInfo) StableID() string

StableID returns the stable team identifier (team_xxx) for path construction and lookups.

type RepoInitRequest

type RepoInitRequest struct {
	RepoID           string           `json:"repo_id"`                      // Required: prefixed UUIDv7
	Type             string           `json:"type"`                         // Required: "git" or "svn"
	InitAt           string           `json:"init_at"`                      // Required: RFC3339 timestamp
	Name             string           `json:"name,omitempty"`               // Optional: display name (e.g. "sageox/ox")
	Teams            []string         `json:"teams,omitempty"`              // Optional: team IDs to associate repo with
	RepoSalt         string           `json:"repo_salt,omitempty"`          // Optional: initial commit hash
	RepoRemoteHashes []string         `json:"repo_remote_hashes,omitempty"` // Optional: salted hashes
	Fingerprint      *RepoFingerprint `json:"fingerprint,omitempty"`        // Optional: repo identity fingerprint
	Identities       any              `json:"identities,omitempty"`         // Optional: resolved user identities (identity.ResolvedIdentities)
	IsPublic         bool             `json:"is_public,omitempty"`          // Optional: prevents fork merging
	CreatedByEmail   string           `json:"created_by_email,omitempty"`   // Optional: git user email (backward compat)
	CreatedByName    string           `json:"created_by_name,omitempty"`    // Optional: git user name (backward compat)
}

RepoInitRequest represents the POST /api/v1/repo/init request

type RepoInitResponse

type RepoInitResponse struct {
	RepoID           string `json:"repo_id"`
	TeamID           string `json:"team_id"`
	WebBaseURL       string `json:"web_base_url,omitempty"`      // web dashboard base URL (for enterprise endpoints)
	ExistingRepoID   string `json:"existing_repo_id,omitempty"`  // set when dedup matched a different repo_id
	DuplicateWarning string `json:"duplicate_warning,omitempty"` // human-readable warning for CLI display
}

RepoInitResponse represents the POST /api/v1/repo/init response

type RepoMarkerData

type RepoMarkerData struct {
	RepoID   string `json:"repo_id"`
	RepoSalt string `json:"repo_salt"`
	Endpoint string `json:"endpoint"`
	// TODO: Remove after 2026-01-31 - legacy field support
	APIEndpoint string `json:"api_endpoint"` // deprecated: use Endpoint
}

RepoMarkerData holds parsed data from a .repo_* marker file

func ReadFirstRepoMarker

func ReadFirstRepoMarker(sageoxDir string) (*RepoMarkerData, error)

ReadFirstRepoMarker reads the first .repo_* marker file found in the sageox directory. Returns the parsed marker data or nil if no marker found. This is useful for getting repo_id and repo_salt before uninstall.

func (*RepoMarkerData) GetEndpoint

func (m *RepoMarkerData) GetEndpoint() string

GetEndpoint returns the endpoint, preferring new field over legacy

type RepoUninstallRequest

type RepoUninstallRequest struct {
	RepoSalt string `json:"repo_salt"` // first commit hash for authentication
}

RepoUninstallRequest represents the POST /api/v1/repo/{repo_id}/uninstall request

type ReposResponse

type ReposResponse struct {
	Token     string              `json:"token"`      // PAT for git operations
	ServerURL string              `json:"server_url"` // GitLab server URL
	Username  string              `json:"username"`   // GitLab username
	ExpiresAt time.Time           `json:"expires_at"` // Token expiration
	Repos     map[string]RepoInfo `json:"repos"`      // Repos indexed by name
	Teams     []TeamMembership    `json:"teams"`      // User's team memberships
}

ReposResponse represents the GET /api/v1/cli/repos response. This API returns team context repos only (user-scoped). Ledger URLs are fetched separately via GET /api/v1/repos/{repo_id}/ledger-status (project-scoped).

func (*ReposResponse) TeamMembershipsFromRepos

func (r *ReposResponse) TeamMembershipsFromRepos() []TeamMembership

TeamMembershipsFromRepos derives team memberships from the repos map. Each repo with type "team-context" represents a team the user belongs to. Falls back to the Teams array if populated.

type TeamInfoResponse

type TeamInfoResponse struct {
	ID       string `json:"id"`                  // team ID (team_xxx)
	Name     string `json:"name"`                // display name
	Slug     string `json:"slug,omitempty"`      // URL-friendly name
	RepoURL  string `json:"repo_url,omitempty"`  // team context git repo URL
	GitToken string `json:"git_token,omitempty"` // token for git operations
}

TeamInfoResponse represents the GET /api/v1/teams/{id} response

type TeamMembership

type TeamMembership struct {
	ID   string `json:"id"`             // team_xxx
	Name string `json:"name"`           // display name
	Slug string `json:"slug,omitempty"` // kebab-case team slug
	Role string `json:"role"`           // "owner", "admin", "member"
}

TeamMembership represents a team the user belongs to

Jump to

Keyboard shortcuts

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