@pinepaper.studio/mcp-server 1.4.2 → 1.5.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.
Files changed (199) hide show
  1. package/README.md +213 -18
  2. package/dist/__tests__/fixtures/mock-generators.d.ts +147 -0
  3. package/dist/__tests__/fixtures/mock-generators.d.ts.map +1 -1
  4. package/dist/__tests__/fixtures/mock-generators.js +85 -0
  5. package/dist/__tests__/fixtures/mock-generators.js.map +1 -1
  6. package/dist/agent/export-manager.d.ts +77 -0
  7. package/dist/agent/export-manager.d.ts.map +1 -0
  8. package/dist/agent/export-manager.js +455 -0
  9. package/dist/agent/export-manager.js.map +1 -0
  10. package/dist/agent/index.d.ts +9 -0
  11. package/dist/agent/index.d.ts.map +1 -0
  12. package/dist/agent/index.js +12 -0
  13. package/dist/agent/index.js.map +1 -0
  14. package/dist/agent/session-manager.d.ts +109 -0
  15. package/dist/agent/session-manager.d.ts.map +1 -0
  16. package/dist/agent/session-manager.js +311 -0
  17. package/dist/agent/session-manager.js.map +1 -0
  18. package/dist/agent/types.d.ts +385 -0
  19. package/dist/agent/types.d.ts.map +1 -0
  20. package/dist/agent/types.js +141 -0
  21. package/dist/agent/types.js.map +1 -0
  22. package/dist/browser/index.d.ts +1 -1
  23. package/dist/browser/index.d.ts.map +1 -1
  24. package/dist/browser/index.js +1 -1
  25. package/dist/browser/index.js.map +1 -1
  26. package/dist/browser/puppeteer-controller.d.ts +89 -5
  27. package/dist/browser/puppeteer-controller.d.ts.map +1 -1
  28. package/dist/browser/puppeteer-controller.js +412 -11
  29. package/dist/browser/puppeteer-controller.js.map +1 -1
  30. package/dist/cli.js +3 -2
  31. package/dist/cli.js.map +1 -1
  32. package/dist/i18n/locales/ar.d.ts.map +1 -1
  33. package/dist/i18n/locales/ar.js +37 -0
  34. package/dist/i18n/locales/ar.js.map +1 -1
  35. package/dist/i18n/locales/base.d.ts.map +1 -1
  36. package/dist/i18n/locales/base.js +49 -0
  37. package/dist/i18n/locales/base.js.map +1 -1
  38. package/dist/i18n/locales/bn.d.ts.map +1 -1
  39. package/dist/i18n/locales/bn.js +10 -0
  40. package/dist/i18n/locales/bn.js.map +1 -1
  41. package/dist/i18n/locales/cs.d.ts.map +1 -1
  42. package/dist/i18n/locales/cs.js +10 -0
  43. package/dist/i18n/locales/cs.js.map +1 -1
  44. package/dist/i18n/locales/da.d.ts.map +1 -1
  45. package/dist/i18n/locales/da.js +10 -0
  46. package/dist/i18n/locales/da.js.map +1 -1
  47. package/dist/i18n/locales/de.d.ts.map +1 -1
  48. package/dist/i18n/locales/de.js +107 -0
  49. package/dist/i18n/locales/de.js.map +1 -1
  50. package/dist/i18n/locales/el.d.ts.map +1 -1
  51. package/dist/i18n/locales/el.js +10 -0
  52. package/dist/i18n/locales/el.js.map +1 -1
  53. package/dist/i18n/locales/en.d.ts.map +1 -1
  54. package/dist/i18n/locales/en.js +162 -0
  55. package/dist/i18n/locales/en.js.map +1 -1
  56. package/dist/i18n/locales/es.d.ts.map +1 -1
  57. package/dist/i18n/locales/es.js +106 -0
  58. package/dist/i18n/locales/es.js.map +1 -1
  59. package/dist/i18n/locales/fa.d.ts.map +1 -1
  60. package/dist/i18n/locales/fa.js +10 -0
  61. package/dist/i18n/locales/fa.js.map +1 -1
  62. package/dist/i18n/locales/fi.d.ts.map +1 -1
  63. package/dist/i18n/locales/fi.js +10 -0
  64. package/dist/i18n/locales/fi.js.map +1 -1
  65. package/dist/i18n/locales/fil.d.ts.map +1 -1
  66. package/dist/i18n/locales/fil.js +10 -0
  67. package/dist/i18n/locales/fil.js.map +1 -1
  68. package/dist/i18n/locales/fr.d.ts.map +1 -1
  69. package/dist/i18n/locales/fr.js +37 -0
  70. package/dist/i18n/locales/fr.js.map +1 -1
  71. package/dist/i18n/locales/gu.d.ts.map +1 -1
  72. package/dist/i18n/locales/gu.js +10 -0
  73. package/dist/i18n/locales/gu.js.map +1 -1
  74. package/dist/i18n/locales/he.d.ts.map +1 -1
  75. package/dist/i18n/locales/he.js +10 -0
  76. package/dist/i18n/locales/he.js.map +1 -1
  77. package/dist/i18n/locales/hi.d.ts.map +1 -1
  78. package/dist/i18n/locales/hi.js +37 -0
  79. package/dist/i18n/locales/hi.js.map +1 -1
  80. package/dist/i18n/locales/hu.d.ts.map +1 -1
  81. package/dist/i18n/locales/hu.js +10 -0
  82. package/dist/i18n/locales/hu.js.map +1 -1
  83. package/dist/i18n/locales/id.d.ts.map +1 -1
  84. package/dist/i18n/locales/id.js +108 -0
  85. package/dist/i18n/locales/id.js.map +1 -1
  86. package/dist/i18n/locales/is.d.ts.map +1 -1
  87. package/dist/i18n/locales/is.js +10 -0
  88. package/dist/i18n/locales/is.js.map +1 -1
  89. package/dist/i18n/locales/it.d.ts.map +1 -1
  90. package/dist/i18n/locales/it.js +108 -0
  91. package/dist/i18n/locales/it.js.map +1 -1
  92. package/dist/i18n/locales/ja.d.ts.map +1 -1
  93. package/dist/i18n/locales/ja.js +108 -0
  94. package/dist/i18n/locales/ja.js.map +1 -1
  95. package/dist/i18n/locales/kn.d.ts.map +1 -1
  96. package/dist/i18n/locales/kn.js +10 -0
  97. package/dist/i18n/locales/kn.js.map +1 -1
  98. package/dist/i18n/locales/ko.d.ts.map +1 -1
  99. package/dist/i18n/locales/ko.js +37 -0
  100. package/dist/i18n/locales/ko.js.map +1 -1
  101. package/dist/i18n/locales/ml.d.ts.map +1 -1
  102. package/dist/i18n/locales/ml.js +10 -0
  103. package/dist/i18n/locales/ml.js.map +1 -1
  104. package/dist/i18n/locales/mr.d.ts.map +1 -1
  105. package/dist/i18n/locales/mr.js +10 -0
  106. package/dist/i18n/locales/mr.js.map +1 -1
  107. package/dist/i18n/locales/ms.d.ts.map +1 -1
  108. package/dist/i18n/locales/ms.js +10 -0
  109. package/dist/i18n/locales/ms.js.map +1 -1
  110. package/dist/i18n/locales/nl.d.ts.map +1 -1
  111. package/dist/i18n/locales/nl.js +108 -0
  112. package/dist/i18n/locales/nl.js.map +1 -1
  113. package/dist/i18n/locales/no.d.ts.map +1 -1
  114. package/dist/i18n/locales/no.js +10 -0
  115. package/dist/i18n/locales/no.js.map +1 -1
  116. package/dist/i18n/locales/pa.d.ts.map +1 -1
  117. package/dist/i18n/locales/pa.js +10 -0
  118. package/dist/i18n/locales/pa.js.map +1 -1
  119. package/dist/i18n/locales/pl.d.ts.map +1 -1
  120. package/dist/i18n/locales/pl.js +108 -0
  121. package/dist/i18n/locales/pl.js.map +1 -1
  122. package/dist/i18n/locales/pt-BR.d.ts.map +1 -1
  123. package/dist/i18n/locales/pt-BR.js +108 -0
  124. package/dist/i18n/locales/pt-BR.js.map +1 -1
  125. package/dist/i18n/locales/pt.d.ts.map +1 -1
  126. package/dist/i18n/locales/pt.js +10 -0
  127. package/dist/i18n/locales/pt.js.map +1 -1
  128. package/dist/i18n/locales/ro.d.ts.map +1 -1
  129. package/dist/i18n/locales/ro.js +10 -0
  130. package/dist/i18n/locales/ro.js.map +1 -1
  131. package/dist/i18n/locales/ru.d.ts.map +1 -1
  132. package/dist/i18n/locales/ru.js +108 -0
  133. package/dist/i18n/locales/ru.js.map +1 -1
  134. package/dist/i18n/locales/sv.d.ts.map +1 -1
  135. package/dist/i18n/locales/sv.js +10 -0
  136. package/dist/i18n/locales/sv.js.map +1 -1
  137. package/dist/i18n/locales/ta.d.ts.map +1 -1
  138. package/dist/i18n/locales/ta.js +10 -0
  139. package/dist/i18n/locales/ta.js.map +1 -1
  140. package/dist/i18n/locales/te.d.ts.map +1 -1
  141. package/dist/i18n/locales/te.js +10 -0
  142. package/dist/i18n/locales/te.js.map +1 -1
  143. package/dist/i18n/locales/th.d.ts.map +1 -1
  144. package/dist/i18n/locales/th.js +108 -0
  145. package/dist/i18n/locales/th.js.map +1 -1
  146. package/dist/i18n/locales/tl.d.ts.map +1 -1
  147. package/dist/i18n/locales/tl.js +10 -0
  148. package/dist/i18n/locales/tl.js.map +1 -1
  149. package/dist/i18n/locales/tr.d.ts.map +1 -1
  150. package/dist/i18n/locales/tr.js +108 -0
  151. package/dist/i18n/locales/tr.js.map +1 -1
  152. package/dist/i18n/locales/uk.d.ts.map +1 -1
  153. package/dist/i18n/locales/uk.js +108 -0
  154. package/dist/i18n/locales/uk.js.map +1 -1
  155. package/dist/i18n/locales/ur.d.ts.map +1 -1
  156. package/dist/i18n/locales/ur.js +10 -0
  157. package/dist/i18n/locales/ur.js.map +1 -1
  158. package/dist/i18n/locales/vi.d.ts.map +1 -1
  159. package/dist/i18n/locales/vi.js +108 -0
  160. package/dist/i18n/locales/vi.js.map +1 -1
  161. package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
  162. package/dist/i18n/locales/zh-CN.js +108 -0
  163. package/dist/i18n/locales/zh-CN.js.map +1 -1
  164. package/dist/i18n/locales/zh-TW.d.ts.map +1 -1
  165. package/dist/i18n/locales/zh-TW.js +108 -0
  166. package/dist/i18n/locales/zh-TW.js.map +1 -1
  167. package/dist/index.d.ts +3 -0
  168. package/dist/index.d.ts.map +1 -1
  169. package/dist/index.js +2717 -147
  170. package/dist/index.js.map +1 -1
  171. package/dist/prompts/index.d.ts +39 -0
  172. package/dist/prompts/index.d.ts.map +1 -0
  173. package/dist/prompts/index.js +254 -0
  174. package/dist/prompts/index.js.map +1 -0
  175. package/dist/tools/compact-descriptions.d.ts +9 -0
  176. package/dist/tools/compact-descriptions.d.ts.map +1 -0
  177. package/dist/tools/compact-descriptions.js +260 -0
  178. package/dist/tools/compact-descriptions.js.map +1 -0
  179. package/dist/tools/definitions.d.ts +12 -0
  180. package/dist/tools/definitions.d.ts.map +1 -1
  181. package/dist/tools/definitions.js +4921 -957
  182. package/dist/tools/definitions.js.map +1 -1
  183. package/dist/tools/handlers.d.ts.map +1 -1
  184. package/dist/tools/handlers.js +985 -17
  185. package/dist/tools/handlers.js.map +1 -1
  186. package/dist/tools/index.d.ts +2 -1
  187. package/dist/tools/index.d.ts.map +1 -1
  188. package/dist/tools/index.js +1 -1
  189. package/dist/tools/index.js.map +1 -1
  190. package/dist/types/code-generator.d.ts +233 -1
  191. package/dist/types/code-generator.d.ts.map +1 -1
  192. package/dist/types/code-generator.js +2578 -26
  193. package/dist/types/code-generator.js.map +1 -1
  194. package/dist/types/schemas.d.ts +5438 -805
  195. package/dist/types/schemas.d.ts.map +1 -1
  196. package/dist/types/schemas.js +1135 -4
  197. package/dist/types/schemas.js.map +1 -1
  198. package/manifest.json +106 -2
  199. package/package.json +2 -2
