Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fencing, and stdin/keyring secrets.
[![ci](https://github.com/rnwolfe/rivr/actions/workflows/ci.yml/badge.svg)](https://github.com/rnwolfe/rivr/actions/workflows/ci.yml)
[![release](https://img.shields.io/github/v/release/rnwolfe/rivr?sort=semver)](https://github.com/rnwolfe/rivr/releases/latest)
[![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![Agent CLI Guidelines: Full](https://aclig.dev/badge/agent-cli-guidelines-full.svg)](https://aclig.dev/conformance/)
[![Agent CLI Guidelines v0.4.0: Full](https://aclig.dev/badge/agent-cli-guidelines-full.svg)](https://aclig.dev/conformance/)

![rivr demo](./demo/rivr.gif)

Expand Down Expand Up @@ -133,6 +133,23 @@ Global flags: `--json`/`--format json|plain|tsv`, `--select a,b.c`, `--limit N`,
`9` upstream / schema drift · `10` config · `11` unsupported by provider · `13` input required.
Full table: `rivr schema`.

## Conformance

rivr conforms to the [Agent CLI Guidelines](https://aclig.dev/) **v0.4.0** at the **Full**
level. The contract version is machine-verifiable from the binary — `rivr schema` emits a
`conformance` block, so an agent can confirm the standard it was built against without
trusting this README:

```json
{
"conformance": {
"level": "Full",
"spec": "agent-cli-guidelines",
"version": "0.4.0"
}
}
```

## Affiliate attribution (how rivr is funded)

Product deep links are tagged with an Amazon Associates ID by default — if a referred link
Expand Down
3 changes: 3 additions & 0 deletions internal/cli/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ func (c *SchemaCmd) Run(rt *Runtime) error {
"tool": "rivr",
"version": version.String(),
"schemaVersion": provider.SchemaVersion,
"conformance": map[string]any{
"spec": "agent-cli-guidelines", "version": version.Spec, "level": "Full",
},
"commands": nodeToMap(k.Model.Node),
"exit_codes": errs.Table(),
"providers": provider.Names(),
Expand Down
5 changes: 5 additions & 0 deletions internal/cli/testdata/schema.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@
}
]
},
"conformance": {
"level": "Full",
"spec": "agent-cli-guidelines",
"version": "0.4.0"
},
"exit_codes": {
"auth_required": 4,
"cancelled": 130,
Expand Down
5 changes: 5 additions & 0 deletions internal/update/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package update
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
Expand Down Expand Up @@ -65,11 +66,15 @@ func fetchLatest(ctx context.Context) (string, error) {
defer cancel()
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, apiURL(), nil)
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("User-Agent", "rivr-version-check") // GitHub's REST API rejects requests with no UA
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("release source: %s", resp.Status)
}
var body struct {
TagName string `json:"tag_name"`
}
Expand Down
20 changes: 20 additions & 0 deletions internal/update/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,23 @@ func TestCheckUsesEndpointAndCache(t *testing.T) {
t.Fatalf("force should re-fetch; hits=%d", hits)
}
}

// TestFetchSetsUserAgent guards the GitHub-API contract: requests with no User-Agent are
// rejected (HTTP 403), so the version check must always send one.
func TestFetchSetsUserAgent(t *testing.T) {
t.Setenv("XDG_STATE_HOME", t.TempDir())
var gotUA string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotUA = r.Header.Get("User-Agent")
w.Write([]byte(`{"tag_name":"v0.9.0"}`))
}))
defer srv.Close()
t.Setenv("RIVR_UPDATE_URL", srv.URL)

if _, _, err := Check(context.Background(), "v0.2.0", true, time.Unix(1_750_000_000, 0)); err != nil {
t.Fatal(err)
}
if gotUA == "" {
t.Fatal("version check sent no User-Agent; GitHub's REST API rejects such requests")
}
}
3 changes: 3 additions & 0 deletions internal/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package version

import "runtime/debug"

// Spec is the Agent CLI Guidelines version this tool conforms to (declared in `schema`).
const Spec = "0.4.0"

// version is a plain literal so -ldflags "-X .../version.version=vX" can override it.
// It MUST NOT be initialized from a function call (golang/go#64246).
var version = "dev"
Expand Down
Loading