packetdial: finish resource wrapping (reads + gated writes across the platform)

Added read wrappers: addresses (E911), smsnumbers, blocked-numbers, moh, dialrules,
recording, transcriptions. Added gated write wrappers: DID update/delete, per-user device
CRUD, E911 address CRUD, contact CRUD, site create/update, auto-attendant create, SMS
number CRUD, block/unblock numbers, MoH TTS create/delete.

Verification: contact create→delete lifecycle verified on arizonacomputerguru (id field is
`unique-id`); reads for addresses/blocked-numbers/moh verified. Remaining writes are plumbed
per the OpenAPI spec [P] but not lifecycle-verified (test domain lacks the feature or needs a
special body) — SKILL.md marks each [V]/[P] and documents the gotchas (E911 pidflo via
addresses/validate; SMS not provisioned on test domain; number-filters add 202'd but didn't
persist; MoH file upload is multipart -> raw). Capability map + api.md history updated.
All writes --confirm-gated; anything unwrapped still reachable via `raw`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-22 08:05:06 -07:00
parent fec8b364d6
commit 1062a5ed44
4 changed files with 321 additions and 9 deletions

View File

@@ -63,6 +63,13 @@ bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py sites <domain> # m
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py autoattendants <domain> # IVR menus
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py contacts <domain> # shared address book
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py billing <domain> # limits + current usage counts
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py addresses <domain> # E911 addresses
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py smsnumbers <domain> # SMS numbers (needs SMS-provisioned domain)
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py blocked-numbers <domain># blocked-number filters
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py moh <domain> # music-on-hold entries
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py dialrules <domain> <dialplan>
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py recording <domain> <callid>
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py transcriptions <domain>
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
```
@@ -127,6 +134,45 @@ bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py raw GET domains/acme/users
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py raw POST domains/acme/users --body '{...}' --confirm
```
### Additional resource edits (gated)
All `--confirm`-gated. Verification legend: **[V]** lifecycle-verified on the
`arizonacomputerguru` test domain; **[P]** plumbed per the OpenAPI spec but not
lifecycle-verified (test domain lacks the feature / needs a special body) — exercise
on a real domain or via `raw` and confirm.
```bash
# DIDs — complete CRUD (create-did exists; add update/delete) [P]
ns.py update-did <domain> <number> --body '{"dial-rule-translation-destination-user":"101"}' --confirm
ns.py delete-did <domain> <number> --confirm
# per-user SIP devices [P]
ns.py create-device <domain> <user> --body '{"device":"sip:101a@<domain>"}' --confirm
ns.py update-device <domain> <user> <device> --body '{...}' --confirm
ns.py delete-device <domain> <user> <device> --confirm
# contacts (domain address book) [V]
ns.py create-contact <domain> --body '{"name-first-name":"Jane","name-last-name":"Doe","email":"j@x.com"}' --confirm
ns.py update-contact <domain> <unique-id> --body '{...}' --confirm # id = the contact's "unique-id"
ns.py delete-contact <domain> <unique-id> --confirm
# E911 addresses [P] (create needs address-formatted-pidflo -> POST .../addresses/validate first via raw)
ns.py create-address <domain> --body '{...}' --confirm
ns.py update-address <domain> <emergency-address-id> --body '{...}' --confirm
ns.py delete-address <domain> <emergency-address-id> --confirm
# sites / auto-attendants [P]
ns.py create-site <domain> --body '{...}' --confirm
ns.py update-site <domain> <site> --body '{...}' --confirm
ns.py create-autoattendant <domain> --body '{"attendant-name":"Main","user":"3000"}' --confirm
# SMS numbers [P] (needs SMS-provisioned domain)
ns.py create-smsnumber <domain> --body '{"number":"1520...","application":"..."}' --confirm
ns.py update-smsnumber <domain> <smsnumber> --body '{...}' --confirm
ns.py delete-smsnumber <domain> <smsnumber> --confirm
# blocked-number filters [P] (on the empty test domain add 202'd but didn't persist; verify on a live domain)
ns.py block-numbers <domain> --body '{"phone-numbers-to-reject":["15205550199"]}' --confirm
ns.py unblock-numbers <domain> --body '{"phone-numbers-to-reject":["15205550199"]}' --confirm
# music on hold (TTS create + delete; file UPLOAD is multipart -> use raw) [P]
ns.py create-moh <domain> --body '{"synchronous":"yes","script":"Thanks for holding"}' --confirm
ns.py delete-moh <domain> <index> --confirm
```
## API capability map (RTFM — spec v44.4.10, mapped 2026-06-22)
The live OpenAPI spec is **NetSapiens API v2 v44.4.10****239 paths / 354 operations**
@@ -136,15 +182,19 @@ The live OpenAPI spec is **NetSapiens API v2 v44.4.10** — **239 paths / 354 op
| Resource (`/domains/{domain}/…`) | Ops | Skill wrapper | Notes |
|---|---|---|---|
| `users` (+ `/devices`, answerrules, voicemail, contacts under a user) | R/W, biggest surface | `users`, `user`, `devices`, `create/update/delete-user` | per-user devices live at `users/{u}/devices` |
| `phonenumbers` (DIDs + dial-rule routing) | R/W | `dids`, `create-did` | |
| `phones` (SIP devices, MAC provisioning) | R/W | `phones`, `create-phone` | |
| `callqueues` (ACD: agents, dispatch, live stats) | R/W + PATCH | `callqueues` + `create/update/delete-callqueue`, `add/update/remove-agent` | full CRUD wrapped (verified) |
| `timeframes` (business-hours / holiday schedules) | R/W | `timeframes` + `create/update/delete-timeframe` | body-discriminated; CRUD wrapped (verified) |
| `autoattendants` (IVR menus) | R/W | `autoattendants` (read) | |
| `sites` (multi-site) | R/W | `sites` (read) | |
| `contacts` (domain address book) | R/W | `contacts` (read) | |
| `addresses` (E911) · `moh` (music on hold) · `dialplans` · `msg`/`smsnumbers` (SMS) · `connections` · `number-filters` | R/W | `raw` | not yet wrapped |
| `cdrs` · `calls` · `recordings` · `transcriptions` · `queuedcall` | read | `cdrs`; rest via `raw` | call data |
| `phonenumbers` (DIDs + dial-rule routing) | R/W | `dids`, `create-did`, `update-did`, `delete-did` | CRUD wrapped [P] |
| `phones` (SIP devices, MAC provisioning) | R/W | `phones`, `create-phone`; per-user `create/update/delete-device` | |
| `callqueues` (ACD: agents, dispatch, live stats) | R/W + PATCH | `callqueues` + `create/update/delete-callqueue`, `add/update/remove-agent` | full CRUD wrapped [V]; agent login/logout PATCH via `raw` |
| `timeframes` (business-hours / holiday schedules) | R/W | `timeframes` + `create/update/delete-timeframe` | body-discriminated; CRUD wrapped [V] |
| `autoattendants` (IVR menus) | R/W | `autoattendants`, `create-autoattendant` | per-prompt update via `raw` [P] |
| `sites` (multi-site) | R/W | `sites`, `create-site`, `update-site` | [P] |
| `contacts` (domain address book) | R/W | `contacts`, `create/update/delete-contact` | full CRUD wrapped [V]; id = `unique-id` |
| `addresses` (E911) | R/W | `addresses`, `create/update/delete-address` | create needs `validate` for pidflo [P] |
| `smsnumbers` (SMS) | R/W | `smsnumbers`, `create/update/delete-smsnumber` | needs SMS-provisioned domain [P] |
| `number-filters` (blocked numbers) | R/W | `blocked-numbers`, `block-numbers`, `unblock-numbers` | [P] (test-domain add didn't persist) |
| `moh` (music on hold) | R/W | `moh`, `create-moh` (TTS), `delete-moh` | file upload (multipart) via `raw` [P] |
| `dialplans` · `msg` · `connections` | R/W | `dialrules` (read); writes via `raw` | |
| `cdrs` · `calls` · `recordings` · `transcriptions` · `queuedcall` | read | `cdrs`, `recording`, `transcriptions`; rest via `raw` | call data |
| `billing` (limits + current counts) | read | `billing` | per-domain quota snapshot |
| Top-level: `domains`, `resellers`, `apikeys`, `subscriptions`, `routes`, `configurations`, `certificates`, `templates`, `jwt`, `tokens` | R/W | `domains`/`domain`/`resellers`; rest via `raw` | platform/admin |