@pinepaper.studio/mcp-server 1.6.7 → 1.6.9
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/README.de.md +9 -5
- package/README.es.md +9 -5
- package/README.fr.md +9 -5
- package/README.hi.md +8 -5
- package/README.ja.md +8 -5
- package/README.ko.md +8 -4
- package/README.md +256 -17
- package/README.pt-BR.md +9 -5
- package/README.zh-CN.md +6 -4
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/puppeteer-controller.d.ts +86 -2
- package/dist/browser/puppeteer-controller.d.ts.map +1 -1
- package/dist/browser/puppeteer-controller.js +143 -60
- package/dist/browser/puppeteer-controller.js.map +1 -1
- package/dist/character/data/concepts.json +1 -0
- package/dist/character/data/depictions.json +1 -0
- package/dist/character/data/styles.json +1 -0
- package/dist/character/duration-scale.d.ts +13 -0
- package/dist/character/duration-scale.d.ts.map +1 -0
- package/dist/character/duration-scale.js +13 -0
- package/dist/character/duration-scale.js.map +1 -0
- package/dist/character/path-bbox.d.ts +82 -0
- package/dist/character/path-bbox.d.ts.map +1 -0
- package/dist/character/path-bbox.js +496 -0
- package/dist/character/path-bbox.js.map +1 -0
- package/dist/character/perform.d.ts +149 -0
- package/dist/character/perform.d.ts.map +1 -0
- package/dist/character/perform.js +626 -0
- package/dist/character/perform.js.map +1 -0
- package/dist/character/resolve.d.ts +51 -0
- package/dist/character/resolve.d.ts.map +1 -0
- package/dist/character/resolve.js +239 -0
- package/dist/character/resolve.js.map +1 -0
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -1
- package/dist/design/design-systems.d.ts +224 -0
- package/dist/design/design-systems.d.ts.map +1 -0
- package/dist/design/design-systems.js +434 -0
- package/dist/design/design-systems.js.map +1 -0
- package/dist/execution/canvas-state.d.ts +7 -0
- package/dist/execution/canvas-state.d.ts.map +1 -1
- package/dist/execution/canvas-state.js +20 -8
- package/dist/execution/canvas-state.js.map +1 -1
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/ontology/design-graph.d.ts +9 -0
- package/dist/ontology/design-graph.d.ts.map +1 -1
- package/dist/ontology/design-graph.js +98 -3
- package/dist/ontology/design-graph.js.map +1 -1
- package/dist/ontology/types.d.ts +42 -0
- package/dist/ontology/types.d.ts.map +1 -1
- package/dist/ontology/vocabulary.d.ts.map +1 -1
- package/dist/ontology/vocabulary.js +20 -0
- package/dist/ontology/vocabulary.js.map +1 -1
- package/dist/tools/compact-descriptions.d.ts.map +1 -1
- package/dist/tools/compact-descriptions.js +25 -0
- package/dist/tools/compact-descriptions.js.map +1 -1
- package/dist/tools/definitions.d.ts +1 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +404 -25
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/engine-methods.d.ts +18 -0
- package/dist/tools/engine-methods.d.ts.map +1 -0
- package/dist/tools/engine-methods.js +241 -0
- package/dist/tools/engine-methods.js.map +1 -0
- package/dist/tools/handlers/character.d.ts +121 -0
- package/dist/tools/handlers/character.d.ts.map +1 -0
- package/dist/tools/handlers/character.js +315 -0
- package/dist/tools/handlers/character.js.map +1 -0
- package/dist/tools/handlers.d.ts +2 -0
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +522 -18
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts.map +1 -1
- package/dist/tools/minimal-descriptions.js +15 -3
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +8 -0
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts +106 -1
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +1273 -18
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +6805 -124
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +627 -17
- package/dist/types/schemas.js.map +1 -1
- package/dist/vendor/design/art-deco-generator.d.ts +52 -0
- package/dist/vendor/design/art-deco-generator.d.ts.map +1 -0
- package/dist/vendor/design/art-deco-generator.js +144 -0
- package/dist/vendor/design/art-deco-generator.js.map +1 -0
- package/dist/vendor/design/art-nouveau-generator.d.ts +46 -0
- package/dist/vendor/design/art-nouveau-generator.d.ts.map +1 -0
- package/dist/vendor/design/art-nouveau-generator.js +134 -0
- package/dist/vendor/design/art-nouveau-generator.js.map +1 -0
- package/dist/vendor/design/bauhaus-generator.d.ts +53 -0
- package/dist/vendor/design/bauhaus-generator.d.ts.map +1 -0
- package/dist/vendor/design/bauhaus-generator.js +126 -0
- package/dist/vendor/design/bauhaus-generator.js.map +1 -0
- package/dist/vendor/design/brutalist-generator.d.ts +53 -0
- package/dist/vendor/design/brutalist-generator.d.ts.map +1 -0
- package/dist/vendor/design/brutalist-generator.js +148 -0
- package/dist/vendor/design/brutalist-generator.js.map +1 -0
- package/dist/vendor/design/claymorphism-generator.d.ts +46 -0
- package/dist/vendor/design/claymorphism-generator.d.ts.map +1 -0
- package/dist/vendor/design/claymorphism-generator.js +135 -0
- package/dist/vendor/design/claymorphism-generator.js.map +1 -0
- package/dist/vendor/design/corporate-memphis-generator.d.ts +47 -0
- package/dist/vendor/design/corporate-memphis-generator.d.ts.map +1 -0
- package/dist/vendor/design/corporate-memphis-generator.js +144 -0
- package/dist/vendor/design/corporate-memphis-generator.js.map +1 -0
- package/dist/vendor/design/cyberpunk-generator.d.ts +52 -0
- package/dist/vendor/design/cyberpunk-generator.d.ts.map +1 -0
- package/dist/vendor/design/cyberpunk-generator.js +162 -0
- package/dist/vendor/design/cyberpunk-generator.js.map +1 -0
- package/dist/vendor/design/de-stijl-generator.d.ts +50 -0
- package/dist/vendor/design/de-stijl-generator.d.ts.map +1 -0
- package/dist/vendor/design/de-stijl-generator.js +154 -0
- package/dist/vendor/design/de-stijl-generator.js.map +1 -0
- package/dist/vendor/design/design-style.d.ts +3 -0
- package/dist/vendor/design/design-style.d.ts.map +1 -0
- package/dist/vendor/design/design-style.js +12 -0
- package/dist/vendor/design/design-style.js.map +1 -0
- package/dist/vendor/design/design-systems-dtcg.d.ts +59 -0
- package/dist/vendor/design/design-systems-dtcg.d.ts.map +1 -0
- package/dist/vendor/design/design-systems-dtcg.js +571 -0
- package/dist/vendor/design/design-systems-dtcg.js.map +1 -0
- package/dist/vendor/design/frutiger-aero-generator.d.ts +46 -0
- package/dist/vendor/design/frutiger-aero-generator.d.ts.map +1 -0
- package/dist/vendor/design/frutiger-aero-generator.js +133 -0
- package/dist/vendor/design/frutiger-aero-generator.js.map +1 -0
- package/dist/vendor/design/memphis-generator.d.ts +56 -0
- package/dist/vendor/design/memphis-generator.d.ts.map +1 -0
- package/dist/vendor/design/memphis-generator.js +158 -0
- package/dist/vendor/design/memphis-generator.js.map +1 -0
- package/dist/vendor/design/mid-century-upa-generator.d.ts +46 -0
- package/dist/vendor/design/mid-century-upa-generator.d.ts.map +1 -0
- package/dist/vendor/design/mid-century-upa-generator.js +137 -0
- package/dist/vendor/design/mid-century-upa-generator.js.map +1 -0
- package/dist/vendor/design/neumorphic-generator.d.ts +53 -0
- package/dist/vendor/design/neumorphic-generator.d.ts.map +1 -0
- package/dist/vendor/design/neumorphic-generator.js +182 -0
- package/dist/vendor/design/neumorphic-generator.js.map +1 -0
- package/dist/vendor/design/op-art-generator.d.ts +45 -0
- package/dist/vendor/design/op-art-generator.d.ts.map +1 -0
- package/dist/vendor/design/op-art-generator.js +116 -0
- package/dist/vendor/design/op-art-generator.js.map +1 -0
- package/dist/vendor/design/pixel-art-generator.d.ts +45 -0
- package/dist/vendor/design/pixel-art-generator.d.ts.map +1 -0
- package/dist/vendor/design/pixel-art-generator.js +143 -0
- package/dist/vendor/design/pixel-art-generator.js.map +1 -0
- package/dist/vendor/design/pop-art-generator.d.ts +53 -0
- package/dist/vendor/design/pop-art-generator.d.ts.map +1 -0
- package/dist/vendor/design/pop-art-generator.js +164 -0
- package/dist/vendor/design/pop-art-generator.js.map +1 -0
- package/dist/vendor/design/risograph-generator.d.ts +53 -0
- package/dist/vendor/design/risograph-generator.d.ts.map +1 -0
- package/dist/vendor/design/risograph-generator.js +170 -0
- package/dist/vendor/design/risograph-generator.js.map +1 -0
- package/dist/vendor/design/swiss-style-generator.d.ts +55 -0
- package/dist/vendor/design/swiss-style-generator.d.ts.map +1 -0
- package/dist/vendor/design/swiss-style-generator.js +147 -0
- package/dist/vendor/design/swiss-style-generator.js.map +1 -0
- package/dist/vendor/design/vaporwave-generator.d.ts +51 -0
- package/dist/vendor/design/vaporwave-generator.d.ts.map +1 -0
- package/dist/vendor/design/vaporwave-generator.js +139 -0
- package/dist/vendor/design/vaporwave-generator.js.map +1 -0
- package/manifest.json +33 -1
- package/package.json +13 -5
package/README.zh-CN.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
PinePaper MCP 服务器让 AI 助手通过 MCP 协议在 [PinePaper Studio](https://pinepaper.studio) 中创建和驱动图形动画。兼容任何支持 MCP 工具调用的 AI(Claude、GPT、Gemini、本地模型等)。
|
|
16
16
|
|
|
17
|
-
服务器提供 **
|
|
17
|
+
服务器提供 **152 个工具**,覆盖绘图、动画、图表、地图、排版、物理、图像编辑、数据可视化与导出:
|
|
18
18
|
|
|
19
19
|
- 创建文本、形状、几何图形与复杂图稿
|
|
20
20
|
- 用行为驱动的**关系(relations)**做动画,而不是逐帧关键帧
|
|
@@ -77,9 +77,11 @@ npm install -g @pinepaper.studio/mcp-server
|
|
|
77
77
|
服务器** —— 相同的工具、相同的版本,通过 HTTP 提供,无需安装,也不需要在你的
|
|
78
78
|
机器上运行浏览器。
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
托管需要成本,因此是付费的,按额度(credits)运行。当前价格请见
|
|
81
|
+
[cloud.pinepaper.studio](https://cloud.pinepaper.studio)。能在本地运行就在本地运行,
|
|
82
|
+
但两者并不完全相同:托管服务使用经过测试的 LLM,并在生成的代码进入画布前
|
|
83
|
+
进行修复 —— 这是本地服务器自身无法做到的。哪个模型更合适,你可以自己验证:基准测试用同一个提示词在多个模型上运行,
|
|
84
|
+
并排展示结果,任意一次运行都可分享。目前为邀请制测试版。
|
|
83
85
|
|
|
84
86
|
## 更多
|
|
85
87
|
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* Exports for browser-side usage of PinePaper MCP tools.
|
|
5
5
|
*/
|
|
6
6
|
export { PinePaperBridge, PinePaperStudioHandler, createBridge, createStudioHandler, type BridgeMessage, type BridgeConfig, } from './bridge.js';
|
|
7
|
-
export { PinePaperBrowserController, getBrowserController, resetBrowserController, resetBrowserControllerSync, type BrowserControllerConfig, type ExecuteResult, type AgentConnectOptions, type BatchExecuteResult, } from './puppeteer-controller.js';
|
|
7
|
+
export { PinePaperBrowserController, getBrowserController, resetBrowserController, resetBrowserControllerSync, PAGE_WAIT_UNTIL, type BrowserControllerConfig, type ExecuteCodeOptions, type PageWaitUntil, type ExecuteResult, type AgentConnectOptions, type BatchExecuteResult, } from './puppeteer-controller.js';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,YAAY,EACZ,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,2BAA2B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,YAAY,EACZ,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,2BAA2B,CAAC"}
|
package/dist/browser/index.js
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* Exports for browser-side usage of PinePaper MCP tools.
|
|
5
5
|
*/
|
|
6
6
|
export { PinePaperBridge, PinePaperStudioHandler, createBridge, createStudioHandler, } from './bridge.js';
|
|
7
|
-
export { PinePaperBrowserController, getBrowserController, resetBrowserController, resetBrowserControllerSync, } from './puppeteer-controller.js';
|
|
7
|
+
export { PinePaperBrowserController, getBrowserController, resetBrowserController, resetBrowserControllerSync, PAGE_WAIT_UNTIL, } from './puppeteer-controller.js';
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,YAAY,EACZ,mBAAmB,GAGpB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,YAAY,EACZ,mBAAmB,GAGpB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,eAAe,GAOhB,MAAM,2BAA2B,CAAC"}
|
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
* Controls a real browser instance running PinePaper Studio.
|
|
5
5
|
* Executes code directly via page.evaluate() and provides screenshots.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* What a navigation waits for. Puppeteer's own union, spelled out here so the
|
|
9
|
+
* env var can be validated against it without importing puppeteer — which is
|
|
10
|
+
* an optional peer, absent on most installs (see connect()).
|
|
11
|
+
*/
|
|
12
|
+
export type PageWaitUntil = 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
|
|
13
|
+
export declare const PAGE_WAIT_UNTIL: readonly PageWaitUntil[];
|
|
7
14
|
export interface BrowserControllerConfig {
|
|
8
15
|
/** PinePaper Studio URL (default: https://pinepaper.studio) */
|
|
9
16
|
studioUrl?: string;
|
|
@@ -17,6 +24,45 @@ export interface BrowserControllerConfig {
|
|
|
17
24
|
timeout?: number;
|
|
18
25
|
/** Enable agent mode with ?agent=1 URL parameter */
|
|
19
26
|
agentMode?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* What navigations wait for (default: 'domcontentloaded').
|
|
29
|
+
*
|
|
30
|
+
* Every goto/reload used to be networkidle2, which asks for two-or-fewer
|
|
31
|
+
* connections held for half a second — a bar Studio never clears behind a
|
|
32
|
+
* proxy, or with analytics and polling sockets open. Connect then burned the
|
|
33
|
+
* entire timeout and failed against a page that had been usable for seconds.
|
|
34
|
+
* Readiness is waitForPinePaper()'s job; a navigation only has to hand us a
|
|
35
|
+
* document.
|
|
36
|
+
*/
|
|
37
|
+
waitUntil?: PageWaitUntil;
|
|
38
|
+
/**
|
|
39
|
+
* Chrome --proxy-server value (e.g. 'http://127.0.0.1:8080'). Setting it also
|
|
40
|
+
* passes --ignore-certificate-errors, because a proxy that intercepts TLS
|
|
41
|
+
* presents its own certificate and Chrome would otherwise refuse every
|
|
42
|
+
* origin. Unset → neither flag, and certificate checking stays on.
|
|
43
|
+
*/
|
|
44
|
+
proxy?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Run code through FxTool's governor (app.runGenerated) when the build has
|
|
47
|
+
* one. Default true. False raw-evals instead — see ExecuteCodeOptions.
|
|
48
|
+
*/
|
|
49
|
+
governor?: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface ExecuteCodeOptions {
|
|
52
|
+
/**
|
|
53
|
+
* Governor budget for this run, in ms. FxTool's runGenerated defaults to
|
|
54
|
+
* 10_000 (RUN_DEFAULTS.timeoutMs, an async-tail Promise.race): a sane guard
|
|
55
|
+
* against a runaway script, and the wrong number for an export — the one
|
|
56
|
+
* operation that legitimately runs for minutes.
|
|
57
|
+
*/
|
|
58
|
+
governorTimeoutMs?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Skip app.runGenerated entirely and raw-eval the code. That gives up the
|
|
61
|
+
* governor report, the structured error codes and the seeded PRNG, so it is
|
|
62
|
+
* the escape hatch for a deployed build whose governor ignores the budget
|
|
63
|
+
* above — never a fast path. Defaults to the controller's `governor: false`.
|
|
64
|
+
*/
|
|
65
|
+
bypassGovernor?: boolean;
|
|
20
66
|
}
|
|
21
67
|
export interface AgentConnectOptions {
|
|
22
68
|
/** Run browser in headless mode (default: true for agent workflows) */
|
|
@@ -62,6 +108,29 @@ export interface ExecuteResult {
|
|
|
62
108
|
*/
|
|
63
109
|
canvasReset?: boolean;
|
|
64
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Is PinePaper Studio actually usable? Evaluated IN THE PAGE, so it closes over
|
|
113
|
+
* nothing.
|
|
114
|
+
*
|
|
115
|
+
* The old test was `app !== undefined || pinepaper !== undefined || paper !==
|
|
116
|
+
* undefined`, hand-copied into four places. `window.paper` is Paper.js, which
|
|
117
|
+
* attaches the moment the canvas script loads — long before Studio has built
|
|
118
|
+
* its API — so all four sites could report ready against a page that would
|
|
119
|
+
* throw on the first call.
|
|
120
|
+
*
|
|
121
|
+
* The test is FxTool's own, mirrored rather than invented: js/app.js polls
|
|
122
|
+
* `app && app.itemRegistry && typeof app.create === 'function'` under the
|
|
123
|
+
* comment "No global ready event exists — poll for the app surface the executor
|
|
124
|
+
* needs". Two ordering details make both halves earn their place:
|
|
125
|
+
* `window.PinePaper` is first assigned the CLASS at module load (no `create` —
|
|
126
|
+
* create() is an instance method) and only later the instance, and the instance
|
|
127
|
+
* assigns itself to the global from INSIDE its own constructor.
|
|
128
|
+
*
|
|
129
|
+
* Every candidate global is tested, not just the first truthy one: picking
|
|
130
|
+
* `window.app` because it exists and then failing it would trade a false ready
|
|
131
|
+
* for a false not-ready, which costs the whole timeout.
|
|
132
|
+
*/
|
|
133
|
+
export declare function pinePaperIsReady(): boolean;
|
|
65
134
|
/**
|
|
66
135
|
* Controls PinePaper Studio via Puppeteer.
|
|
67
136
|
*
|
|
@@ -78,6 +147,13 @@ export declare class PinePaperBrowserController {
|
|
|
78
147
|
private isAgentMode;
|
|
79
148
|
private connectingPromise;
|
|
80
149
|
constructor(config?: BrowserControllerConfig);
|
|
150
|
+
/**
|
|
151
|
+
* Chrome flags shared by both launch paths, so a proxy configured once does
|
|
152
|
+
* not go missing from whichever path the caller happens to take.
|
|
153
|
+
*/
|
|
154
|
+
private launchArgs;
|
|
155
|
+
/** Launch options shared by both paths — see MIN_PROTOCOL_TIMEOUT_MS. */
|
|
156
|
+
private launchOptions;
|
|
81
157
|
/**
|
|
82
158
|
* Get URL with agent mode parameter
|
|
83
159
|
*/
|
|
@@ -102,6 +178,14 @@ export declare class PinePaperBrowserController {
|
|
|
102
178
|
* Whether the browser is running in headless mode
|
|
103
179
|
*/
|
|
104
180
|
get isHeadless(): boolean;
|
|
181
|
+
/**
|
|
182
|
+
* The resolved connection settings. Read-only, and a copy: these are what the
|
|
183
|
+
* env vars and the caller's config actually worked out to, which is otherwise
|
|
184
|
+
* only observable by launching a browser.
|
|
185
|
+
*/
|
|
186
|
+
get connectionSettings(): Readonly<Required<BrowserControllerConfig>>;
|
|
187
|
+
/** The Chrome flags this controller would launch with. Exposed for tests. */
|
|
188
|
+
chromeArgs(): string[];
|
|
105
189
|
/**
|
|
106
190
|
* Launch browser and navigate to PinePaper Studio.
|
|
107
191
|
* When agentMode is enabled (default), this automatically uses connectAgent().
|
|
@@ -134,7 +218,7 @@ export declare class PinePaperBrowserController {
|
|
|
134
218
|
/**
|
|
135
219
|
* Execute JavaScript code in PinePaper Studio
|
|
136
220
|
*/
|
|
137
|
-
executeCode(code: string, takeScreenshot?: boolean): Promise<ExecuteResult>;
|
|
221
|
+
executeCode(code: string, takeScreenshot?: boolean, options?: ExecuteCodeOptions): Promise<ExecuteResult>;
|
|
138
222
|
/**
|
|
139
223
|
* Take a screenshot of the current canvas
|
|
140
224
|
*/
|
|
@@ -180,7 +264,7 @@ export declare class PinePaperBrowserController {
|
|
|
180
264
|
/**
|
|
181
265
|
* Execute code with a timeout
|
|
182
266
|
*/
|
|
183
|
-
executeWithTimeout(code: string, timeoutMs: number, takeScreenshot?: boolean): Promise<ExecuteResult>;
|
|
267
|
+
executeWithTimeout(code: string, timeoutMs: number, takeScreenshot?: boolean, options?: ExecuteCodeOptions): Promise<ExecuteResult>;
|
|
184
268
|
/**
|
|
185
269
|
* Quick reset: Clear canvas without full page refresh
|
|
186
270
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"puppeteer-controller.d.ts","sourceRoot":"","sources":["../../src/browser/puppeteer-controller.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,WAAW,uBAAuB;IACtC,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6CAA6C;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6CAA6C;IAC7C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,SAAS,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"puppeteer-controller.d.ts","sourceRoot":"","sources":["../../src/browser/puppeteer-controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,41 @@
|
|
|
4
4
|
* Controls a real browser instance running PinePaper Studio.
|
|
5
5
|
* Executes code directly via page.evaluate() and provides screenshots.
|
|
6
6
|
*/
|
|
7
|
+
export const PAGE_WAIT_UNTIL = ['load', 'domcontentloaded', 'networkidle0', 'networkidle2'];
|
|
8
|
+
/**
|
|
9
|
+
* Is PinePaper Studio actually usable? Evaluated IN THE PAGE, so it closes over
|
|
10
|
+
* nothing.
|
|
11
|
+
*
|
|
12
|
+
* The old test was `app !== undefined || pinepaper !== undefined || paper !==
|
|
13
|
+
* undefined`, hand-copied into four places. `window.paper` is Paper.js, which
|
|
14
|
+
* attaches the moment the canvas script loads — long before Studio has built
|
|
15
|
+
* its API — so all four sites could report ready against a page that would
|
|
16
|
+
* throw on the first call.
|
|
17
|
+
*
|
|
18
|
+
* The test is FxTool's own, mirrored rather than invented: js/app.js polls
|
|
19
|
+
* `app && app.itemRegistry && typeof app.create === 'function'` under the
|
|
20
|
+
* comment "No global ready event exists — poll for the app surface the executor
|
|
21
|
+
* needs". Two ordering details make both halves earn their place:
|
|
22
|
+
* `window.PinePaper` is first assigned the CLASS at module load (no `create` —
|
|
23
|
+
* create() is an instance method) and only later the instance, and the instance
|
|
24
|
+
* assigns itself to the global from INSIDE its own constructor.
|
|
25
|
+
*
|
|
26
|
+
* Every candidate global is tested, not just the first truthy one: picking
|
|
27
|
+
* `window.app` because it exists and then failing it would trade a false ready
|
|
28
|
+
* for a false not-ready, which costs the whole timeout.
|
|
29
|
+
*/
|
|
30
|
+
export function pinePaperIsReady() {
|
|
31
|
+
const w = window;
|
|
32
|
+
return [w.app, w.PinePaper, w.pinepaper].some((api) => !!api && typeof api.create === 'function' && !!api.itemRegistry);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Floor for how long CDP waits on a single call. Puppeteer's default is 180s,
|
|
36
|
+
* which is shorter than a long video export's governor budget (handlers.ts,
|
|
37
|
+
* EXPORT_GOVERNOR_DEFAULT_MS — this number, deliberately) — the protocol must
|
|
38
|
+
* not be the thing that kills an export first, or the error blames the wrong
|
|
39
|
+
* layer. PINEPAPER_TIMEOUT raises it past this floor.
|
|
40
|
+
*/
|
|
41
|
+
const MIN_PROTOCOL_TIMEOUT_MS = 300_000;
|
|
7
42
|
// =============================================================================
|
|
8
43
|
// BROWSER CONTROLLER CLASS
|
|
9
44
|
// =============================================================================
|
|
@@ -42,11 +77,48 @@ export class PinePaperBrowserController {
|
|
|
42
77
|
viewportHeight: config.viewportHeight || 800,
|
|
43
78
|
timeout: config.timeout || 30000,
|
|
44
79
|
agentMode: true, // MCP server always uses agent mode
|
|
80
|
+
waitUntil: config.waitUntil || 'domcontentloaded',
|
|
81
|
+
proxy: config.proxy || '',
|
|
82
|
+
governor: config.governor ?? true,
|
|
45
83
|
};
|
|
46
84
|
console.error(`[PinePaper] Controller initialized:`);
|
|
47
85
|
console.error(`[PinePaper] Original URL: ${originalUrl}`);
|
|
48
86
|
console.error(`[PinePaper] Agent URL: ${studioUrl}`);
|
|
49
87
|
console.error(`[PinePaper] Headless: ${this.config.headless}`);
|
|
88
|
+
console.error(`[PinePaper] waitUntil: ${this.config.waitUntil}, timeout: ${this.config.timeout}ms`);
|
|
89
|
+
if (this.config.proxy) {
|
|
90
|
+
console.error(`[PinePaper] Proxy: ${this.config.proxy} — certificate errors ignored for this session`);
|
|
91
|
+
}
|
|
92
|
+
if (!this.config.governor) {
|
|
93
|
+
console.error('[PinePaper] Governor: OFF — raw eval, no report, no seeded determinism');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Chrome flags shared by both launch paths, so a proxy configured once does
|
|
98
|
+
* not go missing from whichever path the caller happens to take.
|
|
99
|
+
*/
|
|
100
|
+
launchArgs(viewportWidth, viewportHeight, extra = []) {
|
|
101
|
+
const args = [
|
|
102
|
+
'--no-sandbox',
|
|
103
|
+
'--disable-setuid-sandbox',
|
|
104
|
+
`--window-size=${viewportWidth},${viewportHeight}`,
|
|
105
|
+
...extra,
|
|
106
|
+
];
|
|
107
|
+
// Strictly gated on an explicitly configured proxy. Relaxing certificate
|
|
108
|
+
// checking for every run would be a silent downgrade; here it is the
|
|
109
|
+
// consequence of a flag the operator set for their own interception proxy.
|
|
110
|
+
if (this.config.proxy) {
|
|
111
|
+
args.push(`--proxy-server=${this.config.proxy}`, '--ignore-certificate-errors');
|
|
112
|
+
}
|
|
113
|
+
return args;
|
|
114
|
+
}
|
|
115
|
+
/** Launch options shared by both paths — see MIN_PROTOCOL_TIMEOUT_MS. */
|
|
116
|
+
launchOptions(headless, args) {
|
|
117
|
+
return {
|
|
118
|
+
headless,
|
|
119
|
+
args,
|
|
120
|
+
protocolTimeout: Math.max(this.config.timeout, MIN_PROTOCOL_TIMEOUT_MS),
|
|
121
|
+
};
|
|
50
122
|
}
|
|
51
123
|
/**
|
|
52
124
|
* Get URL with agent mode parameter
|
|
@@ -87,6 +159,18 @@ export class PinePaperBrowserController {
|
|
|
87
159
|
get isHeadless() {
|
|
88
160
|
return this.config.headless;
|
|
89
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* The resolved connection settings. Read-only, and a copy: these are what the
|
|
164
|
+
* env vars and the caller's config actually worked out to, which is otherwise
|
|
165
|
+
* only observable by launching a browser.
|
|
166
|
+
*/
|
|
167
|
+
get connectionSettings() {
|
|
168
|
+
return { ...this.config };
|
|
169
|
+
}
|
|
170
|
+
/** The Chrome flags this controller would launch with. Exposed for tests. */
|
|
171
|
+
chromeArgs() {
|
|
172
|
+
return this.launchArgs(this.config.viewportWidth, this.config.viewportHeight);
|
|
173
|
+
}
|
|
90
174
|
/**
|
|
91
175
|
* Launch browser and navigate to PinePaper Studio.
|
|
92
176
|
* When agentMode is enabled (default), this automatically uses connectAgent().
|
|
@@ -144,14 +228,7 @@ export class PinePaperBrowserController {
|
|
|
144
228
|
throw new Error('The browser tools need puppeteer, which is an optional install: run `npm i puppeteer` (or `npm i -g puppeteer` for a global CLI) and retry. It is kept out of the default dependency tree for supply-chain hygiene.');
|
|
145
229
|
}
|
|
146
230
|
console.error('[PinePaper] Launching browser...');
|
|
147
|
-
this.browser = await puppeteer.default.launch(
|
|
148
|
-
headless: this.config.headless,
|
|
149
|
-
args: [
|
|
150
|
-
'--no-sandbox',
|
|
151
|
-
'--disable-setuid-sandbox',
|
|
152
|
-
`--window-size=${this.config.viewportWidth},${this.config.viewportHeight}`,
|
|
153
|
-
],
|
|
154
|
-
});
|
|
231
|
+
this.browser = await puppeteer.default.launch(this.launchOptions(this.config.headless, this.launchArgs(this.config.viewportWidth, this.config.viewportHeight)));
|
|
155
232
|
// Get existing pages first, reuse if available (avoid creating multiple tabs)
|
|
156
233
|
const pages = await this.browser.pages();
|
|
157
234
|
if (pages.length > 0) {
|
|
@@ -170,17 +247,10 @@ export class PinePaperBrowserController {
|
|
|
170
247
|
});
|
|
171
248
|
console.error(`[PinePaper] Navigating to ${this.config.studioUrl}...`);
|
|
172
249
|
await this.page.goto(this.config.studioUrl, {
|
|
173
|
-
waitUntil:
|
|
250
|
+
waitUntil: this.config.waitUntil,
|
|
174
251
|
timeout: this.config.timeout,
|
|
175
252
|
});
|
|
176
|
-
|
|
177
|
-
console.error('[PinePaper] Waiting for PinePaper Studio to initialize...');
|
|
178
|
-
await this.page.waitForFunction(() => {
|
|
179
|
-
// Check if PinePaper's global app or API is available
|
|
180
|
-
return (typeof window.app !== 'undefined' ||
|
|
181
|
-
typeof window.pinepaper !== 'undefined' ||
|
|
182
|
-
typeof window.paper !== 'undefined');
|
|
183
|
-
}, { timeout: this.config.timeout });
|
|
253
|
+
await this.waitForPinePaper();
|
|
184
254
|
this.isConnected = true;
|
|
185
255
|
console.error('[PinePaper] Connected to PinePaper Studio');
|
|
186
256
|
}
|
|
@@ -272,7 +342,7 @@ export class PinePaperBrowserController {
|
|
|
272
342
|
/**
|
|
273
343
|
* Execute JavaScript code in PinePaper Studio
|
|
274
344
|
*/
|
|
275
|
-
async executeCode(code, takeScreenshot = true) {
|
|
345
|
+
async executeCode(code, takeScreenshot = true, options = {}) {
|
|
276
346
|
if (!this.page || !this.isConnected) {
|
|
277
347
|
return {
|
|
278
348
|
success: false,
|
|
@@ -285,11 +355,17 @@ export class PinePaperBrowserController {
|
|
|
285
355
|
// budgets, bulk-create perf, and a machine-readable report. It returns the
|
|
286
356
|
// code's trailing-expression value as result.value (FxTool captures it).
|
|
287
357
|
// Fall back to raw eval on older builds that lack runGenerated.
|
|
288
|
-
const
|
|
358
|
+
const runOptions = {
|
|
359
|
+
bypass: options.bypassGovernor ?? !this.config.governor,
|
|
360
|
+
timeoutMs: options.governorTimeoutMs,
|
|
361
|
+
};
|
|
362
|
+
const result = await this.page.evaluate(async (codeToRun, opts) => {
|
|
289
363
|
const app = window.app || window.PinePaper;
|
|
290
|
-
if (app && typeof app.runGenerated === 'function') {
|
|
364
|
+
if (!opts.bypass && app && typeof app.runGenerated === 'function') {
|
|
291
365
|
try {
|
|
292
|
-
|
|
366
|
+
// timeoutMs is runGenerated's own option (RUN_DEFAULTS.timeoutMs,
|
|
367
|
+
// 10s) — passing undefined leaves the default in place.
|
|
368
|
+
const run = await app.runGenerated(codeToRun, { source: 'agent', timeoutMs: opts.timeoutMs });
|
|
293
369
|
if (run && run.ok === false) {
|
|
294
370
|
return {
|
|
295
371
|
success: false,
|
|
@@ -305,7 +381,9 @@ export class PinePaperBrowserController {
|
|
|
305
381
|
return { success: false, error: e instanceof Error ? e.message : 'Execution error', executedVia: 'governed' };
|
|
306
382
|
}
|
|
307
383
|
}
|
|
308
|
-
// Fallback: raw eval
|
|
384
|
+
// Fallback: raw eval — a pre-governor FxTool build, or an explicit
|
|
385
|
+
// bypass. Both generated-code shapes are `(`-led expression statements,
|
|
386
|
+
// so eval returns the same trailing value runGenerated captures.
|
|
309
387
|
try {
|
|
310
388
|
// eslint-disable-next-line no-eval
|
|
311
389
|
const evalResult = eval(codeToRun);
|
|
@@ -321,7 +399,7 @@ export class PinePaperBrowserController {
|
|
|
321
399
|
executedVia: 'eval',
|
|
322
400
|
};
|
|
323
401
|
}
|
|
324
|
-
}, code);
|
|
402
|
+
}, code, runOptions);
|
|
325
403
|
// Take screenshot if requested
|
|
326
404
|
let screenshot;
|
|
327
405
|
if (takeScreenshot && result.success) {
|
|
@@ -339,7 +417,10 @@ export class PinePaperBrowserController {
|
|
|
339
417
|
// the canvas. Re-bind to the live page and retry ONCE instead.
|
|
340
418
|
if (PinePaperBrowserController.isStaleFrameError(error) && await this.reacquirePage()) {
|
|
341
419
|
try {
|
|
342
|
-
|
|
420
|
+
// Forward `options`: a recovered export that silently reverted to the
|
|
421
|
+
// 10s governor budget would die at the same place the retry exists to
|
|
422
|
+
// get past.
|
|
423
|
+
const retry = await this.executeCode(code, takeScreenshot, options);
|
|
343
424
|
// The page reloaded under us, so the scene may be gone even though
|
|
344
425
|
// execution now succeeds. Say so explicitly — silently continuing
|
|
345
426
|
// against vanished item ids is how this bug wasted a whole rebuild.
|
|
@@ -407,7 +488,7 @@ export class PinePaperBrowserController {
|
|
|
407
488
|
throw new Error('Not connected to browser');
|
|
408
489
|
}
|
|
409
490
|
await this.page.goto(url, {
|
|
410
|
-
waitUntil:
|
|
491
|
+
waitUntil: this.config.waitUntil,
|
|
411
492
|
timeout: this.config.timeout,
|
|
412
493
|
});
|
|
413
494
|
}
|
|
@@ -419,7 +500,7 @@ export class PinePaperBrowserController {
|
|
|
419
500
|
throw new Error('Not connected to browser');
|
|
420
501
|
}
|
|
421
502
|
await this.page.reload({
|
|
422
|
-
waitUntil:
|
|
503
|
+
waitUntil: this.config.waitUntil,
|
|
423
504
|
timeout: this.config.timeout,
|
|
424
505
|
});
|
|
425
506
|
}
|
|
@@ -433,16 +514,11 @@ export class PinePaperBrowserController {
|
|
|
433
514
|
}
|
|
434
515
|
console.error('[PinePaper] Refreshing page...');
|
|
435
516
|
await this.page.reload({
|
|
436
|
-
waitUntil:
|
|
517
|
+
waitUntil: this.config.waitUntil,
|
|
437
518
|
timeout: this.config.timeout,
|
|
438
519
|
});
|
|
439
520
|
// Wait for PinePaper to be ready again
|
|
440
|
-
|
|
441
|
-
await this.page.waitForFunction(() => {
|
|
442
|
-
return (typeof window.app !== 'undefined' ||
|
|
443
|
-
typeof window.pinepaper !== 'undefined' ||
|
|
444
|
-
typeof window.paper !== 'undefined');
|
|
445
|
-
}, { timeout: this.config.timeout });
|
|
521
|
+
await this.waitForPinePaper();
|
|
446
522
|
console.error('[PinePaper] Page refreshed and ready');
|
|
447
523
|
}
|
|
448
524
|
/**
|
|
@@ -453,11 +529,7 @@ export class PinePaperBrowserController {
|
|
|
453
529
|
return false;
|
|
454
530
|
}
|
|
455
531
|
try {
|
|
456
|
-
return await this.page.evaluate(
|
|
457
|
-
return (typeof window.app !== 'undefined' ||
|
|
458
|
-
typeof window.pinepaper !== 'undefined' ||
|
|
459
|
-
typeof window.paper !== 'undefined');
|
|
460
|
-
});
|
|
532
|
+
return await this.page.evaluate(pinePaperIsReady);
|
|
461
533
|
}
|
|
462
534
|
catch {
|
|
463
535
|
return false;
|
|
@@ -481,7 +553,7 @@ export class PinePaperBrowserController {
|
|
|
481
553
|
const agentUrl = this.getAgentUrl(this.config.studioUrl);
|
|
482
554
|
console.error(`[PinePaper] Switching to agent mode: ${agentUrl}`);
|
|
483
555
|
await this.page.goto(agentUrl, {
|
|
484
|
-
waitUntil:
|
|
556
|
+
waitUntil: this.config.waitUntil,
|
|
485
557
|
timeout: this.config.timeout,
|
|
486
558
|
});
|
|
487
559
|
await this.waitForPinePaper();
|
|
@@ -524,18 +596,12 @@ export class PinePaperBrowserController {
|
|
|
524
596
|
const viewportWidth = options.viewportWidth ?? this.config.viewportWidth;
|
|
525
597
|
const viewportHeight = options.viewportHeight ?? this.config.viewportHeight;
|
|
526
598
|
console.error(`[PinePaper] Launching browser in agent mode (headless: ${useHeadless})...`);
|
|
527
|
-
this.browser = await puppeteer.default.launch(
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
// Additional optimization flags for agent mode
|
|
534
|
-
'--disable-gpu',
|
|
535
|
-
'--disable-dev-shm-usage',
|
|
536
|
-
'--disable-extensions',
|
|
537
|
-
],
|
|
538
|
-
});
|
|
599
|
+
this.browser = await puppeteer.default.launch(this.launchOptions(useHeadless, this.launchArgs(viewportWidth, viewportHeight, [
|
|
600
|
+
// Additional optimization flags for agent mode
|
|
601
|
+
'--disable-gpu',
|
|
602
|
+
'--disable-dev-shm-usage',
|
|
603
|
+
'--disable-extensions',
|
|
604
|
+
])));
|
|
539
605
|
// Get existing pages first, reuse if available (avoid creating multiple tabs)
|
|
540
606
|
const pages = await this.browser.pages();
|
|
541
607
|
// Use the first page (about:blank) and close ALL others
|
|
@@ -565,7 +631,7 @@ export class PinePaperBrowserController {
|
|
|
565
631
|
console.error(`[PinePaper] Navigating to agent URL: ${targetUrl}`);
|
|
566
632
|
// Navigate the existing page to PinePaper (converts about:blank to PinePaper)
|
|
567
633
|
await this.page.goto(targetUrl, {
|
|
568
|
-
waitUntil:
|
|
634
|
+
waitUntil: this.config.waitUntil,
|
|
569
635
|
timeout: this.config.timeout,
|
|
570
636
|
});
|
|
571
637
|
// Wait for PinePaper to be ready
|
|
@@ -586,11 +652,7 @@ export class PinePaperBrowserController {
|
|
|
586
652
|
if (!this.page)
|
|
587
653
|
return;
|
|
588
654
|
console.error('[PinePaper] Waiting for PinePaper Studio to initialize...');
|
|
589
|
-
await this.page.waitForFunction(
|
|
590
|
-
return (typeof window.app !== 'undefined' ||
|
|
591
|
-
typeof window.pinepaper !== 'undefined' ||
|
|
592
|
-
typeof window.paper !== 'undefined');
|
|
593
|
-
}, { timeout: this.config.timeout });
|
|
655
|
+
await this.page.waitForFunction(pinePaperIsReady, { timeout: this.config.timeout });
|
|
594
656
|
}
|
|
595
657
|
/**
|
|
596
658
|
* Execute multiple code blocks as a batch for optimal performance.
|
|
@@ -695,11 +757,16 @@ ${codes.map((code, i) => `
|
|
|
695
757
|
/**
|
|
696
758
|
* Execute code with a timeout
|
|
697
759
|
*/
|
|
698
|
-
async executeWithTimeout(code, timeoutMs, takeScreenshot = false) {
|
|
760
|
+
async executeWithTimeout(code, timeoutMs, takeScreenshot = false, options = {}) {
|
|
699
761
|
const timeoutPromise = new Promise((_, reject) => {
|
|
700
762
|
setTimeout(() => reject(new Error('Execution timeout')), timeoutMs);
|
|
701
763
|
});
|
|
702
|
-
|
|
764
|
+
// The caller's outer deadline is the governor's too unless they said
|
|
765
|
+
// otherwise — a governor that fires first would report the wrong cause.
|
|
766
|
+
const executePromise = this.executeCode(code, takeScreenshot, {
|
|
767
|
+
governorTimeoutMs: timeoutMs,
|
|
768
|
+
...options,
|
|
769
|
+
});
|
|
703
770
|
try {
|
|
704
771
|
return await Promise.race([executePromise, timeoutPromise]);
|
|
705
772
|
}
|
|
@@ -775,12 +842,28 @@ let globalController = null;
|
|
|
775
842
|
*/
|
|
776
843
|
export function getBrowserController(config) {
|
|
777
844
|
if (!globalController) {
|
|
778
|
-
//
|
|
845
|
+
// Env resolution lives here, not in the controller: explicit config wins,
|
|
846
|
+
// then the env var, then the default — the shape PINEPAPER_HEADLESS and
|
|
847
|
+
// PINEPAPER_STUDIO_URL already use.
|
|
779
848
|
const envHeadless = process.env.PINEPAPER_HEADLESS;
|
|
780
849
|
const headless = config?.headless ?? (envHeadless !== undefined ? envHeadless !== 'false' : true);
|
|
850
|
+
const envWaitUntil = process.env.PINEPAPER_WAIT_UNTIL?.trim();
|
|
851
|
+
if (envWaitUntil && !PAGE_WAIT_UNTIL.includes(envWaitUntil)) {
|
|
852
|
+
// Loudly, because a typo here silently reinstates the hang it was set to
|
|
853
|
+
// avoid — and a 30s connect failure looks nothing like a bad env var.
|
|
854
|
+
console.error(`[PinePaper] PINEPAPER_WAIT_UNTIL="${envWaitUntil}" is not one of ${PAGE_WAIT_UNTIL.join(', ')} — ignoring it.`);
|
|
855
|
+
}
|
|
856
|
+
const envTimeout = Number(process.env.PINEPAPER_TIMEOUT);
|
|
857
|
+
if (process.env.PINEPAPER_TIMEOUT && !Number.isFinite(envTimeout)) {
|
|
858
|
+
console.error(`[PinePaper] PINEPAPER_TIMEOUT="${process.env.PINEPAPER_TIMEOUT}" is not a number — ignoring it.`);
|
|
859
|
+
}
|
|
781
860
|
const mcpConfig = {
|
|
782
861
|
...config,
|
|
783
862
|
headless,
|
|
863
|
+
waitUntil: config?.waitUntil ?? (envWaitUntil && PAGE_WAIT_UNTIL.includes(envWaitUntil) ? envWaitUntil : undefined),
|
|
864
|
+
timeout: config?.timeout ?? (Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : undefined),
|
|
865
|
+
proxy: config?.proxy ?? process.env.PINEPAPER_PROXY?.trim(),
|
|
866
|
+
governor: config?.governor ?? (process.env.PINEPAPER_GOVERNOR?.trim().toLowerCase() !== 'off'),
|
|
784
867
|
};
|
|
785
868
|
globalController = new PinePaperBrowserController(mcpConfig);
|
|
786
869
|
}
|