@midscene/playground 1.0.1-beta-20251022061922.0 → 1.0.1-beta-20251024063839.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.
@@ -18,7 +18,7 @@ function playgroundForAgent(agent) {
18
18
  console.log(`\u{1F310} Port: ${port}`);
19
19
  if (enableCors) console.log("\uD83D\uDD13 CORS enabled");
20
20
  }
21
- const server = new server_0(webPage, agent, void 0, id);
21
+ const server = new server_0(agent, void 0, id);
22
22
  if (enableCors) server.app.use(cors(corsOptions));
23
23
  const launchedServer = await server.launch(port);
24
24
  if (verbose) console.log(`\u{2705} Playground server started on port ${port}`);
@@ -1 +1 @@
1
- {"version":3,"file":"launcher.mjs","sources":["webpack://@midscene/playground/./src/launcher.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport type { Agent, Agent as PageAgent } from '@midscene/core/agent';\nimport { PLAYGROUND_SERVER_PORT } from '@midscene/shared/constants';\nimport cors from 'cors';\nimport PlaygroundServer from './server';\n\nexport interface LaunchPlaygroundOptions {\n /**\n * Port to start the playground server on\n * @default 5800\n */\n port?: number;\n\n /**\n * Whether to automatically open the playground in browser\n * @default true\n */\n openBrowser?: boolean;\n\n /**\n * Custom browser command to open playground\n * @default 'open' on macOS, 'start' on Windows, 'xdg-open' on Linux\n */\n browserCommand?: string;\n\n /**\n * Whether to show server logs\n * @default true\n */\n verbose?: boolean;\n\n /**\n * Fixed ID for the playground server instance\n * If provided, the same ID will be used across restarts,\n * allowing chat history to persist\n * @default undefined (generates random UUID)\n */\n id?: string;\n\n /**\n * Whether to enable CORS (Cross-Origin Resource Sharing)\n * @default false\n */\n enableCors?: boolean;\n\n /**\n * CORS configuration options\n * @default { origin: '*', credentials: true } when enableCors is true\n */\n corsOptions?: {\n origin?: string | boolean | string[];\n credentials?: boolean;\n methods?: string[];\n allowedHeaders?: string[];\n };\n}\n\nexport interface LaunchPlaygroundResult {\n /**\n * The playground server instance\n */\n server: PlaygroundServer;\n\n /**\n * The server port\n */\n port: number;\n\n /**\n * The server host\n */\n host: string;\n\n /**\n * Function to gracefully shutdown the playground\n */\n close: () => Promise<void>;\n}\n\n/**\n * Create a playground launcher for a specific agent\n *\n * @example\n * ```typescript\n * import { playgroundForAgent } from '@midscene/playground';\n * import { SampleDevice, Agent } from '@midscene/core';\n *\n * const device = new SampleDevice();\n * const agent = new Agent(device);\n *\n * // Launch playground for the agent\n * const server = await playgroundForAgent(agent).launch();\n *\n * // Launch with CORS enabled\n * const serverWithCors = await playgroundForAgent(agent).launch({\n * enableCors: true,\n * corsOptions: {\n * origin: ['http://localhost:3000', 'http://localhost:8080'],\n * credentials: true\n * }\n * });\n *\n * // Later, when you want to shutdown:\n * server.close();\n * ```\n */\nexport function playgroundForAgent(agent: Agent) {\n return {\n /**\n * Launch the playground server with optional configuration\n */\n async launch(\n options: LaunchPlaygroundOptions = {},\n ): Promise<LaunchPlaygroundResult> {\n const {\n port = PLAYGROUND_SERVER_PORT,\n openBrowser = true,\n browserCommand,\n verbose = true,\n id,\n enableCors = false,\n corsOptions = { origin: '*', credentials: true },\n } = options;\n\n // Extract agent components - Agent has interface property\n const webPage = agent.interface;\n if (!webPage) {\n throw new Error('Agent must have an interface property');\n }\n\n if (verbose) {\n console.log('🚀 Starting Midscene Playground...');\n console.log(`📱 Agent: ${agent.constructor.name}`);\n console.log(`🖥️ Page: ${webPage.constructor.name}`);\n console.log(`🌐 Port: ${port}`);\n if (enableCors) {\n console.log('🔓 CORS enabled');\n }\n }\n\n // Create and launch the server with agent instances\n const server = new PlaygroundServer(\n webPage,\n agent as unknown as PageAgent,\n undefined, // staticPath - use default\n id, // Optional override ID (usually not needed now)\n );\n\n // Register CORS middleware if enabled\n if (enableCors) {\n server.app.use(cors(corsOptions));\n }\n\n const launchedServer = (await server.launch(port)) as PlaygroundServer;\n\n if (verbose) {\n console.log(`✅ Playground server started on port ${port}`);\n }\n\n const url = `http://127.0.0.1:${port}`;\n\n // Open browser if requested\n if (openBrowser) {\n await openInBrowser(url, browserCommand, verbose);\n }\n\n return {\n server: launchedServer,\n port,\n host: '127.0.0.1',\n close: async () => {\n if (verbose) {\n console.log('🛑 Shutting down Midscene Playground...');\n }\n\n try {\n await launchedServer.close();\n if (verbose) {\n console.log('✅ Playground shutdown complete');\n }\n } catch (error) {\n if (verbose) {\n console.error('❌ Error during playground shutdown:', error);\n }\n throw error;\n }\n },\n };\n },\n };\n}\n\n/**\n * Open URL in browser using platform-appropriate command\n */\nasync function openInBrowser(\n url: string,\n customCommand?: string,\n verbose = true,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let command: string;\n let args: string[];\n\n if (customCommand) {\n command = customCommand;\n args = [url];\n } else {\n // Detect platform and use appropriate command\n switch (process.platform) {\n case 'darwin':\n command = 'open';\n args = [url];\n break;\n case 'win32':\n command = 'start';\n args = ['', url]; // Empty string for title\n break;\n default:\n command = 'xdg-open';\n args = [url];\n break;\n }\n }\n\n if (verbose) {\n console.log(`🌐 Opening browser: ${command} ${args.join(' ')}`);\n }\n\n const child = spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.on('error', (error) => {\n if (verbose) {\n console.warn('⚠️ Failed to open browser automatically:', error.message);\n console.log(`🌐 Please open manually: ${url}`);\n }\n // Don't reject, just continue - browser opening is optional\n resolve();\n });\n\n child.on('close', () => {\n resolve();\n });\n\n // Don't wait for the browser process\n child.unref();\n });\n}\n"],"names":["playgroundForAgent","agent","options","port","PLAYGROUND_SERVER_PORT","openBrowser","browserCommand","verbose","id","enableCors","corsOptions","webPage","Error","console","server","PlaygroundServer","undefined","cors","launchedServer","url","openInBrowser","error","customCommand","Promise","resolve","reject","command","args","process","child","spawn"],"mappings":";;;;AA0GO,SAASA,mBAAmBC,KAAY;IAC7C,OAAO;QAIL,MAAM,QACJC,UAAmC,CAAC,CAAC;YAErC,MAAM,EACJC,OAAOC,sBAAsB,EAC7BC,cAAc,IAAI,EAClBC,cAAc,EACdC,UAAU,IAAI,EACdC,EAAE,EACFC,aAAa,KAAK,EAClBC,cAAc;gBAAE,QAAQ;gBAAK,aAAa;YAAK,CAAC,EACjD,GAAGR;YAGJ,MAAMS,UAAUV,MAAM,SAAS;YAC/B,IAAI,CAACU,SACH,MAAM,IAAIC,MAAM;YAGlB,IAAIL,SAAS;gBACXM,QAAQ,GAAG,CAAC;gBACZA,QAAQ,GAAG,CAAC,CAAC,iBAAU,EAAEZ,MAAM,WAAW,CAAC,IAAI,EAAE;gBACjDY,QAAQ,GAAG,CAAC,CAAC,wBAAU,EAAEF,QAAQ,WAAW,CAAC,IAAI,EAAE;gBACnDE,QAAQ,GAAG,CAAC,CAAC,gBAAS,EAAEV,MAAM;gBAC9B,IAAIM,YACFI,QAAQ,GAAG,CAAC;YAEhB;YAGA,MAAMC,SAAS,IAAIC,SACjBJ,SACAV,OACAe,QACAR;YAIF,IAAIC,YACFK,OAAO,GAAG,CAAC,GAAG,CAACG,KAAKP;YAGtB,MAAMQ,iBAAkB,MAAMJ,OAAO,MAAM,CAACX;YAE5C,IAAII,SACFM,QAAQ,GAAG,CAAC,CAAC,2CAAoC,EAAEV,MAAM;YAG3D,MAAMgB,MAAM,CAAC,iBAAiB,EAAEhB,MAAM;YAGtC,IAAIE,aACF,MAAMe,cAAcD,KAAKb,gBAAgBC;YAG3C,OAAO;gBACL,QAAQW;gBACRf;gBACA,MAAM;gBACN,OAAO;oBACL,IAAII,SACFM,QAAQ,GAAG,CAAC;oBAGd,IAAI;wBACF,MAAMK,eAAe,KAAK;wBAC1B,IAAIX,SACFM,QAAQ,GAAG,CAAC;oBAEhB,EAAE,OAAOQ,OAAO;wBACd,IAAId,SACFM,QAAQ,KAAK,CAAC,4CAAuCQ;wBAEvD,MAAMA;oBACR;gBACF;YACF;QACF;IACF;AACF;AAKA,eAAeD,cACbD,GAAW,EACXG,aAAsB,EACtBf,UAAU,IAAI;IAEd,OAAO,IAAIgB,QAAQ,CAACC,SAASC;QAC3B,IAAIC;QACJ,IAAIC;QAEJ,IAAIL,eAAe;YACjBI,UAAUJ;YACVK,OAAO;gBAACR;aAAI;QACd,OAEE,OAAQS,QAAQ,QAAQ;YACtB,KAAK;gBACHF,UAAU;gBACVC,OAAO;oBAACR;iBAAI;gBACZ;YACF,KAAK;gBACHO,UAAU;gBACVC,OAAO;oBAAC;oBAAIR;iBAAI;gBAChB;YACF;gBACEO,UAAU;gBACVC,OAAO;oBAACR;iBAAI;gBACZ;QACJ;QAGF,IAAIZ,SACFM,QAAQ,GAAG,CAAC,CAAC,2BAAoB,EAAEa,QAAQ,CAAC,EAAEC,KAAK,IAAI,CAAC,MAAM;QAGhE,MAAME,QAAQC,MAAMJ,SAASC,MAAM;YACjC,UAAU;YACV,OAAO;QACT;QAEAE,MAAM,EAAE,CAAC,SAAS,CAACR;YACjB,IAAId,SAAS;gBACXM,QAAQ,IAAI,CAAC,uDAA6CQ,MAAM,OAAO;gBACvER,QAAQ,GAAG,CAAC,CAAC,gCAAyB,EAAEM,KAAK;YAC/C;YAEAK;QACF;QAEAK,MAAM,EAAE,CAAC,SAAS;YAChBL;QACF;QAGAK,MAAM,KAAK;IACb;AACF"}
1
+ {"version":3,"file":"launcher.mjs","sources":["webpack://@midscene/playground/./src/launcher.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport type { Agent, Agent as PageAgent } from '@midscene/core/agent';\nimport { PLAYGROUND_SERVER_PORT } from '@midscene/shared/constants';\nimport cors from 'cors';\nimport PlaygroundServer from './server';\n\nexport interface LaunchPlaygroundOptions {\n /**\n * Port to start the playground server on\n * @default 5800\n */\n port?: number;\n\n /**\n * Whether to automatically open the playground in browser\n * @default true\n */\n openBrowser?: boolean;\n\n /**\n * Custom browser command to open playground\n * @default 'open' on macOS, 'start' on Windows, 'xdg-open' on Linux\n */\n browserCommand?: string;\n\n /**\n * Whether to show server logs\n * @default true\n */\n verbose?: boolean;\n\n /**\n * Fixed ID for the playground server instance\n * If provided, the same ID will be used across restarts,\n * allowing chat history to persist\n * @default undefined (generates random UUID)\n */\n id?: string;\n\n /**\n * Whether to enable CORS (Cross-Origin Resource Sharing)\n * @default false\n */\n enableCors?: boolean;\n\n /**\n * CORS configuration options\n * @default { origin: '*', credentials: true } when enableCors is true\n */\n corsOptions?: {\n origin?: string | boolean | string[];\n credentials?: boolean;\n methods?: string[];\n allowedHeaders?: string[];\n };\n}\n\nexport interface LaunchPlaygroundResult {\n /**\n * The playground server instance\n */\n server: PlaygroundServer;\n\n /**\n * The server port\n */\n port: number;\n\n /**\n * The server host\n */\n host: string;\n\n /**\n * Function to gracefully shutdown the playground\n */\n close: () => Promise<void>;\n}\n\n/**\n * Create a playground launcher for a specific agent\n *\n * @example\n * ```typescript\n * import { playgroundForAgent } from '@midscene/playground';\n * import { SampleDevice, Agent } from '@midscene/core';\n *\n * const device = new SampleDevice();\n * const agent = new Agent(device);\n *\n * // Launch playground for the agent\n * const server = await playgroundForAgent(agent).launch();\n *\n * // Launch with CORS enabled\n * const serverWithCors = await playgroundForAgent(agent).launch({\n * enableCors: true,\n * corsOptions: {\n * origin: ['http://localhost:3000', 'http://localhost:8080'],\n * credentials: true\n * }\n * });\n *\n * // Later, when you want to shutdown:\n * server.close();\n * ```\n */\nexport function playgroundForAgent(agent: Agent) {\n return {\n /**\n * Launch the playground server with optional configuration\n */\n async launch(\n options: LaunchPlaygroundOptions = {},\n ): Promise<LaunchPlaygroundResult> {\n const {\n port = PLAYGROUND_SERVER_PORT,\n openBrowser = true,\n browserCommand,\n verbose = true,\n id,\n enableCors = false,\n corsOptions = { origin: '*', credentials: true },\n } = options;\n\n // Extract agent components - Agent has interface property\n const webPage = agent.interface;\n if (!webPage) {\n throw new Error('Agent must have an interface property');\n }\n\n if (verbose) {\n console.log('🚀 Starting Midscene Playground...');\n console.log(`📱 Agent: ${agent.constructor.name}`);\n console.log(`🖥️ Page: ${webPage.constructor.name}`);\n console.log(`🌐 Port: ${port}`);\n if (enableCors) {\n console.log('🔓 CORS enabled');\n }\n }\n\n // Create and launch the server with agent instance\n const server = new PlaygroundServer(\n agent as unknown as PageAgent,\n undefined, // staticPath - use default\n id, // Optional override ID (usually not needed now)\n );\n\n // Register CORS middleware if enabled\n if (enableCors) {\n server.app.use(cors(corsOptions));\n }\n\n const launchedServer = (await server.launch(port)) as PlaygroundServer;\n\n if (verbose) {\n console.log(`✅ Playground server started on port ${port}`);\n }\n\n const url = `http://127.0.0.1:${port}`;\n\n // Open browser if requested\n if (openBrowser) {\n await openInBrowser(url, browserCommand, verbose);\n }\n\n return {\n server: launchedServer,\n port,\n host: '127.0.0.1',\n close: async () => {\n if (verbose) {\n console.log('🛑 Shutting down Midscene Playground...');\n }\n\n try {\n await launchedServer.close();\n if (verbose) {\n console.log('✅ Playground shutdown complete');\n }\n } catch (error) {\n if (verbose) {\n console.error('❌ Error during playground shutdown:', error);\n }\n throw error;\n }\n },\n };\n },\n };\n}\n\n/**\n * Open URL in browser using platform-appropriate command\n */\nasync function openInBrowser(\n url: string,\n customCommand?: string,\n verbose = true,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let command: string;\n let args: string[];\n\n if (customCommand) {\n command = customCommand;\n args = [url];\n } else {\n // Detect platform and use appropriate command\n switch (process.platform) {\n case 'darwin':\n command = 'open';\n args = [url];\n break;\n case 'win32':\n command = 'start';\n args = ['', url]; // Empty string for title\n break;\n default:\n command = 'xdg-open';\n args = [url];\n break;\n }\n }\n\n if (verbose) {\n console.log(`🌐 Opening browser: ${command} ${args.join(' ')}`);\n }\n\n const child = spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.on('error', (error) => {\n if (verbose) {\n console.warn('⚠️ Failed to open browser automatically:', error.message);\n console.log(`🌐 Please open manually: ${url}`);\n }\n // Don't reject, just continue - browser opening is optional\n resolve();\n });\n\n child.on('close', () => {\n resolve();\n });\n\n // Don't wait for the browser process\n child.unref();\n });\n}\n"],"names":["playgroundForAgent","agent","options","port","PLAYGROUND_SERVER_PORT","openBrowser","browserCommand","verbose","id","enableCors","corsOptions","webPage","Error","console","server","PlaygroundServer","undefined","cors","launchedServer","url","openInBrowser","error","customCommand","Promise","resolve","reject","command","args","process","child","spawn"],"mappings":";;;;AA0GO,SAASA,mBAAmBC,KAAY;IAC7C,OAAO;QAIL,MAAM,QACJC,UAAmC,CAAC,CAAC;YAErC,MAAM,EACJC,OAAOC,sBAAsB,EAC7BC,cAAc,IAAI,EAClBC,cAAc,EACdC,UAAU,IAAI,EACdC,EAAE,EACFC,aAAa,KAAK,EAClBC,cAAc;gBAAE,QAAQ;gBAAK,aAAa;YAAK,CAAC,EACjD,GAAGR;YAGJ,MAAMS,UAAUV,MAAM,SAAS;YAC/B,IAAI,CAACU,SACH,MAAM,IAAIC,MAAM;YAGlB,IAAIL,SAAS;gBACXM,QAAQ,GAAG,CAAC;gBACZA,QAAQ,GAAG,CAAC,CAAC,iBAAU,EAAEZ,MAAM,WAAW,CAAC,IAAI,EAAE;gBACjDY,QAAQ,GAAG,CAAC,CAAC,wBAAU,EAAEF,QAAQ,WAAW,CAAC,IAAI,EAAE;gBACnDE,QAAQ,GAAG,CAAC,CAAC,gBAAS,EAAEV,MAAM;gBAC9B,IAAIM,YACFI,QAAQ,GAAG,CAAC;YAEhB;YAGA,MAAMC,SAAS,IAAIC,SACjBd,OACAe,QACAR;YAIF,IAAIC,YACFK,OAAO,GAAG,CAAC,GAAG,CAACG,KAAKP;YAGtB,MAAMQ,iBAAkB,MAAMJ,OAAO,MAAM,CAACX;YAE5C,IAAII,SACFM,QAAQ,GAAG,CAAC,CAAC,2CAAoC,EAAEV,MAAM;YAG3D,MAAMgB,MAAM,CAAC,iBAAiB,EAAEhB,MAAM;YAGtC,IAAIE,aACF,MAAMe,cAAcD,KAAKb,gBAAgBC;YAG3C,OAAO;gBACL,QAAQW;gBACRf;gBACA,MAAM;gBACN,OAAO;oBACL,IAAII,SACFM,QAAQ,GAAG,CAAC;oBAGd,IAAI;wBACF,MAAMK,eAAe,KAAK;wBAC1B,IAAIX,SACFM,QAAQ,GAAG,CAAC;oBAEhB,EAAE,OAAOQ,OAAO;wBACd,IAAId,SACFM,QAAQ,KAAK,CAAC,4CAAuCQ;wBAEvD,MAAMA;oBACR;gBACF;YACF;QACF;IACF;AACF;AAKA,eAAeD,cACbD,GAAW,EACXG,aAAsB,EACtBf,UAAU,IAAI;IAEd,OAAO,IAAIgB,QAAQ,CAACC,SAASC;QAC3B,IAAIC;QACJ,IAAIC;QAEJ,IAAIL,eAAe;YACjBI,UAAUJ;YACVK,OAAO;gBAACR;aAAI;QACd,OAEE,OAAQS,QAAQ,QAAQ;YACtB,KAAK;gBACHF,UAAU;gBACVC,OAAO;oBAACR;iBAAI;gBACZ;YACF,KAAK;gBACHO,UAAU;gBACVC,OAAO;oBAAC;oBAAIR;iBAAI;gBAChB;YACF;gBACEO,UAAU;gBACVC,OAAO;oBAACR;iBAAI;gBACZ;QACJ;QAGF,IAAIZ,SACFM,QAAQ,GAAG,CAAC,CAAC,2BAAoB,EAAEa,QAAQ,CAAC,EAAEC,KAAK,IAAI,CAAC,MAAM;QAGhE,MAAME,QAAQC,MAAMJ,SAASC,MAAM;YACjC,UAAU;YACV,OAAO;QACT;QAEAE,MAAM,EAAE,CAAC,SAAS,CAACR;YACjB,IAAId,SAAS;gBACXM,QAAQ,IAAI,CAAC,uDAA6CQ,MAAM,OAAO;gBACvER,QAAQ,GAAG,CAAC,CAAC,gCAAyB,EAAEM,KAAK;YAC/C;YAEAK;QACF;QAEAK,MAAM,EAAE,CAAC,SAAS;YAChBL;QACF;QAGAK,MAAM,KAAK;IACb;AACF"}
@@ -40,8 +40,8 @@ class PlaygroundServer {
40
40
  }));
