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
3 changes: 2 additions & 1 deletion .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
- "install.sh"
- "install.ps1"
- "docs/**"
- "internal-docs/pages/**"
- "cmd/gendocs/**"
- "internal/cli/**"
- "scripts/prepare-pages-site.py"
Expand Down Expand Up @@ -54,7 +55,7 @@ jobs:
run: sudo chown -R "$(id -u):$(id -g)" _site

- name: Prepare human and agent docs
run: python3 scripts/prepare-pages-site.py --source docs --site _site --env-file docs/site.env
run: python3 scripts/prepare-pages-site.py --source docs --site _site --env-file internal-docs/pages/site.env

- name: Verify Pages installer freshness
shell: bash
Expand Down
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Set `AGORA_ALLOW_UPGRADE_IN_CI=1` only when a CI job intentionally needs to muta
### Added

- Add GitHub Pages publishing for generated CLI docs and route `agora open --target docs` to the human CLI docs site, `agora open --target docs-md` to the agent-facing raw Markdown docs under `/md/`, and `product-docs` to Agora product docs.
- Add `docs/site.env` and Pages build-time URL injection so staging docs can publish with different `CLI_DOCS_BASE_URL` / `CLI_DOCS_MD_BASE_URL` values while keeping predictable human and Markdown paths.
- Add `internal-docs/pages/site.env` and Pages build-time URL injection so staging docs can publish with different `CLI_DOCS_BASE_URL` / `CLI_DOCS_MD_BASE_URL` values while keeping predictable human and Markdown paths.
- Add a custom GitHub Pages theme for the human docs with responsive layout, system light/dark mode via `prefers-color-scheme`, and no manual theme toggle.
- Add `make docs-preview` for a Ruby/Jekyll local docs preview that builds with localhost-friendly paths, injects localhost docs URLs, and serves both the human site and `/md/` Markdown tree.
- Add global `--yes` / `-y` and `AGORA_NO_INPUT=1` support to accept defaults and suppress prompts.
Expand Down Expand Up @@ -164,7 +164,7 @@ Set `AGORA_ALLOW_UPGRADE_IN_CI=1` only when a CI job intentionally needs to muta
- `agora doctor` now suggests the exact shell-aware command for fixing a missing PATH entry. The `path_resolution` failure suggestion now reads `echo 'export PATH="<dir>:$PATH"' >> ~/.zshrc && source ~/.zshrc` for zsh, the equivalent for bash and `fish_add_path` for fish, the `setx PATH ...` form on Windows, and a generic `~/.profile` fallback for unknown shells. The doctor derives `<dir>` from the running binary's location, so the command is always copy-pasteable.
- Add a curated `Requirements` and `Verifying release artifacts` section to the README, plus links to `SECURITY.md`, `SUPPORT.md`, the new troubleshooting doc, and `docs/schema/envelope.v1.json` from the Docs index.
- Add a telemetry stub (`internal/cli/telemetry.go`) with the `telemetryClient` interface, default no-op sink, redaction helper, and `sentryClient` placeholder so the next release wires Sentry by adding the SDK + replacing one constant. The on/off contract (`agora telemetry`, `AGORA_SENTRY_ENABLED`, `DO_NOT_TRACK`) is fully wired; transport is a no-op until Sentry is connected.
- Add the proposal documents `docs/proposals/supply-chain-hardening.md`, `docs/proposals/ci-matrix-expansion.md`, and `docs/proposals/telemetry-sentry-wireup.md` for the next release.
- Add the proposal documents `internal-docs/proposals/supply-chain-hardening.md`, `internal-docs/proposals/ci-matrix-expansion.md`, and `internal-docs/proposals/telemetry-sentry-wireup.md` for the next release.

### Changed

