@mainwp/control 1.0.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.
Files changed (204) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +583 -0
  3. package/bin/_exit.js +12 -0
  4. package/bin/dev.js +7 -0
  5. package/bin/run.js +7 -0
  6. package/dist/chat/chat-engine.d.ts +213 -0
  7. package/dist/chat/chat-engine.d.ts.map +1 -0
  8. package/dist/chat/chat-engine.js +636 -0
  9. package/dist/chat/chat-engine.js.map +1 -0
  10. package/dist/chat/index.d.ts +10 -0
  11. package/dist/chat/index.d.ts.map +1 -0
  12. package/dist/chat/index.js +14 -0
  13. package/dist/chat/index.js.map +1 -0
  14. package/dist/chat/providers/anthropic.d.ts +52 -0
  15. package/dist/chat/providers/anthropic.d.ts.map +1 -0
  16. package/dist/chat/providers/anthropic.js +292 -0
  17. package/dist/chat/providers/anthropic.js.map +1 -0
  18. package/dist/chat/providers/gemini.d.ts +52 -0
  19. package/dist/chat/providers/gemini.d.ts.map +1 -0
  20. package/dist/chat/providers/gemini.js +284 -0
  21. package/dist/chat/providers/gemini.js.map +1 -0
  22. package/dist/chat/providers/index.d.ts +19 -0
  23. package/dist/chat/providers/index.d.ts.map +1 -0
  24. package/dist/chat/providers/index.js +23 -0
  25. package/dist/chat/providers/index.js.map +1 -0
  26. package/dist/chat/providers/local.d.ts +37 -0
  27. package/dist/chat/providers/local.d.ts.map +1 -0
  28. package/dist/chat/providers/local.js +130 -0
  29. package/dist/chat/providers/local.js.map +1 -0
  30. package/dist/chat/providers/openai-compatible.d.ts +155 -0
  31. package/dist/chat/providers/openai-compatible.d.ts.map +1 -0
  32. package/dist/chat/providers/openai-compatible.js +264 -0
  33. package/dist/chat/providers/openai-compatible.js.map +1 -0
  34. package/dist/chat/providers/openai.d.ts +24 -0
  35. package/dist/chat/providers/openai.d.ts.map +1 -0
  36. package/dist/chat/providers/openai.js +62 -0
  37. package/dist/chat/providers/openai.js.map +1 -0
  38. package/dist/chat/providers/openrouter.d.ts +26 -0
  39. package/dist/chat/providers/openrouter.d.ts.map +1 -0
  40. package/dist/chat/providers/openrouter.js +65 -0
  41. package/dist/chat/providers/openrouter.js.map +1 -0
  42. package/dist/chat/providers/provider-fetch.d.ts +15 -0
  43. package/dist/chat/providers/provider-fetch.d.ts.map +1 -0
  44. package/dist/chat/providers/provider-fetch.js +35 -0
  45. package/dist/chat/providers/provider-fetch.js.map +1 -0
  46. package/dist/chat/providers/provider.d.ts +214 -0
  47. package/dist/chat/providers/provider.d.ts.map +1 -0
  48. package/dist/chat/providers/provider.js +166 -0
  49. package/dist/chat/providers/provider.js.map +1 -0
  50. package/dist/chat/providers/sse-reader.d.ts +21 -0
  51. package/dist/chat/providers/sse-reader.d.ts.map +1 -0
  52. package/dist/chat/providers/sse-reader.js +48 -0
  53. package/dist/chat/providers/sse-reader.js.map +1 -0
  54. package/dist/chat/system-prompt.d.ts +33 -0
  55. package/dist/chat/system-prompt.d.ts.map +1 -0
  56. package/dist/chat/system-prompt.js +166 -0
  57. package/dist/chat/system-prompt.js.map +1 -0
  58. package/dist/chat/tool-envelope.d.ts +72 -0
  59. package/dist/chat/tool-envelope.d.ts.map +1 -0
  60. package/dist/chat/tool-envelope.js +263 -0
  61. package/dist/chat/tool-envelope.js.map +1 -0
  62. package/dist/commands/abilities/info.d.ts +21 -0
  63. package/dist/commands/abilities/info.d.ts.map +1 -0
  64. package/dist/commands/abilities/info.js +80 -0
  65. package/dist/commands/abilities/info.js.map +1 -0
  66. package/dist/commands/abilities/list.d.ts +19 -0
  67. package/dist/commands/abilities/list.d.ts.map +1 -0
  68. package/dist/commands/abilities/list.js +98 -0
  69. package/dist/commands/abilities/list.js.map +1 -0
  70. package/dist/commands/abilities/run.d.ts +75 -0
  71. package/dist/commands/abilities/run.d.ts.map +1 -0
  72. package/dist/commands/abilities/run.js +468 -0
  73. package/dist/commands/abilities/run.js.map +1 -0
  74. package/dist/commands/chat.d.ts +54 -0
  75. package/dist/commands/chat.d.ts.map +1 -0
  76. package/dist/commands/chat.js +384 -0
  77. package/dist/commands/chat.js.map +1 -0
  78. package/dist/commands/config/show.d.ts +54 -0
  79. package/dist/commands/config/show.d.ts.map +1 -0
  80. package/dist/commands/config/show.js +324 -0
  81. package/dist/commands/config/show.js.map +1 -0
  82. package/dist/commands/doctor.d.ts +77 -0
  83. package/dist/commands/doctor.d.ts.map +1 -0
  84. package/dist/commands/doctor.js +412 -0
  85. package/dist/commands/doctor.js.map +1 -0
  86. package/dist/commands/jobs/watch.d.ts +50 -0
  87. package/dist/commands/jobs/watch.d.ts.map +1 -0
  88. package/dist/commands/jobs/watch.js +269 -0
  89. package/dist/commands/jobs/watch.js.map +1 -0
  90. package/dist/commands/login.d.ts +25 -0
  91. package/dist/commands/login.d.ts.map +1 -0
  92. package/dist/commands/login.js +165 -0
  93. package/dist/commands/login.js.map +1 -0
  94. package/dist/commands/profile/delete.d.ts +22 -0
  95. package/dist/commands/profile/delete.d.ts.map +1 -0
  96. package/dist/commands/profile/delete.js +57 -0
  97. package/dist/commands/profile/delete.js.map +1 -0
  98. package/dist/commands/profile/list.d.ts +19 -0
  99. package/dist/commands/profile/list.d.ts.map +1 -0
  100. package/dist/commands/profile/list.js +53 -0
  101. package/dist/commands/profile/list.js.map +1 -0
  102. package/dist/commands/profile/use.d.ts +22 -0
  103. package/dist/commands/profile/use.d.ts.map +1 -0
  104. package/dist/commands/profile/use.js +46 -0
  105. package/dist/commands/profile/use.js.map +1 -0
  106. package/dist/config/fs-utils.d.ts +14 -0
  107. package/dist/config/fs-utils.d.ts.map +1 -0
  108. package/dist/config/fs-utils.js +31 -0
  109. package/dist/config/fs-utils.js.map +1 -0
  110. package/dist/config/keychain.d.ts +53 -0
  111. package/dist/config/keychain.d.ts.map +1 -0
  112. package/dist/config/keychain.js +175 -0
  113. package/dist/config/keychain.js.map +1 -0
  114. package/dist/config/profile-store.d.ts +85 -0
  115. package/dist/config/profile-store.d.ts.map +1 -0
  116. package/dist/config/profile-store.js +228 -0
  117. package/dist/config/profile-store.js.map +1 -0
  118. package/dist/config/settings.d.ts +71 -0
  119. package/dist/config/settings.d.ts.map +1 -0
  120. package/dist/config/settings.js +151 -0
  121. package/dist/config/settings.js.map +1 -0
  122. package/dist/core/abilities-executor.d.ts +126 -0
  123. package/dist/core/abilities-executor.d.ts.map +1 -0
  124. package/dist/core/abilities-executor.js +264 -0
  125. package/dist/core/abilities-executor.js.map +1 -0
  126. package/dist/core/batch-manager.d.ts +113 -0
  127. package/dist/core/batch-manager.d.ts.map +1 -0
  128. package/dist/core/batch-manager.js +244 -0
  129. package/dist/core/batch-manager.js.map +1 -0
  130. package/dist/core/http-client.d.ts +111 -0
  131. package/dist/core/http-client.d.ts.map +1 -0
  132. package/dist/core/http-client.js +329 -0
  133. package/dist/core/http-client.js.map +1 -0
  134. package/dist/core/safety-controller.d.ts +114 -0
  135. package/dist/core/safety-controller.d.ts.map +1 -0
  136. package/dist/core/safety-controller.js +229 -0
  137. package/dist/core/safety-controller.js.map +1 -0
  138. package/dist/hooks/command-not-found.d.ts +12 -0
  139. package/dist/hooks/command-not-found.d.ts.map +1 -0
  140. package/dist/hooks/command-not-found.js +58 -0
  141. package/dist/hooks/command-not-found.js.map +1 -0
  142. package/dist/index.d.ts +7 -0
  143. package/dist/index.d.ts.map +1 -0
  144. package/dist/index.js +7 -0
  145. package/dist/index.js.map +1 -0
  146. package/dist/lib/base-command.d.ts +123 -0
  147. package/dist/lib/base-command.d.ts.map +1 -0
  148. package/dist/lib/base-command.js +285 -0
  149. package/dist/lib/base-command.js.map +1 -0
  150. package/dist/output/formatter.d.ts +48 -0
  151. package/dist/output/formatter.d.ts.map +1 -0
  152. package/dist/output/formatter.js +138 -0
  153. package/dist/output/formatter.js.map +1 -0
  154. package/dist/output/json-envelope.d.ts +43 -0
  155. package/dist/output/json-envelope.d.ts.map +1 -0
  156. package/dist/output/json-envelope.js +73 -0
  157. package/dist/output/json-envelope.js.map +1 -0
  158. package/dist/utils/audit-logger.d.ts +97 -0
  159. package/dist/utils/audit-logger.d.ts.map +1 -0
  160. package/dist/utils/audit-logger.js +169 -0
  161. package/dist/utils/audit-logger.js.map +1 -0
  162. package/dist/utils/colors.d.ts +29 -0
  163. package/dist/utils/colors.d.ts.map +1 -0
  164. package/dist/utils/colors.js +36 -0
  165. package/dist/utils/colors.js.map +1 -0
  166. package/dist/utils/errors.d.ts +107 -0
  167. package/dist/utils/errors.d.ts.map +1 -0
  168. package/dist/utils/errors.js +149 -0
  169. package/dist/utils/errors.js.map +1 -0
  170. package/dist/utils/exit-codes.d.ts +21 -0
  171. package/dist/utils/exit-codes.d.ts.map +1 -0
  172. package/dist/utils/exit-codes.js +20 -0
  173. package/dist/utils/exit-codes.js.map +1 -0
  174. package/dist/utils/format.d.ts +64 -0
  175. package/dist/utils/format.d.ts.map +1 -0
  176. package/dist/utils/format.js +69 -0
  177. package/dist/utils/format.js.map +1 -0
  178. package/dist/utils/prompt.d.ts +34 -0
  179. package/dist/utils/prompt.d.ts.map +1 -0
  180. package/dist/utils/prompt.js +132 -0
  181. package/dist/utils/prompt.js.map +1 -0
  182. package/dist/utils/retry.d.ts +59 -0
  183. package/dist/utils/retry.d.ts.map +1 -0
  184. package/dist/utils/retry.js +96 -0
  185. package/dist/utils/retry.js.map +1 -0
  186. package/dist/utils/terminal-sanitizer.d.ts +60 -0
  187. package/dist/utils/terminal-sanitizer.d.ts.map +1 -0
  188. package/dist/utils/terminal-sanitizer.js +166 -0
  189. package/dist/utils/terminal-sanitizer.js.map +1 -0
  190. package/dist/validation/input-sanitizer.d.ts +76 -0
  191. package/dist/validation/input-sanitizer.d.ts.map +1 -0
  192. package/dist/validation/input-sanitizer.js +199 -0
  193. package/dist/validation/input-sanitizer.js.map +1 -0
  194. package/dist/validation/schema-validator.d.ts +75 -0
  195. package/dist/validation/schema-validator.d.ts.map +1 -0
  196. package/dist/validation/schema-validator.js +147 -0
  197. package/dist/validation/schema-validator.js.map +1 -0
  198. package/oclif.manifest.json +857 -0
  199. package/package.json +101 -0
  200. package/scripts/completions/README.md +221 -0
  201. package/scripts/completions/mainwpcontrol.bash +193 -0
  202. package/scripts/completions/mainwpcontrol.zsh +267 -0
  203. package/scripts/completions/profile-completer.sh +35 -0
  204. package/scripts/completions/regenerate.sh +78 -0
