@lacneu/wix-openclaw 0.0.1

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 (59) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/LICENSE +21 -0
  3. package/README.md +483 -0
  4. package/dist/config.d.ts +19 -0
  5. package/dist/config.js +60 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/hooks/approval.d.ts +29 -0
  8. package/dist/hooks/approval.js +65 -0
  9. package/dist/hooks/approval.js.map +1 -0
  10. package/dist/index.d.ts +18 -0
  11. package/dist/index.js +97 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/tools/_factory.d.ts +40 -0
  14. package/dist/tools/_factory.js +69 -0
  15. package/dist/tools/_factory.js.map +1 -0
  16. package/dist/tools/blog.d.ts +140 -0
  17. package/dist/tools/blog.js +191 -0
  18. package/dist/tools/blog.js.map +1 -0
  19. package/dist/tools/bookings.d.ts +116 -0
  20. package/dist/tools/bookings.js +97 -0
  21. package/dist/tools/bookings.js.map +1 -0
  22. package/dist/tools/contacts.d.ts +216 -0
  23. package/dist/tools/contacts.js +128 -0
  24. package/dist/tools/contacts.js.map +1 -0
  25. package/dist/tools/data.d.ts +114 -0
  26. package/dist/tools/data.js +129 -0
  27. package/dist/tools/data.js.map +1 -0
  28. package/dist/tools/design.d.ts +32 -0
  29. package/dist/tools/design.js +165 -0
  30. package/dist/tools/design.js.map +1 -0
  31. package/dist/tools/events.d.ts +66 -0
  32. package/dist/tools/events.js +70 -0
  33. package/dist/tools/events.js.map +1 -0
  34. package/dist/tools/faq.d.ts +98 -0
  35. package/dist/tools/faq.js +90 -0
  36. package/dist/tools/faq.js.map +1 -0
  37. package/dist/tools/forms.d.ts +46 -0
  38. package/dist/tools/forms.js +63 -0
  39. package/dist/tools/forms.js.map +1 -0
  40. package/dist/tools/media.d.ts +50 -0
  41. package/dist/tools/media.js +75 -0
  42. package/dist/tools/media.js.map +1 -0
  43. package/dist/tools/multilingual.d.ts +38 -0
  44. package/dist/tools/multilingual.js +48 -0
  45. package/dist/tools/multilingual.js.map +1 -0
  46. package/dist/tools/reviews.d.ts +62 -0
  47. package/dist/tools/reviews.js +72 -0
  48. package/dist/tools/reviews.js.map +1 -0
  49. package/dist/tools/site.d.ts +36 -0
  50. package/dist/tools/site.js +94 -0
  51. package/dist/tools/site.js.map +1 -0
  52. package/dist/types.d.ts +58 -0
  53. package/dist/types.js +7 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/wix-client.d.ts +66 -0
  56. package/dist/wix-client.js +194 -0
  57. package/dist/wix-client.js.map +1 -0
  58. package/openclaw.plugin.json +95 -0
  59. package/package.json +72 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,68 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Changed
