Documentation
¶
Overview ¶
Package output renders one result three ways: for a person, for an agent, and for pasting into a pull request.
Index ¶
- Constants
- func Artifact(a *api.Artifact, f Format, quiet, colour bool, now time.Time) string
- func Authorizing(a Authorization, f Format, colour bool) string
- func Claimed(a *api.Artifact, f Format, quiet, colour bool, now time.Time) string
- func ConfigShow(v ConfigView, f Format, quiet, colour bool) string
- func ConfigWrote(key, value, path string, f Format, quiet, colour bool) string
- func DefaultWorkspace(name, path string, f Format, quiet, colour bool) string
- func Error(err error, f Format, quiet, colour bool) string
- func HumanBytes(n int64) string
- func Key(k *api.Key, f Format, quiet, colour bool) string
- func List(p *api.Page, l Listing, f Format, quiet, colour bool, now time.Time) string
- func MarkdownLink(a *api.Artifact, title string) string
- func MarkdownSurfacesFor(p Paste) string
- func RelativeExpiry(iso string, now time.Time) string
- func Removed(slug string, f Format, quiet, colour bool) string
- func Run(r *api.Run, f Format, quiet, colour bool) string
- func RunDetail(r *api.Run, f Format, quiet, colour bool) string
- func RunList(p *api.RunPage, l Listing, f Format, quiet, colour bool) string
- func StoredKey(l *Login, f Format, quiet, colour bool) string
- func Upload(r Result, f Format, quiet, colour bool, now time.Time) string
- func WorkspaceList(ws Workspaces, f Format, quiet, colour bool) string
- type Authorization
- type Breadcrumb
- type ConfigView
- type Envelope
- type Format
- type Listing
- type Login
- type Paste
- type Result
- type TakenDown
- type Workspaces
Constants ¶
const ( EmbedSurfaces = "GitHub, Linear, Notion — renders the image" // PlainSurfaces replaces EmbedSurfaces when there is no image to embed and // the markdown form is only a plain link, so the label stays honest. PlainSurfaces = "GitHub, Linear, Notion — plain link, no preview to embed" LinkSurfaces = "Slack, Basecamp — they unfurl the link themselves" )
EmbedSurfaces and LinkSurfaces name where each form belongs, so the choice never has to be guessed. Exported because the MCP server says the same thing.
const StatusFinished = "finished"
StatusFinished is the registry's word for a closed run. Its statuses are open and finished — nothing sets "running" — and the difference decides which breadcrumbs a run gets, so it is named here rather than spelled out at each place that branches on it, where the two spellings drifted apart once already.
Variables ¶
This section is empty.
Functions ¶
func Artifact ¶
Artifact renders a single artifact that already exists, for the commands that read or claim one rather than upload it.
The JSON envelope is deliberately the same as an upload's, so an agent parses one shape whichever command it ran. Only the human line differs: `uploads show` did not upload anything, and saying it did would be a lie.
func Authorizing ¶
func Authorizing(a Authorization, f Format, colour bool) string
Authorizing renders that notice.
It goes to stderr rather than stdout, which keeps the one document on stdout the receipt a program parses — but it means that a login which then *fails* leaves stderr carrying this notice ahead of the error envelope. That is unavoidable for a command with something to say before it knows its outcome, so it is said in the same format the rest of the output is in: prose for a person, a JSON document for a program, which makes stderr a stream of documents whose last one is the outcome rather than prose with JSON stuck to it.
The code is shown so it can be compared against the one on the page. That comparison is the whole reason a code exists — the slug collects the key and never appears in a browser, so what the page asks somebody to approve has to be identifiable as the request their own terminal made. It takes no quiet: --quiet drops an envelope, and there is no envelope here to drop. The `authorizing` key is not a wrapper around a result but the thing that says this is not one, so removing it would take away the only field a reader has to tell an interim notice from the verdict on the same stream.
func Claimed ¶
Claimed renders an artifact that has just been claimed. It is Artifact plus the one thing a claim leaves undone: a claimed upload belongs to a workspace but to no run, and `uploads attach` is the only way it ever gets one — the upload could not name a run when it was created, and claiming does not give it one. Saying so here is the difference between an agent knowing that and having to read the help to find out.
Silent when the artifact already has a run, which is what `claim --run` does in one step, and under --quiet, which asks for the record and nothing suggested. Markdown and url are the paste forms and are untouched.
A person gets the same thing, as a line under the artifact. The human output prints a run when there is one and simply omits the fact when there is not, so leaving this to the envelope would have meant the interactive default — the one a person actually sees — never learning that `uploads attach` exists, which is the whole gap this closes.
func ConfigShow ¶ added in v0.2.0
func ConfigShow(v ConfigView, f Format, quiet, colour bool) string
ConfigShow renders the effective configuration.
func ConfigWrote ¶ added in v0.2.0
ConfigWrote is the receipt for `config set` and `config unset`: which key, in which file, and what it now says. value is empty for an unset.
func DefaultWorkspace ¶ added in v0.2.0
DefaultWorkspace is the receipt for `workspaces use`: which stored key is now the machine-wide fallback, and where that pointer was written. It is only the fallback — a repo config or the environment still outranks it, and the crumb points at the command that says what actually resolves.
func Error ¶
Error renders a failure. Human output leads with the code and ends with the fix; JSON hands back the flattened body.
func HumanBytes ¶
HumanBytes renders a byte count the way the terminal output does.
func Key ¶
Key renders a verified API key: which key it is, and the workspace every call with it lands in — the fact worth confirming before an upload. There is no link to a key, so markdown and url fall back to the JSON envelope.
func MarkdownLink ¶
MarkdownLink is the paste-ready link for one artifact. The registry renders this itself — an image embed that clicks through to the card page, anything else a plain link to the card — so its version is used unless a title was asked for, which only the caller knows.
func MarkdownSurfacesFor ¶
MarkdownSurfacesFor is the honest label for a paste's markdown form: it only promises an image where the markdown actually embeds one.
An image embed links through to the card page, so it is spelled `[](card)` and no longer starts with the bang. The embed is looked for anywhere in the string rather than at the front, so this side does not start calling every image a plain link the day the registry changes what it wraps the embed in. A label cannot counterfeit one: the escaper turns a `[` in a filename into `\[`.
func RelativeExpiry ¶
RelativeExpiry turns an RFC 3339 timestamp into "expires in 24h".
func Removed ¶
Removed renders a completed takedown. There is no link left to paste, so markdown and url fall back to the JSON envelope.
func RunDetail ¶
RunDetail renders one run and everything recorded on it. Unlike Run, which reports what just happened to one, this is the whole record — the metadata included, since a run is where all of it lives and the registry keeps none on the artifacts themselves.
func RunList ¶
RunList renders a page of a workspace's runs, newest first. There is no link to a run, so markdown and url fall back to the JSON envelope.
func WorkspaceList ¶ added in v0.2.0
func WorkspaceList(ws Workspaces, f Format, quiet, colour bool) string
WorkspaceList renders the store. There is no link to a workspace, so markdown and url fall back to the JSON envelope.
Types ¶
type Authorization ¶
type Authorization struct {
Code string `json:"code"`
Page string `json:"page"`
// Opened says whether a browser was asked to open the page, so a caller knows
// whether the person in front of it has to do that part themselves.
Opened bool `json:"opened"`
}
Authorization is what a browser login says about itself while it is still waiting: the code to confirm, and the page to confirm it on.
It is not wrapped in an Envelope, and that is deliberate. An envelope carries `ok`, which is a verdict on a command that has finished — and this is written before the command knows whether it worked at all. An agent that read `ok: true` here would take a login that has not happened yet for one that has, so the shape is different on purpose and says what it is instead.
type Breadcrumb ¶
type Breadcrumb struct {
// Action is the short verb phrase, for a menu or a log line.
Action string `json:"action"`
// Cmd is the whole command, ready to run once any <placeholder> in it has
// been substituted.
Cmd string `json:"cmd"`
// Description says what running it achieves, and what happens if it is not
// run when that is the point.
Description string `json:"description"`
}
Breadcrumb is one call left to make, spelled out well enough to run without consulting anything: what it would do, the command that does it with this result's own slugs and tokens already in it, and why it is worth doing.
The three fields are a contract, so all three are always present. An agent reading this decides on the description and pastes the cmd — a breadcrumb that leaves either to be worked out is one it has to go and read the help for, which is the cost this exists to remove.
Cmd carries real arguments, never placeholders, wherever the result knows them. Where it genuinely does not — the run to attach a freshly claimed upload to is the caller's to choose, the file a new run is to be fed is not a thing this side has ever seen — the value is angle-bracketed so it cannot be mistaken for one. An angle-bracketed word is to be substituted before the command is run, and never pasted into a shell as it stands: `<` and `>` are redirection there, so a verbatim paste runs something other than what was suggested, and usually writes a file named after the placeholder.
The share crumb is the one whose Cmd is not a krowk command, for the reason given where it is built: there is no portable command for handing a link to a person, so it carries the link itself.
func AttachCrumb ¶
func AttachCrumb(a *api.Artifact) Breadcrumb
AttachCrumb is the command that puts a claimed upload under a run. The run is the one argument this side cannot fill in: the caller holding the claim token is the one that knows which run the upload came from, and guessing at one would be a command that fails.
func ClaimCrumb ¶
func ClaimCrumb(a *api.Artifact) Breadcrumb
ClaimCrumb is the command that keeps one anonymous upload, with its own token already in it. Exported because it is the breadcrumb the human format prints too, and the two must not drift into quoting different commands.
type ConfigView ¶ added in v0.2.0
type ConfigView struct {
Workspace string `json:"workspace,omitempty"`
// Sources says which layer each value came from, keyed by config key.
Sources map[string]string `json:"sources,omitempty"`
GlobalPath string `json:"global_path"`
// RepoPath is empty outside a git repository, where there is no repo layer
// to write to.
RepoPath string `json:"repo_path,omitempty"`
}
ConfigView is the effective configuration as `krowk config show` reports it: each value alongside which layer said so, and where the two files live — including the one that does not exist yet, because that is where `config set` would write.
type Envelope ¶
type Envelope struct {
OK bool `json:"ok"`
Data any `json:"data,omitempty"`
Paste *Paste `json:"paste,omitempty"`
Summary string `json:"summary,omitempty"`
Breadcrumbs []Breadcrumb `json:"breadcrumbs,omitempty"`
Error map[string]any `json:"error,omitempty"`
}
Envelope wraps every JSON result.
type Listing ¶
type Listing struct {
// Run names the run the page was scoped to, and is empty for a whole
// workspace's listing.
Run string
// Limit is the caller's --limit, or zero when it did not pass one. Zero is
// how the flag arrives unset, and is also what the registry reads as "use the
// default", so it is the honest way to say "the caller chose nothing".
Limit int
}
Listing is what scoped the page being rendered, so the command for the next one can be the same query with only the cursor moved.
type Login ¶
type Login struct {
Path string `json:"path"`
Confirmed bool `json:"confirmed"`
KeyID string `json:"key_id,omitempty"`
Workspace string `json:"workspace,omitempty"`
// Reason says why the registry did not confirm the key, and is empty when it
// did.
Reason string `json:"reason,omitempty"`
// Shadowed says KROWK_TOKEN is set, which outranks the file this login just
// wrote. Without it the receipt would name a workspace the next upload does not
// land in — the one fact a login is a receipt for, wrong.
Shadowed bool `json:"shadowed_by_env,omitempty"`
}
Login is the result of storing a key: where it landed, and whether the registry confirmed it on the way in.
Confirmed is the field worth reading. A login that could not reach the registry still stores the token, so "it worked" and "it is going to work" are different answers, and an agent capturing stdout has no other way to tell them apart. There is no link to a login, so markdown and url fall back to the JSON envelope.
type Paste ¶
type Paste struct {
// Markdown embeds the image and links through to the artifact. This is
// the form for GitHub, Linear and Notion.
Markdown string `json:"markdown"`
// URL is the bare link, for Slack and Basecamp, which unfurl it themselves.
URL string `json:"url"`
}
Paste is one upload in the two forms its destinations need. There is no single paste-ready string: GitHub does not build preview cards for third-party links, so only the image embed shows the artifact there, while Slack renders no markdown image embeds at all and unfurls a bare URL into a card of its own. So the CLI carries both and says which is which.
type Result ¶
type Result struct {
Artifacts []*api.Artifact `json:"artifacts"`
Run *api.Run `json:"run,omitempty"`
Notes []string `json:"notes,omitempty"`
// Title is the label the caller asked for, kept so markdown output can use
// it in place of a filename.
Title string `json:"title,omitempty"`
}
Result is what one upload command produced: an artifact per file, and the run they were grouped under when there was one.
Notes carry what the caller should know that is not a failure — chiefly that a keyless upload had nowhere to put the run metadata it was given.
type TakenDown ¶
TakenDown is what a takedown leaves to report. The registry answers 204 and there is no artifact left to render — a url and markdown naming bytes that are gone would be a lie — so the slug and the fact are the whole result.
The fact is its own field rather than a `state`, because the API's `state` says whether an upload landed (pending, ready) and a tombstone keeps whichever it had. Spelling a takedown as a state would make the two disagree.
type Workspaces ¶ added in v0.2.0
type Workspaces struct {
Stored []api.WorkspaceKey `json:"stored"`
// Resolved is the workspace uploads from this directory land in, and Source
// says which layer decided it — a flag, the environment, a config file, or
// the store's own default. Empty when nothing resolves, which is the
// anonymous case.
Resolved string `json:"resolved,omitempty"`
Source string `json:"source,omitempty"`
// Shadowed says KROWK_TOKEN is set, which outranks every stored key: the
// resolution below names the key krowk would have picked, and the
// environment is using a different one. Without this line the listing
// would answer the question wrongly with confidence.
Shadowed bool `json:"shadowed_by_env,omitempty"`
// KeyMissing says the resolved workspace cannot actually produce a key, so
// every upload here fails — the one qualification without which "uploads
// from here land in X" would be a lie told to whoever reads the listing.
KeyMissing bool `json:"key_missing,omitempty"`
}
Workspaces is what `krowk workspaces` reports: every key the machine holds, and which workspace a command run here would actually use. The two halves answer different questions — "what could I use" is the store, "what will be used" is the resolution — and an agent deciding whether to log in needs both.