/ utils / claudeInChrome / toolRendering.tsx
toolRendering.tsx
  1  import * as React from 'react';
  2  import { MessageResponse } from '../../components/MessageResponse.js';
  3  import { supportsHyperlinks } from '../../ink/supports-hyperlinks.js';
  4  import { Link, Text } from '../../ink.js';
  5  import { renderToolResultMessage as renderDefaultMCPToolResultMessage } from '../../tools/MCPTool/UI.js';
  6  import type { MCPToolResult } from '../../utils/mcpValidation.js';
  7  import { truncateToWidth } from '../format.js';
  8  import { trackClaudeInChromeTabId } from './common.js';
  9  export type { Tool } from '@modelcontextprotocol/sdk/types.js';
 10  
 11  /**
 12   * All tool names from BROWSER_TOOLS in @ant/claude-for-chrome-mcp.
 13   * Keep in sync with the package's BROWSER_TOOLS array.
 14   */
 15  export type ChromeToolName = 'javascript_tool' | 'read_page' | 'find' | 'form_input' | 'computer' | 'navigate' | 'resize_window' | 'gif_creator' | 'upload_image' | 'get_page_text' | 'tabs_context_mcp' | 'tabs_create_mcp' | 'update_plan' | 'read_console_messages' | 'read_network_requests' | 'shortcuts_list' | 'shortcuts_execute';
 16  const CHROME_EXTENSION_FOCUS_TAB_URL_BASE = 'https://clau.de/chrome/tab/';
 17  function renderChromeToolUseMessage(input: Record<string, unknown>, toolName: ChromeToolName, verbose: boolean): React.ReactNode {
 18    const tabId = input.tabId;
 19    if (typeof tabId === 'number') {
 20      trackClaudeInChromeTabId(tabId);
 21    }
 22  
 23    // Build secondary info based on tool type and input
 24    const secondaryInfo: string[] = [];
 25    switch (toolName) {
 26      case 'navigate':
 27        if (typeof input.url === 'string') {
 28          try {
 29            const url = new URL(input.url);
 30            secondaryInfo.push(url.hostname);
 31          } catch {
 32            secondaryInfo.push(truncateToWidth(input.url, 30));
 33          }
 34        }
 35        break;
 36      case 'find':
 37        if (typeof input.query === 'string') {
 38          secondaryInfo.push(`pattern: ${truncateToWidth(input.query, 30)}`);
 39        }
 40        break;
 41      case 'computer':
 42        if (typeof input.action === 'string') {
 43          const action = input.action;
 44          if (action === 'left_click' || action === 'right_click' || action === 'double_click' || action === 'middle_click') {
 45            if (typeof input.ref === 'string') {
 46              secondaryInfo.push(`${action} on ${input.ref}`);
 47            } else if (Array.isArray(input.coordinate)) {
 48              secondaryInfo.push(`${action} at (${input.coordinate.join(', ')})`);
 49            } else {
 50              secondaryInfo.push(action);
 51            }
 52          } else if (action === 'type' && typeof input.text === 'string') {
 53            secondaryInfo.push(`type "${truncateToWidth(input.text, 15)}"`);
 54          } else if (action === 'key' && typeof input.text === 'string') {
 55            secondaryInfo.push(`key ${input.text}`);
 56          } else if (action === 'scroll' && typeof input.scroll_direction === 'string') {
 57            secondaryInfo.push(`scroll ${input.scroll_direction}`);
 58          } else if (action === 'wait' && typeof input.duration === 'number') {
 59            secondaryInfo.push(`wait ${input.duration}s`);
 60          } else if (action === 'left_click_drag') {
 61            secondaryInfo.push('drag');
 62          } else {
 63            secondaryInfo.push(action);
 64          }
 65        }
 66        break;
 67      case 'gif_creator':
 68        if (typeof input.action === 'string') {
 69          secondaryInfo.push(`${input.action}`);
 70        }
 71        break;
 72      case 'resize_window':
 73        if (typeof input.width === 'number' && typeof input.height === 'number') {
 74          secondaryInfo.push(`${input.width}x${input.height}`);
 75        }
 76        break;
 77      case 'read_console_messages':
 78        if (typeof input.pattern === 'string') {
 79          secondaryInfo.push(`pattern: ${truncateToWidth(input.pattern, 20)}`);
 80        }
 81        if (input.onlyErrors === true) {
 82          secondaryInfo.push('errors only');
 83        }
 84        break;
 85      case 'read_network_requests':
 86        if (typeof input.urlPattern === 'string') {
 87          secondaryInfo.push(`pattern: ${truncateToWidth(input.urlPattern, 20)}`);
 88        }
 89        break;
 90      case 'shortcuts_execute':
 91        if (typeof input.shortcutId === 'string') {
 92          secondaryInfo.push(`shortcut_id: ${input.shortcutId}`);
 93        }
 94        break;
 95      case 'javascript_tool':
 96        // In verbose mode, show the full code
 97        if (verbose && typeof input.text === 'string') {
 98          return input.text;
 99        }
100        // In non-verbose mode, return empty string to preserve View Tab layout
101        return '';
102      case 'tabs_create_mcp':
103      case 'tabs_context_mcp':
104      case 'form_input':
105      case 'shortcuts_list':
106      case 'read_page':
107      case 'upload_image':
108      case 'get_page_text':
109      case 'update_plan':
110        // These tools don't have meaningful secondary info to show inline.
111        // Return empty string (not null) to ensure tool header still renders.
112        return '';
113    }
114    return secondaryInfo.join(', ') || null;
115  }
116  
117  /**
118   * Renders a clickable "View Tab" link for Claude in Chrome MCP tools.
119   * Returns null if:
120   * - The tool is not a Claude in Chrome MCP tool
121   * - The input doesn't have a valid tabId
122   * - Hyperlinks are not supported
123   */
124  function renderChromeViewTabLink(input: unknown): React.ReactNode {
125    if (!supportsHyperlinks()) {
126      return null;
127    }
128    if (typeof input !== 'object' || input === null || !('tabId' in input)) {
129      return null;
130    }
131    const tabId = typeof input.tabId === 'number' ? input.tabId : typeof input.tabId === 'string' ? parseInt(input.tabId, 10) : NaN;
132    if (isNaN(tabId)) {
133      return null;
134    }
135    const linkUrl = `${CHROME_EXTENSION_FOCUS_TAB_URL_BASE}${tabId}`;
136    return <Text>
137        {' '}
138        <Link url={linkUrl}>
139          <Text color="subtle">[View Tab]</Text>
140        </Link>
141      </Text>;
142  }
143  
144  /**
145   * Custom tool result message rendering for claude-in-chrome tools.
146   * Shows a brief summary for successful results. Errors are handled by
147   * the default renderToolUseErrorMessage when is_error is set.
148   */
149  export function renderChromeToolResultMessage(output: MCPToolResult, toolName: ChromeToolName, verbose: boolean): React.ReactNode {
150    if (verbose) {
151      return renderDefaultMCPToolResultMessage(output, [], {
152        verbose
153      });
154    }
155    let summary: string | null = null;
156    switch (toolName) {
157      case 'navigate':
158        summary = 'Navigation completed';
159        break;
160      case 'tabs_create_mcp':
161        summary = 'Tab created';
162        break;
163      case 'tabs_context_mcp':
164        summary = 'Tabs read';
165        break;
166      case 'form_input':
167        summary = 'Input completed';
168        break;
169      case 'computer':
170        summary = 'Action completed';
171        break;
172      case 'resize_window':
173        summary = 'Window resized';
174        break;
175      case 'find':
176        summary = 'Search completed';
177        break;
178      case 'gif_creator':
179        summary = 'GIF action completed';
180        break;
181      case 'read_console_messages':
182        summary = 'Console messages retrieved';
183        break;
184      case 'read_network_requests':
185        summary = 'Network requests retrieved';
186        break;
187      case 'shortcuts_list':
188        summary = 'Shortcuts retrieved';
189        break;
190      case 'shortcuts_execute':
191        summary = 'Shortcut executed';
192        break;
193      case 'javascript_tool':
194        summary = 'Script executed';
195        break;
196      case 'read_page':
197        summary = 'Page read';
198        break;
199      case 'upload_image':
200        summary = 'Image uploaded';
201        break;
202      case 'get_page_text':
203        summary = 'Page text retrieved';
204        break;
205      case 'update_plan':
206        summary = 'Plan updated';
207        break;
208    }
209    if (summary) {
210      return <MessageResponse height={1}>
211          <Text dimColor>{summary}</Text>
212        </MessageResponse>;
213    }
214    return null;
215  }
216  
217  /**
218   * Returns tool method overrides for Claude in Chrome MCP tools. Use this to customize
219   * rendering for chrome tools in a single spread operation.
220   */
221  export function getClaudeInChromeMCPToolOverrides(toolName: string): {
222    userFacingName: (input?: Record<string, unknown>) => string;
223    renderToolUseMessage: (input: Record<string, unknown>, options: {
224      verbose: boolean;
225    }) => React.ReactNode;
226    renderToolUseTag: (input: Partial<Record<string, unknown>>) => React.ReactNode;
227    renderToolResultMessage: (output: string | MCPToolResult, progressMessagesForMessage: unknown[], options: {
228      verbose: boolean;
229    }) => React.ReactNode;
230  } {
231    return {
232      userFacingName(_input?: Record<string, unknown>) {
233        // Trim the _mcp postfix that show up in some of the tool names
234        const displayName = toolName.replace(/_mcp$/, '');
235        return `Claude in Chrome[${displayName}]`;
236      },
237      renderToolUseMessage(input: Record<string, unknown>, {
238        verbose
239      }: {
240        verbose: boolean;
241      }): React.ReactNode {
242        return renderChromeToolUseMessage(input, toolName as ChromeToolName, verbose);
243      },
244      renderToolUseTag(input: Partial<Record<string, unknown>>): React.ReactNode {
245        return renderChromeViewTabLink(input);
246      },
247      renderToolResultMessage(output: string | MCPToolResult, _progressMessagesForMessage: unknown[], {
248        verbose
249      }: {
250        verbose: boolean;
251      }): React.ReactNode {
252        if (!isMCPToolResult(output)) {
253          return null;
254        }
255        return renderChromeToolResultMessage(output, toolName as ChromeToolName, verbose);
256      }
257    };
258  }
259  function isMCPToolResult(output: string | MCPToolResult): output is MCPToolResult {
260    return typeof output === 'object' && output !== null;
261  }
262  //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIk1lc3NhZ2VSZXNwb25zZSIsInN1cHBvcnRzSHlwZXJsaW5rcyIsIkxpbmsiLCJUZXh0IiwicmVuZGVyVG9vbFJlc3VsdE1lc3NhZ2UiLCJyZW5kZXJEZWZhdWx0TUNQVG9vbFJlc3VsdE1lc3NhZ2UiLCJNQ1BUb29sUmVzdWx0IiwidHJ1bmNhdGVUb1dpZHRoIiwidHJhY2tDbGF1ZGVJbkNocm9tZVRhYklkIiwiVG9vbCIsIkNocm9tZVRvb2xOYW1lIiwiQ0hST01FX0VYVEVOU0lPTl9GT0NVU19UQUJfVVJMX0JBU0UiLCJyZW5kZXJDaHJvbWVUb29sVXNlTWVzc2FnZSIsImlucHV0IiwiUmVjb3JkIiwidG9vbE5hbWUiLCJ2ZXJib3NlIiwiUmVhY3ROb2RlIiwidGFiSWQiLCJzZWNvbmRhcnlJbmZvIiwidXJsIiwiVVJMIiwicHVzaCIsImhvc3RuYW1lIiwicXVlcnkiLCJhY3Rpb24iLCJyZWYiLCJBcnJheSIsImlzQXJyYXkiLCJjb29yZGluYXRlIiwiam9pbiIsInRleHQiLCJzY3JvbGxfZGlyZWN0aW9uIiwiZHVyYXRpb24iLCJ3aWR0aCIsImhlaWdodCIsInBhdHRlcm4iLCJvbmx5RXJyb3JzIiwidXJsUGF0dGVybiIsInNob3J0Y3V0SWQiLCJyZW5kZXJDaHJvbWVWaWV3VGFiTGluayIsInBhcnNlSW50IiwiTmFOIiwiaXNOYU4iLCJsaW5rVXJsIiwicmVuZGVyQ2hyb21lVG9vbFJlc3VsdE1lc3NhZ2UiLCJvdXRwdXQiLCJzdW1tYXJ5IiwiZ2V0Q2xhdWRlSW5DaHJvbWVNQ1BUb29sT3ZlcnJpZGVzIiwidXNlckZhY2luZ05hbWUiLCJyZW5kZXJUb29sVXNlTWVzc2FnZSIsIm9wdGlvbnMiLCJyZW5kZXJUb29sVXNlVGFnIiwiUGFydGlhbCIsInByb2dyZXNzTWVzc2FnZXNGb3JNZXNzYWdlIiwiX2lucHV0IiwiZGlzcGxheU5hbWUiLCJyZXBsYWNlIiwiX3Byb2dyZXNzTWVzc2FnZXNGb3JNZXNzYWdlIiwiaXNNQ1BUb29sUmVzdWx0Il0sInNvdXJjZXMiOlsidG9vbFJlbmRlcmluZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBNZXNzYWdlUmVzcG9uc2UgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL01lc3NhZ2VSZXNwb25zZS5qcydcbmltcG9ydCB7IHN1cHBvcnRzSHlwZXJsaW5rcyB9IGZyb20gJy4uLy4uL2luay9zdXBwb3J0cy1oeXBlcmxpbmtzLmpzJ1xuaW1wb3J0IHsgTGluaywgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHJlbmRlclRvb2xSZXN1bHRNZXNzYWdlIGFzIHJlbmRlckRlZmF1bHRNQ1BUb29sUmVzdWx0TWVzc2FnZSB9IGZyb20gJy4uLy4uL3Rvb2xzL01DUFRvb2wvVUkuanMnXG5pbXBvcnQgdHlwZSB7IE1DUFRvb2xSZXN1bHQgfSBmcm9tICcuLi8uLi91dGlscy9tY3BWYWxpZGF0aW9uLmpzJ1xuaW1wb3J0IHsgdHJ1bmNhdGVUb1dpZHRoIH0gZnJvbSAnLi4vZm9ybWF0LmpzJ1xuaW1wb3J0IHsgdHJhY2tDbGF1ZGVJbkNocm9tZVRhYklkIH0gZnJvbSAnLi9jb21tb24uanMnXG5cbmV4cG9ydCB0eXBlIHsgVG9vbCB9IGZyb20gJ0Btb2RlbGNvbnRleHRwcm90b2NvbC9zZGsvdHlwZXMuanMnXG5cbi8qKlxuICogQWxsIHRvb2wgbmFtZXMgZnJvbSBCUk9XU0VSX1RPT0xTIGluIEBhbnQvY2xhdWRlLWZvci1jaHJvbWUtbWNwLlxuICogS2VlcCBpbiBzeW5jIHdpdGggdGhlIHBhY2thZ2UncyBCUk9XU0VSX1RPT0xTIGFycmF5LlxuICovXG5leHBvcnQgdHlwZSBDaHJvbWVUb29sTmFtZSA9XG4gIHwgJ2phdmFzY3JpcHRfdG9vbCdcbiAgfCAncmVhZF9wYWdlJ1xuICB8ICdmaW5kJ1xuICB8ICdmb3JtX2lucHV0J1xuICB8ICdjb21wdXRlcidcbiAgfCAnbmF2aWdhdGUnXG4gIHwgJ3Jlc2l6ZV93aW5kb3cnXG4gIHwgJ2dpZl9jcmVhdG9yJ1xuICB8ICd1cGxvYWRfaW1hZ2UnXG4gIHwgJ2dldF9wYWdlX3RleHQnXG4gIHwgJ3RhYnNfY29udGV4dF9tY3AnXG4gIHwgJ3RhYnNfY3JlYXRlX21jcCdcbiAgfCAndXBkYXRlX3BsYW4nXG4gIHwgJ3JlYWRfY29uc29sZV9tZXNzYWdlcydcbiAgfCAncmVhZF9uZXR3b3JrX3JlcXVlc3RzJ1xuICB8ICdzaG9ydGN1dHNfbGlzdCdcbiAgfCAnc2hvcnRjdXRzX2V4ZWN1dGUnXG5cbmNvbnN0IENIUk9NRV9FWFRFTlNJT05fRk9DVVNfVEFCX1VSTF9CQVNFID0gJ2h0dHBzOi8vY2xhdS5kZS9jaHJvbWUvdGFiLydcblxuZnVuY3Rpb24gcmVuZGVyQ2hyb21lVG9vbFVzZU1lc3NhZ2UoXG4gIGlucHV0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgdG9vbE5hbWU6IENocm9tZVRvb2xOYW1lLFxuICB2ZXJib3NlOiBib29sZWFuLFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgdGFiSWQgPSBpbnB1dC50YWJJZFxuICBpZiAodHlwZW9mIHRhYklkID09PSAnbnVtYmVyJykge1xuICAgIHRyYWNrQ2xhdWRlSW5DaHJvbWVUYWJJZCh0YWJJZClcbiAgfVxuXG4gIC8vIEJ1aWxkIHNlY29uZGFyeSBpbmZvIGJhc2VkIG9uIHRvb2wgdHlwZSBhbmQgaW5wdXRcbiAgY29uc3Qgc2Vjb25kYXJ5SW5mbzogc3RyaW5nW10gPSBbXVxuXG4gIHN3aXRjaCAodG9vbE5hbWUpIHtcbiAgICBjYXNlICduYXZpZ2F0ZSc6XG4gICAgICBpZiAodHlwZW9mIGlucHV0LnVybCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCB1cmwgPSBuZXcgVVJMKGlucHV0LnVybClcbiAgICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2godXJsLmhvc3RuYW1lKVxuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2godHJ1bmNhdGVUb1dpZHRoKGlucHV0LnVybCwgMzApKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBicmVha1xuXG4gICAgY2FzZSAnZmluZCc6XG4gICAgICBpZiAodHlwZW9mIGlucHV0LnF1ZXJ5ID09PSAnc3RyaW5nJykge1xuICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2goYHBhdHRlcm46ICR7dHJ1bmNhdGVUb1dpZHRoKGlucHV0LnF1ZXJ5LCAzMCl9YClcbiAgICAgIH1cbiAgICAgIGJyZWFrXG5cbiAgICBjYXNlICdjb21wdXRlcic6XG4gICAgICBpZiAodHlwZW9mIGlucHV0LmFjdGlvbiA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgY29uc3QgYWN0aW9uID0gaW5wdXQuYWN0aW9uXG4gICAgICAgIGlmIChcbiAgICAgICAgICBhY3Rpb24gPT09ICdsZWZ0X2NsaWNrJyB8fFxuICAgICAgICAgIGFjdGlvbiA9PT0gJ3JpZ2h0X2NsaWNrJyB8fFxuICAgICAgICAgIGFjdGlvbiA9PT0gJ2RvdWJsZV9jbGljaycgfHxcbiAgICAgICAgICBhY3Rpb24gPT09ICdtaWRkbGVfY2xpY2snXG4gICAgICAgICkge1xuICAgICAgICAgIGlmICh0eXBlb2YgaW5wdXQucmVmID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGAke2FjdGlvbn0gb24gJHtpbnB1dC5yZWZ9YClcbiAgICAgICAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkoaW5wdXQuY29vcmRpbmF0ZSkpIHtcbiAgICAgICAgICAgIHNlY29uZGFyeUluZm8ucHVzaChgJHthY3Rpb259IGF0ICgke2lucHV0LmNvb3JkaW5hdGUuam9pbignLCAnKX0pYClcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGFjdGlvbilcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoYWN0aW9uID09PSAndHlwZScgJiYgdHlwZW9mIGlucHV0LnRleHQgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGB0eXBlIFwiJHt0cnVuY2F0ZVRvV2lkdGgoaW5wdXQudGV4dCwgMTUpfVwiYClcbiAgICAgICAgfSBlbHNlIGlmIChhY3Rpb24gPT09ICdrZXknICYmIHR5cGVvZiBpbnB1dC50ZXh0ID09PSAnc3RyaW5nJykge1xuICAgICAgICAgIHNlY29uZGFyeUluZm8ucHVzaChga2V5ICR7aW5wdXQudGV4dH1gKVxuICAgICAgICB9IGVsc2UgaWYgKFxuICAgICAgICAgIGFjdGlvbiA9PT0gJ3Njcm9sbCcgJiZcbiAgICAgICAgICB0eXBlb2YgaW5wdXQuc2Nyb2xsX2RpcmVjdGlvbiA9PT0gJ3N0cmluZydcbiAgICAgICAgKSB7XG4gICAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGBzY3JvbGwgJHtpbnB1dC5zY3JvbGxfZGlyZWN0aW9ufWApXG4gICAgICAgIH0gZWxzZSBpZiAoYWN0aW9uID09PSAnd2FpdCcgJiYgdHlwZW9mIGlucHV0LmR1cmF0aW9uID09PSAnbnVtYmVyJykge1xuICAgICAgICAgIHNlY29uZGFyeUluZm8ucHVzaChgd2FpdCAke2lucHV0LmR1cmF0aW9ufXNgKVxuICAgICAgICB9IGVsc2UgaWYgKGFjdGlvbiA9PT0gJ2xlZnRfY2xpY2tfZHJhZycpIHtcbiAgICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2goJ2RyYWcnKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHNlY29uZGFyeUluZm8ucHVzaChhY3Rpb24pXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGJyZWFrXG5cbiAgICBjYXNlICdnaWZfY3JlYXRvcic6XG4gICAgICBpZiAodHlwZW9mIGlucHV0LmFjdGlvbiA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGAke2lucHV0LmFjdGlvbn1gKVxuICAgICAgfVxuICAgICAgYnJlYWtcblxuICAgIGNhc2UgJ3Jlc2l6ZV93aW5kb3cnOlxuICAgICAgaWYgKHR5cGVvZiBpbnB1dC53aWR0aCA9PT0gJ251bWJlcicgJiYgdHlwZW9mIGlucHV0LmhlaWdodCA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGAke2lucHV0LndpZHRofXgke2lucHV0LmhlaWdodH1gKVxuICAgICAgfVxuICAgICAgYnJlYWtcblxuICAgIGNhc2UgJ3JlYWRfY29uc29sZV9tZXNzYWdlcyc6XG4gICAgICBpZiAodHlwZW9mIGlucHV0LnBhdHRlcm4gPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIHNlY29uZGFyeUluZm8ucHVzaChgcGF0dGVybjogJHt0cnVuY2F0ZVRvV2lkdGgoaW5wdXQucGF0dGVybiwgMjApfWApXG4gICAgICB9XG4gICAgICBpZiAoaW5wdXQub25seUVycm9ycyA9PT0gdHJ1ZSkge1xuICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2goJ2Vycm9ycyBvbmx5JylcbiAgICAgIH1cbiAgICAgIGJyZWFrXG5cbiAgICBjYXNlICdyZWFkX25ldHdvcmtfcmVxdWVzdHMnOlxuICAgICAgaWYgKHR5cGVvZiBpbnB1dC51cmxQYXR0ZXJuID09PSAnc3RyaW5nJykge1xuICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2goYHBhdHRlcm46ICR7dHJ1bmNhdGVUb1dpZHRoKGlucHV0LnVybFBhdHRlcm4sIDIwKX1gKVxuICAgICAgfVxuICAgICAgYnJlYWtcblxuICAgIGNhc2UgJ3Nob3J0Y3V0c19leGVjdXRlJzpcbiAgICAgIGlmICh0eXBlb2YgaW5wdXQuc2hvcnRjdXRJZCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGBzaG9ydGN1dF9pZDogJHtpbnB1dC5zaG9ydGN1dElkfWApXG4gICAgICB9XG4gICAgICBicmVha1xuXG4gICAgY2FzZSAnamF2YXNjcmlwdF90b29sJzpcbiAgICAgIC8vIEluIHZlcmJvc2UgbW9kZSwgc2hvdyB0aGUgZnVsbCBjb2RlXG4gICAgICBpZiAodmVyYm9zZSAmJiB0eXBlb2YgaW5wdXQudGV4dCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgcmV0dXJuIGlucHV0LnRleHRcbiAgICAgIH1cbiAgICAgIC8vIEluIG5vbi12ZXJib3NlIG1vZGUsIHJldHVybiBlbXB0eSBzdHJpbmcgdG8gcHJlc2VydmUgVmlldyBUYWIgbGF5b3V0XG4gICAgICByZXR1cm4gJydcblxuICAgIGNhc2UgJ3RhYnNfY3JlYXRlX21jcCc6XG4gICAgY2FzZSAndGFic19jb250ZXh0X21jcCc6XG4gICAgY2FzZSAnZm9ybV9pbnB1dCc6XG4gICAgY2FzZSAnc2hvcnRjdXRzX2xpc3QnOlxuICAgIGNhc2UgJ3JlYWRfcGFnZSc6XG4gICAgY2FzZSAndXBsb2FkX2ltYWdlJzpcbiAgICBjYXNlICdnZXRfcGFnZV90ZXh0JzpcbiAgICBjYXNlICd1cGRhdGVfcGxhbic6XG4gICAgICAvLyBUaGVzZSB0b29scyBkb24ndCBoYXZlIG1lYW5pbmdmdWwgc2Vjb25kYXJ5IGluZm8gdG8gc2hvdyBpbmxpbmUuXG4gICAgICAvLyBSZXR1cm4gZW1wdHkgc3RyaW5nIChub3QgbnVsbCkgdG8gZW5zdXJlIHRvb2wgaGVhZGVyIHN0aWxsIHJlbmRlcnMuXG4gICAgICByZXR1cm4gJydcbiAgfVxuXG4gIHJldHVybiBzZWNvbmRhcnlJbmZvLmpvaW4oJywgJykgfHwgbnVsbFxufVxuXG4vKipcbiAqIFJlbmRlcnMgYSBjbGlja2FibGUgXCJWaWV3IFRhYlwiIGxpbmsgZm9yIENsYXVkZSBpbiBDaHJvbWUgTUNQIHRvb2xzLlxuICogUmV0dXJucyBudWxsIGlmOlxuICogLSBUaGUgdG9vbCBpcyBub3QgYSBDbGF1ZGUgaW4gQ2hyb21lIE1DUCB0b29sXG4gKiAtIFRoZSBpbnB1dCBkb2Vzbid0IGhhdmUgYSB2YWxpZCB0YWJJZFxuICogLSBIeXBlcmxpbmtzIGFyZSBub3Qgc3VwcG9ydGVkXG4gKi9cbmZ1bmN0aW9uIHJlbmRlckNocm9tZVZpZXdUYWJMaW5rKGlucHV0OiB1bmtub3duKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKCFzdXBwb3J0c0h5cGVybGlua3MoKSkge1xuICAgIHJldHVybiBudWxsXG4gIH1cbiAgaWYgKHR5cGVvZiBpbnB1dCAhPT0gJ29iamVjdCcgfHwgaW5wdXQgPT09IG51bGwgfHwgISgndGFiSWQnIGluIGlucHV0KSkge1xuICAgIHJldHVybiBudWxsXG4gIH1cbiAgY29uc3QgdGFiSWQgPVxuICAgIHR5cGVvZiBpbnB1dC50YWJJZCA9PT0gJ251bWJlcidcbiAgICAgID8gaW5wdXQudGFiSWRcbiAgICAgIDogdHlwZW9mIGlucHV0LnRhYklkID09PSAnc3RyaW5nJ1xuICAgICAgICA/IHBhcnNlSW50KGlucHV0LnRhYklkLCAxMClcbiAgICAgICAgOiBOYU5cbiAgaWYgKGlzTmFOKHRhYklkKSkge1xuICAgIHJldHVybiBudWxsXG4gIH1cbiAgY29uc3QgbGlua1VybCA9IGAke0NIUk9NRV9FWFRFTlNJT05fRk9DVVNfVEFCX1VSTF9CQVNFfSR7dGFiSWR9YFxuICByZXR1cm4gKFxuICAgIDxUZXh0PlxuICAgICAgeycgJ31cbiAgICAgIDxMaW5rIHVybD17bGlua1VybH0+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwic3VidGxlXCI+W1ZpZXcgVGFiXTwvVGV4dD5cbiAgICAgIDwvTGluaz5cbiAgICA8L1RleHQ+XG4gIClcbn1cblxuLyoqXG4gKiBDdXN0b20gdG9vbCByZXN1bHQgbWVzc2FnZSByZW5kZXJpbmcgZm9yIGNsYXVkZS1pbi1jaHJvbWUgdG9vbHMuXG4gKiBTaG93cyBhIGJyaWVmIHN1bW1hcnkgZm9yIHN1Y2Nlc3NmdWwgcmVzdWx0cy4gRXJyb3JzIGFyZSBoYW5kbGVkIGJ5XG4gKiB0aGUgZGVmYXVsdCByZW5kZXJUb29sVXNlRXJyb3JNZXNzYWdlIHdoZW4gaXNfZXJyb3IgaXMgc2V0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyQ2hyb21lVG9vbFJlc3VsdE1lc3NhZ2UoXG4gIG91dHB1dDogTUNQVG9vbFJlc3VsdCxcbiAgdG9vbE5hbWU6IENocm9tZVRvb2xOYW1lLFxuICB2ZXJib3NlOiBib29sZWFuLFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKHZlcmJvc2UpIHtcbiAgICByZXR1cm4gcmVuZGVyRGVmYXVsdE1DUFRvb2xSZXN1bHRNZXNzYWdlKG91dHB1dCwgW10sIHsgdmVyYm9zZSB9KVxuICB9XG5cbiAgbGV0IHN1bW1hcnk6IHN0cmluZyB8IG51bGwgPSBudWxsXG4gIHN3aXRjaCAodG9vbE5hbWUpIHtcbiAgICBjYXNlICduYXZpZ2F0ZSc6XG4gICAgICBzdW1tYXJ5ID0gJ05hdmlnYXRpb24gY29tcGxldGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICd0YWJzX2NyZWF0ZV9tY3AnOlxuICAgICAgc3VtbWFyeSA9ICdUYWIgY3JlYXRlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAndGFic19jb250ZXh0X21jcCc6XG4gICAgICBzdW1tYXJ5ID0gJ1RhYnMgcmVhZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAnZm9ybV9pbnB1dCc6XG4gICAgICBzdW1tYXJ5ID0gJ0lucHV0IGNvbXBsZXRlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAnY29tcHV0ZXInOlxuICAgICAgc3VtbWFyeSA9ICdBY3Rpb24gY29tcGxldGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICdyZXNpemVfd2luZG93JzpcbiAgICAgIHN1bW1hcnkgPSAnV2luZG93IHJlc2l6ZWQnXG4gICAgICBicmVha1xuICAgIGNhc2UgJ2ZpbmQnOlxuICAgICAgc3VtbWFyeSA9ICdTZWFyY2ggY29tcGxldGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICdnaWZfY3JlYXRvcic6XG4gICAgICBzdW1tYXJ5ID0gJ0dJRiBhY3Rpb24gY29tcGxldGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICdyZWFkX2NvbnNvbGVfbWVzc2FnZXMnOlxuICAgICAgc3VtbWFyeSA9ICdDb25zb2xlIG1lc3NhZ2VzIHJldHJpZXZlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAncmVhZF9uZXR3b3JrX3JlcXVlc3RzJzpcbiAgICAgIHN1bW1hcnkgPSAnTmV0d29yayByZXF1ZXN0cyByZXRyaWV2ZWQnXG4gICAgICBicmVha1xuICAgIGNhc2UgJ3Nob3J0Y3V0c19saXN0JzpcbiAgICAgIHN1bW1hcnkgPSAnU2hvcnRjdXRzIHJldHJpZXZlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAnc2hvcnRjdXRzX2V4ZWN1dGUnOlxuICAgICAgc3VtbWFyeSA9ICdTaG9ydGN1dCBleGVjdXRlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAnamF2YXNjcmlwdF90b29sJzpcbiAgICAgIHN1bW1hcnkgPSAnU2NyaXB0IGV4ZWN1dGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICdyZWFkX3BhZ2UnOlxuICAgICAgc3VtbWFyeSA9ICdQYWdlIHJlYWQnXG4gICAgICBicmVha1xuICAgIGNhc2UgJ3VwbG9hZF9pbWFnZSc6XG4gICAgICBzdW1tYXJ5ID0gJ0ltYWdlIHVwbG9hZGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICdnZXRfcGFnZV90ZXh0JzpcbiAgICAgIHN1bW1hcnkgPSAnUGFnZSB0ZXh0IHJldHJpZXZlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAndXBkYXRlX3BsYW4nOlxuICAgICAgc3VtbWFyeSA9ICdQbGFuIHVwZGF0ZWQnXG4gICAgICBicmVha1xuICB9XG5cbiAgaWYgKHN1bW1hcnkpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPE1lc3NhZ2VSZXNwb25zZSBoZWlnaHQ9ezF9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj57c3VtbWFyeX08L1RleHQ+XG4gICAgICA8L01lc3NhZ2VSZXNwb25zZT5cbiAgICApXG4gIH1cblxuICByZXR1cm4gbnVsbFxufVxuXG4vKipcbiAqIFJldHVybnMgdG9vbCBtZXRob2Qgb3ZlcnJpZGVzIGZvciBDbGF1ZGUgaW4gQ2hyb21lIE1DUCB0b29scy4gVXNlIHRoaXMgdG8gY3VzdG9taXplXG4gKiByZW5kZXJpbmcgZm9yIGNocm9tZSB0b29scyBpbiBhIHNpbmdsZSBzcHJlYWQgb3BlcmF0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q2xhdWRlSW5DaHJvbWVNQ1BUb29sT3ZlcnJpZGVzKHRvb2xOYW1lOiBzdHJpbmcpOiB7XG4gIHVzZXJGYWNpbmdOYW1lOiAoaW5wdXQ/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgPT4gc3RyaW5nXG4gIHJlbmRlclRvb2xVc2VNZXNzYWdlOiAoXG4gICAgaW5wdXQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICAgIG9wdGlvbnM6IHsgdmVyYm9zZTogYm9vbGVhbiB9LFxuICApID0+IFJlYWN0LlJlYWN0Tm9kZVxuICByZW5kZXJUb29sVXNlVGFnOiAoaW5wdXQ6IFBhcnRpYWw8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+KSA9PiBSZWFjdC5SZWFjdE5vZGVcbiAgcmVuZGVyVG9vbFJlc3VsdE1lc3NhZ2U6IChcbiAgICBvdXRwdXQ6IHN0cmluZyB8IE1DUFRvb2xSZXN1bHQsXG4gICAgcHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2U6IHVua25vd25bXSxcbiAgICBvcHRpb25zOiB7IHZlcmJvc2U6IGJvb2xlYW4gfSxcbiAgKSA9PiBSZWFjdC5SZWFjdE5vZGVcbn0ge1xuICByZXR1cm4ge1xuICAgIHVzZXJGYWNpbmdOYW1lKF9pbnB1dD86IFJlY29yZDxzdHJpbmcsIHVua25vd24+KSB7XG4gICAgICAvLyBUcmltIHRoZSBfbWNwIHBvc3RmaXggdGhhdCBzaG93IHVwIGluIHNvbWUgb2YgdGhlIHRvb2wgbmFtZXNcbiAgICAgIGNvbnN0IGRpc3BsYXlOYW1lID0gdG9vbE5hbWUucmVwbGFjZSgvX21jcCQvLCAnJylcbiAgICAgIHJldHVybiBgQ2xhdWRlIGluIENocm9tZVske2Rpc3BsYXlOYW1lfV1gXG4gICAgfSxcbiAgICByZW5kZXJUb29sVXNlTWVzc2FnZShcbiAgICAgIGlucHV0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICAgIHsgdmVyYm9zZSB9OiB7IHZlcmJvc2U6IGJvb2xlYW4gfSxcbiAgICApOiBSZWFjdC5SZWFjdE5vZGUge1xuICAgICAgcmV0dXJuIHJlbmRlckNocm9tZVRvb2xVc2VNZXNzYWdlKFxuICAgICAgICBpbnB1dCxcbiAgICAgICAgdG9vbE5hbWUgYXMgQ2hyb21lVG9vbE5hbWUsXG4gICAgICAgIHZlcmJvc2UsXG4gICAgICApXG4gICAgfSxcbiAgICByZW5kZXJUb29sVXNlVGFnKGlucHV0OiBQYXJ0aWFsPFJlY29yZDxzdHJpbmcsIHVua25vd24+Pik6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gICAgICByZXR1cm4gcmVuZGVyQ2hyb21lVmlld1RhYkxpbmsoaW5wdXQpXG4gICAgfSxcbiAgICByZW5kZXJUb29sUmVzdWx0TWVzc2FnZShcbiAgICAgIG91dHB1dDogc3RyaW5nIHwgTUNQVG9vbFJlc3VsdCxcbiAgICAgIF9wcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZTogdW5rbm93bltdLFxuICAgICAgeyB2ZXJib3NlIH06IHsgdmVyYm9zZTogYm9vbGVhbiB9LFxuICAgICk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gICAgICBpZiAoIWlzTUNQVG9vbFJlc3VsdChvdXRwdXQpKSB7XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG4gICAgICByZXR1cm4gcmVuZGVyQ2hyb21lVG9vbFJlc3VsdE1lc3NhZ2UoXG4gICAgICAgIG91dHB1dCxcbiAgICAgICAgdG9vbE5hbWUgYXMgQ2hyb21lVG9vbE5hbWUsXG4gICAgICAgIHZlcmJvc2UsXG4gICAgICApXG4gICAgfSxcbiAgfVxufVxuXG5mdW5jdGlvbiBpc01DUFRvb2xSZXN1bHQoXG4gIG91dHB1dDogc3RyaW5nIHwgTUNQVG9vbFJlc3VsdCxcbik6IG91dHB1dCBpcyBNQ1BUb29sUmVzdWx0IHtcbiAgcmV0dXJuIHR5cGVvZiBvdXRwdXQgPT09ICdvYmplY3QnICYmIG91dHB1dCAhPT0gbnVsbFxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFP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