Files
claudetools/.claude/skills/packetdial/SKILL.md
Mike Swanson 9b1c5c391d harness: fix py-vs-python3 doc gap — add py.sh resolver, repoint skill/command docs
The skill/command DOCS instructed Claude to run a bare `py ...`, which is the
Windows py-launcher — absent on Linux/macOS (exit 127, hit on GURU-KALI). A blind
py->python3 swap is wrong too: python3 is a broken MS Store shim on some Windows
boxes where `py` is the correct launcher.

Fix mirrors the resolution the .sh skill scripts already do:
- New .claude/scripts/py.sh: picks the interpreter that actually RUNS —
  identity.json python.command first, then py -> python3 -> python, each
  validated with `-c 'import sys'` so the MS Store stub is skipped. exec's it.
- Repointed all DOC invocations (10 files, ~70 sites) from `py ...` to
  `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ...` (incl. the `py -c` and
  `py -` heredoc forms in checkpoint.md / mailbox.md).
- Left the .sh skill scripts untouched — they already resolve py/python/python3.
- errorlog.md: marked the GURU-KALI entry RESOLVED.

Depends on CLAUDETOOLS_ROOT (seeded by ensure-settings-env.py); py.sh also
self-resolves the repo root via git/cwd as a fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 09:48:54 -07:00

116 lines
5.5 KiB
Markdown

---
name: packetdial
description: "Manage the ACG PacketDial/OITVOIP hosted VoIP via the NetSapiens API (pbx.packetdial.com). List/inspect domains, users, devices, DIDs, resellers; pull CDRs; provision domains/users/SIP/numbers (writes gated --confirm; read-only default). Triggers: packetdial, oitvoip, netsapiens, voip domain/user/extension, provision phone, add did, CDR. Live production PBX."
---
# PacketDial / NetSapiens (OITVOIP) Skill
Standalone CLI client for the NetSapiens SNAPsolution **API v2** that backs
ACG's hosted-VoIP offering through OITVOIP / PacketDial. Read-only by default;
every write (create / update / delete) is gated behind `--confirm`.
## The two hostnames (important)
| Host | What it is | API? |
|---|---|---|
| `voip.packetdial.com` | Customer-facing white-label portal / UC & fax dashboard (e.g. Cascades fax account **28598**). Login-gated UI. | **No** |
| `pbx.packetdial.com` | Reseller PBX platform — NetSapiens v44.4. | **Yes** — this skill targets it |
- API base: `https://pbx.packetdial.com/ns-api/v2`
- Token endpoint: `https://pbx.packetdial.com/ns-api/v2/tokens`
- Live OpenAPI spec: `https://pbx.packetdial.com/ns-api/webroot/openapi/openapi.json`
- Live Swagger UI: `https://pbx.packetdial.com/ns-api/openapi`
- Vendor docs: https://docs.ns-api.com/ (login) and https://voipdocs.io/oitvoip-access-platform-apis
## Credentials — ONE-TIME SETUP (not yet provisioned)
As of this skill's creation **no API key exists yet** — the vault entry
`msp-tools/oitvoip.sops.yaml` is empty/absent, so every command will fail with a
clear "No credentials found" error until you do this once:
1. Log into `pbx.packetdial.com` -> **Admin > API Keys** and create a
reseller-scoped key (prefix `nsr_`). If self-service key creation is not
available, reply to **Darwin Escaro (OITVOIP)** for reseller OAuth client
credentials.
2. Store it in the SOPS vault. Preferred (static bearer key):
```
# msp-tools/oitvoip.sops.yaml
credentials:
api_key: nsr_xxxxxxxxxxxxxxxx
```
Or, for OAuth2 password-grant credentials:
```
credentials:
client_id: <client id>
client_secret: <client secret>
username: <portal user@domain>
password: <portal password>
```
3. That's it — the client auto-detects which shape is present.
The client never hardcodes secrets. Resolution order: `PACKETDIAL_API_KEY` env
-> `PACKETDIAL_CLIENT_ID`+friends env -> vault `credentials.api_key` -> vault
OAuth fields. Env overrides exist for quick testing without touching the vault.
## Running the CLI
This machine's Python launcher is `py` (per identity.json); `python` / `python3`
also work. Run from the scripts dir so the two modules resolve.
```bash
cd C:/claudetools/.claude/skills/packetdial/scripts
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py status # API version + authenticated key identity
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py domains # list all domains
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py domain <domain> # one domain's config
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py users <domain> # users / extensions in a domain
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py user <domain> <user>
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py phones <domain> # SIP devices registered in a domain
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py dids <domain> # phone numbers (DIDs) on a domain
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py devices <domain> <user>
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py cdrs --domain <domain> --start 2026-06-01 --end 2026-06-02
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py resellers
```
## Writes (gated)
Every mutating command prints a `[DRY RUN]` line and exits non-zero unless you
pass `--confirm`. Bodies are raw JSON matching the NetSapiens v2 schema.
```bash
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py create-domain --body '{"domain":"acme","description":"Acme Inc"}' --confirm
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py create-user acme --body '{"user":"101","name-first-name":"Jane"}' --confirm
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py create-phone acme --body '{...}' --confirm
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py create-did acme --body '{"phonenumber":"15205551234"}' --confirm
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py update-user acme 101 --body '{"name-last-name":"Doe"}' --confirm
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py delete-user acme 101 --confirm
```
## Raw escape hatch (any of the 239 v2 paths)
The named commands cover the common surface; for anything else, hit the path
directly. Non-GET methods still require `--confirm`.
```bash
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py raw GET domains/acme/users/101/answerrules
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py raw POST domains/acme/users --body '{...}' --confirm
```
## Standard provisioning flow (new customer)
1. `create-domain` -> dial plan auto-generates
2. `create-user` per extension
3. `create-phone` per SIP device (MAC-provisioned)
4. `create-did` to attach DIDs and route them to users
5. Log the work back to the Syncro ticket
## Notes
- This is the LIVE production reseller PBX. A bad `create-domain` or
`delete-user` affects real customers — confirm the target domain first with a
read command before any write.
- CDR queries can be large; always pass `--start`/`--end` and a `--limit`.
- Reference detail (auth shapes, full endpoint inventory) lives in
`references/api.md`.