41
41
  this._app.use((req, _res, next)=>{
42
42
  const { context } = req.body || {};
43
- if (context && 'updateContext' in this.page && 'function' == typeof this.page.updateContext) {
44
- this.page.updateContext(context);
43
+ if (context && 'updateContext' in this.agent.interface && 'function' == typeof this.agent.interface.updateContext) {
44
+ this.agent.interface.updateContext(context);
45
45
  console.log('Context updated by PlaygroundServer middleware');
46
46
  }
47
47
  next();
@@ -60,6 +60,22 @@ class PlaygroundServer {
60
60
  writeFileSync(tmpFile, context);
61
61
  return tmpFile;
62
62
  }
63
+ async recreateAgent() {
64
+ if (!this.agentFactory) return void console.warn('Cannot recreate agent: factory function not provided. Agent recreation is only available when using factory mode.');
65
+ console.log('Recreating agent to cancel current task...');
66
+ try {
67
+ if (this.agent && 'function' == typeof this.agent.destroy) await this.agent.destroy();
68
+ } catch (error) {
69
+ console.warn('Failed to destroy old agent:', error);
70
+ }
71
+ try {
72
+ this.agent = await this.agentFactory();
73
+ console.log('Agent recreated successfully');
74
+ } catch (error) {
75
+ console.error('Failed to recreate agent:', error);
76
+ throw error;
77
+ }
78
+ }
63
79
  setupRoutes() {
64
80
  this._app.get('/status', async (req, res)=>{
65
81
  res.send({
@@ -87,7 +103,7 @@ class PlaygroundServer {
87
103
  this._app.post('/action-space', async (req, res)=>{
88
104
  try {
89
105
  let actionSpace = [];
90
- actionSpace = await this.page.actionSpace();
106
+ actionSpace = await this.agent.interface.actionSpace();
91
107
  const processedActionSpace = actionSpace.map((action)=>{
92
108
  if (action && 'object' == typeof action && 'paramSchema' in action) {
93
109
  const typedAction = action;
@@ -136,7 +152,12 @@ class PlaygroundServer {
136
152
  if (!type) return res.status(400).json({
137
153
  error: 'type is required'
138
154
  });
155
+ if (this.currentTaskId) return res.status(409).json({
156
+ error: 'Another task is already running',
157
+ currentTaskId: this.currentTaskId
158
+ });
139
159
  if (requestId) {
160
+ this.currentTaskId = requestId;
140
161
  this.taskProgressTips[requestId] = '';
141
162
  this.agent.onTaskStartTip = (tip)=>{
142
163
  this.taskProgressTips[requestId] = tip;
@@ -151,7 +172,7 @@ class PlaygroundServer {
151
172
  };
152
173
  const startTime = Date.now();
153
174
  try {
154
- const actionSpace = await this.page.actionSpace();
175
+ const actionSpace = await this.agent.interface.actionSpace();
155
176
  const value = {
156
177
  type,
157
178
  prompt,
@@ -178,7 +199,10 @@ class PlaygroundServer {
178
199
  const timeCost = Date.now() - startTime;
179
200
  if (response.error) console.error(`handle request failed after ${timeCost}ms: requestId: ${requestId}, ${response.error}`);
180
201
  else console.log(`handle request done after ${timeCost}ms: requestId: ${requestId}`);
181
- if (requestId) delete this.taskProgressTips[requestId];
202
+ if (requestId) {
203
+ delete this.taskProgressTips[requestId];
204
+ if (this.currentTaskId === requestId) this.currentTaskId = null;
205
+ }
182
206
  });
183
207
  this._app.post('/cancel/:requestId', async (req, res)=>{
184
208
  const { requestId } = req.params;
@@ -186,9 +210,17 @@ class PlaygroundServer {
186
210
  error: 'requestId is required'
187
211
  });
188
212
  try {
189
- if (this.taskProgressTips[requestId]) delete this.taskProgressTips[requestId];
213
+ if (this.currentTaskId !== requestId) return res.json({
214
+ status: 'not_found',
215
+ message: 'Task not found or already completed'
216
+ });
217
+ console.log(`Cancelling task: ${requestId}`);
218
+ await this.recreateAgent();
219
+ delete this.taskProgressTips[requestId];
220
+ this.currentTaskId = null;
190
221
  res.json({
191
- status: 'cancelled'
222
+ status: 'cancelled',
223
+ message: 'Task cancelled successfully by recreating agent'
192
224
  });
193
225
  } catch (error) {
194
226
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
@@ -200,10 +232,10 @@ class PlaygroundServer {
200
232
  });
201
233
  this._app.get('/screenshot', async (_req, res)=>{
202
234
  try {
203
- if ('function' != typeof this.page.screenshotBase64) return res.status(500).json({
235
+ if ('function' != typeof this.agent.interface.screenshotBase64) return res.status(500).json({
204
236
  error: 'Screenshot method not available on current interface'
205
237
  });
206
- const base64Screenshot = await this.page.screenshotBase64();
238
+ const base64Screenshot = await this.agent.interface.screenshotBase64();
207
239
  res.json({
208
240
  screenshot: base64Screenshot,
209
241
  timestamp: Date.now()
@@ -218,9 +250,9 @@ class PlaygroundServer {
218
250
  });
219
251
  this._app.get('/interface-info', async (_req, res)=>{
220
252
  try {
221
- var _this_page_describe, _this_page;
222
- const type = this.page.interfaceType || 'Unknown';
223
- const description = (null == (_this_page_describe = (_this_page = this.page).describe) ? void 0 : _this_page_describe.call(_this_page)) || void 0;
253
+ var _this_agent_interface_describe, _this_agent_interface;
254
+ const type = this.agent.interface.interfaceType || 'Unknown';
255
+ const description = (null == (_this_agent_interface_describe = (_this_agent_interface = this.agent.interface).describe) ? void 0 : _this_agent_interface_describe.call(_this_agent_interface)) || void 0;
224
256
  res.json({
225
257
  type,
226
258
  description
@@ -288,6 +320,11 @@ class PlaygroundServer {
288
320
  }
289
321
  }
290
322
  async launch(port) {
323
+ if (this.agentFactory) {
324
+ console.log('Initializing agent from factory function...');
325
+ this.agent = await this.agentFactory();
326
+ console.log('Agent initialized successfully');
327
+ }
291
328
  this.initializeApp();
292
329
  this.port = port || defaultPort;
293
330
  return new Promise((resolve)=>{
@@ -316,24 +353,30 @@ class PlaygroundServer {
316
353
  } else resolve();
317
354
  });
318
355
  }
319
- constructor(page, agent, staticPath = STATIC_PATH, id){
356
+ constructor(agent, staticPath = STATIC_PATH, id){
320
357
  _define_property(this, "_app", void 0);
321
358
  _define_property(this, "tmpDir", void 0);
322
359
  _define_property(this, "server", void 0);
323
360
  _define_property(this, "port", void 0);
324
- _define_property(this, "page", void 0);
325
361
  _define_property(this, "agent", void 0);
326
362
  _define_property(this, "staticPath", void 0);
327
363
  _define_property(this, "taskProgressTips", void 0);
328
364
  _define_property(this, "id", void 0);
329
365
  _define_property(this, "_initialized", false);
366
+ _define_property(this, "agentFactory", void 0);
367
+ _define_property(this, "currentTaskId", null);
330
368
  this._app = express();
331
369
  this.tmpDir = getTmpDir();
332
- this.page = page;
333
- this.agent = agent;
334
370
  this.staticPath = staticPath;
335
371
  this.taskProgressTips = {};
336
372
  this.id = id || utils_uuid();
373
+ if ('function' == typeof agent) {
374
+ this.agentFactory = agent;
375
+ this.agent = null;
376
+ } else {
377
+ this.agent = agent;
378
+ this.agentFactory = null;
379
+ }
337
380
  }
338
381
  }
339
382
  const server = PlaygroundServer;
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","sources":["webpack://@midscene/playground/./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 { Agent as PageAgent } from '@midscene/core/agent';\nimport type { AbstractInterface } from '@midscene/core/device';\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 page: AbstractInterface;\n agent: PageAgent;\n staticPath: string;\n taskProgressTips: Record<string, string>;\n id: string; // Unique identifier for this server instance\n\n private _initialized = false;\n\n constructor(\n page: AbstractInterface,\n agent: PageAgent,\n staticPath = STATIC_PATH,\n id?: string, // Optional override ID\n ) {\n this._app = express();\n this.tmpDir = getTmpDir()!;\n this.page = page;\n this.agent = agent;\n this.staticPath = staticPath;\n this.taskProgressTips = {};\n // Use provided ID, or generate random UUID for each startup\n this.id = id || uuid();\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(page, 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.page &&\n typeof this.page.updateContext === 'function'\n ) {\n this.page.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 * 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 res.json({\n tip: this.taskProgressTips[requestId] || '',\n });\n },\n );\n\n this._app.post('/action-space', async (req: Request, res: Response) => {\n try {\n let actionSpace = [];\n\n actionSpace = await this.page.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 } = req.body;\n\n if (!type) {\n return res.status(400).json({\n error: 'type is required',\n });\n }\n\n if (requestId) {\n this.taskProgressTips[requestId] = '';\n\n this.agent.onTaskStartTip = (tip: string) => {\n this.taskProgressTips[requestId] = tip;\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 = await this.page.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 },\n );\n } catch (error: unknown) {\n response.error = formatErrorMessage(error);\n }\n\n try {\n response.dump = JSON.parse(this.agent.dumpDataString());\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 progress tip after execution completes\n if (requestId) {\n delete this.taskProgressTips[requestId];\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 // Since we only have one agent, just clear the task progress tip\n if (this.taskProgressTips[requestId]) {\n delete this.taskProgressTips[requestId];\n }\n res.json({ status: 'cancelled' });\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.page.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.page.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.page.interfaceType || 'Unknown';\n const description = this.page.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 return res.json({\n status: 'ok',\n message: 'AI config updated successfully',\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 // Initialize routes now, after any middleware has been added\n this.initializeApp();\n\n this.port = port || defaultPort;\n\n // Keep the random UUID as-is, no need to regenerate\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.taskProgressTips = {};\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","contextFile","existsSync","readFileSync","requestId","actionSpace","processedActionSpace","action","typedAction","processedSchema","e","actionName","error","type","prompt","params","deepThink","screenshotIncluded","domIncluded","tip","response","startTime","Date","value","executeAction","formatErrorMessage","JSON","timeCost","_req","base64Screenshot","_this_page","description","undefined","aiConfig","Object","overrideAIConfig","htmlPath","html","scrcpyPort","global","configScript","port","Promise","resolve","serverPort","reject","page","agent","staticPath","id","getTmpDir"],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","sources":["webpack://@midscene/playground/./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 { 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 taskProgressTips: Record<string, string>;\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.taskProgressTips = {};\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 console.warn(\n 'Cannot recreate agent: factory function not provided. Agent recreation is only available when using factory mode.',\n );\n return;\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 res.json({\n tip: this.taskProgressTips[requestId] || '',\n });\n },\n );\n\n this._app.post('/action-space', async (req: Request, res: Response) => {\n try {\n let actionSpace = [];\n\n actionSpace = await 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 } = req.body;\n\n if (!type) {\n return res.status(400).json({\n error: 'type is required',\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.taskProgressTips[requestId] = '';\n\n this.agent.onTaskStartTip = (tip: string) => {\n this.taskProgressTips[requestId] = tip;\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 = await 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 },\n );\n } catch (error: unknown) {\n response.error = formatErrorMessage(error);\n }\n\n try {\n response.dump = JSON.parse(this.agent.dumpDataString());\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 progress tip and unlock after execution completes\n if (requestId) {\n delete this.taskProgressTips[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 // Recreate agent to cancel the current task\n await this.recreateAgent();\n\n // Clean up\n delete this.taskProgressTips[requestId];\n this.currentTaskId = null;\n\n res.json({\n status: 'cancelled',\n message: 'Task cancelled successfully by recreating agent',\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 return res.json({\n status: 'ok',\n message: 'AI config updated successfully',\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.taskProgressTips = {};\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","actionSpace","processedActionSpace","action","typedAction","processedSchema","e","actionName","type","prompt","params","deepThink","screenshotIncluded","domIncluded","tip","response","startTime","Date","value","executeAction","formatErrorMessage","JSON","timeCost","_req","base64Screenshot","_this_agent_interface","description","undefined","aiConfig","Object","overrideAIConfig","htmlPath","html","scrcpyPort","global","configScript","port","Promise","resolve","serverPort","reject","agent","staticPath","id","getTmpDir"],"mappings":"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"}
@@ -57,7 +57,7 @@ function playgroundForAgent(agent) {
57
57
  console.log(`\u{1F310} Port: ${port}`);
58
58
  if (enableCors) console.log("\uD83D\uDD13 CORS enabled");
59
59
  }
60
- const server = new (external_server_js_default())(webPage, agent, void 0, id);
60
+ const server = new (external_server_js_default())(agent, void 0, id);
61
61
  if (enableCors) server.app.use(external_cors_default()(corsOptions));
62
62
  const launchedServer = await server.launch(port);
63
63
  if (verbose) console.log(`\u{2705} Playground server started on port ${port}`);
@@ -1 +1 @@
1
- {"version":3,"file":"launcher.js","sources":["webpack://@midscene/playground/webpack/runtime/compat_get_default_export","webpack://@midscene/playground/webpack/runtime/define_property_getters","webpack://@midscene/playground/webpack/runtime/has_own_property","webpack://@midscene/playground/webpack/runtime/make_namespace_object","webpack://@midscene/playground/./src/launcher.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__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 { spawn } from 'node:child_process';\nimport type { Agent, Agent as PageAgent } from '@midscene/core/agent';\nimport { PLAYGROUND_SERVER_PORT } from '@midscene/shared/constants';\nimport cors from 'cors';\nimport PlaygroundServer from './server';\n\nexport interface LaunchPlaygroundOptions {\n /**\n * Port to start the playground server on\n * @default 5800\n */\n port?: number;\n\n /**\n * Whether to automatically open the playground in browser\n * @default true\n */\n openBrowser?: boolean;\n\n /**\n * Custom browser command to open playground\n * @default 'open' on macOS, 'start' on Windows, 'xdg-open' on Linux\n */\n browserCommand?: string;\n\n /**\n * Whether to show server logs\n * @default true\n */\n verbose?: boolean;\n\n /**\n * Fixed ID for the playground server instance\n * If provided, the same ID will be used across restarts,\n * allowing chat history to persist\n * @default undefined (generates random UUID)\n */\n id?: string;\n\n /**\n * Whether to enable CORS (Cross-Origin Resource Sharing)\n * @default false\n */\n enableCors?: boolean;\n\n /**\n * CORS configuration options\n * @default { origin: '*', credentials: true } when enableCors is true\n */\n corsOptions?: {\n origin?: string | boolean | string[];\n credentials?: boolean;\n methods?: string[];\n allowedHeaders?: string[];\n };\n}\n\nexport interface LaunchPlaygroundResult {\n /**\n * The playground server instance\n */\n server: PlaygroundServer;\n\n /**\n * The server port\n */\n port: number;\n\n /**\n * The server host\n */\n host: string;\n\n /**\n * Function to gracefully shutdown the playground\n */\n close: () => Promise<void>;\n}\n\n/**\n * Create a playground launcher for a specific agent\n *\n * @example\n * ```typescript\n * import { playgroundForAgent } from '@midscene/playground';\n * import { SampleDevice, Agent } from '@midscene/core';\n *\n * const device = new SampleDevice();\n * const agent = new Agent(device);\n *\n * // Launch playground for the agent\n * const server = await playgroundForAgent(agent).launch();\n *\n * // Launch with CORS enabled\n * const serverWithCors = await playgroundForAgent(agent).launch({\n * enableCors: true,\n * corsOptions: {\n * origin: ['http://localhost:3000', 'http://localhost:8080'],\n * credentials: true\n * }\n * });\n *\n * // Later, when you want to shutdown:\n * server.close();\n * ```\n */\nexport function playgroundForAgent(agent: Agent) {\n return {\n /**\n * Launch the playground server with optional configuration\n */\n async launch(\n options: LaunchPlaygroundOptions = {},\n ): Promise<LaunchPlaygroundResult> {\n const {\n port = PLAYGROUND_SERVER_PORT,\n openBrowser = true,\n browserCommand,\n verbose = true,\n id,\n enableCors = false,\n corsOptions = { origin: '*', credentials: true },\n } = options;\n\n // Extract agent components - Agent has interface property\n const webPage = agent.interface;\n if (!webPage) {\n throw new Error('Agent must have an interface property');\n }\n\n if (verbose) {\n console.log('🚀 Starting Midscene Playground...');\n console.log(`📱 Agent: ${agent.constructor.name}`);\n console.log(`🖥️ Page: ${webPage.constructor.name}`);\n console.log(`🌐 Port: ${port}`);\n if (enableCors) {\n console.log('🔓 CORS enabled');\n }\n }\n\n // Create and launch the server with agent instances\n const server = new PlaygroundServer(\n webPage,\n agent as unknown as PageAgent,\n undefined, // staticPath - use default\n id, // Optional override ID (usually not needed now)\n );\n\n // Register CORS middleware if enabled\n if (enableCors) {\n server.app.use(cors(corsOptions));\n }\n\n const launchedServer = (await server.launch(port)) as PlaygroundServer;\n\n if (verbose) {\n console.log(`✅ Playground server started on port ${port}`);\n }\n\n const url = `http://127.0.0.1:${port}`;\n\n // Open browser if requested\n if (openBrowser) {\n await openInBrowser(url, browserCommand, verbose);\n }\n\n return {\n server: launchedServer,\n port,\n host: '127.0.0.1',\n close: async () => {\n if (verbose) {\n console.log('🛑 Shutting down Midscene Playground...');\n }\n\n try {\n await launchedServer.close();\n if (verbose) {\n console.log('✅ Playground shutdown complete');\n }\n } catch (error) {\n if (verbose) {\n console.error('❌ Error during playground shutdown:', error);\n }\n throw error;\n }\n },\n };\n },\n };\n}\n\n/**\n * Open URL in browser using platform-appropriate command\n */\nasync function openInBrowser(\n url: string,\n customCommand?: string,\n verbose = true,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let command: string;\n let args: string[];\n\n if (customCommand) {\n command = customCommand;\n args = [url];\n } else {\n // Detect platform and use appropriate command\n switch (process.platform) {\n case 'darwin':\n command = 'open';\n args = [url];\n break;\n case 'win32':\n command = 'start';\n args = ['', url]; // Empty string for title\n break;\n default:\n command = 'xdg-open';\n args = [url];\n break;\n }\n }\n\n if (verbose) {\n console.log(`🌐 Opening browser: ${command} ${args.join(' ')}`);\n }\n\n const child = spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.on('error', (error) => {\n if (verbose) {\n console.warn('⚠️ Failed to open browser automatically:', error.message);\n console.log(`🌐 Please open manually: ${url}`);\n }\n // Don't reject, just continue - browser opening is optional\n resolve();\n });\n\n child.on('close', () => {\n resolve();\n });\n\n // Don't wait for the browser process\n child.unref();\n });\n}\n"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","playgroundForAgent","agent","options","port","PLAYGROUND_SERVER_PORT","openBrowser","browserCommand","verbose","id","enableCors","corsOptions","webPage","Error","console","server","PlaygroundServer","undefined","cors","launchedServer","url","openInBrowser","error","customCommand","Promise","resolve","reject","command","args","process","child","spawn"],"mappings":"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"}
1
+ {"version":3,"file":"launcher.js","sources":["webpack://@midscene/playground/webpack/runtime/compat_get_default_export","webpack://@midscene/playground/webpack/runtime/define_property_getters","webpack://@midscene/playground/webpack/runtime/has_own_property","webpack://@midscene/playground/webpack/runtime/make_namespace_object","webpack://@midscene/playground/./src/launcher.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__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 { spawn } from 'node:child_process';\nimport type { Agent, Agent as PageAgent } from '@midscene/core/agent';\nimport { PLAYGROUND_SERVER_PORT } from '@midscene/shared/constants';\nimport cors from 'cors';\nimport PlaygroundServer from './server';\n\nexport interface LaunchPlaygroundOptions {\n /**\n * Port to start the playground server on\n * @default 5800\n */\n port?: number;\n\n /**\n * Whether to automatically open the playground in browser\n * @default true\n */\n openBrowser?: boolean;\n\n /**\n * Custom browser command to open playground\n * @default 'open' on macOS, 'start' on Windows, 'xdg-open' on Linux\n */\n browserCommand?: string;\n\n /**\n * Whether to show server logs\n * @default true\n */\n verbose?: boolean;\n\n /**\n * Fixed ID for the playground server instance\n * If provided, the same ID will be used across restarts,\n * allowing chat history to persist\n * @default undefined (generates random UUID)\n */\n id?: string;\n\n /**\n * Whether to enable CORS (Cross-Origin Resource Sharing)\n * @default false\n */\n enableCors?: boolean;\n\n /**\n * CORS configuration options\n * @default { origin: '*', credentials: true } when enableCors is true\n */\n corsOptions?: {\n origin?: string | boolean | string[];\n credentials?: boolean;\n methods?: string[];\n allowedHeaders?: string[];\n };\n}\n\nexport interface LaunchPlaygroundResult {\n /**\n * The playground server instance\n */\n server: PlaygroundServer;\n\n /**\n * The server port\n */\n port: number;\n\n /**\n * The server host\n */\n host: string;\n\n /**\n * Function to gracefully shutdown the playground\n */\n close: () => Promise<void>;\n}\n\n/**\n * Create a playground launcher for a specific agent\n *\n * @example\n * ```typescript\n * import { playgroundForAgent } from '@midscene/playground';\n * import { SampleDevice, Agent } from '@midscene/core';\n *\n * const device = new SampleDevice();\n * const agent = new Agent(device);\n *\n * // Launch playground for the agent\n * const server = await playgroundForAgent(agent).launch();\n *\n * // Launch with CORS enabled\n * const serverWithCors = await playgroundForAgent(agent).launch({\n * enableCors: true,\n * corsOptions: {\n * origin: ['http://localhost:3000', 'http://localhost:8080'],\n * credentials: true\n * }\n * });\n *\n * // Later, when you want to shutdown:\n * server.close();\n * ```\n */\nexport function playgroundForAgent(agent: Agent) {\n return {\n /**\n * Launch the playground server with optional configuration\n */\n async launch(\n options: LaunchPlaygroundOptions = {},\n ): Promise<LaunchPlaygroundResult> {\n const {\n port = PLAYGROUND_SERVER_PORT,\n openBrowser = true,\n browserCommand,\n verbose = true,\n id,\n enableCors = false,\n corsOptions = { origin: '*', credentials: true },\n } = options;\n\n // Extract agent components - Agent has interface property\n const webPage = agent.interface;\n if (!webPage) {\n throw new Error('Agent must have an interface property');\n }\n\n if (verbose) {\n console.log('🚀 Starting Midscene Playground...');\n console.log(`📱 Agent: ${agent.constructor.name}`);\n console.log(`🖥️ Page: ${webPage.constructor.name}`);\n console.log(`🌐 Port: ${port}`);\n if (enableCors) {\n console.log('🔓 CORS enabled');\n }\n }\n\n // Create and launch the server with agent instance\n const server = new PlaygroundServer(\n agent as unknown as PageAgent,\n undefined, // staticPath - use default\n id, // Optional override ID (usually not needed now)\n );\n\n // Register CORS middleware if enabled\n if (enableCors) {\n server.app.use(cors(corsOptions));\n }\n\n const launchedServer = (await server.launch(port)) as PlaygroundServer;\n\n if (verbose) {\n console.log(`✅ Playground server started on port ${port}`);\n }\n\n const url = `http://127.0.0.1:${port}`;\n\n // Open browser if requested\n if (openBrowser) {\n await openInBrowser(url, browserCommand, verbose);\n }\n\n return {\n server: launchedServer,\n port,\n host: '127.0.0.1',\n close: async () => {\n if (verbose) {\n console.log('🛑 Shutting down Midscene Playground...');\n }\n\n try {\n await launchedServer.close();\n if (verbose) {\n console.log('✅ Playground shutdown complete');\n }\n } catch (error) {\n if (verbose) {\n console.error('❌ Error during playground shutdown:', error);\n }\n throw error;\n }\n },\n };\n },\n };\n}\n\n/**\n * Open URL in browser using platform-appropriate command\n */\nasync function openInBrowser(\n url: string,\n customCommand?: string,\n verbose = true,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let command: string;\n let args: string[];\n\n if (customCommand) {\n command = customCommand;\n args = [url];\n } else {\n // Detect platform and use appropriate command\n switch (process.platform) {\n case 'darwin':\n command = 'open';\n args = [url];\n break;\n case 'win32':\n command = 'start';\n args = ['', url]; // Empty string for title\n break;\n default:\n command = 'xdg-open';\n args = [url];\n break;\n }\n }\n\n if (verbose) {\n console.log(`🌐 Opening browser: ${command} ${args.join(' ')}`);\n }\n\n const child = spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.on('error', (error) => {\n if (verbose) {\n console.warn('⚠️ Failed to open browser automatically:', error.message);\n console.log(`🌐 Please open manually: ${url}`);\n }\n // Don't reject, just continue - browser opening is optional\n resolve();\n });\n\n child.on('close', () => {\n resolve();\n });\n\n // Don't wait for the browser process\n child.unref();\n });\n}\n"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","playgroundForAgent","agent","options","port","PLAYGROUND_SERVER_PORT","openBrowser","browserCommand","verbose","id","enableCors","corsOptions","webPage","Error","console","server","PlaygroundServer","undefined","cors","launchedServer","url","openInBrowser","error","customCommand","Promise","resolve","reject","command","args","process","child","spawn"],"mappings":"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"}
@@ -82,8 +82,8 @@ class PlaygroundServer {
82
82
  }));
83
83
  this._app.use((req, _res, next)=>{
84
84
  const { context } = req.body || {};
85
- if (context && 'updateContext' in this.page && 'function' == typeof this.page.updateContext) {
86
- this.page.updateContext(context);
85
+ if (context && 'updateContext' in this.agent.interface && 'function' == typeof this.agent.interface.updateContext) {
86
+ this.agent.interface.updateContext(context);
87
87
  console.log('Context updated by PlaygroundServer middleware');
88
88
  }
89
89
  next();
@@ -102,6 +102,22 @@ class PlaygroundServer {
102
102
  (0, external_node_fs_namespaceObject.writeFileSync)(tmpFile, context);
103
103
  return tmpFile;
104
104
  }
105
+ async recreateAgent() {
106
+ if (!this.agentFactory) return void console.warn('Cannot recreate agent: factory function not provided. Agent recreation is only available when using factory mode.');
107
+ console.log('Recreating agent to cancel current task...');
108
+ try {
109
+ if (this.agent && 'function' == typeof this.agent.destroy) await this.agent.destroy();
110
+ } catch (error) {
111
+ console.warn('Failed to destroy old agent:', error);
112
+ }
113
+ try {
114
+ this.agent = await this.agentFactory();
115
+ console.log('Agent recreated successfully');
116
+ } catch (error) {
117
+ console.error('Failed to recreate agent:', error);
118
+ throw error;
119
+ }
120
+ }
105
121
  setupRoutes() {
106
122
  this._app.get('/status', async (req, res)=>{
107
123
  res.send({
@@ -129,7 +145,7 @@ class PlaygroundServer {
129
145
  this._app.post('/action-space', async (req, res)=>{
130
146
  try {
131
147
  let actionSpace = [];
132
- actionSpace = await this.page.actionSpace();
148
+ actionSpace = await this.agent.interface.actionSpace();
133
149
  const processedActionSpace = actionSpace.map((action)=>{
134
150
  if (action && 'object' == typeof action && 'paramSchema' in action) {
135
151
  const typedAction = action;
@@ -178,7 +194,12 @@ class PlaygroundServer {
178
194
  if (!type) return res.status(400).json({
179
195
  error: 'type is required'
180
196
  });
197
+ if (this.currentTaskId) return res.status(409).json({
198
+ error: 'Another task is already running',
199
+ currentTaskId: this.currentTaskId
200
+ });
181
201
  if (requestId) {
202
+ this.currentTaskId = requestId;
182
203
  this.taskProgressTips[requestId] = '';
183
204
  this.agent.onTaskStartTip = (tip)=>{
184
205
  this.taskProgressTips[requestId] = tip;
@@ -193,7 +214,7 @@ class PlaygroundServer {
193
214
  };
194
215
  const startTime = Date.now();
195
216
  try {
196
- const actionSpace = await this.page.actionSpace();
217
+ const actionSpace = await this.agent.interface.actionSpace();
197
218
  const value = {
198
219
  type,
199
220
  prompt,
@@ -220,7 +241,10 @@ class PlaygroundServer {
220
241
  const timeCost = Date.now() - startTime;
221
242
  if (response.error) console.error(`handle request failed after ${timeCost}ms: requestId: ${requestId}, ${response.error}`);
222
243
  else console.log(`handle request done after ${timeCost}ms: requestId: ${requestId}`);
223
- if (requestId) delete this.taskProgressTips[requestId];
244
+ if (requestId) {
245
+ delete this.taskProgressTips[requestId];
246
+ if (this.currentTaskId === requestId) this.currentTaskId = null;
247
+ }
224
248
  });
225
249
  this._app.post('/cancel/:requestId', async (req, res)=>{
226
250
  const { requestId } = req.params;
@@ -228,9 +252,17 @@ class PlaygroundServer {
228
252
  error: 'requestId is required'
229
253
  });
230
254
  try {
231
- if (this.taskProgressTips[requestId]) delete this.taskProgressTips[requestId];
255
+ if (this.currentTaskId !== requestId) return res.json({
256
+ status: 'not_found',
257
+ message: 'Task not found or already completed'
258
+ });
259
+ console.log(`Cancelling task: ${requestId}`);
260
+ await this.recreateAgent();
261
+ delete this.taskProgressTips[requestId];
262
+ this.currentTaskId = null;
232
263
  res.json({
233
- status: 'cancelled'
264
+ status: 'cancelled',
265
+ message: 'Task cancelled successfully by recreating agent'
234
266
  });
235
267
  } catch (error) {
236
268
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
@@ -242,10 +274,10 @@ class PlaygroundServer {
242
274
  });
243
275
  this._app.get('/screenshot', async (_req, res)=>{
244
276
  try {
245
- if ('function' != typeof this.page.screenshotBase64) return res.status(500).json({
277
+ if ('function' != typeof this.agent.interface.screenshotBase64) return res.status(500).json({
246
278
  error: 'Screenshot method not available on current interface'
247
279
  });
248
- const base64Screenshot = await this.page.screenshotBase64();
280
+ const base64Screenshot = await this.agent.interface.screenshotBase64();
249
281
  res.json({
250
282
  screenshot: base64Screenshot,
251
283
  timestamp: Date.now()
@@ -260,9 +292,9 @@ class PlaygroundServer {
260
292
  });
261
293
  this._app.get('/interface-info', async (_req, res)=>{
262
294
  try {
263
- var _this_page_describe, _this_page;
264
- const type = this.page.interfaceType || 'Unknown';
265
- const description = (null == (_this_page_describe = (_this_page = this.page).describe) ? void 0 : _this_page_describe.call(_this_page)) || void 0;
295
+ var _this_agent_interface_describe, _this_agent_interface;
296
+ const type = this.agent.interface.interfaceType || 'Unknown';
297
+ const description = (null == (_this_agent_interface_describe = (_this_agent_interface = this.agent.interface).describe) ? void 0 : _this_agent_interface_describe.call(_this_agent_interface)) || void 0;
266
298
  res.json({
267
299
  type,
268
300
  description
@@ -330,6 +362,11 @@ class PlaygroundServer {
330
362
  }
331
363
  }
332
364
  async launch(port) {
365
+ if (this.agentFactory) {
366
+ console.log('Initializing agent from factory function...');
367
+ this.agent = await this.agentFactory();
368
+ console.log('Agent initialized successfully');
369
+ }
333
370
  this.initializeApp();
334
371
  this.port = port || defaultPort;
335
372
  return new Promise((resolve)=>{
@@ -358,24 +395,30 @@ class PlaygroundServer {
358
395
  } else resolve();
359
396
  });
360
397
  }
361
- constructor(page, agent, staticPath = STATIC_PATH, id){
398
+ constructor(agent, staticPath = STATIC_PATH, id){
362
399
  _define_property(this, "_app", void 0);
363
400
  _define_property(this, "tmpDir", void 0);
364
401
  _define_property(this, "server", void 0);
365
402
  _define_property(this, "port", void 0);
366
- _define_property(this, "page", void 0);
367
403
  _define_property(this, "agent", void 0);
368
404
  _define_property(this, "staticPath", void 0);
369
405
  _define_property(this, "taskProgressTips", void 0);
370
406
  _define_property(this, "id", void 0);
371
407
  _define_property(this, "_initialized", false);
408
+ _define_property(this, "agentFactory", void 0);
409
+ _define_property(this, "currentTaskId", null);
372
410
  this._app = external_express_default()();
373
411
  this.tmpDir = (0, utils_namespaceObject.getTmpDir)();
374
- this.page = page;
375
- this.agent = agent;
376
412
  this.staticPath = staticPath;
377
413
  this.taskProgressTips = {};
378
414
  this.id = id || (0, shared_utils_namespaceObject.uuid)();
415
+ if ('function' == typeof agent) {
416
+ this.agentFactory = agent;
417
+ this.agent = null;
418
+ } else {
419
+ this.agent = agent;
420
+ this.agentFactory = null;
421
+ }
379
422
  }
380
423
  }
381
424
  const server = PlaygroundServer;