πŸ“„ File detail

tools/FileReadTool/UI.tsx

🧩 .tsxπŸ“ 185 linesπŸ’Ύ 22,540 bytesπŸ“ text
← Back to All Files

🎯 Use case

This module implements the β€œFileReadTool” tool (File Read) β€” something the model can call at runtime alongside other agent tools. On the API surface it exposes renderToolUseMessage, renderToolUseTag, renderToolResultMessage, renderToolUseErrorMessage, and userFacingName (and more) β€” mainly functions, hooks, or classes. Dependencies touch @anthropic-ai, React UI, and src. It composes internal code from components, ink, utils, and FileReadTool (relative imports).

Generated from folder role, exports, dependency roots, and inline comments β€” not hand-reviewed for every path.

🧠 Inline summary

import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'; import * as React from 'react'; import { extractTag } from 'src/utils/messages.js'; import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js'; import { FilePathLink } from '../../components/FilePathLink.js';

πŸ“€ Exports (heuristic)

  • renderToolUseMessage
  • renderToolUseTag
  • renderToolResultMessage
  • renderToolUseErrorMessage
  • userFacingName
  • getToolUseSummary

πŸ“š External import roots

Package roots from from "…" (relative paths omitted).

  • @anthropic-ai
  • react
  • src

πŸ–₯️ Source preview

import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs';
import * as React from 'react';
import { extractTag } from 'src/utils/messages.js';
import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js';
import { FilePathLink } from '../../components/FilePathLink.js';
import { MessageResponse } from '../../components/MessageResponse.js';
import { Text } from '../../ink.js';
import { FILE_NOT_FOUND_CWD_NOTE, getDisplayPath } from '../../utils/file.js';
import { formatFileSize } from '../../utils/format.js';
import { getPlansDirectory } from '../../utils/plans.js';
import { getTaskOutputDir } from '../../utils/task/diskOutput.js';
import type { Input, Output } from './FileReadTool.js';

/**
 * Check if a file path is an agent output file and extract the task ID.
 * Agent output files follow the pattern: {projectTempDir}/tasks/{taskId}.output
 */