11
+ - `wix_site_url_get` now reads `viewUrl` from the Site List API response
12
+ rather than calling the legacy `/urls-server/v2/published-site-urls`
13
+ endpoint, which returns 403 for headless API keys even when the
14
+ "Read Site URLs" permission is granted. The "Read Site URLs"
15
+ permission is no longer required.
16
+ - Forms submission tools migrated from `/form-submissions/v1/...` to
17
+ `/form-submission-service/v4/submissions/namespace/query`. The query
18
+ filter now uses the canonical `{ namespace: { $eq: "..." } }` shape
19
+ enforced by Wix.
20
+ - Multilingual tools migrated from `/multilingual/v1/sitetranslatables/...`
21
+ to the current `/locales/v2/locale/query` and
22
+ `/translation-schema/v1/schemas/site` endpoints. `wix_multilingual_get_translation`
23
+ removed in favour of `wix_multilingual_list_schemas`.
24
+ - Sites + business info endpoints corrected:
25
+ `wix_sites_list` → `POST /site-list/v2/sites/query`,
26
+ `wix_business_info_get` → `GET /site-properties/v4/properties`.
27
+ - Documented the per-app installation requirements that gate certain
28
+ tool families (Velo for `wix_data_*`, Wix Events / Reviews / FAQ /
29
+ Multilingual for their respective tool groups).
30
+
31
+ ### Added
32
+ - `scripts/smoke-test.mjs` + `npm run smoke-test` — live read-only
33
+ endpoint validation against a real Wix account using `.env.smoketest`
34
+ credentials. Generates `smoke-test-report.md`.
35
+
36
+ ## [0.1.0] - 2026-04-28
37
+
38
+ ### Added
39
+ - Initial release of `@lacneu/wix-openclaw`.
40
+ - ~50 tools spanning Wix Blog, Site Media, Sites, CMS Data, Forms, Contacts,
41
+ Bookings, Events, Reviews, FAQ, and Multilingual (read-only).
42
+ - `wix_design_brief` tool — pure prompt structurer that turns a loose textual
43
+ brief into a markdown artefact (palette, typography, recommended sections,
44
+ copy starters, prompts for image-generation tools). Does not call the Wix API.
45
+ - Authenticated `WixClient` with the Wix-specific header convention
46
+ (`Authorization: <key>` raw — no `Bearer ` prefix), automatic injection of
47
+ `wix-account-id` and `wix-site-id`, retry on 429/5xx with exponential
48
+ backoff, and JSON serialisation.
49
+ - **Site whitelist**: every site-scoped call is checked against
50
+ `allowedSiteIds` before any HTTP request is built. Calls to a site outside
51
+ the list throw `WixSiteNotAllowedError` and surface as a tool failure.
52
+ - **Approval gating** via the `before_tool_call` hook: every destructive
53
+ operation (publish, delete, cancel, moderate) returns a `requireApproval`
54
+ directive with severity `critical`, 10-minute timeout, and `deny` as the
55
+ timeout fallback.
56
+ - TypeScript build with strict mode (`noImplicitAny`, `noUnusedLocals`,
57
+ `noUnusedParameters`, `noImplicitReturns`, `noFallthroughCasesInSwitch`).
58
+ - TypeBox-based parameter schemas — the agent sees fully typed params
59
+ consistent with the rest of the OpenClaw tool surface.
60
+ - Test suite (`node:test`) covering the security-critical paths:
61
+ whitelist enforcement, header injection, 429 retry, approval matching,
62
+ and a smoke test on two blog tools.
63
+ - CI workflow on Node.js 22 + 24.
64
+ - Release workflow using **npm Trusted Publishing (OIDC)** — no `NPM_TOKEN`
65
+ secret required, provenance attached automatically.
66
+
67
+ [Unreleased]: https://github.com/OlivierNeu/wix-openclaw-plugin/compare/v0.1.0...HEAD
68
+ [0.1.0]: https://github.com/OlivierNeu/wix-openclaw-plugin/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OlivierNeu
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 ADDED
@@ -0,0 +1,483 @@
1
+ # wix-openclaw-plugin
2
+
3
+ > **Wix REST API plugin for OpenClaw**
4
+ > Manage blog, CMS data, forms, bookings, contacts, events, FAQ on a single
5
+ > Wix site — with **site_id whitelist** and **approval gating** on every
6
+ > destructive operation.
7
+
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
+ [![OpenClaw](https://img.shields.io/badge/OpenClaw-%E2%89%A5v2026.4.0-blue)](https://github.com/openclaw/openclaw)
10
+ [![npm version](https://img.shields.io/npm/v/@lacneu/wix-openclaw.svg)](https://www.npmjs.com/package/@lacneu/wix-openclaw)
11
+
12
+ ---
13
+
14
+ ## Overview
15
+
16
+ `wix-openclaw` is an OpenClaw plugin that lets your agent operate a Wix site
17
+ through the official Wix REST API. It registers ~50 tools split across a dozen
18
+ Wix products (Blog, CMS Data, Forms, Bookings, Contacts, Events, Reviews, FAQ,
19
+ Multilingual, Sites, Site Media), plus a `wix_design_brief` helper that turns
20
+ loose textual briefs into structured markdown artefacts.
21
+
22
+ Every tool funnels through a single authenticated HTTP client that:
23
+
24
+ 1. Injects the correct headers (`Authorization`, `wix-account-id`,
25
+ `wix-site-id`)
26
+ 2. Refuses any call to a site UUID not in `allowedSiteIds` — **before** the
27
+ network round trip
28
+ 3. Retries on 429/5xx with exponential backoff
29
+ 4. Maps non-retryable errors to clean tool failures (`WixApiError`)
30
+
31
+ Destructive operations (publish, delete, cancel, moderate) trigger a
32
+ `before_tool_call` approval prompt that the operator must accept in the
33
+ Control UI / Telegram before the call goes through.
34
+
35
+ ---
36
+
37
+ ## Why a whitelist?
38
+
39
+ Wix accounts can host many sites. The `wix-site-id` header is the only thing
40
+ between an agent and "we just published a draft on the wrong site".
41
+
42
+ `allowedSiteIds` is enforced in the client itself, **before** any HTTP request
43
+ is built. If the LLM hallucinates a site UUID, the call fails locally — no
44
+ traffic is generated and no Wix logs are polluted. This is the same reason
45
+ `google_workspace` enforces a domain allowlist: prompt injection should never
46
+ reach the wrong tenant.
47
+
48
+ ---
49
+
50
+ ## Installation
51
+
52
+ ### Requirements
53
+
54
+ - OpenClaw `>= v2026.4.0`
55
+ - A Wix REST API key with the **minimum required permissions** (see
56
+ [Wix API Key permissions](#wix-api-key-permissions) below)
57
+ - The Wix account UUID and at least one site UUID
58
+
59
+ ### Install via OpenClaw CLI
60
+
61
+ ```bash
62
+ openclaw plugins install @lacneu/wix-openclaw
63
+ openclaw plugins inspect @lacneu/wix-openclaw
64
+ ```
65
+
66
+ ### Configuration
67
+
68
+ Add the entry to your `openclaw.json`:
69
+
70
+ ```json
71
+ {
72
+ "plugins": {
73
+ "allow": ["wix-openclaw", "openclaw-knowledge", "telegram"],
74
+ "entries": {
75
+ "wix-openclaw": {
76
+ "enabled": true,
77
+ "config": {
78
+ "apiKey": "${WIX_API_KEY}",
79
+ "accountId": "${WIX_ACCOUNT_ID}",
80
+ "allowedSiteIds": ["${WIX_SITE_ID_ATARAXIS}"],
81
+ "defaultSiteId": "${WIX_SITE_ID_ATARAXIS}",
82
+ "logLevel": "info"
83
+ }
84
+ }
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ Add the secrets to your `.env`:
91
+
92
+ ```dotenv
93
+ WIX_API_KEY=<your-wix-api-key>
94
+ WIX_ACCOUNT_ID=<your-wix-account-uuid>
95
+ WIX_SITE_ID_ATARAXIS=<your-target-site-uuid>
96
+ ```
97
+
98
+ Restart the gateway:
99
+
100
+ ```bash
101
+ openclaw gateway restart
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Configuration reference
107
+
108
+ | Field | Type | Default | Description |
109
+ |---|---|---|---|
110
+ | `enabled` | boolean | `true` | Master switch — disables all `wix_*` tools when false |
111
+ | `apiKey` | string | — | Wix REST API key. Sent **raw** in `Authorization` (no `Bearer ` prefix). Supports `${ENV_VAR}` |
112
+ | `accountId` | string | — | Wix account UUID. Sent as `wix-account-id`. Supports `${ENV_VAR}` |
113
+ | `allowedSiteIds` | string[] | `[]` | Whitelist of Wix site UUIDs. Calls to other sites are rejected before the network round trip |
114
+ | `defaultSiteId` | string | first of `allowedSiteIds` | Site UUID injected when a tool does not explicitly specify one |
115
+ | `approvalRequired` | string[] | (every destructive tool) | Tool names that trigger a `before_tool_call` approval prompt |
116
+ | `logLevel` | enum | `info` | `debug` / `info` / `warn` / `error`. `debug` logs request bodies (mind PII) |
117
+
118
+ ---
119
+
120
+ ## Authentication header convention
121
+
122
+ Wix REST APIs use a slightly non-standard auth pattern: the API key goes raw
123
+ in the `Authorization` header — **without** the `Bearer ` prefix.
124
+
125
+ ```http
126
+ Authorization: <wix-api-key>
127
+ wix-account-id: <wix-account-uuid>
128
+ wix-site-id: <wix-site-uuid>
129
+ ```
130
+
131
+ This plugin handles that for you; you only need to provide the three values.
132
+
133
+ ---
134
+
135
+ ## Wix API Key permissions
136
+
137
+ A Wix REST API key has **account-wide reach** by design — it can target any
138
+ of the sites under the Wix account it belongs to. Wix does **not** let you
139
+ scope a key to a single site at creation time. The plugin compensates with
140
+ its `allowedSiteIds` whitelist (cf. [Why a whitelist?](#why-a-whitelist)),
141
+ but the API key permission set itself remains your most important defense
142
+ against accidental — or malicious — over-reach. Treat it as a
143
+ least-privilege configuration exercise.
144
+
145
+ > **Where to configure them**
146
+ > Wix Dashboard → **Settings** → **Headless Settings** → **API Keys** →
147
+ > click your key → **Edit API Key** → **Permissions**.
148
+
149
+ ### Minimum required (for the V1 toolset shipped here)
150
+
151
+ Tick **only** the following permissions on your API key. Everything else
152
+ should stay unticked.
153
+
154
+ #### Basic permissions
155
+ - [x] **Get Sites List** — required to discover and validate site UUIDs
156
+
157
+ #### Site permissions
158
+ - [x] **Wix Blog** — drafts, posts, categories, tags
159
+ - [x] **Manage Site Media** — upload images for blog cover & rich content
160
+ - [x] **Business Info** — name, address, hours, timezone
161
+ - [x] **Wix Data** — custom CMS collections (read & write)
162
+ - [x] **Wix Forms** — read submitted forms
163
+ - [x] **Manage Form Submissions** — read leads / contact requests
164
+ - [x] **Wix Contacts & Members** — CRM contacts + members
165
+ - [x] **Wix Bookings** — services, sessions, bookings
166
+ - [x] **Wix Events** — events, RSVPs, guests
167
+ - [x] **Wix Reviews** — list & moderate reviews
168
+ - [x] **Manage FAQ** — FAQ categories and questions
169
+ - [x] **Multilingual Translation Schema Read** — read site translations
170
+ - [x] **List Marketing Tags** — read-only marketing/analytics tags
171
+
172
+ ### Optional, depending on the business model
173
+
174
+ Tick these only if the corresponding Wix product is actually in use on
175
+ the target site:
176
+
177
+ - [ ] **Pricing Plans** — if you sell subscription packages
178
+ - [ ] **Wix Stores** + **Wix eCommerce** — if you sell physical/digital
179
+ products. Without these, the `wix_*` tools that touch e-commerce
180
+ resources (none in V1) cannot run.
181
+
182
+ ### Permissions to **never** tick (defense in depth)
183
+
184
+ The plugin does **not** need any of the following, and granting them
185
+ needlessly amplifies the blast radius if the key ever leaks. Audit your
186
+ key periodically and keep these unticked:
187
+
188
+ | Permission | Why it's dangerous |
189
+ |---|---|
190
+ | `Manage Sites` | Allows site **deletion** |
191
+ | `Manage Domains` | Allows transferring your domain away |
192
+ | `Publish Metasite` | Allows un-publishing the entire site |
193
+ | `Manage Embedded Scripts` | Allows arbitrary JavaScript injection on every page |
194
+ | `OAuth Apps` | Allows creating persistent OAuth apps that survive key revocation |
195
+ | `Manage Functions` / `Manage Your App` | Arbitrary server-side code |
196
+ | `Connect Wix Payments Account` / `Wix Payments` / `Wix Cashier` | Reroutes customer payments |
197
+ | `Manage Email Marketing` / `Manage Email Subscriptions` | Mass-email spam & subscription tampering |
198
+ | `Server Sign On for Members` | Lets the holder impersonate any member |
199
+ | `Manage Roles` / `Manage Site Members` | Account take-over |
200
+ | `Wix CLI - Git Integration` | Modify deployed code |
201
+ | `Multilingual Translation Schema Write` | Tamper with on-site copy across languages |
202
+ | `Manage Notifications` | Push spam to members |
203
+ | `Invoke AI Models` | Bills your account for arbitrary LLM usage |
204
+ | `Manage Wixel Projects` / `Manage Site Branches` | Layout/structure mutation |
205
+ | Any **All account permissions** master toggle | Unties every account-wide capability above |
206
+
207
+ ### Mapping: tool group → required permission
208
+
209
+ Use this table to figure out which permission a given group of tools
210
+ relies on. If you want a slimmer key (e.g. blog-only first), drop entire
211
+ columns and remove the matching tools from your agent's allow-list.
212
+
213
+ | Tool group | Wix permission | Required if you use… |
214
+ |---|---|---|
215
+ | `wix_sites_list`, `wix_site_url_get` | Get Sites List | Always (used internally — `wix_site_url_get` reads `viewUrl` from the Site object, no separate "Read Site URLs" permission needed) |
216
+ | `wix_business_info_get` | Business Info | `wix_business_info_get` |
217
+ | `wix_blog_*` | Wix Blog | Any blog tool |
218
+ | `wix_media_*` | Manage Site Media | Any media tool, **and** rich-content blog drafts |
219
+ | `wix_data_*` | Wix Data | Any CMS collection tool |
220
+ | `wix_forms_*` | Wix Forms + Manage Form Submissions | Lead reading |
221
+ | `wix_contacts_*` | Wix Contacts & Members | CRM tooling |
222
+ | `wix_bookings_*` | Wix Bookings | Booking tooling |
223
+ | `wix_events_*` | Wix Events | Event tooling |
224
+ | `wix_reviews_*` | Wix Reviews | Reviews moderation |
225
+ | `wix_faq_*` | Manage FAQ | FAQ tooling |
226
+ | `wix_multilingual_*` | Multilingual Translation Schema Read | Translation reads |
227
+ | `wix_design_brief` | _(none — no API call)_ | Always |
228
+
229
+ ### Wix app installation requirements
230
+
231
+ Some Wix REST endpoints only exist when the corresponding **Wix app** is
232
+ installed on the target site. The plugin ships them all generically —
233
+ they will simply return a clean error when the app is missing, and start
234
+ working as soon as the app is installed (no plugin upgrade needed).
235
+
236
+ | Tool group | Wix app required | Error returned when missing |
237
+ |---|---|---|
238
+ | `wix_blog_*` | Wix Blog | `404` |
239
+ | `wix_data_*` | Velo / Wix Code (enable in Editor) | `400 WDE0110: Wix Code not enabled` |
240
+ | `wix_forms_*` | Wix Forms | `404` |
241
+ | `wix_bookings_*` | Wix Bookings | `404` |
242
+ | `wix_events_*` | Wix Events | `428 WIX_EVENTS_APP_NOT_INSTALLED` |
243
+ | `wix_reviews_*` | Wix Reviews | `428 APP_NOT_INSTALLED` |
244
+ | `wix_faq_*` | Wix FAQ | `400 App is not installed` |
245
+ | `wix_multilingual_*` | Wix Multilingual | `403` |
246
+ | `wix_contacts_*`, `wix_media_*`, `wix_sites_list`, `wix_site_url_get`, `wix_business_info_get` | (always available) | — |
247
+
248
+ To install a Wix app on your site: Wix Dashboard → **App Market** → search
249
+ for the app → **Add to Site**. It is free in most cases and takes effect
250
+ immediately for API calls.
251
+
252
+ ### Rotating the key
253
+
254
+ Wix keys can be revoked at any time from the same **API Keys** page. If
255
+ a key leaks, or if you need to ship a temporary smoke-test key with
256
+ narrower scope:
257
+
258
+ 1. Generate a new key with the desired scope.
259
+ 2. Update `WIX_API_KEY` in your environment.
260
+ 3. Restart the OpenClaw gateway.
261
+ 4. Revoke the old key.
262
+
263
+ The plugin reads the key on each request through the resolved config —
264
+ no in-memory caching beyond the gateway lifecycle, so a restart fully
265
+ flushes the previous credential.
266
+
267
+ ---
268
+
269
+ ## Tools
270
+
271
+ ### Blog
272
+
273
+ | Tool | What it does | Approval |
274
+ |---|---|---|
275
+ | `wix_blog_list_drafts` | List draft posts | — |
276
+ | `wix_blog_get_draft` | Fetch a draft by id | — |
277
+ | `wix_blog_create_draft` | Create a new draft | — |
278
+ | `wix_blog_update_draft` | Patch a draft | — |
279
+ | `wix_blog_publish_draft` | Publish a draft | required |
280
+ | `wix_blog_list_published` | List published posts | — |
281
+ | `wix_blog_get_post` | Fetch a published post | — |
282
+ | `wix_blog_unpublish` | Move post to trash (reversible) | required |
283
+ | `wix_blog_delete_draft` | Permanently delete a draft | required |
284
+ | `wix_blog_list_categories` | List blog categories | — |
285
+ | `wix_blog_list_tags` | List blog tags | — |
286
+
287
+ ### Media
288
+
289
+ | Tool | What it does | Approval |
290
+ |---|---|---|
291
+ | `wix_media_upload` | Import image into Site Media by URL | — |
292
+ | `wix_media_list` | List files in the media library | — |
293
+
294
+ The `wix_media_upload` tool returns the Wix file metadata, including the
295
+ `id`. Pass that id back into `wix_blog_create_draft` as
296
+ `coverMedia.imageId` or embed it into a Ricos rich-content node — see
297
+ the tool description for the contract the LLM should follow.
298
+
299
+ ### Site
300
+
301
+ | Tool | What it does | Approval |
302
+ |---|---|---|
303
+ | `wix_sites_list` | List sites under the account (account-scoped) | — |
304
+ | `wix_site_url_get` | Get published URL of a site | — |
305
+ | `wix_business_info_get` | Read business info (name, address, hours) | — |
306
+
307
+ ### CMS Data
308
+
309
+ | Tool | What it does | Approval |
310
+ |---|---|---|
311
+ | `wix_data_list_collections` | List CMS collections + schemas | — |
312
+ | `wix_data_query_items` | Mongo-style filter on a collection | — |
313
+ | `wix_data_get_item` | Fetch one item by id | — |
314
+ | `wix_data_insert_item` | Insert a new item | — |
315
+ | `wix_data_update_item` | Update an item | — |
316
+ | `wix_data_remove_item` | Delete an item | required |
317
+
318
+ ### Forms
319
+
320
+ | Tool | What it does | Approval |
321
+ |---|---|---|
322
+ | `wix_forms_list_submissions` | List submissions / leads | — |
323
+ | `wix_forms_get_submission` | Fetch a single submission | — |
324
+
325
+ ### Contacts
326
+
327
+ | Tool | What it does | Approval |
328
+ |---|---|---|
329
+ | `wix_contacts_query` | Search contacts | — |
330
+ | `wix_contacts_get` | Get one contact | — |
331
+ | `wix_contacts_create` | Create a contact | — |
332
+ | `wix_contacts_update` | Update a contact | — |
333
+ | `wix_contacts_label_add` | Add labels to a contact | — |
334
+ | `wix_contacts_delete` | Delete a contact | required |
335
+
336
+ ### Bookings
337
+
338
+ | Tool | What it does | Approval |
339
+ |---|---|---|
340
+ | `wix_bookings_services_list` | List bookable services | — |
341
+ | `wix_bookings_query_bookings` | Query existing bookings | — |
342
+ | `wix_bookings_get_booking` | Fetch one booking | — |
343
+ | `wix_bookings_create` | Create a booking | — |
344
+ | `wix_bookings_reschedule` | Move to a different slot | required |
345
+ | `wix_bookings_cancel` | Cancel a booking | required |
346
+
347
+ ### Events
348
+
349
+ | Tool | What it does | Approval |
350
+ |---|---|---|
351
+ | `wix_events_list` | List events | — |
352
+ | `wix_events_get` | Fetch one event | — |
353
+ | `wix_events_list_guests` | List guests / RSVPs / orders | — |
354
+
355
+ ### Reviews
356
+
357
+ | Tool | What it does | Approval |
358
+ |---|---|---|
359
+ | `wix_reviews_list` | List reviews | — |
360
+ | `wix_reviews_get` | Fetch one review | — |
361
+ | `wix_reviews_moderate` | Approve / reject a pending review | required |
362
+
363
+ ### FAQ
364
+
365
+ | Tool | What it does | Approval |
366
+ |---|---|---|
367
+ | `wix_faq_list_categories` | List FAQ categories | — |
368
+ | `wix_faq_list_questions` | List questions (filterable by category) | — |
369
+ | `wix_faq_create_question` | Create a question | — |
370
+ | `wix_faq_update_question` | Update a question | — |
371
+ | `wix_faq_delete_question` | Delete a question | required |
372
+
373
+ ### Multilingual (read-only in V1)
374
+
375
+ | Tool | What it does | Approval |
376
+ |---|---|---|
377
+ | `wix_multilingual_list_languages` | List configured languages | — |
378
+ | `wix_multilingual_get_translation` | Read a translation by key | — |
379
+
380
+ ### Design
381
+
382
+ | Tool | What it does | Approval |
383
+ |---|---|---|
384
+ | `wix_design_brief` | Turn a loose textual brief into a structured markdown artefact (palette, typography, sections, image-gen prompts). **Does NOT call the Wix API.** | — |
385
+
386
+ ---
387
+
388
+ ## Approval flow
389
+
390
+ When the LLM calls a destructive tool, the plugin's `before_tool_call` hook
391
+ fires and returns a `requireApproval` directive:
392
+
393
+ ```json
394
+ {
395
+ "requireApproval": {
396
+ "title": "Wix: confirm wix_blog_publish_draft",
397
+ "description": "Tool `wix_blog_publish_draft` is about to run with the following parameters:\n\n```json\n{ \"draftPostId\": \"abc-123\" }\n```\n...",
398
+ "severity": "critical",
399
+ "timeoutMs": 600000,
400
+ "timeoutBehavior": "deny"
401
+ }
402
+ }
403
+ ```
404
+
405
+ OpenClaw surfaces this to whichever channel/UI the operator is using
406
+ (Telegram inline buttons, Control UI badge, etc.). If no decision is made
407
+ within 10 minutes, the call is denied automatically.
408
+
409
+ You can override the gated set via `approvalRequired` in the plugin config —
410
+ use an empty array to disable gating entirely (not recommended in production).
411
+
412
+ ---
413
+
414
+ ## Development
415
+
416
+ ### Project layout
417
+
418
+ ```
419
+ wix-openclaw-plugin/
420
+ ├── src/
421
+ │ ├── index.ts # Entry point + tool collection
422
+ │ ├── config.ts # resolveEnv + default resolution
423
+ │ ├── wix-client.ts # Authenticated HTTP client + whitelist
424
+ │ ├── types.ts # Shared interfaces
425
+ │ ├── hooks/
426
+ │ │ └── approval.ts # before_tool_call approval gating
427
+ │ └── tools/
428
+ │ ├── _factory.ts # defineWixTool helper
429
+ │ ├── blog.ts
430
+ │ ├── media.ts
431
+ │ ├── site.ts
432
+ │ ├── data.ts
433
+ │ ├── forms.ts
434
+ │ ├── contacts.ts
435
+ │ ├── bookings.ts
436
+ │ ├── events.ts
437
+ │ ├── reviews.ts
438
+ │ ├── faq.ts
439
+ │ ├── multilingual.ts
440
+ │ └── design.ts
441
+ ├── test/
442
+ │ ├── wix-client.test.ts
443
+ │ ├── manifest.test.ts
444
+ │ ├── hooks/approval.test.ts
445
+ │ └── tools/blog.test.ts
446
+ ├── tsconfig.json
447
+ ├── tsconfig.test.json
448
+ ├── tsconfig.test-build.json
449
+ ├── openclaw.plugin.json
450
+ └── package.json
451
+ ```
452
+
453
+ ### Build and test
454
+
455
+ ```bash
456
+ npm install
457
+ npm run typecheck # strict TS check (src + test)
458
+ npm test # compile tests + run node:test
459
+ npm run build # compile src/ → dist/
460
+ npm run clean # remove dist + dist-test
461
+ ```
462
+
463
+ ### Release process
464
+
465
+ 1. Update `CHANGELOG.md` with a new `## [x.y.z] - YYYY-MM-DD` section
466
+ 2. Commit
467
+ 3. Tag and push:
468
+ ```bash
469
+ git tag v0.1.0
470
+ git push origin v0.1.0
471
+ ```
472
+ 4. GitHub Actions will:
473
+ - Run typecheck, tests, build on Node.js 24
474
+ - Stamp the version into `package.json` and `openclaw.plugin.json`
475
+ - Publish to npm via **Trusted Publishing (OIDC)** — no `NPM_TOKEN`
476
+ secret needed
477
+ - Create a GitHub Release with the changelog excerpt
478
+
479
+ ---
480
+
481
+ ## License
482
+
483
+ MIT — see [LICENSE](LICENSE)
@@ -0,0 +1,19 @@
1
+ import type { ResolvedWixConfig, WixPluginConfig } from "./types.js";
2
+ /**
3
+ * Expand `${VAR_NAME}` patterns in a config string against `process.env`.
4
+ * Non-string values are returned untouched so the helper can be used on any
5
+ * raw config field without type narrowing at the call site. Missing env vars
6
+ * become empty strings to avoid leaking `undefined` into downstream code.
7
+ */
8
+ export declare function resolveEnv<T>(value: T): T;
9
+ /**
10
+ * Apply defaults and env substitution to the raw plugin config.
11
+ *
12
+ * - `enabled` is true unless explicitly set to `false`.
13
+ * - `defaultSiteId`, when blank, falls back to the first `allowedSiteIds`
14
+ * entry. This keeps single-site setups (the typical case) one field
15
+ * shorter without surprising whitelist behaviour.
16
+ * - `approvalRequired` defaults to every destructive operation; pass an
17
+ * empty array to disable gating entirely.
18
+ */
19
+ export declare function resolveConfig(cfg?: WixPluginConfig): ResolvedWixConfig;
package/dist/config.js ADDED
@@ -0,0 +1,60 @@
1
+ // Plugin configuration helpers.
2
+ //
3
+ // These helpers are the only place that touches `process.env`, keeping the
4
+ // rest of the plugin easy to test with deterministic values.
5
+ /**
6
+ * Expand `${VAR_NAME}` patterns in a config string against `process.env`.
7
+ * Non-string values are returned untouched so the helper can be used on any
8
+ * raw config field without type narrowing at the call site. Missing env vars
9
+ * become empty strings to avoid leaking `undefined` into downstream code.
10
+ */
11
+ export function resolveEnv(value) {
12
+ if (typeof value !== "string")
13
+ return value;
14
+ return value.replace(/\$\{(\w+)\}/g, (_, name) => {
15
+ return process.env[name] ?? "";
16
+ });
17
+ }
18
+ const DEFAULT_APPROVAL_REQUIRED = [
19
+ "wix_blog_publish_draft",
20
+ "wix_blog_unpublish",
21
+ "wix_blog_delete_draft",
22
+ "wix_data_remove_item",
23
+ "wix_contacts_delete",
24
+ "wix_bookings_reschedule",
25
+ "wix_bookings_cancel",
26
+ "wix_reviews_moderate",
27
+ "wix_faq_delete_question",
28
+ ];
29
+ const VALID_LOG_LEVELS = ["debug", "info", "warn", "error"];
30
+ /**
31
+ * Apply defaults and env substitution to the raw plugin config.
32
+ *
33
+ * - `enabled` is true unless explicitly set to `false`.
34
+ * - `defaultSiteId`, when blank, falls back to the first `allowedSiteIds`
35
+ * entry. This keeps single-site setups (the typical case) one field
36
+ * shorter without surprising whitelist behaviour.
37
+ * - `approvalRequired` defaults to every destructive operation; pass an
38
+ * empty array to disable gating entirely.
39
+ */
40
+ export function resolveConfig(cfg = {}) {
41
+ const apiKey = resolveEnv(cfg.apiKey ?? "");
42
+ const accountId = resolveEnv(cfg.accountId ?? "");
43
+ const allowedSiteIds = (cfg.allowedSiteIds ?? []).map((id) => resolveEnv(id));
44
+ const explicitDefault = resolveEnv(cfg.defaultSiteId ?? "");
45
+ const defaultSiteId = explicitDefault || (allowedSiteIds[0] ?? "");
46
+ const approvalRequired = cfg.approvalRequired ?? DEFAULT_APPROVAL_REQUIRED;
47
+ const logLevel = VALID_LOG_LEVELS.includes(cfg.logLevel)
48
+ ? cfg.logLevel
49
+ : "info";
50
+ return {
51
+ enabled: cfg.enabled !== false,
52
+ apiKey,
53
+ accountId,
54
+ allowedSiteIds,
55
+ defaultSiteId,
56
+ approvalRequired: new Set(approvalRequired),
57
+ logLevel,
58
+ };
59
+ }
60
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,EAAE;AACF,2EAA2E;AAC3E,6DAA6D;AAQ7D;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAI,KAAQ;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,IAAY,EAAE,EAAE;QACvD,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACjC,CAAC,CAAiB,CAAC;AACrB,CAAC;AAED,MAAM,yBAAyB,GAAG;IAChC,wBAAwB;IACxB,oBAAoB;IACpB,uBAAuB;IACvB,sBAAsB;IACtB,qBAAqB;IACrB,yBAAyB;IACzB,qBAAqB;IACrB,sBAAsB;IACtB,yBAAyB;CAC1B,CAAC;AAEF,MAAM,gBAAgB,GAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAE3E;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAuB,EAAE;IAEzB,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,cAAc,GAAG,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9E,MAAM,eAAe,GAAG,UAAU,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC;IAC5D,MAAM,aAAa,GACjB,eAAe,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAE/C,MAAM,gBAAgB,GAAG,GAAG,CAAC,gBAAgB,IAAI,yBAAyB,CAAC;IAE3E,MAAM,QAAQ,GAAgB,gBAAgB,CAAC,QAAQ,CACrD,GAAG,CAAC,QAAuB,CAC5B;QACC,CAAC,CAAE,GAAG,CAAC,QAAwB;QAC/B,CAAC,CAAC,MAAM,CAAC;IAEX,OAAO;QACL,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,KAAK;QAC9B,MAAM;QACN,SAAS;QACT,cAAc;QACd,aAAa;QACb,gBAAgB,EAAE,IAAI,GAAG,CAAC,gBAAgB,CAAC;QAC3C,QAAQ;KACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { ResolvedWixConfig, WixLogger } from "../types.js";
2
+ /**
3
+ * Shape of a `before_tool_call` event payload — only the fields we use.
4
+ * Mirrors `PluginHookBeforeToolCallEvent` from the SDK.
5
+ */
6
+ export interface BeforeToolCallEvent {
7
+ toolName: string;
8
+ params: Record<string, unknown>;
9
+ runId?: string;
10
+ toolCallId?: string;
11
+ }
12
+ /**
13
+ * Subset of the SDK's `PluginHookBeforeToolCallResult` that we produce.
14
+ */
15
+ export interface BeforeToolCallResult {
16
+ requireApproval?: {
17
+ title: string;
18
+ description: string;
19
+ severity?: "info" | "warning" | "critical";
20
+ timeoutMs?: number;
21
+ timeoutBehavior?: "allow" | "deny";
22
+ };
23
+ }
24
+ /**
25
+ * Build a `before_tool_call` handler bound to a resolved Wix config.
26
+ * Extracted as a factory so tests can exercise it without a full plugin
27
+ * registration.
28
+ */
29
+ export declare function createApprovalHook(config: ResolvedWixConfig, logger: WixLogger): (event: BeforeToolCallEvent) => BeforeToolCallResult | undefined;