sbuilder-mcp 0.36.0 → 0.36.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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,10 @@ 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.36.1] - 2026-09-11
10
+
11
+ - fix(import,api): a capture waited on iframes, and a recovery list was unreadable
12
+
9
13
  ## [0.36.0] - 2026-09-11
10
14
 
11
15
  ### Added
package/dist/tools/api.js CHANGED
@@ -45,6 +45,33 @@ function pickFields(item, fields) {
45
45
  * it would have been and how to narrow the call. A non-list answer is never
46
46
  * cut: there is no honest place to stop inside one object.
47
47
  */
48
+ /**
49
+ * LIST OPERATIONS WHOSE EVERY ROW CARRIES A WHOLE PAGE DOCUMENT.
50
+ *
51
+ * The page recovery surface answers with the documents themselves — which is
52
+ * right, since a restore has to have something to restore from — and useless to
53
+ * read. MEASURED against a live server: one version of a TWO-NODE page is 1,690
54
+ * bytes, so a realistic 120-node page runs about 70 KB per version and a listing
55
+ * of twenty is **1.4 MB in one answer**. An agent choosing which version to
56
+ * restore would be handed a truncated blob and no reliable way to pick.
57
+ *
58
+ * `sb_publish` already had this exact problem and the same answer: a published
59
+ * row carries `document`, `html` and `css` for every page the cascade touched,
60
+ * so it PROJECTS the rows. This is that, applied where the caller cannot know to
61
+ * ask — and it is a DEFAULT rather than a rule: an explicit `pick` still wins,
62
+ * so the document is one argument away for a caller that wants to read one.
63
+ */
64
+ const LIST_PROJECTIONS = {
65
+ 'get:/api/sites/{siteId}/pages/{pageId}/versions': [
66
+ 'id',
67
+ 'versionNo',
68
+ 'label',
69
+ 'createdBy',
70
+ 'createdAt',
71
+ 'isLive',
72
+ ],
73
+ 'get:/api/sites/{siteId}/pages/{pageId}/history': ['id', 'createdBy', 'createdAt'],
74
+ };
48
75
  export function shapeResponse(raw, opts) {
49
76
  const asked = opts.pick !== undefined || opts.max_items !== undefined;
50
77
  const isObj = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
@@ -271,7 +298,10 @@ export async function callOperation(ctx, args) {
271
298
  if (raw === null || raw === undefined) {
272
299
  return { ok: true, method: op.method, path, note: 'The platform answered with no content.' };
273
300
  }
274
- return shapeResponse(raw, { pick: args.pick, max_items: args.max_items });
301
+ // The caller's own `pick` outranks the default: asking for `document` is how
302
+ // you read a version rather than merely choose one.
303
+ const projection = args.pick ?? LIST_PROJECTIONS[op.id];
304
+ return shapeResponse(raw, { pick: projection, max_items: args.max_items });
275
305
  }
276
306
  export function registerApiTools(server, ctx) {
277
307
  server.registerTool('sb_api_find', {
@@ -1127,7 +1127,22 @@ async function readPage(browser, url, width, work) {
1127
1127
  let page;
1128
1128
  try {
1129
1129
  page = await browser.newPage({ viewport: { width, height: 900 } });
1130
- await page.goto(url, { waitUntil: 'load', timeout: 30_000 });
1130
+ // `domcontentloaded`, NOT `load`, and `settleDom` does the rest.
1131
+ //
1132
+ // `load` waits for every SUBRESOURCE — including third-party iframes, which
1133
+ // an import has no use for: this walk reads the iframe's `src` ATTRIBUTE and
1134
+ // never needs the frame to render. So a page carrying an ad frame, a chat
1135
+ // widget or a slow video embed stalled the whole capture for up to thirty
1136
+ // seconds and then THREW, losing an import whose DOM had been ready the
1137
+ // entire time. Caught by this repo's own test, whose fixture embeds real
1138
+ // YouTube, Vimeo and Google Maps frames: it started failing at exactly 30s
1139
+ // with nothing about the page having changed.
1140
+ //
1141
+ // The same lesson `sb_look` already paid for with `networkidle`, one wait
1142
+ // earlier: the right question is "has the DOM stopped changing", and
1143
+ // `settleDom` answers it directly and bounded. A page that genuinely needs
1144
+ // its images is the SHOOT path's problem, and that one still waits.
1145
+ await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 });
1131
1146
  // THE SAME SETTLE `sb_look` USES, not a flat sleep. A fixed 600ms is wrong
1132
1147
  // at both ends: example.com is finished long before it, and a page that
1133
1148
  // builds itself with scripts is not finished after it — which is exactly the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sbuilder-mcp",
3
- "version": "0.36.0",
3
+ "version": "0.36.1",
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",