@notis_ai/cli 0.2.19 → 0.2.20

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.
@@ -37,7 +37,7 @@ const CAPTURE = { type: 'png', animations: 'disabled', caret: 'hide', scale: 'cs
37
37
  * it. Expansion already set fixed and sticky elements static, so no element
38
38
  * repeats from one tile to the next.
39
39
  */
40
- async function capturePage(page, sliceHeight, maxBytes) {
40
+ async function capturePage(page, sliceHeight, maxBytes, maxHeight, maxPixels) {
41
41
  // Playwright's own full-page size, so tiles cover exactly what one capture would.
42
42
  const measure = () => page.evaluate(() => {
43
43
  const body = document.body, root = document.documentElement;
@@ -46,6 +46,8 @@ async function capturePage(page, sliceHeight, maxBytes) {
46
46
  viewport: { width: innerWidth, height: innerHeight } };
47
47
  });
48
48
  const size = await measure();
49
+ if (size.height > maxHeight || size.width * size.height > maxPixels)
50
+ throw new ViewRenderError('render_too_large', 'This view exceeds the full-page render limit; nothing was cropped.', 413);
49
51
  const tile = Math.min(sliceHeight, size.viewport.height);
50
52
  // A page within one tile fits the viewport, where Playwright captures it as is.
51
53
  if (size.height <= tile) return page.screenshot({ ...CAPTURE, fullPage: true });
@@ -220,7 +222,11 @@ export async function renderView({ broker, outputs, width, timeoutMs, signal, po
220
222
  }
221
223
  if (!settled) throw new ViewRenderError('render_unstable', 'This view did not settle into a full-page capture; nothing was cropped.', 409);
222
224
  if (blocked.some(value => value.code === 'render_paid_background_denied')) throw new ViewRenderError('render_paid_background_denied', 'Background snapshots cannot run paid reads.', 403);
223
- const dimensions = await page.evaluate(() => ({ width: innerWidth, height: Math.max(document.documentElement.scrollHeight, document.body.scrollHeight) }));
225
+ const dimensions = await page.evaluate(() => {
226
+ const body = document.body, root = document.documentElement;
227
+ return { width: Math.max(body.scrollWidth, root.scrollWidth, body.offsetWidth, root.offsetWidth, body.clientWidth, root.clientWidth),
228
+ height: Math.max(body.scrollHeight, root.scrollHeight, body.offsetHeight, root.offsetHeight, body.clientHeight, root.clientHeight) };
229
+ });
224
230
  if (dimensions.height > maxHeight || dimensions.width * dimensions.height > maxPixels) throw new ViewRenderError('render_too_large', 'This view exceeds the full-page render limit; nothing was cropped.', 413);
225
231
  let markdown, screenshot;
226
232
  if (config.outputs.includes('markdown')) {
@@ -237,9 +243,11 @@ export async function renderView({ broker, outputs, width, timeoutMs, signal, po
237
243
  }
238
244
  if (config.outputs.includes('screenshot')) {
239
245
  stage = 'screenshot';
240
- const png = await capturePage(page, sliceHeight, maxPngBytes);
246
+ const png = await capturePage(page, sliceHeight, maxPngBytes, maxHeight, maxPixels);
247
+ if (png.readUInt32BE(20) > maxHeight || png.readUInt32BE(16) * png.readUInt32BE(20) > maxPixels)
248
+ throw new ViewRenderError('render_too_large', 'This view exceeds the full-page render limit; nothing was cropped.', 413);
241
249
  if (png.length > maxPngBytes) throw new ViewRenderError('render_too_large', 'The PNG exceeds the render transport limit; nothing was cropped.', 413);
242
- screenshot = { mime_type: 'image/png', data_base64: png.toString('base64'), width: png.readUInt32BE(16), height: png.readUInt32BE(20) };
250
+ screenshot = { mime_type: 'image/png', viewport_width: config.width, data_base64: png.toString('base64'), width: png.readUInt32BE(16), height: png.readUInt32BE(20) };
243
251
  }
244
252
  stage = 'read_completion';
245
253
  if (blocked.some(value => value.code === 'render_paid_background_denied')) throw new ViewRenderError('render_paid_background_denied', 'Background snapshots cannot run paid reads.', 403);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notis_ai/cli",
3
- "version": "0.2.19",
3
+ "version": "0.2.20",
4
4
  "description": "Agent-first Notis CLI for Spaces, Skills and generic tool execution",
5
5
  "type": "module",
6
6
  "bin": {
@@ -52,8 +52,8 @@
52
52
  "react-dom": "19.3.0"
53
53
  },
54
54
  "notisRelease": {
55
- "sourceSha": "974cae565cf52fde4d5a93fb0502c52b83e2abf9",
55
+ "sourceSha": "2f38334adf5ebfc2aecbc3c7ae6f5ad7a14c6a0b",
56
56
  "channel": "stable"
57
57
  },
58
- "gitHead": "974cae565cf52fde4d5a93fb0502c52b83e2abf9"
58
+ "gitHead": "2f38334adf5ebfc2aecbc3c7ae6f5ad7a14c6a0b"
59
59
  }