wldx-admin-cli 0.0.0-stage → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vladimir Gorobets
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,446 @@
1
- # Temporary Holding Version
1
+ # wldx-admin-cli — `wx-admin`, an admin command line for the Wildix PBX
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Version 1.2.0** · unofficial · MIT
4
+
5
+ `wx-admin` manages a Wildix PBX through its REST API (`https://<pbx>/api/v1`): the system and its settings, users,
6
+ ACL, queues, dialplans and the objects they use (timetables, IVR, switches, paging, the dialplan database), trunks,
7
+ devices, phonebooks, sounds, call history, recordings and voicemails; any other operation through `wx-admin api`. It works with several PBX at once. Its twin for the everyday user is
8
+ [`wx`](https://github.com/gorets/wildix-cli/tree/main/packages/wx) (npm `wldx-cli`); they share the x-bees login.
9
+
10
+ ```sh
11
+ npm i -g wldx-admin-cli # the command is wx-admin
12
+ wx-admin pbx add mypbx --api-key # a Company API Key, typed hidden (or piped)
13
+ wx-admin dialplan list --pbx mypbx
14
+ wx-admin dialplan rules users --pbx mypbx
15
+
16
+ npx wldx-admin-cli --help # without installing
17
+ ```
18
+
19
+ Requires Node.js 22 or newer. One dependency: [`wldx-core`](https://github.com/gorets/wildix-cli/tree/main/packages/core), the core it shares with `wx`, from the
20
+ same repository and of the same version.
21
+
22
+ Unofficial: not made or supported by Wildix. It uses the PBX API as documented on the PBX
23
+ (`https://<pbx>/api/v1/doc/`), which may change between WMS versions.
24
+
25
+ ## Access to a PBX
26
+
27
+ Every request runs with the rights of the access that makes it:
28
+
29
+ | Access | How | Rights | Notes |
30
+ | --- | --- | --- | --- |
31
+ | **Company API Key** (`wsk-v1-…`) | `wx-admin pbx add <host> --api-key` | the scopes of the key, per group: `colleagues`, `aclgroups`, `callgroups`, `dialplan`, `trunks`, `devices`, `phonebooks`, `sounds`, `callhistory`, `recordings`, `smtp`, `security`, `info`, `pbxpower` (`:read` for reads, `:*` for changes), or `pbx:read` / `pbx:*` for all | Recommended by Wildix for automation (WMS 7.09+). A root admin makes it in WMS: **PBX → Integrations → Company API Keys**; it is shown once and expires (1 hour to 1 year). It acts as `admin`, or as the PBX user it is bound to. |
32
+ | **x-bees login** | `wx-admin login <e-mail>` (or `wx login`) | the ACL of that user: dialplans need the Admin role | Only the user's own PBX, named in the login. Shared with `wx` and the `wildix-auth` agent skill. |
33
+ | Simple Token | `wx-admin pbx add <host> --token` | the ACL of its application | Deprecated by Wildix: no new ones can be made. |
34
+ | User and password | `wx-admin pbx add <host> --basic [--user admin]` | that user's ACL | The PBX accepts it only with `ALLOW_BASIC_AUTH=true` in `/rw2/etc/env.custom.ini`. |
35
+
36
+ The secret is typed hidden, or read from stdin (`... | wx-admin pbx add <host> --api-key`), never taken from the
37
+ command line. `pbx add` checks it with one read of the PBX before it keeps it. For CI, `WILDIX_PBX_API_KEY` with `WILDIX_PBX_HOST` gives a
38
+ Company API Key for that one PBX without saving anything: the key is never sent to another `--pbx`, and `--all-pbx` is
39
+ refused while it is set.
40
+
41
+ ### Several PBX
42
+
43
+ Which PBX a command works on:
44
+
45
+ 1. `--pbx PBX` (anywhere on the command line): `name.wildixin.com`, a bare `name` (= `name.wildixin.com`), an IP
46
+ address, or the serial of a known PBX, bare or as `<serial>.wildixin.com` (`pbx add` and `pbx check` record the
47
+ serial; the PBX of the x-bees login has it in the login),
48
+ 2. else the default PBX (`wx-admin pbx use HOST`; `wx-admin pbx use --login` goes back to the next rule),
49
+ 3. else the PBX of the x-bees login.
50
+
51
+ `wx-admin pbx list` shows them (`*` is the default), `wx-admin pbx check` says which one a command would use and
52
+ whether it accepts the access. Saved access lives in `~/.wildix/admin/pbx/<host>.json` (folder 0700, files 0600).
53
+
54
+ `--all-pbx` runs a command that only reads on every PBX (saved access, then the x-bees login's) and prints each
55
+ result under its PBX: `wx-admin --all-pbx system info`, `wx-admin --all-pbx user registrations`.
56
+
57
+ ## Dialplans
58
+
59
+ A dialplan (procedure) is `{name, description, numbers, includedDialplans}`; its rules are `numbers`, run in order,
60
+ each `{number, apps: [{name, params}], comment}`. There is no API per rule: `rule-add` and `rule-remove` read the
61
+ dialplan and write it back whole. The PBX wants every parameter of an application, empty ones too: `wx-admin` fills
62
+ the ones it knows, and when the PBX names another missing one it adds it empty and tries again (and says so).
63
+
64
+ ```sh
65
+ wx-admin dialplan rule-add users 201 --app "DialPhone number=201 timeout=20" --app Hangup --comment "Anna"
66
+ wx-admin dialplan export main --to main.json
67
+ wx-admin dialplan import --from main.json --as main-copy
68
+ wx-admin dialplan apps --used # the applications this PBX uses, with their parameters
69
+ ```
70
+
71
+ ## Configuration as code
72
+
73
+ `wx-admin config backup --to DIR` writes the configuration of a PBX to a folder, one JSON file per object: dialplans,
74
+ dialplan settings, timetables, IVR menus, switches, paging groups, queues, trunks (passwords masked), trunk groups and
75
+ ACL groups. Ids and runtime fields are left out and included dialplans are named, so two PBX compare cleanly. Keep it
76
+ in git; `wx-admin config diff DIR` shows what changed on the PBX since (`+` only on the PBX, `-` only in the folder,
77
+ `~` changed, with the paths), `--with OTHERPBX` compares two PBX, `--all-pbx` compares every PBX with one folder.
78
+
79
+ ## Moving configuration to another PBX
80
+
81
+ A WMS backup restored on another PBX carries LDAP and values bound to the serial of the first one, and breaks the
82
+ second. `wx-admin` carries a chosen part of the configuration instead, with nothing bound to the source:
83
+
84
+ ```sh
85
+ wx-admin config export --pbx old --to ./move # tick what to carry (or --all, --only KIND:NAME,…; --users)
86
+ wx-admin config import ./move --pbx new --dry-run # the plan: new, the same, differs, blocked (and why)
87
+ wx-admin config import ./move --pbx new # tick, type the trunk passwords, confirm, write
88
+ wx-admin config copy --from old --to new # the same without the folder
89
+ ```
90
+
91
+ - **What is carried:** dialplans (with included dialplans), general dialplan settings, timetables, IVR menus,
92
+ switches, paging groups, queues, dialplan database keys (the families the dialplans use; more with
93
+ `--db-family`), ACL groups, SIP trunks, trunk groups, price lists, and users with `--users` (portable fields
94
+ only). **Never:** devices, licenses, PSTN and FXO trunks, call history, recordings, voicemails, LDAP ids, jids,
95
+ passwords, system settings.
96
+ - **References** between objects are kept by name (`{"$ref": "queues/Support", "as": "id"}`) and become the ids,
97
+ names or LDAP dns of the new PBX on import; ACL admin rules about the old PBX are about the new one. An object
98
+ where the serial or the host of the old PBX is still found is not carried (listed as bound).
99
+ - **The folder** is one JSON Lines file per kind (`users.jsonl`: one object per line) and `manifest.json`; it holds
100
+ no password and not the serial.
101
+ - **The import** compares every object with the new PBX by name: new ones are ticked, one that differs is
102
+ overwritten only when ticked (or `--overwrite`), blocked ones say why (a reference to nothing, no free license,
103
+ an extension taken). Passwords of the chosen trunks are typed hidden (or `--secrets FILE`). Objects are written in
104
+ the order they need each other; one that fails does not stop the rest, and running the same import again writes
105
+ only what is left.
106
+ - **At the end**, a list of what is left to do on the new PBX (where in WMS, which `wx-admin` command), the blocking
107
+ ones first, and a log file `wx-admin-import-<host>-<time>.log` (here, or `--log DIR`), written every time, also
108
+ with `--dry-run`. Without a terminal: `--all` or `--only`, and `--yes` to write.
109
+
110
+ ## Commands
111
+
112
+ Every command has `-h`, takes `--pbx HOST` and `--profile EMAIL`, and prints text or, with `--output json`, JSON.
113
+
114
+ <!-- commands:start (generated by tools/readme-commands.mjs) -->
115
+ ### wx-admin system
116
+
117
+ The PBX itself: version, license, settings (features, SMTP, NTP, proxy, trusted IPs), ports, upgrade, reboot.
118
+
119
+ | Command | What it does |
120
+ | --- | --- |
121
+ | `wx-admin system info` | version, ping and license summary of the PBX in one |
122
+ | `wx-admin system license` | the license of the PBX: limits, license types used and bought, options and until when |
123
+ | `wx-admin system settings` | the settings of the PBX: name, version, cloud, language, license, features, network interfaces |
124
+ | `wx-admin system settings-set --feature NAME (--enable \| --disable) --yes` | turn a PBX feature on or off (needs --yes: it changes the whole PBX) |
125
+ | `wx-admin system smtp [--show-secrets]` | the SMTP settings the PBX sends mail with (the password is never shown) |
126
+ | `wx-admin system smtp-set [--host HOST] [--port N] [--from EMAIL] [--user NAME] [--password] [--auth METHOD] [--tls on\|off] [--starttls on\|off] [--timeout S] [--domain NAME] [--cloud-default on\|off] --yes` | change the SMTP settings (needs --yes: mail of the whole PBX goes through them) |
127
+ | `wx-admin system smtp-test --to EMAIL [--from EMAIL] [--body TEXT] [--wait SECONDS]` | send a test e-mail with the SMTP settings and show the log of the SMTP server |
128
+ | `wx-admin system ntp` | the NTP servers of the PBX and its time |
129
+ | `wx-admin system ntp-set [--primary HOST] [--secondary HOST] --yes` | change the NTP servers (needs --yes: the clock of the whole PBX follows them) |
130
+ | `wx-admin system proxy [--show-secrets]` | the HTTP proxy of the PBX (for the support VPN; the password is never shown) |
131
+ | `wx-admin system proxy-set [--enable \| --disable] [--host HOST] [--port N] [--auth none\|basic\|ntlm] [--login NAME] [--password] --yes` | change the HTTP proxy (needs --yes: the support VPN of the PBX goes through it) |
132
+ | `wx-admin system trusted-ip` | the trusted IP addresses of the PBX |
133
+ | `wx-admin system trusted-ip-set [--add IP]... [--remove IP]... [--clear] --yes` | add or remove trusted IP addresses (needs --yes: the security of the whole PBX) |
134
+ | `wx-admin system ports` | whether the ports of the PBX are open from outside (HTTP, SIP, RTP), as last checked |
135
+ | `wx-admin system upgrade` | the state of the last upgrade of the PBX and its log |
136
+ | `wx-admin system upgrade-check [--status] [--wait SECONDS]` | look for a new WMS version and packages (runs the check on the PBX and waits for it) |
137
+ | `wx-admin system upgrade-settings` | how the PBX upgrades: repository, automatic upgrades, custom firmware server |
138
+ | `wx-admin system upgrade-start --yes` | start the upgrade of the PBX (needs --yes); then follow it with system upgrade, and reboot |
139
+ | `wx-admin system reboot --yes` | reboot the PBX (needs --yes: every call stops) |
140
+
141
+ ### wx-admin user
142
+
143
+ Users (colleagues): list, show, create, change, delete, copy preferences, presence, SIP registrations, groups, departments.
144
+
145
+ | Command | What it does |
146
+ | --- | --- |
147
+ | `wx-admin user list [--search TEXT] [--group GROUP] [--department NAME] [--role ROLE] [--license TYPE] [--sort FIELD [--desc]] [--limit N] [--start N] [--full]` | the users of the PBX, with filters |
148
+ | `wx-admin user get <user>` | one user: role, license, group, department, e-mail, phones, dialplan |
149
+ | `wx-admin user create --extension N --name NAME --license TYPE [--email EMAIL] [--role ROLE] [--group GROUP] [--department NAME] [... \| --from FILE\|-]` | create a user (it takes a license; the PBX may send the user a welcome e-mail) |
150
+ | `wx-admin user update <user> [--name NAME] [--email EMAIL] [--role ROLE] [--license TYPE] [--group GROUP] [... \| --from FILE\|-] [--transfer-chats]` | change fields of a user (only the ones given) |
151
+ | `wx-admin user delete <user>... --yes [--also ITEM]...` | delete users (needs --yes: it cannot be undone); --also removes their data too |
152
+ | `wx-admin user copy-preferences <from> <to>... --yes` | copy the preferences of one user to others (needs --yes: theirs are overwritten) |
153
+ | `wx-admin user presence <user>... [--full]` | the presence of users: online, status, message, until when, what their phone does |
154
+ | `wx-admin user presence-set <extension>... --status STATUS [--message TEXT] [--until "YYYY-MM-DD HH:MM"\|+2h]` | set the presence of users (what their colleagues see): available, away, dnd, mur |
155
+ | `wx-admin user registrations [<extension>...] [--full]` | the SIP registrations of users (all, or the extensions given): devices online, user agents |
156
+ | `wx-admin user groups` | the user groups (ACL groups) of the PBX |
157
+ | `wx-admin user departments` | the departments of the PBX, as a tree, with the number of users |
158
+
159
+ ### wx-admin acl
160
+
161
+ ACL groups: what the users of a group may do; list, show, create, change, delete, permissions.
162
+
163
+ | Command | What it does |
164
+ | --- | --- |
165
+ | `wx-admin acl groups` | the ACL groups of the PBX |
166
+ | `wx-admin acl group <group>` | one ACL group with its rules and admin rules |
167
+ | `wx-admin acl create <name> [--rule "KEY ABILITY [GROUP]"]... [--inherits GROUP] [--from FILE\|-]` | create an ACL group, empty, with --rule, or from a JSON file ({name?, inherits?, rules?}) |
168
+ | `wx-admin acl update <group> [--name NAME] [--inherits GROUP \| --no-inherits] [--rule "KEY ABILITY [GROUP]"]... [--remove-rule "KEY [GROUP]"]... [--admin-rule "SUBJECT OPERATION"]... [--remove-admin-rule SUBJECT]... [--from FILE\|-] [--yes]` | change an ACL group: name, parent, rules (added, replaced or removed) or admin rules |
169
+ | `wx-admin acl delete <group>... --yes` | delete ACL groups (needs --yes: their users lose the rights of the group) |
170
+ | `wx-admin acl permissions [--permission KEY]... [--group GROUP]...` | each permission and the groups that have it (filter by --permission and --group) |
171
+ | `wx-admin acl rules [--admin]` | the rules an ACL group can have: keys, abilities, whether they are about other groups |
172
+
173
+ ### wx-admin queue
174
+
175
+ Call groups (queues): list, show, create, change, delete, stats, dynamic members, forward a call.
176
+
177
+ | Command | What it does |
178
+ | --- | --- |
179
+ | `wx-admin queue list` | the call groups of the PBX |
180
+ | `wx-admin queue get <queue>` | one call group: settings, static members, and who is in the queue now |
181
+ | `wx-admin queue create <name> [--member EXT]... [--strategy S] [--timeout SECONDS] [--wrap-up SECONDS] [--max-len N] [--from FILE\|-]` | create a call group |
182
+ | `wx-admin queue update <queue> [--name NAME] [--member EXT]... [--no-members] [--strategy S] [--timeout SECONDS] [--wrap-up SECONDS] [--max-len N] [--from FILE\|-] [--yes]` | change a call group: name, settings, static members (--member replaces them) |
183
+ | `wx-admin queue delete <queue>... --yes` | delete call groups (needs --yes: calls sent to them stop) |
184
+ | `wx-admin queue stats [<queue>...]` | the queues now: members logged in, available, busy; calls waiting (all, or the ones given) |
185
+ | `wx-admin queue member-add <queue> <member> [--penalty N] [--paused] [--manager]` | add a dynamic member (an agent) to a queue, at once, until removed |
186
+ | `wx-admin queue member-remove <queue> <member> [--manager]` | remove a dynamic member (an agent) from a queue |
187
+ | `wx-admin queue forward <queue> <sip-call-id> <destination>` | forward a call waiting in a queue to an extension or a number |
188
+
189
+ ### wx-admin dialplan
190
+
191
+ Dialplans (procedures) and their rules, and the general dialplan settings.
192
+
193
+ | Command | What it does |
194
+ | --- | --- |
195
+ | `wx-admin dialplan list` | the dialplans of the PBX |
196
+ | `wx-admin dialplan get <dialplan>` | one dialplan with its rules and included dialplans |
197
+ | `wx-admin dialplan create <name> [--description TEXT] [--from FILE\|-]` | create a dialplan, empty or from a JSON file ({description, numbers, includedDialplans}) |
198
+ | `wx-admin dialplan update <dialplan> [--description TEXT] [--from FILE\|-] [--yes]` | change a dialplan: its description, or its rules and includes from a JSON file (replaces them) |
199
+ | `wx-admin dialplan delete <dialplan>... --yes` | delete dialplans (needs --yes: calls routed to them stop working) |
200
+ | `wx-admin dialplan rules <dialplan>` | the rules of a dialplan, in order: number pattern → applications |
201
+ | `wx-admin dialplan rule-add <dialplan> <number> (--app "Name key=value…"... \| --from FILE\|-) [--comment TEXT] [--position N] [--replace]` | add a rule to a dialplan (at the end, or at --position) |
202
+ | `wx-admin dialplan rule-remove <dialplan> <number> --yes` | remove the rule of a number from a dialplan (needs --yes) |
203
+ | `wx-admin dialplan export <dialplan> [--to FILE]` | a dialplan as JSON that dialplan import takes (to stdout, or to a new file) |
204
+ | `wx-admin dialplan import --from FILE\|- [--as NAME] [--replace --yes]` | create a dialplan from the JSON of dialplan export; --replace overwrites one of the same name |
205
+ | `wx-admin dialplan apps [--used]` | the dialplan applications; --used shows the ones the dialplans of the PBX use, with their parameters |
206
+ | `wx-admin dialplan settings` | the general dialplan settings (prefixes, timeouts, recording and voicemail mail, variables) |
207
+ | `wx-admin dialplan settings-set (--set KEY=VALUE... \| --from FILE\|-) --yes` | change general dialplan settings (needs --yes: they apply to every call) |
208
+
209
+ ### wx-admin timetable
210
+
211
+ Timetables (the hours a dialplan checks): list, show, create, change, delete, import.
212
+
213
+ | Command | What it does |
214
+ | --- | --- |
215
+ | `wx-admin timetable list` | the timetables of the PBX with their items |
216
+ | `wx-admin timetable get <timetable>` | one timetable and its items |
217
+ | `wx-admin timetable create <title> [--state 0\|1\|2] [--item SPEC]... [--from FILE\|-]` | create a timetable (without items it covers every day, all day) |
218
+ | `wx-admin timetable update <timetable> [--title TEXT] [--state 0\|1\|2] [--item SPEC]... [--add-item SPEC]... [--from FILE\|-]` | change a timetable: title, state, items (--item replaces them all, --add-item adds) |
219
+ | `wx-admin timetable delete <timetable>... --yes` | delete timetables (needs --yes: rules that check them stop matching) |
220
+ | `wx-admin timetable import --from FILE\|- [--replace]` | create timetables from JSON (the output of timetable list or get); --replace overwrites those of the same title |
221
+
222
+ ### wx-admin ivr
223
+
224
+ IVR menus (voice menus a dialplan sends calls to): list, show, create, change, delete.
225
+
226
+ | Command | What it does |
227
+ | --- | --- |
228
+ | `wx-admin ivr list` | the IVR menus of the PBX |
229
+ | `wx-admin ivr get <ivr>` | one IVR menu as a tree: keys and where they send the call |
230
+ | `wx-admin ivr create <title> --sound PATH [--times N] [--timeout SECONDS] [--timeout-to DIALPLAN/NUMBER] [--back-key K] [--key K=DIALPLAN/NUMBER]... [--from FILE\|-]` | create an IVR menu |
231
+ | `wx-admin ivr update <ivr> [--title TEXT] [--sound PATH] [--times N] [--timeout SECONDS] [--timeout-to DIALPLAN/NUMBER] [--back-key K] [--key K=DIALPLAN/NUMBER]... [--remove-key K]... [--from FILE\|-]` | change an IVR menu: title, sound, timeout, keys (--key sets one, --remove-key drops one) |
232
+ | `wx-admin ivr delete <ivr>... --yes` | delete IVR menus (needs --yes: calls sent to them stop working) |
233
+
234
+ ### wx-admin switch
235
+
236
+ Switches (on/off flags dialplans branch on): list, show, create, rename, on, off, blink, delete.
237
+
238
+ | Command | What it does |
239
+ | --- | --- |
240
+ | `wx-admin switch list` | the switches of the PBX and their state |
241
+ | `wx-admin switch get <switch>` | one switch and its state |
242
+ | `wx-admin switch create <title> [--three-state] [--state off\|on\|blinking]` | create a switch (off unless --state); --three-state lets it blink too (fixed once made) |
243
+ | `wx-admin switch update <switch> [--title TEXT] [--state off\|on\|blinking [--yes]]` | rename a switch, or set its state (a state change needs --yes) |
244
+ | `wx-admin switch on <switch> --yes` | set a switch on (needs --yes: calls are routed by it) |
245
+ | `wx-admin switch off <switch> --yes` | set a switch off (needs --yes: calls are routed by it) |
246
+ | `wx-admin switch blink <switch> --yes` | set a switch blinking (needs --yes: calls are routed by it) |
247
+ | `wx-admin switch delete <switch>... --yes` | delete switches (needs --yes: rules that check them change) |
248
+
249
+ ### wx-admin paging
250
+
251
+ Paging groups (the phones a page reaches): list, show, create, change, delete.
252
+
253
+ | Command | What it does |
254
+ | --- | --- |
255
+ | `wx-admin paging list` | the paging groups of the PBX and their members |
256
+ | `wx-admin paging get <group>` | one paging group and its members |
257
+ | `wx-admin paging create <title> [--member EXTENSION]...` | create a paging group |
258
+ | `wx-admin paging update <group> [--title TEXT] [--member EXTENSION]... [--add EXTENSION]... [--remove EXTENSION]...` | change a paging group: title, members (--member replaces them, --add and --remove change them) |
259
+ | `wx-admin paging delete <group>... --yes` | delete paging groups (needs --yes) |
260
+
261
+ ### wx-admin db
262
+
263
+ The dialplan database (families of keys and values that dialplans read and set): list, get, set, delete.
264
+
265
+ | Command | What it does |
266
+ | --- | --- |
267
+ | `wx-admin db list <family>` | the keys and values of a family |
268
+ | `wx-admin db get <family> <key>` | the value of a key |
269
+ | `wx-admin db set <family> <key> <value>` | set the value of a key (made if missing) |
270
+ | `wx-admin db delete <family> [<key>] --yes` | delete a key, or a whole family without a key (needs --yes) |
271
+
272
+ ### wx-admin trunk
273
+
274
+ Trunks (SIP, PSTN, FXO), trunk groups and price lists: list, show, create, change, delete.
275
+
276
+ | Command | What it does |
277
+ | --- | --- |
278
+ | `wx-admin trunk list [--type sip\|pstn\|fxo] [--show-secrets]` | the trunks of the PBX: kind, name, host, dialplan, registration |
279
+ | `wx-admin trunk get <trunk> [--type sip\|pstn\|fxo] [--show-secrets]` | one trunk and its settings (passwords never in text) |
280
+ | `wx-admin trunk create <title> --host HOST --user LOGIN [--secret] [--port N] [--context DIALPLAN] [--transport udp\|tcp\|tls] [--register] [--from FILE]` | create a SIP trunk |
281
+ | `wx-admin trunk update <trunk> [--type sip\|pstn\|fxo] [--title TEXT] [--context DIALPLAN] [--host HOST] [--port N] [--user LOGIN] [--transport udp\|tcp\|tls] [--secret] [--from FILE] --yes` | change a trunk (needs --yes: a wrong setting stops its calls) |
282
+ | `wx-admin trunk delete <trunk> --yes` | delete a SIP trunk (needs --yes: its calls stop) |
283
+ | `wx-admin trunk groups [<group>]` | the trunk groups (trunks tried in order for outgoing calls) |
284
+ | `wx-admin trunk group-create <name> --trunk SPEC... [--description TEXT]` | create a trunk group |
285
+ | `wx-admin trunk group-update <group> [--name NAME] [--description TEXT] [--trunk SPEC]... [--yes]` | change a trunk group: name, description, trunks (--trunk replaces them all) |
286
+ | `wx-admin trunk group-delete <group>... --yes` | delete trunk groups (needs --yes: dialplans that dial them fail) |
287
+ | `wx-admin trunk prices [<pricelist>]` | the price lists of the trunks (call costs); with one, its rates |
288
+ | `wx-admin trunk price-create <name> --file CSV` | create a price list from a CSV file (the format of price-export) |
289
+ | `wx-admin trunk price-update <pricelist> [--name NAME] [--file CSV]` | rename a price list, or replace its rates from a CSV file |
290
+ | `wx-admin trunk price-delete <pricelist>... --yes` | delete price lists (needs --yes) |
291
+ | `wx-admin trunk price-export <pricelist> [--to FILE]` | a price list as CSV (to stdout, or to a new file) |
292
+
293
+ ### wx-admin device
294
+
295
+ Devices of the PBX: list, add, change, delete, scan the network, remote access, Wizyconf token.
296
+
297
+ | Command | What it does |
298
+ | --- | --- |
299
+ | `wx-admin device list [--count N] [--start N] [--show-secrets]` | the devices of the PBX: MAC, model, users, online, address, firmware |
300
+ | `wx-admin device add <mac>...` | add devices by MAC address (they get their configuration from the PBX) |
301
+ | `wx-admin device update <mac> [--description TEXT] [--user EXT] [--users EXT,EXT] [--dns PRIMARY[,SECONDARY]] [--ntp HOST] [--timezone TZ] [--syslog HOST] [--from FILE\|-]` | change a device: description, user(s), DNS, NTP, syslog, or anything from a JSON file |
302
+ | `wx-admin device delete <mac> --yes` | delete a device from the PBX (needs --yes: it stops working with the PBX) |
303
+ | `wx-admin device scan <range>` | look for devices in a range of addresses of the network of the PBX |
304
+ | `wx-admin device verify <address> [--port N]` | check a device at an address: its MAC, model, and whether the PBX may provision it |
305
+ | `wx-admin device connect <mac>` | open the remote access to the web interface of a device, through the PBX |
306
+ | `wx-admin device disconnect <mac> --yes` | close the remote access to a device (needs --yes: whoever uses it loses it) |
307
+ | `wx-admin device reset-token <pin> --yes` | reset the token of a Wizyconf device, by the PIN it shows, to provision it again (needs --yes) |
308
+
309
+ ### wx-admin phonebook
310
+
311
+ Phonebooks and their contacts: list, show, create, change, delete, export, import.
312
+
313
+ | Command | What it does |
314
+ | --- | --- |
315
+ | `wx-admin phonebook list [--search TEXT]` | the phonebooks of the PBX |
316
+ | `wx-admin phonebook get <phonebook>` | one phonebook |
317
+ | `wx-admin phonebook create <name> [--type global\|personal]` | create a phonebook |
318
+ | `wx-admin phonebook update <phonebook> [--name NAME] [--type global\|personal]` | rename a phonebook or change its type |
319
+ | `wx-admin phonebook delete <phonebook> --yes` | delete a phonebook with all its contacts (needs --yes) |
320
+ | `wx-admin phonebook contacts <phonebook> [--search TEXT] [--count N] [--offset N]` | the contacts of a phonebook |
321
+ | `wx-admin phonebook contact <phonebook> <contactId>` | one contact of a phonebook: its filled fields |
322
+ | `wx-admin phonebook contact-add <phonebook> --name TEXT [--phone N] [--mobile N] [--email A] [--organization T] [--set FIELD=VALUE]...` | add a contact to a phonebook |
323
+ | `wx-admin phonebook contact-update <phonebook> <contactId> [--name TEXT] [--phone N] … [--set FIELD=VALUE]...` | change fields of a contact (the others stay) |
324
+ | `wx-admin phonebook contact-delete <phonebook> <contactId>... --yes` | delete contacts of a phonebook (needs --yes) |
325
+ | `wx-admin phonebook export <phonebook> [--to FILE] [--format csv\|json]` | the contacts of a phonebook as CSV or JSON (to stdout, or to a new file) |
326
+ | `wx-admin phonebook import <phonebook> --from FILE\|- [--format csv\|json]` | add the contacts of a CSV or JSON file to a phonebook |
327
+
328
+ ### wx-admin sound
329
+
330
+ Sound files (music on hold, ring tones, prompts) and their folders: list, upload, rename, move, delete.
331
+
332
+ | Command | What it does |
333
+ | --- | --- |
334
+ | `wx-admin sound list [<folder>]` | the sounds and folders of a folder (the top one by default) |
335
+ | `wx-admin sound upload <file> [--to FOLDER] [--name NAME]` | upload a sound file (the PBX converts it to its formats) |
336
+ | `wx-admin sound rename <path> <new-name>` | rename a sound or a folder |
337
+ | `wx-admin sound move <path> <folder>` | move a sound or a folder into another folder |
338
+ | `wx-admin sound delete <path>... --yes` | delete sounds (needs --yes) |
339
+ | `wx-admin sound mkdir <folder>` | make a folder for sounds |
340
+ | `wx-admin sound rmdir <folder> --yes` | delete a folder with the sounds in it (needs --yes) |
341
+
342
+ ### wx-admin records
343
+
344
+ What calls leave behind: the call history, call recordings, voicemails.
345
+
346
+ | Command | What it does |
347
+ | --- | --- |
348
+ | `wx-admin records history [--since DATE] [--until DATE] [--disposition D] [--type T] [--search TEXT] [--filter FIELD=VALUE]... [--count N] [--offset N] [--sort FIELD] [--asc]` | the call history of the whole PBX, the newest first |
349
+ | `wx-admin records history-user <extension> [the options of records history]` | the call history of one user (extension), the newest first |
350
+ | `wx-admin records call <callId>` | one call of the history: times, parties, result, its recordings and voicemail |
351
+ | `wx-admin records recordings [--since DATE] [--until DATE] [--count N] [--page TOKEN]` | the call recordings of the PBX, the newest first |
352
+ | `wx-admin records recording-download <path\|callId> --to FILE [--index N]` | save a call recording to a new file (by its path, or by the id of its call) |
353
+ | `wx-admin records recordings-batch (--since DATE --until DATE \| --task ID) [--to FILE] [--wait-seconds N]` | pack the recordings of a time range into one archive on the PBX, wait for it, optionally save it |
354
+ | `wx-admin records voicemails [--mailbox EXT] [--folder NAME]` | the voicemails of the PBX, the newest first |
355
+ | `wx-admin records voicemail-download <msg_id\|path> --to FILE` | save a voicemail message to a new file |
356
+ | `wx-admin records voicemail-delete <msg_id>... --yes` | delete voicemails for good (needs --yes) |
357
+
358
+ ### wx-admin config
359
+
360
+ The configuration as files: back up a PBX to a folder, compare a folder or another PBX with a PBX.
361
+
362
+ | Command | What it does |
363
+ | --- | --- |
364
+ | `wx-admin config backup --to DIR [--only KIND,KIND…]` | write the configuration of the PBX to a new folder, one JSON file per object (passwords masked) |
365
+ | `wx-admin config import <dir> [--all \| --only KIND:NAME,…] [--overwrite] [--secrets FILE] [--dry-run] [--yes] [--log DIR]` | write a config export to this PBX: a plan, what to write (chosen), the passwords, a log and what is left to do |
366
+ | `wx-admin config copy --from PBX --to PBX [--all \| --only KIND:NAME,…] [--users] [--overwrite] [--secrets FILE] [--dry-run] [--yes] [--log DIR]` | carry the configuration of one PBX to another: export in memory, then import (plan, choice, log) |
367
+ | `wx-admin config export --to DIR [--all \| --only KIND:NAME,…] [--users] [--db-family FAMILY]...` | choose what to carry to another PBX and write it to a new folder, without anything bound to this PBX |
368
+ | `wx-admin config diff (<dir> \| --with PBX) [--only KIND,KIND…]` | what differs between a backup folder (or another PBX) and the PBX |
369
+
370
+ ### wx-admin pbx
371
+
372
+ The PBX to work on: saved access (API key, token, user), the default one, a check.
373
+
374
+ | Command | What it does |
375
+ | --- | --- |
376
+ | `wx-admin pbx add <host> (--api-key \| --token \| --basic [--user NAME]) [--port N] [--no-check]` | save access to a PBX; the secret is typed hidden or read from stdin, never an argument |
377
+ | `wx-admin pbx list` | the PBX with saved access and the PBX of the x-bees login; * marks the default one |
378
+ | `wx-admin pbx use (<host> \| --login)` | make a PBX with saved access the default one; --login goes back to the PBX of the x-bees login |
379
+ | `wx-admin pbx remove <host>` | forget the saved access to a PBX (on this computer only) |
380
+ | `wx-admin pbx check` | which PBX a command would use, and whether it accepts the access (one read) |
381
+
382
+ ### wx-admin profile
383
+
384
+ Your profiles (the e-mails you are logged in with) and the default one.
385
+
386
+ | Command | What it does |
387
+ | --- | --- |
388
+ | `wx-admin profile list` | your profiles; * marks the default one |
389
+ | `wx-admin profile use <e-mail>` | make a profile the default one (used when no --profile is given) |
390
+
391
+ ### wx-admin api
392
+
393
+ | Command | What it does |
394
+ | --- | --- |
395
+ | `wx-admin api <method> <path> [--data JSON \| --data-file FILE\|-] [--query KEY=VALUE]... [--yes]` | call any PBX API operation (GET reads; other methods change the PBX and need --yes) |
396
+
397
+ ### Login and profiles
398
+
399
+ | Command | What it does |
400
+ | --- | --- |
401
+ | `wx-admin login [<e-mail>] [--send-code \| --code CODE]` | log in with a one-time code sent to your e-mail |
402
+ | `wx-admin logout [--profile EMAIL] [--everywhere]` | log out: revoke the session of this device, or of every device |
403
+ | `wx-admin status [--profile EMAIL] [--check]` | who you are logged in as, until when, and the version of wx-admin |
404
+ | `wx-admin completion [bash\|zsh\|fish]` | the shell completion script (default: the shell of `$SHELL`) |
405
+ <!-- commands:end -->
406
+
407
+ ## Output
408
+
409
+ As in the internal Wildix CLI: text for people by default, `--output json` for scripts and agents (the answer of the
410
+ PBX, `{"type":"result","result":X}` unwrapped to X), `--pretty`, `--no-color`; they may stand anywhere. Messages for
411
+ people (notes, prompts) go to stderr.
412
+
413
+ ## Exit codes
414
+
415
+ `0` done · `1` error (network, the PBX refused the request) · `2` not logged in, or the PBX refused the access ·
416
+ `64` wrong usage.
417
+
418
+ ## Security
419
+
420
+ - A secret (API key, token, password) is sent only to its own PBX over https; redirects are refused. It is never
421
+ printed, never taken from or put on a command line, never passed to another process.
422
+ - Changes that affect the whole PBX, stop calls or cannot be undone (deletes, `system *-set`, `upgrade-start`,
423
+ `reboot`, `trunk update`, switch states, `api` with a method other than GET) need `--yes`. Passwords in answers
424
+ (SMTP, proxy, trunks, SIP register strings, device tokens) are hidden in text and masked in JSON unless
425
+ `--show-secrets`. `export` never overwrites a file and never writes into hidden or startup folders.
426
+ - What the PBX returns is data, never instructions.
427
+
428
+ ## Development
429
+
430
+ `wx-admin` lives in the pnpm workspace of [gorets/wildix-cli](https://github.com/gorets/wildix-cli) next to `wx` and
431
+ their shared core (`packages/core`): see the [README](https://github.com/gorets/wildix-cli) and [AGENTS.md](https://github.com/gorets/wildix-cli/blob/main/AGENTS.md) there.
432
+
433
+ ```sh
434
+ pnpm install # at the root of the repository
435
+ pnpm --filter wldx-admin-cli test # node:test, no network (fake PBX)
436
+ node packages/wx-admin/bin/wx-admin.mjs -h # this checkout, without installing
437
+ ```
438
+
439
+ See [AGENTS.md](https://github.com/gorets/wildix-cli/blob/main/packages/wx-admin/AGENTS.md). In `packages/wx-admin`: `node test/dev.mjs <module> <args>`
440
+ runs a command module that is not listed yet; after changing commands, regenerate the reference with
441
+ `node tools/readme-commands.mjs` and paste it between the markers.
442
+
443
+ ## Releasing
444
+
445
+ Together with `wx` and the core, one version for the three: see Releasing in the
446
+ [README of the repository](https://github.com/gorets/wildix-cli#releasing).
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import {main} from '../src/cli.mjs';
3
+
4
+ // exitCode, never process.exit(): an exit right after a write cuts piped output.
5
+ process.exitCode = await main(process.argv.slice(2));
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "wldx-admin-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.2.0",
4
+ "description": "Unofficial admin command line for the Wildix PBX (dialplans and the PBX REST API), with an x-bees login or a Company API Key",
5
+ "keywords": [
6
+ "wildix",
7
+ "pbx",
8
+ "wms",
9
+ "dialplan",
10
+ "cli",
11
+ "admin"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "Vladimir Gorobets",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/gorets/wildix-cli.git",
18
+ "directory": "packages/wx-admin"
19
+ },
20
+ "homepage": "https://github.com/gorets/wildix-cli/tree/main/packages/wx-admin#readme",
21
+ "bugs": "https://github.com/gorets/wildix-cli/issues",
22
+ "type": "module",
23
+ "bin": {
24
+ "wx-admin": "bin/wx-admin.mjs"
25
+ },
26
+ "files": [
27
+ "bin/",
28
+ "src/"
29
+ ],
30
+ "engines": {
31
+ "node": ">=22"
32
+ },
33
+ "dependencies": {
34
+ "wldx-core": "1.2.0"
35
+ },
36
+ "scripts": {
37
+ "test": "node --test test/*.test.mjs"
38
+ }
6
39
  }
package/src/cli.mjs ADDED
@@ -0,0 +1,71 @@
1
+ // wx-admin: the spec of the command line on the router of wldx-core: its commands, the global options --pbx and
2
+ // --all-pbx, the completion of PBX names, and --all-pbx itself (a command that reads, run once per PBX).
3
+ import {createMain, line, render} from 'wldx-core/cli';
4
+ import {complete as completeOf} from 'wldx-core/completion';
5
+ import {AuthError, UsageError} from 'wldx-core/errors';
6
+ import {clean, paint} from 'wldx-core/format';
7
+ import {program, setProgram} from 'wldx-core/program';
8
+ import {COMMANDS, GLOBAL_OPTIONS, GROUPS} from './commands/index.mjs';
9
+ import {loginPbxOf, savedPbxs} from './pbx.mjs';
10
+ import {VERSION} from './version.mjs';
11
+
12
+ setProgram({name: 'wx-admin', pkg: 'wldx-admin-cli', version: VERSION});
13
+
14
+ // --all-pbx: a command that only reads runs once per PBX (saved access, then the PBX of the x-bees login) and every
15
+ // result is printed under its PBX; a PBX that fails does not stop the others. JSON: {results: [{pbx, result} |
16
+ // {pbx, error}]}. Exit 1 when one failed.
17
+ async function runOnAllPbx(cmd, ctx, io) {
18
+ if (!cmd.readOnly)
19
+ throw new UsageError(
20
+ '--all-pbx runs only commands that read (a change on every PBX at once is too easy to get wrong)',
21
+ ctx.usage(),
22
+ );
23
+ if (ctx.pbx) throw new UsageError('Give --pbx or --all-pbx, not both');
24
+ if (io.env.WILDIX_PBX_API_KEY)
25
+ throw new UsageError('--all-pbx is refused while WILDIX_PBX_API_KEY is set: that key is for one PBX only');
26
+ const hosts = savedPbxs(io.env).map((p) => p.host);
27
+ const login = loginPbxOf(ctx);
28
+ if (login && !hosts.includes(login.host)) hosts.push(login.host);
29
+ if (!hosts.length)
30
+ throw new AuthError(`No PBX: add one (${program.name} pbx add <host>) or log in (${program.name} login <e-mail>)`);
31
+ const results = [];
32
+ for (const pbx of hosts) {
33
+ try {
34
+ const result = await cmd.run({...ctx, pbx});
35
+ results.push({pbx, result});
36
+ if (ctx.output !== 'json') line(io.stdout, `${paint(ctx).bold(`== ${pbx}`)}\n${render(result, cmd.text, ctx)}`);
37
+ } catch (e) {
38
+ results.push({pbx, error: e.message});
39
+ if (ctx.output !== 'json')
40
+ line(io.stdout, `${paint(ctx).bold(`== ${pbx}`)}\n${paint(ctx).red(clean(e.message))}`);
41
+ }
42
+ }
43
+ if (ctx.output === 'json') line(io.stdout, JSON.stringify({results}, null, io.globals.pretty ? 2 : 0));
44
+ if (results.some((r) => r.error)) {
45
+ const err = new Error(`${results.filter((r) => r.error).length} of ${results.length} PBX failed`);
46
+ err.exitCode = 1;
47
+ throw err;
48
+ }
49
+ }
50
+
51
+ export const SPEC = {
52
+ groups: GROUPS,
53
+ commands: COMMANDS,
54
+ globals: GLOBAL_OPTIONS,
55
+ help: {
56
+ options: [
57
+ ' --pbx PBX the PBX: name.wildixin.com, name, an IP, or the serial of a known PBX; else the',
58
+ ' default one (wx-admin pbx use), else the PBX of the x-bees login',
59
+ " --all-pbx run a command that reads on every PBX: saved access, then the x-bees login's",
60
+ ],
61
+ exitCodes: 'Exit codes: 0 done, 1 error, 2 not logged in or the access was refused, 64 wrong usage.',
62
+ },
63
+ // The PBX with saved access, by host and by serial.
64
+ completers: {
65
+ pbxs: (env) => savedPbxs(env).flatMap((p) => [[p.host, p.auth], ...(p.serial ? [[p.serial, p.host]] : [])]),
66
+ },
67
+ around: (cmd, ctx, io, run) => (ctx.allPbx ? runOnAllPbx(cmd, ctx, io) : run()),
68
+ };
69
+
70
+ export const main = createMain(SPEC);
71
+ export const complete = (words, env = process.env) => completeOf(words, env, SPEC);