@@ -15,6 +15,32 @@ export interface BrowserControllerConfig {
15
15
  viewportHeight?: number;
16
16
  /** Timeout for page operations in ms (default: 30000) */
17
17
  timeout?: number;
18
+ /** Enable agent mode with ?agent=1 URL parameter */
19
+ agentMode?: boolean;
20
+ }
21
+ export interface AgentConnectOptions {
22
+ /** Run browser in headless mode (default: true for agent workflows) */
23
+ headless?: boolean;
24
+ /** Canvas preset to apply (optional) */
25
+ canvasPreset?: string;
26
+ /** Custom viewport width */
27
+ viewportWidth?: number;
28
+ /** Custom viewport height */
29
+ viewportHeight?: number;
30
+ }
31
+ export interface BatchExecuteResult {
32
+ success: boolean;
33
+ results: Array<{
34
+ index: number;
35
+ success: boolean;
36
+ result?: unknown;
37
+ itemId?: string;
38
+ error?: string;
39
+ }>;
40
+ itemIds: string[];
41
+ executionTime: number;
42
+ error?: string;
43
+ screenshot?: string;
18
44
  }
19
45
  export interface ExecuteResult {
20
46
  success: boolean;
@@ -35,17 +61,32 @@ export declare class PinePaperBrowserController {
35
61
  private page;
36
62
  private config;
37
63
  private isConnected;
64
+ private isAgentMode;
65
+ private connectingPromise;
38
66
  constructor(config?: BrowserControllerConfig);
67
+ /**
68
+ * Get URL with agent mode parameter
69
+ */
70
+ private getAgentUrl;
71
+ /**
72
+ * Check if in agent mode
73
+ */
74
+ get agentMode(): boolean;
39
75
  /**
40
76
  * Check if connected to browser
41
77
  */
42
78
  get connected(): boolean;
43
79
  /**
44
- * Get current studio URL
80
+ * Get current studio URL (always includes agent parameters for MCP server)
45
81
  */
46
82
  get studioUrl(): string;
47
83
  /**
48
- * Launch browser and navigate to PinePaper Studio
84
+ * Get the actual URL being used (same as studioUrl since MCP always uses agent mode)
85
+ */
86
+ get actualUrl(): string;
87
+ /**
88
+ * Launch browser and navigate to PinePaper Studio.
89
+ * When agentMode is enabled (default), this automatically uses connectAgent().
49
90
  */
50
91
  connect(): Promise<void>;
51
92
  /**
@@ -81,13 +122,56 @@ export declare class PinePaperBrowserController {
81
122
  * Check if PinePaper API is available
82
123
  */
83
124
  checkPinePaperReady(): Promise<boolean>;
125
+ /**
126
+ * Connect in agent mode with optimized settings for automation.
127
+ * Uses headless mode by default and adds ?agent=1 URL parameter.
128
+ */
129
+ connectAgent(options?: AgentConnectOptions): Promise<void>;
130
+ /**
131
+ * Wait for PinePaper API to be available
132
+ */
133
+ private waitForPinePaper;
134
+ /**
135
+ * Execute multiple code blocks as a batch for optimal performance.
136
+ * Combines all code into a single execution context.
137
+ */
138
+ executeBatch(codes: string[], options?: {
139
+ takeScreenshot?: boolean;
140
+ atomic?: boolean;
141
+ }): Promise<BatchExecuteResult>;
142
+ /**
143
+ * Execute code with a timeout
144
+ */
145
+ executeWithTimeout(code: string, timeoutMs: number, takeScreenshot?: boolean): Promise<ExecuteResult>;
146
+ /**
147
+ * Quick reset: Clear canvas without full page refresh
148
+ */
149
+ quickReset(options?: {
150
+ preserveBackground?: boolean;
151
+ }): Promise<ExecuteResult>;
152
+ /**
153
+ * Set canvas size from platform preset
154
+ */
155
+ setCanvasPreset(preset: string): Promise<ExecuteResult>;
84
156
  }
85
157
  /**
86
- * Get or create the global browser controller instance
158
+ * Get or create the global browser controller instance.
159
+ *
160
+ * IMPORTANT: This is a singleton - only ONE browser controller exists.
161
+ * If a controller already exists, the config parameter is ignored.
162
+ * Call resetBrowserController() first if you need to change config.
163
+ *
164
+ * NOTE: MCP server ALWAYS uses agent mode with ?agent=1&mode=agent URL parameters.
165
+ * This uses headless browser by default for optimal performance.
87
166
  */
88
167
  export declare function getBrowserController(config?: BrowserControllerConfig): PinePaperBrowserController;
89
168
  /**
90
- * Reset the global controller (for testing)
169
+ * Reset the global controller (for testing or when config needs to change).
170
+ * This will close any existing browser and clear the singleton.
171
+ */
172
+ export declare function resetBrowserController(): Promise<void>;
173
+ /**
174
+ * Synchronous reset for testing (fire and forget)
91
175
  */
92
- export declare function resetBrowserController(): void;
176
+ export declare function resetBrowserControllerSync(): void;
93
177
  //# sourceMappingURL=puppeteer-controller.d.ts.map
@@ -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,8EAA8E;IAC9E,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;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD;;;;;;;GAOG;AACH,qBAAa,0BAA0B;IACrC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,MAAM,CAAoC;IAClD,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,GAAE,uBAA4B;IAgBhD;;OAEG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwD9B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAcjC;;OAEG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,UAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IA6C9E;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IA6BnD;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAIlD;;OAEG;IACG,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5C;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAU7B;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BlC;;OAEG;IACG,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC;CAiB9C;AAQD;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,CAAC,EAAE,uBAAuB,GAC/B,0BAA0B,CAK5B;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAK7C"}
1
+ {"version":3,"file":"puppeteer-controller.d.ts","sourceRoot":"","sources":["../../src/browser/puppeteer-controller.ts"],"names":[],"mappings":"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"}
@@ -20,19 +20,48 @@ export class PinePaperBrowserController {
20
20
  page = null;
21
21
  config;
22
22
  isConnected = false;
23
+ isAgentMode = false;
24
+ connectingPromise = null;
23
25
  constructor(config = {}) {
24
26
  // Ensure URL points to /editor where the code console and API are available
25
27
  let studioUrl = config.studioUrl || 'https://pinepaper.studio';
28
+ const originalUrl = studioUrl;
26
29
  if (!studioUrl.includes('/editor')) {
27
30
  studioUrl = studioUrl.replace(/\/$/, '') + '/editor';
28
31
  }
32
+ // MCP server ALWAYS uses agent mode - add agent parameter to URL
33
+ // This is critical for proper PinePaper agent integration
34
+ const url = new URL(studioUrl);
35
+ url.searchParams.set('agent', '1');
36
+ url.searchParams.set('mode', 'agent');
37
+ studioUrl = url.toString();
29
38
  this.config = {
30
39
  studioUrl,
31
- headless: config.headless ?? false, // Default to visible browser
40
+ headless: config.headless ?? false, // Default to visible browser so user can see PinePaper Studio
32
41
  viewportWidth: config.viewportWidth || 1280,
33
42
  viewportHeight: config.viewportHeight || 800,
34
43
  timeout: config.timeout || 30000,
44
+ agentMode: true, // MCP server always uses agent mode
35
45
  };
46
+ console.error(`[PinePaper] Controller initialized:`);
47
+ console.error(`[PinePaper] Original URL: ${originalUrl}`);
48
+ console.error(`[PinePaper] Agent URL: ${studioUrl}`);
49
+ console.error(`[PinePaper] Headless: ${this.config.headless}`);
50
+ }
51
+ /**
52
+ * Get URL with agent mode parameter
53
+ */
54
+ getAgentUrl(baseUrl) {
55
+ const url = new URL(baseUrl);
56
+ url.searchParams.set('agent', '1');
57
+ url.searchParams.set('mode', 'agent');
58
+ return url.toString();
59
+ }
60
+ /**
61
+ * Check if in agent mode
62
+ */
63
+ get agentMode() {
64
+ return this.isAgentMode;
36
65
  }
37
66
  /**
38
67
  * Check if connected to browser
@@ -41,20 +70,62 @@ export class PinePaperBrowserController {
41
70
  return this.isConnected && this.page !== null;
42
71
  }
43
72
  /**
44
- * Get current studio URL
73
+ * Get current studio URL (always includes agent parameters for MCP server)
45
74
  */
46
75
  get studioUrl() {
47
76
  return this.config.studioUrl;
48
77
  }
49
78
  /**
50
- * Launch browser and navigate to PinePaper Studio
79
+ * Get the actual URL being used (same as studioUrl since MCP always uses agent mode)
80
+ */
81
+ get actualUrl() {
82
+ return this.config.studioUrl;
83
+ }
84
+ /**
85
+ * Launch browser and navigate to PinePaper Studio.
86
+ * When agentMode is enabled (default), this automatically uses connectAgent().
51
87
  */
52
88
  async connect() {
53
- if (this.isConnected) {
54
- console.error('[PinePaper] Already connected');
55
- return;
89
+ // Prevent concurrent connection attempts (race condition when multiple tools fire at once)
90
+ if (this.connectingPromise) {
91
+ console.error('[PinePaper] Connection already in progress, waiting...');
92
+ return this.connectingPromise;
93
+ }
94
+ console.error(`[PinePaper] connect() called - agentMode=${this.config.agentMode}, headless=${this.config.headless}`);
95
+ // If agent mode is configured, delegate to connectAgent()
96
+ if (this.config.agentMode) {
97
+ console.error('[PinePaper] Delegating to connectAgent()');
98
+ this.connectingPromise = this.connectAgent({ headless: this.config.headless })
99
+ .finally(() => { this.connectingPromise = null; });
100
+ return this.connectingPromise;
101
+ }
102
+ console.error('[PinePaper] Using standard connect (non-agent mode)');
103
+ // Check if already connected and page is still valid
104
+ if (this.isConnected && this.page) {
105
+ try {
106
+ // Verify the page is still responsive
107
+ await this.page.evaluate(() => true);
108
+ console.error('[PinePaper] Already connected');
109
+ return;
110
+ }
111
+ catch {
112
+ // Page is stale, need to reconnect
113
+ console.error('[PinePaper] Existing connection stale, reconnecting...');
114
+ this.isConnected = false;
115
+ }
56
116
  }
57
117
  try {
118
+ // Clean up any existing browser instance first to prevent multiple windows
119
+ if (this.browser) {
120
+ try {
121
+ await this.browser.close();
122
+ }
123
+ catch {
124
+ // Ignore close errors
125
+ }
126
+ this.browser = null;
127
+ this.page = null;
128
+ }
58
129
  // Dynamic import to avoid issues when puppeteer isn't installed
59
130
  const puppeteer = await import('puppeteer');
60
131
  console.error('[PinePaper] Launching browser...');
@@ -66,7 +137,18 @@ export class PinePaperBrowserController {
66
137
  `--window-size=${this.config.viewportWidth},${this.config.viewportHeight}`,
67
138
  ],
68
139
  });
69
- this.page = await this.browser.newPage();
140
+ // Get existing pages first, reuse if available (avoid creating multiple tabs)
141
+ const pages = await this.browser.pages();
142
+ if (pages.length > 0) {
143
+ this.page = pages[0];
144
+ // Close any extra pages that might have been created
145
+ for (let i = 1; i < pages.length; i++) {
146
+ await pages[i].close();
147
+ }
148
+ }
149
+ else {
150
+ this.page = await this.browser.newPage();
151
+ }
70
152
  await this.page.setViewport({
71
153
  width: this.config.viewportWidth,
72
154
  height: this.config.viewportHeight,
@@ -107,6 +189,7 @@ export class PinePaperBrowserController {
107
189
  this.browser = null;
108
190
  this.page = null;
109
191
  this.isConnected = false;
192
+ this.connectingPromise = null;
110
193
  console.error('[PinePaper] Disconnected from browser');
111
194
  }
112
195
  /**
@@ -254,24 +337,342 @@ export class PinePaperBrowserController {
254
337
  return false;
255
338
  }
256
339
  }
340
+ // =============================================================================
341
+ // AGENT MODE METHODS
342
+ // =============================================================================
343
+ /**
344
+ * Connect in agent mode with optimized settings for automation.
345
+ * Uses headless mode by default and adds ?agent=1 URL parameter.
346
+ */
347
+ async connectAgent(options = {}) {
348
+ // Check if already connected and page is still valid
349
+ if (this.isConnected && this.page) {
350
+ try {
351
+ // Verify the page is still responsive
352
+ await this.page.evaluate(() => true);
353
+ // If already connected but not in agent mode, navigate to agent URL
354
+ if (!this.isAgentMode) {
355
+ const agentUrl = this.getAgentUrl(this.config.studioUrl);
356
+ console.error(`[PinePaper] Switching to agent mode: ${agentUrl}`);
357
+ await this.page.goto(agentUrl, {
358
+ waitUntil: 'networkidle2',
359
+ timeout: this.config.timeout,
360
+ });
361
+ await this.waitForPinePaper();
362
+ this.isAgentMode = true;
363
+ }
364
+ console.error('[PinePaper] Already connected in agent mode');
365
+ return;
366
+ }
367
+ catch {
368
+ // Page is stale, need to reconnect
369
+ console.error('[PinePaper] Existing connection stale, reconnecting...');
370
+ this.isConnected = false;
371
+ }
372
+ }
373
+ try {
374
+ // Clean up any existing browser instance first to prevent multiple windows
375
+ if (this.browser) {
376
+ try {
377
+ await this.browser.close();
378
+ }
379
+ catch {
380
+ // Ignore close errors
381
+ }
382
+ this.browser = null;
383
+ this.page = null;
384
+ }
385
+ // Dynamic import to avoid issues when puppeteer isn't installed
386
+ const puppeteer = await import('puppeteer');
387
+ // Agent mode defaults to visible browser so user can see PinePaper Studio
388
+ const useHeadless = options.headless ?? this.config.headless;
389
+ const viewportWidth = options.viewportWidth ?? this.config.viewportWidth;
390
+ const viewportHeight = options.viewportHeight ?? this.config.viewportHeight;
391
+ console.error(`[PinePaper] Launching browser in agent mode (headless: ${useHeadless})...`);
392
+ this.browser = await puppeteer.default.launch({
393
+ headless: useHeadless,
394
+ args: [
395
+ '--no-sandbox',
396
+ '--disable-setuid-sandbox',
397
+ `--window-size=${viewportWidth},${viewportHeight}`,
398
+ // Additional optimization flags for agent mode
399
+ '--disable-gpu',
400
+ '--disable-dev-shm-usage',
401
+ '--disable-extensions',
402
+ ],
403
+ });
404
+ // Get existing pages first, reuse if available (avoid creating multiple tabs)
405
+ const pages = await this.browser.pages();
406
+ // Use the first page (about:blank) and close ALL others
407
+ if (pages.length > 0) {
408
+ this.page = pages[0];
409
+ // Close ALL extra pages
410
+ for (let i = 1; i < pages.length; i++) {
411
+ try {
412
+ await pages[i].close();
413
+ }
414
+ catch {
415
+ // Ignore close errors
416
+ }
417
+ }
418
+ }
419
+ else {
420
+ this.page = await this.browser.newPage();
421
+ }
422
+ await this.page.setViewport({
423
+ width: viewportWidth,
424
+ height: viewportHeight,
425
+ });
426
+ // Ensure URL has agent parameters (double-check)
427
+ const targetUrl = this.config.studioUrl.includes('agent=')
428
+ ? this.config.studioUrl
429
+ : this.getAgentUrl(this.config.studioUrl);
430
+ console.error(`[PinePaper] Navigating to agent URL: ${targetUrl}`);
431
+ // Navigate the existing page to PinePaper (converts about:blank to PinePaper)
432
+ await this.page.goto(targetUrl, {
433
+ waitUntil: 'networkidle2',
434
+ timeout: this.config.timeout,
435
+ });
436
+ // Wait for PinePaper to be ready
437
+ await this.waitForPinePaper();
438
+ this.isConnected = true;
439
+ this.isAgentMode = true;
440
+ console.error('[PinePaper] Connected to PinePaper Studio in agent mode');
441
+ }
442
+ catch (error) {
443
+ await this.disconnect();
444
+ throw new Error(`Failed to connect in agent mode: ${error instanceof Error ? error.message : 'Unknown error'}`);
445
+ }
446
+ }
447
+ /**
448
+ * Wait for PinePaper API to be available
449
+ */
450
+ async waitForPinePaper() {
451
+ if (!this.page)
452
+ return;
453
+ console.error('[PinePaper] Waiting for PinePaper Studio to initialize...');
454
+ await this.page.waitForFunction(() => {
455
+ return (typeof window.app !== 'undefined' ||
456
+ typeof window.pinepaper !== 'undefined' ||
457
+ typeof window.paper !== 'undefined');
458
+ }, { timeout: this.config.timeout });
459
+ }
460
+ /**
461
+ * Execute multiple code blocks as a batch for optimal performance.
462
+ * Combines all code into a single execution context.
463
+ */
464
+ async executeBatch(codes, options = {}) {
465
+ const startTime = Date.now();
466
+ if (!this.page || !this.isConnected) {
467
+ return {
468
+ success: false,
469
+ results: [],
470
+ itemIds: [],
471
+ executionTime: 0,
472
+ error: 'Not connected to PinePaper Studio',
473
+ };
474
+ }
475
+ const atomic = options.atomic ?? true;
476
+ // Wrap all codes in a single IIFE for batch execution
477
+ const batchCode = `
478
+ (async function() {
479
+ const _results = [];
480
+ const _itemIds = [];
481
+
482
+ try {
483
+ ${codes.map((code, i) => `
484
+ // === Operation ${i + 1} ===
485
+ try {
486
+ const _result${i} = await (async () => {
487
+ ${code}
488
+ })();
489
+ _results.push({ index: ${i}, success: true, result: _result${i} });
490
+ if (_result${i} && typeof _result${i} === 'object') {
491
+ if (_result${i}.itemId) _itemIds.push(_result${i}.itemId);
492
+ if (_result${i}.registryId) _itemIds.push(_result${i}.registryId);
493
+ }
494
+ } catch (e) {
495
+ _results.push({ index: ${i}, success: false, error: e.message });
496
+ ${atomic ? 'throw e; // Atomic mode: stop on first error' : '// Continue despite error'}
497
+ }
498
+ `).join('\n')}
499
+
500
+ // Save history once for all operations
501
+ if (app.historyManager) {
502
+ app.historyManager.saveState();
503
+ }
504
+
505
+ return { success: true, itemIds: _itemIds, results: _results };
506
+ } catch (e) {
507
+ return {
508
+ success: false,
509
+ error: e.message,
510
+ itemIds: _itemIds,
511
+ results: _results
512
+ };
513
+ }
514
+ })();
515
+ `.trim();
516
+ try {
517
+ const result = await this.page.evaluate((codeToRun) => {
518
+ try {
519
+ // eslint-disable-next-line no-eval
520
+ const evalResult = eval(codeToRun);
521
+ if (evalResult instanceof Promise) {
522
+ return evalResult;
523
+ }
524
+ return evalResult;
525
+ }
526
+ catch (e) {
527
+ return {
528
+ success: false,
529
+ error: e instanceof Error ? e.message : 'Batch execution error',
530
+ results: [],
531
+ itemIds: [],
532
+ };
533
+ }
534
+ }, batchCode);
535
+ const executionTime = Date.now() - startTime;
536
+ // Take screenshot if requested
537
+ let screenshot;
538
+ if (options.takeScreenshot && result.success) {
539
+ screenshot = await this.takeScreenshot();
540
+ }
541
+ return {
542
+ success: result.success ?? false,
543
+ results: result.results ?? [],
544
+ itemIds: result.itemIds ?? [],
545
+ executionTime,
546
+ error: result.error,
547
+ screenshot,
548
+ };
549
+ }
550
+ catch (error) {
551
+ return {
552
+ success: false,
553
+ results: [],
554
+ itemIds: [],
555
+ executionTime: Date.now() - startTime,
556
+ error: error instanceof Error ? error.message : 'Unknown batch error',
557
+ };
558
+ }
559
+ }
560
+ /**
561
+ * Execute code with a timeout
562
+ */
563
+ async executeWithTimeout(code, timeoutMs, takeScreenshot = false) {
564
+ const timeoutPromise = new Promise((_, reject) => {
565
+ setTimeout(() => reject(new Error('Execution timeout')), timeoutMs);
566
+ });
567
+ const executePromise = this.executeCode(code, takeScreenshot);
568
+ try {
569
+ return await Promise.race([executePromise, timeoutPromise]);
570
+ }
571
+ catch (error) {
572
+ return {
573
+ success: false,
574
+ error: error instanceof Error ? error.message : 'Timeout error',
575
+ };
576
+ }
577
+ }
578
+ /**
579
+ * Quick reset: Clear canvas without full page refresh
580
+ */
581
+ async quickReset(options = {}) {
582
+ const resetCode = `
583
+ (function() {
584
+ // Clear all items
585
+ if (app.clearCanvas) {
586
+ app.clearCanvas();
587
+ } else {
588
+ if (app.textItemGroup) app.textItemGroup.removeChildren();
589
+ if (app.patternGroup) app.patternGroup.removeChildren();
590
+ if (app.itemRegistry) app.itemRegistry.clear();
591
+ if (app.relationRegistry) app.relationRegistry.clear();
592
+ }
593
+
594
+ ${!options.preserveBackground ? `
595
+ // Reset background
596
+ if (app.setBackgroundColor) {
597
+ app.setBackgroundColor('#1a1a2e');
598
+ }
599
+ ` : ''}
600
+
601
+ // Save state
602
+ if (app.historyManager) {
603
+ app.historyManager.saveState();
604
+ }
605
+
606
+ return { success: true, reset: true };
607
+ })();
608
+ `.trim();
609
+ return this.executeCode(resetCode, false);
610
+ }
611
+ /**
612
+ * Set canvas size from platform preset
613
+ */
614
+ async setCanvasPreset(preset) {
615
+ const code = `
616
+ (function() {
617
+ if (app.setCanvasSize) {
618
+ app.setCanvasSize('${preset}');
619
+ return { success: true, preset: '${preset}' };
620
+ }
621
+ return { success: false, error: 'setCanvasSize not available' };
622
+ })();
623
+ `.trim();
624
+ return this.executeCode(code, false);
625
+ }
257
626
  }
258
627
  // =============================================================================
259
628
  // SINGLETON INSTANCE
260
629
  // =============================================================================
261
630
  let globalController = null;
262
631
  /**
263
- * Get or create the global browser controller instance
632
+ * Get or create the global browser controller instance.
633
+ *
634
+ * IMPORTANT: This is a singleton - only ONE browser controller exists.
635
+ * If a controller already exists, the config parameter is ignored.
636
+ * Call resetBrowserController() first if you need to change config.
637
+ *
638
+ * NOTE: MCP server ALWAYS uses agent mode with ?agent=1&mode=agent URL parameters.
639
+ * This uses headless browser by default for optimal performance.
264
640
  */
265
641
  export function getBrowserController(config) {
266
642
  if (!globalController) {
267
- globalController = new PinePaperBrowserController(config);
643
+ // MCP server always uses headless by default
644
+ const mcpConfig = {
645
+ ...config,
646
+ headless: config?.headless ?? true,
647
+ };
648
+ globalController = new PinePaperBrowserController(mcpConfig);
649
+ }
650
+ else if (config) {
651
+ // Log that config is being ignored since controller already exists
652
+ console.error('[PinePaper] Browser controller already exists, config parameter ignored. Call resetBrowserController() to create a new controller with different config.');
268
653
  }
269
654
  return globalController;
270
655
  }
271
656
  /**
272
- * Reset the global controller (for testing)
657
+ * Reset the global controller (for testing or when config needs to change).
658
+ * This will close any existing browser and clear the singleton.
659
+ */
660
+ export async function resetBrowserController() {
661
+ if (globalController) {
662
+ try {
663
+ await globalController.disconnect();
664
+ }
665
+ catch {
666
+ // Ignore disconnect errors during reset
667
+ }
668
+ globalController = null;
669
+ console.error('[PinePaper] Browser controller reset');
670
+ }
671
+ }
672
+ /**
673
+ * Synchronous reset for testing (fire and forget)
273
674
  */
274
- export function resetBrowserController() {
675
+ export function resetBrowserControllerSync() {
275
676
  if (globalController) {
276
677
  globalController.disconnect().catch(() => { });
277
678
  globalController = null;