@midscene/playground 1.2.2-beta-20260115092052.0 → 1.2.2-beta-20260115120150.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.
@@ -1,3 +1,4 @@
1
+ import { GroupedActionDump } from "@midscene/core";
1
2
  import { overrideAIConfig } from "@midscene/shared/env";
2
3
  import { uuid } from "@midscene/shared/utils";
3
4
  import { executeAction, parseStructuredParams } from "../common.mjs";
@@ -112,7 +113,7 @@ class LocalExecutionAdapter extends BasePlaygroundAdapter {
112
113
  if (agent.dumpDataString) {
113
114
  const dumpString = agent.dumpDataString();
114
115
  if (dumpString) {
115
- const groupedDump = JSON.parse(dumpString);
116
+ const groupedDump = GroupedActionDump.fromSerializedString(dumpString);
116
117
  response.dump = groupedDump.executions?.[0] || null;
117
118
  }
118
119
  }
@@ -151,7 +152,7 @@ class LocalExecutionAdapter extends BasePlaygroundAdapter {
151
152
  if ('function' == typeof this.agent.dumpDataString) {
152
153
  const dumpString = this.agent.dumpDataString();
153
154
  if (dumpString) {
154
- const groupedDump = JSON.parse(dumpString);
155
+ const groupedDump = GroupedActionDump.fromSerializedString(dumpString);
155
156
  dump = groupedDump.executions?.[0] ?? null;
156
157
  }
157
158
  }
@@ -193,7 +194,7 @@ class LocalExecutionAdapter extends BasePlaygroundAdapter {
193
194
  if (this.agent?.dumpDataString) {
194
195
  const dumpString = this.agent.dumpDataString();
195
196
  if (dumpString) {
196
- const groupedDump = JSON.parse(dumpString);
197
+ const groupedDump = GroupedActionDump.fromSerializedString(dumpString);
197
198
  response.dump = groupedDump.executions?.[0] || null;
198
199
  }
199
200
  }
@@ -1 +1 @@
1
- {"version":3,"file":"adapters/local-execution.mjs","sources":["../../../src/adapters/local-execution.ts"],"sourcesContent":["import type { DeviceAction, ExecutionDump } from '@midscene/core';\nimport { overrideAIConfig } from '@midscene/shared/env';\nimport { uuid } from '@midscene/shared/utils';\nimport { executeAction, parseStructuredParams } from '../common';\nimport type {\n AgentFactory,\n ExecutionOptions,\n FormValue,\n PlaygroundAgent,\n} from '../types';\nimport { BasePlaygroundAdapter } from './base';\n\nexport class LocalExecutionAdapter extends BasePlaygroundAdapter {\n private agent: PlaygroundAgent | null;\n private agentFactory?: AgentFactory; // Factory function for recreating agent\n private dumpUpdateCallback?: (\n dump: string,\n executionDump?: ExecutionDump,\n ) => void;\n private progressCallback?: (tip: string) => void;\n private readonly _id: string; // Unique identifier for this local adapter instance\n private currentRequestId?: string; // Track current request to prevent stale callbacks\n\n constructor(agent?: PlaygroundAgent, agentFactory?: AgentFactory) {\n super();\n this.agent = agent ?? null;\n this.agentFactory = agentFactory;\n this._id = uuid(); // Generate unique ID for local adapter\n }\n\n // Get adapter ID\n get id(): string {\n return this._id;\n }\n\n onDumpUpdate(\n callback: (dump: string, executionDump?: ExecutionDump) => void,\n ): void {\n // Clear any existing callback before setting new one\n this.dumpUpdateCallback = undefined;\n // Set the new callback\n this.dumpUpdateCallback = callback;\n }\n\n // Set progress callback for monitoring operation status\n setProgressCallback(callback: (tip: string) => void): void {\n this.progressCallback = undefined;\n this.progressCallback = callback;\n }\n\n async parseStructuredParams(\n action: DeviceAction<unknown>,\n params: Record<string, unknown>,\n options: ExecutionOptions,\n ): Promise<unknown[]> {\n // Use shared implementation from common.ts\n return await parseStructuredParams(action, params, options);\n }\n\n formatErrorMessage(error: any): string {\n const errorMessage = error?.message || '';\n if (errorMessage.includes('of different extension')) {\n return 'Conflicting extension detected. Please disable the suspicious plugins and refresh the page. Guide: https://midscenejs.com/quick-experience.html#faq';\n }\n return this.formatBasicErrorMessage(error);\n }\n\n // Local execution - use base implementation\n // (inherits default executeAction from BasePlaygroundAdapter)\n\n // Local execution gets actionSpace from internal agent (parameter is for backward compatibility)\n async getActionSpace(context?: unknown): Promise<DeviceAction<unknown>[]> {\n // If agent doesn't exist but we have a factory, create one temporarily to get actionSpace\n if (!this.agent && this.agentFactory) {\n try {\n this.agent = await this.agentFactory();\n } catch (error) {\n console.warn('Failed to create agent for actionSpace:', error);\n return [];\n }\n }\n\n // Priority 1: Use agent's getActionSpace method\n if (this.agent?.getActionSpace) {\n return await this.agent.getActionSpace();\n }\n\n // Priority 2: Use agent's interface.actionSpace method\n if (\n this.agent &&\n 'interface' in this.agent &&\n typeof this.agent.interface === 'object'\n ) {\n const page = this.agent.interface as {\n actionSpace?: () => DeviceAction<unknown>[];\n };\n if (page?.actionSpace) {\n return page.actionSpace();\n }\n }\n\n // Priority 3: Fallback to context parameter (for backward compatibility with tests)\n if (context && typeof context === 'object' && 'actionSpace' in context) {\n const contextPage = context as {\n actionSpace: () => DeviceAction<unknown>[];\n };\n return contextPage.actionSpace();\n }\n\n return [];\n }\n\n // Local execution doesn't use a server, so always return true\n async checkStatus(): Promise<boolean> {\n return true;\n }\n\n async overrideConfig(aiConfig: Record<string, unknown>): Promise<void> {\n // For local execution, use the shared env override function\n overrideAIConfig(aiConfig);\n console.log('Config updated. Agent will be recreated on next execution.');\n }\n\n /**\n * Safely detaches the Chrome debugger without destroying the agent.\n * This removes the \"Debugger attached\" banner from the browser window\n * while keeping the agent instance intact for potential reuse.\n * Called on errors to improve user experience by cleaning up the UI.\n */\n private async detachDebuggerSafely() {\n try {\n const page = this.agent?.interface as\n | { detachDebugger?: () => Promise<void> }\n | undefined;\n await page?.detachDebugger?.();\n } catch (error) {\n console.warn('Failed to detach debugger:', error);\n }\n }\n\n async executeAction(\n actionType: string,\n value: FormValue,\n options: ExecutionOptions,\n ): Promise<unknown> {\n // If agentFactory is provided, always recreate agent with latest config before execution\n if (this.agentFactory) {\n if (this.agent) {\n console.log('Destroying old agent before execution...');\n try {\n await this.agent.destroy?.();\n } catch (error) {\n console.warn('Failed to destroy old agent:', error);\n }\n this.agent = null;\n }\n\n // Create new agent with latest config\n await this.recreateAgent();\n }\n\n // Agent must exist (either recreated or provided in constructor)\n if (!this.agent) {\n throw new Error(\n 'No agent available. Please provide either an agent instance or agentFactory.',\n );\n }\n\n const agent = this.agent;\n\n // Get actionSpace using our simplified getActionSpace method\n const actionSpace = await this.getActionSpace();\n let removeListener: (() => void) | undefined;\n\n // Reset dump at the start of execution to ensure clean state\n try {\n agent.resetDump?.();\n } catch (error: unknown) {\n console.warn('Failed to reset dump before execution:', error);\n }\n\n // Setup dump update tracking if requestId is provided\n if (options.requestId) {\n // Track current request ID to prevent stale callbacks\n this.currentRequestId = options.requestId;\n\n // Add listener and save remove function\n removeListener = agent.addDumpUpdateListener(\n (dump: string, executionDump?: ExecutionDump) => {\n // Only process if this is still the current request\n if (this.currentRequestId !== options.requestId) {\n return;\n }\n\n // Forward to external callback\n if (this.dumpUpdateCallback) {\n this.dumpUpdateCallback(dump, executionDump);\n }\n },\n );\n }\n\n try {\n let result = null;\n let executionError = null;\n\n try {\n // Call the base implementation with the original signature\n result = await executeAction(\n agent,\n actionType,\n actionSpace,\n value,\n options,\n );\n } catch (error: unknown) {\n // Capture error but don't throw yet - we need to get dump/reportHTML first\n executionError = error;\n }\n\n // Always construct response with dump and reportHTML, regardless of success/failure\n const response = {\n result,\n dump: null as unknown,\n reportHTML: null as string | null,\n error: executionError\n ? executionError instanceof Error\n ? executionError.message\n : String(executionError)\n : null,\n };\n\n // Get dump data - separate try-catch to ensure dump is retrieved even if reportHTML fails\n try {\n if (agent.dumpDataString) {\n const dumpString = agent.dumpDataString();\n if (dumpString) {\n const groupedDump = JSON.parse(dumpString);\n response.dump = groupedDump.executions?.[0] || null;\n }\n }\n } catch (error: unknown) {\n console.warn('Failed to get dump from agent:', error);\n }\n\n // Try to get reportHTML - may fail in browser environment (fs not available)\n try {\n if (agent.reportHTMLString) {\n response.reportHTML = agent.reportHTMLString() || null;\n }\n } catch (error: unknown) {\n // reportHTMLString may throw in browser environment\n // This is expected in chrome-extension, continue without reportHTML\n }\n\n // Write out action dumps - may also fail in browser environment\n try {\n if (agent.writeOutActionDumps) {\n agent.writeOutActionDumps();\n }\n } catch (error: unknown) {\n // writeOutActionDumps may fail in browser environment\n }\n\n // Don't throw the error - return it in response so caller can access dump/reportHTML\n // The caller (usePlaygroundExecution) will check response.error to determine success\n return response;\n } finally {\n // Remove listener to prevent accumulation\n if (removeListener) {\n removeListener();\n }\n }\n }\n\n /**\n * Recreate agent instance using the factory function.\n * Called automatically when executeAction is called.\n */\n private async recreateAgent(): Promise<void> {\n if (!this.agentFactory) {\n throw new Error(\n 'Cannot recreate agent: factory function not provided. Please provide agentFactory in PlaygroundConfig to enable agent recreation.',\n );\n }\n\n console.log('Creating new agent with latest config...');\n try {\n this.agent = await this.agentFactory();\n console.log('Agent created successfully');\n } catch (error) {\n console.error('Failed to create agent:', error);\n throw error;\n }\n }\n\n // Local execution task cancellation - returns dump and reportHTML before destroying\n async cancelTask(_requestId: string): Promise<{\n error?: string;\n success?: boolean;\n dump?: ExecutionDump | null;\n reportHTML?: string | null;\n }> {\n if (!this.agent) {\n return { error: 'No active agent found for this requestId' };\n }\n\n // Get execution data BEFORE destroying the agent\n let dump: ExecutionDump | null = null;\n let reportHTML: string | null = null;\n\n // Get dump data separately - don't let reportHTML errors affect dump retrieval\n // IMPORTANT: Must extract dump BEFORE agent.destroy(), as dump is stored in agent memory\n try {\n if (typeof this.agent.dumpDataString === 'function') {\n const dumpString = this.agent.dumpDataString();\n if (dumpString) {\n // dumpDataString() returns GroupedActionDump: { executions: ExecutionDump[] }\n // In Playground, each \"Run\" creates one execution, so we take executions[0]\n const groupedDump = JSON.parse(dumpString);\n dump = groupedDump.executions?.[0] ?? null;\n }\n }\n } catch (error) {\n console.warn(\n '[LocalExecutionAdapter] Failed to get dump data before cancel:',\n error,\n );\n }\n\n // Try to get reportHTML separately - this may fail in browser environment\n // where fs.readFileSync is not available\n try {\n if (typeof this.agent.reportHTMLString === 'function') {\n const html = this.agent.reportHTMLString();\n if (\n html &&\n typeof html === 'string' &&\n !html.includes('REPLACE_ME_WITH_REPORT_HTML')\n ) {\n reportHTML = html;\n }\n }\n } catch (error) {\n // reportHTMLString may throw in browser environment (fs not available)\n // This is expected, just continue with dump data only\n console.warn(\n '[LocalExecutionAdapter] reportHTMLString not available in this environment',\n );\n }\n\n try {\n await this.agent.destroy?.();\n this.agent = null; // Clear agent reference\n return { success: true, dump, reportHTML };\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(\n `[LocalExecutionAdapter] Failed to cancel agent: ${errorMessage}`,\n );\n return { error: `Failed to cancel: ${errorMessage}`, dump, reportHTML };\n }\n }\n\n /**\n * Get current execution data without resetting\n * This allows retrieving dump and report when execution is stopped\n */\n async getCurrentExecutionData(): Promise<{\n dump: ExecutionDump | null;\n reportHTML: string | null;\n }> {\n const response = {\n dump: null as ExecutionDump | null,\n reportHTML: null as string | null,\n };\n\n try {\n // Get dump data\n if (this.agent?.dumpDataString) {\n const dumpString = this.agent.dumpDataString();\n if (dumpString) {\n const groupedDump = JSON.parse(dumpString);\n response.dump = groupedDump.executions?.[0] || null;\n }\n }\n\n // Get report HTML\n if (this.agent?.reportHTMLString) {\n response.reportHTML = this.agent.reportHTMLString() || null;\n }\n } catch (error: unknown) {\n console.error('Failed to get current execution data:', error);\n }\n\n return response;\n }\n\n // Get interface information from the agent\n async getInterfaceInfo(): Promise<{\n type: string;\n description?: string;\n } | null> {\n if (!this.agent?.interface) {\n return null;\n }\n\n try {\n const type = this.agent.interface.interfaceType || 'Unknown';\n const description = this.agent.interface.describe?.() || undefined;\n\n return {\n type,\n description,\n };\n } catch (error: unknown) {\n console.error('Failed to get interface info:', error);\n return null;\n }\n }\n}\n"],"names":["LocalExecutionAdapter","BasePlaygroundAdapter","callback","undefined","action","params","options","parseStructuredParams","error","errorMessage","context","console","page","contextPage","aiConfig","overrideAIConfig","actionType","value","Error","agent","actionSpace","removeListener","dump","executionDump","result","executionError","executeAction","response","String","dumpString","groupedDump","JSON","_requestId","reportHTML","html","type","description","agentFactory","uuid"],"mappings":"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"}
1
+ {"version":3,"file":"adapters/local-execution.mjs","sources":["../../../src/adapters/local-execution.ts"],"sourcesContent":["import type { DeviceAction, ExecutionDump } from '@midscene/core';\nimport { GroupedActionDump } from '@midscene/core';\nimport { overrideAIConfig } from '@midscene/shared/env';\nimport { uuid } from '@midscene/shared/utils';\nimport { executeAction, parseStructuredParams } from '../common';\nimport type {\n AgentFactory,\n ExecutionOptions,\n FormValue,\n PlaygroundAgent,\n} from '../types';\nimport { BasePlaygroundAdapter } from './base';\n\nexport class LocalExecutionAdapter extends BasePlaygroundAdapter {\n private agent: PlaygroundAgent | null;\n private agentFactory?: AgentFactory; // Factory function for recreating agent\n private dumpUpdateCallback?: (\n dump: string,\n executionDump?: ExecutionDump,\n ) => void;\n private progressCallback?: (tip: string) => void;\n private readonly _id: string; // Unique identifier for this local adapter instance\n private currentRequestId?: string; // Track current request to prevent stale callbacks\n\n constructor(agent?: PlaygroundAgent, agentFactory?: AgentFactory) {\n super();\n this.agent = agent ?? null;\n this.agentFactory = agentFactory;\n this._id = uuid(); // Generate unique ID for local adapter\n }\n\n // Get adapter ID\n get id(): string {\n return this._id;\n }\n\n onDumpUpdate(\n callback: (dump: string, executionDump?: ExecutionDump) => void,\n ): void {\n // Clear any existing callback before setting new one\n this.dumpUpdateCallback = undefined;\n // Set the new callback\n this.dumpUpdateCallback = callback;\n }\n\n // Set progress callback for monitoring operation status\n setProgressCallback(callback: (tip: string) => void): void {\n this.progressCallback = undefined;\n this.progressCallback = callback;\n }\n\n async parseStructuredParams(\n action: DeviceAction<unknown>,\n params: Record<string, unknown>,\n options: ExecutionOptions,\n ): Promise<unknown[]> {\n // Use shared implementation from common.ts\n return await parseStructuredParams(action, params, options);\n }\n\n formatErrorMessage(error: any): string {\n const errorMessage = error?.message || '';\n if (errorMessage.includes('of different extension')) {\n return 'Conflicting extension detected. Please disable the suspicious plugins and refresh the page. Guide: https://midscenejs.com/quick-experience.html#faq';\n }\n return this.formatBasicErrorMessage(error);\n }\n\n // Local execution - use base implementation\n // (inherits default executeAction from BasePlaygroundAdapter)\n\n // Local execution gets actionSpace from internal agent (parameter is for backward compatibility)\n async getActionSpace(context?: unknown): Promise<DeviceAction<unknown>[]> {\n // If agent doesn't exist but we have a factory, create one temporarily to get actionSpace\n if (!this.agent && this.agentFactory) {\n try {\n this.agent = await this.agentFactory();\n } catch (error) {\n console.warn('Failed to create agent for actionSpace:', error);\n return [];\n }\n }\n\n // Priority 1: Use agent's getActionSpace method\n if (this.agent?.getActionSpace) {\n return await this.agent.getActionSpace();\n }\n\n // Priority 2: Use agent's interface.actionSpace method\n if (\n this.agent &&\n 'interface' in this.agent &&\n typeof this.agent.interface === 'object'\n ) {\n const page = this.agent.interface as {\n actionSpace?: () => DeviceAction<unknown>[];\n };\n if (page?.actionSpace) {\n return page.actionSpace();\n }\n }\n\n // Priority 3: Fallback to context parameter (for backward compatibility with tests)\n if (context && typeof context === 'object' && 'actionSpace' in context) {\n const contextPage = context as {\n actionSpace: () => DeviceAction<unknown>[];\n };\n return contextPage.actionSpace();\n }\n\n return [];\n }\n\n // Local execution doesn't use a server, so always return true\n async checkStatus(): Promise<boolean> {\n return true;\n }\n\n async overrideConfig(aiConfig: Record<string, unknown>): Promise<void> {\n // For local execution, use the shared env override function\n overrideAIConfig(aiConfig);\n console.log('Config updated. Agent will be recreated on next execution.');\n }\n\n /**\n * Safely detaches the Chrome debugger without destroying the agent.\n * This removes the \"Debugger attached\" banner from the browser window\n * while keeping the agent instance intact for potential reuse.\n * Called on errors to improve user experience by cleaning up the UI.\n */\n private async detachDebuggerSafely() {\n try {\n const page = this.agent?.interface as\n | { detachDebugger?: () => Promise<void> }\n | undefined;\n await page?.detachDebugger?.();\n } catch (error) {\n console.warn('Failed to detach debugger:', error);\n }\n }\n\n async executeAction(\n actionType: string,\n value: FormValue,\n options: ExecutionOptions,\n ): Promise<unknown> {\n // If agentFactory is provided, always recreate agent with latest config before execution\n if (this.agentFactory) {\n if (this.agent) {\n console.log('Destroying old agent before execution...');\n try {\n await this.agent.destroy?.();\n } catch (error) {\n console.warn('Failed to destroy old agent:', error);\n }\n this.agent = null;\n }\n\n // Create new agent with latest config\n await this.recreateAgent();\n }\n\n // Agent must exist (either recreated or provided in constructor)\n if (!this.agent) {\n throw new Error(\n 'No agent available. Please provide either an agent instance or agentFactory.',\n );\n }\n\n const agent = this.agent;\n\n // Get actionSpace using our simplified getActionSpace method\n const actionSpace = await this.getActionSpace();\n let removeListener: (() => void) | undefined;\n\n // Reset dump at the start of execution to ensure clean state\n try {\n agent.resetDump?.();\n } catch (error: unknown) {\n console.warn('Failed to reset dump before execution:', error);\n }\n\n // Setup dump update tracking if requestId is provided\n if (options.requestId) {\n // Track current request ID to prevent stale callbacks\n this.currentRequestId = options.requestId;\n\n // Add listener and save remove function\n removeListener = agent.addDumpUpdateListener(\n (dump: string, executionDump?: ExecutionDump) => {\n // Only process if this is still the current request\n if (this.currentRequestId !== options.requestId) {\n return;\n }\n\n // Forward to external callback\n if (this.dumpUpdateCallback) {\n this.dumpUpdateCallback(dump, executionDump);\n }\n },\n );\n }\n\n try {\n let result = null;\n let executionError = null;\n\n try {\n // Call the base implementation with the original signature\n result = await executeAction(\n agent,\n actionType,\n actionSpace,\n value,\n options,\n );\n } catch (error: unknown) {\n // Capture error but don't throw yet - we need to get dump/reportHTML first\n executionError = error;\n }\n\n // Always construct response with dump and reportHTML, regardless of success/failure\n const response = {\n result,\n dump: null as unknown,\n reportHTML: null as string | null,\n error: executionError\n ? executionError instanceof Error\n ? executionError.message\n : String(executionError)\n : null,\n };\n\n // Get dump data - separate try-catch to ensure dump is retrieved even if reportHTML fails\n try {\n if (agent.dumpDataString) {\n const dumpString = agent.dumpDataString();\n if (dumpString) {\n const groupedDump =\n GroupedActionDump.fromSerializedString(dumpString);\n response.dump = groupedDump.executions?.[0] || null;\n }\n }\n } catch (error: unknown) {\n console.warn('Failed to get dump from agent:', error);\n }\n\n // Try to get reportHTML - may fail in browser environment (fs not available)\n try {\n if (agent.reportHTMLString) {\n response.reportHTML = agent.reportHTMLString() || null;\n }\n } catch (error: unknown) {\n // reportHTMLString may throw in browser environment\n // This is expected in chrome-extension, continue without reportHTML\n }\n\n // Write out action dumps - may also fail in browser environment\n try {\n if (agent.writeOutActionDumps) {\n agent.writeOutActionDumps();\n }\n } catch (error: unknown) {\n // writeOutActionDumps may fail in browser environment\n }\n\n // Don't throw the error - return it in response so caller can access dump/reportHTML\n // The caller (usePlaygroundExecution) will check response.error to determine success\n return response;\n } finally {\n // Remove listener to prevent accumulation\n if (removeListener) {\n removeListener();\n }\n }\n }\n\n /**\n * Recreate agent instance using the factory function.\n * Called automatically when executeAction is called.\n */\n private async recreateAgent(): Promise<void> {\n if (!this.agentFactory) {\n throw new Error(\n 'Cannot recreate agent: factory function not provided. Please provide agentFactory in PlaygroundConfig to enable agent recreation.',\n );\n }\n\n console.log('Creating new agent with latest config...');\n try {\n this.agent = await this.agentFactory();\n console.log('Agent created successfully');\n } catch (error) {\n console.error('Failed to create agent:', error);\n throw error;\n }\n }\n\n // Local execution task cancellation - returns dump and reportHTML before destroying\n async cancelTask(_requestId: string): Promise<{\n error?: string;\n success?: boolean;\n dump?: ExecutionDump | null;\n reportHTML?: string | null;\n }> {\n if (!this.agent) {\n return { error: 'No active agent found for this requestId' };\n }\n\n // Get execution data BEFORE destroying the agent\n let dump: ExecutionDump | null = null;\n let reportHTML: string | null = null;\n\n // Get dump data separately - don't let reportHTML errors affect dump retrieval\n // IMPORTANT: Must extract dump BEFORE agent.destroy(), as dump is stored in agent memory\n try {\n if (typeof this.agent.dumpDataString === 'function') {\n const dumpString = this.agent.dumpDataString();\n if (dumpString) {\n // dumpDataString() returns GroupedActionDump: { executions: ExecutionDump[] }\n // In Playground, each \"Run\" creates one execution, so we take executions[0]\n const groupedDump =\n GroupedActionDump.fromSerializedString(dumpString);\n dump = groupedDump.executions?.[0] ?? null;\n }\n }\n } catch (error) {\n console.warn(\n '[LocalExecutionAdapter] Failed to get dump data before cancel:',\n error,\n );\n }\n\n // Try to get reportHTML separately - this may fail in browser environment\n // where fs.readFileSync is not available\n try {\n if (typeof this.agent.reportHTMLString === 'function') {\n const html = this.agent.reportHTMLString();\n if (\n html &&\n typeof html === 'string' &&\n !html.includes('REPLACE_ME_WITH_REPORT_HTML')\n ) {\n reportHTML = html;\n }\n }\n } catch (error) {\n // reportHTMLString may throw in browser environment (fs not available)\n // This is expected, just continue with dump data only\n console.warn(\n '[LocalExecutionAdapter] reportHTMLString not available in this environment',\n );\n }\n\n try {\n await this.agent.destroy?.();\n this.agent = null; // Clear agent reference\n return { success: true, dump, reportHTML };\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(\n `[LocalExecutionAdapter] Failed to cancel agent: ${errorMessage}`,\n );\n return { error: `Failed to cancel: ${errorMessage}`, dump, reportHTML };\n }\n }\n\n /**\n * Get current execution data without resetting\n * This allows retrieving dump and report when execution is stopped\n */\n async getCurrentExecutionData(): Promise<{\n dump: ExecutionDump | null;\n reportHTML: string | null;\n }> {\n const response = {\n dump: null as ExecutionDump | null,\n reportHTML: null as string | null,\n };\n\n try {\n // Get dump data\n if (this.agent?.dumpDataString) {\n const dumpString = this.agent.dumpDataString();\n if (dumpString) {\n const groupedDump =\n GroupedActionDump.fromSerializedString(dumpString);\n response.dump = groupedDump.executions?.[0] || null;\n }\n }\n\n // Get report HTML\n if (this.agent?.reportHTMLString) {\n response.reportHTML = this.agent.reportHTMLString() || null;\n }\n } catch (error: unknown) {\n console.error('Failed to get current execution data:', error);\n }\n\n return response;\n }\n\n // Get interface information from the agent\n async getInterfaceInfo(): Promise<{\n type: string;\n description?: string;\n } | null> {\n if (!this.agent?.interface) {\n return null;\n }\n\n try {\n const type = this.agent.interface.interfaceType || 'Unknown';\n const description = this.agent.interface.describe?.() || undefined;\n\n return {\n type,\n description,\n };\n } catch (error: unknown) {\n console.error('Failed to get interface info:', error);\n return null;\n }\n }\n}\n"],"names":["LocalExecutionAdapter","BasePlaygroundAdapter","callback","undefined","action","params","options","parseStructuredParams","error","errorMessage","context","console","page","contextPage","aiConfig","overrideAIConfig","actionType","value","Error","agent","actionSpace","removeListener","dump","executionDump","result","executionError","executeAction","response","String","dumpString","groupedDump","GroupedActionDump","_requestId","reportHTML","html","type","description","agentFactory","uuid"],"mappings":";;;;;;;;;;;;;;;AAaO,MAAMA,8BAA8BC;IAmBzC,IAAI,KAAa;QACf,OAAO,IAAI,CAAC,GAAG;IACjB;IAEA,aACEC,QAA+D,EACzD;QAEN,IAAI,CAAC,kBAAkB,GAAGC;QAE1B,IAAI,CAAC,kBAAkB,GAAGD;IAC5B;IAGA,oBAAoBA,QAA+B,EAAQ;QACzD,IAAI,CAAC,gBAAgB,GAAGC;QACxB,IAAI,CAAC,gBAAgB,GAAGD;IAC1B;IAEA,MAAM,sBACJE,MAA6B,EAC7BC,MAA+B,EAC/BC,OAAyB,EACL;QAEpB,OAAO,MAAMC,sBAAsBH,QAAQC,QAAQC;IACrD;IAEA,mBAAmBE,KAAU,EAAU;QACrC,MAAMC,eAAeD,OAAO,WAAW;QACvC,IAAIC,aAAa,QAAQ,CAAC,2BACxB,OAAO;QAET,OAAO,IAAI,CAAC,uBAAuB,CAACD;IACtC;IAMA,MAAM,eAAeE,OAAiB,EAAoC;QAExE,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,EAClC,IAAI;YACF,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY;QACtC,EAAE,OAAOF,OAAO;YACdG,QAAQ,IAAI,CAAC,2CAA2CH;YACxD,OAAO,EAAE;QACX;QAIF,IAAI,IAAI,CAAC,KAAK,EAAE,gBACd,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,cAAc;QAIxC,IACE,IAAI,CAAC,KAAK,IACV,eAAe,IAAI,CAAC,KAAK,IACzB,AAAgC,YAAhC,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,EAC3B;YACA,MAAMI,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS;YAGjC,IAAIA,MAAM,aACR,OAAOA,KAAK,WAAW;QAE3B;QAGA,IAAIF,WAAW,AAAmB,YAAnB,OAAOA,WAAwB,iBAAiBA,SAAS;YACtE,MAAMG,cAAcH;YAGpB,OAAOG,YAAY,WAAW;QAChC;QAEA,OAAO,EAAE;IACX;IAGA,MAAM,cAAgC;QACpC,OAAO;IACT;IAEA,MAAM,eAAeC,QAAiC,EAAiB;QAErEC,iBAAiBD;QACjBH,QAAQ,GAAG,CAAC;IACd;IAQA,MAAc,uBAAuB;QACnC,IAAI;YACF,MAAMC,OAAO,IAAI,CAAC,KAAK,EAAE;YAGzB,MAAMA,MAAM;QACd,EAAE,OAAOJ,OAAO;YACdG,QAAQ,IAAI,CAAC,8BAA8BH;QAC7C;IACF;IAEA,MAAM,cACJQ,UAAkB,EAClBC,KAAgB,EAChBX,OAAyB,EACP;QAElB,IAAI,IAAI,CAAC,YAAY,EAAE;YACrB,IAAI,IAAI,CAAC,KAAK,EAAE;gBACdK,QAAQ,GAAG,CAAC;gBACZ,IAAI;oBACF,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO;gBAC1B,EAAE,OAAOH,OAAO;oBACdG,QAAQ,IAAI,CAAC,gCAAgCH;gBAC/C;gBACA,IAAI,CAAC,KAAK,GAAG;YACf;YAGA,MAAM,IAAI,CAAC,aAAa;QAC1B;QAGA,IAAI,CAAC,IAAI,CAAC,KAAK,EACb,MAAM,IAAIU,MACR;QAIJ,MAAMC,QAAQ,IAAI,CAAC,KAAK;QAGxB,MAAMC,cAAc,MAAM,IAAI,CAAC,cAAc;QAC7C,IAAIC;QAGJ,IAAI;YACFF,MAAM,SAAS;QACjB,EAAE,OAAOX,OAAgB;YACvBG,QAAQ,IAAI,CAAC,0CAA0CH;QACzD;QAGA,IAAIF,QAAQ,SAAS,EAAE;YAErB,IAAI,CAAC,gBAAgB,GAAGA,QAAQ,SAAS;YAGzCe,iBAAiBF,MAAM,qBAAqB,CAC1C,CAACG,MAAcC;gBAEb,IAAI,IAAI,CAAC,gBAAgB,KAAKjB,QAAQ,SAAS,EAC7C;gBAIF,IAAI,IAAI,CAAC,kBAAkB,EACzB,IAAI,CAAC,kBAAkB,CAACgB,MAAMC;YAElC;QAEJ;QAEA,IAAI;YACF,IAAIC,SAAS;YACb,IAAIC,iBAAiB;YAErB,IAAI;gBAEFD,SAAS,MAAME,cACbP,OACAH,YACAI,aACAH,OACAX;YAEJ,EAAE,OAAOE,OAAgB;gBAEvBiB,iBAAiBjB;YACnB;YAGA,MAAMmB,WAAW;gBACfH;gBACA,MAAM;gBACN,YAAY;gBACZ,OAAOC,iBACHA,0BAA0BP,QACxBO,eAAe,OAAO,GACtBG,OAAOH,kBACT;YACN;YAGA,IAAI;gBACF,IAAIN,MAAM,cAAc,EAAE;oBACxB,MAAMU,aAAaV,MAAM,cAAc;oBACvC,IAAIU,YAAY;wBACd,MAAMC,cACJC,kBAAkB,oBAAoB,CAACF;wBACzCF,SAAS,IAAI,GAAGG,YAAY,UAAU,EAAE,CAAC,EAAE,IAAI;oBACjD;gBACF;YACF,EAAE,OAAOtB,OAAgB;gBACvBG,QAAQ,IAAI,CAAC,kCAAkCH;YACjD;YAGA,IAAI;gBACF,IAAIW,MAAM,gBAAgB,EACxBQ,SAAS,UAAU,GAAGR,MAAM,gBAAgB,MAAM;YAEtD,EAAE,OAAOX,OAAgB,CAGzB;YAGA,IAAI;gBACF,IAAIW,MAAM,mBAAmB,EAC3BA,MAAM,mBAAmB;YAE7B,EAAE,OAAOX,OAAgB,CAEzB;YAIA,OAAOmB;QACT,SAAU;YAER,IAAIN,gBACFA;QAEJ;IACF;IAMA,MAAc,gBAA+B;QAC3C,IAAI,CAAC,IAAI,CAAC,YAAY,EACpB,MAAM,IAAIH,MACR;QAIJP,QAAQ,GAAG,CAAC;QACZ,IAAI;YACF,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY;YACpCA,QAAQ,GAAG,CAAC;QACd,EAAE,OAAOH,OAAO;YACdG,QAAQ,KAAK,CAAC,2BAA2BH;YACzC,MAAMA;QACR;IACF;IAGA,MAAM,WAAWwB,UAAkB,EAKhC;QACD,IAAI,CAAC,IAAI,CAAC,KAAK,EACb,OAAO;YAAE,OAAO;QAA2C;QAI7D,IAAIV,OAA6B;QACjC,IAAIW,aAA4B;QAIhC,IAAI;YACF,IAAI,AAAqC,cAArC,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,EAAiB;gBACnD,MAAMJ,aAAa,IAAI,CAAC,KAAK,CAAC,cAAc;gBAC5C,IAAIA,YAAY;oBAGd,MAAMC,cACJC,kBAAkB,oBAAoB,CAACF;oBACzCP,OAAOQ,YAAY,UAAU,EAAE,CAAC,EAAE,IAAI;gBACxC;YACF;QACF,EAAE,OAAOtB,OAAO;YACdG,QAAQ,IAAI,CACV,kEACAH;QAEJ;QAIA,IAAI;YACF,IAAI,AAAuC,cAAvC,OAAO,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAiB;gBACrD,MAAM0B,OAAO,IAAI,CAAC,KAAK,CAAC,gBAAgB;gBACxC,IACEA,QACA,AAAgB,YAAhB,OAAOA,QACP,CAACA,KAAK,QAAQ,CAAC,gCAEfD,aAAaC;YAEjB;QACF,EAAE,OAAO1B,OAAO;YAGdG,QAAQ,IAAI,CACV;QAEJ;QAEA,IAAI;YACF,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO;YACxB,IAAI,CAAC,KAAK,GAAG;YACb,OAAO;gBAAE,SAAS;gBAAMW;gBAAMW;YAAW;QAC3C,EAAE,OAAOzB,OAAgB;YACvB,MAAMC,eACJD,iBAAiBU,QAAQV,MAAM,OAAO,GAAG;YAC3CG,QAAQ,KAAK,CACX,CAAC,gDAAgD,EAAEF,cAAc;YAEnE,OAAO;gBAAE,OAAO,CAAC,kBAAkB,EAAEA,cAAc;gBAAEa;gBAAMW;YAAW;QACxE;IACF;IAMA,MAAM,0BAGH;QACD,MAAMN,WAAW;YACf,MAAM;YACN,YAAY;QACd;QAEA,IAAI;YAEF,IAAI,IAAI,CAAC,KAAK,EAAE,gBAAgB;gBAC9B,MAAME,aAAa,IAAI,CAAC,KAAK,CAAC,cAAc;gBAC5C,IAAIA,YAAY;oBACd,MAAMC,cACJC,kBAAkB,oBAAoB,CAACF;oBACzCF,SAAS,IAAI,GAAGG,YAAY,UAAU,EAAE,CAAC,EAAE,IAAI;gBACjD;YACF;YAGA,IAAI,IAAI,CAAC,KAAK,EAAE,kBACdH,SAAS,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,MAAM;QAE3D,EAAE,OAAOnB,OAAgB;YACvBG,QAAQ,KAAK,CAAC,yCAAyCH;QACzD;QAEA,OAAOmB;IACT;IAGA,MAAM,mBAGI;QACR,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,WACf,OAAO;QAGT,IAAI;YACF,MAAMQ,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,aAAa,IAAI;YACnD,MAAMC,cAAc,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,QAAQjC;YAEzD,OAAO;gBACLgC;gBACAC;YACF;QACF,EAAE,OAAO5B,OAAgB;YACvBG,QAAQ,KAAK,CAAC,iCAAiCH;YAC/C,OAAO;QACT;IACF;IAhZA,YAAYW,KAAuB,EAAEkB,YAA2B,CAAE;QAChE,KAAK,IAXP,uBAAQ,SAAR,SACA,uBAAQ,gBAAR,SACA,uBAAQ,sBAAR,SAIA,uBAAQ,oBAAR,SACA,uBAAiB,OAAjB,SACA,uBAAQ,oBAAR;QAIE,IAAI,CAAC,KAAK,GAAGlB,SAAS;QACtB,IAAI,CAAC,YAAY,GAAGkB;QACpB,IAAI,CAAC,GAAG,GAAGC;IACb;AA4YF"}
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { GroupedActionDump } from "@midscene/core";
4
5
  import { getTmpDir } from "@midscene/core/utils";
5
6
  import { PLAYGROUND_SERVER_PORT } from "@midscene/shared/constants";
6
7
  import { overrideAIConfig } from "@midscene/shared/env";
@@ -213,7 +214,7 @@ class PlaygroundServer {
213
214
  try {
214
215
  const dumpString = this.agent.dumpDataString();
215
216
  if (dumpString) {
216
- const groupedDump = JSON.parse(dumpString);
217
+ const groupedDump = GroupedActionDump.fromSerializedString(dumpString);
217
218
  response.dump = groupedDump.executions?.[0] || null;
218
219
  } else response.dump = null;
219
220
  response.reportHTML = this.agent.reportHTMLString() || null;
@@ -248,7 +249,7 @@ class PlaygroundServer {
248
249
  try {
249
250
  const dumpString = this.agent.dumpDataString?.();
250
251
  if (dumpString) {
251
- const groupedDump = JSON.parse(dumpString);
252
+ const groupedDump = GroupedActionDump.fromSerializedString(dumpString);
252
253
  dump = groupedDump.executions?.[0] || null;
253
254
  }
254
255
  reportHTML = this.agent.reportHTMLString?.() || null;
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","sources":["../../src/server.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport type { Server } from 'node:http';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { ExecutionDump } from '@midscene/core';\nimport type { Agent as PageAgent } from '@midscene/core/agent';\nimport { getTmpDir } from '@midscene/core/utils';\nimport { PLAYGROUND_SERVER_PORT } from '@midscene/shared/constants';\nimport { overrideAIConfig } from '@midscene/shared/env';\nimport { uuid } from '@midscene/shared/utils';\nimport express, { type Request, type Response } from 'express';\nimport { executeAction, formatErrorMessage } from './common';\n\nimport 'dotenv/config';\n\nconst defaultPort = PLAYGROUND_SERVER_PORT;\n\n// Static path for playground files\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst STATIC_PATH = join(__dirname, '..', '..', 'static');\n\nconst errorHandler = (\n err: unknown,\n req: Request,\n res: Response,\n next: express.NextFunction,\n) => {\n console.error(err);\n const errorMessage =\n err instanceof Error ? err.message : 'Internal server error';\n res.status(500).json({\n error: errorMessage,\n });\n};\n\nclass PlaygroundServer {\n private _app: express.Application;\n tmpDir: string;\n server?: Server;\n port?: number | null;\n agent: PageAgent;\n staticPath: string;\n taskExecutionDumps: Record<string, ExecutionDump | null>; // Store execution dumps directly\n id: string; // Unique identifier for this server instance\n\n private _initialized = false;\n\n // Factory function for recreating agent\n private agentFactory?: (() => PageAgent | Promise<PageAgent>) | null;\n\n // Track current running task\n private currentTaskId: string | null = null;\n\n constructor(\n agent: PageAgent | (() => PageAgent) | (() => Promise<PageAgent>),\n staticPath = STATIC_PATH,\n id?: string, // Optional override ID\n ) {\n this._app = express();\n this.tmpDir = getTmpDir()!;\n this.staticPath = staticPath;\n this.taskExecutionDumps = {}; // Initialize as empty object\n // Use provided ID, or generate random UUID for each startup\n this.id = id || uuid();\n\n // Support both instance and factory function modes\n if (typeof agent === 'function') {\n this.agentFactory = agent;\n this.agent = null as any; // Will be initialized in launch()\n } else {\n this.agent = agent;\n this.agentFactory = null;\n }\n }\n\n /**\n * Get the Express app instance for custom configuration\n *\n * IMPORTANT: Add middleware (like CORS) BEFORE calling launch()\n * The routes are initialized when launch() is called, so middleware\n * added after launch() will not affect the API routes.\n *\n * @example\n * ```typescript\n * import cors from 'cors';\n *\n * const server = new PlaygroundServer(agent);\n *\n * // Add CORS middleware before launch\n * server.app.use(cors({\n * origin: true,\n * credentials: true,\n * methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']\n * }));\n *\n * await server.launch();\n * ```\n */\n get app(): express.Application {\n return this._app;\n }\n\n /**\n * Initialize Express app with all routes and middleware\n * Called automatically by launch() if not already initialized\n */\n private initializeApp(): void {\n if (this._initialized) return;\n\n // Built-in middleware to parse JSON bodies\n this._app.use(express.json({ limit: '50mb' }));\n\n // Context update middleware (after JSON parsing)\n this._app.use(\n (req: Request, _res: Response, next: express.NextFunction) => {\n const { context } = req.body || {};\n if (\n context &&\n 'updateContext' in this.agent.interface &&\n typeof this.agent.interface.updateContext === 'function'\n ) {\n this.agent.interface.updateContext(context);\n console.log('Context updated by PlaygroundServer middleware');\n }\n next();\n },\n );\n\n // NOTE: CORS middleware should be added externally via server.app.use()\n // before calling server.launch() if needed\n\n // API routes\n this.setupRoutes();\n\n // Static file serving (if staticPath is provided)\n this.setupStaticRoutes();\n\n // Error handler middleware (must be last)\n this._app.use(errorHandler);\n\n this._initialized = true;\n }\n\n filePathForUuid(uuid: string) {\n return join(this.tmpDir, `${uuid}.json`);\n }\n\n saveContextFile(uuid: string, context: string) {\n const tmpFile = this.filePathForUuid(uuid);\n console.log(`save context file: ${tmpFile}`);\n writeFileSync(tmpFile, context);\n return tmpFile;\n }\n\n /**\n * Recreate agent instance (for cancellation)\n */\n private async recreateAgent(): Promise<void> {\n if (!this.agentFactory) {\n throw new Error(\n 'Cannot recreate agent: factory function not provided. Attempting to destroy existing agent only.',\n );\n }\n\n console.log('Recreating agent to cancel current task...');\n\n // Destroy old agent instance\n try {\n if (this.agent && typeof this.agent.destroy === 'function') {\n await this.agent.destroy();\n }\n } catch (error) {\n console.warn('Failed to destroy old agent:', error);\n }\n\n // Create new agent instance\n try {\n this.agent = await this.agentFactory();\n console.log('Agent recreated successfully');\n } catch (error) {\n console.error('Failed to recreate agent:', error);\n throw error;\n }\n }\n\n /**\n * Setup all API routes\n */\n private setupRoutes(): void {\n this._app.get('/status', async (req: Request, res: Response) => {\n res.send({\n status: 'ok',\n id: this.id,\n });\n });\n\n this._app.get('/context/:uuid', async (req: Request, res: Response) => {\n const { uuid } = req.params;\n const contextFile = this.filePathForUuid(uuid);\n\n if (!existsSync(contextFile)) {\n return res.status(404).json({\n error: 'Context not found',\n });\n }\n\n const context = readFileSync(contextFile, 'utf8');\n res.json({\n context,\n });\n });\n\n this._app.get(\n '/task-progress/:requestId',\n async (req: Request, res: Response) => {\n const { requestId } = req.params;\n const executionDump = this.taskExecutionDumps[requestId] || null;\n\n res.json({\n executionDump,\n });\n },\n );\n\n this._app.post('/action-space', async (req: Request, res: Response) => {\n try {\n let actionSpace = [];\n\n actionSpace = this.agent.interface.actionSpace();\n\n // Process actionSpace to make paramSchema serializable with shape info\n const processedActionSpace = actionSpace.map((action: unknown) => {\n if (action && typeof action === 'object' && 'paramSchema' in action) {\n const typedAction = action as {\n paramSchema?: { shape?: object; [key: string]: unknown };\n [key: string]: unknown;\n };\n if (\n typedAction.paramSchema &&\n typeof typedAction.paramSchema === 'object'\n ) {\n // Extract shape information from Zod schema\n let processedSchema = null;\n\n try {\n // Extract shape from runtime Zod object\n if (\n typedAction.paramSchema.shape &&\n typeof typedAction.paramSchema.shape === 'object'\n ) {\n processedSchema = {\n type: 'ZodObject',\n shape: typedAction.paramSchema.shape,\n };\n }\n } catch (e) {\n const actionName =\n 'name' in typedAction && typeof typedAction.name === 'string'\n ? typedAction.name\n : 'unknown';\n console.warn(\n 'Failed to process paramSchema for action:',\n actionName,\n e,\n );\n }\n\n return {\n ...typedAction,\n paramSchema: processedSchema,\n };\n }\n }\n return action;\n });\n\n res.json(processedActionSpace);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error('Failed to get action space:', error);\n res.status(500).json({\n error: errorMessage,\n });\n }\n });\n\n // -------------------------\n // actions from report file\n this._app.post(\n '/playground-with-context',\n async (req: Request, res: Response) => {\n const context = req.body.context;\n\n if (!context) {\n return res.status(400).json({\n error: 'context is required',\n });\n }\n\n const requestId = uuid();\n this.saveContextFile(requestId, context);\n return res.json({\n location: `/playground/${requestId}`,\n uuid: requestId,\n });\n },\n );\n\n this._app.post('/execute', async (req: Request, res: Response) => {\n const {\n type,\n prompt,\n params,\n requestId,\n deepThink,\n screenshotIncluded,\n domIncluded,\n deviceOptions,\n } = req.body;\n\n if (!type) {\n return res.status(400).json({\n error: 'type is required',\n });\n }\n\n // Always recreate agent before execution to ensure latest config is applied\n if (this.agentFactory) {\n console.log('Destroying old agent before execution...');\n try {\n if (this.agent && typeof this.agent.destroy === 'function') {\n await this.agent.destroy();\n }\n } catch (error) {\n console.warn('Failed to destroy old agent:', error);\n }\n\n console.log('Creating new agent with latest config...');\n try {\n this.agent = await this.agentFactory();\n console.log('Agent created successfully');\n } catch (error) {\n console.error('Failed to create agent:', error);\n return res.status(500).json({\n error: `Failed to create agent: ${error instanceof Error ? error.message : 'Unknown error'}`,\n });\n }\n }\n\n // Update device options if provided\n if (\n deviceOptions &&\n this.agent.interface &&\n 'options' in this.agent.interface\n ) {\n this.agent.interface.options = {\n ...(this.agent.interface.options || {}),\n ...deviceOptions,\n };\n }\n\n // Check if another task is running\n if (this.currentTaskId) {\n return res.status(409).json({\n error: 'Another task is already running',\n currentTaskId: this.currentTaskId,\n });\n }\n\n // Lock this task\n if (requestId) {\n this.currentTaskId = requestId;\n this.taskExecutionDumps[requestId] = null;\n\n // Use onDumpUpdate to receive and store executionDump directly\n this.agent.onDumpUpdate = (\n _dump: string,\n executionDump?: ExecutionDump,\n ) => {\n if (executionDump) {\n // Store the execution dump directly without transformation\n this.taskExecutionDumps[requestId] = executionDump;\n }\n };\n }\n\n const response: {\n result: unknown;\n dump: string | null;\n error: string | null;\n reportHTML: string | null;\n requestId?: string;\n } = {\n result: null,\n dump: null,\n error: null,\n reportHTML: null,\n requestId,\n };\n\n const startTime = Date.now();\n try {\n // Get action space to check for dynamic actions\n const actionSpace = this.agent.interface.actionSpace();\n\n // Prepare value object for executeAction\n const value = {\n type,\n prompt,\n params,\n };\n\n response.result = await executeAction(\n this.agent,\n type,\n actionSpace,\n value,\n {\n deepThink,\n screenshotIncluded,\n domIncluded,\n deviceOptions,\n },\n );\n } catch (error: unknown) {\n response.error = formatErrorMessage(error);\n }\n\n try {\n const dumpString = this.agent.dumpDataString();\n if (dumpString) {\n const groupedDump = JSON.parse(dumpString);\n // Extract first execution from grouped dump, matching local execution adapter behavior\n response.dump = groupedDump.executions?.[0] || null;\n } else {\n response.dump = null;\n }\n response.reportHTML = this.agent.reportHTMLString() || null;\n\n this.agent.writeOutActionDumps();\n this.agent.resetDump();\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(\n `write out dump failed: requestId: ${requestId}, ${errorMessage}`,\n );\n }\n\n res.send(response);\n const timeCost = Date.now() - startTime;\n\n if (response.error) {\n console.error(\n `handle request failed after ${timeCost}ms: requestId: ${requestId}, ${response.error}`,\n );\n } else {\n console.log(\n `handle request done after ${timeCost}ms: requestId: ${requestId}`,\n );\n }\n\n // Clean up task execution dumps and unlock after execution completes\n if (requestId) {\n delete this.taskExecutionDumps[requestId];\n // Release the lock\n if (this.currentTaskId === requestId) {\n this.currentTaskId = null;\n }\n }\n });\n\n this._app.post(\n '/cancel/:requestId',\n async (req: Request, res: Response) => {\n const { requestId } = req.params;\n\n if (!requestId) {\n return res.status(400).json({\n error: 'requestId is required',\n });\n }\n\n try {\n // Check if this is the current running task\n if (this.currentTaskId !== requestId) {\n return res.json({\n status: 'not_found',\n message: 'Task not found or already completed',\n });\n }\n\n console.log(`Cancelling task: ${requestId}`);\n\n // Get current execution data before cancelling (dump and reportHTML)\n let dump: any = null;\n let reportHTML: string | null = null;\n\n try {\n const dumpString = this.agent.dumpDataString?.();\n if (dumpString) {\n const groupedDump = JSON.parse(dumpString);\n // Extract first execution from grouped dump\n dump = groupedDump.executions?.[0] || null;\n }\n\n reportHTML = this.agent.reportHTMLString?.() || null;\n } catch (error: unknown) {\n console.warn('Failed to get execution data before cancel:', error);\n }\n\n // Recreate/destroy agent to cancel the current task\n await this.recreateAgent();\n\n // Clean up\n delete this.taskExecutionDumps[requestId];\n this.currentTaskId = null;\n\n res.json({\n status: 'cancelled',\n message: 'Task cancelled successfully',\n dump,\n reportHTML,\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(`Failed to cancel: ${errorMessage}`);\n res.status(500).json({\n error: `Failed to cancel: ${errorMessage}`,\n });\n }\n },\n );\n\n // Screenshot API for real-time screenshot polling\n this._app.get('/screenshot', async (_req: Request, res: Response) => {\n try {\n // Check if page has screenshotBase64 method\n if (typeof this.agent.interface.screenshotBase64 !== 'function') {\n return res.status(500).json({\n error: 'Screenshot method not available on current interface',\n });\n }\n\n const base64Screenshot = await this.agent.interface.screenshotBase64();\n\n res.json({\n screenshot: base64Screenshot,\n timestamp: Date.now(),\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(`Failed to take screenshot: ${errorMessage}`);\n res.status(500).json({\n error: `Failed to take screenshot: ${errorMessage}`,\n });\n }\n });\n\n // Interface info API for getting interface type and description\n this._app.get('/interface-info', async (_req: Request, res: Response) => {\n try {\n const type = this.agent.interface.interfaceType || 'Unknown';\n const description = this.agent.interface.describe?.() || undefined;\n\n res.json({\n type,\n description,\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(`Failed to get interface info: ${errorMessage}`);\n res.status(500).json({\n error: `Failed to get interface info: ${errorMessage}`,\n });\n }\n });\n\n this.app.post('/config', async (req: Request, res: Response) => {\n const { aiConfig } = req.body;\n\n if (!aiConfig || typeof aiConfig !== 'object') {\n return res.status(400).json({\n error: 'aiConfig is required and must be an object',\n });\n }\n\n if (Object.keys(aiConfig).length === 0) {\n return res.json({\n status: 'ok',\n message: 'AI config not changed due to empty object',\n });\n }\n\n try {\n overrideAIConfig(aiConfig);\n\n // Note: Agent will be recreated on next execution to apply new config\n return res.json({\n status: 'ok',\n message:\n 'AI config updated. Agent will be recreated on next execution.',\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(`Failed to update AI config: ${errorMessage}`);\n return res.status(500).json({\n error: `Failed to update AI config: ${errorMessage}`,\n });\n }\n });\n }\n\n /**\n * Setup static file serving routes\n */\n private setupStaticRoutes(): void {\n // Handle index.html with port injection\n this._app.get('/', (_req: Request, res: Response) => {\n this.serveHtmlWithPorts(res);\n });\n\n this._app.get('/index.html', (_req: Request, res: Response) => {\n this.serveHtmlWithPorts(res);\n });\n\n // Use express.static middleware for secure static file serving\n this._app.use(express.static(this.staticPath));\n\n // Fallback to index.html for SPA routing\n this._app.get('*', (_req: Request, res: Response) => {\n this.serveHtmlWithPorts(res);\n });\n }\n\n /**\n * Serve HTML with injected port configuration\n */\n private serveHtmlWithPorts(res: Response): void {\n try {\n const htmlPath = join(this.staticPath, 'index.html');\n let html = readFileSync(htmlPath, 'utf8');\n\n // Get scrcpy server port from global\n const scrcpyPort = (global as any).scrcpyServerPort || this.port! + 1;\n\n // Inject scrcpy port configuration script into HTML head\n const configScript = `\n <script>\n window.SCRCPY_PORT = ${scrcpyPort};\n </script>\n `;\n\n // Insert the script before closing </head> tag\n html = html.replace('</head>', `${configScript}</head>`);\n\n res.setHeader('Content-Type', 'text/html');\n res.send(html);\n } catch (error) {\n console.error('Error serving HTML with ports:', error);\n res.status(500).send('Internal Server Error');\n }\n }\n\n /**\n * Launch the server on specified port\n */\n async launch(port?: number): Promise<PlaygroundServer> {\n // If using factory mode, initialize agent\n if (this.agentFactory) {\n console.log('Initializing agent from factory function...');\n this.agent = await this.agentFactory();\n console.log('Agent initialized successfully');\n }\n\n // Initialize routes now, after any middleware has been added\n this.initializeApp();\n\n this.port = port || defaultPort;\n\n return new Promise((resolve) => {\n const serverPort = this.port;\n this.server = this._app.listen(serverPort, () => {\n resolve(this);\n });\n });\n }\n\n /**\n * Close the server and clean up resources\n */\n async close(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n // Clean up the single agent\n try {\n this.agent.destroy();\n } catch (error) {\n console.warn('Failed to destroy agent:', error);\n }\n this.taskExecutionDumps = {};\n\n // Close the server\n this.server.close((error) => {\n if (error) {\n reject(error);\n } else {\n this.server = undefined;\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n}\n\nexport default PlaygroundServer;\nexport { PlaygroundServer };\n"],"names":["defaultPort","PLAYGROUND_SERVER_PORT","__filename","fileURLToPath","__dirname","dirname","STATIC_PATH","join","errorHandler","err","req","res","next","console","errorMessage","Error","PlaygroundServer","express","_res","context","uuid","tmpFile","writeFileSync","error","contextFile","existsSync","readFileSync","requestId","executionDump","actionSpace","processedActionSpace","action","typedAction","processedSchema","e","actionName","type","prompt","params","deepThink","screenshotIncluded","domIncluded","deviceOptions","_dump","response","startTime","Date","value","executeAction","formatErrorMessage","dumpString","groupedDump","JSON","timeCost","dump","reportHTML","_req","base64Screenshot","description","undefined","aiConfig","Object","overrideAIConfig","htmlPath","html","scrcpyPort","global","configScript","port","Promise","resolve","serverPort","reject","agent","staticPath","id","getTmpDir"],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","sources":["../../src/server.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport type { Server } from 'node:http';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { ExecutionDump } from '@midscene/core';\nimport { GroupedActionDump } from '@midscene/core';\nimport type { Agent as PageAgent } from '@midscene/core/agent';\nimport { getTmpDir } from '@midscene/core/utils';\nimport { PLAYGROUND_SERVER_PORT } from '@midscene/shared/constants';\nimport { overrideAIConfig } from '@midscene/shared/env';\nimport { uuid } from '@midscene/shared/utils';\nimport express, { type Request, type Response } from 'express';\nimport { executeAction, formatErrorMessage } from './common';\n\nimport 'dotenv/config';\n\nconst defaultPort = PLAYGROUND_SERVER_PORT;\n\n// Static path for playground files\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst STATIC_PATH = join(__dirname, '..', '..', 'static');\n\nconst errorHandler = (\n err: unknown,\n req: Request,\n res: Response,\n next: express.NextFunction,\n) => {\n console.error(err);\n const errorMessage =\n err instanceof Error ? err.message : 'Internal server error';\n res.status(500).json({\n error: errorMessage,\n });\n};\n\nclass PlaygroundServer {\n private _app: express.Application;\n tmpDir: string;\n server?: Server;\n port?: number | null;\n agent: PageAgent;\n staticPath: string;\n taskExecutionDumps: Record<string, ExecutionDump | null>; // Store execution dumps directly\n id: string; // Unique identifier for this server instance\n\n private _initialized = false;\n\n // Factory function for recreating agent\n private agentFactory?: (() => PageAgent | Promise<PageAgent>) | null;\n\n // Track current running task\n private currentTaskId: string | null = null;\n\n constructor(\n agent: PageAgent | (() => PageAgent) | (() => Promise<PageAgent>),\n staticPath = STATIC_PATH,\n id?: string, // Optional override ID\n ) {\n this._app = express();\n this.tmpDir = getTmpDir()!;\n this.staticPath = staticPath;\n this.taskExecutionDumps = {}; // Initialize as empty object\n // Use provided ID, or generate random UUID for each startup\n this.id = id || uuid();\n\n // Support both instance and factory function modes\n if (typeof agent === 'function') {\n this.agentFactory = agent;\n this.agent = null as any; // Will be initialized in launch()\n } else {\n this.agent = agent;\n this.agentFactory = null;\n }\n }\n\n /**\n * Get the Express app instance for custom configuration\n *\n * IMPORTANT: Add middleware (like CORS) BEFORE calling launch()\n * The routes are initialized when launch() is called, so middleware\n * added after launch() will not affect the API routes.\n *\n * @example\n * ```typescript\n * import cors from 'cors';\n *\n * const server = new PlaygroundServer(agent);\n *\n * // Add CORS middleware before launch\n * server.app.use(cors({\n * origin: true,\n * credentials: true,\n * methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']\n * }));\n *\n * await server.launch();\n * ```\n */\n get app(): express.Application {\n return this._app;\n }\n\n /**\n * Initialize Express app with all routes and middleware\n * Called automatically by launch() if not already initialized\n */\n private initializeApp(): void {\n if (this._initialized) return;\n\n // Built-in middleware to parse JSON bodies\n this._app.use(express.json({ limit: '50mb' }));\n\n // Context update middleware (after JSON parsing)\n this._app.use(\n (req: Request, _res: Response, next: express.NextFunction) => {\n const { context } = req.body || {};\n if (\n context &&\n 'updateContext' in this.agent.interface &&\n typeof this.agent.interface.updateContext === 'function'\n ) {\n this.agent.interface.updateContext(context);\n console.log('Context updated by PlaygroundServer middleware');\n }\n next();\n },\n );\n\n // NOTE: CORS middleware should be added externally via server.app.use()\n // before calling server.launch() if needed\n\n // API routes\n this.setupRoutes();\n\n // Static file serving (if staticPath is provided)\n this.setupStaticRoutes();\n\n // Error handler middleware (must be last)\n this._app.use(errorHandler);\n\n this._initialized = true;\n }\n\n filePathForUuid(uuid: string) {\n return join(this.tmpDir, `${uuid}.json`);\n }\n\n saveContextFile(uuid: string, context: string) {\n const tmpFile = this.filePathForUuid(uuid);\n console.log(`save context file: ${tmpFile}`);\n writeFileSync(tmpFile, context);\n return tmpFile;\n }\n\n /**\n * Recreate agent instance (for cancellation)\n */\n private async recreateAgent(): Promise<void> {\n if (!this.agentFactory) {\n throw new Error(\n 'Cannot recreate agent: factory function not provided. Attempting to destroy existing agent only.',\n );\n }\n\n console.log('Recreating agent to cancel current task...');\n\n // Destroy old agent instance\n try {\n if (this.agent && typeof this.agent.destroy === 'function') {\n await this.agent.destroy();\n }\n } catch (error) {\n console.warn('Failed to destroy old agent:', error);\n }\n\n // Create new agent instance\n try {\n this.agent = await this.agentFactory();\n console.log('Agent recreated successfully');\n } catch (error) {\n console.error('Failed to recreate agent:', error);\n throw error;\n }\n }\n\n /**\n * Setup all API routes\n */\n private setupRoutes(): void {\n this._app.get('/status', async (req: Request, res: Response) => {\n res.send({\n status: 'ok',\n id: this.id,\n });\n });\n\n this._app.get('/context/:uuid', async (req: Request, res: Response) => {\n const { uuid } = req.params;\n const contextFile = this.filePathForUuid(uuid);\n\n if (!existsSync(contextFile)) {\n return res.status(404).json({\n error: 'Context not found',\n });\n }\n\n const context = readFileSync(contextFile, 'utf8');\n res.json({\n context,\n });\n });\n\n this._app.get(\n '/task-progress/:requestId',\n async (req: Request, res: Response) => {\n const { requestId } = req.params;\n const executionDump = this.taskExecutionDumps[requestId] || null;\n\n res.json({\n executionDump,\n });\n },\n );\n\n this._app.post('/action-space', async (req: Request, res: Response) => {\n try {\n let actionSpace = [];\n\n actionSpace = this.agent.interface.actionSpace();\n\n // Process actionSpace to make paramSchema serializable with shape info\n const processedActionSpace = actionSpace.map((action: unknown) => {\n if (action && typeof action === 'object' && 'paramSchema' in action) {\n const typedAction = action as {\n paramSchema?: { shape?: object; [key: string]: unknown };\n [key: string]: unknown;\n };\n if (\n typedAction.paramSchema &&\n typeof typedAction.paramSchema === 'object'\n ) {\n // Extract shape information from Zod schema\n let processedSchema = null;\n\n try {\n // Extract shape from runtime Zod object\n if (\n typedAction.paramSchema.shape &&\n typeof typedAction.paramSchema.shape === 'object'\n ) {\n processedSchema = {\n type: 'ZodObject',\n shape: typedAction.paramSchema.shape,\n };\n }\n } catch (e) {\n const actionName =\n 'name' in typedAction && typeof typedAction.name === 'string'\n ? typedAction.name\n : 'unknown';\n console.warn(\n 'Failed to process paramSchema for action:',\n actionName,\n e,\n );\n }\n\n return {\n ...typedAction,\n paramSchema: processedSchema,\n };\n }\n }\n return action;\n });\n\n res.json(processedActionSpace);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error('Failed to get action space:', error);\n res.status(500).json({\n error: errorMessage,\n });\n }\n });\n\n // -------------------------\n // actions from report file\n this._app.post(\n '/playground-with-context',\n async (req: Request, res: Response) => {\n const context = req.body.context;\n\n if (!context) {\n return res.status(400).json({\n error: 'context is required',\n });\n }\n\n const requestId = uuid();\n this.saveContextFile(requestId, context);\n return res.json({\n location: `/playground/${requestId}`,\n uuid: requestId,\n });\n },\n );\n\n this._app.post('/execute', async (req: Request, res: Response) => {\n const {\n type,\n prompt,\n params,\n requestId,\n deepThink,\n screenshotIncluded,\n domIncluded,\n deviceOptions,\n } = req.body;\n\n if (!type) {\n return res.status(400).json({\n error: 'type is required',\n });\n }\n\n // Always recreate agent before execution to ensure latest config is applied\n if (this.agentFactory) {\n console.log('Destroying old agent before execution...');\n try {\n if (this.agent && typeof this.agent.destroy === 'function') {\n await this.agent.destroy();\n }\n } catch (error) {\n console.warn('Failed to destroy old agent:', error);\n }\n\n console.log('Creating new agent with latest config...');\n try {\n this.agent = await this.agentFactory();\n console.log('Agent created successfully');\n } catch (error) {\n console.error('Failed to create agent:', error);\n return res.status(500).json({\n error: `Failed to create agent: ${error instanceof Error ? error.message : 'Unknown error'}`,\n });\n }\n }\n\n // Update device options if provided\n if (\n deviceOptions &&\n this.agent.interface &&\n 'options' in this.agent.interface\n ) {\n this.agent.interface.options = {\n ...(this.agent.interface.options || {}),\n ...deviceOptions,\n };\n }\n\n // Check if another task is running\n if (this.currentTaskId) {\n return res.status(409).json({\n error: 'Another task is already running',\n currentTaskId: this.currentTaskId,\n });\n }\n\n // Lock this task\n if (requestId) {\n this.currentTaskId = requestId;\n this.taskExecutionDumps[requestId] = null;\n\n // Use onDumpUpdate to receive and store executionDump directly\n this.agent.onDumpUpdate = (\n _dump: string,\n executionDump?: ExecutionDump,\n ) => {\n if (executionDump) {\n // Store the execution dump directly without transformation\n this.taskExecutionDumps[requestId] = executionDump;\n }\n };\n }\n\n const response: {\n result: unknown;\n dump: ExecutionDump | null;\n error: string | null;\n reportHTML: string | null;\n requestId?: string;\n } = {\n result: null,\n dump: null,\n error: null,\n reportHTML: null,\n requestId,\n };\n\n const startTime = Date.now();\n try {\n // Get action space to check for dynamic actions\n const actionSpace = this.agent.interface.actionSpace();\n\n // Prepare value object for executeAction\n const value = {\n type,\n prompt,\n params,\n };\n\n response.result = await executeAction(\n this.agent,\n type,\n actionSpace,\n value,\n {\n deepThink,\n screenshotIncluded,\n domIncluded,\n deviceOptions,\n },\n );\n } catch (error: unknown) {\n response.error = formatErrorMessage(error);\n }\n\n try {\n const dumpString = this.agent.dumpDataString();\n if (dumpString) {\n const groupedDump =\n GroupedActionDump.fromSerializedString(dumpString);\n // Extract first execution from grouped dump, matching local execution adapter behavior\n response.dump = groupedDump.executions?.[0] || null;\n } else {\n response.dump = null;\n }\n response.reportHTML = this.agent.reportHTMLString() || null;\n\n this.agent.writeOutActionDumps();\n this.agent.resetDump();\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(\n `write out dump failed: requestId: ${requestId}, ${errorMessage}`,\n );\n }\n\n res.send(response);\n const timeCost = Date.now() - startTime;\n\n if (response.error) {\n console.error(\n `handle request failed after ${timeCost}ms: requestId: ${requestId}, ${response.error}`,\n );\n } else {\n console.log(\n `handle request done after ${timeCost}ms: requestId: ${requestId}`,\n );\n }\n\n // Clean up task execution dumps and unlock after execution completes\n if (requestId) {\n delete this.taskExecutionDumps[requestId];\n // Release the lock\n if (this.currentTaskId === requestId) {\n this.currentTaskId = null;\n }\n }\n });\n\n this._app.post(\n '/cancel/:requestId',\n async (req: Request, res: Response) => {\n const { requestId } = req.params;\n\n if (!requestId) {\n return res.status(400).json({\n error: 'requestId is required',\n });\n }\n\n try {\n // Check if this is the current running task\n if (this.currentTaskId !== requestId) {\n return res.json({\n status: 'not_found',\n message: 'Task not found or already completed',\n });\n }\n\n console.log(`Cancelling task: ${requestId}`);\n\n // Get current execution data before cancelling (dump and reportHTML)\n let dump: any = null;\n let reportHTML: string | null = null;\n\n try {\n const dumpString = this.agent.dumpDataString?.();\n if (dumpString) {\n const groupedDump =\n GroupedActionDump.fromSerializedString(dumpString);\n // Extract first execution from grouped dump\n dump = groupedDump.executions?.[0] || null;\n }\n\n reportHTML = this.agent.reportHTMLString?.() || null;\n } catch (error: unknown) {\n console.warn('Failed to get execution data before cancel:', error);\n }\n\n // Recreate/destroy agent to cancel the current task\n await this.recreateAgent();\n\n // Clean up\n delete this.taskExecutionDumps[requestId];\n this.currentTaskId = null;\n\n res.json({\n status: 'cancelled',\n message: 'Task cancelled successfully',\n dump,\n reportHTML,\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(`Failed to cancel: ${errorMessage}`);\n res.status(500).json({\n error: `Failed to cancel: ${errorMessage}`,\n });\n }\n },\n );\n\n // Screenshot API for real-time screenshot polling\n this._app.get('/screenshot', async (_req: Request, res: Response) => {\n try {\n // Check if page has screenshotBase64 method\n if (typeof this.agent.interface.screenshotBase64 !== 'function') {\n return res.status(500).json({\n error: 'Screenshot method not available on current interface',\n });\n }\n\n const base64Screenshot = await this.agent.interface.screenshotBase64();\n\n res.json({\n screenshot: base64Screenshot,\n timestamp: Date.now(),\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(`Failed to take screenshot: ${errorMessage}`);\n res.status(500).json({\n error: `Failed to take screenshot: ${errorMessage}`,\n });\n }\n });\n\n // Interface info API for getting interface type and description\n this._app.get('/interface-info', async (_req: Request, res: Response) => {\n try {\n const type = this.agent.interface.interfaceType || 'Unknown';\n const description = this.agent.interface.describe?.() || undefined;\n\n res.json({\n type,\n description,\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(`Failed to get interface info: ${errorMessage}`);\n res.status(500).json({\n error: `Failed to get interface info: ${errorMessage}`,\n });\n }\n });\n\n this.app.post('/config', async (req: Request, res: Response) => {\n const { aiConfig } = req.body;\n\n if (!aiConfig || typeof aiConfig !== 'object') {\n return res.status(400).json({\n error: 'aiConfig is required and must be an object',\n });\n }\n\n if (Object.keys(aiConfig).length === 0) {\n return res.json({\n status: 'ok',\n message: 'AI config not changed due to empty object',\n });\n }\n\n try {\n overrideAIConfig(aiConfig);\n\n // Note: Agent will be recreated on next execution to apply new config\n return res.json({\n status: 'ok',\n message:\n 'AI config updated. Agent will be recreated on next execution.',\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(`Failed to update AI config: ${errorMessage}`);\n return res.status(500).json({\n error: `Failed to update AI config: ${errorMessage}`,\n });\n }\n });\n }\n\n /**\n * Setup static file serving routes\n */\n private setupStaticRoutes(): void {\n // Handle index.html with port injection\n this._app.get('/', (_req: Request, res: Response) => {\n this.serveHtmlWithPorts(res);\n });\n\n this._app.get('/index.html', (_req: Request, res: Response) => {\n this.serveHtmlWithPorts(res);\n });\n\n // Use express.static middleware for secure static file serving\n this._app.use(express.static(this.staticPath));\n\n // Fallback to index.html for SPA routing\n this._app.get('*', (_req: Request, res: Response) => {\n this.serveHtmlWithPorts(res);\n });\n }\n\n /**\n * Serve HTML with injected port configuration\n */\n private serveHtmlWithPorts(res: Response): void {\n try {\n const htmlPath = join(this.staticPath, 'index.html');\n let html = readFileSync(htmlPath, 'utf8');\n\n // Get scrcpy server port from global\n const scrcpyPort = (global as any).scrcpyServerPort || this.port! + 1;\n\n // Inject scrcpy port configuration script into HTML head\n const configScript = `\n <script>\n window.SCRCPY_PORT = ${scrcpyPort};\n </script>\n `;\n\n // Insert the script before closing </head> tag\n html = html.replace('</head>', `${configScript}</head>`);\n\n res.setHeader('Content-Type', 'text/html');\n res.send(html);\n } catch (error) {\n console.error('Error serving HTML with ports:', error);\n res.status(500).send('Internal Server Error');\n }\n }\n\n /**\n * Launch the server on specified port\n */\n async launch(port?: number): Promise<PlaygroundServer> {\n // If using factory mode, initialize agent\n if (this.agentFactory) {\n console.log('Initializing agent from factory function...');\n this.agent = await this.agentFactory();\n console.log('Agent initialized successfully');\n }\n\n // Initialize routes now, after any middleware has been added\n this.initializeApp();\n\n this.port = port || defaultPort;\n\n return new Promise((resolve) => {\n const serverPort = this.port;\n this.server = this._app.listen(serverPort, () => {\n resolve(this);\n });\n });\n }\n\n /**\n * Close the server and clean up resources\n */\n async close(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n // Clean up the single agent\n try {\n this.agent.destroy();\n } catch (error) {\n console.warn('Failed to destroy agent:', error);\n }\n this.taskExecutionDumps = {};\n\n // Close the server\n this.server.close((error) => {\n if (error) {\n reject(error);\n } else {\n this.server = undefined;\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n}\n\nexport default PlaygroundServer;\nexport { PlaygroundServer };\n"],"names":["defaultPort","PLAYGROUND_SERVER_PORT","__filename","fileURLToPath","__dirname","dirname","STATIC_PATH","join","errorHandler","err","req","res","next","console","errorMessage","Error","PlaygroundServer","express","_res","context","uuid","tmpFile","writeFileSync","error","contextFile","existsSync","readFileSync","requestId","executionDump","actionSpace","processedActionSpace","action","typedAction","processedSchema","e","actionName","type","prompt","params","deepThink","screenshotIncluded","domIncluded","deviceOptions","_dump","response","startTime","Date","value","executeAction","formatErrorMessage","dumpString","groupedDump","GroupedActionDump","timeCost","dump","reportHTML","_req","base64Screenshot","description","undefined","aiConfig","Object","overrideAIConfig","htmlPath","html","scrcpyPort","global","configScript","port","Promise","resolve","serverPort","reject","agent","staticPath","id","getTmpDir"],"mappings":"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"}
@@ -26,6 +26,7 @@ __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
27
  LocalExecutionAdapter: ()=>LocalExecutionAdapter
28
28
  });
29
+ const core_namespaceObject = require("@midscene/core");
29
30
  const env_namespaceObject = require("@midscene/shared/env");
30
31
  const utils_namespaceObject = require("@midscene/shared/utils");
31
32
  const external_common_js_namespaceObject = require("../common.js");
@@ -140,7 +141,7 @@ class LocalExecutionAdapter extends external_base_js_namespaceObject.BasePlaygro
140
141
  if (agent.dumpDataString) {
141
142
  const dumpString = agent.dumpDataString();
142
143
  if (dumpString) {
143
- const groupedDump = JSON.parse(dumpString);
144
+ const groupedDump = core_namespaceObject.GroupedActionDump.fromSerializedString(dumpString);
144
145
  response.dump = groupedDump.executions?.[0] || null;
145
146
  }
146
147
  }
@@ -179,7 +180,7 @@ class LocalExecutionAdapter extends external_base_js_namespaceObject.BasePlaygro
179
180
  if ('function' == typeof this.agent.dumpDataString) {
180
181
  const dumpString = this.agent.dumpDataString();
181
182
  if (dumpString) {
182
- const groupedDump = JSON.parse(dumpString);
183
+ const groupedDump = core_namespaceObject.GroupedActionDump.fromSerializedString(dumpString);
183
184
  dump = groupedDump.executions?.[0] ?? null;
184
185
  }
185
186
  }
@@ -221,7 +222,7 @@ class LocalExecutionAdapter extends external_base_js_namespaceObject.BasePlaygro
221
222
  if (this.agent?.dumpDataString) {
222
223
  const dumpString = this.agent.dumpDataString();
223
224
  if (dumpString) {
224
- const groupedDump = JSON.parse(dumpString);
225
+ const groupedDump = core_namespaceObject.GroupedActionDump.fromSerializedString(dumpString);
225
226
  response.dump = groupedDump.executions?.[0] || null;
226
227
  }
227
228
  }
@@ -1 +1 @@
1
- {"version":3,"file":"adapters/local-execution.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/adapters/local-execution.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import type { DeviceAction, ExecutionDump } from '@midscene/core';\nimport { overrideAIConfig } from '@midscene/shared/env';\nimport { uuid } from '@midscene/shared/utils';\nimport { executeAction, parseStructuredParams } from '../common';\nimport type {\n AgentFactory,\n ExecutionOptions,\n FormValue,\n PlaygroundAgent,\n} from '../types';\nimport { BasePlaygroundAdapter } from './base';\n\nexport class LocalExecutionAdapter extends BasePlaygroundAdapter {\n private agent: PlaygroundAgent | null;\n private agentFactory?: AgentFactory; // Factory function for recreating agent\n private dumpUpdateCallback?: (\n dump: string,\n executionDump?: ExecutionDump,\n ) => void;\n private progressCallback?: (tip: string) => void;\n private readonly _id: string; // Unique identifier for this local adapter instance\n private currentRequestId?: string; // Track current request to prevent stale callbacks\n\n constructor(agent?: PlaygroundAgent, agentFactory?: AgentFactory) {\n super();\n this.agent = agent ?? null;\n this.agentFactory = agentFactory;\n this._id = uuid(); // Generate unique ID for local adapter\n }\n\n // Get adapter ID\n get id(): string {\n return this._id;\n }\n\n onDumpUpdate(\n callback: (dump: string, executionDump?: ExecutionDump) => void,\n ): void {\n // Clear any existing callback before setting new one\n this.dumpUpdateCallback = undefined;\n // Set the new callback\n this.dumpUpdateCallback = callback;\n }\n\n // Set progress callback for monitoring operation status\n setProgressCallback(callback: (tip: string) => void): void {\n this.progressCallback = undefined;\n this.progressCallback = callback;\n }\n\n async parseStructuredParams(\n action: DeviceAction<unknown>,\n params: Record<string, unknown>,\n options: ExecutionOptions,\n ): Promise<unknown[]> {\n // Use shared implementation from common.ts\n return await parseStructuredParams(action, params, options);\n }\n\n formatErrorMessage(error: any): string {\n const errorMessage = error?.message || '';\n if (errorMessage.includes('of different extension')) {\n return 'Conflicting extension detected. Please disable the suspicious plugins and refresh the page. Guide: https://midscenejs.com/quick-experience.html#faq';\n }\n return this.formatBasicErrorMessage(error);\n }\n\n // Local execution - use base implementation\n // (inherits default executeAction from BasePlaygroundAdapter)\n\n // Local execution gets actionSpace from internal agent (parameter is for backward compatibility)\n async getActionSpace(context?: unknown): Promise<DeviceAction<unknown>[]> {\n // If agent doesn't exist but we have a factory, create one temporarily to get actionSpace\n if (!this.agent && this.agentFactory) {\n try {\n this.agent = await this.agentFactory();\n } catch (error) {\n console.warn('Failed to create agent for actionSpace:', error);\n return [];\n }\n }\n\n // Priority 1: Use agent's getActionSpace method\n if (this.agent?.getActionSpace) {\n return await this.agent.getActionSpace();\n }\n\n // Priority 2: Use agent's interface.actionSpace method\n if (\n this.agent &&\n 'interface' in this.agent &&\n typeof this.agent.interface === 'object'\n ) {\n const page = this.agent.interface as {\n actionSpace?: () => DeviceAction<unknown>[];\n };\n if (page?.actionSpace) {\n return page.actionSpace();\n }\n }\n\n // Priority 3: Fallback to context parameter (for backward compatibility with tests)\n if (context && typeof context === 'object' && 'actionSpace' in context) {\n const contextPage = context as {\n actionSpace: () => DeviceAction<unknown>[];\n };\n return contextPage.actionSpace();\n }\n\n return [];\n }\n\n // Local execution doesn't use a server, so always return true\n async checkStatus(): Promise<boolean> {\n return true;\n }\n\n async overrideConfig(aiConfig: Record<string, unknown>): Promise<void> {\n // For local execution, use the shared env override function\n overrideAIConfig(aiConfig);\n console.log('Config updated. Agent will be recreated on next execution.');\n }\n\n /**\n * Safely detaches the Chrome debugger without destroying the agent.\n * This removes the \"Debugger attached\" banner from the browser window\n * while keeping the agent instance intact for potential reuse.\n * Called on errors to improve user experience by cleaning up the UI.\n */\n private async detachDebuggerSafely() {\n try {\n const page = this.agent?.interface as\n | { detachDebugger?: () => Promise<void> }\n | undefined;\n await page?.detachDebugger?.();\n } catch (error) {\n console.warn('Failed to detach debugger:', error);\n }\n }\n\n async executeAction(\n actionType: string,\n value: FormValue,\n options: ExecutionOptions,\n ): Promise<unknown> {\n // If agentFactory is provided, always recreate agent with latest config before execution\n if (this.agentFactory) {\n if (this.agent) {\n console.log('Destroying old agent before execution...');\n try {\n await this.agent.destroy?.();\n } catch (error) {\n console.warn('Failed to destroy old agent:', error);\n }\n this.agent = null;\n }\n\n // Create new agent with latest config\n await this.recreateAgent();\n }\n\n // Agent must exist (either recreated or provided in constructor)\n if (!this.agent) {\n throw new Error(\n 'No agent available. Please provide either an agent instance or agentFactory.',\n );\n }\n\n const agent = this.agent;\n\n // Get actionSpace using our simplified getActionSpace method\n const actionSpace = await this.getActionSpace();\n let removeListener: (() => void) | undefined;\n\n // Reset dump at the start of execution to ensure clean state\n try {\n agent.resetDump?.();\n } catch (error: unknown) {\n console.warn('Failed to reset dump before execution:', error);\n }\n\n // Setup dump update tracking if requestId is provided\n if (options.requestId) {\n // Track current request ID to prevent stale callbacks\n this.currentRequestId = options.requestId;\n\n // Add listener and save remove function\n removeListener = agent.addDumpUpdateListener(\n (dump: string, executionDump?: ExecutionDump) => {\n // Only process if this is still the current request\n if (this.currentRequestId !== options.requestId) {\n return;\n }\n\n // Forward to external callback\n if (this.dumpUpdateCallback) {\n this.dumpUpdateCallback(dump, executionDump);\n }\n },\n );\n }\n\n try {\n let result = null;\n let executionError = null;\n\n try {\n // Call the base implementation with the original signature\n result = await executeAction(\n agent,\n actionType,\n actionSpace,\n value,\n options,\n );\n } catch (error: unknown) {\n // Capture error but don't throw yet - we need to get dump/reportHTML first\n executionError = error;\n }\n\n // Always construct response with dump and reportHTML, regardless of success/failure\n const response = {\n result,\n dump: null as unknown,\n reportHTML: null as string | null,\n error: executionError\n ? executionError instanceof Error\n ? executionError.message\n : String(executionError)\n : null,\n };\n\n // Get dump data - separate try-catch to ensure dump is retrieved even if reportHTML fails\n try {\n if (agent.dumpDataString) {\n const dumpString = agent.dumpDataString();\n if (dumpString) {\n const groupedDump = JSON.parse(dumpString);\n response.dump = groupedDump.executions?.[0] || null;\n }\n }\n } catch (error: unknown) {\n console.warn('Failed to get dump from agent:', error);\n }\n\n // Try to get reportHTML - may fail in browser environment (fs not available)\n try {\n if (agent.reportHTMLString) {\n response.reportHTML = agent.reportHTMLString() || null;\n }\n } catch (error: unknown) {\n // reportHTMLString may throw in browser environment\n // This is expected in chrome-extension, continue without reportHTML\n }\n\n // Write out action dumps - may also fail in browser environment\n try {\n if (agent.writeOutActionDumps) {\n agent.writeOutActionDumps();\n }\n } catch (error: unknown) {\n // writeOutActionDumps may fail in browser environment\n }\n\n // Don't throw the error - return it in response so caller can access dump/reportHTML\n // The caller (usePlaygroundExecution) will check response.error to determine success\n return response;\n } finally {\n // Remove listener to prevent accumulation\n if (removeListener) {\n removeListener();\n }\n }\n }\n\n /**\n * Recreate agent instance using the factory function.\n * Called automatically when executeAction is called.\n */\n private async recreateAgent(): Promise<void> {\n if (!this.agentFactory) {\n throw new Error(\n 'Cannot recreate agent: factory function not provided. Please provide agentFactory in PlaygroundConfig to enable agent recreation.',\n );\n }\n\n console.log('Creating new agent with latest config...');\n try {\n this.agent = await this.agentFactory();\n console.log('Agent created successfully');\n } catch (error) {\n console.error('Failed to create agent:', error);\n throw error;\n }\n }\n\n // Local execution task cancellation - returns dump and reportHTML before destroying\n async cancelTask(_requestId: string): Promise<{\n error?: string;\n success?: boolean;\n dump?: ExecutionDump | null;\n reportHTML?: string | null;\n }> {\n if (!this.agent) {\n return { error: 'No active agent found for this requestId' };\n }\n\n // Get execution data BEFORE destroying the agent\n let dump: ExecutionDump | null = null;\n let reportHTML: string | null = null;\n\n // Get dump data separately - don't let reportHTML errors affect dump retrieval\n // IMPORTANT: Must extract dump BEFORE agent.destroy(), as dump is stored in agent memory\n try {\n if (typeof this.agent.dumpDataString === 'function') {\n const dumpString = this.agent.dumpDataString();\n if (dumpString) {\n // dumpDataString() returns GroupedActionDump: { executions: ExecutionDump[] }\n // In Playground, each \"Run\" creates one execution, so we take executions[0]\n const groupedDump = JSON.parse(dumpString);\n dump = groupedDump.executions?.[0] ?? null;\n }\n }\n } catch (error) {\n console.warn(\n '[LocalExecutionAdapter] Failed to get dump data before cancel:',\n error,\n );\n }\n\n // Try to get reportHTML separately - this may fail in browser environment\n // where fs.readFileSync is not available\n try {\n if (typeof this.agent.reportHTMLString === 'function') {\n const html = this.agent.reportHTMLString();\n if (\n html &&\n typeof html === 'string' &&\n !html.includes('REPLACE_ME_WITH_REPORT_HTML')\n ) {\n reportHTML = html;\n }\n }\n } catch (error) {\n // reportHTMLString may throw in browser environment (fs not available)\n // This is expected, just continue with dump data only\n console.warn(\n '[LocalExecutionAdapter] reportHTMLString not available in this environment',\n );\n }\n\n try {\n await this.agent.destroy?.();\n this.agent = null; // Clear agent reference\n return { success: true, dump, reportHTML };\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(\n `[LocalExecutionAdapter] Failed to cancel agent: ${errorMessage}`,\n );\n return { error: `Failed to cancel: ${errorMessage}`, dump, reportHTML };\n }\n }\n\n /**\n * Get current execution data without resetting\n * This allows retrieving dump and report when execution is stopped\n */\n async getCurrentExecutionData(): Promise<{\n dump: ExecutionDump | null;\n reportHTML: string | null;\n }> {\n const response = {\n dump: null as ExecutionDump | null,\n reportHTML: null as string | null,\n };\n\n try {\n // Get dump data\n if (this.agent?.dumpDataString) {\n const dumpString = this.agent.dumpDataString();\n if (dumpString) {\n const groupedDump = JSON.parse(dumpString);\n response.dump = groupedDump.executions?.[0] || null;\n }\n }\n\n // Get report HTML\n if (this.agent?.reportHTMLString) {\n response.reportHTML = this.agent.reportHTMLString() || null;\n }\n } catch (error: unknown) {\n console.error('Failed to get current execution data:', error);\n }\n\n return response;\n }\n\n // Get interface information from the agent\n async getInterfaceInfo(): Promise<{\n type: string;\n description?: string;\n } | null> {\n if (!this.agent?.interface) {\n return null;\n }\n\n try {\n const type = this.agent.interface.interfaceType || 'Unknown';\n const description = this.agent.interface.describe?.() || undefined;\n\n return {\n type,\n description,\n };\n } catch (error: unknown) {\n console.error('Failed to get interface info:', error);\n return null;\n }\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","LocalExecutionAdapter","BasePlaygroundAdapter","callback","undefined","action","params","options","parseStructuredParams","error","errorMessage","context","console","page","contextPage","aiConfig","overrideAIConfig","actionType","value","Error","agent","actionSpace","removeListener","dump","executionDump","result","executionError","executeAction","response","String","dumpString","groupedDump","JSON","_requestId","reportHTML","html","type","description","agentFactory","uuid"],"mappings":"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"}
1
+ {"version":3,"file":"adapters/local-execution.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/adapters/local-execution.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import type { DeviceAction, ExecutionDump } from '@midscene/core';\nimport { GroupedActionDump } from '@midscene/core';\nimport { overrideAIConfig } from '@midscene/shared/env';\nimport { uuid } from '@midscene/shared/utils';\nimport { executeAction, parseStructuredParams } from '../common';\nimport type {\n AgentFactory,\n ExecutionOptions,\n FormValue,\n PlaygroundAgent,\n} from '../types';\nimport { BasePlaygroundAdapter } from './base';\n\nexport class LocalExecutionAdapter extends BasePlaygroundAdapter {\n private agent: PlaygroundAgent | null;\n private agentFactory?: AgentFactory; // Factory function for recreating agent\n private dumpUpdateCallback?: (\n dump: string,\n executionDump?: ExecutionDump,\n ) => void;\n private progressCallback?: (tip: string) => void;\n private readonly _id: string; // Unique identifier for this local adapter instance\n private currentRequestId?: string; // Track current request to prevent stale callbacks\n\n constructor(agent?: PlaygroundAgent, agentFactory?: AgentFactory) {\n super();\n this.agent = agent ?? null;\n this.agentFactory = agentFactory;\n this._id = uuid(); // Generate unique ID for local adapter\n }\n\n // Get adapter ID\n get id(): string {\n return this._id;\n }\n\n onDumpUpdate(\n callback: (dump: string, executionDump?: ExecutionDump) => void,\n ): void {\n // Clear any existing callback before setting new one\n this.dumpUpdateCallback = undefined;\n // Set the new callback\n this.dumpUpdateCallback = callback;\n }\n\n // Set progress callback for monitoring operation status\n setProgressCallback(callback: (tip: string) => void): void {\n this.progressCallback = undefined;\n this.progressCallback = callback;\n }\n\n async parseStructuredParams(\n action: DeviceAction<unknown>,\n params: Record<string, unknown>,\n options: ExecutionOptions,\n ): Promise<unknown[]> {\n // Use shared implementation from common.ts\n return await parseStructuredParams(action, params, options);\n }\n\n formatErrorMessage(error: any): string {\n const errorMessage = error?.message || '';\n if (errorMessage.includes('of different extension')) {\n return 'Conflicting extension detected. Please disable the suspicious plugins and refresh the page. Guide: https://midscenejs.com/quick-experience.html#faq';\n }\n return this.formatBasicErrorMessage(error);\n }\n\n // Local execution - use base implementation\n // (inherits default executeAction from BasePlaygroundAdapter)\n\n // Local execution gets actionSpace from internal agent (parameter is for backward compatibility)\n async getActionSpace(context?: unknown): Promise<DeviceAction<unknown>[]> {\n // If agent doesn't exist but we have a factory, create one temporarily to get actionSpace\n if (!this.agent && this.agentFactory) {\n try {\n this.agent = await this.agentFactory();\n } catch (error) {\n console.warn('Failed to create agent for actionSpace:', error);\n return [];\n }\n }\n\n // Priority 1: Use agent's getActionSpace method\n if (this.agent?.getActionSpace) {\n return await this.agent.getActionSpace();\n }\n\n // Priority 2: Use agent's interface.actionSpace method\n if (\n this.agent &&\n 'interface' in this.agent &&\n typeof this.agent.interface === 'object'\n ) {\n const page = this.agent.interface as {\n actionSpace?: () => DeviceAction<unknown>[];\n };\n if (page?.actionSpace) {\n return page.actionSpace();\n }\n }\n\n // Priority 3: Fallback to context parameter (for backward compatibility with tests)\n if (context && typeof context === 'object' && 'actionSpace' in context) {\n const contextPage = context as {\n actionSpace: () => DeviceAction<unknown>[];\n };\n return contextPage.actionSpace();\n }\n\n return [];\n }\n\n // Local execution doesn't use a server, so always return true\n async checkStatus(): Promise<boolean> {\n return true;\n }\n\n async overrideConfig(aiConfig: Record<string, unknown>): Promise<void> {\n // For local execution, use the shared env override function\n overrideAIConfig(aiConfig);\n console.log('Config updated. Agent will be recreated on next execution.');\n }\n\n /**\n * Safely detaches the Chrome debugger without destroying the agent.\n * This removes the \"Debugger attached\" banner from the browser window\n * while keeping the agent instance intact for potential reuse.\n * Called on errors to improve user experience by cleaning up the UI.\n */\n private async detachDebuggerSafely() {\n try {\n const page = this.agent?.interface as\n | { detachDebugger?: () => Promise<void> }\n | undefined;\n await page?.detachDebugger?.();\n } catch (error) {\n console.warn('Failed to detach debugger:', error);\n }\n }\n\n async executeAction(\n actionType: string,\n value: FormValue,\n options: ExecutionOptions,\n ): Promise<unknown> {\n // If agentFactory is provided, always recreate agent with latest config before execution\n if (this.agentFactory) {\n if (this.agent) {\n console.log('Destroying old agent before execution...');\n try {\n await this.agent.destroy?.();\n } catch (error) {\n console.warn('Failed to destroy old agent:', error);\n }\n this.agent = null;\n }\n\n // Create new agent with latest config\n await this.recreateAgent();\n }\n\n // Agent must exist (either recreated or provided in constructor)\n if (!this.agent) {\n throw new Error(\n 'No agent available. Please provide either an agent instance or agentFactory.',\n );\n }\n\n const agent = this.agent;\n\n // Get actionSpace using our simplified getActionSpace method\n const actionSpace = await this.getActionSpace();\n let removeListener: (() => void) | undefined;\n\n // Reset dump at the start of execution to ensure clean state\n try {\n agent.resetDump?.();\n } catch (error: unknown) {\n console.warn('Failed to reset dump before execution:', error);\n }\n\n // Setup dump update tracking if requestId is provided\n if (options.requestId) {\n // Track current request ID to prevent stale callbacks\n this.currentRequestId = options.requestId;\n\n // Add listener and save remove function\n removeListener = agent.addDumpUpdateListener(\n (dump: string, executionDump?: ExecutionDump) => {\n // Only process if this is still the current request\n if (this.currentRequestId !== options.requestId) {\n return;\n }\n\n // Forward to external callback\n if (this.dumpUpdateCallback) {\n this.dumpUpdateCallback(dump, executionDump);\n }\n },\n );\n }\n\n try {\n let result = null;\n let executionError = null;\n\n try {\n // Call the base implementation with the original signature\n result = await executeAction(\n agent,\n actionType,\n actionSpace,\n value,\n options,\n );\n } catch (error: unknown) {\n // Capture error but don't throw yet - we need to get dump/reportHTML first\n executionError = error;\n }\n\n // Always construct response with dump and reportHTML, regardless of success/failure\n const response = {\n result,\n dump: null as unknown,\n reportHTML: null as string | null,\n error: executionError\n ? executionError instanceof Error\n ? executionError.message\n : String(executionError)\n : null,\n };\n\n // Get dump data - separate try-catch to ensure dump is retrieved even if reportHTML fails\n try {\n if (agent.dumpDataString) {\n const dumpString = agent.dumpDataString();\n if (dumpString) {\n const groupedDump =\n GroupedActionDump.fromSerializedString(dumpString);\n response.dump = groupedDump.executions?.[0] || null;\n }\n }\n } catch (error: unknown) {\n console.warn('Failed to get dump from agent:', error);\n }\n\n // Try to get reportHTML - may fail in browser environment (fs not available)\n try {\n if (agent.reportHTMLString) {\n response.reportHTML = agent.reportHTMLString() || null;\n }\n } catch (error: unknown) {\n // reportHTMLString may throw in browser environment\n // This is expected in chrome-extension, continue without reportHTML\n }\n\n // Write out action dumps - may also fail in browser environment\n try {\n if (agent.writeOutActionDumps) {\n agent.writeOutActionDumps();\n }\n } catch (error: unknown) {\n // writeOutActionDumps may fail in browser environment\n }\n\n // Don't throw the error - return it in response so caller can access dump/reportHTML\n // The caller (usePlaygroundExecution) will check response.error to determine success\n return response;\n } finally {\n // Remove listener to prevent accumulation\n if (removeListener) {\n removeListener();\n }\n }\n }\n\n /**\n * Recreate agent instance using the factory function.\n * Called automatically when executeAction is called.\n */\n private async recreateAgent(): Promise<void> {\n if (!this.agentFactory) {\n throw new Error(\n 'Cannot recreate agent: factory function not provided. Please provide agentFactory in PlaygroundConfig to enable agent recreation.',\n );\n }\n\n console.log('Creating new agent with latest config...');\n try {\n this.agent = await this.agentFactory();\n console.log('Agent created successfully');\n } catch (error) {\n console.error('Failed to create agent:', error);\n throw error;\n }\n }\n\n // Local execution task cancellation - returns dump and reportHTML before destroying\n async cancelTask(_requestId: string): Promise<{\n error?: string;\n success?: boolean;\n dump?: ExecutionDump | null;\n reportHTML?: string | null;\n }> {\n if (!this.agent) {\n return { error: 'No active agent found for this requestId' };\n }\n\n // Get execution data BEFORE destroying the agent\n let dump: ExecutionDump | null = null;\n let reportHTML: string | null = null;\n\n // Get dump data separately - don't let reportHTML errors affect dump retrieval\n // IMPORTANT: Must extract dump BEFORE agent.destroy(), as dump is stored in agent memory\n try {\n if (typeof this.agent.dumpDataString === 'function') {\n const dumpString = this.agent.dumpDataString();\n if (dumpString) {\n // dumpDataString() returns GroupedActionDump: { executions: ExecutionDump[] }\n // In Playground, each \"Run\" creates one execution, so we take executions[0]\n const groupedDump =\n GroupedActionDump.fromSerializedString(dumpString);\n dump = groupedDump.executions?.[0] ?? null;\n }\n }\n } catch (error) {\n console.warn(\n '[LocalExecutionAdapter] Failed to get dump data before cancel:',\n error,\n );\n }\n\n // Try to get reportHTML separately - this may fail in browser environment\n // where fs.readFileSync is not available\n try {\n if (typeof this.agent.reportHTMLString === 'function') {\n const html = this.agent.reportHTMLString();\n if (\n html &&\n typeof html === 'string' &&\n !html.includes('REPLACE_ME_WITH_REPORT_HTML')\n ) {\n reportHTML = html;\n }\n }\n } catch (error) {\n // reportHTMLString may throw in browser environment (fs not available)\n // This is expected, just continue with dump data only\n console.warn(\n '[LocalExecutionAdapter] reportHTMLString not available in this environment',\n );\n }\n\n try {\n await this.agent.destroy?.();\n this.agent = null; // Clear agent reference\n return { success: true, dump, reportHTML };\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n console.error(\n `[LocalExecutionAdapter] Failed to cancel agent: ${errorMessage}`,\n );\n return { error: `Failed to cancel: ${errorMessage}`, dump, reportHTML };\n }\n }\n\n /**\n * Get current execution data without resetting\n * This allows retrieving dump and report when execution is stopped\n */\n async getCurrentExecutionData(): Promise<{\n dump: ExecutionDump | null;\n reportHTML: string | null;\n }> {\n const response = {\n dump: null as ExecutionDump | null,\n reportHTML: null as string | null,\n };\n\n try {\n // Get dump data\n if (this.agent?.dumpDataString) {\n const dumpString = this.agent.dumpDataString();\n if (dumpString) {\n const groupedDump =\n GroupedActionDump.fromSerializedString(dumpString);\n response.dump = groupedDump.executions?.[0] || null;\n }\n }\n\n // Get report HTML\n if (this.agent?.reportHTMLString) {\n response.reportHTML = this.agent.reportHTMLString() || null;\n }\n } catch (error: unknown) {\n console.error('Failed to get current execution data:', error);\n }\n\n return response;\n }\n\n // Get interface information from the agent\n async getInterfaceInfo(): Promise<{\n type: string;\n description?: string;\n } | null> {\n if (!this.agent?.interface) {\n return null;\n }\n\n try {\n const type = this.agent.interface.interfaceType || 'Unknown';\n const description = this.agent.interface.describe?.() || undefined;\n\n return {\n type,\n description,\n };\n } catch (error: unknown) {\n console.error('Failed to get interface info:', error);\n return null;\n }\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","LocalExecutionAdapter","BasePlaygroundAdapter","callback","undefined","action","params","options","parseStructuredParams","error","errorMessage","context","console","page","contextPage","aiConfig","overrideAIConfig","actionType","value","Error","agent","actionSpace","removeListener","dump","executionDump","result","executionError","executeAction","response","String","dumpString","groupedDump","GroupedActionDump","_requestId","reportHTML","html","type","description","agentFactory","uuid"],"mappings":"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"}
@@ -42,6 +42,7 @@ __webpack_require__.d(__webpack_exports__, {
42
42
  const external_node_fs_namespaceObject = require("node:fs");
43
43
  const external_node_path_namespaceObject = require("node:path");
44
44
  const external_node_url_namespaceObject = require("node:url");
45
+ const core_namespaceObject = require("@midscene/core");
45
46
  const utils_namespaceObject = require("@midscene/core/utils");
46
47
  const constants_namespaceObject = require("@midscene/shared/constants");
47
48
  const env_namespaceObject = require("@midscene/shared/env");
@@ -255,7 +256,7 @@ class PlaygroundServer {
255
256
  try {
256
257
  const dumpString = this.agent.dumpDataString();
257
258
  if (dumpString) {
258
- const groupedDump = JSON.parse(dumpString);
259
+ const groupedDump = core_namespaceObject.GroupedActionDump.fromSerializedString(dumpString);
259
260
  response.dump = groupedDump.executions?.[0] || null;
260
261
  } else response.dump = null;
261
262
  response.reportHTML = this.agent.reportHTMLString() || null;
@@ -290,7 +291,7 @@ class PlaygroundServer {
290
291
  try {
291
292
  const dumpString = this.agent.dumpDataString?.();
292
293
  if (dumpString) {
293
- const groupedDump = JSON.parse(dumpString);
294
+ const groupedDump = core_namespaceObject.GroupedActionDump.fromSerializedString(dumpString);
294
295
  dump = groupedDump.executions?.[0] || null;
295
296
  }
296
297
  reportHTML = this.agent.reportHTMLString?.() || null;