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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 0 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,3 @@ jobs:
- run: go build ./...

- run: go vet ./...

# Includes the golden files that pin every command and request.
- run: go test ./...

# The naming tests read the spec, which is built from the committed collections.
- run: cd codegen && python3 extract_api_spec.py && cd .. && python3 -m unittest discover -s codegen -p 'test_*.py'
10 changes: 0 additions & 10 deletions .github/workflows/update-collections.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,6 @@ jobs:
- name: Build and vet
run: make check

# The command and request golden files change with the collections; the
# PR diff shows each published command that changed.
- name: Update golden files
run: make golden

# CI does not run on a PR this workflow pushes with GITHUB_TOKEN, so the tests
# run here: a refresh that breaks a catalog binding fails before a PR opens.
- name: Run tests
run: make test

- name: Generate changelog
run: python3 codegen/diff_spec.py /tmp/old_spec.json codegen/api_spec.json > /tmp/spec_diff.md

Expand Down
46 changes: 11 additions & 35 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,33 @@ A CLI for Webex Admin, Calling, Contact Center, Devices, Meetings, and Messaging

## Project Layout

- Go CLI at repo root (`main.go`, `cmd/`, `internal/`), built on the Go SDK in `pkg/`
- `codegen/` — Python scripts that generate the operation table (`pkg/webexops`) from Postman collections
- `cmd/admin/`, `cmd/calling/`, `cmd/cc/`, `cmd/device/`, `cmd/meetings/`, `cmd/messaging/` — each area's `commands.go` builds its commands from the operation table at startup (`internal/apicmd`)
- Go CLI at repo root (`main.go`, `cmd/`, `internal/`)
- `codegen/` — Python scripts that generate area command packages from Postman collections
- `cmd/admin/`, `cmd/calling/`, `cmd/cc/`, `cmd/device/`, `cmd/meetings/`, `cmd/messaging/` — generated cobra commands (do not edit by hand)
- `cmd/*.go` — handwritten root/area/auth/config/MCP entrypoints
- `cmd/mcp/` — MCP server (stdio or loopback HTTP)
- `cmd/**/custom_*.go` — handwritten commands for operations the table cannot describe (multipart uploads, downloads, search)
- `testdata/golden/` — every command's flags and help, and the request each API command sends; `cli_golden_test.go` checks them
- `cmd/**/custom_*.go` — handwritten replacements for endpoints the generator cannot emit (multipart uploads, downloads, search)
- `extension/` — Claude Desktop DXT package (`make extension`)
- `skill/` — agent skill files; each area’s Command Reference is regenerated by codegen
- `site/` — Astro Starlight docs (GitHub Pages)

## Code Generation Pipeline

The generated `pkg/webexops/zz_*.go` files must not be edited by hand. Change `codegen/generate_cli.py`, `internal/apicmd` (how operations become commands), or a `custom_*.go` file instead.
Generated `*.go` files in the six area packages must not be edited by hand. Change `codegen/generate_cli.py` (or a `custom_*.go` file) instead.

```bash
# Full pipeline (download + generate operations + generate skills + build):
# Full pipeline (download + generate CLI + generate skills + build):
make refresh

# Regenerate from existing committed collections:
make codegen && make build

# Download collections only:
make download

# Go and codegen tests; rewrite the golden files after an intended change:
make test
make golden
```

`make codegen` runs `extract_api_spec.py`, `generate_cli.py`, and `generate_skills.py`.

The golden files pin the published CLI. A change to codegen or `internal/apicmd` that is meant to keep commands the same must pass `go test -run Golden .` unchanged; one that changes them needs `make golden` and a reviewed diff. The weekly workflow runs `make golden` after a collection refresh.

For command naming exceptions, edit `codegen/naming_overrides.py`; see
`docs/command-migration.md` for naming and compatibility conventions, and
`docs/command-inventory.md` for the reviewed refresh inventory. These route-based
Expand Down Expand Up @@ -86,40 +79,23 @@ gh release edit v0.X.0 --title "v0.X.0" --notes "## Improvements

## Key Files

- `cmd/root.go` — Root cobra command, global flags (token, org, output, debug, dry-run, paginate, 429 retries, region, cc-region)
- `cmd/root.go` — Root cobra command, global flags (token, org, output, debug, dry-run, paginate, 429 retries, region)
- `cmd/auth.go` — Auth subcommand (status, list, switch, set-org, clear-org)
- `cmd/login.go` / `cmd/logout.go` — OAuth PKCE and device login
- `cmd/mcp.go` / `cmd/mcp/` — MCP serve/stop, tools, resources, usage log
- `cmd/update.go` / `cmd/postinstall.go` / `cmd/skillupdate.go` — self-update and skill install
- `internal/auth/` — Token storage (OS keyring, credentials file fallback), OAuth, device login, org resolution
- `npm/` — npm wrapper package, staging, and publish scripts
- `internal/client/` — Request builder (JSON and multipart bodies), CLI pagination, and the CLI's use of `pkg/webexapi` with its process-wide flags; `SDK()` gives SDK packages a transport set up the same way
- `internal/client/` — HTTP client, request builder (JSON and multipart bodies), pagination, 429 Retry-After
- `internal/appconfig/` — Config file (`~/.webex-cli/config.json`)
- `internal/config/` — Process-local flags (token, org, debug, dry-run, retry, regions and hosts)
- `internal/config/` — Process-local flags (token, org, debug, dry-run, retry)
- `internal/output/` — JSON/table/csv/raw formatting
- `internal/audio/` — Binary download from pre-signed URLs (no Webex token)
- `internal/aiagent/` — The private-API notice, and `pkg/aiagent` with this run's login and hosts

