@pinepaper.studio/mcp-server 1.6.16 → 1.6.18

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 (90) hide show
  1. package/README.de.md +1 -1
  2. package/README.es.md +1 -1
  3. package/README.fr.md +1 -1
  4. package/README.hi.md +1 -1
  5. package/README.ja.md +1 -1
  6. package/README.ko.md +1 -1
  7. package/README.md +142 -4
  8. package/README.pt-BR.md +1 -1
  9. package/README.zh-CN.md +1 -1
  10. package/dist/browser/puppeteer-controller.d.ts +61 -1
  11. package/dist/browser/puppeteer-controller.d.ts.map +1 -1
  12. package/dist/browser/puppeteer-controller.js +168 -6
  13. package/dist/browser/puppeteer-controller.js.map +1 -1
  14. package/dist/build-info.json +2 -2
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +75 -10
  18. package/dist/index.js.map +1 -1
  19. package/dist/ontology/ontology-validator.d.ts.map +1 -1
  20. package/dist/ontology/ontology-validator.js +9 -5
  21. package/dist/ontology/ontology-validator.js.map +1 -1
  22. package/dist/ontology/upstream-vocabulary.d.ts +2 -2
  23. package/dist/ontology/upstream-vocabulary.js +2 -2
  24. package/dist/tools/definitions.d.ts.map +1 -1
  25. package/dist/tools/definitions.js +198 -47
  26. package/dist/tools/definitions.js.map +1 -1
  27. package/dist/tools/engine-lists.d.ts +20 -0
  28. package/dist/tools/engine-lists.d.ts.map +1 -0
  29. package/dist/tools/engine-lists.js +86 -0
  30. package/dist/tools/engine-lists.js.map +1 -0
  31. package/dist/tools/engine-methods.d.ts.map +1 -1
  32. package/dist/tools/engine-methods.js +8 -0
  33. package/dist/tools/engine-methods.js.map +1 -1
  34. package/dist/tools/engine-surface.d.ts +1 -1
  35. package/dist/tools/engine-surface.d.ts.map +1 -1
  36. package/dist/tools/engine-surface.js +20 -4
  37. package/dist/tools/engine-surface.js.map +1 -1
  38. package/dist/tools/handlers/beats.d.ts +64 -0
  39. package/dist/tools/handlers/beats.d.ts.map +1 -0
  40. package/dist/tools/handlers/beats.js +83 -0
  41. package/dist/tools/handlers/beats.js.map +1 -0
  42. package/dist/tools/handlers/brand-intake.d.ts +95 -0
  43. package/dist/tools/handlers/brand-intake.d.ts.map +1 -0
  44. package/dist/tools/handlers/brand-intake.js +242 -0
  45. package/dist/tools/handlers/brand-intake.js.map +1 -0
  46. package/dist/tools/handlers.d.ts +23 -11
  47. package/dist/tools/handlers.d.ts.map +1 -1
  48. package/dist/tools/handlers.js +265 -14
  49. package/dist/tools/handlers.js.map +1 -1
  50. package/dist/tools/minimal-descriptions.d.ts.map +1 -1
  51. package/dist/tools/minimal-descriptions.js +5 -3
  52. package/dist/tools/minimal-descriptions.js.map +1 -1
  53. package/dist/tools/shape-params.d.ts +2 -2
  54. package/dist/tools/shape-params.js +2 -2
  55. package/dist/tools/stick-vocabulary.d.ts +1 -1
  56. package/dist/tools/stick-vocabulary.js +1 -1
  57. package/dist/tools/toolkits.d.ts.map +1 -1
  58. package/dist/tools/toolkits.js +2 -0
  59. package/dist/tools/toolkits.js.map +1 -1
  60. package/dist/types/code-generator.d.ts +63 -2
  61. package/dist/types/code-generator.d.ts.map +1 -1
  62. package/dist/types/code-generator.js +533 -49
  63. package/dist/types/code-generator.js.map +1 -1
  64. package/dist/types/schemas.d.ts +2199 -640
  65. package/dist/types/schemas.d.ts.map +1 -1
  66. package/dist/types/schemas.js +186 -50
  67. package/dist/types/schemas.js.map +1 -1
  68. package/dist/utils/a11y.d.ts +2 -0
  69. package/dist/utils/a11y.d.ts.map +1 -1
  70. package/dist/utils/a11y.js +1 -1
  71. package/dist/utils/a11y.js.map +1 -1
  72. package/dist/utils/beats.d.ts +58 -0
  73. package/dist/utils/beats.d.ts.map +1 -0
  74. package/dist/utils/beats.js +110 -0
  75. package/dist/utils/beats.js.map +1 -0
  76. package/dist/utils/mcp-log.d.ts +16 -0
  77. package/dist/utils/mcp-log.d.ts.map +1 -0
  78. package/dist/utils/mcp-log.js +10 -0
  79. package/dist/utils/mcp-log.js.map +1 -0
  80. package/dist/utils/progress.d.ts +28 -0
  81. package/dist/utils/progress.d.ts.map +1 -0
  82. package/dist/utils/progress.js +24 -0
  83. package/dist/utils/progress.js.map +1 -0
  84. package/dist/utils/svg-size.d.ts +22 -0
  85. package/dist/utils/svg-size.d.ts.map +1 -0
  86. package/dist/utils/svg-size.js +43 -0
  87. package/dist/utils/svg-size.js.map +1 -0
  88. package/dist/vendor/design/design-style.js +1 -1
  89. package/manifest.json +9 -1
  90. package/package.json +7 -4