function getAgentOutputTaskId(filePath: string): string | null {
  const prefix = `${getTaskOutputDir()}/`;
  const suffix = '.output';
  if (filePath.startsWith(prefix) && filePath.endsWith(suffix)) {
    const taskId = filePath.slice(prefix.length, -suffix.length);
    // Validate it looks like a task ID (alphanumeric, reasonable length)
    if (taskId.length > 0 && taskId.length <= 20 && /^[a-zA-Z0-9_-]+$/.test(taskId)) {
      return taskId;
    }
  }
  return null;
}
export function renderToolUseMessage({
  file_path,
  offset,
  limit,
  pages
}: Partial<Input>, {
  verbose
}: {
  verbose: boolean;
}): React.ReactNode {
  if (!file_path) {
    return null;
  }

  // For agent output files, return empty string so no parentheses are shown
  // The task ID is displayed separately by AssistantToolUseMessage
  if (getAgentOutputTaskId(file_path)) {
    return '';
  }
  const displayPath = verbose ? file_path : getDisplayPath(file_path);
  if (pages) {
    return <>
        <FilePathLink filePath={file_path}>{displayPath}</FilePathLink>
        {` Β· pages ${pages}`}
      </>;
  }
  if (verbose && (offset || limit)) {
    const startLine = offset ?? 1;
    const lineRange = limit ? `lines ${startLine}-${startLine + limit - 1}` : `from line ${startLine}`;
    return <>
        <FilePathLink filePath={file_path}>{displayPath}</FilePathLink>
        {` Β· ${lineRange}`}
      </>;
  }
  return <FilePathLink filePath={file_path}>{displayPath}</FilePathLink>;
}
export function renderToolUseTag({
  file_path
}: Partial<Input>): React.ReactNode {
  const agentTaskId = file_path ? getAgentOutputTaskId(file_path) : null;

  // Show agent task ID for Read tool when reading agent output
  if (!agentTaskId) {
    return null;
  }
  return <Text dimColor> {agentTaskId}</Text>;
}
export function renderToolResultMessage(output: Output): React.ReactNode {
  // TODO: Render recursively
  switch (output.type) {
    case 'image':
      {
        const {
          originalSize
        } = output.file;
        const formattedSize = formatFileSize(originalSize);
        return <MessageResponse height={1}>
          <Text>Read image ({formattedSize})</Text>
        </MessageResponse>;
      }
    case 'notebook':
      {
        const {
          cells
        } = output.file;
        if (!cells || cells.length < 1) {
          return <Text color="error">No cells found in notebook</Text>;
        }
        return <MessageResponse height={1}>
          <Text>
            Read <Text bold>{cells.length}</Text> cells
          </Text>
        </MessageResponse>;
      }
    case 'pdf':
      {
        const {
          originalSize
        } = output.file;
        const formattedSize = formatFileSize(originalSize);
        return <MessageResponse height={1}>
          <Text>Read PDF ({formattedSize})</Text>
        </MessageResponse>;
      }
    case 'parts':
      {
        return <MessageResponse height={1}>
          <Text>
            Read <Text bold>{output.file.count}</Text>{' '}
            {output.file.count === 1 ? 'page' : 'pages'} (
            {formatFileSize(output.file.originalSize)})
          </Text>
        </MessageResponse>;
      }
    case 'text':
      {
        const {
          numLines
        } = output.file;
        return <MessageResponse height={1}>
          <Text>
            Read <Text bold>{numLines}</Text>{' '}
            {numLines === 1 ? 'line' : 'lines'}
          </Text>
        </MessageResponse>;
      }
    case 'file_unchanged':
      {
        return <MessageResponse height={1}>
          <Text dimColor>Unchanged since last read</Text>
        </MessageResponse>;
      }
  }
}
export function renderToolUseErrorMessage(result: ToolResultBlockParam['content'], {
  verbose
}: {
  verbose: boolean;
}): React.ReactNode {
  if (!verbose && typeof result === 'string') {
    // FileReadTool throws from call() so errors lack <tool_use_error> wrapping β€”
    // check the raw string directly for the cwd note marker.
    if (result.includes(FILE_NOT_FOUND_CWD_NOTE)) {
      return <MessageResponse>
          <Text color="error">File not found</Text>
        </MessageResponse>;
    }
    if (extractTag(result, 'tool_use_error')) {
      return <MessageResponse>
          <Text color="error">Error reading file</Text>
        </MessageResponse>;
    }
  }
  return <FallbackToolUseErrorMessage result={result} verbose={verbose} />;
}
export function userFacingName(input: Partial<Input> | undefined): string {
  if (input?.file_path?.startsWith(getPlansDirectory())) {
    return 'Reading Plan';
  }
  if (input?.file_path && getAgentOutputTaskId(input.file_path)) {
    return 'Read agent output';
  }
  return 'Read';
}
export function getToolUseSummary(input: Partial<Input> | undefined): string | null {
  if (!input?.file_path) {
    return null;
  }
  // For agent output files, just show the task ID
  const agentTaskId = getAgentOutputTaskId(input.file_path);
  if (agentTaskId) {
    return agentTaskId;
  }
  return getDisplayPath(input.file_path);
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJUb29sUmVzdWx0QmxvY2tQYXJhbSIsIlJlYWN0IiwiZXh0cmFjdFRhZyIsIkZhbGxiYWNrVG9vbFVzZUVycm9yTWVzc2FnZSIsIkZpbGVQYXRoTGluayIsIk1lc3NhZ2VSZXNwb25zZSIsIlRleHQiLCJGSUxFX05PVF9GT1VORF9DV0RfTk9URSIsImdldERpc3BsYXlQYXRoIiwiZm9ybWF0RmlsZVNpemUiLCJnZXRQbGFuc0RpcmVjdG9yeSIsImdldFRhc2tPdXRwdXREaXIiLCJJbnB1dCIsIk91dHB1dCIsImdldEFnZW50T3V0cHV0VGFza0lkIiwiZmlsZVBhdGgiLCJwcmVmaXgiLCJzdWZmaXgiLCJzdGFydHNXaXRoIiwiZW5kc1dpdGgiLCJ0YXNrSWQiLCJzbGljZSIsImxlbmd0aCIsInRlc3QiLCJyZW5kZXJUb29sVXNlTWVzc2FnZSIsImZpbGVfcGF0aCIsIm9mZnNldCIsImxpbWl0IiwicGFnZXMiLCJQYXJ0aWFsIiwidmVyYm9zZSIsIlJlYWN0Tm9kZSIsImRpc3BsYXlQYXRoIiwic3RhcnRMaW5lIiwibGluZVJhbmdlIiwicmVuZGVyVG9vbFVzZVRhZyIsImFnZW50VGFza0lkIiwicmVuZGVyVG9vbFJlc3VsdE1lc3NhZ2UiLCJvdXRwdXQiLCJ0eXBlIiwib3JpZ2luYWxTaXplIiwiZmlsZSIsImZvcm1hdHRlZFNpemUiLCJjZWxscyIsImNvdW50IiwibnVtTGluZXMiLCJyZW5kZXJUb29sVXNlRXJyb3JNZXNzYWdlIiwicmVzdWx0IiwiaW5jbHVkZXMiLCJ1c2VyRmFjaW5nTmFtZSIsImlucHV0IiwiZ2V0VG9vbFVzZVN1bW1hcnkiXSwic291cmNlcyI6WyJVSS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBUb29sUmVzdWx0QmxvY2tQYXJhbSB9IGZyb20gJ0BhbnRocm9waWMtYWkvc2RrL3Jlc291cmNlcy9pbmRleC5tanMnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGV4dHJhY3RUYWcgfSBmcm9tICdzcmMvdXRpbHMvbWVzc2FnZXMuanMnXG5pbXBvcnQgeyBGYWxsYmFja1Rvb2xVc2VFcnJvck1lc3NhZ2UgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0ZhbGxiYWNrVG9vbFVzZUVycm9yTWVzc2FnZS5qcydcbmltcG9ydCB7IEZpbGVQYXRoTGluayB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvRmlsZVBhdGhMaW5rLmpzJ1xuaW1wb3J0IHsgTWVzc2FnZVJlc3BvbnNlIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9NZXNzYWdlUmVzcG9uc2UuanMnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgRklMRV9OT1RfRk9VTkRfQ1dEX05PVEUsIGdldERpc3BsYXlQYXRoIH0gZnJvbSAnLi4vLi4vdXRpbHMvZmlsZS5qcydcbmltcG9ydCB7IGZvcm1hdEZpbGVTaXplIH0gZnJvbSAnLi4vLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHsgZ2V0UGxhbnNEaXJlY3RvcnkgfSBmcm9tICcuLi8uLi91dGlscy9wbGFucy5qcydcbmltcG9ydCB7IGdldFRhc2tPdXRwdXREaXIgfSBmcm9tICcuLi8uLi91dGlscy90YXNrL2Rpc2tPdXRwdXQuanMnXG5pbXBvcnQgdHlwZSB7IElucHV0LCBPdXRwdXQgfSBmcm9tICcuL0ZpbGVSZWFkVG9vbC5qcydcblxuLyoqXG4gKiBDaGVjayBpZiBhIGZpbGUgcGF0aCBpcyBhbiBhZ2VudCBvdXRwdXQgZmlsZSBhbmQgZXh0cmFjdCB0aGUgdGFzayBJRC5cbiAqIEFnZW50IG91dHB1dCBmaWxlcyBmb2xsb3cgdGhlIHBhdHRlcm46IHtwcm9qZWN0VGVtcERpcn0vdGFza3Mve3Rhc2tJZH0ub3V0cHV0XG4gKi9cbmZ1bmN0aW9uIGdldEFnZW50T3V0cHV0VGFza0lkKGZpbGVQYXRoOiBzdHJpbmcpOiBzdHJpbmcgfCBudWxsIHtcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0VGFza091dHB1dERpcigpfS9gXG4gIGNvbnN0IHN1ZmZpeCA9ICcub3V0cHV0J1xuICBpZiAoZmlsZVBhdGguc3RhcnRzV2l0aChwcmVmaXgpICYmIGZpbGVQYXRoLmVuZHNXaXRoKHN1ZmZpeCkpIHtcbiAgICBjb25zdCB0YXNrSWQgPSBmaWxlUGF0aC5zbGljZShwcmVmaXgubGVuZ3RoLCAtc3VmZml4Lmxlbmd0aClcbiAgICAvLyBWYWxpZGF0ZSBpdCBsb29rcyBsaWtlIGEgdGFzayBJRCAoYWxwaGFudW1lcmljLCByZWFzb25hYmxlIGxlbmd0aClcbiAgICBpZiAoXG4gICAgICB0YXNrSWQubGVuZ3RoID4gMCAmJlxuICAgICAgdGFza0lkLmxlbmd0aCA8PSAyMCAmJlxuICAgICAgL15bYS16QS1aMC05Xy1dKyQvLnRlc3QodGFza0lkKVxuICAgICkge1xuICAgICAgcmV0dXJuIHRhc2tJZFxuICAgIH1cbiAgfVxuICByZXR1cm4gbnVsbFxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyVG9vbFVzZU1lc3NhZ2UoXG4gIHsgZmlsZV9wYXRoLCBvZmZzZXQsIGxpbWl0LCBwYWdlcyB9OiBQYXJ0aWFsPElucHV0PixcbiAgeyB2ZXJib3NlIH06IHsgdmVyYm9zZTogYm9vbGVhbiB9LFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKCFmaWxlX3BhdGgpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgLy8gRm9yIGFnZW50IG91dHB1dCBmaWxlcywgcmV0dXJuIGVtcHR5IHN0cmluZyBzbyBubyBwYXJlbnRoZXNlcyBhcmUgc2hvd25cbiAgLy8gVGhlIHRhc2sgSUQgaXMgZGlzcGxheWVkIHNlcGFyYXRlbHkgYnkgQXNzaXN0YW50VG9vbFVzZU1lc3NhZ2VcbiAgaWYgKGdldEFnZW50T3V0cHV0VGFza0lkKGZpbGVfcGF0aCkpIHtcbiAgICByZXR1cm4gJydcbiAgfVxuXG4gIGNvbnN0IGRpc3BsYXlQYXRoID0gdmVyYm9zZSA/IGZpbGVfcGF0aCA6IGdldERpc3BsYXlQYXRoKGZpbGVfcGF0aClcbiAgaWYgKHBhZ2VzKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDw+XG4gICAgICAgIDxGaWxlUGF0aExpbmsgZmlsZVBhdGg9e2ZpbGVfcGF0aH0+e2Rpc3BsYXlQYXRofTwvRmlsZVBhdGhMaW5rPlxuICAgICAgICB7YCDCtyBwYWdlcyAke3BhZ2VzfWB9XG4gICAgICA8Lz5cbiAgICApXG4gIH1cbiAgaWYgKHZlcmJvc2UgJiYgKG9mZnNldCB8fCBsaW1pdCkpIHtcbiAgICBjb25zdCBzdGFydExpbmUgPSBvZmZzZXQgPz8gMVxuICAgIGNvbnN0IGxpbmVSYW5nZSA9IGxpbWl0XG4gICAgICA/IGBsaW5lcyAke3N0YXJ0TGluZX0tJHtzdGFydExpbmUgKyBsaW1pdCAtIDF9YFxuICAgICAgOiBgZnJvbSBsaW5lICR7c3RhcnRMaW5lfWBcbiAgICByZXR1cm4gKFxuICAgICAgPD5cbiAgICAgICAgPEZpbGVQYXRoTGluayBmaWxlUGF0aD17ZmlsZV9wYXRofT57ZGlzcGxheVBhdGh9PC9GaWxlUGF0aExpbms+XG4gICAgICAgIHtgIMK3ICR7bGluZVJhbmdlfWB9XG4gICAgICA8Lz5cbiAgICApXG4gIH1cbiAgcmV0dXJuIDxGaWxlUGF0aExpbmsgZmlsZVBhdGg9e2ZpbGVfcGF0aH0+e2Rpc3BsYXlQYXRofTwvRmlsZVBhdGhMaW5rPlxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyVG9vbFVzZVRhZyh7XG4gIGZpbGVfcGF0aCxcbn06IFBhcnRpYWw8SW5wdXQ+KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgYWdlbnRUYXNrSWQgPSBmaWxlX3BhdGggPyBnZXRBZ2VudE91dHB1dFRhc2tJZChmaWxlX3BhdGgpIDogbnVsbFxuXG4gIC8vIFNob3cgYWdlbnQgdGFzayBJRCBmb3IgUmVhZCB0b29sIHdoZW4gcmVhZGluZyBhZ2VudCBvdXRwdXRcbiAgaWYgKCFhZ2VudFRhc2tJZCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cbiAgcmV0dXJuIDxUZXh0IGRpbUNvbG9yPiB7YWdlbnRUYXNrSWR9PC9UZXh0PlxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyVG9vbFJlc3VsdE1lc3NhZ2Uob3V0cHV0OiBPdXRwdXQpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBUT0RPOiBSZW5kZXIgcmVjdXJzaXZlbHlcbiAgc3dpdGNoIChvdXRwdXQudHlwZSkge1xuICAgIGNhc2UgJ2ltYWdlJzoge1xuICAgICAgY29uc3QgeyBvcmlnaW5hbFNpemUgfSA9IG91dHB1dC5maWxlXG4gICAgICBjb25zdCBmb3JtYXR0ZWRTaXplID0gZm9ybWF0RmlsZVNpemUob3JpZ2luYWxTaXplKVxuXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8TWVzc2FnZVJlc3BvbnNlIGhlaWdodD17MX0+XG4gICAgICAgICAgPFRleHQ+UmVhZCBpbWFnZSAoe2Zvcm1hdHRlZFNpemV9KTwvVGV4dD5cbiAgICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgICApXG4gICAgfVxuICAgIGNhc2UgJ25vdGVib29rJzoge1xuICAgICAgY29uc3QgeyBjZWxscyB9ID0gb3V0cHV0LmZpbGVcbiAgICAgIGlmICghY2VsbHMgfHwgY2VsbHMubGVuZ3RoIDwgMSkge1xuICAgICAgICByZXR1cm4gPFRleHQgY29sb3I9XCJlcnJvclwiPk5vIGNlbGxzIGZvdW5kIGluIG5vdGVib29rPC9UZXh0PlxuICAgICAgfVxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPE1lc3NhZ2VSZXNwb25zZSBoZWlnaHQ9ezF9PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgUmVhZCA8VGV4dCBib2xkPntjZWxscy5sZW5ndGh9PC9UZXh0PiBjZWxsc1xuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgICApXG4gICAgfVxuICAgIGNhc2UgJ3BkZic6IHtcbiAgICAgIGNvbnN0IHsgb3JpZ2luYWxTaXplIH0gPSBvdXRwdXQuZmlsZVxuICAgICAgY29uc3QgZm9ybWF0dGVkU2l6ZSA9IGZvcm1hdEZpbGVTaXplKG9yaWdpbmFsU2l6ZSlcblxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPE1lc3NhZ2VSZXNwb25zZSBoZWlnaHQ9ezF9PlxuICAgICAgICAgIDxUZXh0PlJlYWQgUERGICh7Zm9ybWF0dGVkU2l6ZX0pPC9UZXh0PlxuICAgICAgICA8L01lc3NhZ2VSZXNwb25zZT5cbiAgICAgIClcbiAgICB9XG4gICAgY2FzZSAncGFydHMnOiB7XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8TWVzc2FnZVJlc3BvbnNlIGhlaWdodD17MX0+XG4gICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICBSZWFkIDxUZXh0IGJvbGQ+e291dHB1dC5maWxlLmNvdW50fTwvVGV4dD57JyAnfVxuICAgICAgICAgICAge291dHB1dC5maWxlLmNvdW50ID09PSAxID8gJ3BhZ2UnIDogJ3BhZ2VzJ30gKFxuICAgICAgICAgICAge2Zvcm1hdEZpbGVTaXplKG91dHB1dC5maWxlLm9yaWdpbmFsU2l6ZSl9KVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgICApXG4gICAgfVxuICAgIGNhc2UgJ3RleHQnOiB7XG4gICAgICBjb25zdCB7IG51bUxpbmVzIH0gPSBvdXRwdXQuZmlsZVxuXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8TWVzc2FnZVJlc3BvbnNlIGhlaWdodD17MX0+XG4gICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICBSZWFkIDxUZXh0IGJvbGQ+e251bUxpbmVzfTwvVGV4dD57JyAnfVxuICAgICAgICAgICAge251bUxpbmVzID09PSAxID8gJ2xpbmUnIDogJ2xpbmVzJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvTWVzc2FnZVJlc3BvbnNlPlxuICAgICAgKVxuICAgIH1cbiAgICBjYXNlICdmaWxlX3VuY2hhbmdlZCc6IHtcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxNZXNzYWdlUmVzcG9uc2UgaGVpZ2h0PXsxfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5VbmNoYW5nZWQgc2luY2UgbGFzdCByZWFkPC9UZXh0PlxuICAgICAgICA8L01lc3NhZ2VSZXNwb25zZT5cbiAgICAgIClcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlclRvb2xVc2VFcnJvck1lc3NhZ2UoXG4gIHJlc3VsdDogVG9vbFJlc3VsdEJsb2NrUGFyYW1bJ2NvbnRlbnQnXSxcbiAgeyB2ZXJib3NlIH06IHsgdmVyYm9zZTogYm9vbGVhbiB9LFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKCF2ZXJib3NlICYmIHR5cGVvZiByZXN1bHQgPT09ICdzdHJpbmcnKSB7XG4gICAgLy8gRmlsZVJlYWRUb29sIHRocm93cyBmcm9tIGNhbGwoKSBzbyBlcnJvcnMgbGFjayA8dG9vbF91c2VfZXJyb3I+IHdyYXBwaW5nIOKAlFxuICAgIC8vIGNoZWNrIHRoZSByYXcgc3RyaW5nIGRpcmVjdGx5IGZvciB0aGUgY3dkIG5vdGUgbWFya2VyLlxuICAgIGlmIChyZXN1bHQuaW5jbHVkZXMoRklMRV9OT1RfRk9VTkRfQ1dEX05PVEUpKSB7XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8TWVzc2FnZVJlc3BvbnNlPlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5GaWxlIG5vdCBmb3VuZDwvVGV4dD5cbiAgICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgICApXG4gICAgfVxuICAgIGlmIChleHRyYWN0VGFnKHJlc3VsdCwgJ3Rvb2xfdXNlX2Vycm9yJykpIHtcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxNZXNzYWdlUmVzcG9uc2U+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPkVycm9yIHJlYWRpbmcgZmlsZTwvVGV4dD5cbiAgICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgICApXG4gICAgfVxuICB9XG4gIHJldHVybiA8RmFsbGJhY2tUb29sVXNlRXJyb3JNZXNzYWdlIHJlc3VsdD17cmVzdWx0fSB2ZXJib3NlPXt2ZXJib3NlfSAvPlxufVxuXG5leHBvcnQgZnVuY3Rpb24gdXNlckZhY2luZ05hbWUoaW5wdXQ6IFBhcnRpYWw8SW5wdXQ+IHwgdW5kZWZpbmVkKTogc3RyaW5nIHtcbiAgaWYgKGlucHV0Py5maWxlX3BhdGg/LnN0YXJ0c1dpdGgoZ2V0UGxhbnNEaXJlY3RvcnkoKSkpIHtcbiAgICByZXR1cm4gJ1JlYWRpbmcgUGxhbidcbiAgfVxuICBpZiAoaW5wdXQ/LmZpbGVfcGF0aCAmJiBnZXRBZ2VudE91dHB1dFRhc2tJZChpbnB1dC5maWxlX3BhdGgpKSB7XG4gICAgcmV0dXJuICdSZWFkIGFnZW50IG91dHB1dCdcbiAgfVxuICByZXR1cm4gJ1JlYWQnXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRUb29sVXNlU3VtbWFyeShcbiAgaW5wdXQ6IFBhcnRpYWw8SW5wdXQ+IHwgdW5kZWZpbmVkLFxuKTogc3RyaW5nIHwgbnVsbCB7XG4gIGlmICghaW5wdXQ/LmZpbGVfcGF0aCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cbiAgLy8gRm9yIGFnZW50IG91dHB1dCBmaWxlcywganVzdCBzaG93IHRoZSB0YXNrIElEXG4gIGNvbnN0IGFnZW50VGFza0lkID0gZ2V0QWdlbnRPdXRwdXRUYXNrSWQoaW5wdXQuZmlsZV9wYXRoKVxuICBpZiAoYWdlbnRUYXNrSWQpIHtcbiAgICByZXR1cm4gYWdlbnRUYXNrSWRcbiAgfVxuICByZXR1cm4gZ2V0RGlzcGxheVBhdGgoaW5wdXQuZmlsZV9wYXRoKVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxjQUFjQSxvQkFBb0IsUUFBUSx1Q0FBdUM7QUFDakYsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxVQUFVLFFBQVEsdUJBQXVCO0FBQ2xELFNBQVNDLDJCQUEyQixRQUFRLGlEQUFpRDtBQUM3RixTQUFTQyxZQUFZLFFBQVEsa0NBQWtDO0FBQy9ELFNBQVNDLGVBQWUsUUFBUSxxQ0FBcUM7QUFDckUsU0FBU0MsSUFBSSxRQUFRLGNBQWM7QUFDbkMsU0FBU0MsdUJBQXVCLEVBQUVDLGNB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