Rewrite namecheap skill in Python to address PR review

Replace the Bash namecheap.sh with a stdlib-only Python CLI (namecheap.py)
that resolves all Copilot PR review feedback:

- Cache the public IP once per run instead of per request
- Build API requests in-process via urllib so the API key never appears
  in process argv or shell history
- Broaden multi-part TLD detection (co.uk, com.au, etc.) and document the
  limitation
- Allow setup to update existing stored credentials
- Stop soliciting the API key via chat; use terminal getpass or env vars
- Remove non-portable Bash constructs (inline local, grep -oP)

Also normalize domain casing, preserve MX priority 0, accept
case-insensitive record types, and handle mixed-case email-forwarding
attributes. Update SKILL.md and regenerate the skills README.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Bruno Borges
2026-05-29 15:23:06 -04:00
parent 65c205fb96
commit b839f3e71b
4 changed files with 721 additions and 944 deletions
+23 -24
View File
@@ -17,67 +17,66 @@ Before executing any API commands, verify credentials are configured:
1. **Check for existing config** — look for `~/.namecheap-api`
2. If not configured, guide the user through setup:
a. **Show public IP** — run `curl -s https://api.ipify.org` to display the user's public IP
a. **Show public IP** — run `python3 namecheap.py public-ip` to display the user's public IP
b. **Instruct IP whitelisting** — tell the user to go to https://ap.www.namecheap.com/settings/tools/apiaccess/, enable API (select ON), and whitelist the displayed IP
c. **Collect credentials** — use `ask_user` to get their Namecheap username, then their API key
d. **Save config**write credentials to `~/.namecheap-api` with `chmod 600`
e. **Validate** — run a test API call to confirm access works
c. **Have the user run setup themselves** — ask the user to run `python3 namecheap.py setup` directly **in their own terminal**. The script prompts for the username and reads the API key with a hidden prompt (`getpass`), writes `~/.namecheap-api` with `chmod 600`, and validates the connection. **Never ask the user to paste their API key into the chat, and never log, echo, or display the API key value.** If you cannot run an interactive terminal for the user, instruct them to run `setup` themselves, or to export `NAMECHEAP_API_USER` and `NAMECHEAP_API_KEY` as environment variables in their own shell — rather than collecting the secret via `ask_user`.
d. **Confirm**once the user reports setup succeeded, proceed with DNS operations.
### DNS Operations
Use the `namecheap.sh` script (bundled in this skill's directory) for all API interactions:
Use the `namecheap.py` script (bundled in this skill's directory) for all API interactions. It requires only Python 3 (standard library only — no `pip install` needed) and works the same on macOS, Linux, and Windows:
```bash
# Show public IP (for setup)
bash namecheap.sh public-ip
python3 namecheap.py public-ip
# Run setup flow
bash namecheap.sh setup
python3 namecheap.py setup
# List domains
bash namecheap.sh domains.getList
python3 namecheap.py domains.getList
# Get nameservers for a domain (shows if using Namecheap DNS or custom)
bash namecheap.sh domains.dns.getList --domain example.com
python3 namecheap.py domains.dns.getList --domain example.com
# Get DNS records for a domain
bash namecheap.sh domains.dns.getHosts --domain example.com
python3 namecheap.py domains.dns.getHosts --domain example.com
# Add a single record (preserves existing records)
bash namecheap.sh dns.addHost --domain example.com --type A --name www --address 1.2.3.4 --ttl 1800
python3 namecheap.py dns.addHost --domain example.com --type A --name www --address 1.2.3.4 --ttl 1800
# Remove a single record
bash namecheap.sh dns.removeHost --domain example.com --type A --name www --address 1.2.3.4
python3 namecheap.py dns.removeHost --domain example.com --type A --name www --address 1.2.3.4
# Replace all records from a JSON file
bash namecheap.sh domains.dns.setHosts --domain example.com --hosts records.json
python3 namecheap.py domains.dns.setHosts --domain example.com --hosts records.json
# Switch to Namecheap default DNS
bash namecheap.sh domains.dns.setDefault --domain example.com
python3 namecheap.py domains.dns.setDefault --domain example.com
# Switch to custom nameservers
bash namecheap.sh domains.dns.setCustom --domain example.com --nameservers ns1.cloudflare.com,ns2.cloudflare.com
python3 namecheap.py domains.dns.setCustom --domain example.com --nameservers ns1.cloudflare.com,ns2.cloudflare.com
# Get email forwarding rules
bash namecheap.sh domains.dns.getEmailForwarding --domain example.com
python3 namecheap.py domains.dns.getEmailForwarding --domain example.com
# Set email forwarding (single rule)
bash namecheap.sh domains.dns.setEmailForwarding --domain example.com --mailbox info --forward-to user@gmail.com
python3 namecheap.py domains.dns.setEmailForwarding --domain example.com --mailbox info --forward-to user@gmail.com
# Set email forwarding (from JSON file)
bash namecheap.sh domains.dns.setEmailForwarding --domain example.com --forwards forwards.json
python3 namecheap.py domains.dns.setEmailForwarding --domain example.com --forwards forwards.json
# Create a child nameserver (glue record)
bash namecheap.sh domains.ns.create --domain example.com --nameserver ns1.example.com --ip 1.2.3.4
python3 namecheap.py domains.ns.create --domain example.com --nameserver ns1.example.com --ip 1.2.3.4
# Delete a child nameserver
bash namecheap.sh domains.ns.delete --domain example.com --nameserver ns1.example.com
python3 namecheap.py domains.ns.delete --domain example.com --nameserver ns1.example.com
# Get nameserver info
bash namecheap.sh domains.ns.getInfo --domain example.com --nameserver ns1.example.com
python3 namecheap.py domains.ns.getInfo --domain example.com --nameserver ns1.example.com
# Update nameserver IP
bash namecheap.sh domains.ns.update --domain example.com --nameserver ns1.example.com --old-ip 1.2.3.4 --ip 5.6.7.8
python3 namecheap.py domains.ns.update --domain example.com --nameserver ns1.example.com --old-ip 1.2.3.4 --ip 5.6.7.8
```
## Behavior
@@ -87,7 +86,7 @@ bash namecheap.sh domains.ns.update --domain example.com --nameserver ns1.exampl
- **Use `ask_user` to confirm destructive changes.** Before removing records or replacing all records with `setHosts`, confirm with the user.
- **The Namecheap `setHosts` API replaces ALL records.** Never call `domains.dns.setHosts` directly unless you have fetched all existing records first. Use `dns.addHost` and `dns.removeHost` for safe single-record operations — they handle the fetch-modify-write cycle internally.
- **Explain TTL in human terms.** When the user asks about TTL, explain that 1800 = 30 minutes, 3600 = 1 hour, etc.
- **Handle multi-part TLDs.** Domains like `example.co.uk` have SLD=example and TLD=co.uk. The script handles this automatically.
- **Handle multi-part TLDs.** Domains like `example.co.uk` have SLD=example and TLD=co.uk. The script recognizes a built-in list of common second-level suffixes (e.g. `co.uk`, `com.au`, `co.jp`, `com.br`). This list is best-effort and not a full public-suffix database — if a domain with an unlisted multi-part suffix returns a `2019166` ("Domain not found") error, the SLD/TLD split was likely wrong. In that case, confirm the registered domain with the user and report the limitation.
## Credential Storage
@@ -98,7 +97,7 @@ NAMECHEAP_API_USER="username"
NAMECHEAP_API_KEY="api-key-here"
```
This file must have `600` permissions (owner read/write only).
This file must have `600` permissions (owner read/write only). Alternatively, the script reads credentials from the `NAMECHEAP_API_USER` and `NAMECHEAP_API_KEY` environment variables, which take precedence over the file when both are set.
## Supported Record Types