{"version":3,"file":"utils/share.mjs","sources":["webpack://@agent-tars/server/./src/utils/share.ts"],"sourcesContent":["/*\n * Copyright (c) 2025 Bytedance, Inc. and its affiliates.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport os from 'os';\nimport { AgentEventStream } from '@agent-tars/core';\nimport { SessionMetadata } from '../storage';\n\n/**\n * ShareUtils - Utility functions for sharing session data\n *\n * Provides methods for:\n * - Generating HTML for sharing\n * - Uploading share HTML to providers\n */\nexport class ShareUtils {\n  /**\n   * Generate shareable HTML content for a session\n   * @param events Session events to include\n   * @param metadata Session metadata\n   * @param staticPath Path to static web UI files\n   * @param modelInfo Model information to include\n   * @returns Generated HTML content\n   */\n  static generateShareHtml(\n    events: AgentEventStream.Event[],\n    metadata: SessionMetadata,\n    staticPath: string,\n    modelInfo: { provider: string; model: string },\n  ): string {\n    if (!staticPath) {\n      throw new Error('Cannot found static path.');\n    }\n\n    const indexPath = path.join(staticPath, 'index.html');\n    if (!fs.existsSync(indexPath)) {\n      throw new Error('Static web ui not found.');\n    }\n\n    try {\n      let htmlContent = fs.readFileSync(indexPath, 'utf8');\n\n      const safeEventJson = this.safeJsonStringify(events);\n      const safeMetadataJson = this.safeJsonStringify(metadata);\n      const safeModelInfoJson = this.safeJsonStringify(modelInfo);\n\n      // Inject session data and event stream\n      const scriptTag = `<script>\n        window.AGENT_TARS_REPLAY_MODE = true;\n        window.AGENT_TARS_SESSION_DATA = ${safeMetadataJson};\n        window.AGENT_TARS_EVENT_STREAM = ${safeEventJson};\n        window.AGENT_TARS_MODEL_INFO = ${safeModelInfoJson};\n      </script>\n      <script>\n        // Add a fallback mechanism for when routes don't match in shared HTML files\n        window.addEventListener('DOMContentLoaded', function() {\n          // Give React time to attempt normal routing\n          setTimeout(function() {\n            const root = document.getElementById('root');\n            if (root && (!root.children || root.children.length === 0)) {\n              console.log('[ReplayMode] No content rendered, applying fallback');\n              // Try to force the app to re-render if no content is displayed\n              window.dispatchEvent(new Event('resize'));\n            }\n          }, 1000);\n        });\n      </script>`;\n\n      // Insert script before the head end tag\n      htmlContent = htmlContent.replace('</head>', `${scriptTag}\\n</head>`);\n\n      return htmlContent;\n    } catch (error) {\n      console.error('Failed to generate share HTML:', error);\n      throw new Error(\n        `Failed to generate share HTML: ${error instanceof Error ? error.message : String(error)}`,\n      );\n    }\n  }\n\n  /**\n   * Safely stringify JSON data containing HTML content\n   * This ensures HTML in the data won't break the embedding script\n   * @param data The data to stringify\n   * @returns Safe JSON string\n   */\n  private static safeJsonStringify(data: object): string {\n    let jsonString = JSON.stringify(data);\n\n    // Escape all characters that may destroy the HTML structure\n    // 1. Escape all angle brackets to prevent any HTML tags from being parsed by the browser\n    jsonString = jsonString.replace(/</g, '\\\\u003C');\n    jsonString = jsonString.replace(/>/g, '\\\\u003E');\n\n    // 2. Escape other potentially dangerous characters\n    jsonString = jsonString.replace(/\\//g, '\\\\/'); // Escape slashes to prevent closing tags such as </script>\n\n    return jsonString;\n  }\n\n  /**\n   * Upload HTML to a share provider\n   * @param html HTML content to upload\n   * @param sessionId Session ID\n   * @param shareProviderUrl URL of the share provider\n   * @param options Additional share metadata options\n   * @returns URL of the shared content\n   */\n  static async uploadShareHtml(\n    html: string,\n    sessionId: string,\n    shareProviderUrl: string,\n    options?: {\n      /**\n       * Session metadata containing additional session information\n       */\n      metadata?: SessionMetadata;\n\n      /**\n       * Normalized slug for semantic URLs, derived from user query\n       */\n      slug?: string;\n\n      /**\n       * Original query that initiated the conversation\n       */\n      query?: string;\n    },\n  ): Promise<string> {\n    if (!shareProviderUrl) {\n      throw new Error('Share provider not configured');\n    }\n\n    try {\n      // Create temporary directory\n      const tempDir = path.join(os.tmpdir(), 'agent-tars-share');\n      if (!fs.existsSync(tempDir)) {\n        fs.mkdirSync(tempDir, { recursive: true });\n      }\n\n      const fileName = `agent-tars-${sessionId}-${Date.now()}.html`;\n      const filePath = path.join(tempDir, fileName);\n\n      // Write HTML content to temporary file\n      fs.writeFileSync(filePath, html);\n\n      // Create form data using native FormData\n      const formData = new FormData();\n\n      // Create a File object from the HTML content\n      const file = new File([html], fileName, { type: 'text/html' });\n      formData.append('file', file);\n      formData.append('sessionId', sessionId);\n\n      // Add additional metadata fields if provided\n      if (options) {\n        // Add normalized slug for semantic URLs\n        if (options.slug) {\n          formData.append('slug', options.slug);\n        }\n\n        // Add original query\n        if (options.query) {\n          formData.append('query', options.query);\n        }\n\n        // Add session metadata fields\n        if (options.metadata) {\n          formData.append('name', options.metadata.name || '');\n          // Add tags if available\n          if (options.metadata.tags && options.metadata.tags.length > 0) {\n            formData.append('tags', JSON.stringify(options.metadata.tags));\n          }\n        }\n      }\n\n      // Send request to share provider using fetch\n      const response = await fetch(shareProviderUrl, {\n        method: 'POST',\n        body: formData,\n      });\n\n      // Clean up temporary file\n      fs.unlinkSync(filePath);\n\n      if (!response.ok) {\n        throw new Error(`HTTP error! status: ${response.status}`);\n      }\n\n      const responseData = await response.json();\n\n      // Return share URL\n      if (responseData && responseData.url) {\n        return responseData.url;\n      }\n\n      throw new Error('Invalid response from share provider');\n    } catch (error) {\n      console.error('Failed to upload share HTML:', error);\n      throw error;\n    }\n  }\n}\n"],"names":["ShareUtils","events","metadata","staticPath","modelInfo","Error","indexPath","path","fs","htmlContent","safeEventJson","safeMetadataJson","safeModelInfoJson","scriptTag","error","console","String","data","jsonString","JSON","html","sessionId","shareProviderUrl","options","tempDir","os","fileName","Date","filePath","formData","FormData","file","File","response","fetch","responseData"],"mappings":"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"}