Expand Down Expand Up @@ -208,7 +208,7 @@ Set `AGORA_ALLOW_UPGRADE_IN_CI=1` only when a CI job intentionally needs to muta
- README updates land in logical sections (Install requirements, Verifying releases, Docs index, Command Model additions, Troubleshooting redirect) without disturbing the marketing-first opening.
- `CONTRIBUTING.md` documents the branching model (`main` is releasable, topic branches off `main`), the commit-message convention, the optional DCO sign-off path, and the per-command example requirement for new commands.
- `docs/automation.md` adds a section documenting the `agora project env` raw-stdout exception and links the new envelope JSON Schema.
- `docs/proposals/` introduces a new directory for deferred-implementation proposals (supply-chain hardening, CI matrix expansion, Sentry wire-up). Each proposal carries a `status: proposed`, `target-release: next` front-matter so contributors can see what's planned without bisecting branches.
- `internal-docs/proposals/` introduces a new directory for deferred-implementation proposals (supply-chain hardening, CI matrix expansion, Sentry wire-up). Each proposal carries a `status: proposed`, `target-release: next` front-matter so contributors can see what's planned without bisecting branches.

## [0.1.9] - 2026-04-30

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ make docs-preview # optional: local Jekyll site + /md
Documentation work:

- Run `make docs-commands` after command-tree changes; CI uses `go run ./cmd/gendocs -check`.
- For GitHub Pages content, use `make docs-preview` (see `scripts/preview-pages-site.sh`). Published docs resolve `@@CLI_DOCS_*@@` and `@@CLI_INSTALL_*@@` tokens via `scripts/prepare-pages-site.py` and `docs/site.env` as documented in `docs/automation.md`.
- For GitHub Pages content, use `make docs-preview` (see `scripts/preview-pages-site.sh`). Published docs resolve `@@CLI_DOCS_*@@` and `@@CLI_INSTALL_*@@` tokens via `scripts/prepare-pages-site.py` and `internal-docs/pages/site.env` as documented in `docs/automation.md`.

Install `golangci-lint` **v1.64.8** (matches CI). CI builds it with `go install` against your toolchain; locally prefer:

Expand Down
3 changes: 0 additions & 3 deletions docs/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,3 @@ defaults:
path: ""
values:
layout: default

exclude:
- site.env
2 changes: 1 addition & 1 deletion docs/automation.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Use this guide for:
- Set `AGORA_AGENT=<tool-name>` in automated environments to explicitly label agent traffic in the API `User-Agent`. When unset, the CLI may infer a coarse label such as `cursor`, `claude-code`, `cline`, `windsurf`, `codex`, or `aider` from known agent environment markers. Set `AGORA_AGENT_DISABLE_INFER=1` to disable inference.
- Use `agora mcp serve` to expose local Agora CLI tools to MCP-capable agents. The full surface is exposed: `agora.version`, `agora.introspect`, `agora.auth.{status,logout}`, `agora.config.{path,get}`, `agora.telemetry.status`, `agora.upgrade.check`, `agora.project.{list,show,use,create,doctor,env,env_write}`, `agora.project.feature.{list,status,enable}`, `agora.project.webhook.{events,list,show,create,update,delete}`, `agora.quickstart.{list,create,env_write}`, and `agora.init`. Authentication is intentionally **not** exposed via MCP because OAuth requires an interactive browser; run `agora login` once on the host first.
- Use `agora open --target docs` for the human GitHub Pages docs and `agora open --target docs-md` for the agent-facing raw Markdown index. In CI/non-TTY runs the command defaults to URL-only output unless `--browser` is set. The Markdown tree is published under predictable `/md/` URLs, for example `/md/commands.md`, `/md/automation.md`, and `/md/error-codes.md`.
- Docs publishing reads `docs/site.env` for `CLI_DOCS_*` and `CLI_INSTALL_*` URL defaults; staging Pages builds can override those environment variables at workflow time without changing docs content. The resolved values are published as `/docs.env` for transparency.
- Docs publishing reads `internal-docs/pages/site.env` for `CLI_DOCS_*` and `CLI_INSTALL_*` URL defaults; staging Pages builds can override those environment variables at workflow time without changing docs content. The resolved values are published as `/docs.env` for transparency.
- The CLI maintains a short-lived on-disk completion cache for `agora project use <TAB>` under `<AGORA_HOME>/cache/projects.json`. The cache is only used for completions when a **local unexpired session exists** (`session.json` with a non-empty access token and a future `expiresAt`, when present), so Tab does not suggest stale project names after logout or local session expiry. The cache TTL is 5 minutes by default; override with `AGORA_PROJECT_CACHE_TTL_SECONDS=<seconds>` (set to `0` to disable). Cache files older than 24 h are pruned at every CLI startup. Set `AGORA_DISABLE_CACHE=1` to drop the cache on the next startup. The cache is invalidated automatically by `agora logout` and `agora project create` (the latter clears the file; it does not embed the new project until the next successful list fetch). To **force-refresh** the cached completion page, run `agora project list --refresh-cache` while authenticated; that command fetches the unfiltered first page used by completion and rewrites `projects.json` when it succeeds.