package/README.de.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  Der PinePaper MCP-Server lässt KI-Assistenten über das Model Context Protocol Grafiken in [PinePaper Studio](https://pinepaper.studio) erstellen und animieren. Funktioniert mit jeder KI, die MCP-Tool-Aufrufe unterstützt (Claude, GPT, Gemini, lokale Modelle usw.).
21
21
 
22
- Der Server bietet **157 Tools**: Zeichnen, Animation, Diagramme, Karten, Typografie, Physik, Bildbearbeitung, Datenvisualisierung und Export:
22
+ Der Server bietet **159 Tools**: Zeichnen, Animation, Diagramme, Karten, Typografie, Physik, Bildbearbeitung, Datenvisualisierung und Export:
23
23
 
24
24
  - Text, Formen, Geometrie und komplexe Grafiken erstellen
25
25
  - Mit deklarativen **Relationen** animieren statt mit Keyframes
package/README.es.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  El servidor MCP de PinePaper permite a los asistentes de IA crear y animar gráficos en [PinePaper Studio](https://pinepaper.studio) a través del Model Context Protocol. Funciona con cualquier IA que soporte llamadas a herramientas MCP (Claude, GPT, Gemini, modelos locales, etc.).
21
21
 
22
- Expone **157 herramientas**: dibujo, animación, diagramas, mapas, tipografía, física, edición de imágenes, visualización de datos y exportación:
22
+ Expone **159 herramientas**: dibujo, animación, diagramas, mapas, tipografía, física, edición de imágenes, visualización de datos y exportación:
23
23
 
24
24
  - Crear texto, formas, geometría y gráficos complejos
25
25
  - Animar con **relaciones** declarativas en lugar de fotogramas clave
package/README.fr.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  Le serveur MCP PinePaper permet aux assistants IA de créer et d'animer des graphiques dans [PinePaper Studio](https://pinepaper.studio) via le Model Context Protocol. Compatible avec toute IA gérant les appels d'outils MCP (Claude, GPT, Gemini, modèles locaux, etc.).
21
21
 
22
- Il expose **157 outils** : dessin, animation, diagrammes, cartes, typographie, physique, retouche d'image, visualisation de données et export :
22
+ Il expose **159 outils** : dessin, animation, diagrammes, cartes, typographie, physique, retouche d'image, visualisation de données et export :
23
23
 
24
24
  - Créer du texte, des formes, de la géométrie et des graphiques complexes
25
25
  - Animer avec des **relations** déclaratives plutôt que des images clés
package/README.hi.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  PinePaper MCP सर्वर AI असिस्टेंट्स को Model Context Protocol के ज़रिए [PinePaper Studio](https://pinepaper.studio) में ग्राफ़िक्स बनाने और एनिमेट करने देता है। MCP टूल कॉलिंग वाले किसी भी AI (Claude, GPT, Gemini, लोकल मॉडल आदि) के साथ काम करता है।
21
21
 
22
- सर्वर **157 टूल** देता है — ड्रॉइंग, एनिमेशन, डायग्राम, नक्शे, टाइपोग्राफ़ी, फिज़िक्स, इमेज एडिटिंग, डेटा विज़ुअलाइज़ेशन और एक्सपोर्ट:
22
+ सर्वर **159 टूल** देता है — ड्रॉइंग, एनिमेशन, डायग्राम, नक्शे, टाइपोग्राफ़ी, फिज़िक्स, इमेज एडिटिंग, डेटा विज़ुअलाइज़ेशन और एक्सपोर्ट:
23
23
 
24
24
  - टेक्स्ट, आकृतियाँ, ज्यामिति और जटिल ग्राफ़िक्स बनाना
25
25
  - कीफ़्रेम की जगह व्यवहार बताने वाले **रिलेशन** से एनिमेशन
package/README.ja.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  PinePaper MCP サーバーは、MCP 経由で AI アシスタントが [PinePaper Studio](https://pinepaper.studio) 上にグラフィックを作成・アニメーションさせるためのサーバーです。MCP ツールコールに対応した AI(Claude、GPT、Gemini、ローカルモデルなど)で動作します。
21
21
 
22
- **157 個のツール**を提供 — 描画・アニメーション・図・地図・タイポグラフィ・物理・画像編集・データ可視化・エクスポート:
22
+ **159 個のツール**を提供 — 描画・アニメーション・図・地図・タイポグラフィ・物理・画像編集・データ可視化・エクスポート:
23
23
 
24
24
  - テキスト、図形、ジオメトリ、複雑なグラフィックの作成
25
25
  - キーフレームではなく、ふるまいを宣言する**リレーション**でアニメーション
package/README.ko.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  PinePaper MCP 서버는 AI 어시스턴트가 MCP를 통해 [PinePaper Studio](https://pinepaper.studio)에서 그래픽을 만들고 애니메이션하게 해줍니다. MCP 툴 콜을 지원하는 모든 AI(Claude, GPT, Gemini, 로컬 모델 등)와 호환됩니다.
21
21
 
22
- **157개의 툴** 제공 — 드로잉, 애니메이션, 다이어그램, 지도, 타이포그래피, 물리, 이미지 편집, 데이터 시각화, 내보내기:
22
+ **159개의 툴** 제공 — 드로잉, 애니메이션, 다이어그램, 지도, 타이포그래피, 물리, 이미지 편집, 데이터 시각화, 내보내기:
23
23
 
24
24
  - 텍스트·도형·기하·복잡한 그래픽 생성
25
25
  - 키프레임 대신 행동을 선언하는 **릴레이션**으로 애니메이션
package/README.md CHANGED
@@ -30,7 +30,7 @@
30
30
 
31
31
  PinePaper MCP Server enables AI assistants to create and animate graphics in [PinePaper Studio](https://pinepaper.studio) via the Model Context Protocol (MCP). Works with any AI that supports MCP tool calling (Claude, GPT, Gemini, local models, etc.).
32
32
 
33
- The server exposes **157 tools** across drawing, animation, diagrams, maps, typography, physics, image editing, data visualization, and export. Using natural language, you can:
33
+ The server exposes **159 tools** across drawing, animation, diagrams, maps, typography, physics, image editing, data visualization, and export. Using natural language, you can:
34
34
 
35
35
  - Create geometric shapes, text collages, diagrams, and data charts
36
36
  - Apply 37+ character-level text effects and 28+ vertex deformation presets
@@ -41,7 +41,7 @@ The server exposes **157 tools** across drawing, animation, diagrams, maps, typo
41
41
 
42
42
  ## Running it: local or hosted
43
43
 
44
- **Local is free and complete.** Every one of the 157 tools works when you run
44
+ **Local is free and complete.** Every one of the 159 tools works when you run
45
45
  this server yourself. There is no reduced tier and nothing held back.
46
46
 
47
47
  What it needs:
@@ -246,6 +246,142 @@ If you do not want an agent executing anything, `code` mode is a first-class pat
246
246
  - Puppeteer mode launches Chrome with `--no-sandbox` and `--disable-setuid-sandbox`. That is routine for headless automation and it does weaken Chrome's own process sandbox. If that matters where you are running it, use `code` mode or put the server in a container.
247
247
  - Puppeteer itself is an **optional** peer dependency, kept out of the default tree precisely because a headless browser plus an install script is what scanners flag hardest. Install it only if you want the executing mode.
248
248
 
249
+ ## What's new in 1.6.18
250
+
251
+ ### New: spring easings, the standard easing set, and custom curves
252
+
253
+ Keyframes take three spring easings: `spring` (settles with no overshoot),
254
+ `springSnappy` (quick, one small overshoot) and `springPlayful` (bouncy, a few
255
+ visible wobbles).
256
+
257
+ The standard easings are here too: sine, quad, cubic, quart, quint, expo,
258
+ circ and back as `easeIn…`, `easeOut…` and `easeInOut…` (for example
259
+ `easeOutExpo`, `easeInOutQuart`), plus in and out bounce and elastic.
260
+ `pinepaper_get_available_easings` lists every name.
261
+
262
+ `pinepaper_keyframe_animate` and `pinepaper_camera_animate` also take a custom
263
+ cubic-bezier `[x1, y1, x2, y2]`: x within 0..1, y may go outside it for
264
+ overshoot.
265
+
266
+ Every easing renders exactly in video, GIF and APNG. Lottie and CSS get the
267
+ closest single curve, so `springPlayful` loses its wobble there and the in-out
268
+ quart, quint, expo and circ curves are within a few percent.
269
+
270
+ ### New: look at your own animation
271
+
272
+ `pinepaper_capture_frames` takes `sheet`: many frames tiled into one small
273
+ image, saved to a file, with a rubric to score it against (hook, phone
274
+ readability, motion, variety, composition, brand, sound sync). Choose the
275
+ frames by time, every N seconds, a strip around a moment, or the loop's last
276
+ frame beside its first. Tiles are 360 px wide by default, the width of a
277
+ phone.
278
+
279
+ ### New: make one like this
280
+
281
+ `pinepaper_media` takes `analyze_reference`: give it an uploaded reference
282
+ video and it measures the format: where it cuts, how long each shot runs,
283
+ each shot's colours, how fast it opens, and its beat. `remake_from_reference`
284
+ then builds a new piece on that structure in your own words. Nothing of the
285
+ reference's footage, words or sound is copied.
286
+
287
+ ### New: start from the real brand
288
+
289
+ `pinepaper_brand_kit` takes `from_url`: give it a product's site and it saves
290
+ screenshots and the logo, and proposes a brand kit from the colours and fonts
291
+ the site actually uses, ready to review and apply. The site is read in a
292
+ separate browser context; your canvas is not touched.
293
+
294
+ ### New: watercolour, oil paint and cut paper
295
+
296
+ `pinepaper_design_medium` takes `apply`: render any shape in watercolour, oil
297
+ paint, cut paper, thread or hatching, with that medium's own options. A medium
298
+ the studio cannot make, or one that is a brush rather than a fill, is refused
299
+ with the reason.
300
+
301
+ ### New: motion blur
302
+
303
+ `pinepaper_agent_export` takes `motionBlur` for MP4 and WebM: each frame is
304
+ rendered several times across the shutter and averaged, so fast movement
305
+ smears the way it does on camera instead of strobing. It multiplies render
306
+ time; `estimateOnly` reports by how much before you commit to it.
307
+
308
+ ### Changed: plan generations before paying for them
309
+
310
+ `pinepaper_generate` and `pinepaper_generate_estimate` now tell agents to plan
311
+ the whole timeline and price the whole plan before the first paid call, to
312
+ reuse images already generated, and to pick video lengths the model supports
313
+ rather than generating long and cropping. `pinepaper_beat_cuts` treats a
314
+ generated song as the backbone of the edit, used whole.
315
+
316
+ ### Fixed
317
+
318
+ - The scene validator no longer flags valid easings as unknown.
319
+ - `pinepaper_agent_export` always saves PNG, JPG and WebP to a file and
320
+ returns its path. Small images used to come back inline as base64, which
321
+ could exceed a client's response limit.
322
+ - `pinepaper_execute_custom_code`: when the code fails part-way, the items it
323
+ created are removed, so the scene is not left half-built. The error says
324
+ what was removed. Edits to items that already existed are not reverted. The
325
+ 4-second loop limit is this tool's, per call: split long work across several
326
+ calls.
327
+ - `pinepaper_design_medium` `apply_thread`: the spine points format is
328
+ documented (`{x, y}` or `[x, y]`).
329
+ - `pinepaper_world3d`: colours take hex (`"#03050c"`) everywhere: the world's
330
+ sky, fog, light and ground colours, and material and light colours. A
331
+ refused `configure` now says why, naming the key. `ground_height` takes
332
+ `{x, z}`. `create` lists the configurable parameters by name.
333
+ - A browser session lost while idle recovers by itself: `pinepaper_refresh_page`
334
+ and every other tool relaunch the browser when no page answers, restore the
335
+ canvas size, and say in the result that the canvas was reset.
336
+ - `pinepaper_world3d` `extrude_path` takes `upright` (stand a drawing up, like
337
+ a sign) and `placement` (`centered`, the default, stands the mesh on the
338
+ ground at the point you give). JPG and WebP stills of a 3D world scene show
339
+ the world rather than a white frame.
340
+
341
+ ## What's new in 1.6.17
342
+
343
+ ### New: tell an agent "at 4 s, make it orange"
344
+
345
+ `pinepaper_modify_item` takes `atTime`: the change is written as a keyframe at
346
+ that moment, the same as dragging with auto-key on, eased from the previous
347
+ value and without disturbing the rest of the animation.
348
+ `pinepaper_query_mutations` shows a property's values over time.
349
+
350
+ ### New: cuts on the beat
351
+
352
+ `pinepaper_beat_cuts` analyses a music track, fits a beat grid and returns cut
353
+ points on every beat, every bar or every N beats — and can split a clip at each.
354
+ When the tempo is uncertain it says so and lists the likely alternatives;
355
+ `bpm` or `bpmHint` settles it.
356
+
357
+ ### New: exact frame counts, and larger canvases
358
+
359
+ - `pinepaper_agent_export` takes `frames` for an exact frame count (a duration
360
+ rounds up), for segments that must line up.
361
+ - Canvases up to 16384 px per side, for digital out-of-home and projection;
362
+ above 8192 the result notes that Safari and iOS may not manage it.
363
+ - `pinepaper_import_image` takes `maxEdge` (or `'native'`) and `nativeSize`, and
364
+ says when the studio stored an image smaller than its source.
365
+ - `pinepaper_media` `upload_video` takes `fit` (`contain`, `cover`, `none`), and
366
+ `scale` is documented as relative to it.
367
+
368
+ ### Changed: exports say more about what they produced
369
+
370
+ - When an MP4 cannot be made at the requested size, the file is named by what it
371
+ is (WebM) and a warning says why.
372
+ - A video whose animation did not play, or whose encoder froze, is flagged.
373
+ - Asking for ProRes or HAP explains the route that works (a PNG sequence).
374
+ - Frame captures, region stills and flash checks wait for video on screen, and
375
+ report whether they could.
376
+ - The engine's structured warnings reach the caller whole.
377
+
378
+ ### Fixed
379
+
380
+ - `pinepaper_accessibility_check` judged text only at the current playhead, so
381
+ timed captions could pass unchecked; it now samples the timeline, and says
382
+ when no text was visible rather than passing.
383
+ - `pinepaper_agent_export` accepts `fps` and `scale` for APNG.
384
+
249
385
  ## What's new in 1.6.16
250
386
 
251
387
  ### New: video editing on the timeline
@@ -1226,7 +1362,7 @@ Fourteen new tools (121 → 135) and new actions across the surface — the rele
1226
1362
 
1227
1363
  ## Toolkits & Token Budget
1228
1364
 
1229
- 157 tools is a lot of context. The server ships a **toolkit** system that serves only the tools a given client needs, plus a **verbosity** system that controls how long each tool description is.
1365
+ 159 tools is a lot of context. The server ships a **toolkit** system that serves only the tools a given client needs, plus a **verbosity** system that controls how long each tool description is.
1230
1366
 
1231
1367
  **Toolkit profiles** (`PINEPAPER_TOOLKIT`):
1232
1368
 
@@ -1431,7 +1567,7 @@ Generate instruction/code pairs for LLM fine-tuning:
1431
1567
 
1432
1568
  ## Tools Reference
1433
1569
 
1434
- All 157 tools, grouped by the tag used for toolkit filtering.
1570
+ All 159 tools, grouped by the tag used for toolkit filtering.
1435
1571
 
1436
1572
  ### Canvas (`canvas`)
1437
1573
  | Tool | Description |
@@ -1653,6 +1789,8 @@ All 157 tools, grouped by the tag used for toolkit filtering.
1653
1789
  | `pinepaper_analyze_design` | Analyze design quality |
1654
1790
  | `pinepaper_validate_design` | Validate against design rules |
1655
1791
  | `pinepaper_validate` | General validation |
1792
+ | `pinepaper_beat_cuts` | Cut points on the music's beats or bars; split a clip at each |
1793
+ | `pinepaper_query_mutations` | A property's keyframe series over time |
1656
1794
  | `pinepaper_accessibility_check` | Text contrast and flashing (WCAG), as a heuristic screen |
1657
1795
  | `pinepaper_validate_scene` | Validate scene integrity |
1658
1796
  | `pinepaper_instantiate_ontology` | Compile a pp: design graph into a scene |
package/README.pt-BR.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  O servidor MCP do PinePaper permite que assistentes de IA criem e animem gráficos no [PinePaper Studio](https://pinepaper.studio) via Model Context Protocol. Funciona com qualquer IA que suporte chamadas de ferramenta MCP (Claude, GPT, Gemini, modelos locais etc.).
21
21
 
22
- São **157 ferramentas**: desenho, animação, diagramas, mapas, tipografia, física, edição de imagens, visualização de dados e exportação:
22
+ São **159 ferramentas**: desenho, animação, diagramas, mapas, tipografia, física, edição de imagens, visualização de dados e exportação:
23
23
 
24
24
  - Criar texto, formas, geometria e gráficos complexos
25
25
  - Animar com **relações** declarativas em vez de keyframes
package/README.zh-CN.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  PinePaper MCP 服务器让 AI 助手通过 MCP 协议在 [PinePaper Studio](https://pinepaper.studio) 中创建和驱动图形动画。兼容任何支持 MCP 工具调用的 AI(Claude、GPT、Gemini、本地模型等)。
21
21
 
22
- 服务器提供 **157 个工具**,覆盖绘图、动画、图表、地图、排版、物理、图像编辑、数据可视化与导出:
22
+ 服务器提供 **159 个工具**,覆盖绘图、动画、图表、地图、排版、物理、图像编辑、数据可视化与导出:
23
23
 
24
24
  - 创建文本、形状、几何图形与复杂图稿
25
25
  - 用行为驱动的**关系(relations)**做动画,而不是逐帧关键帧
@@ -4,6 +4,7 @@
4
4
  * Controls a real browser instance running PinePaper Studio.
5
5
  * Executes code directly via page.evaluate() and provides screenshots.
6
6
  */
7
+ import type { Page } from 'puppeteer';
7
8
  /**
8
9
  * What a navigation waits for. Puppeteer's own union, spelled out here so the
9
10
  * env var can be validated against it without importing puppeteer — which is
@@ -123,6 +124,16 @@ export interface ExecuteResult {
123
124
  * keep referencing item ids from before the reload. undefined = unknown.
124
125
  */
125
126
  canvasReset?: boolean;
127
+ /**
128
+ * Set alongside `recovered` when no live tab could be re-bound and the
129
+ * browser was RELAUNCHED: the scene is gone for certain (canvasReset true).
130
+ */
131
+ relaunched?: boolean;
132
+ /** The canvas size put back after a relaunch, when one was known. */
133
+ canvasSizeRestored?: {
134
+ width: number;
135
+ height: number;
136
+ };
126
137
  }
127
138
  /**
128
139
  * Is PinePaper Studio actually usable? Evaluated IN THE PAGE, so it closes over
@@ -182,6 +193,28 @@ export declare class PinePaperBrowserController {
182
193
  * Check if connected to browser
183
194
  */
184
195
  get connected(): boolean;
196
+ /**
197
+ * The studio's video export, if one is running: its 0..100 progress. Read
198
+ * concurrently with the call that started it (the export awaits between
199
+ * frames, so a second evaluate gets a turn). null when nothing is exporting
200
+ * or the page cannot be read.
201
+ */
202
+ exportStatus(): Promise<{
203
+ percent: number;
204
+ } | null>;
205
+ /**
206
+ * Stop a running video export (VideoExporter.cancel: closes the encoders and
207
+ * the muxer). For a cancelled MCP request, so the studio does not keep
208
+ * rendering minutes of video nobody will receive. True if one was stopped.
209
+ */
210
+ cancelExport(): Promise<boolean>;
211
+ /**
212
+ * Run `fn` on a page in a fresh, ISOLATED browser context (its own cookies
213
+ * and storage), closed afterwards. For reading a site that is not the
214
+ * studio (brand intake): the studio tab, and the canvas in it, is never
215
+ * navigated away, and the site's cookies never mix with the studio's.
216
+ */
217
+ withIsolatedPage<T>(fn: (page: Page) => Promise<T>): Promise<T>;
185
218
  /**
186
219
  * Get current studio URL (always includes agent parameters for MCP server)
187
220
  */
@@ -229,6 +262,20 @@ export declare class PinePaperBrowserController {
229
262
  * @returns true when a responsive page was re-bound.
230
263
  */
231
264
  private reacquirePage;
265
+ /**
266
+ * The last canvas size seen on a healthy page. A relaunched browser comes
267
+ * up at the studio's default (800x600), so without this a session that
268
+ * died idle came back at the wrong size, silently (M6, 2026-10-02).
269
+ */
270
+ private lastCanvasSize;
271
+ private rememberCanvasSize;
272
+ /**
273
+ * Get a working page back after a stale-frame error. First re-bind a live
274
+ * tab (the scene may survive); if none answers, as after days idle when
275
+ * every frame is detached, relaunch ONCE and put the canvas size back.
276
+ * 'relaunched' means the scene is gone. false means even that failed.
277
+ */
278
+ private recoverSession;
232
279
  /** Number of registry items currently on the canvas, or null if unknown. */
233
280
  private canvasItemCount;
234
281
  /**
@@ -255,7 +302,20 @@ export declare class PinePaperBrowserController {
255
302
  * Refresh the page and wait for PinePaper to be ready again.
256
303
  * This is the most reliable way to get a clean canvas.
257
304
  */
258
- refreshPage(): Promise<void>;
305
+ /**
306
+ * Reload the studio. The tool whose job is recovery could not recover: on a
307
+ * detached frame, page.reload() threw the same "detached Frame" error as
308
+ * every other call (M6, after ~2 days idle). A stale page is now recovered
309
+ * first: re-bound and reloaded, or the browser relaunched at the remembered
310
+ * canvas size. The return says which, so the caller knows the scene is gone.
311
+ */
312
+ refreshPage(): Promise<{
313
+ relaunched?: boolean;
314
+ canvasSizeRestored?: {
315
+ width: number;
316
+ height: number;
317
+ };
318
+ }>;
259
319
  /**
260
320
  * Check if PinePaper API is available
261
321
  */
@@ -1 +1 @@
1
- {"version":3,"file":"puppeteer-controller.d.ts","sourceRoot":"","sources":["../../src/browser/puppeteer-controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"puppeteer-controller.d.ts","sourceRoot":"","sources":["../../src/browser/puppeteer-controller.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@
4
4
  * Controls a real browser instance running PinePaper Studio.
5
5
  * Executes code directly via page.evaluate() and provides screenshots.
6
6
  */
7
+ import { mcpLog } from '../utils/mcp-log.js';
7
8
  import { LAZY_HEAVY_SUBSYSTEMS, LAZY_HEAVY_CLASSES, ENGINE_SURFACE } from '../tools/engine-surface.js';
8
9
  export const PAGE_WAIT_UNTIL = ['load', 'domcontentloaded', 'networkidle0', 'networkidle2'];
9
10
  /**
@@ -157,6 +158,69 @@ export class PinePaperBrowserController {
157
158
  get connected() {
158
159
  return this.isConnected && this.page !== null;
159
160
  }
161
+ /**
162
+ * The studio's video export, if one is running: its 0..100 progress. Read
163
+ * concurrently with the call that started it (the export awaits between
164
+ * frames, so a second evaluate gets a turn). null when nothing is exporting
165
+ * or the page cannot be read.
166
+ */
167
+ async exportStatus() {
168
+ if (!this.page || !this.isConnected)
169
+ return null;
170
+ try {
171
+ return await this.page.evaluate(() => {
172
+ const app = window.app || window.PinePaper;
173
+ const ve = app && app.exportEngine && app.exportEngine.videoExporter;
174
+ if (!ve || !ve.isRecording)
175
+ return null;
176
+ const p = Number(ve.progress);
177
+ return { percent: Number.isFinite(p) ? Math.max(0, Math.min(100, p)) : 0 };
178
+ });
179
+ }
180
+ catch {
181
+ return null;
182
+ }
183
+ }
184
+ /**
185
+ * Stop a running video export (VideoExporter.cancel: closes the encoders and
186
+ * the muxer). For a cancelled MCP request, so the studio does not keep
187
+ * rendering minutes of video nobody will receive. True if one was stopped.
188
+ */
189
+ async cancelExport() {
190
+ if (!this.page || !this.isConnected)
191
+ return false;
192
+ try {
193
+ return await this.page.evaluate(async () => {
194
+ const app = window.app || window.PinePaper;
195
+ const ve = app && app.exportEngine && app.exportEngine.videoExporter;
196
+ if (!ve || !ve.isRecording || typeof ve.cancel !== 'function')
197
+ return false;
198
+ await ve.cancel();
199
+ return true;
200
+ });
201
+ }
202
+ catch {
203
+ return false;
204
+ }
205
+ }
206
+ /**
207
+ * Run `fn` on a page in a fresh, ISOLATED browser context (its own cookies
208
+ * and storage), closed afterwards. For reading a site that is not the
209
+ * studio (brand intake): the studio tab, and the canvas in it, is never
210
+ * navigated away, and the site's cookies never mix with the studio's.
211
+ */
212
+ async withIsolatedPage(fn) {
213
+ if (!this.browser)
214
+ throw new Error('Not connected to browser');
215
+ const context = await this.browser.createBrowserContext();
216
+ try {
217
+ const page = await context.newPage();
218
+ return await fn(page);
219
+ }
220
+ finally {
221
+ await context.close().catch(() => { });
222
+ }
223
+ }
160
224
  /**
161
225
  * Get current studio URL (always includes agent parameters for MCP server)
162
226
  */
@@ -340,6 +404,68 @@ export class PinePaperBrowserController {
340
404
  return false;
341
405
  }
342
406
  }
407
+ /**
408
+ * The last canvas size seen on a healthy page. A relaunched browser comes
409
+ * up at the studio's default (800x600), so without this a session that
410
+ * died idle came back at the wrong size, silently (M6, 2026-10-02).
411
+ */
412
+ lastCanvasSize = null;
413
+ async rememberCanvasSize() {
414
+ if (!this.page)
415
+ return;
416
+ try {
417
+ const size = await this.page.evaluate(() => {
418
+ const app = window.app || window.PinePaper;
419
+ const s = app && typeof app.getCanvasSize === 'function' ? app.getCanvasSize() : null;
420
+ return s && s.width > 0 && s.height > 0 ? { width: s.width, height: s.height } : null;
421
+ });
422
+ if (size)
423
+ this.lastCanvasSize = size;
424
+ }
425
+ catch { /* keep the last known size */ }
426
+ }
427
+ /**
428
+ * Get a working page back after a stale-frame error. First re-bind a live
429
+ * tab (the scene may survive); if none answers, as after days idle when
430
+ * every frame is detached, relaunch ONCE and put the canvas size back.
431
+ * 'relaunched' means the scene is gone. false means even that failed.
432
+ */
433
+ async recoverSession() {
434
+ if (await this.reacquirePage())
435
+ return { kind: 'reacquired' };
436
+ const size = this.lastCanvasSize;
437
+ try {
438
+ await this.disconnect();
439
+ await this.connect();
440
+ }
441
+ catch (e) {
442
+ console.error('[PinePaper] Relaunch after a lost session failed:', e instanceof Error ? e.message : e);
443
+ mcpLog('error', { event: 'session_lost', message: 'The browser session was lost and could not be relaunched: call pinepaper_browser_disconnect, then pinepaper_browser_connect.' });
444
+ return false;
445
+ }
446
+ if (!this.page || !this.isConnected)
447
+ return false;
448
+ let restored;
449
+ if (size) {
450
+ try {
451
+ const ok = await this.page.evaluate((w, h) => {
452
+ const app = window.app || window.PinePaper;
453
+ if (!app || typeof app.setCanvasSize !== 'function')
454
+ return false;
455
+ app.setCanvasSize(w, h);
456
+ return true;
457
+ }, size.width, size.height);
458
+ if (ok) {
459
+ restored = size;
460
+ this.lastCanvasSize = size;
461
+ }
462
+ }
463
+ catch { /* reported as not restored */ }
464
+ }
465
+ console.error('[PinePaper] Session lost; browser relaunched' + (restored ? ` at ${restored.width}x${restored.height}` : ''));
466
+ mcpLog('warning', { event: 'session_relaunched', message: 'The browser session was lost; the browser was relaunched and the canvas is empty.', ...(restored ? { canvasSizeRestored: restored } : {}) });
467
+ return { kind: 'relaunched', ...(restored ? { canvasSizeRestored: restored } : {}) };
468
+ }
343
469
  /** Number of registry items currently on the canvas, or null if unknown. */
344
470
  async canvasItemCount() {
345
471
  if (!this.page)
@@ -538,6 +664,8 @@ export class PinePaperBrowserController {
538
664
  if (takeScreenshot && result.success) {
539
665
  screenshot = await this.takeScreenshot();
540
666
  }
667
+ if (result.success)
668
+ await this.rememberCanvasSize();
541
669
  return {
542
670
  ...result,
543
671
  screenshot,
@@ -548,7 +676,8 @@ export class PinePaperBrowserController {
548
676
  // so every later call hit the same error until the caller manually
549
677
  // disconnected — and disconnect+connect relaunches the browser, losing
550
678
  // the canvas. Re-bind to the live page and retry ONCE instead.
551
- if (PinePaperBrowserController.isStaleFrameError(error) && await this.reacquirePage()) {
679
+ const recovery = PinePaperBrowserController.isStaleFrameError(error) ? await this.recoverSession() : false;
680
+ if (recovery) {
552
681
  try {
553
682
  // Forward `options`: a recovered export that silently reverted to the
554
683
  // 10s governor budget would die at the same place the retry exists to
@@ -557,6 +686,15 @@ export class PinePaperBrowserController {
557
686
  // The page reloaded under us, so the scene may be gone even though
558
687
  // execution now succeeds. Say so explicitly — silently continuing
559
688
  // against vanished item ids is how this bug wasted a whole rebuild.
689
+ if (recovery.kind === 'relaunched') {
690
+ return {
691
+ ...retry,
692
+ recovered: true,
693
+ relaunched: true,
694
+ canvasReset: true,
695
+ ...(recovery.canvasSizeRestored ? { canvasSizeRestored: recovery.canvasSizeRestored } : {}),
696
+ };
697
+ }
560
698
  const items = await this.canvasItemCount();
561
699
  return {
562
700
  ...retry,
@@ -572,9 +710,12 @@ export class PinePaperBrowserController {
572
710
  };
573
711
  }
574
712
  }
713
+ const message = error instanceof Error ? error.message : 'Unknown execution error';
575
714
  return {
576
715
  success: false,
577
- error: error instanceof Error ? error.message : 'Unknown execution error',
716
+ error: PinePaperBrowserController.isStaleFrameError(error)
717
+ ? `${message}. The browser session was lost and could not be recovered automatically: call pinepaper_browser_disconnect, then pinepaper_browser_connect.`
718
+ : message,
578
719
  };
579
720
  }
580
721
  }
@@ -641,18 +782,39 @@ export class PinePaperBrowserController {
641
782
  * Refresh the page and wait for PinePaper to be ready again.
642
783
  * This is the most reliable way to get a clean canvas.
643
784
  */
785
+ /**
786
+ * Reload the studio. The tool whose job is recovery could not recover: on a
787
+ * detached frame, page.reload() threw the same "detached Frame" error as
788
+ * every other call (M6, after ~2 days idle). A stale page is now recovered
789
+ * first: re-bound and reloaded, or the browser relaunched at the remembered
790
+ * canvas size. The return says which, so the caller knows the scene is gone.
791
+ */
644
792
  async refreshPage() {
645
793
  if (!this.page) {
646
794
  throw new Error('Not connected to browser');
647
795
  }
648
796
  console.error('[PinePaper] Refreshing page...');
649
- await this.page.reload({
650
- waitUntil: this.config.waitUntil,
651
- timeout: this.config.timeout,
652
- });
797
+ try {
798
+ await this.page.reload({ waitUntil: this.config.waitUntil, timeout: this.config.timeout });
799
+ }
800
+ catch (error) {
801
+ if (!PinePaperBrowserController.isStaleFrameError(error))
802
+ throw error;
803
+ const recovery = await this.recoverSession();
804
+ if (!recovery) {
805
+ throw new Error('The browser session was lost and could not be recovered: call pinepaper_browser_disconnect, then pinepaper_browser_connect.');
806
+ }
807
+ if (recovery.kind === 'relaunched') {
808
+ console.error('[PinePaper] Page refreshed by relaunching the browser');
809
+ return { relaunched: true, ...(recovery.canvasSizeRestored ? { canvasSizeRestored: recovery.canvasSizeRestored } : {}) };
810
+ }
811
+ await this.page.reload({ waitUntil: this.config.waitUntil, timeout: this.config.timeout });
812
+ }
653
813
  // Wait for PinePaper to be ready again
654
814
  await this.waitForPinePaper();
815
+ await this.rememberCanvasSize();
655
816
  console.error('[PinePaper] Page refreshed and ready');
817
+ return {};
656
818
  }
657
819
  /**
658
820
  * Check if PinePaper API is available