package/README.md ADDED
@@ -0,0 +1,583 @@
1
+ # MainWP Control
2
+
3
+ A CLI for managing your MainWP Dashboard from the terminal. List sites, push updates, sync data, run batch operations across dozens of sites. The command is `mainwpcontrol`.
4
+
5
+ **Looking for the MCP Server instead?** [MainWP MCP Server](https://github.com/mainwp/mainwp-mcp) is for conversational AI management inside Claude, Cursor, or any MCP-compatible client. MainWP Control is for automation: cron jobs, CI/CD pipelines, monitoring scripts, and batch operations. Both talk to the same Abilities API with the same safety model.
6
+
7
+ ---
8
+
9
+ ## Quick Start
10
+
11
+ You need Node.js 20+ and a MainWP Dashboard (v6+) with an [Application Password](https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/).
12
+
13
+ ```bash
14
+ npm install -g @mainwp/control
15
+
16
+ mainwpcontrol login
17
+
18
+ mainwpcontrol abilities list
19
+ ```
20
+
21
+ You should see something like this:
22
+
23
+ ```text
24
+ Abilities (87 total)
25
+
26
+ Sites
27
+ Name Description Type
28
+ ---------------- -------------------- --------------
29
+ list-sites-v1 List MainWP sites 📖 read
30
+ mainwpcontrol abilities run list-sites-v1
31
+ get-site-v1 Get site details 📖 read
32
+ mainwpcontrol abilities run get-site-v1
33
+ sync-sites-v1 Sync all sites ✏️ write
34
+ mainwpcontrol abilities run sync-sites-v1
35
+ ```
36
+
37
+ That's it. You're connected and you can see every operation your Dashboard supports.
38
+
39
+ ---
40
+
41
+ ## What Just Happened
42
+
43
+ `abilities list` shows every operation available on your Dashboard. These are called "abilities" and they cover sites, plugins, themes, updates, clients, tags, and more.
44
+
45
+ Each ability has a name (like `list-sites-v1`) that you pass to `abilities run` to execute it. The list tells you whether each one is read-only, a write operation, or destructive.
46
+
47
+ ---
48
+
49
+ ## Common Use Cases
50
+
51
+ **List all your sites:**
52
+
53
+ ```bash
54
+ mainwpcontrol abilities run list-sites-v1 --json
55
+ ```
56
+
57
+ **Check for pending updates across sites:**
58
+
59
+ ```bash
60
+ mainwpcontrol abilities run list-updates-v1 --json
61
+ ```
62
+
63
+ **Get details for a specific site:**
64
+
65
+ ```bash
66
+ mainwpcontrol abilities run get-site-v1 --input '{"site_id": 1}' --json
67
+ ```
68
+
69
+ **Preview a destructive action before running it:**
70
+
71
+ ```bash
72
+ mainwpcontrol abilities run delete-site-v1 \
73
+ --input '{"site_id_or_domain": "mysite.com"}' \
74
+ --dry-run --json
75
+ ```
76
+
77
+ Nothing changes until you explicitly pass `--confirm`.
78
+
79
+ **Update plugins and wait for completion:**
80
+
81
+ ```bash
82
+ mainwpcontrol abilities run update-site-plugins-v1 \
83
+ --input '{"site_id": 1}' \
84
+ --wait --json
85
+ ```
86
+
87
+ `--wait` blocks until the operation finishes. Useful in CI pipelines.
88
+
89
+ ---
90
+
91
+ ## Installation
92
+
93
+ ### Standard install (recommended)
94
+
95
+ Pre-built keychain binaries are included for macOS, Windows, and Linux (x64 and arm64). On other platforms you may need C++ build tools during installation.
96
+
97
+ ```bash
98
+ npm install -g @mainwp/control
99
+
100
+ # Interactive login (stores credentials in your OS keychain)
101
+ mainwpcontrol login
102
+ ```
103
+
104
+ ### Environment variable auth (CI, Docker, headless)
105
+
106
+ Use this when no OS keychain is available, or if keytar fails to build.
107
+
108
+ ```bash
109
+ npm install -g @mainwp/control
110
+
111
+ export MAINWP_APP_PASSWORD='xxxx xxxx xxxx xxxx xxxx xxxx'
112
+
113
+ mainwpcontrol login --url https://dashboard.example.com --username admin
114
+ ```
115
+
116
+ When the OS keychain is unavailable, credentials are not stored on disk. Keep `MAINWP_APP_PASSWORD` set for each run.
117
+
118
+ If keytar is installed but broken, set `MAINWPCONTROL_NO_KEYTAR=1` to skip loading it.
119
+
120
+ ---
121
+
122
+ <details>
123
+ <summary><strong>New to the Command Line?</strong></summary>
124
+
125
+ If you haven't used a terminal before, here's what you need to know.
126
+
127
+ ### What is a terminal?
128
+
129
+ A terminal is where you type commands instead of clicking buttons. You'll see it called "command line" or "shell" in different places.
130
+
131
+ **How to open it:**
132
+ - **macOS**: Open **Terminal** (search in Spotlight, or look in Applications > Utilities)
133
+ - **Windows**: Open **PowerShell** (search in the Start menu)
134
+ - **Linux**: Open your distribution's **Terminal** app (usually in the applications menu)
135
+
136
+ ### What does `npm install -g` do?
137
+
138
+ `npm` is the Node.js package manager. It downloads and installs JavaScript packages. The `-g` flag installs globally, which makes `mainwpcontrol` available as a command anywhere on your system, not only in one project folder.
139
+
140
+ ### What is an environment variable?
141
+
142
+ An environment variable is a named value that programs can read. They're commonly used for passwords and API keys.
143
+
144
+ **Setting one:**
145
+ ```bash
146
+ # macOS / Linux (lasts until you close the terminal)
147
+ export MAINWP_APP_PASSWORD='xxxx xxxx xxxx xxxx xxxx xxxx'
148
+
149
+ # Windows PowerShell (lasts until you close the window)
150
+ $env:MAINWP_APP_PASSWORD = 'xxxx xxxx xxxx xxxx xxxx xxxx'
151
+ ```
152
+
153
+ For long-term storage, use the OS keychain (the default when you run `mainwpcontrol login`) or a restricted-permission `.env` file rather than pasting credentials into shell profile files.
154
+
155
+ ### What is an Application Password?
156
+
157
+ WordPress Application Passwords let external tools like `mainwpcontrol` access your site without using your main login password. They look like groups of four characters separated by spaces (e.g., `abcd efgh ijkl mnop qrst uvwx`).
158
+
159
+ **To create one:** Log into WordPress admin > Users > Your Profile > scroll to **Application Passwords** > enter a name like "mainwpcontrol" > click **Add New Application Password** > copy the generated password.
160
+
161
+ ### Reading command output
162
+
163
+ When you run a command, the output appears in your terminal. A few things to know:
164
+
165
+ - **`--json`** tells `mainwpcontrol` to output structured JSON (useful for scripting and piping to other tools)
166
+ - **Exit codes** indicate success (`0`) or failure (`1` through `5`). You won't see them directly, but scripts and CI use them to decide what happens next. Run `echo $?` (macOS/Linux) or `echo $LASTEXITCODE` (PowerShell) after a command to check.
167
+
168
+ </details>
169
+
170
+ ---
171
+
172
+ ## Basic Usage
173
+
174
+ ### Abilities
175
+
176
+ Your Dashboard exposes its operations as "abilities." You browse them, pick one, and run it. Every ability has a versioned name like `list-sites-v1` that you pass to `abilities run`.
177
+
178
+ ```bash
179
+ # List all abilities
180
+ mainwpcontrol abilities list
181
+
182
+ # Filter by category
183
+ mainwpcontrol abilities list --category sites
184
+
185
+ # Get full details and input schema for an ability
186
+ mainwpcontrol abilities info list-sites-v1
187
+
188
+ # Run an ability
189
+ mainwpcontrol abilities run list-sites-v1 --json
190
+
191
+ # Run with input parameters
192
+ mainwpcontrol abilities run get-site-v1 --input '{"site_id": 1}' --json
193
+ ```
194
+
195
+ ### Profiles
196
+
197
+ Each `mainwpcontrol login` creates a profile, a named connection to a Dashboard, identified by hostname. If you manage multiple Dashboards, run `login` once per Dashboard to create a profile for each.
198
+
199
+ ```bash
200
+ # List all profiles
201
+ mainwpcontrol profile list
202
+
203
+ # Switch active profile
204
+ mainwpcontrol profile use production.example.com
205
+
206
+ # Use a profile for one command without switching
207
+ mainwpcontrol abilities list --profile staging.example.com
208
+
209
+ # Delete a profile and its keychain credentials
210
+ mainwpcontrol profile delete staging.example.com
211
+ ```
212
+
213
+ ### Diagnostics
214
+
215
+ `doctor` checks your configuration, credentials, and Dashboard connectivity. Run it first if something isn't working.
216
+
217
+ ```bash
218
+ # Check configuration and connectivity
219
+ mainwpcontrol doctor
220
+
221
+ # Verbose output
222
+ mainwpcontrol doctor -v
223
+
224
+ # JSON output
225
+ mainwpcontrol doctor --json
226
+ ```
227
+
228
+ ### Chat Mode
229
+
230
+ If you have an LLM API key, you can talk to your Dashboard in plain English instead of constructing commands. Good for exploration, not required for anything.
231
+
232
+ Set one of these environment variables to enable it:
233
+
234
+ ```bash
235
+ # Pick one (Anthropic, OpenAI, Google, or OpenRouter)
236
+ export ANTHROPIC_API_KEY='sk-ant-...'
237
+
238
+ mainwpcontrol chat
239
+ mainwpcontrol chat "list all sites with pending updates"
240
+ ```
241
+
242
+ See [Chat Mode Configuration](#chat-mode-configuration) for all supported providers and flags.
243
+
244
+ ### Global Flags
245
+
246
+ These flags work on every command.
247
+
248
+ | Flag | Description |
249
+ |------|-------------|
250
+ | `--json` | Structured JSON output |
251
+ | `--quiet` / `-q` | Suppress output (exit code only) |
252
+ | `--profile <name>` | Use a specific profile |
253
+ | `--debug` | Show redacted debug diagnostics on stderr |
254
+ | `--help` | Show help |
255
+
256
+ ### Abilities Run Flags
257
+
258
+ Extra flags for `abilities run`. These control input, safety checks, and batch job behavior.
259
+
260
+ | Flag | Description |
261
+ |------|-------------|
262
+ | `--input` / `-i` | Input parameters as JSON (use `-` for stdin) |
263
+ | `--input-file` | Read input from a JSON file |
264
+ | `--dry-run` | Preview changes without executing |
265
+ | `--confirm` | Execute a destructive ability |
266
+ | `--force` | Skip interactive confirmation (CI mode) |
267
+ | `--wait` | Block until batch job completes |
268
+ | `--wait-timeout` | Max seconds to wait (default: 300) |
269
+
270
+ ---
271
+
272
+ ## Concepts
273
+
274
+ ### Abilities
275
+
276
+ Abilities are the operations your MainWP Dashboard exposes through its REST API. Each one has:
277
+
278
+ - A versioned name (e.g., `list-sites-v1`, `delete-site-v1`)
279
+ - An input schema (what parameters it accepts)
280
+ - Annotations that tell you what kind of operation it is
281
+
282
+ The annotations matter:
283
+ - **Readonly**: Safe to run anytime. Cannot modify data.
284
+ - **Destructive**: Permanently changes or deletes data. Requires `--dry-run` preview, then `--confirm` to execute.
285
+ - **Idempotent**: Safe to re-run. Same result on repeated calls.
286
+
287
+ Run `mainwpcontrol abilities info <name>` to see the full schema and annotations for any ability.
288
+
289
+ ### Profiles
290
+
291
+ A profile is a named connection to a MainWP Dashboard. It stores the Dashboard URL and username. Your password stays in the OS keychain (or in the `MAINWP_APP_PASSWORD` environment variable when no keychain is available).
292
+
293
+ Running `mainwpcontrol login` creates a profile automatically, named after the Dashboard hostname:
294
+
295
+ ```bash
296
+ # Creates profile "staging.example.com"
297
+ mainwpcontrol login --url https://staging.example.com --username admin
298
+
299
+ # Creates profile "production.example.com"
300
+ mainwpcontrol login --url https://production.example.com --username admin
301
+ ```
302
+
303
+ The profile file at `~/.config/mainwpcontrol/profiles.json` never contains passwords.
304
+
305
+ ### Safety Model
306
+
307
+ Destructive operations follow a two-step pattern: preview first, then execute.
308
+
309
+ ```bash
310
+ # Step 1: Preview (nothing changes)
311
+ mainwpcontrol abilities run delete-site-v1 \
312
+ --input '{"site_id_or_domain": "mysite.com"}' \
313
+ --dry-run --json
314
+
315
+ # Step 2: Execute after reviewing the preview
316
+ mainwpcontrol abilities run delete-site-v1 \
317
+ --input '{"site_id_or_domain": "mysite.com"}' \
318
+ --confirm --force --json
319
+ ```
320
+
321
+ `--dry-run` and `--confirm` are mutually exclusive. You cannot pass both.
322
+
323
+ In CI/scripted workflows where you've already validated the operation, pass `--confirm --force` directly to skip the interactive prompt.
324
+
325
+ ### Batch Jobs
326
+
327
+ Operations that affect many items (200+) are automatically queued as batch jobs. The command returns a `job_id` immediately, and you can watch progress:
328
+
329
+ ```bash
330
+ mainwpcontrol jobs watch <job-id>
331
+
332
+ # With a timeout
333
+ mainwpcontrol jobs watch <job-id> --timeout 120
334
+ ```
335
+
336
+ Or use `--wait` on the original command to block until completion:
337
+
338
+ ```bash
339
+ mainwpcontrol abilities run sync-sites-v1 --wait --wait-timeout 300 --json
340
+ ```
341
+
342
+ ---
343
+
344
+ ## Advanced Usage
345
+
346
+ ### CI/CD Patterns
347
+
348
+ ```bash
349
+ # Non-interactive login
350
+ export MAINWP_APP_PASSWORD='xxxx xxxx xxxx xxxx xxxx xxxx'
351
+ mainwpcontrol login --url https://dashboard.example.com --username admin
352
+
353
+ # Silent execution with exit codes
354
+ mainwpcontrol abilities run list-sites-v1 --json --quiet
355
+ echo "Exit code: $?"
356
+
357
+ # Pipeline branching on exit codes
358
+ if mainwpcontrol abilities run check-sites-v1 --json --quiet; then
359
+ echo "All sites healthy"
360
+ else
361
+ echo "Issues detected"
362
+ fi
363
+ ```
364
+
365
+ ### Input from Files and Stdin
366
+
367
+ ```bash
368
+ # From a JSON file
369
+ mainwpcontrol abilities run update-site-plugins-v1 --input-file params.json --json
370
+
371
+ # From stdin
372
+ echo '{"site_id": 1}' | mainwpcontrol abilities run get-site-v1 --input - --json
373
+
374
+ # Heredoc
375
+ mainwpcontrol abilities run get-site-v1 --input - --json <<EOF
376
+ {"site_id": 1}
377
+ EOF
378
+ ```
379
+
380
+ ### Chat Mode Configuration
381
+
382
+ Chat requires one of these environment variables:
383
+
384
+ | Variable | Provider |
385
+ |----------|----------|
386
+ | `ANTHROPIC_API_KEY` | Anthropic Claude |
387
+ | `OPENAI_API_KEY` | OpenAI GPT |
388
+ | `GOOGLE_API_KEY` | Google Gemini |
389
+ | `OPENROUTER_API_KEY` | OpenRouter |
390
+ | `LOCAL_LLM_API_KEY` | Local LLM (with optional `LOCAL_LLM_URL`) |
391
+
392
+ Additional chat flags: `--provider`, `--model`, `--max-turns`, `--max-context-messages`, `--no-stream`.
393
+
394
+ In non-TTY environments (pipes, CI), `mainwpcontrol chat` without a message argument exits with guidance instead of hanging.
395
+
396
+ ### Shell Completion
397
+
398
+ ```bash
399
+ # Bash
400
+ source /path/to/mainwp-control/scripts/completions/mainwpcontrol.bash
401
+
402
+ # Zsh
403
+ source /path/to/mainwp-control/scripts/completions/mainwpcontrol.zsh
404
+ ```
405
+
406
+ ### Configuration File
407
+
408
+ Settings live at `~/.config/mainwpcontrol/settings.json`:
409
+
410
+ ```json
411
+ {
412
+ "defaultJsonOutput": true,
413
+ "timeout": 30000,
414
+ "debug": false,
415
+ "llmProvider": "openai",
416
+ "chatContextMessages": 20
417
+ }
418
+ ```
419
+
420
+ ### Workflow Guides
421
+
422
+ Step-by-step guides for common automation patterns:
423
+
424
+ | Workflow | Description |
425
+ |----------|-------------|
426
+ | [Daily Health Check](docs/workflows/daily-health-check.md) | Cron job that checks site connectivity and alerts via Slack |
427
+ | [Plugin Deployment Verification](docs/workflows/plugin-deployment-verification.md) | GitHub Actions workflow to verify a plugin exists across all sites |
428
+ | [Monthly Batch Updates](docs/workflows/monthly-batch-updates.md) | Preview and apply updates safely, scripted and GitHub Actions variants |
429
+ | [Input from File](docs/workflows/input-from-file.md) | Pass complex parameters via JSON files, stdin pipes, or heredocs |
430
+ | [Monitoring Integration](docs/workflows/monitoring-integration.md) | Send site metrics to Datadog, StatsD, or other monitoring tools |
431
+
432
+ ---
433
+
434
+ ## Reference
435
+
436
+ ### Exit Codes
437
+
438
+ | Code | Meaning | CI Usage |
439
+ |------|---------|----------|
440
+ | 0 | Success | Continue pipeline |
441
+ | 1 | User/input error | Fix command syntax |
442
+ | 2 | Auth/config error | Check credentials |
443
+ | 3 | Network error | Retry or check connectivity |
444
+ | 4 | API error | Check ability parameters |
445
+ | 5 | Internal error | Report bug |
446
+
447
+ ### Environment Variables
448
+
449
+ #### MainWP Configuration
450
+
451
+ | Variable | Description |
452
+ |----------|-------------|
453
+ | `MAINWP_APP_PASSWORD` | Application password for non-interactive login and commands when keychain is unavailable |
454
+ | `MAINWPCONTROL_NO_KEYTAR` | Set to `1` to skip keytar (keychain) loading entirely |
455
+ | `MAINWP_ALLOW_HTTP` | Set to `1` to allow insecure HTTP Dashboard URLs |
456
+
457
+ #### Chat/LLM Configuration
458
+
459
+ | Variable | Description |
460
+ |----------|-------------|
461
+ | `ANTHROPIC_API_KEY` | Anthropic Claude |
462
+ | `OPENAI_API_KEY` | OpenAI GPT |
463
+ | `GOOGLE_API_KEY` | Google Gemini |
464
+ | `OPENROUTER_API_KEY` | OpenRouter |
465
+ | `LOCAL_LLM_API_KEY` | Local LLM provider (required to enable local provider) |
466
+ | `LOCAL_LLM_URL` | Local endpoint URL (optional, defaults to localhost) |
467
+ | `MAINWP_LLM_PROVIDER` | Override auto-detected provider |
468
+ | `MAINWP_LLM_MODEL` | Specify model to use |
469
+
470
+ ### Configuration Settings
471
+
472
+ All settings in `~/.config/mainwpcontrol/settings.json`:
473
+
474
+ | Setting | Type | Description |
475
+ |---------|------|-------------|
476
+ | `defaultJsonOutput` | boolean | Default to JSON output |
477
+ | `timeout` | number | HTTP request timeout in milliseconds |
478
+ | `debug` | boolean | Enable debug output |
479
+ | `llmProvider` | string | Default LLM provider for chat |
480
+ | `chatContextMessages` | number | Max messages in chat context |
481
+ | `skipSSLVerification` | boolean | Disable TLS verification (insecure, prefer per-profile setting via `login --skip-ssl-verify`) |
482
+ | `allowInsecureHttp` | boolean | Allow `http://` Dashboard URLs without `MAINWP_ALLOW_HTTP=1` |
483
+
484
+ ---
485
+
486
+ ## Troubleshooting
487
+
488
+ <details>
489
+ <summary><strong>"keytar failed to build" or native module errors during install</strong></summary>
490
+
491
+ Keytar requires native C++ compilation on some platforms. If it fails:
492
+
493
+ 1. **Use environment variable auth instead** (bypasses keytar entirely):
494
+ ```bash
495
+ export MAINWP_APP_PASSWORD='your-application-password'
496
+ mainwpcontrol login --url https://dashboard.example.com --username admin
497
+ ```
498
+ 2. **Or skip keytar explicitly** by setting `MAINWPCONTROL_NO_KEYTAR=1` before running commands.
499
+
500
+ The pre-built binaries cover macOS, Windows, and Linux (x64/arm64). If you're on a different platform or architecture, you'll need C++ build tools (`gcc`, `g++`, `make`) or the env var approach.
501
+
502
+ </details>
503
+
504
+ <details>
505
+ <summary><strong>"command not found" after install</strong></summary>
506
+
507
+ This usually means your npm global bin directory isn't in your system PATH.
508
+
509
+ 1. **Find where npm installs global packages:**
510
+ ```bash
511
+ npm config get prefix
512
+ ```
513
+ 2. **Add the `bin` subdirectory to your PATH.** For example, if the prefix is `/usr/local`:
514
+ ```bash
515
+ # Add to ~/.bashrc, ~/.zshrc, or your shell profile:
516
+ export PATH="/usr/local/bin:$PATH"
517
+ ```
518
+ 3. **Restart your terminal** (or run `source ~/.zshrc` / `source ~/.bashrc`) and try again.
519
+
520
+ On Windows, the npm global directory is usually already in PATH after installing Node.js.
521
+
522
+ </details>
523
+
524
+ <details>
525
+ <summary><strong>"connection refused" or network errors</strong></summary>
526
+
527
+ If `mainwpcontrol login` or commands fail with connection errors:
528
+
529
+ 1. **Check the Dashboard URL.** Make sure it's the full URL with `https://` (e.g., `https://dashboard.example.com`). Don't include a trailing slash.
530
+ 2. **Verify HTTPS.** `mainwpcontrol` requires HTTPS by default. If your Dashboard uses HTTP (not recommended), set `MAINWP_ALLOW_HTTP=1`.
531
+ 3. **Check firewall/network.** Make sure your machine can reach the Dashboard:
532
+ ```bash
533
+ curl -I https://dashboard.example.com
534
+ ```
535
+ 4. **SSL certificate issues.** If using a self-signed certificate, you can use `mainwpcontrol login --skip-ssl-verify` (not recommended for production).
536
+
537
+ </details>
538
+
539
+ ---
540
+
541
+ ## Contributing
542
+
543
+ ```bash
544
+ npm run build # Build the project
545
+ npm test # Run tests (unit + e2e, no network needed)
546
+ npm run lint # Check code style
547
+ ```
548
+
549
+ ### Live Integration Tests
550
+
551
+ `npm run test:live` runs tests against a real MainWP Dashboard, including workflow documentation validation. These require a running Dashboard and credentials:
552
+
553
+ ```bash
554
+ export MAINWP_API_URL=https://your-dashboard.example.com
555
+ export MAINWP_USER=your-admin-username
556
+ export MAINWP_APP_PASSWORD=your-application-password
557
+
558
+ npm run test:live
559
+ ```
560
+
561
+ The live suite includes API tests (login, abilities discovery, read-only execution, safety model, exit codes) and workflow doc tests (validates that every jq expression, field name, and data pipeline documented in `docs/workflows/` works against the real API).
562
+
563
+ Live tests are safe: they only run read-only operations and `--dry-run` previews, never mutations.
564
+
565
+ ---
566
+
567
+ ## License
568
+
569
+ GPL-3.0-or-later
570
+
571
+ ---
572
+
573
+ ## Requirements
574
+
575
+ - Node.js 20 LTS or later
576
+ - MainWP Dashboard 6+ with Abilities API
577
+ - WordPress Application Password
578
+
579
+ ---
580
+
581
+ - [MainWP](https://mainwp.com/)
582
+ - [MainWP MCP Server](https://github.com/mainwp/mainwp-mcp)
583
+ - [Issue Tracker](https://github.com/mainwp/mainwp-control/issues)
package/bin/_exit.js ADDED
@@ -0,0 +1,12 @@
1
+ // Shared entrypoint setup: SIGPIPE handling and clean exit for native addons.
2
+
3
+ // SIGPIPE: exit cleanly when piped to `head`, `grep -q`, etc.
4
+ process.on('SIGPIPE', () => process.exit(0));
5
+
6
+ // Force exit to prevent native addon handles (e.g. keytar) from keeping
7
+ // the process alive. Drain stdout/stderr first to avoid truncating piped output.
8
+ const drain = (s) => new Promise((resolve) => s.write('', resolve));
9
+ export async function drainAndExit() {
10
+ await Promise.all([drain(process.stdout), drain(process.stderr)]);
11
+ process.exit(process.exitCode ?? 0);
12
+ }
package/bin/dev.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { drainAndExit } from './_exit.js';
4
+ import { execute } from '@oclif/core';
5
+
6
+ await execute({ development: true, dir: import.meta.url });
7
+ await drainAndExit();
package/bin/run.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { drainAndExit } from './_exit.js';
4
+ import { execute } from '@oclif/core';
5
+
6
+ await execute({ dir: import.meta.url });
7
+ await drainAndExit();