### CI auto-detect
Expand Down
4 changes: 1 addition & 3 deletions docs/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ key pattern (case-insensitive) and replaces the value with the literal
> **Status (current release):** the on/off contract below is fully
> wired. The transport (Sentry SDK) is not yet linked into the binary,
> so all telemetry calls are no-ops at runtime. The next release will
> wire Sentry per
> [`docs/proposals/telemetry-sentry-wireup.md`](proposals/telemetry-sentry-wireup.md);
> the surface and field schema will not change.
> wire Sentry; the surface and field schema will not change.

## Inspect or change the setting

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
## Task 0: Backend Confirmation Check

**Files:**
- Modify: `docs/superpowers/specs/2026-06-07-project-webhook-design.md`
- Modify: `internal-docs/specs/2026-06-07-project-webhook-design.md`

- [ ] **Step 1: Confirm event ID mapping before production behavior is wired**

Expand All @@ -45,7 +45,7 @@ eventIds uses ncs-events.items[].eventId.

- [ ] **Step 2: Record the confirmation in the design spec**

Edit `docs/superpowers/specs/2026-06-07-project-webhook-design.md` and replace the final sentence in the event API paragraph with:
Edit `internal-docs/specs/2026-06-07-project-webhook-design.md` and replace the final sentence in the event API paragraph with:

