sbuilder-mcp 0.83.3 → 0.85.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/CHANGELOG.md +20 -0
- package/CHANGELOG.vi.md +20 -0
- package/README.md +1 -1
- package/README.vi.md +1 -1
- package/dist/catalog/api.generated.js +297 -3
- package/dist/catalog/deadkeys.generated.js +1 -1
- package/dist/catalog/shapes.generated.js +99 -15
- package/dist/catalog/source.generated.js +2 -2
- package/dist/core/patch.js +12 -1
- package/dist/domains/site/findings.js +2 -1
- package/dist/domains/site/readiness.js +3 -2
- package/dist/domains/site/review.js +20 -4
- package/dist/tools/api.js +26 -1
- package/dist/tools/page.js +2 -1
- package/dist/tools/pageupdate.js +2 -1
- package/dist/tools/store.js +3 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,26 @@ All notable changes to this project are documented in this file.
|
|
|
6
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.85.0] - 2026-10-11
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `sb_api_find` and `sb_api_call` reach 843 platform operations, including reading, editing and deleting a site (name, slug, subdomain redirect, `maintenanceMode`), site members and site export.
|
|
13
|
+
- `sb_review` suggests switching a shop in maintenance mode back on with `sb_api_call` on `patch:/api/sites/{siteId}`.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- `sb_api_call` names "the handler's body" instead of "(inline)" in its `unknown_keys` note when the handler has no named body struct.
|
|
17
|
+
- Issuing tickets through `tickets/issued` no longer requires an `eventId`.
|
|
18
|
+
|
|
19
|
+
## [0.84.0] - 2026-10-11
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- `sb_api_call` reports `unknown_keys` for body keys the handler's Go struct does not have, which the platform otherwise drops silently (for example `price` on a delivery method saves `feeCents` as 0 and still returns 201).
|
|
23
|
+
- `sb_review` flags `cartLine.*` bindings used outside a cart-order context as `no_data_context`.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- Undoing an `sb_move` rewrites only the node that changed instead of all five nodes on the page, because document comparisons no longer depend on the key order the platform returns.
|
|
27
|
+
- The request shape of the `/refund` and `/refund-via-gateway` operations is now `refundRequest` with `note`, so `sb_api_call` no longer validates them against the charge shape.
|
|
28
|
+
|
|
9
29
|
## [0.83.3] - 2026-10-11
|
|
10
30
|
|
|
11
31
|
### Fixed
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -6,6 +6,26 @@ Mọi thay đổi đáng chú ý của dự án được ghi lại trong file n
|
|
|
6
6
|
Định dạng dựa trên [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
và dự án tuân theo [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.85.0] - 2026-10-11
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `sb_api_find` và `sb_api_call` truy cập được 843 operation của platform, gồm đọc, sửa và xoá site (tên, slug, chuyển hướng subdomain, `maintenanceMode`), thành viên site và xuất site.
|
|
13
|
+
- `sb_review` gợi ý bật lại shop đang bảo trì bằng `sb_api_call` trên `patch:/api/sites/{siteId}`.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- `sb_api_call` ghi "the handler's body" thay vì "(inline)" trong ghi chú `unknown_keys` khi handler không có struct body được đặt tên.
|
|
17
|
+
- Phát vé qua `tickets/issued` không còn yêu cầu `eventId`.
|
|
18
|
+
|
|
19
|
+
## [0.84.0] - 2026-10-11
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- `sb_api_call` báo `unknown_keys` cho các key trong body mà struct Go của handler không có, vì platform vốn bỏ chúng lặng lẽ (ví dụ `price` trên phương thức giao hàng lưu `feeCents` bằng 0 mà vẫn trả 201).
|
|
23
|
+
- `sb_review` báo binding `cartLine.*` dùng ngoài ngữ cảnh cart-order là `no_data_context`.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- Hoàn tác một `sb_move` chỉ ghi lại node đã thay đổi thay vì cả năm node của trang, vì việc so sánh tài liệu không còn phụ thuộc thứ tự key mà platform trả về.
|
|
27
|
+
- Shape request của operation `/refund` và `/refund-via-gateway` giờ là `refundRequest` với `note`, nên `sb_api_call` không còn kiểm tra chúng theo shape của charge.
|
|
28
|
+
|
|
9
29
|
## [0.83.3] - 2026-10-11
|
|
10
30
|
|
|
11
31
|
### Fixed
|
package/README.md
CHANGED
|
@@ -118,7 +118,7 @@ in this client: one would put the very key the platform exists to hold back into
|
|
|
118
118
|
| `sb_store` | Run a store flow that must happen in a fixed order — `checkout` (the four writes that make a working one), `checkout_sync` (the store's current payment and delivery methods into every existing order form, checkout page republished), `form` (any of the platform's 17 templates with its own field document), `chrome` (one shared header or footer on a real site menu — desktop menu, mobile drawer, cart and account icons), `menu` (a menu node bound to the site's menu, its links resolved), `overlay_attach` (a pop-up or quick view on the open page), `cart` (the site's cart drawer, when it has none; `relocalize` puts an existing one in the site's language) and `app` (a built-in app plus the pages it needs), `global_attach` / `global_detach` (put an EXISTING shared section on the open page, or take it off), `global_promote` (make a section on the page a NEW shared section) and `template_save` (save a section as a reusable template) |
|
|
119
119
|
| `sb_undo` | Undo / redo this session's writes — page and form edits and `sb_api_call` PUTs; a page is refused if someone else saved it since. The platform's versions and history (`sb_page_version`) outlive this process |
|
|
120
120
|
|
|
121
|
-
Twenty-eight tools, **
|
|
121
|
+
Twenty-eight tools, **843 API operations** (309 of the 389 writes carrying a body shape read
|
|
122
122
|
off the handler), 123 elements, 94 binding sources. `sb_api_find`
|
|
123
123
|
is an index rather than a tool per endpoint, so the tool list stays short while everything
|
|
124
124
|
the platform can do stays reachable — and operations added to the platform arrive with the
|
package/README.vi.md
CHANGED
|
@@ -115,7 +115,7 @@ nhét ngược lại vào mọi bản cài.
|
|
|
115
115
|
| `sb_store` | Chạy một luồng cửa hàng bắt buộc đúng thứ tự — `checkout` (bốn lệnh ghi tạo nên trang thanh toán), `checkout_sync` (đưa phương thức thanh toán và giao hàng hiện tại vào mọi form đặt hàng đã có, publish lại trang thanh toán), `form` (một trong 17 template của nền tảng kèm field document của nó), `chrome` (một header hoặc footer dùng chung trên menu thật của site — menu desktop, drawer mobile, icon giỏ hàng và tài khoản), `menu` (một node menu bind vào menu của site, link đã phân giải), `overlay_attach` (một pop-up hay quick view trên trang đang mở), `cart` (ngăn giỏ hàng của site, khi chưa có; `relocalize` đưa ngăn giỏ đã có về ngôn ngữ của site), `app` (một app dựng sẵn kèm những trang nó cần), và `global_attach` / `global_detach` (đặt một section dùng chung ĐÃ CÓ lên trang đang mở, hoặc gỡ ra), `global_promote` (biến một section trên trang thành section dùng chung MỚI) và `template_save` (lưu một section làm mẫu dùng lại) |
|
|
116
116
|
| `sb_undo` | Hoàn tác / làm lại các thao tác ghi trong phiên — chỉnh sửa trang, form và PUT qua `sb_api_call`; trang bị từ chối nếu người khác đã lưu sau đó. Version và history của nền tảng (`sb_page_version`) sống lâu hơn process này |
|
|
117
117
|
|
|
118
|
-
Hai mươi tám tool, **
|
|
118
|
+
Hai mươi tám tool, **843 operation API** (309 trong 389 lệnh ghi có hình dạng body đọc thẳng
|
|
119
119
|
từ handler), 123 element, 94 nguồn binding. `sb_api_find` là
|
|
120
120
|
một chỉ mục chứ không phải mỗi endpoint một tool, nên danh sách tool vẫn ngắn trong khi mọi
|
|
121
121
|
thứ nền tảng làm được vẫn với tới — và operation mới thêm bên nền tảng sẽ tự có sau lần
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const SWAGGER_SOURCE = {
|
|
2
|
-
"operations":
|
|
2
|
+
"operations": 843,
|
|
3
3
|
"definitions": 278,
|
|
4
|
-
"bodyCarrying":
|
|
5
|
-
"bodyUndescribed":
|
|
4
|
+
"bodyCarrying": 373,
|
|
5
|
+
"bodyUndescribed": 102,
|
|
6
6
|
"generatedFrom": "server/docs/swagger.json"
|
|
7
7
|
};
|
|
8
8
|
export const API_OPERATIONS = [
|
|
@@ -4206,6 +4206,118 @@ export const API_OPERATIONS = [
|
|
|
4206
4206
|
"bodyRef": null,
|
|
4207
4207
|
"credential": "siteScoped"
|
|
4208
4208
|
},
|
|
4209
|
+
{
|
|
4210
|
+
"id": "get:/api/sites/{siteId}",
|
|
4211
|
+
"method": "GET",
|
|
4212
|
+
"path": "/api/sites/{siteId}",
|
|
4213
|
+
"tags": [
|
|
4214
|
+
"sites"
|
|
4215
|
+
],
|
|
4216
|
+
"summary": "Read, edit or trash one site",
|
|
4217
|
+
"params": [
|
|
4218
|
+
{
|
|
4219
|
+
"name": "siteId",
|
|
4220
|
+
"in": "path",
|
|
4221
|
+
"required": true,
|
|
4222
|
+
"type": "string",
|
|
4223
|
+
"description": "Site ID"
|
|
4224
|
+
},
|
|
4225
|
+
{
|
|
4226
|
+
"name": "site",
|
|
4227
|
+
"in": "body",
|
|
4228
|
+
"required": false,
|
|
4229
|
+
"type": "object",
|
|
4230
|
+
"description": "PATCH/PUT: {name?, slug?, subdomainRedirect?, subdomainRedirectCode?, maintenanceMode?}"
|
|
4231
|
+
}
|
|
4232
|
+
],
|
|
4233
|
+
"bodyDescribed": false,
|
|
4234
|
+
"bodyRef": null,
|
|
4235
|
+
"credential": "siteScoped"
|
|
4236
|
+
},
|
|
4237
|
+
{
|
|
4238
|
+
"id": "put:/api/sites/{siteId}",
|
|
4239
|
+
"method": "PUT",
|
|
4240
|
+
"path": "/api/sites/{siteId}",
|
|
4241
|
+
"tags": [
|
|
4242
|
+
"sites"
|
|
4243
|
+
],
|
|
4244
|
+
"summary": "Read, edit or trash one site",
|
|
4245
|
+
"params": [
|
|
4246
|
+
{
|
|
4247
|
+
"name": "siteId",
|
|
4248
|
+
"in": "path",
|
|
4249
|
+
"required": true,
|
|
4250
|
+
"type": "string",
|
|
4251
|
+
"description": "Site ID"
|
|
4252
|
+
},
|
|
4253
|
+
{
|
|
4254
|
+
"name": "site",
|
|
4255
|
+
"in": "body",
|
|
4256
|
+
"required": false,
|
|
4257
|
+
"type": "object",
|
|
4258
|
+
"description": "PATCH/PUT: {name?, slug?, subdomainRedirect?, subdomainRedirectCode?, maintenanceMode?}"
|
|
4259
|
+
}
|
|
4260
|
+
],
|
|
4261
|
+
"bodyDescribed": false,
|
|
4262
|
+
"bodyRef": null,
|
|
4263
|
+
"credential": "siteScoped"
|
|
4264
|
+
},
|
|
4265
|
+
{
|
|
4266
|
+
"id": "patch:/api/sites/{siteId}",
|
|
4267
|
+
"method": "PATCH",
|
|
4268
|
+
"path": "/api/sites/{siteId}",
|
|
4269
|
+
"tags": [
|
|
4270
|
+
"sites"
|
|
4271
|
+
],
|
|
4272
|
+
"summary": "Read, edit or trash one site",
|
|
4273
|
+
"params": [
|
|
4274
|
+
{
|
|
4275
|
+
"name": "siteId",
|
|
4276
|
+
"in": "path",
|
|
4277
|
+
"required": true,
|
|
4278
|
+
"type": "string",
|
|
4279
|
+
"description": "Site ID"
|
|
4280
|
+
},
|
|
4281
|
+
{
|
|
4282
|
+
"name": "site",
|
|
4283
|
+
"in": "body",
|
|
4284
|
+
"required": false,
|
|
4285
|
+
"type": "object",
|
|
4286
|
+
"description": "PATCH/PUT: {name?, slug?, subdomainRedirect?, subdomainRedirectCode?, maintenanceMode?}"
|
|
4287
|
+
}
|
|
4288
|
+
],
|
|
4289
|
+
"bodyDescribed": false,
|
|
4290
|
+
"bodyRef": null,
|
|
4291
|
+
"credential": "siteScoped"
|
|
4292
|
+
},
|
|
4293
|
+
{
|
|
4294
|
+
"id": "delete:/api/sites/{siteId}",
|
|
4295
|
+
"method": "DELETE",
|
|
4296
|
+
"path": "/api/sites/{siteId}",
|
|
4297
|
+
"tags": [
|
|
4298
|
+
"sites"
|
|
4299
|
+
],
|
|
4300
|
+
"summary": "Read, edit or trash one site",
|
|
4301
|
+
"params": [
|
|
4302
|
+
{
|
|
4303
|
+
"name": "siteId",
|
|
4304
|
+
"in": "path",
|
|
4305
|
+
"required": true,
|
|
4306
|
+
"type": "string",
|
|
4307
|
+
"description": "Site ID"
|
|
4308
|
+
},
|
|
4309
|
+
{
|
|
4310
|
+
"name": "site",
|
|
4311
|
+
"in": "body",
|
|
4312
|
+
"required": false,
|
|
4313
|
+
"type": "object",
|
|
4314
|
+
"description": "PATCH/PUT: {name?, slug?, subdomainRedirect?, subdomainRedirectCode?, maintenanceMode?}"
|
|
4315
|
+
}
|
|
4316
|
+
],
|
|
4317
|
+
"bodyDescribed": false,
|
|
4318
|
+
"bodyRef": null,
|
|
4319
|
+
"credential": "siteScoped"
|
|
4320
|
+
},
|
|
4209
4321
|
{
|
|
4210
4322
|
"id": "get:/api/sites/{siteId}/activity",
|
|
4211
4323
|
"method": "GET",
|
|
@@ -9175,6 +9287,27 @@ export const API_OPERATIONS = [
|
|
|
9175
9287
|
"bodyRef": null,
|
|
9176
9288
|
"credential": "siteScoped"
|
|
9177
9289
|
},
|
|
9290
|
+
{
|
|
9291
|
+
"id": "get:/api/sites/{siteId}/export",
|
|
9292
|
+
"method": "GET",
|
|
9293
|
+
"path": "/api/sites/{siteId}/export",
|
|
9294
|
+
"tags": [
|
|
9295
|
+
"sites"
|
|
9296
|
+
],
|
|
9297
|
+
"summary": "Download the whole site as one archive",
|
|
9298
|
+
"params": [
|
|
9299
|
+
{
|
|
9300
|
+
"name": "siteId",
|
|
9301
|
+
"in": "path",
|
|
9302
|
+
"required": true,
|
|
9303
|
+
"type": "string",
|
|
9304
|
+
"description": "Site ID"
|
|
9305
|
+
}
|
|
9306
|
+
],
|
|
9307
|
+
"bodyDescribed": false,
|
|
9308
|
+
"bodyRef": null,
|
|
9309
|
+
"credential": "siteScoped"
|
|
9310
|
+
},
|
|
9178
9311
|
{
|
|
9179
9312
|
"id": "get:/api/sites/{siteId}/filter-facets",
|
|
9180
9313
|
"method": "GET",
|
|
@@ -12052,6 +12185,167 @@ export const API_OPERATIONS = [
|
|
|
12052
12185
|
"bodyRef": null,
|
|
12053
12186
|
"credential": "siteScoped"
|
|
12054
12187
|
},
|
|
12188
|
+
{
|
|
12189
|
+
"id": "get:/api/sites/{siteId}/members",
|
|
12190
|
+
"method": "GET",
|
|
12191
|
+
"path": "/api/sites/{siteId}/members",
|
|
12192
|
+
"tags": [
|
|
12193
|
+
"sites"
|
|
12194
|
+
],
|
|
12195
|
+
"summary": "List or invite site members; change a member's role, or remove them",
|
|
12196
|
+
"params": [
|
|
12197
|
+
{
|
|
12198
|
+
"name": "siteId",
|
|
12199
|
+
"in": "path",
|
|
12200
|
+
"required": true,
|
|
12201
|
+
"type": "string",
|
|
12202
|
+
"description": "Site ID"
|
|
12203
|
+
},
|
|
12204
|
+
{
|
|
12205
|
+
"name": "member",
|
|
12206
|
+
"in": "body",
|
|
12207
|
+
"required": false,
|
|
12208
|
+
"type": "object",
|
|
12209
|
+
"description": "POST: {email, role}; PATCH/PUT: {role}"
|
|
12210
|
+
}
|
|
12211
|
+
],
|
|
12212
|
+
"bodyDescribed": false,
|
|
12213
|
+
"bodyRef": null,
|
|
12214
|
+
"credential": "siteScoped"
|
|
12215
|
+
},
|
|
12216
|
+
{
|
|
12217
|
+
"id": "post:/api/sites/{siteId}/members",
|
|
12218
|
+
"method": "POST",
|
|
12219
|
+
"path": "/api/sites/{siteId}/members",
|
|
12220
|
+
"tags": [
|
|
12221
|
+
"sites"
|
|
12222
|
+
],
|
|
12223
|
+
"summary": "List or invite site members; change a member's role, or remove them",
|
|
12224
|
+
"params": [
|
|
12225
|
+
{
|
|
12226
|
+
"name": "siteId",
|
|
12227
|
+
"in": "path",
|
|
12228
|
+
"required": true,
|
|
12229
|
+
"type": "string",
|
|
12230
|
+
"description": "Site ID"
|
|
12231
|
+
},
|
|
12232
|
+
{
|
|
12233
|
+
"name": "member",
|
|
12234
|
+
"in": "body",
|
|
12235
|
+
"required": false,
|
|
12236
|
+
"type": "object",
|
|
12237
|
+
"description": "POST: {email, role}; PATCH/PUT: {role}"
|
|
12238
|
+
}
|
|
12239
|
+
],
|
|
12240
|
+
"bodyDescribed": false,
|
|
12241
|
+
"bodyRef": null,
|
|
12242
|
+
"credential": "siteScoped"
|
|
12243
|
+
},
|
|
12244
|
+
{
|
|
12245
|
+
"id": "put:/api/sites/{siteId}/members/{userId}",
|
|
12246
|
+
"method": "PUT",
|
|
12247
|
+
"path": "/api/sites/{siteId}/members/{userId}",
|
|
12248
|
+
"tags": [
|
|
12249
|
+
"sites"
|
|
12250
|
+
],
|
|
12251
|
+
"summary": "List or invite site members; change a member's role, or remove them",
|
|
12252
|
+
"params": [
|
|
12253
|
+
{
|
|
12254
|
+
"name": "siteId",
|
|
12255
|
+
"in": "path",
|
|
12256
|
+
"required": true,
|
|
12257
|
+
"type": "string",
|
|
12258
|
+
"description": "Site ID"
|
|
12259
|
+
},
|
|
12260
|
+
{
|
|
12261
|
+
"name": "userId",
|
|
12262
|
+
"in": "path",
|
|
12263
|
+
"required": true,
|
|
12264
|
+
"type": "string",
|
|
12265
|
+
"description": "Member's user ID (/{userId} routes)"
|
|
12266
|
+
},
|
|
12267
|
+
{
|
|
12268
|
+
"name": "member",
|
|
12269
|
+
"in": "body",
|
|
12270
|
+
"required": false,
|
|
12271
|
+
"type": "object",
|
|
12272
|
+
"description": "POST: {email, role}; PATCH/PUT: {role}"
|
|
12273
|
+
}
|
|
12274
|
+
],
|
|
12275
|
+
"bodyDescribed": false,
|
|
12276
|
+
"bodyRef": null,
|
|
12277
|
+
"credential": "siteScoped"
|
|
12278
|
+
},
|
|
12279
|
+
{
|
|
12280
|
+
"id": "patch:/api/sites/{siteId}/members/{userId}",
|
|
12281
|
+
"method": "PATCH",
|
|
12282
|
+
"path": "/api/sites/{siteId}/members/{userId}",
|
|
12283
|
+
"tags": [
|
|
12284
|
+
"sites"
|
|
12285
|
+
],
|
|
12286
|
+
"summary": "List or invite site members; change a member's role, or remove them",
|
|
12287
|
+
"params": [
|
|
12288
|
+
{
|
|
12289
|
+
"name": "siteId",
|
|
12290
|
+
"in": "path",
|
|
12291
|
+
"required": true,
|
|
12292
|
+
"type": "string",
|
|
12293
|
+
"description": "Site ID"
|
|
12294
|
+
},
|
|
12295
|
+
{
|
|
12296
|
+
"name": "userId",
|
|
12297
|
+
"in": "path",
|
|
12298
|
+
"required": true,
|
|
12299
|
+
"type": "string",
|
|
12300
|
+
"description": "Member's user ID (/{userId} routes)"
|
|
12301
|
+
},
|
|
12302
|
+
{
|
|
12303
|
+
"name": "member",
|
|
12304
|
+
"in": "body",
|
|
12305
|
+
"required": false,
|
|
12306
|
+
"type": "object",
|
|
12307
|
+
"description": "POST: {email, role}; PATCH/PUT: {role}"
|
|
12308
|
+
}
|
|
12309
|
+
],
|
|
12310
|
+
"bodyDescribed": false,
|
|
12311
|
+
"bodyRef": null,
|
|
12312
|
+
"credential": "siteScoped"
|
|
12313
|
+
},
|
|
12314
|
+
{
|
|
12315
|
+
"id": "delete:/api/sites/{siteId}/members/{userId}",
|
|
12316
|
+
"method": "DELETE",
|
|
12317
|
+
"path": "/api/sites/{siteId}/members/{userId}",
|
|
12318
|
+
"tags": [
|
|
12319
|
+
"sites"
|
|
12320
|
+
],
|
|
12321
|
+
"summary": "List or invite site members; change a member's role, or remove them",
|
|
12322
|
+
"params": [
|
|
12323
|
+
{
|
|
12324
|
+
"name": "siteId",
|
|
12325
|
+
"in": "path",
|
|
12326
|
+
"required": true,
|
|
12327
|
+
"type": "string",
|
|
12328
|
+
"description": "Site ID"
|
|
12329
|
+
},
|
|
12330
|
+
{
|
|
12331
|
+
"name": "userId",
|
|
12332
|
+
"in": "path",
|
|
12333
|
+
"required": true,
|
|
12334
|
+
"type": "string",
|
|
12335
|
+
"description": "Member's user ID (/{userId} routes)"
|
|
12336
|
+
},
|
|
12337
|
+
{
|
|
12338
|
+
"name": "member",
|
|
12339
|
+
"in": "body",
|
|
12340
|
+
"required": false,
|
|
12341
|
+
"type": "object",
|
|
12342
|
+
"description": "POST: {email, role}; PATCH/PUT: {role}"
|
|
12343
|
+
}
|
|
12344
|
+
],
|
|
12345
|
+
"bodyDescribed": false,
|
|
12346
|
+
"bodyRef": null,
|
|
12347
|
+
"credential": "siteScoped"
|
|
12348
|
+
},
|
|
12055
12349
|
{
|
|
12056
12350
|
"id": "get:/api/sites/{siteId}/orders",
|
|
12057
12351
|
"method": "GET",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const SHAPE_SOURCE = {
|
|
2
|
-
"writeOperations":
|
|
3
|
-
"shaped":
|
|
4
|
-
"fromHandlers":
|
|
2
|
+
"writeOperations": 389,
|
|
3
|
+
"shaped": 309,
|
|
4
|
+
"fromHandlers": 299,
|
|
5
5
|
"fromSwaggerOnly": 10,
|
|
6
6
|
"withReadOnly": 30,
|
|
7
7
|
"structsRead": 3194
|
|
@@ -5869,30 +5869,22 @@ export const REQUEST_SHAPES = {
|
|
|
5869
5869
|
"post:/api/sites/{siteId}/payment-transactions/{transactionId}/refund": {
|
|
5870
5870
|
"fields": [
|
|
5871
5871
|
{
|
|
5872
|
-
"name": "
|
|
5873
|
-
"type": "string"
|
|
5874
|
-
},
|
|
5875
|
-
{
|
|
5876
|
-
"name": "provider",
|
|
5872
|
+
"name": "note",
|
|
5877
5873
|
"type": "string"
|
|
5878
5874
|
}
|
|
5879
5875
|
],
|
|
5880
5876
|
"source": "go",
|
|
5881
|
-
"goType": "
|
|
5877
|
+
"goType": "refundRequest"
|
|
5882
5878
|
},
|
|
5883
5879
|
"post:/api/sites/{siteId}/payment-transactions/{transactionId}/refund-via-gateway": {
|
|
5884
5880
|
"fields": [
|
|
5885
5881
|
{
|
|
5886
|
-
"name": "
|
|
5887
|
-
"type": "string"
|
|
5888
|
-
},
|
|
5889
|
-
{
|
|
5890
|
-
"name": "provider",
|
|
5882
|
+
"name": "note",
|
|
5891
5883
|
"type": "string"
|
|
5892
5884
|
}
|
|
5893
5885
|
],
|
|
5894
5886
|
"source": "go",
|
|
5895
|
-
"goType": "
|
|
5887
|
+
"goType": "refundRequest"
|
|
5896
5888
|
},
|
|
5897
5889
|
"post:/api/sites/{id}/plan-checkout": {
|
|
5898
5890
|
"fields": [
|
|
@@ -10381,6 +10373,98 @@ export const REQUEST_SHAPES = {
|
|
|
10381
10373
|
"source": "go",
|
|
10382
10374
|
"goType": "(inline)"
|
|
10383
10375
|
},
|
|
10376
|
+
"patch:/api/sites/{siteId}": {
|
|
10377
|
+
"fields": [
|
|
10378
|
+
{
|
|
10379
|
+
"name": "name",
|
|
10380
|
+
"type": "string"
|
|
10381
|
+
},
|
|
10382
|
+
{
|
|
10383
|
+
"name": "slug",
|
|
10384
|
+
"type": "string"
|
|
10385
|
+
},
|
|
10386
|
+
{
|
|
10387
|
+
"name": "subdomainRedirect",
|
|
10388
|
+
"type": "boolean | null",
|
|
10389
|
+
"note": "Pointer, not bool: `false` is a MEANINGFUL value here (\"serve the built-in address directly\"), and a plain bool cannot be told apart from \"field omitted\" — a rename would silently switch redirecting off on every site."
|
|
10390
|
+
},
|
|
10391
|
+
{
|
|
10392
|
+
"name": "subdomainRedirectCode",
|
|
10393
|
+
"type": "number",
|
|
10394
|
+
"note": "301 or 302; 0/absent keeps the site's current choice."
|
|
10395
|
+
},
|
|
10396
|
+
{
|
|
10397
|
+
"name": "maintenanceMode",
|
|
10398
|
+
"type": "boolean | null",
|
|
10399
|
+
"note": "Takes the whole storefront offline."
|
|
10400
|
+
}
|
|
10401
|
+
],
|
|
10402
|
+
"source": "go",
|
|
10403
|
+
"goType": "(inline)"
|
|
10404
|
+
},
|
|
10405
|
+
"put:/api/sites/{siteId}": {
|
|
10406
|
+
"fields": [
|
|
10407
|
+
{
|
|
10408
|
+
"name": "name",
|
|
10409
|
+
"type": "string"
|
|
10410
|
+
},
|
|
10411
|
+
{
|
|
10412
|
+
"name": "slug",
|
|
10413
|
+
"type": "string"
|
|
10414
|
+
},
|
|
10415
|
+
{
|
|
10416
|
+
"name": "subdomainRedirect",
|
|
10417
|
+
"type": "boolean | null",
|
|
10418
|
+
"note": "Pointer, not bool: `false` is a MEANINGFUL value here (\"serve the built-in address directly\"), and a plain bool cannot be told apart from \"field omitted\" — a rename would silently switch redirecting off on every site."
|
|
10419
|
+
},
|
|
10420
|
+
{
|
|
10421
|
+
"name": "subdomainRedirectCode",
|
|
10422
|
+
"type": "number",
|
|
10423
|
+
"note": "301 or 302; 0/absent keeps the site's current choice."
|
|
10424
|
+
},
|
|
10425
|
+
{
|
|
10426
|
+
"name": "maintenanceMode",
|
|
10427
|
+
"type": "boolean | null",
|
|
10428
|
+
"note": "Takes the whole storefront offline."
|
|
10429
|
+
}
|
|
10430
|
+
],
|
|
10431
|
+
"source": "go",
|
|
10432
|
+
"goType": "(inline)"
|
|
10433
|
+
},
|
|
10434
|
+
"post:/api/sites/{siteId}/members": {
|
|
10435
|
+
"fields": [
|
|
10436
|
+
{
|
|
10437
|
+
"name": "email",
|
|
10438
|
+
"type": "string"
|
|
10439
|
+
},
|
|
10440
|
+
{
|
|
10441
|
+
"name": "role",
|
|
10442
|
+
"type": "site.Role"
|
|
10443
|
+
}
|
|
10444
|
+
],
|
|
10445
|
+
"source": "go",
|
|
10446
|
+
"goType": "(inline)"
|
|
10447
|
+
},
|
|
10448
|
+
"patch:/api/sites/{siteId}/members/{userId}": {
|
|
10449
|
+
"fields": [
|
|
10450
|
+
{
|
|
10451
|
+
"name": "role",
|
|
10452
|
+
"type": "site.Role"
|
|
10453
|
+
}
|
|
10454
|
+
],
|
|
10455
|
+
"source": "go",
|
|
10456
|
+
"goType": "(inline)"
|
|
10457
|
+
},
|
|
10458
|
+
"put:/api/sites/{siteId}/members/{userId}": {
|
|
10459
|
+
"fields": [
|
|
10460
|
+
{
|
|
10461
|
+
"name": "role",
|
|
10462
|
+
"type": "site.Role"
|
|
10463
|
+
}
|
|
10464
|
+
],
|
|
10465
|
+
"source": "go",
|
|
10466
|
+
"goType": "(inline)"
|
|
10467
|
+
},
|
|
10384
10468
|
"post:/api/sites/{siteId}/roles": {
|
|
10385
10469
|
"fields": [
|
|
10386
10470
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// GENERATED by scripts/gen-catalog.ts — do not edit by hand.
|
|
2
2
|
// The platform commit every other generated file here describes.
|
|
3
3
|
export const PLATFORM_SOURCE = {
|
|
4
|
-
"commit": "
|
|
5
|
-
"committedAt": "2026-10-
|
|
4
|
+
"commit": "8384af1dd6df12bb80a75a8b53e88c377d3f722c",
|
|
5
|
+
"committedAt": "2026-10-12T01:13:06+07:00",
|
|
6
6
|
"dirty": false
|
|
7
7
|
};
|
package/dist/core/patch.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The document patch primitive, mirrored from the editor's live-edit channel
|
|
3
|
+
* (editor/src/history/patchRecorder.ts + features/liveedit/ops.ts).
|
|
4
|
+
*
|
|
5
|
+
* The shape has to match exactly: these patches go on the wire to peers running
|
|
6
|
+
* the editor's own code, and a shape it does not recognise is dropped silently.
|
|
7
|
+
*/
|
|
8
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
1
9
|
/** Segments that must never appear in a path we apply or send. */
|
|
2
10
|
const FORBIDDEN = new Set(['__proto__', 'constructor', 'prototype']);
|
|
3
11
|
/**
|
|
@@ -149,7 +157,10 @@ export function applyPatches(state, patches) {
|
|
|
149
157
|
export function documentPatches(before, after) {
|
|
150
158
|
const out = [];
|
|
151
159
|
for (const [id, node] of Object.entries(after.nodes)) {
|
|
152
|
-
|
|
160
|
+
// Not JSON.stringify: a document read back from the platform (Postgres
|
|
161
|
+
// jsonb) orders keys differently from one built here, so every node
|
|
162
|
+
// "differed" — undoing one sb_move rewrote all five nodes of a page, live.
|
|
163
|
+
if (!isDeepStrictEqual(before.nodes[id], node))
|
|
153
164
|
out.push({ op: 'set', path: ['nodes', id], value: node });
|
|
154
165
|
}
|
|
155
166
|
for (const id of Object.keys(before.nodes)) {
|
|
@@ -103,7 +103,8 @@ export const FIX = {
|
|
|
103
103
|
'renderer resolves a page id), { "id": "<popup id>" } for popup.',
|
|
104
104
|
no_data_context: 'Give it a record: put it inside a list-dataset / dataset-block, pin one with sb_bind ' +
|
|
105
105
|
'(target id), or move it to a page whose type supplies "<key>" (product, category, course, ' +
|
|
106
|
-
'post, blog). Off those it renders its
|
|
106
|
+
'post, blog). A cartLine field goes inside a cart-order instead. Off those it renders its ' +
|
|
107
|
+
'placeholder for ever.',
|
|
107
108
|
unread_value: 'Write a value the renderer reads: sb_traits_for "<type>" lists them; sb_set id "<id>" ' +
|
|
108
109
|
'with one, at the same breakpoint it is stored at.',
|
|
109
110
|
unknown_icon: 'Write a real icon name: sb_set id "<id>", namespace <ns>, keys { "<key>": "<name>" } — the ' +
|
|
@@ -263,11 +263,12 @@ export function readinessGaps(input) {
|
|
|
263
263
|
problem: 'This storefront is switched OFF right now and has no published page of the "maintain" ' +
|
|
264
264
|
'type, so every address is answering a bare 503 sentence — no header, no logo, no word ' +
|
|
265
265
|
'about when it is back, and nothing that looks like this shop.',
|
|
266
|
-
fix: draft
|
|
266
|
+
fix: (draft
|
|
267
267
|
? 'Publish the maintenance page that already exists.'
|
|
268
268
|
: 'Create a page of type "maintain" and publish it. It has no address of its own — the ' +
|
|
269
269
|
'storefront serves it as the BODY of the 503 — so give it the shop\'s name and a line ' +
|
|
270
|
-
'saying when you are back.'
|
|
270
|
+
'saying when you are back.') +
|
|
271
|
+
' Or switch the shop back on: sb_api_call id "patch:/api/sites/{siteId}" body { "maintenanceMode": false }.',
|
|
271
272
|
});
|
|
272
273
|
}
|
|
273
274
|
// A MISTYPED URL ANSWERS IN GO, NOT IN THE SHOP'S VOICE.
|
|
@@ -131,7 +131,20 @@ function drawsItsOwnContent(type) {
|
|
|
131
131
|
return (BOUND_SPECIALS[type] ?? []).some((k) => !LINK_SPECIALS.has(k));
|
|
132
132
|
}
|
|
133
133
|
/** Binding-source prefixes that name a RECORD (not `site.*`, which every page has). */
|
|
134
|
-
|
|
134
|
+
// cartLine: only a cart-order's line row supplies it (render/nodes/cart-order,
|
|
135
|
+
// scope.PlaceholderCartLine) — see ROW_SUPPLIER. searchRow is left out: no
|
|
136
|
+
// element supplies it yet, so there is no right place to point at.
|
|
137
|
+
const RECORD_ENTITIES = new Set(['product', 'category', 'course', 'article', 'blogCategory', 'brand', 'cartLine']);
|
|
138
|
+
/** A record only one element type's rows supply, which is not a dataset repeater. */
|
|
139
|
+
const ROW_SUPPLIER = { cartLine: 'cart-order' };
|
|
140
|
+
/** Does this node sit inside an element of `type`? */
|
|
141
|
+
function insideType(d, id, type) {
|
|
142
|
+
for (let x = d.nodes[id]?.data.parent; x && d.nodes[x]; x = d.nodes[x].data.parent) {
|
|
143
|
+
if (d.nodes[x].data.type === type)
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
135
148
|
/**
|
|
136
149
|
* Which records a page TYPE supplies outside any repeater — read off the
|
|
137
150
|
* platform's own seeds (STORE_PAGE_SEEDS bind exactly these at top level).
|
|
@@ -443,13 +456,16 @@ opts = {}) {
|
|
|
443
456
|
const missing = (n.bindings ?? [])
|
|
444
457
|
.map((b) => String(b?.source ?? '').split('.')[0])
|
|
445
458
|
.find((e) => RECORD_ENTITIES.has(e) && !entities.includes(e));
|
|
446
|
-
|
|
459
|
+
const supplied = missing && ROW_SUPPLIER[missing] ? insideType(d, id, ROW_SUPPLIER[missing]) : false;
|
|
460
|
+
if (missing && !supplied && !pinnedAbove(d, id)) {
|
|
447
461
|
out.push({
|
|
448
462
|
code: 'no_data_context',
|
|
449
463
|
nodeId: id,
|
|
450
464
|
type,
|
|
451
|
-
problem:
|
|
452
|
-
|
|
465
|
+
problem: (ROW_SUPPLIER[missing]
|
|
466
|
+
? `Bound to a ${missing} field, but it sits outside any ${ROW_SUPPLIER[missing]} — only its line rows supply one, so it renders its placeholder on the published page.`
|
|
467
|
+
: `Bound to a ${missing} record, but it sits in no repeater and a "${opts.pageType}" ` +
|
|
468
|
+
'page supplies none — it renders its placeholder on the published page.'),
|
|
453
469
|
key: missing,
|
|
454
470
|
fix: fill('no_data_context', { id, key: missing }),
|
|
455
471
|
});
|
package/dist/tools/api.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
import { API_OPERATIONS, SWAGGER_SOURCE } from '../catalog/api.generated.js';
|
|
3
4
|
import { REQUEST_SHAPES } from '../catalog/shapes.generated.js';
|
|
@@ -54,12 +55,33 @@ export function deepMerge(base, over) {
|
|
|
54
55
|
out[k] = deepMerge(base[k], v);
|
|
55
56
|
return out;
|
|
56
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Top-level body keys the handler's Go struct has no field for. encoding/json
|
|
60
|
+
* DROPS them without a word, so a misspelt field is a write that "worked":
|
|
61
|
+
* measured, `price: 30000` on a shipping method stored `feeCents: 0`. Matched
|
|
62
|
+
* case-insensitively, as Go's decoder does. Silent when the shape is unknown or
|
|
63
|
+
* partial — a warning there would be a guess.
|
|
64
|
+
*/
|
|
65
|
+
export function unknownBodyKeys(opId, body) {
|
|
66
|
+
const shape = REQUEST_SHAPES[opId];
|
|
67
|
+
if (!shape || shape.partial || !isPlain(body))
|
|
68
|
+
return undefined;
|
|
69
|
+
const known = new Set(shape.fields.map((f) => f.name.toLowerCase()));
|
|
70
|
+
const keys = Object.keys(body).filter((k) => !known.has(k.toLowerCase()));
|
|
71
|
+
if (!keys.length)
|
|
72
|
+
return undefined;
|
|
73
|
+
return {
|
|
74
|
+
keys,
|
|
75
|
+
note: `Not fields of ${shape.goType === '(inline)' ? "the handler's body" : shape.goType}: the platform ignores them silently. ` +
|
|
76
|
+
`Its fields: ${shape.fields.map((f) => f.name).join(', ')}.`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
57
79
|
/** Leaf paths where `b` differs from `a` (arrays compared whole). */
|
|
58
80
|
function changedPaths(a, b, at = '') {
|
|
59
81
|
if (isPlain(a) && isPlain(b)) {
|
|
60
82
|
return [...new Set([...Object.keys(a), ...Object.keys(b)])].flatMap((k) => changedPaths(a[k], b[k], at ? `${at}.${k}` : k));
|
|
61
83
|
}
|
|
62
|
-
return
|
|
84
|
+
return isDeepStrictEqual(a, b) ? [] : [at];
|
|
63
85
|
}
|
|
64
86
|
const isEmpty = (v) => v === null || v === undefined || v === '' || v === false || v === 0 ||
|
|
65
87
|
(Array.isArray(v) && v.length === 0) || (isPlain(v) && Object.keys(v).length === 0);
|
|
@@ -604,6 +626,7 @@ export async function callOperation(ctx, args) {
|
|
|
604
626
|
`${dryRun ? 'would be' : 'were'} erased: ${listPaths(erased)}. ` +
|
|
605
627
|
(dryRun ? 'Pass merge:true to keep them.' : 'sb_undo puts them back.')
|
|
606
628
|
: undefined;
|
|
629
|
+
const unknownKeys = unknownBodyKeys(op.id, args.file ? undefined : args.body);
|
|
607
630
|
// If-Match: given, or — on a route that refuses a write without one — the
|
|
608
631
|
// version the GET answers right now.
|
|
609
632
|
const headers = {};
|
|
@@ -672,6 +695,7 @@ export async function callOperation(ctx, args) {
|
|
|
672
695
|
...(download ? { would_save: args.file.path } : {}),
|
|
673
696
|
...(merged ? { merge: merged } : {}),
|
|
674
697
|
...(replaceWarning ? { replace_warning: replaceWarning } : {}),
|
|
698
|
+
...(unknownKeys ? { unknown_keys: unknownKeys } : {}),
|
|
675
699
|
// Shaping is part of what the call WOULD do, so the preview says it;
|
|
676
700
|
// otherwise a dry run reads identically whether or not it was asked for.
|
|
677
701
|
...(args.pick !== undefined || args.max_items !== undefined || args.item_offset !== undefined
|
|
@@ -745,6 +769,7 @@ export async function callOperation(ctx, args) {
|
|
|
745
769
|
...(ifMatchRead ? { if_match: { sent: headers['If-Match'], read_from: `GET ${ifMatchRead}` } } : {}),
|
|
746
770
|
...(merged ? { merge: merged } : {}),
|
|
747
771
|
...(replaceWarning ? { replace_warning: replaceWarning } : {}),
|
|
772
|
+
...(unknownKeys ? { unknown_keys: unknownKeys } : {}),
|
|
748
773
|
};
|
|
749
774
|
const told = Object.keys(extra).length && isPlain(answer) ? { ...answer, ...extra } : answer;
|
|
750
775
|
if (!op.raw)
|
package/dist/tools/page.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
import { composeWarnings } from '../domains/site/findings.js';
|
|
3
4
|
import { text } from '../mcp/response.js';
|
|
@@ -681,7 +682,7 @@ export class PageSession {
|
|
|
681
682
|
const d = this.current();
|
|
682
683
|
// Rev when both sides have one; an older platform reports none, and the
|
|
683
684
|
// document is then compared whole — a false refusal is the safe failure.
|
|
684
|
-
const moved = fence.rev > 0 && this.sourceRev > 0 ? this.sourceRev !== fence.rev :
|
|
685
|
+
const moved = fence.rev > 0 && this.sourceRev > 0 ? this.sourceRev !== fence.rev : !isDeepStrictEqual(d.doc, JSON.parse(fence.json));
|
|
685
686
|
if (moved) {
|
|
686
687
|
throw new Error(`sbuilder: refusing to undo — page ${pageId}'s draft moved since this process last saved it ` +
|
|
687
688
|
`(rev ${this.sourceRev}, last stored here ${fence.rev}): someone else saved it, and restoring ` +
|
package/dist/tools/pageupdate.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
import { text } from '../mcp/response.js';
|
|
3
4
|
import { request, redact } from '../transport/http.js';
|
|
@@ -87,7 +88,7 @@ export function registerPageUpdateTools(server, ctx) {
|
|
|
87
88
|
// courseGate, courseLesson and keys unknown here — so the write is always the
|
|
88
89
|
// whole old blob with only the named keys moved, as the editor's PageSeoDialog does.
|
|
89
90
|
const changes = {};
|
|
90
|
-
const differs = (a, b) =>
|
|
91
|
+
const differs = (a, b) => !isDeepStrictEqual(a, b);
|
|
91
92
|
const settings = { ...old };
|
|
92
93
|
for (const [k, v] of Object.entries(seo ?? {})) {
|
|
93
94
|
if (differs(old[k], v))
|
package/dist/tools/store.js
CHANGED
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
* `app.ts`) and are dispatched from here; the checkout and `form` are written
|
|
35
35
|
* out below, because they came first.
|
|
36
36
|
*/
|
|
37
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
37
38
|
import { z } from 'zod';
|
|
38
39
|
import { text } from '../mcp/response.js';
|
|
39
40
|
import { request, redact, ApiError } from '../transport/http.js';
|
|
@@ -121,7 +122,7 @@ export function syncOrderDocument(doc, fresh, shipping) {
|
|
|
121
122
|
if (sp?.name === 'payment_method') {
|
|
122
123
|
const had = new Map((sp.methods ?? []).map((m) => [m.value, m]));
|
|
123
124
|
const next = fresh.map((m) => had.get(m.value) ?? m);
|
|
124
|
-
if (
|
|
125
|
+
if (!isDeepStrictEqual(next, sp.methods ?? [])) {
|
|
125
126
|
sp.methods = next;
|
|
126
127
|
if (!next.some((m) => m.value === sp.defaultValue))
|
|
127
128
|
sp.defaultValue = next[0]?.value ?? '';
|
|
@@ -130,7 +131,7 @@ export function syncOrderDocument(doc, fresh, shipping) {
|
|
|
130
131
|
out.payment_methods = next.map((m) => m.value);
|
|
131
132
|
}
|
|
132
133
|
if (sp?.name === 'shipping_method') {
|
|
133
|
-
if (
|
|
134
|
+
if (!isDeepStrictEqual(sp.options ?? [], shipping)) {
|
|
134
135
|
sp.options = [...shipping];
|
|
135
136
|
out.changed = true;
|
|
136
137
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbuilder-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.85.0",
|
|
4
4
|
"description": "MCP server that designs and operates a Store Builder site — pages, data, theme and publish — through the platform's own API and live-edit protocol.",
|
|
5
5
|
"mcpName": "io.github.vuluu2k/sbuilder-mcp",
|
|
6
6
|
"type": "module",
|