### Go SDK (`pkg/`)

Other Go programs import these, and Webex Admin Studio is built on them. They hold no process-wide state and take a context on every call. Keep CLI concerns (flags, output, the config file) out of them.

- `pkg/webexapi/` — HTTP transport: 401 refresh, 429 Retry-After, structured errors, fail-closed list paging; `Hosts` for every data region and Contact Center data center; org ID conversion; the Contact Center pacer
- `pkg/webexops/` — Generated operation table: one exported `*Operation` per API call (route, parameters, body fields, paging, CLI names), and `Call`
- `pkg/resource/` — Object types as data: fields, JSON paths, write mode, and the operations (`Route`) each type uses. API facts only; presentation belongs to the program
- `pkg/catalog/` — The object types. A type is `Managed` (writable) only once its writes are proven on a live tenant; its tests check every route's operation, method and parameter bindings. How Studio names types in Terraform config lives in Studio, not here
- `pkg/webex/` — Reads and writes objects from their definitions
- `pkg/guard/` — The write protocol: conflict check, read-back, stop after a failure
- `pkg/changeset/` — The change set file format Webex Admin Studio imports and exports: types, validation, JSON Schema
- `pkg/aiagent/` — AI Agent Studio: agent token lookup, credential redaction, `Call`
- `pkg/fakeapi/` — The fake Webex the SDK's and Studio's tests run against

Live Webex behavior that the catalog encodes (full-object Contact Center PUTs, read-by-filter work types, Contact Center rate limits) is recorded in Webex Admin Studio's AGENTS.md, where it was found.
- `internal/aiagent/` — AI Agent Studio support for generated commands: agent runtime token lookup and response redaction
- `codegen/supplemental/` — Hand-maintained Postman collections for APIs not in Cisco's collections
- `codegen/extract_api_outline.py` — Postman → normalized CLI names
- `codegen/extract_api_spec.py` — Postman → enriched API spec (params, body fields)
- `codegen/generate_cli.py` — API spec → operation table (`pkg/webexops/zz_*.go`)
- `internal/apicmd/` — Builds `webex <area> <group> <command>` from the operation table
- `codegen/generate_cli.py` — API spec → Go cobra command files
- `codegen/generate_skills.py` — API spec → Command Reference blocks in `skill/<area>/SKILL.md`
- `codegen/diff_spec.py` — Changelog between two spec snapshots (used by the weekly workflow)

Expand Down
10 changes: 1 addition & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build download codegen refresh check golden test clean extension skill
.PHONY: build download codegen refresh check clean extension skill

build:
go build -o webex .
Expand All @@ -16,14 +16,6 @@ refresh: download codegen build
check: build
go vet ./...

test:
go test ./...
python3 -m unittest discover -s codegen -p 'test_*.py'

# Rewrite testdata/golden after an intended change to commands or requests.
golden:
go test -run Golden -update .

extension: build
cd extension && zip -r ../webex-mcp.dxt manifest.json icon.png
@echo "Built webex-mcp.dxt"
Expand Down
47 changes: 2 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,10 @@ webex auth switch <email> # Switch default user
webex auth set-org <org-id> # Set a persistent org override
webex auth clear-org # Clear the org override
webex auth export # Print the refresh token for $WEBEX_REFRESH_TOKEN
webex auth token # Print the access token
```

Token resolution order: `--token` flag > `$WEBEX_TOKEN` env var > `$WEBEX_REFRESH_TOKEN` env var > stored login.

`webex auth list` and `auth token` print JSON with `--output json`. `auth token --output json` includes the token's expiry, user, and organization. `webex auth token --min-ttl 30m` refreshes the token first if less than that time remains, for handing it to a long-running process; it needs a stored login.

### Headless and Unattended Use

Device login needs no local browser: `webex login` shows `Open <url> and enter <code>` (and a QR code on a terminal), and you approve from a browser on any device. With `--output json`, the URL, code, and expiry are printed as JSON on stdout so an agent can relay them. `WEBEX_LOGIN_MODE=device|browser` picks the flow without a flag. A custom integration needs the redirect URIs `https://oauth-helper-{a,r,k,d}.wbx2.com/helperservice/v1/actions/device/callback` for device login.
Expand Down Expand Up @@ -251,10 +248,6 @@ webex cc users list --output csv > users.csv
| `--debug` | Show HTTP request/response details |
| `--paginate` | Auto-paginate list results (ignored by commands that return one object) |
| `--dry-run` | Print write requests without executing them |
| `--region us\|ca\|eu\|eun\|in\|au` | Data region for region-specific hosts (Detailed Call History) |
| `--cc-region us1\|eu1\|eu2\|anz1\|ca1\|jp1\|sg1` | Contact Center data center, which selects the Contact Center and AI Agent Studio hosts (default `us1`; also `$WEBEX_CC_REGION` or `webex config set cc-region`) |