```markdown
Backend owner confirmation: config `eventIds` are populated from event `eventId`, not `eventType`.
Expand All @@ -56,7 +56,7 @@ Backend owner confirmation: config `eventIds` are populated from event `eventId`
Run:

```bash
git add docs/superpowers/specs/2026-06-07-project-webhook-design.md
git add internal-docs/specs/2026-06-07-project-webhook-design.md
git commit -m "docs: confirm webhook event id mapping"
```

Expand Down Expand Up @@ -360,7 +360,9 @@ Append these tests:

```go
func TestNormalizeWebhookEventsIgnoresChineseDisplayName(t *testing.T) {
resp := ncsEventListResponse{Items: []ncsEvent{{EventID: 1001, DisplayName: "Channel Created", DisplayNameCn: "频道创建", EventType: 7, Payload: `{"x":1}`}}}
resp := ncsEventListResponse{Items: []ncsEvent{
{EventID: 1001, DisplayName: "Channel Created", DisplayNameCn: "频道创建", EventType: 7, Payload: `{"x":1}`},
}}
got := normalizeWebhookEvents(resp)
if len(got) != 1 || got[0].Key != "channel-created" || got[0].ID != 1001 || got[0].EventType != 7 || got[0].Payload != `{"x":1}` {
t.Fatalf("unexpected normalized events: %#v", got)
Expand Down Expand Up @@ -724,7 +726,9 @@ func TestProjectWebhookUpdateReadMergePut(t *testing.T) {
defer api.server.Close()
project := buildFakeProject("demo", "prj_0001", "app_0001", "global")
api.projects[project.ProjectID] = &project
api.ncsConfigs["prj_0001/rtc"] = []fakeNCSConfig{{ConfigID: 42, URL: "https://old.example/webhook", URLRegion: "eu", Enabled: true, EventIDs: []int{1001}, UseIPWhitelist: false, Secret: "secret_123"}}
api.ncsConfigs["prj_0001/rtc"] = []fakeNCSConfig{
{ConfigID: 42, URL: "https://old.example/webhook", URLRegion: "eu", Enabled: true, EventIDs: []int{1001}, UseIPWhitelist: false, Secret: "secret_123"},
}
configHome := t.TempDir()
persistSessionForIntegration(t, configHome)
result := runCLI(t, bin, map[string]string{"XDG_CONFIG_HOME": configHome, "AGORA_CLI_BASE_URL": api.baseURL}, "project", "webhook", "update", "42", "--project", "demo", "--feature", "rtc", "--url", "https://new.example/webhook", "--json")
Expand Down Expand Up @@ -795,7 +799,9 @@ func TestProjectWebhookListRedactsAndShowWithSecretReveals(t *testing.T) {
defer api.server.Close()
project := buildFakeProject("demo", "prj_0001", "app_0001", "global")
api.projects[project.ProjectID] = &project
api.ncsConfigs["prj_0001/rtc"] = []fakeNCSConfig{{ConfigID: 42, URL: "https://example.com/webhook", URLRegion: "na", Enabled: true, EventIDs: []int{1001}, UseIPWhitelist: false, Secret: "secret_123"}}
api.ncsConfigs["prj_0001/rtc"] = []fakeNCSConfig{
{ConfigID: 42, URL: "https://example.com/webhook", URLRegion: "na", Enabled: true, EventIDs: []int{1001}, UseIPWhitelist: false, Secret: "secret_123"},
}
configHome := t.TempDir()
persistSessionForIntegration(t, configHome)

Expand Down Expand Up @@ -1062,7 +1068,10 @@ case "project webhook events":
}
case "project webhook list":
m := data.(map[string]any)
printBlock(out, "Webhooks", [][2]string{{"Project", asString(m["projectName"])}, {"Feature", asString(m["feature"])}})
printBlock(out, "Webhooks", [][2]string{
{"Project", asString(m["projectName"])},
{"Feature", asString(m["feature"])},
})
if items, ok := m["items"].([]webhookConfig); ok {
for _, item := range items {
fmt.Fprintf(out, "- %d %s %s enabled=%v\n", item.ConfigID, item.URL, item.URLRegion, item.Enabled)
Expand All @@ -1077,7 +1086,12 @@ case "project webhook show", "project webhook create", "project webhook update":
}
case "project webhook delete":
m := data.(map[string]any)
printBlock(out, "Webhook", [][2]string{{"Project", asString(m["projectName"])}, {"Feature", asString(m["feature"])}, {"Config ID", asString(m["configId"])}, {"Deleted", "true"}})
printBlock(out, "Webhook", [][2]string{
{"Project", asString(m["projectName"])},
{"Feature", asString(m["feature"])},
{"Config ID", asString(m["configId"])},
{"Deleted", "true"},
})
```

Add helper:
Expand All @@ -1093,7 +1107,17 @@ func printWebhookBlock(out io.Writer, m map[string]any) {
if len(keys) > 0 {
events = strings.Join(keys, ", ")
}
printBlock(out, "Webhook", [][2]string{{"Project", asString(m["projectName"])}, {"Feature", asString(m["feature"])}, {"Config ID", asString(cfg.ConfigID)}, {"URL", cfg.URL}, {"Events", events}, {"Delivery Region", renderWebhookDeliveryRegion(cfg.URLRegion)}, {"Enabled", asString(cfg.Enabled)}, {"Retry", asString(cfg.Retry)}, {"Secret", cfg.Secret}})
printBlock(out, "Webhook", [][2]string{
{"Project", asString(m["projectName"])},
{"Feature", asString(m["feature"])},
{"Config ID", asString(cfg.ConfigID)},
{"URL", cfg.URL},
{"Events", events},
{"Delivery Region", renderWebhookDeliveryRegion(cfg.URLRegion)},
{"Enabled", asString(cfg.Enabled)},
{"Retry", asString(cfg.Retry)},
{"Secret", cfg.Secret},
})
}
}
```
Expand Down
8 changes: 4 additions & 4 deletions internal/cli/open_targets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,24 +129,24 @@ func TestCLIDocsURLMatchesPagesWorkflow(t *testing.T) {
}
if !strings.Contains(string(body), "Prepare human and agent docs") ||
!strings.Contains(string(body), "scripts/prepare-pages-site.py") ||
!strings.Contains(string(body), "docs/site.env") {
!strings.Contains(string(body), "internal-docs/pages/site.env") {
t.Fatal("pages.yml does not prepare docs with the env-file driven Pages script")
}
}

func TestPagesSiteEnvDefaultsMatchOpenTargets(t *testing.T) {
repoRoot := findRepoRootForTest(t)
siteEnv := filepath.Join(repoRoot, "docs", "site.env")
siteEnv := filepath.Join(repoRoot, "internal-docs", "pages", "site.env")
body, err := os.ReadFile(siteEnv)
if err != nil {
t.Fatalf("could not read %s: %v", siteEnv, err)
}
content := string(body)
if !strings.Contains(content, "CLI_DOCS_BASE_URL="+strings.TrimSuffix(cliDocsURL, "/")) {
t.Fatalf("docs/site.env does not match cliDocsURL %q:\n%s", cliDocsURL, content)
t.Fatalf("internal-docs/pages/site.env does not match cliDocsURL %q:\n%s", cliDocsURL, content)
}
if !strings.Contains(content, "CLI_DOCS_MD_BASE_URL="+strings.TrimSuffix(cliDocsURL, "/")+"/md") {
t.Fatalf("docs/site.env does not match cliDocsMarkdownURL base %q:\n%s", cliDocsMarkdownURL, content)
t.Fatalf("internal-docs/pages/site.env does not match cliDocsMarkdownURL base %q:\n%s", cliDocsMarkdownURL, content)
}
}

Expand Down
6 changes: 3 additions & 3 deletions internal/cli/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
// Today this file ships a no-op default. The Sentry-backed sink is
// scaffolded as `sentryClient` below so the next release can flip the
// constructor over without changing the public API. See
// docs/proposals/telemetry-sentry-wireup.md for the wire-up plan.
// internal-docs/proposals/telemetry-sentry-wireup.md for the wire-up plan.
type telemetryClient interface {
// Enabled reports whether the underlying sink will actually emit.
Enabled() bool
Expand All @@ -46,7 +46,7 @@ type telemetryClient interface {
// agoraSentryDSN is the Sentry project DSN that the CLI ships with.
// Empty string disables Sentry transport entirely (the default until the
// Sentry SDK is wired in for the next release). When set, events go to the
// Agora CLI Sentry project (see docs/proposals/telemetry-sentry-wireup.md).
// Agora CLI Sentry project (see internal-docs/proposals/telemetry-sentry-wireup.md).
const agoraSentryDSN = ""

// initTelemetry returns the telemetry client appropriate for the current
Expand All @@ -73,7 +73,7 @@ func initTelemetry(configEnabled bool, env map[string]string, _ versionInformati
if agoraSentryDSN == "" {
// Sentry SDK not compiled in for this build. The wire-up is
// scheduled for the next release; until then this is the
// expected path. See docs/proposals/telemetry-sentry-wireup.md.
// expected path. See internal-docs/proposals/telemetry-sentry-wireup.md.
return noopTelemetry{}
}
return newSentryClient(agoraSentryDSN, env)
Expand Down
4 changes: 2 additions & 2 deletions scripts/prepare-pages-site.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

The Pages workflow renders the human site with Jekyll, then this script:

1. Reads docs/site.env for default published URLs.
1. Reads internal-docs/pages/site.env for default published URLs.
2. Lets workflow/job env vars override those defaults for staging builds.
3. Replaces URL placeholders in rendered site files.
4. Copies raw Markdown and MDC files to _site/md/ with the same replacements.
Expand Down Expand Up @@ -128,7 +128,7 @@ def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--source", default="docs", type=Path)
parser.add_argument("--site", default="_site", type=Path)
parser.add_argument("--env-file", default=Path("docs/site.env"), type=Path)
parser.add_argument("--env-file", default=Path("internal-docs/pages/site.env"), type=Path)
parser.add_argument("--repo-root", default=Path("."), type=Path)
args = parser.parse_args()

Expand Down
2 changes: 1 addition & 1 deletion scripts/preview-pages-site.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ CLI_DOCS_BASE_URL="http://localhost:${PORT}" \
CLI_DOCS_MD_BASE_URL="http://localhost:${PORT}/md" \
CLI_INSTALL_SH_URL="http://localhost:${PORT}/install.sh" \
CLI_INSTALL_PS1_URL="http://localhost:${PORT}/install.ps1" \
python3 scripts/prepare-pages-site.py --source docs --site "$SITE_DIR" --env-file docs/site.env
python3 scripts/prepare-pages-site.py --source docs --site "$SITE_DIR" --env-file internal-docs/pages/site.env

cat <<EOF

Expand Down
Loading