A Contact Center update (`PUT`) replaces the whole object: fields the body leaves out are cleared. Get the object first and send the full body back with your changes.

## Configuration

Expand All @@ -263,8 +256,6 @@ webex config set client-id <id> # Use custom OAuth client ID
webex config set client-secret <secret> # Use custom OAuth client secret
webex config set scopes <scopes> # Override OAuth scopes
webex config set read-only-scopes <scopes> # Override scopes for login --read-only
webex config set region <region> # Data region for Detailed Call History
webex config set cc-region <dc> # Contact Center data center (us1, eu1, ...)
webex config get client-id # View current value
```

Expand Down Expand Up @@ -303,7 +294,7 @@ And 2 MCP resources:
| `webex://commands` | JSON array of all available CLI commands with short descriptions |
| `webex://usage` | Last 50 raw lines of the usage log |

Rather than exposing fixed per-API tools, the dispatcher pattern lets AI clients invoke the full CLI surface via `webex_read` and `webex_write` — the command tree expands automatically as new commands are added. The `webex_write` description warns that a Contact Center update replaces the whole object, so agents send the full body.
Rather than exposing fixed per-API tools, the dispatcher pattern lets AI clients invoke the full CLI surface via `webex_read` and `webex_write` — the command tree expands automatically as new commands are added.

Auth is shared with the CLI — run `webex login` once and the MCP server uses the same stored credentials. Token refresh is handled automatically.

Expand Down Expand Up @@ -362,45 +353,11 @@ Run `npx -y @cloverhound/webex-cli agent-setup` and follow what it prints.

See the [docs](https://cloverhound.github.io/webex-cli/agent-skill/) for manual setup instructions.

## Go SDK

The CLI is built on a Go SDK in `pkg/`, which other programs import as `github.com/Cloverhound/webex-cli/pkg/...`. Webex Admin Studio is built on it too.

| Package | What it is |
|---|---|
| `webexapi` | HTTP transport: 401 refresh, 429 retry, structured errors, lists that fail instead of ending short, hosts for every region, org ID conversion, Contact Center pacing. No process-wide state; every call takes a context. |
| `webexops` | Every API operation as data, generated from Cisco's Postman collections: route, parameters, body fields, paging. `Call` sends one. |
| `resource` | Object types as data: fields, JSON paths, write mode, and the operations each type uses. |
| `catalog` | The object types, and which can be written. |
| `webex` | Reads and writes objects by type: lists that read items in parallel where a list leaves fields out, full or partial writes as the route needs. |
| `guard` | The write protocol: conflict check before a write, read-back after it, and stop after a failure. |
| `changeset` | The change set file format that Webex Admin Studio imports and exports. |
| `aiagent` | AI Agent Studio's private routes: agent token lookup and credential redaction. |
| `fakeapi` | A fake Webex for tests, with fault injection. |

```go
hosts, _ := webexapi.NewHosts("us1", "us")
api := &webexapi.Client{Token: func(context.Context) (string, error) { return token, nil }, MaxRetries: 3}

// One operation.
resp, err := webexops.Call(ctx, api, hosts, webexops.CCTeamGet, webexops.Args{
Path: map[string]string{"orgid": orgUUID, "id": teamID},
})

// Objects by type, with safe writes.
c := &webex.Client{API: api, Hosts: hosts, OrgUUID: orgUUID}
team, _ := catalog.ByName("cc_team")
g := &guard.Guard{API: c, Writable: true}
values, err := g.Update(ctx, team, key, before, after)
```

Each operation is an exported variable, so a program that uses one fails to build when a Postman refresh drops or renames it.

## Development

See [AGENTS.md](AGENTS.md) for project structure and development workflow.

API commands are built at startup from the operation table in `pkg/webexops`, which is **generated** from Postman collections — do not edit it by hand. Run `make refresh` to re-download collections, regenerate the table, update skill documentation, and rebuild. `testdata/golden/` records every command's flags and the request each one sends; run `make golden` after an intended change and review the diff. See the [code generation pipeline](AGENTS.md#code-generation-pipeline) for details.
Commands in `cmd/calling/`, `cmd/cc/`, and the other area packages are **generated** from Postman collections — do not edit by hand. Run `make refresh` to re-download collections, regenerate Go files, update skill documentation, and rebuild. See the [code generation pipeline](AGENTS.md#code-generation-pipeline) for details.

## License

Expand Down
Loading
Loading