{"version":3,"sources":["/home/runner/work/wuying-agentbay-sdk/wuying-agentbay-sdk/typescript/dist/index.cjs","../node_modules/dotenv/package.json","../node_modules/dotenv/lib/main.js","../node_modules/dotenv/lib/env-options.js","../node_modules/dotenv/lib/cli-options.js","../src/index.ts","../src/version.ts","../src/agent-bay.ts","../node_modules/dotenv/config.js","../src/api/index.ts","../src/api/client.ts","../src/context.ts","../src/exceptions.ts","../src/utils/logger.ts","../src/types/api-response.ts","../src/context-sync.ts","../src/session.ts","../src/agent/agent.ts","../src/browser/index.ts","../src/browser/browser.ts","../src/browser/browser_agent.ts","../src/browser/fingerprint.ts","../src/code/index.ts","../src/code/code.ts","../src/command/command.ts","../src/command/command-templates.ts","../src/computer/index.ts","../src/computer/computer.ts","../src/context-manager.ts","../src/filesystem/filesystem.ts","../src/filesystem/file-transfer.ts","../src/mobile/index.ts","../src/mobile/mobile.ts","../src/oss/index.ts","../src/oss/oss.ts","../src/agent/index.ts","../src/extension.ts","../src/types/index.ts","../src/types/list-session-params.ts","../src/types/extra-configs.ts","../src/session-params.ts"],"names":["fs","path","crypto","parse","context","errorMessage","resolve","elapsed","UploadStrategy","DownloadStrategy","UploadMode","Lifecycle","MouseButton","ScrollDirection","fullResponse","BROWSER_DATA_PATH","requestId","BROWSER_FINGERPRINT_PERSIST_PATH"],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;ACrGA,IAAA,gBAAA,EAAA,0CAAA;AAAA,EAAA,kCAAA,CAAA,OAAA,EAAA,MAAA,EAAA;AAAA,IAAA,MAAA,CAAA,QAAA,EAAA;AAAA,MACE,IAAA,EAAQ,QAAA;AAAA,MACR,OAAA,EAAW,QAAA;AAAA,MACX,WAAA,EAAe,4CAAA;AAAA,MACf,IAAA,EAAQ,aAAA;AAAA,MACR,KAAA,EAAS,eAAA;AAAA,MACT,OAAA,EAAW;AAAA,QACT,GAAA,EAAK;AAAA,UACH,KAAA,EAAS,iBAAA;AAAA,UACT,OAAA,EAAW,eAAA;AAAA,UACX,OAAA,EAAW;AAAA,QACb,CAAA;AAAA,QACA,UAAA,EAAY,aAAA;AAAA,QACZ,aAAA,EAAe,aAAA;AAAA,QACf,mBAAA,EAAqB,sBAAA;AAAA,QACrB,sBAAA,EAAwB,sBAAA;AAAA,QACxB,mBAAA,EAAqB,sBAAA;AAAA,QACrB,sBAAA,EAAwB,sBAAA;AAAA,QACxB,gBAAA,EAAkB;AAAA,MACpB,CAAA;AAAA,MACA,OAAA,EAAW;AAAA,QACT,WAAA,EAAa,yCAAA;AAAA,QACb,IAAA,EAAQ,UAAA;AAAA,QACR,OAAA,EAAW,mCAAA;AAAA,QACX,IAAA,EAAQ,mE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,UAAA;AACA,UAAA;AACL,QAAA;AACF,MAAA;AACF,IAAA;AAE+B,IAAA;AACN,IAAA;AACW,IAAA;AACP,IAAA;AACtB,IAAA;AACT,EAAA;AACF;AAtQsE;AAA/D;AA2QuD;AAC7B,EAAA;AACjC;AAFgB;AxCozWuC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/wuying-agentbay-sdk/wuying-agentbay-sdk/typescript/dist/index.cjs","sourcesContent":[null,"{\n  \"name\": \"dotenv\",\n  \"version\": \"16.6.1\",\n  \"description\": \"Loads environment variables from .env file\",\n  \"main\": \"lib/main.js\",\n  \"types\": \"lib/main.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./lib/main.d.ts\",\n      \"require\": \"./lib/main.js\",\n      \"default\": \"./lib/main.js\"\n    },\n    \"./config\": \"./config.js\",\n    \"./config.js\": \"./config.js\",\n    \"./lib/env-options\": \"./lib/env-options.js\",\n    \"./lib/env-options.js\": \"./lib/env-options.js\",\n    \"./lib/cli-options\": \"./lib/cli-options.js\",\n    \"./lib/cli-options.js\": \"./lib/cli-options.js\",\n    \"./package.json\": \"./package.json\"\n  },\n  \"scripts\": {\n    \"dts-check\": \"tsc --project tests/types/tsconfig.json\",\n    \"lint\": \"standard\",\n    \"pretest\": \"npm run lint && npm run dts-check\",\n    \"test\": \"tap run --allow-empty-coverage --disable-coverage --timeout=60000\",\n    \"test:coverage\": \"tap run --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov\",\n    \"prerelease\": \"npm test\",\n    \"release\": \"standard-version\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/motdotla/dotenv.git\"\n  },\n  \"homepage\": \"https://github.com/motdotla/dotenv#readme\",\n  \"funding\": \"https://dotenvx.com\",\n  \"keywords\": [\n    \"dotenv\",\n    \"env\",\n    \".env\",\n    \"environment\",\n    \"variables\",\n    \"config\",\n    \"settings\"\n  ],\n  \"readmeFilename\": \"README.md\",\n  \"license\": \"BSD-2-Clause\",\n  \"devDependencies\": {\n    \"@types/node\": \"^18.11.3\",\n    \"decache\": \"^4.6.2\",\n    \"sinon\": \"^14.0.1\",\n    \"standard\": \"^17.0.0\",\n    \"standard-version\": \"^9.5.0\",\n    \"tap\": \"^19.2.0\",\n    \"typescript\": \"^4.8.4\"\n  },\n  \"engines\": {\n    \"node\": \">=12\"\n  },\n  \"browser\": {\n    \"fs\": false\n  }\n}\n","const fs = require('fs')\nconst path = require('path')\nconst os = require('os')\nconst crypto = require('crypto')\nconst packageJson = require('../package.json')\n\nconst version = packageJson.version\n\nconst LINE = /(?:^|^)\\s*(?:export\\s+)?([\\w.-]+)(?:\\s*=\\s*?|:\\s+?)(\\s*'(?:\\\\'|[^'])*'|\\s*\"(?:\\\\\"|[^\"])*\"|\\s*`(?:\\\\`|[^`])*`|[^#\\r\\n]+)?\\s*(?:#.*)?(?:$|$)/mg\n\n// Parse src into an Object\nfunction parse (src) {\n  const obj = {}\n\n  // Convert buffer to string\n  let lines = src.toString()\n\n  // Convert line breaks to same format\n  lines = lines.replace(/\\r\\n?/mg, '\\n')\n\n  let match\n  while ((match = LINE.exec(lines)) != null) {\n    const key = match[1]\n\n    // Default undefined or null to empty string\n    let value = (match[2] || '')\n\n    // Remove whitespace\n    value = value.trim()\n\n    // Check if double quoted\n    const maybeQuote = value[0]\n\n    // Remove surrounding quotes\n    value = value.replace(/^(['\"`])([\\s\\S]*)\\1$/mg, '$2')\n\n    // Expand newlines if double quoted\n    if (maybeQuote === '\"') {\n      value = value.replace(/\\\\n/g, '\\n')\n      value = value.replace(/\\\\r/g, '\\r')\n    }\n\n    // Add to object\n    obj[key] = value\n  }\n\n  return obj\n}\n\nfunction _parseVault (options) {\n  options = options || {}\n\n  const vaultPath = _vaultPath(options)\n  options.path = vaultPath // parse .env.vault\n  const result = DotenvModule.configDotenv(options)\n  if (!result.parsed) {\n    const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`)\n    err.code = 'MISSING_DATA'\n    throw err\n  }\n\n  // handle scenario for comma separated keys - for use with key rotation\n  // example: DOTENV_KEY=\"dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=prod,dotenv://:key_7890@dotenvx.com/vault/.env.vault?environment=prod\"\n  const keys = _dotenvKey(options).split(',')\n  const length = keys.length\n\n  let decrypted\n  for (let i = 0; i < length; i++) {\n    try {\n      // Get full key\n      const key = keys[i].trim()\n\n      // Get instructions for decrypt\n      const attrs = _instructions(result, key)\n\n      // Decrypt\n      decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key)\n\n      break\n    } catch (error) {\n      // last key\n      if (i + 1 >= length) {\n        throw error\n      }\n      // try next key\n    }\n  }\n\n  // Parse decrypted .env string\n  return DotenvModule.parse(decrypted)\n}\n\nfunction _warn (message) {\n  console.log(`[dotenv@${version}][WARN] ${message}`)\n}\n\nfunction _debug (message) {\n  console.log(`[dotenv@${version}][DEBUG] ${message}`)\n}\n\nfunction _log (message) {\n  console.log(`[dotenv@${version}] ${message}`)\n}\n\nfunction _dotenvKey (options) {\n  // prioritize developer directly setting options.DOTENV_KEY\n  if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {\n    return options.DOTENV_KEY\n  }\n\n  // secondary infra already contains a DOTENV_KEY environment variable\n  if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {\n    return process.env.DOTENV_KEY\n  }\n\n  // fallback to empty string\n  return ''\n}\n\nfunction _instructions (result, dotenvKey) {\n  // Parse DOTENV_KEY. Format is a URI\n  let uri\n  try {\n    uri = new URL(dotenvKey)\n  } catch (error) {\n    if (error.code === 'ERR_INVALID_URL') {\n      const err = new Error('INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development')\n      err.code = 'INVALID_DOTENV_KEY'\n      throw err\n    }\n\n    throw error\n  }\n\n  // Get decrypt key\n  const key = uri.password\n  if (!key) {\n    const err = new Error('INVALID_DOTENV_KEY: Missing key part')\n    err.code = 'INVALID_DOTENV_KEY'\n    throw err\n  }\n\n  // Get environment\n  const environment = uri.searchParams.get('environment')\n  if (!environment) {\n    const err = new Error('INVALID_DOTENV_KEY: Missing environment part')\n    err.code = 'INVALID_DOTENV_KEY'\n    throw err\n  }\n\n  // Get ciphertext payload\n  const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`\n  const ciphertext = result.parsed[environmentKey] // DOTENV_VAULT_PRODUCTION\n  if (!ciphertext) {\n    const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`)\n    err.code = 'NOT_FOUND_DOTENV_ENVIRONMENT'\n    throw err\n  }\n\n  return { ciphertext, key }\n}\n\nfunction _vaultPath (options) {\n  let possibleVaultPath = null\n\n  if (options && options.path && options.path.length > 0) {\n    if (Array.isArray(options.path)) {\n      for (const filepath of options.path) {\n        if (fs.existsSync(filepath)) {\n          possibleVaultPath = filepath.endsWith('.vault') ? filepath : `${filepath}.vault`\n        }\n      }\n    } else {\n      possibleVaultPath = options.path.endsWith('.vault') ? options.path : `${options.path}.vault`\n    }\n  } else {\n    possibleVaultPath = path.resolve(process.cwd(), '.env.vault')\n  }\n\n  if (fs.existsSync(possibleVaultPath)) {\n    return possibleVaultPath\n  }\n\n  return null\n}\n\nfunction _resolveHome (envPath) {\n  return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath\n}\n\nfunction _configVault (options) {\n  const debug = Boolean(options && options.debug)\n  const quiet = options && 'quiet' in options ? options.quiet : true\n\n  if (debug || !quiet) {\n    _log('Loading env from encrypted .env.vault')\n  }\n\n  const parsed = DotenvModule._parseVault(options)\n\n  let processEnv = process.env\n  if (options && options.processEnv != null) {\n    processEnv = options.processEnv\n  }\n\n  DotenvModule.populate(processEnv, parsed, options)\n\n  return { parsed }\n}\n\nfunction configDotenv (options) {\n  const dotenvPath = path.resolve(process.cwd(), '.env')\n  let encoding = 'utf8'\n  const debug = Boolean(options && options.debug)\n  const quiet = options && 'quiet' in options ? options.quiet : true\n\n  if (options && options.encoding) {\n    encoding = options.encoding\n  } else {\n    if (debug) {\n      _debug('No encoding is specified. UTF-8 is used by default')\n    }\n  }\n\n  let optionPaths = [dotenvPath] // default, look for .env\n  if (options && options.path) {\n    if (!Array.isArray(options.path)) {\n      optionPaths = [_resolveHome(options.path)]\n    } else {\n      optionPaths = [] // reset default\n      for (const filepath of options.path) {\n        optionPaths.push(_resolveHome(filepath))\n      }\n    }\n  }\n\n  // Build the parsed data in a temporary object (because we need to return it).  Once we have the final\n  // parsed data, we will combine it with process.env (or options.processEnv if provided).\n  let lastError\n  const parsedAll = {}\n  for (const path of optionPaths) {\n    try {\n      // Specifying an encoding returns a string instead of a buffer\n      const parsed = DotenvModule.parse(fs.readFileSync(path, { encoding }))\n\n      DotenvModule.populate(parsedAll, parsed, options)\n    } catch (e) {\n      if (debug) {\n        _debug(`Failed to load ${path} ${e.message}`)\n      }\n      lastError = e\n    }\n  }\n\n  let processEnv = process.env\n  if (options && options.processEnv != null) {\n    processEnv = options.processEnv\n  }\n\n  DotenvModule.populate(processEnv, parsedAll, options)\n\n  if (debug || !quiet) {\n    const keysCount = Object.keys(parsedAll).length\n    const shortPaths = []\n    for (const filePath of optionPaths) {\n      try {\n        const relative = path.relative(process.cwd(), filePath)\n        shortPaths.push(relative)\n      } catch (e) {\n        if (debug) {\n          _debug(`Failed to load ${filePath} ${e.message}`)\n        }\n        lastError = e\n      }\n    }\n\n    _log(`injecting env (${keysCount}) from ${shortPaths.join(',')}`)\n  }\n\n  if (lastError) {\n    return { parsed: parsedAll, error: lastError }\n  } else {\n    return { parsed: parsedAll }\n  }\n}\n\n// Populates process.env from .env file\nfunction config (options) {\n  // fallback to original dotenv if DOTENV_KEY is not set\n  if (_dotenvKey(options).length === 0) {\n    return DotenvModule.configDotenv(options)\n  }\n\n  const vaultPath = _vaultPath(options)\n\n  // dotenvKey exists but .env.vault file does not exist\n  if (!vaultPath) {\n    _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`)\n\n    return DotenvModule.configDotenv(options)\n  }\n\n  return DotenvModule._configVault(options)\n}\n\nfunction decrypt (encrypted, keyStr) {\n  const key = Buffer.from(keyStr.slice(-64), 'hex')\n  let ciphertext = Buffer.from(encrypted, 'base64')\n\n  const nonce = ciphertext.subarray(0, 12)\n  const authTag = ciphertext.subarray(-16)\n  ciphertext = ciphertext.subarray(12, -16)\n\n  try {\n    const aesgcm = crypto.createDecipheriv('aes-256-gcm', key, nonce)\n    aesgcm.setAuthTag(authTag)\n    return `${aesgcm.update(ciphertext)}${aesgcm.final()}`\n  } catch (error) {\n    const isRange = error instanceof RangeError\n    const invalidKeyLength = error.message === 'Invalid key length'\n    const decryptionFailed = error.message === 'Unsupported state or unable to authenticate data'\n\n    if (isRange || invalidKeyLength) {\n      const err = new Error('INVALID_DOTENV_KEY: It must be 64 characters long (or more)')\n      err.code = 'INVALID_DOTENV_KEY'\n      throw err\n    } else if (decryptionFailed) {\n      const err = new Error('DECRYPTION_FAILED: Please check your DOTENV_KEY')\n      err.code = 'DECRYPTION_FAILED'\n      throw err\n    } else {\n      throw error\n    }\n  }\n}\n\n// Populate process.env with parsed values\nfunction populate (processEnv, parsed, options = {}) {\n  const debug = Boolean(options && options.debug)\n  const override = Boolean(options && options.override)\n\n  if (typeof parsed !== 'object') {\n    const err = new Error('OBJECT_REQUIRED: Please check the processEnv argument being passed to populate')\n    err.code = 'OBJECT_REQUIRED'\n    throw err\n  }\n\n  // Set process.env\n  for (const key of Object.keys(parsed)) {\n    if (Object.prototype.hasOwnProperty.call(processEnv, key)) {\n      if (override === true) {\n        processEnv[key] = parsed[key]\n      }\n\n      if (debug) {\n        if (override === true) {\n          _debug(`\"${key}\" is already defined and WAS overwritten`)\n        } else {\n          _debug(`\"${key}\" is already defined and was NOT overwritten`)\n        }\n      }\n    } else {\n      processEnv[key] = parsed[key]\n    }\n  }\n}\n\nconst DotenvModule = {\n  configDotenv,\n  _configVault,\n  _parseVault,\n  config,\n  decrypt,\n  parse,\n  populate\n}\n\nmodule.exports.configDotenv = DotenvModule.configDotenv\nmodule.exports._configVault = DotenvModule._configVault\nmodule.exports._parseVault = DotenvModule._parseVault\nmodule.exports.config = DotenvModule.config\nmodule.exports.decrypt = DotenvModule.decrypt\nmodule.exports.parse = DotenvModule.parse\nmodule.exports.populate = DotenvModule.populate\n\nmodule.exports = DotenvModule\n","// ../config.js accepts options via environment variables\nconst options = {}\n\nif (process.env.DOTENV_CONFIG_ENCODING != null) {\n  options.encoding = process.env.DOTENV_CONFIG_ENCODING\n}\n\nif (process.env.DOTENV_CONFIG_PATH != null) {\n  options.path = process.env.DOTENV_CONFIG_PATH\n}\n\nif (process.env.DOTENV_CONFIG_QUIET != null) {\n  options.quiet = process.env.DOTENV_CONFIG_QUIET\n}\n\nif (process.env.DOTENV_CONFIG_DEBUG != null) {\n  options.debug = process.env.DOTENV_CONFIG_DEBUG\n}\n\nif (process.env.DOTENV_CONFIG_OVERRIDE != null) {\n  options.override = process.env.DOTENV_CONFIG_OVERRIDE\n}\n\nif (process.env.DOTENV_CONFIG_DOTENV_KEY != null) {\n  options.DOTENV_KEY = process.env.DOTENV_CONFIG_DOTENV_KEY\n}\n\nmodule.exports = options\n","const re = /^dotenv_config_(encoding|path|quiet|debug|override|DOTENV_KEY)=(.+)$/\n\nmodule.exports = function optionMatcher (args) {\n  const options = args.reduce(function (acc, cur) {\n    const matches = cur.match(re)\n    if (matches) {\n      acc[matches[1]] = matches[2]\n    }\n    return acc\n  }, {})\n\n  if (!('quiet' in options)) {\n    options.quiet = 'true'\n  }\n\n  return options\n}\n","// IMPORTANT: Load config first to ensure .env file is loaded before logger initialization\nexport { type Config } from \"./config\";\n\n// Export version information\nexport { VERSION, IS_RELEASE } from \"./version\";\n\n// Export all public classes and interfaces\nexport { AgentBay, type CreateSessionParams} from \"./agent-bay\";\nexport * from \"./agent\";\nexport * from \"./api\";\nexport * from \"./browser\";\nexport { Code } from \"./code\";\nexport * from \"./command\";\nexport { Context, ContextService } from \"./context\";\nexport { Computer } from \"./computer\";\nexport * from \"./exceptions\";\nexport * from \"./extension\";\nexport * from \"./filesystem\";\nexport * from \"./oss\";\nexport { Mobile } from \"./mobile\";\nexport { Session } from \"./session\";\nexport { type ListSessionParams } from \"./types\";\nexport * from \"./types\";\nexport * from \"./context-sync\";\nexport * from \"./context-manager\";\nexport * from \"./session-params\";\n// Export utility functions\nexport {\n  log,\n  logDebug,\n  logInfo,\n  logWarn,\n  logError,\n  setLogLevel,\n  getLogLevel,\n  setupLogger,\n  type LogLevel,\n  type LoggerConfig\n} from \"./utils/logger\";\n","/**\n * Version information for the AgentBay SDK\n * Automatically read from package.json\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\nfunction getVersionFromPackageJson(): string {\n  try {\n    // Get the path to package.json (relative to this file)\n    // When compiled, this will be in dist/, so we need to go up to find package.json\n    const packageJsonPath = path.join(__dirname, \"..\", \"package.json\");\n    \n    if (fs.existsSync(packageJsonPath)) {\n      const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, \"utf-8\"));\n      return packageJson.version || \"0.0.0\";\n    }\n  } catch (error) {\n    // Fallback to default version if reading fails\n  }\n  \n  // Fallback version if package.json cannot be read\n  return \"0.9.4\";\n}\n\n/**\n * Check if this is a release build.\n *\n * This value can be overridden at build time by replacing the placeholder below.\n * The CI/CD workflow will replace __AGENTBAY_IS_RELEASE_BUILD__ with true for release builds.\n */\nfunction isReleaseBuild(): boolean {\n  // This placeholder will be replaced by the build process\n  // For release builds: sed -i 's/__AGENTBAY_IS_RELEASE_BUILD__/true/g' src/version.ts\n  return __AGENTBAY_IS_RELEASE_BUILD__;  // Default: false for development builds\n}\n\n// For release builds, the CI/CD will replace __AGENTBAY_IS_RELEASE_BUILD__ with true\nconst __AGENTBAY_IS_RELEASE_BUILD__ = true;\n\nexport const VERSION = getVersionFromPackageJson();\nexport const IS_RELEASE = isReleaseBuild();\n\n","import { $OpenApiUtil } from \"@alicloud/openapi-core\";\nimport \"dotenv/config\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as dotenv from \"dotenv\";\nimport * as $_client from \"./api\";\nimport { ListSessionRequest, CreateMcpSessionRequestPersistenceDataList, GetSessionRequest as $GetSessionRequest } from \"./api/models/model\";\nimport { Client } from \"./api/client\";\n\nimport { Config } from \"./config\";\nimport { ContextService } from \"./context\";\nimport { ContextSync } from \"./context-sync\";\nimport { APIError, AuthenticationError } from \"./exceptions\";\nimport { Session } from \"./session\";\nimport { BrowserContext } from \"./session-params\";\nimport { Context } from \"./context\";\nimport { ExtraConfigs } from \"./types/extra-configs\";\n\nimport {\n  DeleteResult,\n  extractRequestId,\n  GetSessionResult as $GetSessionResult,\n  SessionResult,\n} from \"./types/api-response\";\nimport {\n  ListSessionParams,\n  SessionListResult,\n} from \"./types/list-session-params\";\nimport {\n  log,\n  logError,\n  logInfo,\n  logDebug,\n  logAPICall,\n  logAPIResponseWithDetails,\n  maskSensitiveData,\n  setRequestId,\n  getRequestId,\n  logInfoWithColor,\n} from \"./utils/logger\";\nimport { VERSION, IS_RELEASE } from \"./version\";\n\n// Browser data path constant (moved from config.ts)\nconst BROWSER_DATA_PATH = \"/tmp/agentbay_browser\";\n\n/**\n * Returns the default configuration\n */\nfunction defaultConfig(): Config {\n  return {\n    endpoint: \"wuyingai.cn-shanghai.aliyuncs.com\",\n    timeout_ms: 60000,\n  };\n}\n\n/**\n * Find .env file by searching upward from start_path.\n */\nfunction findDotEnvFile(startPath?: string): string | null {\n  const currentPath = startPath ? path.resolve(startPath) : process.cwd();\n  let searchPath = currentPath;\n\n  while (searchPath !== path.dirname(searchPath)) {\n    const envFile = path.join(searchPath, \".env\");\n    if (fs.existsSync(envFile)) {\n      return envFile;\n    }\n\n    const gitDir = path.join(searchPath, \".git\");\n    if (fs.existsSync(gitDir)) {\n      // Found git root, continue searching\n    }\n\n    searchPath = path.dirname(searchPath);\n  }\n\n  const rootEnv = path.join(searchPath, \".env\");\n  if (fs.existsSync(rootEnv)) {\n    return rootEnv;\n  }\n\n  return null;\n}\n\n/**\n * Load .env file with improved search strategy.\n */\nfunction loadDotEnvWithFallback(customEnvPath?: string): void {\n  if (customEnvPath) {\n    if (fs.existsSync(customEnvPath)) {\n      try {\n        const envConfig = dotenv.parse(fs.readFileSync(customEnvPath));\n        for (const k in envConfig) {\n          if (!process.env.hasOwnProperty(k)) {\n            process.env[k] = envConfig[k];\n          }\n        }\n        return;\n      } catch (error) {\n        // Silently fail - .env loading is optional\n      }\n    }\n  }\n\n  const envFile = findDotEnvFile();\n  if (envFile) {\n    try {\n      const envConfig = dotenv.parse(fs.readFileSync(envFile));\n      for (const k in envConfig) {\n        if (!process.env.hasOwnProperty(k)) {\n          process.env[k] = envConfig[k];\n        }\n      }\n    } catch (error) {\n      // Silently fail - .env loading is optional\n    }\n  }\n}\n\n/**\n * Load configuration with improved .env file search.\n */\nfunction loadConfig(customConfig?: Config, customEnvPath?: string): Config {\n  if (customConfig) {\n    return customConfig;\n  }\n\n  const config = defaultConfig();\n\n  try {\n    loadDotEnvWithFallback(customEnvPath);\n  } catch (error) {\n    // Silently fail - .env loading is optional\n  }\n\n  if (process.env.AGENTBAY_ENDPOINT) {\n    config.endpoint = process.env.AGENTBAY_ENDPOINT;\n  }\n\n  if (process.env.AGENTBAY_TIMEOUT_MS) {\n    const timeout = parseInt(process.env.AGENTBAY_TIMEOUT_MS, 10);\n    if (!isNaN(timeout) && timeout > 0) {\n      config.timeout_ms = timeout;\n    }\n  }\n\n  return config;\n}\n\n/**\n * Generate a random context name using alphanumeric characters with timestamp.\n * This function is similar to the Python version's generate_random_context_name.\n */\nfunction generateRandomContextName(length = 8, includeTimestamp = true): string {\n  const timestamp = new Date().toISOString().replace(/[-T:.Z]/g, '').slice(0, 14);\n\n  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n  let randomPart = '';\n  for (let i = 0; i < length; i++) {\n    randomPart += characters.charAt(Math.floor(Math.random() * characters.length));\n  }\n\n  return includeTimestamp ? `${timestamp}_${randomPart}` : randomPart;\n}\n\n/**\n * Parameters for creating a session.\n */\nexport interface CreateSessionParams {\n  labels?: Record<string, string>;\n  imageId?: string;\n  contextSync?: ContextSync[];\n  browserContext?: BrowserContext;\n  isVpc?: boolean;\n  policyId?: string;\n  enableBrowserReplay?: boolean;\n  extraConfigs?: ExtraConfigs;\n  framework?: string;\n}\n\n/**\n * Main class for interacting with the AgentBay cloud runtime environment.\n */\nexport class AgentBay {\n  private apiKey: string;\n  private client: Client;\n  private endpoint: string;\n  private sessions: Map<string, Session> = new Map();\n  private fileTransferContext: Context | null = null;\n\n  /**\n   * Context service for managing persistent contexts.\n   */\n  context: ContextService;\n\n  /**\n   * Initialize the AgentBay client.\n   *\n   * @param options - Configuration options\n   * @param options.apiKey - API key for authentication. If not provided, will look for AGENTBAY_API_KEY environment variable.\n   * @param options.config - Custom configuration object. If not provided, will use environment-based configuration.\n   * @param options.envFile - Custom path to .env file. If not provided, will search upward from current directory.\n   */\n  constructor(\n    options: {\n      apiKey?: string;\n      config?: Config;\n      envFile?: string;\n    } = {}\n  ) {\n    // Load .env file first to ensure AGENTBAY_API_KEY is available\n    loadDotEnvWithFallback(options.envFile);\n\n    this.apiKey = options.apiKey || process.env.AGENTBAY_API_KEY || \"\";\n\n    if (!this.apiKey) {\n      throw new AuthenticationError(\n        \"API key is required. Provide it as a parameter or set the AGENTBAY_API_KEY environment variable.\"\n      );\n    }\n\n    // Load configuration using the enhanced loadConfig function\n    const configData = loadConfig(options.config, options.envFile);\n    this.endpoint = configData.endpoint;\n\n    const config = new $OpenApiUtil.Config({\n      regionId: \"\",\n      endpoint: this.endpoint,\n    });\n\n    config.readTimeout = configData.timeout_ms;\n    config.connectTimeout = configData.timeout_ms;\n\n    try {\n      this.client = new Client(config);\n\n      // Initialize context service\n      this.context = new ContextService(this);\n    } catch (error) {\n      logError(`Failed to constructor:`, error);\n      throw new AuthenticationError(`Failed to constructor: ${error}`);\n    }\n  }\n\n  /**\n   * Update browser replay context with AppInstanceId from response data.\n   *\n   * @param responseData - Response data containing AppInstanceId\n   * @param recordContextId - The record context ID to update\n   */\n  private async _updateBrowserReplayContext(responseData: any, recordContextId: string): Promise<void> {\n    // Check if record_context_id is provided\n    if (!recordContextId) {\n      return;\n    }\n\n    try {\n      // Extract AppInstanceId from response data\n      const appInstanceId = responseData?.appInstanceId;\n      if (!appInstanceId) {\n        logError(\"AppInstanceId not found in response data, skipping browser replay context update\");\n        return;\n      }\n\n      // Create context name with prefix\n      const contextName = `browserreplay-${appInstanceId}`;\n\n      // Create Context object for update\n      const contextObj = new Context(recordContextId, contextName);\n\n      // Call context.update interface\n      logDebug(`Updating browser replay context: ${contextName} -> ${recordContextId}`);\n      const updateResult = await this.context.update(contextObj);\n\n      if (updateResult.success) {\n        logInfo(`✅ Successfully updated browser replay context: ${contextName}`);\n      } else {\n        logError(`⚠️ Failed to update browser replay context: ${updateResult.errorMessage}`);\n      }\n    } catch (error) {\n      logError(`❌ Error updating browser replay context: ${error}`);\n      // Continue execution even if context update fails\n    }\n  }\n\n  /**\n   * Create a new session in the AgentBay cloud environment.\n   *\n   * @param params - Optional parameters for creating the session\n   * @returns SessionResult containing the created session and request ID\n   */\n  /**\n   * Creates a new AgentBay session with specified configuration.\n   *\n   * @param params - Configuration parameters for the session:\n   *                 - labels: Key-value pairs for session metadata\n   *                 - imageId: Custom image ID for the session environment\n   *                 - contextSync: Array of context synchronization configurations\n   *                 - browserContext: Browser-specific context configuration\n   *                 - isVpc: Whether to create a VPC session\n   *                 - policyId: Security policy ID\n   *                 - enableBrowserReplay: Enable browser session recording\n   *                 - extraConfigs: Additional configuration options\n   *                 - framework: Framework identifier for tracking\n   *\n   * @returns Promise resolving to SessionResult containing:\n   *          - success: Whether session creation succeeded\n   *          - session: Session object for interacting with the environment\n   *          - requestId: Unique identifier for this API request\n   *          - errorMessage: Error description if creation failed\n   *\n   * @throws Error if API call fails or authentication is invalid.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ labels: { project: 'demo' } });\n   * if (result.success) {\n   *   await result.session.filesystem.readFile('/etc/hostname');\n   *   await result.session.delete();\n   * }\n   * ```\n   *\n   * @remarks\n   * **Behavior:**\n   * - Creates a new isolated cloud runtime environment\n   * - Automatically creates file transfer context if not provided\n   * - Waits for context synchronization if contextSync is specified\n   * - For VPC sessions, includes VPC-specific configuration\n   * - Browser replay creates a separate recording context\n   *\n   * @see {@link get}, {@link list}, {@link Session.delete}\n   */\n  async create(params: CreateSessionParams = {}): Promise<SessionResult> {\n    try {\n      logDebug(`default context syncs length: ${params.contextSync?.length}`);\n      // Create a default context for file transfer operations if none provided\n      // and no context_syncs are specified\n      const contextName = `file-transfer-context-${Date.now()}`;\n      const contextResult = await this.context.get(contextName, true);\n      if (contextResult.success && contextResult.context) {\n        this.fileTransferContext = contextResult.context;\n        // Add the context to the session params for file transfer operations\n        const fileTransferContextSync = new ContextSync(\n          contextResult.context.id,\n          \"/temp/file-transfer\"\n        );\n        if (!params.contextSync) {\n          params.contextSync = [];\n        }\n        logDebug(`Adding context sync for file transfer operations: ${fileTransferContextSync}`);\n        params.contextSync.push(fileTransferContextSync);\n      }\n\n      const request = new $_client.CreateMcpSessionRequest({\n        authorization: \"Bearer \" + this.apiKey,\n      });\n\n      // Add SDK stats for tracking\n      const framework = params?.framework || \"\";\n      const sdkStatsJson = `{\"source\":\"sdk\",\"sdk_language\":\"typescript\",\"sdk_version\":\"${VERSION}\",\"is_release\":${IS_RELEASE},\"framework\":\"${framework}\"}`;\n      request.sdkStats = sdkStatsJson;\n\n      // Add labels if provided\n      if (params.labels) {\n        request.labels = JSON.stringify(params.labels);\n      }\n\n      // Add image_id if provided\n      if (params.imageId) {\n        request.imageId = params.imageId;\n      }\n\n      // Add PolicyId if provided\n      if (params.policyId) {\n        request.mcpPolicyId = params.policyId;\n      }\n\n      // Add VPC resource if specified\n      request.vpcResource = params.isVpc || false;\n\n      // Flag to indicate if we need to wait for context synchronization\n      let needsContextSync = false;\n\n      // Add context sync configurations if provided\n      if (params.contextSync && params.contextSync.length > 0) {\n        const persistenceDataList: CreateMcpSessionRequestPersistenceDataList[] = [];\n        for (const contextSync of params.contextSync) {\n          const persistenceItem = new CreateMcpSessionRequestPersistenceDataList({\n            contextId: contextSync.contextId,\n            path: contextSync.path,\n          });\n\n          // Convert policy to JSON string if provided\n          if (contextSync.policy) {\n            persistenceItem.policy = JSON.stringify(contextSync.policy);\n          }\n\n          persistenceDataList.push(persistenceItem);\n        }\n        request.persistenceDataList = persistenceDataList;\n        needsContextSync = persistenceDataList.length > 0;\n      }\n\n      // Add BrowserContext as a ContextSync if provided\n      if (params.browserContext) {\n        // Create a simple sync policy for browser context\n        const syncPolicy = {\n          uploadPolicy: { autoUpload: params.browserContext.autoUpload },\n          downloadPolicy: null,\n          deletePolicy: null,\n          bwList: null,\n          recyclePolicy: null,\n        };\n\n        // Create browser context sync item\n        const browserContextSync = new CreateMcpSessionRequestPersistenceDataList({\n          contextId: params.browserContext.contextId,\n          path: BROWSER_DATA_PATH, // Using a constant path for browser data\n          policy: JSON.stringify(syncPolicy)\n        });\n\n        // Add to persistence data list or create new one if not exists\n        if (!request.persistenceDataList) {\n          request.persistenceDataList = [];\n        }\n        request.persistenceDataList.push(browserContextSync);\n        needsContextSync = true;\n      }\n\n      // Add browser recording persistence if enabled\n      let recordContextId = \"\"; // Initialize record_context_id\n      if (params.enableBrowserReplay) {\n        // Create browser recording persistence configuration\n        const recordPath = \"/home/guest/record\";\n        const recordContextName = generateRandomContextName();\n        const result = await this.context.get(recordContextName, true);\n        recordContextId = result.success ? result.contextId : \"\";\n        const recordPersistence = new CreateMcpSessionRequestPersistenceDataList({\n          contextId: recordContextId,\n          path: recordPath,\n        });\n\n        // Add to persistence data list or create new one if not exists\n        if (!request.persistenceDataList) {\n          request.persistenceDataList = [];\n        }\n        request.persistenceDataList.push(recordPersistence);\n      }\n\n      // Add extra configs if provided\n      if (params.extraConfigs) {\n        request.extraConfigs = JSON.stringify(params.extraConfigs);\n      }\n\n      // Log API request\n      logAPICall(\"CreateMcpSession\", {\n        labels: params.labels,\n        imageId: params.imageId,\n        policyId: params.policyId,\n        isVpc: params.isVpc,\n        persistenceDataCount: params.contextSync ? params.contextSync.length : 0,\n      });\n\n      const response = await this.client.createMcpSession(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n      setRequestId(requestId);\n\n      const sessionData = response.body;\n\n      if (!sessionData || typeof sessionData !== \"object\") {\n        logAPIResponseWithDetails(\n          \"CreateMcpSession\",\n          requestId,\n          false,\n          {},\n          \"Invalid response format: expected a dictionary\"\n        );\n        logDebug(\"Full response:\", JSON.stringify(sessionData, null, 2));\n        return {\n          requestId,\n          success: false,\n          errorMessage: \"Invalid response format: expected a dictionary\",\n        };\n      }\n\n      // Check for API-level errors\n      if (sessionData.success === false && sessionData.code) {\n        const errorMessage = `[${sessionData.code}] ${sessionData.message || 'Unknown error'}`;\n        logAPIResponseWithDetails(\n          \"CreateMcpSession\",\n          requestId,\n          false,\n          {},\n          errorMessage\n        );\n        logDebug(\"Full response:\", JSON.stringify(sessionData, null, 2));\n        return {\n          requestId,\n          success: false,\n          errorMessage,\n        };\n      }\n\n      const data = sessionData.data;\n      if (!data || typeof data !== \"object\") {\n        logAPIResponseWithDetails(\n          \"CreateMcpSession\",\n          requestId,\n          false,\n          {},\n          \"Invalid response format: 'data' field is not a dictionary\"\n        );\n        logDebug(\"Full response:\", JSON.stringify(sessionData, null, 2));\n        return {\n          requestId,\n          success: false,\n          errorMessage:\n            \"Invalid response format: 'data' field is not a dictionary\",\n        };\n      }\n\n      // Check data-level success field (business logic success)\n      if (data.success === false) {\n        const errorMessage = data.errMsg || \"Session creation failed\";\n        logAPIResponseWithDetails(\n          \"CreateMcpSession\",\n          requestId,\n          false,\n          {},\n          errorMessage\n        );\n        logDebug(\"Full response:\", JSON.stringify(sessionData, null, 2));\n        return {\n          requestId,\n          success: false,\n          errorMessage,\n        };\n      }\n\n      const sessionId = data.sessionId;\n      if (!sessionId) {\n        logAPIResponseWithDetails(\n          \"CreateMcpSession\",\n          requestId,\n          false,\n          {},\n          \"SessionId not found in response\"\n        );\n        logDebug(\"Full response:\", JSON.stringify(sessionData, null, 2));\n        return {\n          requestId,\n          success: false,\n          errorMessage: \"SessionId not found in response\",\n        };\n      }\n\n      // ResourceUrl is optional in CreateMcpSession response\n      const resourceUrl = data.resourceUrl || \"\";\n\n      logAPIResponseWithDetails(\n        \"CreateMcpSession\",\n        requestId,\n        true,\n        {\n          sessionId,\n          resourceUrl,\n        }\n      );\n\n      const session = new Session(this, sessionId);\n\n      // Set VPC-related information from response\n      session.isVpc = params.isVpc || false;\n      if (data.networkInterfaceIp) {\n        session.networkInterfaceIp = data.networkInterfaceIp;\n      }\n      if (data.httpPort) {\n        session.httpPort = data.httpPort;\n      }\n      if (data.token) {\n        session.token = data.token;\n      }\n\n      // Set ResourceUrl\n      session.resourceUrl = resourceUrl;\n\n      // Set browser recording state\n      session.enableBrowserReplay = params.enableBrowserReplay || false;\n\n      // Store the file transfer context ID if we created one\n      session.fileTransferContextId = this.fileTransferContext ? this.fileTransferContext.id : null;\n\n      // Store the browser recording context ID if we created one\n      session.recordContextId = recordContextId || null;\n\n      // Store imageId used for this session\n      (session as any).imageId = params.imageId;\n\n\n      this.sessions.set(session.sessionId, session);\n\n      // Apply mobile configuration if provided\n      if (params.extraConfigs && params.extraConfigs.mobile) {\n        log(\"Applying mobile configuration...\");\n        try {\n          const configResult = await session.mobile.configure(params.extraConfigs.mobile);\n          if (configResult.success) {\n            log(\"Mobile configuration applied successfully\");\n          } else {\n            logError(`Warning: Failed to apply mobile configuration: ${configResult.errorMessage}`);\n            // Continue with session creation even if mobile config fails\n          }\n        } catch (error) {\n          logError(`Warning: Failed to apply mobile configuration: ${error}`);\n          // Continue with session creation even if mobile config fails\n        }\n      }\n\n      // Update browser replay context if enabled\n      if (params.enableBrowserReplay) {\n        await this._updateBrowserReplayContext(data, recordContextId);\n      }\n\n      // For VPC sessions, automatically fetch MCP tools information\n      if (params.isVpc) {\n        logDebug(\"VPC session detected, automatically fetching MCP tools...\");\n        try {\n          const toolsResult = await session.listMcpTools();\n          logDebug(`Successfully fetched ${toolsResult.tools.length} MCP tools for VPC session (RequestID: ${toolsResult.requestId})`);\n        } catch (error) {\n          logError(`Warning: Failed to fetch MCP tools for VPC session: ${error}`);\n          // Continue with session creation even if tools fetch fails\n        }\n      }\n\n      // If we have persistence data, wait for context synchronization\n      if (needsContextSync) {\n        logDebug(\"Waiting for context synchronization to complete...\");\n\n        // Wait for context synchronization to complete\n        const maxRetries = 150; // Maximum number of retries\n        const retryInterval = 1500; // Milliseconds to wait between retries\n\n        for (let retry = 0; retry < maxRetries; retry++) {\n          try {\n            // Get context status data\n            const infoResult = await session.context.info();\n\n            // Check if all context items have status \"Success\" or \"Failed\"\n            let allCompleted = true;\n            let hasFailure = false;\n\n            for (const item of infoResult.contextStatusData) {\n              logDebug(`Context ${item.contextId} status: ${item.status}, path: ${item.path}`);\n\n              if (item.status !== \"Success\" && item.status !== \"Failed\") {\n                allCompleted = false;\n                break;\n              }\n\n              if (item.status === \"Failed\") {\n                hasFailure = true;\n                logError(`Context synchronization failed for ${item.contextId}: ${item.errorMessage}`);\n              }\n            }\n\n            if (allCompleted || infoResult.contextStatusData.length === 0) {\n              if (hasFailure) {\n                logDebug(\"Context synchronization completed with failures\");\n              } else {\n                logDebug(\"Context synchronization completed successfully\");\n              }\n              break;\n            }\n\n            logDebug(`Waiting for context synchronization, attempt ${retry+1}/${maxRetries}`);\n            await new Promise(resolve => setTimeout(resolve, retryInterval));\n          } catch (error) {\n            logError(`Error checking context status on attempt ${retry+1}: ${error}`);\n            await new Promise(resolve => setTimeout(resolve, retryInterval));\n          }\n        }\n      }\n\n      // Return SessionResult with request ID\n      return {\n        requestId,\n        success: true,\n        session,\n      };\n    } catch (error) {\n      logError(\"Error calling create_mcp_session:\", error);\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to create session: ${error}`,\n      };\n    }\n  }\n\n\n  /**\n   * Returns paginated list of session IDs filtered by labels.\n   *\n   * @param labels - Optional labels to filter sessions (defaults to empty object)\n   * @param page - Optional page number for pagination (starting from 1, defaults to 1)\n   * @param limit - Optional maximum number of items per page (defaults to 10)\n   * @returns SessionListResult - Paginated list of session IDs that match the labels\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: \"your_api_key\" });\n   * const result = await agentBay.list({ project: \"demo\" }, 1, 10);\n   * if (result.success) {\n   *   console.log(`Found ${result.sessionIds.length} sessions`);\n   * }\n   * ```\n   */\n  async list(\n    labels: Record<string, string> = {},\n    page?: number,\n    limit = 10\n  ): Promise<SessionListResult> {\n    try {\n      // Validate page number\n      if (page !== undefined && page < 1) {\n        return {\n          requestId: \"\",\n          success: false,\n          errorMessage: `Cannot reach page ${page}: Page number must be >= 1`,\n          sessionIds: [],\n          nextToken: \"\",\n          maxResults: limit,\n          totalCount: 0,\n        };\n      }\n\n      // Calculate next_token based on page number\n      // Page 1 or undefined means no next_token (first page)\n      // For page > 1, we need to make multiple requests to get to that page\n      let nextToken = \"\";\n      if (page !== undefined && page > 1) {\n        // We need to fetch pages 1 through page-1 to get the next_token\n        let currentPage = 1;\n        while (currentPage < page) {\n          // Make API call to get next_token\n          const request = new ListSessionRequest({\n            authorization: `Bearer ${this.apiKey}`,\n            labels: JSON.stringify(labels),\n            maxResults: limit,\n          });\n          if (nextToken) {\n            request.nextToken = nextToken;\n          }\n\n          const response = await this.client.listSession(request);\n          const requestId = extractRequestId(response) || \"\";\n\n          if (!response.body?.success) {\n            const code = response.body?.code || \"Unknown\";\n            const message = response.body?.message || \"Unknown error\";\n            return {\n              requestId,\n              success: false,\n              errorMessage: `[${code}] ${message}`,\n              sessionIds: [],\n              nextToken: \"\",\n              maxResults: limit,\n              totalCount: 0,\n            };\n          }\n\n          nextToken = response.body.nextToken || \"\";\n          if (!nextToken) {\n            // No more pages available\n            return {\n              requestId,\n              success: false,\n              errorMessage: `Cannot reach page ${page}: No more pages available`,\n              sessionIds: [],\n              nextToken: \"\",\n              maxResults: limit,\n              totalCount: response.body.totalCount || 0,\n            };\n          }\n          currentPage += 1;\n        }\n      }\n\n      // Make the actual request for the desired page\n      const request = new ListSessionRequest({\n        authorization: `Bearer ${this.apiKey}`,\n        labels: JSON.stringify(labels),\n        maxResults: limit,\n      });\n      if (nextToken) {\n        request.nextToken = nextToken;\n      }\n\n      // Log API request\n      logAPICall(\"ListSession\", {\n        labels,\n        maxResults: limit,\n        nextToken: nextToken || undefined,\n      });\n\n      const response = await this.client.listSession(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n      setRequestId(requestId);\n\n      // Check for errors in the response\n      if (!response.body?.success) {\n        const code = response.body?.code || \"Unknown\";\n        const message = response.body?.message || \"Unknown error\";\n        logAPIResponseWithDetails(\n          \"ListSession\",\n          requestId,\n          false,\n          {},\n          `[${code}] ${message}`\n        );\n        logDebug(\"Full ListSession response:\", JSON.stringify(response.body, null, 2));\n        return {\n          requestId,\n          success: false,\n          errorMessage: `[${code}] ${message}`,\n          sessionIds: [],\n          nextToken: \"\",\n          maxResults: limit,\n          totalCount: 0,\n        };\n      }\n\n      const sessionIds: string[] = [];\n\n      // Extract session data\n      if (response.body.data) {\n        for (const sessionData of response.body.data) {\n          if (sessionData.sessionId) {\n            sessionIds.push(sessionData.sessionId);\n          }\n        }\n      }\n\n      logAPIResponseWithDetails(\n        \"ListSession\",\n        requestId,\n        true,\n        {\n          sessionCount: sessionIds.length,\n          totalCount: response.body.totalCount || 0,\n          maxResults: response.body.maxResults || limit,\n        }\n      );\n\n      // Return SessionListResult with request ID and pagination info\n      return {\n        requestId,\n        success: true,\n        sessionIds,\n        nextToken: response.body.nextToken || \"\",\n        maxResults: response.body.maxResults || limit,\n        totalCount: response.body.totalCount || 0,\n      };\n    } catch (error) {\n      logError(\"Error calling list_session:\", error);\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to list sessions: ${error}`,\n        sessionIds: [],\n      };\n    }\n  }\n\n  /**\n   * Delete a session by session object.\n   *\n   * @param session - The session to delete.\n   * @param syncContext - Whether to sync context data (trigger file uploads) before deleting the session. Defaults to false.\n   * @returns DeleteResult indicating success or failure and request ID\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   await agentBay.delete(result.session);\n   * }\n   * ```\n   */\n  async delete(session: Session, syncContext = false): Promise<DeleteResult> {\n    try {\n      // Delete the session and get the result\n      logAPICall(\"DeleteSession\", { sessionId: session.sessionId });\n      const deleteResult = await session.delete(syncContext);\n\n      logAPIResponseWithDetails(\n        \"DeleteSession\",\n        deleteResult.requestId,\n        deleteResult.success,\n        { sessionId: session.sessionId }\n      );\n\n      this.sessions.delete(session.sessionId);\n\n      // Return the DeleteResult obtained from session.delete()\n      return deleteResult;\n    } catch (error) {\n      logError(\"Error deleting session:\", error);\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to delete session ${session.sessionId}: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Remove a session from the internal session cache.\n   *\n   * This is an internal utility method that removes a session reference from the AgentBay client's\n   * session cache without actually deleting the session from the cloud. Use this when you need to\n   * clean up local references to a session that was deleted externally or no longer needed.\n   *\n   * @param sessionId - The ID of the session to remove from the cache.\n   *\n   * @internal\n   *\n   * @remarks\n   * **Note:** This method only removes the session from the local cache. It does not delete the\n   * session from the cloud. To delete a session from the cloud, use {@link delete} or\n   * {@link Session.delete}.\n   *\n   * @see {@link delete}, {@link Session.delete}\n   */\n  public removeSession(sessionId: string): void {\n    this.sessions.delete(sessionId);\n  }\n\n  /**\n   * Get session information by session ID.\n   *\n   * @param sessionId - The ID of the session to retrieve.\n   * @returns GetSessionResult containing session information\n   */\n  async getSession(sessionId: string): Promise<$GetSessionResult> {\n    try {\n      logAPICall(\"GetSession\", { sessionId });\n\n      const request = new $GetSessionRequest({\n        authorization: `Bearer ${this.apiKey}`,\n        sessionId: sessionId,\n      });\n\n      const response = await this.client.getSession(request);\n      const requestId = extractRequestId(response) || \"\";\n      const body = response.body;\n\n      setRequestId(requestId);\n\n      // Check for API-level errors\n      if (body?.success === false && body.code) {\n        logAPIResponseWithDetails(\n          \"GetSession\",\n          requestId,\n          false,\n          {},\n          `[${body.code}] ${body.message || 'Unknown error'}`\n        );\n        logDebug(\"Full GetSession response:\", JSON.stringify(body, null, 2));\n        return {\n          requestId,\n          httpStatusCode: body.httpStatusCode || 0,\n          code: body.code,\n          success: false,\n          errorMessage: `[${body.code}] ${body.message || 'Unknown error'}`,\n        };\n      }\n\n      const result: $GetSessionResult = {\n        requestId,\n        httpStatusCode: body?.httpStatusCode || 0,\n        code: body?.code || \"\",\n        success: body?.success || false,\n        errorMessage: \"\",\n      };\n\n      if (body?.data) {\n        result.data = {\n          appInstanceId: body.data.appInstanceId || \"\",\n          resourceId: body.data.resourceId || \"\",\n          sessionId: body.data.sessionId || \"\",\n          success: body.data.success || false,\n          httpPort: body.data.httpPort || \"\",\n          networkInterfaceIp: body.data.networkInterfaceIp || \"\",\n          token: body.data.token || \"\",\n          vpcResource: body.data.vpcResource || false,\n          resourceUrl: body.data.resourceUrl || \"\",\n        };\n\n        logAPIResponseWithDetails(\n          \"GetSession\",\n          requestId,\n          true,\n          {\n            sessionId: result.data.sessionId,\n            resourceId: result.data.resourceId,\n            httpPort: result.data.httpPort,\n          }\n        );\n      }\n\n      return result;\n    } catch (error: any) {\n      // Check if this is an expected business error (e.g., session not found)\n      const errorStr = String(error);\n      const errorCode = error?.data?.Code || error?.code || \"\";\n\n      if (errorCode === \"InvalidMcpSession.NotFound\" || errorStr.includes(\"NotFound\")) {\n        // This is an expected error - session doesn't exist\n        // Use info level logging without stack trace, but with red color for visibility\n        logInfoWithColor(`Session not found: ${sessionId}`);\n        logDebug(`GetSession error details: ${errorStr}`);\n        return {\n          requestId: \"\",\n          httpStatusCode: 400,\n          code: \"InvalidMcpSession.NotFound\",\n          success: false,\n          errorMessage: `Session ${sessionId} not found`,\n        };\n      } else {\n        // This is an unexpected error - log with full error\n        logError(\"Error calling GetSession:\", error);\n        return {\n          requestId: \"\",\n          httpStatusCode: 0,\n          code: \"\",\n          success: false,\n          errorMessage: `Failed to get session ${sessionId}: ${error}`,\n        };\n      }\n    }\n  }\n\n  /**\n   * Get a session by its ID.\n   *\n   * This method retrieves a session by calling the GetSession API\n   * and returns a SessionResult containing the Session object and request ID.\n   *\n   * @param sessionId - The ID of the session to retrieve\n   * @returns Promise resolving to SessionResult with the Session instance, request ID, and success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const createResult = await agentBay.create();\n   * if (createResult.success) {\n   *   const result = await agentBay.get(createResult.session.sessionId);\n   *   await result.session?.filesystem.readFile('/etc/hostname');\n   *   await result.session?.delete();\n   * }\n   * ```\n   */\n  async get(sessionId: string): Promise<SessionResult> {\n    // Validate input\n    if (\n      !sessionId ||\n      (typeof sessionId === \"string\" && !sessionId.trim())\n    ) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: \"session_id is required\",\n      };\n    }\n\n    // Call GetSession API\n    const getResult = await this.getSession(sessionId);\n\n    // Check if the API call was successful\n    if (!getResult.success) {\n      const errorMsg = getResult.errorMessage || \"Unknown error\";\n      return {\n        requestId: getResult.requestId,\n        success: false,\n        errorMessage: `Failed to get session ${sessionId}: ${errorMsg}`,\n      };\n    }\n\n    // Create the Session object\n    const session = new Session(this, sessionId);\n\n    // Set VPC-related information and ResourceUrl from GetSession response\n    if (getResult.data) {\n      session.isVpc = getResult.data.vpcResource;\n      session.networkInterfaceIp = getResult.data.networkInterfaceIp;\n      session.httpPort = getResult.data.httpPort;\n      session.token = getResult.data.token;\n      session.resourceUrl = getResult.data.resourceUrl;\n    }\n\n    // Create a default context for file transfer operations for the recovered session\n    const contextName = `file-transfer-context-${Date.now()}`;\n    const contextResult = await this.context.get(contextName, true);\n    if (contextResult.success && contextResult.context) {\n      session.fileTransferContextId = contextResult.context.id;\n      logInfo(`📁 Created file transfer context for recovered session: ${contextResult.context.id}`);\n    } else {\n      logError(`⚠️  Failed to create file transfer context for recovered session: ${contextResult.errorMessage || 'Unknown error'}`);\n    }\n\n    return {\n      requestId: getResult.requestId,\n      success: true,\n      session,\n    };\n  }\n\n  /**\n   * Get the internal HTTP client instance.\n   *\n   * This is primarily for internal use and advanced scenarios where you need direct access\n   * to the underlying API client.\n   *\n   * @returns The Client instance used for API communication\n   *\n   * @internal\n   *\n   * @remarks\n   * **Note:** This method is primarily for internal use. Most users should interact\n   * with the SDK through higher-level methods like `create()`, `get()`, and `list()`.\n   */\n  getClient(): Client {\n    return this.client;\n  }\n\n  /**\n   * Get the API key used for authentication.\n   *\n   * @returns The API key string\n   *\n   * @internal\n   *\n   * @remarks\n   * **Security Note:** Be careful when logging or exposing API keys. Always keep them secure\n   * and never commit them to version control.\n   */\n  getAPIKey(): string {\n    return this.apiKey;\n  }\n}\n\n/**\n * Creates a new AgentBay client using default configuration.\n * This is a convenience function that allows creating an AgentBay instance without a config parameter.\n *\n * @param apiKey - API key for authentication\n * @returns A new AgentBay instance with default configuration\n */\nexport function newAgentBayWithDefaults(apiKey: string): AgentBay {\n  return new AgentBay({ apiKey });\n}\n","(function () {\n  require('./lib/main').config(\n    Object.assign(\n      {},\n      require('./lib/env-options'),\n      require('./lib/cli-options')(process.argv)\n    )\n  )\n})()\n","// Export the client and models\nimport { Client } from \"./client\";\nexport { Client } from \"./client\";\nexport * from \"./models/model\";\n\nexport default Client;\n","// This file is auto-generated, don't edit it\nimport * as $dara from \"@darabonba/typescript\";\nimport OpenApi from \"@alicloud/openapi-core\";\nimport { OpenApiUtil, $OpenApiUtil } from \"@alicloud/openapi-core\";\n\nimport * as $_model from \"./models/model\";\nexport * from \"./models/model\";\n\nexport class Client extends OpenApi {\n  constructor(config: $OpenApiUtil.Config) {\n    super(config);\n    this._signatureAlgorithm = \"v2\";\n    this._endpointRule = \"\";\n    this.checkConfig(config);\n    this._endpoint = this.getEndpoint(\n      \"wuyingai\",\n      this._regionId,\n      this._endpointRule,\n      this._network,\n      this._suffix,\n      this._endpointMap,\n      this._endpoint\n    );\n  }\n\n  getEndpoint(\n    productId: string,\n    regionId: string,\n    endpointRule: string,\n    network: string,\n    suffix: string,\n    endpointMap: { [key: string]: string },\n    endpoint: string\n  ): string {\n    if (!$dara.isNull(endpoint)) {\n      return endpoint;\n    }\n\n    if (!$dara.isNull(endpointMap) && !$dara.isNull(endpointMap[regionId])) {\n      return endpointMap[regionId];\n    }\n\n    return OpenApiUtil.getEndpointRules(\n      productId,\n      regionId,\n      endpointRule,\n      network,\n      suffix\n    );\n  }\n\n  /**\n   * Call MCP tool\n   *\n   * @param request - CallMcpToolRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns CallMcpToolResponse\n   */\n  async callMcpToolWithOptions(\n    request: $_model.CallMcpToolRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.CallMcpToolResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.args)) {\n      body[\"Args\"] = request.args;\n    }\n\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.autoGenSession)) {\n      body[\"AutoGenSession\"] = request.autoGenSession;\n    }\n\n    if (!$dara.isNull(request.externalUserId)) {\n      body[\"ExternalUserId\"] = request.externalUserId;\n    }\n\n    if (!$dara.isNull(request.imageId)) {\n      body[\"ImageId\"] = request.imageId;\n    }\n\n    if (!$dara.isNull(request.name)) {\n      body[\"Name\"] = request.name;\n    }\n\n    if (!$dara.isNull(request.server)) {\n      body[\"Server\"] = request.server;\n    }\n\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    if (!$dara.isNull(request.tool)) {\n      body[\"Tool\"] = request.tool;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"CallMcpTool\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.CallMcpToolResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.CallMcpToolResponse({})\n    );\n  }\n\n  /**\n   * Call MCP tool\n   *\n   * @param request - CallMcpToolRequest\n   * @returns CallMcpToolResponse\n   */\n  async callMcpTool(\n    request: $_model.CallMcpToolRequest\n  ): Promise<$_model.CallMcpToolResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.callMcpToolWithOptions(request, runtime);\n  }\n\n  /**\n   * Delete Persistent Context\n   * \n   * @param request - ClearContextRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns ClearContextResponse\n   */\n  async clearContextWithOptions(request: $_model.ClearContextRequest, runtime: $dara.RuntimeOptions): Promise<$_model.ClearContextResponse> {\n    request.validate();\n    const body : {[key: string ]: any} = { };\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.id)) {\n      body[\"Id\"] = request.id;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"ClearContext\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.ClearContextResponse>(await this.doRPCRequest(params.action, params.version, params.protocol, params.method, params.authType, params.bodyType, req, runtime), new $_model.ClearContextResponse({}));\n  }\n\n  /**\n   * Delete Persistent Context\n   * \n   * @param request - ClearContextRequest\n   * @returns ClearContextResponse\n   */\n  async clearContext(request: $_model.ClearContextRequest): Promise<$_model.ClearContextResponse> {\n    const runtime = new $dara.RuntimeOptions({ });\n    return await this.clearContextWithOptions(request, runtime);\n  }\n  /**\n   * Create MCP session\n   *\n   * @param tmpReq - CreateMcpSessionRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns CreateMcpSessionResponse\n   */\n  async createMcpSessionWithOptions(\n    tmpReq: $_model.CreateMcpSessionRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.CreateMcpSessionResponse> {\n    tmpReq.validate();\n    const request = new $_model.CreateMcpSessionShrinkRequest({});\n    OpenApiUtil.convert(tmpReq, request);\n    if (!$dara.isNull(tmpReq.persistenceDataList)) {\n      request.persistenceDataListShrink =\n        OpenApiUtil.arrayToStringWithSpecifiedStyle(\n          tmpReq.persistenceDataList,\n          \"PersistenceDataList\",\n          \"json\"\n        );\n    }\n\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.contextId)) {\n      body[\"ContextId\"] = request.contextId;\n    }\n\n    if (!$dara.isNull(request.externalUserId)) {\n      body[\"ExternalUserId\"] = request.externalUserId;\n    }\n\n    if (!$dara.isNull(request.imageId)) {\n      body[\"ImageId\"] = request.imageId;\n    }\n\n    if (!$dara.isNull(request.labels)) {\n      body[\"Labels\"] = request.labels;\n    }\n\n    if (!$dara.isNull(request.mcpPolicyId)) {\n      body[\"McpPolicyId\"] = request.mcpPolicyId;\n    }\n\n    if (!$dara.isNull(request.persistenceDataListShrink)) {\n      body[\"PersistenceDataList\"] = request.persistenceDataListShrink;\n    }\n\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    if (!$dara.isNull(request.vpcResource)) {\n      body[\"VpcResource\"] = request.vpcResource;\n    }\n\n    if (!$dara.isNull(request.extraConfigs)) {\n      body[\"ExtraConfigs\"] = request.extraConfigs;\n    }\n\n    if (!$dara.isNull(request.sdkStats)) {\n      body[\"SdkStats\"] = request.sdkStats;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"CreateMcpSession\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.CreateMcpSessionResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.CreateMcpSessionResponse({})\n    );\n  }\n\n  /**\n   * Create MCP session\n   *\n   * @param request - CreateMcpSessionRequest\n   * @returns CreateMcpSessionResponse\n   */\n  async createMcpSession(\n    request: $_model.CreateMcpSessionRequest\n  ): Promise<$_model.CreateMcpSessionResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.createMcpSessionWithOptions(request, runtime);\n  }\n\n  /**\n   * Delete persistent context\n   *\n   * @param request - DeleteContextRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns DeleteContextResponse\n   */\n  async deleteContextWithOptions(\n    request: $_model.DeleteContextRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.DeleteContextResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.id)) {\n      body[\"Id\"] = request.id;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"DeleteContext\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.DeleteContextResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.DeleteContextResponse({})\n    );\n  }\n\n  /**\n   * Delete persistent context\n   *\n   * @param request - DeleteContextRequest\n   * @returns DeleteContextResponse\n   */\n  async deleteContext(\n    request: $_model.DeleteContextRequest\n  ): Promise<$_model.DeleteContextResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.deleteContextWithOptions(request, runtime);\n  }\n\n  /**\n   * Get context\n   *\n   * @param request - GetContextRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns GetContextResponse\n   */\n  async getContextWithOptions(\n    request: $_model.GetContextRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.GetContextResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.allowCreate)) {\n      body[\"AllowCreate\"] = request.allowCreate;\n    }\n\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.contextId)) {\n      body[\"ContextId\"] = request.contextId;\n    }\n\n    if (!$dara.isNull(request.name)) {\n      body[\"Name\"] = request.name;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"GetContext\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.GetContextResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.GetContextResponse({})\n    );\n  }\n\n  /**\n   * Get context\n   *\n   * @param request - GetContextRequest\n   * @returns GetContextResponse\n   */\n  async getContext(\n    request: $_model.GetContextRequest\n  ): Promise<$_model.GetContextResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.getContextWithOptions(request, runtime);\n  }\n\n  /**\n   * Get context information\n   *\n   * @param request - GetContextInfoRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns GetContextInfoResponse\n   */\n  async getContextInfoWithOptions(\n    request: $_model.GetContextInfoRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.GetContextInfoResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.contextId)) {\n      body[\"ContextId\"] = request.contextId;\n    }\n\n    if (!$dara.isNull(request.path)) {\n      body[\"Path\"] = request.path;\n    }\n\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    if (!$dara.isNull(request.taskType)) {\n      body[\"TaskType\"] = request.taskType;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"GetContextInfo\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.GetContextInfoResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.GetContextInfoResponse({})\n    );\n  }\n\n  /**\n   * Get context information\n   *\n   * @param request - GetContextInfoRequest\n   * @returns GetContextInfoResponse\n   */\n  async getContextInfo(\n    request: $_model.GetContextInfoRequest\n  ): Promise<$_model.GetContextInfoResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.getContextInfoWithOptions(request, runtime);\n  }\n\n  /**\n   * Get labels\n   *\n   * @param request - GetLabelRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns GetLabelResponse\n   */\n  async getLabelWithOptions(\n    request: $_model.GetLabelRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.GetLabelResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.maxResults)) {\n      body[\"MaxResults\"] = request.maxResults;\n    }\n\n    if (!$dara.isNull(request.nextToken)) {\n      body[\"NextToken\"] = request.nextToken;\n    }\n\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"GetLabel\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.GetLabelResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.GetLabelResponse({})\n    );\n  }\n\n  /**\n   * Get labels\n   *\n   * @param request - GetLabelRequest\n   * @returns GetLabelResponse\n   */\n  async getLabel(\n    request: $_model.GetLabelRequest\n  ): Promise<$_model.GetLabelResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.getLabelWithOptions(request, runtime);\n  }\n\n  /**\n   * Get session information\n   *\n   * @param request - GetSessionRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns GetSessionResponse\n   */\n  async getSessionWithOptions(\n    request: $_model.GetSessionRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.GetSessionResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"GetSession\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.GetSessionResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.GetSessionResponse({})\n    );\n  }\n\n  /**\n   * Get session information\n   *\n   * @param request - GetSessionRequest\n   * @returns GetSessionResponse\n   */\n  async getSession(\n    request: $_model.GetSessionRequest\n  ): Promise<$_model.GetSessionResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.getSessionWithOptions(request, runtime);\n  }\n\n  /**\n   * Get forwarding link for specific port\n   *\n   * @param request - GetLinkRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns GetLinkResponse\n   */\n  async getLinkWithOptions(\n    request: $_model.GetLinkRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.GetLinkResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.port)) {\n      body[\"Port\"] = request.port;\n    }\n\n    if (!$dara.isNull(request.protocolType)) {\n      body[\"ProtocolType\"] = request.protocolType;\n    }\n\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    if (!$dara.isNull(request.option)) {\n      body[\"Option\"] = request.option;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"GetLink\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.GetLinkResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.GetLinkResponse({})\n    );\n  }\n\n  /**\n   * Get forwarding link for specific port\n   *\n   * @param request - GetLinkRequest\n   * @returns GetLinkResponse\n   */\n  async getLink(\n    request: $_model.GetLinkRequest\n  ): Promise<$_model.GetLinkResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.getLinkWithOptions(request, runtime);\n  }\n\n  /**\n   * Get MCP resource information\n   *\n   * @param request - GetMcpResourceRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns GetMcpResourceResponse\n   */\n  async getMcpResourceWithOptions(\n    request: $_model.GetMcpResourceRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.GetMcpResourceResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"GetMcpResource\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.GetMcpResourceResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.GetMcpResourceResponse({})\n    );\n  }\n\n  /**\n   * Get MCP resource information\n   *\n   * @param request - GetMcpResourceRequest\n   * @returns GetMcpResourceResponse\n   */\n  async getMcpResource(\n    request: $_model.GetMcpResourceRequest\n  ): Promise<$_model.GetMcpResourceResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.getMcpResourceWithOptions(request, runtime);\n  }\n\n  /**\n   * Get context list\n   *\n   * @param request - ListContextsRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns ListContextsResponse\n   */\n  async listContextsWithOptions(\n    request: $_model.ListContextsRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.ListContextsResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.maxResults)) {\n      body[\"MaxResults\"] = request.maxResults;\n    }\n\n    if (!$dara.isNull(request.nextToken)) {\n      body[\"NextToken\"] = request.nextToken;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"ListContexts\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.ListContextsResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.ListContextsResponse({})\n    );\n  }\n\n  /**\n   * Get context list\n   *\n   * @param request - ListContextsRequest\n   * @returns ListContextsResponse\n   */\n  async listContexts(\n    request: $_model.ListContextsRequest\n  ): Promise<$_model.ListContextsResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.listContextsWithOptions(request, runtime);\n  }\n\n  /**\n   * ListMcpTools\n   *\n   * @param request - ListMcpToolsRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns ListMcpToolsResponse\n   */\n  async listMcpToolsWithOptions(\n    request: $_model.ListMcpToolsRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.ListMcpToolsResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.imageId)) {\n      body[\"ImageId\"] = request.imageId;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"ListMcpTools\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.ListMcpToolsResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.ListMcpToolsResponse({})\n    );\n  }\n\n  /**\n   * ListMcpTools\n   *\n   * @param request - ListMcpToolsRequest\n   * @returns ListMcpToolsResponse\n   */\n  async listMcpTools(\n    request: $_model.ListMcpToolsRequest\n  ): Promise<$_model.ListMcpToolsResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.listMcpToolsWithOptions(request, runtime);\n  }\n\n  /**\n   * Query session list by label\n   *\n   * @param request - ListSessionRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns ListSessionResponse\n   */\n  async listSessionWithOptions(\n    request: $_model.ListSessionRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.ListSessionResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.labels)) {\n      body[\"Labels\"] = request.labels;\n    }\n\n    if (!$dara.isNull(request.maxResults)) {\n      body[\"MaxResults\"] = request.maxResults;\n    }\n\n    if (!$dara.isNull(request.nextToken)) {\n      body[\"NextToken\"] = request.nextToken;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"ListSession\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.ListSessionResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.ListSessionResponse({})\n    );\n  }\n\n  /**\n   * Query session list by label\n   *\n   * @param request - ListSessionRequest\n   * @returns ListSessionResponse\n   */\n  async listSession(\n    request: $_model.ListSessionRequest\n  ): Promise<$_model.ListSessionResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.listSessionWithOptions(request, runtime);\n  }\n\n  /**\n   * Modify context\n   *\n   * @param request - ModifyContextRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns ModifyContextResponse\n   */\n  async modifyContextWithOptions(\n    request: $_model.ModifyContextRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.ModifyContextResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.id)) {\n      body[\"Id\"] = request.id;\n    }\n\n    if (!$dara.isNull(request.name)) {\n      body[\"Name\"] = request.name;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"ModifyContext\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.ModifyContextResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.ModifyContextResponse({})\n    );\n  }\n\n  /**\n   * Modify context\n   *\n   * @param request - ModifyContextRequest\n   * @returns ModifyContextResponse\n   */\n  async modifyContext(\n    request: $_model.ModifyContextRequest\n  ): Promise<$_model.ModifyContextResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.modifyContextWithOptions(request, runtime);\n  }\n\n  /**\n   * Release MCP session\n   *\n   * @param request - ReleaseMcpSessionRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns ReleaseMcpSessionResponse\n   */\n  async releaseMcpSessionWithOptions(\n    request: $_model.ReleaseMcpSessionRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.ReleaseMcpSessionResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"ReleaseMcpSession\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.ReleaseMcpSessionResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.ReleaseMcpSessionResponse({})\n    );\n  }\n\n  /**\n   * Release MCP session\n   *\n   * @param request - ReleaseMcpSessionRequest\n   * @returns ReleaseMcpSessionResponse\n   */\n  async releaseMcpSession(\n    request: $_model.ReleaseMcpSessionRequest\n  ): Promise<$_model.ReleaseMcpSessionResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.releaseMcpSessionWithOptions(request, runtime);\n  }\n\n  /**\n   * Set labels\n   *\n   * @param request - SetLabelRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns SetLabelResponse\n   */\n  async setLabelWithOptions(\n    request: $_model.SetLabelRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.SetLabelResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.labels)) {\n      body[\"Labels\"] = request.labels;\n    }\n\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"SetLabel\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.SetLabelResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.SetLabelResponse({})\n    );\n  }\n\n  /**\n   * Set labels\n   *\n   * @param request - SetLabelRequest\n   * @returns SetLabelResponse\n   */\n  async setLabel(\n    request: $_model.SetLabelRequest\n  ): Promise<$_model.SetLabelResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.setLabelWithOptions(request, runtime);\n  }\n\n  /**\n   * Sync context\n   *\n   * @param request - SyncContextRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns SyncContextResponse\n   */\n  async syncContextWithOptions(\n    request: $_model.SyncContextRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.SyncContextResponse> {\n    request.validate();\n    const query: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      query[\"Authorization\"] = request.authorization;\n    }\n\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.contextId)) {\n      body[\"ContextId\"] = request.contextId;\n    }\n\n    if (!$dara.isNull(request.mode)) {\n      body[\"Mode\"] = request.mode;\n    }\n\n    if (!$dara.isNull(request.path)) {\n      body[\"Path\"] = request.path;\n    }\n\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      query: OpenApiUtil.query(query),\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"SyncContext\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.SyncContextResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.SyncContextResponse({})\n    );\n  }\n\n  /**\n   * Sync context\n   *\n   * @param request - SyncContextRequest\n   * @returns SyncContextResponse\n   */\n  async syncContext(\n    request: $_model.SyncContextRequest\n  ): Promise<$_model.SyncContextResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.syncContextWithOptions(request, runtime);\n  }\n\n  /**\n   * Initialize browser\n   *\n   * @param tmpReq - InitBrowserRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns InitBrowserResponse\n   */\n  async initBrowserWithOptions(\n    request: $_model.InitBrowserRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.InitBrowserResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n    if (!$dara.isNull(request.persistentPath)) {\n      body[\"PersistentPath\"] = request.persistentPath;\n    }\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n    if (!$dara.isNull(request.browserOption)) {\n      body[\"BrowserOption\"] = JSON.stringify(request.browserOption);\n    }\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"InitBrowser\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.InitBrowserResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.InitBrowserResponse({})\n    );\n  }\n\n  /**\n   * Initialize browser\n   *\n   * @param request - InitBrowserRequest\n   * @returns InitBrowserResponse\n   */\n  async initBrowser(\n    request: $_model.InitBrowserRequest\n  ): Promise<$_model.InitBrowserResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.initBrowserWithOptions(request, runtime);\n  }\n\n  /**\n   * Initialize browser (sync version)\n   *\n   * @param request - InitBrowserRequest\n   * @returns InitBrowserResponse\n   */\n  initBrowserSync(\n    request: $_model.InitBrowserRequest\n  ): $_model.InitBrowserResponse {\n    const runtime = new $dara.RuntimeOptions({});\n    \n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n    if (!$dara.isNull(request.persistentPath)) {\n      body[\"PersistentPath\"] = request.persistentPath;\n    }\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n    if (!$dara.isNull(request.browserOption)) {\n      body[\"BrowserOption\"] = JSON.stringify(request.browserOption);\n    }\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"InitBrowser\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.InitBrowserResponse>(\n      this.callApi(params, req, runtime),\n      new $_model.InitBrowserResponse({})\n    );\n  }\n\n  /**\n   * Get context file upload URL\n   *\n   * @param request - DeleteContextFileRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns DeleteContextFileResponse\n   */\n  async deleteContextFileWithOptions(\n    request: $_model.DeleteContextFileRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.DeleteContextFileResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.contextId)) {\n      body[\"ContextId\"] = request.contextId;\n    }\n\n    if (!$dara.isNull(request.filePath)) {\n      body[\"FilePath\"] = request.filePath;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"DeleteContextFile\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.DeleteContextFileResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.DeleteContextFileResponse({})\n    );\n  }\n\n  /**\n   * Get context file upload URL\n   *\n   * @param request - DeleteContextFileRequest\n   * @returns DeleteContextFileResponse\n   */\n  async deleteContextFile(\n    request: $_model.DeleteContextFileRequest\n  ): Promise<$_model.DeleteContextFileResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.deleteContextFileWithOptions(request, runtime);\n  }\n\n  /**\n   * Query context specific directory files\n   *\n   * @param request - DescribeContextFilesRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns DescribeContextFilesResponse\n   */\n  async describeContextFilesWithOptions(\n    request: $_model.DescribeContextFilesRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.DescribeContextFilesResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.pageNumber)) {\n      body[\"PageNumber\"] = request.pageNumber;\n    }\n\n    if (!$dara.isNull(request.pageSize)) {\n      body[\"PageSize\"] = request.pageSize;\n    }\n\n    if (!$dara.isNull(request.parentFolderPath)) {\n      body[\"ParentFolderPath\"] = request.parentFolderPath;\n    }\n\n    if (!$dara.isNull(request.contextId)) {\n      body[\"ContextId\"] = request.contextId;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"DescribeContextFiles\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.DescribeContextFilesResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.DescribeContextFilesResponse({})\n    );\n  }\n\n  /**\n   * Query context specific directory files\n   *\n   * @param request - DescribeContextFilesRequest\n   * @returns DescribeContextFilesResponse\n   */\n  async describeContextFiles(\n    request: $_model.DescribeContextFilesRequest\n  ): Promise<$_model.DescribeContextFilesResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.describeContextFilesWithOptions(request, runtime);\n  }\n\n  /**\n   * Get context file upload URL\n   *\n   * @param request - GetContextFileDownloadUrlRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns GetContextFileDownloadUrlResponse\n   */\n  async getContextFileDownloadUrlWithOptions(\n    request: $_model.GetContextFileDownloadUrlRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.GetContextFileDownloadUrlResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.contextId)) {\n      body[\"ContextId\"] = request.contextId;\n    }\n\n    if (!$dara.isNull(request.filePath)) {\n      body[\"FilePath\"] = request.filePath;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"GetContextFileDownloadUrl\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.GetContextFileDownloadUrlResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.GetContextFileDownloadUrlResponse({})\n    );\n  }\n\n  /**\n   * Get context file upload URL\n   *\n   * @param request - GetContextFileDownloadUrlRequest\n   * @returns GetContextFileDownloadUrlResponse\n   */\n  async getContextFileDownloadUrl(\n    request: $_model.GetContextFileDownloadUrlRequest\n  ): Promise<$_model.GetContextFileDownloadUrlResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.getContextFileDownloadUrlWithOptions(request, runtime);\n  }\n\n  /**\n   * Get context file upload URL\n   *\n   * @param request - GetContextFileUploadUrlRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns GetContextFileUploadUrlResponse\n   */\n  async getContextFileUploadUrlWithOptions(\n    request: $_model.GetContextFileUploadUrlRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.GetContextFileUploadUrlResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n\n    if (!$dara.isNull(request.contextId)) {\n      body[\"ContextId\"] = request.contextId;\n    }\n\n    if (!$dara.isNull(request.filePath)) {\n      body[\"FilePath\"] = request.filePath;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"GetContextFileUploadUrl\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.GetContextFileUploadUrlResponse>(\n      await this.callApi(params, req, runtime),\n      new $_model.GetContextFileUploadUrlResponse({})\n    );\n  }\n\n  /**\n   * Get context file upload URL\n   *\n   * @param request - GetContextFileUploadUrlRequest\n   * @returns GetContextFileUploadUrlResponse\n   */\n  async getContextFileUploadUrl(\n    request: $_model.GetContextFileUploadUrlRequest\n  ): Promise<$_model.GetContextFileUploadUrlResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.getContextFileUploadUrlWithOptions(request, runtime);\n  }\n\n  /**\n   * Get CDP link\n   *\n   * @param request - GetCdpLinkRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns GetCdpLinkResponse\n   */\n  async getCdpLinkWithOptions(\n    request: $_model.GetCdpLinkRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.GetCdpLinkResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"GetCdpLink\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.GetCdpLinkResponse>(\n      await this.doRPCRequest(\n        params.action,\n        params.version,\n        params.protocol,\n        params.method,\n        params.authType,\n        params.bodyType,\n        req,\n        runtime\n      ),\n      new $_model.GetCdpLinkResponse({})\n    );\n  }\n\n  /**\n   * Get CDP link\n   *\n   * @param request - GetCdpLinkRequest\n   * @returns GetCdpLinkResponse\n   */\n  async getCdpLink(\n    request: $_model.GetCdpLinkRequest\n  ): Promise<$_model.GetCdpLinkResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.getCdpLinkWithOptions(request, runtime);\n  }\n\n  /**\n   * Get ADB link\n   *\n   * @param request - GetAdbLinkRequest\n   * @param runtime - runtime options for this request RuntimeOptions\n   * @returns GetAdbLinkResponse\n   */\n  async getAdbLinkWithOptions(\n    request: $_model.GetAdbLinkRequest,\n    runtime: $dara.RuntimeOptions\n  ): Promise<$_model.GetAdbLinkResponse> {\n    request.validate();\n    const body: { [key: string]: any } = {};\n    if (!$dara.isNull(request.authorization)) {\n      body[\"Authorization\"] = request.authorization;\n    }\n    if (!$dara.isNull(request.option)) {\n      body[\"Option\"] = request.option;\n    }\n    if (!$dara.isNull(request.sessionId)) {\n      body[\"SessionId\"] = request.sessionId;\n    }\n\n    const req = new $OpenApiUtil.OpenApiRequest({\n      body: OpenApiUtil.parseToMap(body),\n    });\n    const params = new $OpenApiUtil.Params({\n      action: \"GetAdbLink\",\n      version: \"2025-05-06\",\n      protocol: \"HTTPS\",\n      pathname: \"/\",\n      method: \"POST\",\n      authType: \"Anonymous\",\n      style: \"RPC\",\n      reqBodyType: \"formData\",\n      bodyType: \"json\",\n    });\n    return $dara.cast<$_model.GetAdbLinkResponse>(\n      await this.doRPCRequest(\n        params.action,\n        params.version,\n        params.protocol,\n        params.method,\n        params.authType,\n        params.bodyType,\n        req,\n        runtime\n      ),\n      new $_model.GetAdbLinkResponse({})\n    );\n  }\n\n  /**\n   * Get ADB link\n   *\n   * @param request - GetAdbLinkRequest\n   * @returns GetAdbLinkResponse\n   */\n  async getAdbLink(\n    request: $_model.GetAdbLinkRequest\n  ): Promise<$_model.GetAdbLinkResponse> {\n    const runtime = new $dara.RuntimeOptions({});\n    return await this.getAdbLinkWithOptions(request, runtime);\n  }\n}\n","import { AgentBay } from \"./agent-bay\";\nimport { APIError } from \"./exceptions\";\nimport * as $_client from \"./api\";\nimport {\n  log,\n  logError,\n  logInfo,\n  logDebug,\n  logAPICall,\n  logAPIResponseWithDetails,\n  setRequestId,\n  getRequestId,\n} from \"./utils/logger\";\nimport {\n  extractRequestId,\n  ContextResult,\n  ContextListResult,\n  OperationResult,\n  FileUrlResult,\n  ContextFileListResult,\n  ContextFileEntry,\n  ClearContextResult,\n} from \"./types/api-response\";\n\n/**\n * Represents a persistent storage context in the AgentBay cloud environment.\n */\nexport class Context {\n  /**\n   * The unique identifier of the context.\n   */\n  id: string;\n\n  /**\n   * The name of the context.\n   */\n  name: string;\n\n  /**\n   * Date and time when the Context was created.\n   */\n  createdAt?: string;\n\n  /**\n   * Date and time when the Context was last used.\n   */\n  lastUsedAt?: string;\n\n  /**\n   * Initialize a Context object.\n   *\n   * @param id - The unique identifier of the context.\n   * @param name - The name of the context.\n   * @param createdAt - Date and time when the Context was created.\n   * @param lastUsedAt - Date and time when the Context was last used.\n   */\n  constructor(\n    id: string,\n    name: string,\n    createdAt?: string,\n    lastUsedAt?: string\n  ) {\n    this.id = id;\n    this.name = name;\n    this.createdAt = createdAt;\n    this.lastUsedAt = lastUsedAt;\n  }\n}\n\n/**\n * Parameters for listing contexts with pagination support.\n */\nexport interface ContextListParams {\n  /**\n   * Maximum number of results per page.\n   * Defaults to 10 if not specified.\n   */\n  maxResults?: number;\n\n  /**\n   * Token for the next page of results.\n   */\n  nextToken?: string;\n}\n\n/**\n * Provides methods to manage persistent contexts in the AgentBay cloud environment.\n */\nexport class ContextService {\n  private agentBay: AgentBay;\n\n  /**\n   * Initialize the ContextService.\n   *\n   * @param agentBay - The AgentBay instance.\n   */\n  constructor(agentBay: AgentBay) {\n    this.agentBay = agentBay;\n  }\n\n  /**\n   * Lists all available contexts with pagination support.\n   * Corresponds to Python's list() method\n   *\n   * @param params - Optional parameters for listing contexts.\n   * @returns ContextListResult with contexts list and pagination information\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.context.list({ maxResults: 10 });\n   * if (result.success) {\n   *   console.log(`Total contexts: ${result.totalCount}`);\n   *   console.log(`Page has ${result.contexts.length} contexts`);\n   * }\n   * ```\n   */\n  async list(params?: ContextListParams): Promise<ContextListResult> {\n    try {\n      // Set default maxResults if not specified\n      const maxResults = params?.maxResults !== undefined ? params.maxResults : 10;\n\n      const request = new $_client.ListContextsRequest({\n        authorization: `Bearer ${this.agentBay.getAPIKey()}`,\n        maxResults: maxResults,\n        nextToken: params?.nextToken,\n      });\n\n      // Log API request\n      logAPICall(\"ListContexts\");\n      logDebug(`Request: MaxResults=${maxResults}`, params?.nextToken ? `, NextToken=${params.nextToken}` : \"\");\n\n      const response = await this.agentBay.getClient().listContexts(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Check for API-level errors\n      if (response.body?.success === false && response.body?.code) {\n        const errorMessage = `[${response.body.code}] ${response.body.message || 'Unknown error'}`;\n        const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n        logAPIResponseWithDetails(\"ListContexts\", requestId, false, undefined, fullResponse);\n        return {\n          requestId,\n          success: false,\n          contexts: [],\n          errorMessage,\n        };\n      }\n\n      const contexts: Context[] = [];\n      if (response.body?.data) {\n        for (const contextData of response.body.data) {\n          contexts.push(\n            new Context(\n              contextData.id || \"\",\n              contextData.name || \"\",\n              contextData.createTime,\n              contextData.lastUsedTime\n            )\n          );\n        }\n      }\n\n      // Log API response with key fields\n      const keyFields: Record<string, any> = {\n        context_count: contexts.length,\n        max_results: maxResults,\n      };\n      if (response.body?.totalCount !== undefined) {\n        keyFields.total_count = response.body.totalCount;\n      }\n      const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"ListContexts\", requestId, true, keyFields, fullResponse);\n\n      return {\n        requestId,\n        success: true,\n        contexts,\n        nextToken: response.body?.nextToken,\n        maxResults: response.body?.maxResults || maxResults,\n        totalCount: response.body?.totalCount,\n      };\n    } catch (error) {\n      logError(\"Error calling ListContexts:\", error);\n      return {\n        requestId: \"\",\n        success: false,\n        contexts: [],\n        errorMessage: `Failed to list contexts: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Retrieves an existing context or creates a new one.\n   *\n   * @param name - The name of the context to retrieve or create.\n   * @param create - If true, creates the context if it doesn't exist. Defaults to false.\n   *\n   * @returns Promise resolving to ContextResult containing the Context object.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.context.get('my-context', true);\n   * if (result.success) {\n   *   console.log(`Context ID: ${result.context.id}`);\n   *   console.log(`Context Name: ${result.context.name}`);\n   * }\n   * ```\n   *\n   * @see {@link update}, {@link list}\n   */\n  async get(name: string, create = false): Promise<ContextResult> {\n    try {\n      const request = new $_client.GetContextRequest({\n        name: name,\n        allowCreate: create ? \"true\" : \"false\",\n        authorization: `Bearer ${this.agentBay.getAPIKey()}`,\n      });\n\n      // Log API request\n      logAPICall(\"GetContext\");\n      logDebug(`Request: Name=${name}, AllowCreate=${create}`);\n\n      const response = await this.agentBay.getClient().getContext(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Check for API-level errors\n      if (response.body?.success === false && response.body?.code) {\n        const errorMessage = `[${response.body.code}] ${response.body.message || 'Unknown error'}`;\n        const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n        logAPIResponseWithDetails(\"GetContext\", requestId, false, undefined, fullResponse);\n        return {\n          requestId,\n          success: false,\n          contextId: \"\",\n          context: undefined,\n          errorMessage,\n        };\n      }\n\n      const contextId = response.body?.data?.id || \"\";\n      if (!contextId) {\n        logAPIResponseWithDetails(\"GetContext\", requestId, false, undefined, \"Context ID not found in response\");\n        return {\n          requestId,\n          success: false,\n          contextId: \"\",\n          context: undefined,\n          errorMessage: \"Context ID not found in response\",\n        };\n      }\n\n      // Get the full context details\n      try {\n        const contextsResponse = await this.list();\n        for (const context of contextsResponse.contexts) {\n          if (context.id === contextId) {\n            // Log API response with key fields\n            const keyFields: Record<string, any> = {\n              context_id: contextId,\n              context_name: context.name,\n            };\n            const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n            logAPIResponseWithDetails(\"GetContext\", requestId, true, keyFields, fullResponse);\n            return {\n              requestId,\n              success: true,\n              contextId,\n              context,\n            };\n          }\n        }\n      } catch (listError) {\n        logDebug(`Warning: Failed to get full context details from list: ${listError}`);\n      }\n\n      // If we couldn't find the context in the list, create a basic one\n      const context = new Context(contextId, name);\n      const keyFields: Record<string, any> = {\n        context_id: contextId,\n        context_name: name,\n      };\n      const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"GetContext\", requestId, true, keyFields, fullResponse);\n      return {\n        requestId,\n        success: true,\n        contextId,\n        context,\n      };\n    } catch (error) {\n      logError(\"Error calling GetContext:\", error);\n      return {\n        requestId: \"\",\n        success: false,\n        contextId: \"\",\n        context: undefined,\n        errorMessage: `Failed to get context ${name}: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Creates a new context with the given name.\n   * Corresponds to Python's create() method\n   *\n   * @param name - The name for the new context.\n   * @returns ContextResult with created context data and requestId\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.context.create('my-new-context');\n   * if (result.success) {\n   *   console.log(`Context ID: ${result.context.id}`);\n   *   console.log(`Context Name: ${result.context.name}`);\n   * }\n   * ```\n   */\n  async create(name: string): Promise<ContextResult> {\n    return await this.get(name, true);\n  }\n\n  /**\n   * Updates a context's name.\n   *\n   * @param context - The Context object with updated name.\n   *\n   * @returns Promise resolving to OperationResult with success status.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const getResult = await agentBay.context.get('old-name');\n   * if (getResult.success && getResult.context) {\n   *   getResult.context.name = 'new-name';\n   *   const updateResult = await agentBay.context.update(getResult.context);\n   *   console.log('Context updated:', updateResult.success);\n   * }\n   * ```\n   *\n   * @see {@link get}, {@link list}\n   */\n  async update(context: Context): Promise<OperationResult> {\n    try {\n      const request = new $_client.ModifyContextRequest({\n        id: context.id,\n        name: context.name,\n        authorization: `Bearer ${this.agentBay.getAPIKey()}`,\n      });\n\n      // Log API request\n      logAPICall(\"ModifyContext\");\n      logDebug(`Request: Id=${context.id}, Name=${context.name}`);\n\n      const response = await this.agentBay.getClient().modifyContext(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Check for success (matching Python logic)\n      const success = response.body?.success !== false;\n      const errorMessage = success\n        ? \"\"\n        : `[${response.body?.code || 'Unknown'}] ${response.body?.message || 'Unknown error'}`;\n\n      // Log API response with key fields\n      const keyFields: Record<string, any> = {\n        context_id: context.id,\n        context_name: context.name,\n      };\n      const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"ModifyContext\", requestId, success, keyFields, fullResponse);\n\n      return {\n        requestId,\n        success,\n        data: success,\n        errorMessage,\n      };\n    } catch (error) {\n      logError(\"Error calling ModifyContext:\", error);\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to update context ${context.id}: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Deletes the specified context.\n   * Corresponds to Python's delete() method\n   *\n   * @param context - The Context object to delete.\n   * @returns OperationResult with requestId\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const getResult = await agentBay.context.get('my-context');\n   * if (getResult.success && getResult.context) {\n   *   const deleteResult = await agentBay.context.delete(getResult.context);\n   *   console.log('Context deleted:', deleteResult.success);\n   * }\n   * ```\n   */\n  async delete(context: Context): Promise<OperationResult> {\n    try {\n      const request = new $_client.DeleteContextRequest({\n        id: context.id,\n        authorization: `Bearer ${this.agentBay.getAPIKey()}`,\n      });\n\n      // Log API request\n      logAPICall(\"DeleteContext\");\n      logDebug(`Request: Id=${context.id}`);\n\n      const response = await this.agentBay.getClient().deleteContext(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Check for success (matching Python logic)\n      const success = response.body?.success !== false;\n      const errorMessage = success\n        ? \"\"\n        : `[${response.body?.code || 'Unknown'}] ${response.body?.message || 'Unknown error'}`;\n\n      // Log API response with key fields\n      const keyFields: Record<string, any> = {\n        context_id: context.id,\n      };\n      const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"DeleteContext\", requestId, success, keyFields, fullResponse);\n\n      return {\n        requestId,\n        success,\n        data: success,\n        errorMessage,\n      };\n    } catch (error) {\n      logError(\"Error calling DeleteContext:\", error);\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to delete context ${context.id}: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Get a presigned upload URL for a file in a context.\n   *\n   * @param contextId - The ID of the context.\n   * @param filePath - The path to the file in the context.\n   * @returns FileUrlResult with the presigned URL and expiration time.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const contextResult = await agentBay.context.get('my-context', true);\n   * if (contextResult.success) {\n   *   const urlResult = await agentBay.context.getFileUploadUrl(contextResult.context.id, '/data/file.txt');\n   *   console.log('Upload URL:', urlResult.url);\n   *   console.log('Expires at:', urlResult.expireTime);\n   * }\n   * ```\n   */\n  async getFileUploadUrl(contextId: string, filePath: string): Promise<FileUrlResult> {\n    logAPICall(\"GetContextFileUploadUrl\");\n    logDebug(`Request: ContextId=${contextId}, FilePath=${filePath}`);\n    const req = new $_client.GetContextFileUploadUrlRequest({\n      authorization: `Bearer ${this.agentBay.getAPIKey()}`,\n      contextId,\n      filePath,\n    });\n    const resp = await this.agentBay.getClient().getContextFileUploadUrl(req);\n    const requestId = extractRequestId(resp) || \"\";\n    const body = resp.body;\n\n    // Check for API-level errors\n    if (body?.success === false && body.code) {\n      const errorMessage = `[${body.code}] ${body.message || 'Unknown error'}`;\n      const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"GetContextFileUploadUrl\", requestId, false, undefined, fullResponse);\n      return {\n        requestId,\n        success: false,\n        url: \"\",\n        expireTime: undefined,\n        errorMessage,\n      };\n    }\n\n    const data = body?.data;\n    const success = !!(body && body.success);\n\n    // Log API response with key fields\n    const keyFields: Record<string, any> = {\n      context_id: contextId,\n      file_path: filePath,\n    };\n    if (data?.expireTime) {\n      keyFields.expire_time = data.expireTime;\n    }\n    const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n    logAPIResponseWithDetails(\"GetContextFileUploadUrl\", requestId, success, keyFields, fullResponse);\n\n    return {\n      requestId,\n      success,\n      url: data?.url || \"\",\n      expireTime: data?.expireTime,\n      errorMessage: undefined,\n    };\n  }\n\n  /**\n   * Get a presigned download URL for a file in a context.\n   *\n   * @param contextId - The ID of the context.\n   * @param filePath - The path to the file in the context.\n   * @returns FileUrlResult with the presigned URL and expiration time.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const contextResult = await agentBay.context.get('my-context');\n   * if (contextResult.success) {\n   *   const urlResult = await agentBay.context.getFileDownloadUrl(contextResult.context.id, '/data/file.txt');\n   *   console.log('Download URL:', urlResult.url);\n   *   console.log('Expires at:', urlResult.expireTime);\n   * }\n   * ```\n   */\n  async getFileDownloadUrl(contextId: string, filePath: string): Promise<FileUrlResult> {\n    logAPICall(\"GetContextFileDownloadUrl\");\n    logDebug(`Request: ContextId=${contextId}, FilePath=${filePath}`);\n    const req = new $_client.GetContextFileDownloadUrlRequest({\n      authorization: `Bearer ${this.agentBay.getAPIKey()}`,\n      contextId,\n      filePath,\n    });\n    const resp = await this.agentBay.getClient().getContextFileDownloadUrl(req);\n    const requestId = extractRequestId(resp) || \"\";\n    const body = resp.body;\n\n    // Check for API-level errors\n    if (body?.success === false && body.code) {\n      const errorMessage = `[${body.code}] ${body.message || 'Unknown error'}`;\n      const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"GetContextFileDownloadUrl\", requestId, false, undefined, fullResponse);\n      return {\n        requestId,\n        success: false,\n        url: \"\",\n        expireTime: undefined,\n        errorMessage,\n      };\n    }\n\n    const data = body?.data;\n    const success = !!(body && body.success);\n\n    // Log API response with key fields\n    const keyFields: Record<string, any> = {\n      context_id: contextId,\n      file_path: filePath,\n    };\n    if (data?.expireTime) {\n      keyFields.expire_time = data.expireTime;\n    }\n    const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n    logAPIResponseWithDetails(\"GetContextFileDownloadUrl\", requestId, success, keyFields, fullResponse);\n\n    return {\n      requestId,\n      success,\n      url: data?.url || \"\",\n      expireTime: data?.expireTime,\n      errorMessage: undefined,\n    };\n  }\n\n  /**\n   * Delete a file in a context.\n   *\n   * @param contextId - The ID of the context.\n   * @param filePath - The path to the file to delete.\n   * @returns OperationResult indicating success or failure.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const contextResult = await agentBay.context.get('my-context');\n   * if (contextResult.success) {\n   *   const deleteResult = await agentBay.context.deleteFile(contextResult.context.id, '/data/file.txt');\n   *   console.log('File deleted:', deleteResult.success);\n   * }\n   * ```\n   */\n  async deleteFile(contextId: string, filePath: string): Promise<OperationResult> {\n    logAPICall(\"DeleteContextFile\");\n    logDebug(`Request: ContextId=${contextId}, FilePath=${filePath}`);\n    const req = new $_client.DeleteContextFileRequest({\n      authorization: `Bearer ${this.agentBay.getAPIKey()}`,\n      contextId,\n      filePath,\n    });\n    const resp = await this.agentBay.getClient().deleteContextFile(req);\n    const requestId = extractRequestId(resp) || \"\";\n    const body = resp.body;\n    const success = !!(body && body.success);\n\n    // Check for API-level errors\n    const errorMessage = success\n      ? \"\"\n      : `[${body?.code || 'Unknown'}] ${body?.message || 'Failed to delete file'}`;\n\n    // Log API response with key fields\n    const keyFields: Record<string, any> = {\n      context_id: contextId,\n      file_path: filePath,\n    };\n    const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n    logAPIResponseWithDetails(\"DeleteContextFile\", requestId, success, keyFields, fullResponse);\n\n    return {\n      requestId,\n      success,\n      data: success,\n      errorMessage,\n    };\n  }\n\n  /**\n   * List files under a specific folder path in a context.\n   *\n   * @param contextId - The ID of the context.\n   * @param parentFolderPath - The parent folder path to list files from.\n   * @param pageNumber - Page number for pagination (default: 1).\n   * @param pageSize - Number of files per page (default: 50).\n   * @returns ContextFileListResult with file entries and total count.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const contextResult = await agentBay.context.get('my-context');\n   * if (contextResult.success) {\n   *   const listResult = await agentBay.context.listFiles(contextResult.context.id, '/data');\n   *   console.log(`Found ${listResult.entries.length} files`);\n   *   console.log(`Total count: ${listResult.count}`);\n   * }\n   * ```\n   */\n  async listFiles(\n    contextId: string,\n    parentFolderPath: string,\n    pageNumber = 1,\n    pageSize = 50\n  ): Promise<ContextFileListResult> {\n    logAPICall(\"DescribeContextFiles\");\n    logDebug(\n      `Request: ContextId=${contextId}, ParentFolderPath=${parentFolderPath}, PageNumber=${pageNumber}, PageSize=${pageSize}`\n    );\n    const req = new $_client.DescribeContextFilesRequest({\n      authorization: `Bearer ${this.agentBay.getAPIKey()}`,\n      pageNumber,\n      pageSize,\n      parentFolderPath,\n      contextId,\n    });\n    const resp = await this.agentBay.getClient().describeContextFiles(req);\n    const requestId = extractRequestId(resp) || \"\";\n    const body = resp.body;\n\n    // Check for API-level errors\n    if (body?.success === false && body.code) {\n      const errorMessage = `[${body.code}] ${body.message || 'Unknown error'}`;\n      const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"DescribeContextFiles\", requestId, false, undefined, fullResponse);\n      return {\n        requestId,\n        success: false,\n        entries: [],\n        count: undefined,\n        errorMessage,\n      };\n    }\n\n    const rawList = body?.data || [];\n    const entries: ContextFileEntry[] = rawList.map((it: any) => ({\n      fileId: it.fileId,\n      fileName: it.fileName,\n      filePath: it.filePath || \"\",\n      fileType: it.fileType,\n      gmtCreate: it.gmtCreate,\n      gmtModified: it.gmtModified,\n      size: it.size,\n      status: it.status,\n    }));\n\n    const success = !!(body && body.success);\n\n    // Log API response with key fields\n    const keyFields: Record<string, any> = {\n      context_id: contextId,\n      parent_folder_path: parentFolderPath,\n      file_count: entries.length,\n      page_number: pageNumber,\n      page_size: pageSize,\n    };\n    if (body?.count !== undefined) {\n      keyFields.count = body.count;\n    }\n    const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n    logAPIResponseWithDetails(\"DescribeContextFiles\", requestId, success, keyFields, fullResponse);\n\n    return {\n      requestId,\n      success,\n      entries,\n      count: body?.count,\n      errorMessage: undefined,\n    };\n  }\n\n  /**\n   * Asynchronously initiate a task to clear the context's persistent data.\n   *\n   * This is a non-blocking method that returns immediately after initiating the clearing task\n   * on the backend. The context's state will transition to \"clearing\" while the operation\n   * is in progress.\n   *\n   * @param contextId - Unique ID of the context to clear.\n   * @returns A ClearContextResult object indicating the task has been successfully started,\n   *          with status field set to \"clearing\".\n   * @throws APIError - If the backend API rejects the clearing request (e.g., invalid ID).\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const getResult = await agentBay.context.get('my-context');\n   * if (getResult.success) {\n   *   const clearResult = await agentBay.context.clearAsync(getResult.context.id);\n   *   console.log('Clear task started:', clearResult.success);\n   *   console.log('Status:', clearResult.status);\n   * }\n   * ```\n   */\n  async clearAsync(contextId: string): Promise<ClearContextResult> {\n    try {\n      logAPICall(\"ClearContext\");\n      logDebug(`Request: ContextId=${contextId}`);\n\n      const request = new $_client.ClearContextRequest({\n        authorization: `Bearer ${this.agentBay.getAPIKey()}`,\n        id: contextId,\n      });\n\n      const response = await this.agentBay.getClient().clearContext(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Directly access response body object\n      if (!response.body) {\n        const fullResponse = \"Empty response body\";\n        logAPIResponseWithDetails(\"ClearContext\", requestId, false, undefined, fullResponse);\n        return {\n          requestId,\n          success: false,\n          errorMessage: \"Empty response body\",\n        };\n      }\n\n      const body = response.body;\n\n      // Check for API-level errors\n      if (!body.success && body.code) {\n        const errorMessage = `[${body.code}] ${body.message || 'Unknown error'}`;\n        const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n        logAPIResponseWithDetails(\"ClearContext\", requestId, false, undefined, fullResponse);\n        return {\n          requestId,\n          success: false,\n          errorMessage,\n        };\n      }\n\n      // ClearContext API returns success info without Data field\n      // Initial status is \"clearing\" when the task starts\n      const keyFields: Record<string, any> = {\n        context_id: contextId,\n        status: \"clearing\",\n      };\n      const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"ClearContext\", requestId, true, keyFields, fullResponse);\n\n      return {\n        requestId,\n        success: true,\n        contextId,\n        status: \"clearing\",\n        errorMessage: \"\",\n      };\n    } catch (error) {\n      logError(\"Error calling ClearContext:\", error);\n      throw new APIError(`Failed to start context clearing for ${contextId}: ${error}`);\n    }\n  }\n\n  /**\n   * Queries the status of the clearing task.\n   *\n   * This method calls GetContext API directly and parses the raw response to extract\n   * the state field, which indicates the current clearing status.\n   *\n   * @param contextId - ID of the context.\n   * @returns ClearContextResult object containing the current task status.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const getResult = await agentBay.context.get('my-context');\n   * if (getResult.success) {\n   *   const statusResult = await agentBay.context.getClearStatus(getResult.context.id);\n   *   console.log('Current status:', statusResult.status);\n   * }\n   * ```\n   */\n  async getClearStatus(contextId: string): Promise<ClearContextResult> {\n    try {\n      logAPICall(\"GetContext\");\n      logDebug(`Request: ContextId=${contextId} (for clear status)`);\n\n      const request = new $_client.GetContextRequest({\n        authorization: `Bearer ${this.agentBay.getAPIKey()}`,\n        contextId: contextId,\n        allowCreate: \"false\",\n      });\n\n      const response = await this.agentBay.getClient().getContext(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Directly access response body object\n      if (!response.body) {\n        const fullResponse = \"Empty response body\";\n        logAPIResponseWithDetails(\"GetContext (for clear status)\", requestId, false, undefined, fullResponse);\n        return {\n          requestId,\n          success: false,\n          errorMessage: \"Empty response body\",\n        };\n      }\n\n      const body = response.body;\n\n      // Check for API-level errors\n      if (!body.success && body.code) {\n        const errorMessage = `[${body.code}] ${body.message || 'Unknown error'}`;\n        const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n        logAPIResponseWithDetails(\"GetContext (for clear status)\", requestId, false, undefined, fullResponse);\n        return {\n          requestId,\n          success: false,\n          errorMessage,\n        };\n      }\n\n      // Check if data exists\n      if (!body.data) {\n        const fullResponse = \"No data in response\";\n        logAPIResponseWithDetails(\"GetContext (for clear status)\", requestId, false, undefined, fullResponse);\n        return {\n          requestId,\n          success: false,\n          errorMessage: \"No data in response\",\n        };\n      }\n\n      const data = body.data;\n\n      // Extract clearing status from the response data object\n      // The server's state field indicates the clearing status:\n      // - \"clearing\": Clearing is in progress\n      // - \"available\": Clearing completed successfully\n      // - \"in-use\": Context is being used\n      // - \"pre-available\": Context is being prepared\n      const contextIdFromResponse = data.id || \"\";\n      const state = data.state || \"clearing\"; // Extract state from response object\n      const errorMessage = \"\"; // ErrorMessage is not in GetContextResponseBodyData\n\n      const keyFields: Record<string, any> = {\n        context_id: contextIdFromResponse,\n        state: state,\n      };\n      const fullResponse = body ? JSON.stringify(body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"GetContext (for clear status)\", requestId, true, keyFields, fullResponse);\n\n      return {\n        requestId,\n        success: true,\n        contextId: contextIdFromResponse,\n        status: state,\n        errorMessage,\n      };\n    } catch (error) {\n      logError(\"Error calling GetContext (for clear status):\", error);\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to get clear status: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Synchronously clear the context's persistent data and wait for the final result.\n   *\n   * This method wraps the `clearAsync` and `getClearStatus` polling logic,\n   * providing the simplest and most direct way to handle clearing tasks.\n   *\n   * The clearing process transitions through the following states:\n   * - \"clearing\": Data clearing is in progress\n   * - \"available\": Clearing completed successfully (final success state)\n   *\n   * @param contextId - Unique ID of the context to clear.\n   * @param timeout - (Optional) Timeout in seconds to wait for task completion, default is 60 seconds.\n   * @param pollInterval - (Optional) Interval in seconds between status polls, default is 2 seconds.\n   * @returns A ClearContextResult object containing the final task result.\n   *          The status field will be \"available\" on success, or other states if interrupted.\n   * @throws APIError - If the task fails to complete within the specified timeout.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const getResult = await agentBay.context.get('my-context');\n   * if (getResult.success) {\n   *   const clearResult = await agentBay.context.clear(getResult.context.id);\n   *   console.log('Context cleared:', clearResult.success);\n   *   console.log('Final status:', clearResult.status);\n   * }\n   * ```\n   */\n  async clear(contextId: string, timeout = 60, pollInterval = 2.0): Promise<ClearContextResult> {\n    // 1. Asynchronously start the clearing task\n    const startResult = await this.clearAsync(contextId);\n    if (!startResult.success) {\n      return startResult;\n    }\n\n    logInfo(`Started context clearing task for: ${contextId}`);\n\n    // 2. Poll task status until completion or timeout\n    const startTime = Date.now();\n    const maxAttempts = Math.floor(timeout / pollInterval);\n    let attempt = 0;\n\n    while (attempt < maxAttempts) {\n      // Wait before querying\n      await new Promise(resolve => setTimeout(resolve, pollInterval * 1000));\n      attempt++;\n\n      // Query task status (using GetContext API with context ID)\n      const statusResult = await this.getClearStatus(contextId);\n\n      if (!statusResult.success) {\n        logError(`Failed to get clear status: ${statusResult.errorMessage}`);\n        return statusResult;\n      }\n\n      const status = statusResult.status;\n      logInfo(`Clear task status: ${status} (attempt ${attempt}/${maxAttempts})`);\n\n      // Check if completed\n      // When clearing is complete, the state changes from \"clearing\" to \"available\"\n      if (status === \"available\") {\n        const elapsed = (Date.now() - startTime) / 1000;\n        logInfo(`Context cleared successfully in ${elapsed.toFixed(2)} seconds`);\n        return {\n          requestId: startResult.requestId,\n          success: true,\n          contextId: statusResult.contextId,\n          status,\n          errorMessage: \"\",\n        };\n      } else if (status && ![\"clearing\", \"pre-available\"].includes(status)) {\n        // If status is not \"clearing\" or \"pre-available\", and not \"available\",\n        // treat it as a potential error or unexpected state\n        const elapsed = (Date.now() - startTime) / 1000;\n        logError(`Context in unexpected state after ${elapsed.toFixed(2)} seconds: ${status}`);\n        // Continue polling as the state might transition to \"available\"\n      }\n    }\n\n    // Timeout\n    const elapsed = (Date.now() - startTime) / 1000;\n    const errorMsg = `Context clearing timed out after ${elapsed.toFixed(2)} seconds`;\n    logError(errorMsg);\n    throw new APIError(errorMsg);\n  }\n}\n","/**\n * Base exception for all AgentBay SDK errors.\n */\nexport class AgentBayError extends Error {\n  extra: Record<string, any>;\n\n  constructor(message?: string, extra: Record<string, any> = {}) {\n    const errorMessage = message || \"AgentBayError\";\n    super(errorMessage);\n    this.name = \"AgentBayError\";\n    this.extra = extra;\n    Object.setPrototypeOf(this, AgentBayError.prototype);\n  }\n}\n\n/**\n * Raised when there is an authentication error.\n */\nexport class AuthenticationError extends AgentBayError {\n  constructor(\n    message = \"Authentication failed\",\n    extra: Record<string, any> = {}\n  ) {\n    super(message, extra);\n    this.name = \"AuthenticationError\";\n    Object.setPrototypeOf(this, AuthenticationError.prototype);\n  }\n}\n\n/**\n * Raised when there is an error with the API.\n */\nexport class APIError extends AgentBayError {\n  statusCode?: number;\n\n  constructor(\n    message = \"API error\",\n    statusCode?: number,\n    extra: Record<string, any> = {}\n  ) {\n    super(message, extra);\n    this.name = \"APIError\";\n    this.statusCode = statusCode;\n    Object.setPrototypeOf(this, APIError.prototype);\n  }\n}\n\n/**\n * Raised for errors related to file operations.\n */\nexport class FileError extends AgentBayError {\n  constructor(\n    message = \"File operation error\",\n    extra: Record<string, any> = {}\n  ) {\n    super(message, extra);\n    this.name = \"FileError\";\n    Object.setPrototypeOf(this, FileError.prototype);\n  }\n}\n\n/**\n * Raised for errors related to session operations.\n */\nexport class SessionError extends AgentBayError {\n  constructor(message = \"Session error\", extra: Record<string, any> = {}) {\n    super(message, extra);\n    this.name = \"SessionError\";\n    Object.setPrototypeOf(this, SessionError.prototype);\n  }\n}\n\n/**\n * Raised for errors related to OSS operations.\n */\nexport class OssError extends AgentBayError {\n  constructor(\n    message = \"OSS operation error\",\n    extra: Record<string, any> = {}\n  ) {\n    super(message, extra);\n    this.name = \"OssError\";\n    Object.setPrototypeOf(this, OssError.prototype);\n  }\n}\n\n/**\n * Raised for errors related to browser operations.\n */\nexport class BrowserError extends AgentBayError {\n  constructor(\n    message = \"Browser operation error\",\n    extra: Record<string, any> = {}\n  ) {\n    super(message, extra);\n    this.name = \"BrowserError\";\n    Object.setPrototypeOf(this, BrowserError.prototype);\n  }\n}\n","/**\n * Utility functions for structured logging with multiple levels, API tracking,\n * sensitive data masking, and RequestID management.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n/**\n * Log level type\n */\nexport type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';\n\n/**\n * Logger configuration options\n *\n * @example\n * ```typescript\n * import { AgentBay, setupLogger } from 'wuying-agentbay-sdk';\n *\n * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n *\n * async function demonstrateLogging() {\n *     try {\n *         // Configure logging with file output\n *         setupLogger({\n *             level: 'DEBUG',\n *             logFile: '/var/log/agentbay.log',\n *             maxFileSize: '100 MB',\n *             enableConsole: true\n *         });\n *\n *         // Create a session - logs will be written to both console and file\n *         const result = await agentBay.create();\n *         if (result.success) {\n *             const session = result.session;\n *             console.log(`Session created: ${session.sessionId}`);\n *             await session.delete();\n *         }\n *     } catch (error) {\n *         console.error('Error:', error);\n *     }\n * }\n *\n * demonstrateLogging().catch(console.error);\n * ```\n */\nexport interface LoggerConfig {\n  level?: LogLevel;\n  logFile?: string;\n  maxFileSize?: string;\n  enableConsole?: boolean;\n}\n\n/**\n * Log level numeric values for comparison\n */\nconst LOG_LEVEL_VALUES: Record<LogLevel, number> = {\n  DEBUG: 0,\n  INFO: 1,\n  WARN: 2,\n  ERROR: 3,\n};\n\n/**\n * RequestID storage for tracking across API calls\n */\nlet currentRequestId = '';\n\n/**\n * Current log level configuration\n * Supports both LOG_LEVEL and AGENTBAY_LOG_LEVEL environment variables\n */\nlet currentLogLevel: LogLevel = (\n  (process.env.LOG_LEVEL as LogLevel) ||\n  (process.env.AGENTBAY_LOG_LEVEL as LogLevel) ||\n  'INFO'\n);\n\n/**\n * File logging configuration\n */\nlet fileLoggingEnabled = false;\nlet logFilePath: string | null = null;\nlet logFileMaxSize = 10 * 1024 * 1024; // 10MB default\nlet consoleLoggingEnabled = true;\n\n/**\n * Determine whether to use colors in output\n * Priority: DISABLE_COLORS > FORCE_COLOR > TTY > IDE > default\n */\nfunction shouldUseColors(): boolean {\n  // Priority 1: Explicit disable via DISABLE_COLORS\n  if (process.env.DISABLE_COLORS === 'true') {\n    return false;\n  }\n\n  // Priority 2: Explicit enable via FORCE_COLOR\n  if (process.env.FORCE_COLOR !== undefined && process.env.FORCE_COLOR !== '0') {\n    return true;\n  }\n\n  // Priority 3: TTY detection (terminal output)\n  const isTTY = process.stdout?.isTTY || process.stderr?.isTTY;\n  if (isTTY) {\n    return true;\n  }\n\n  // Priority 4: IDE environment detection\n  const isVSCode = process.env.TERM_PROGRAM === 'vscode';\n  const isGoLand = process.env.GOLAND !== undefined;\n  const isIntelliJ = process.env.IDEA_INITIAL_DIRECTORY !== undefined;\n  if (isVSCode || isGoLand || isIntelliJ) {\n    return true;\n  }\n\n  // Default: no colors (safe for file output, CI/CD, pipes)\n  return false;\n}\n\n/**\n * ANSI color codes\n */\nconst ANSI_RESET = '\\x1b[0m';\nconst ANSI_BLUE = '\\x1b[34m';\nconst ANSI_CYAN = '\\x1b[36m';\nconst ANSI_YELLOW = '\\x1b[33m';\nconst ANSI_RED = '\\x1b[31m';\nconst ANSI_GREEN = '\\x1b[32m';\n\n/**\n * Determine if colors should be used (evaluated once at startup)\n */\nconst useColors = shouldUseColors();\n\n/**\n * Sensitive field names for data masking\n */\nconst SENSITIVE_FIELDS = [\n  'api_key', 'apikey', 'api-key',\n  'password', 'passwd', 'pwd',\n  'token', 'access_token', 'auth_token',\n  'secret', 'private_key', 'authorization',\n];\n\n/**\n * Get emoji for log level\n */\nfunction getLogLevelEmoji(level: LogLevel): string {\n  switch (level) {\n    case 'DEBUG':\n      return '🐛 DEBUG';\n    case 'INFO':\n      return 'ℹ️  INFO';\n    case 'WARN':\n      return '⚠️  WARN';\n    case 'ERROR':\n      return '❌ ERROR';\n    default:\n      return level;\n  }\n}\n\n/**\n * Check if a message should be logged based on current log level\n */\nfunction shouldLog(level: LogLevel): boolean {\n  return LOG_LEVEL_VALUES[level] >= LOG_LEVEL_VALUES[currentLogLevel];\n}\n\n/**\n * Format log message with level and RequestID\n */\nfunction formatLogMessage(level: LogLevel, message: string, forFile = false): string {\n  let formattedMessage = `${getLogLevelEmoji(level)}: ${message}`;\n  if (currentRequestId) {\n    formattedMessage += ` [RequestId=${currentRequestId}]`;\n  }\n\n  if (forFile || !useColors) {\n    return formattedMessage;\n  }\n\n  // Apply colors based on log level\n  const color = getColorForLevel(level);\n  return `${color}${formattedMessage}${ANSI_RESET}`;\n}\n\n/**\n * Get color code for log level\n */\nfunction getColorForLevel(level: LogLevel): string {\n  switch (level) {\n    case 'DEBUG':\n      return ANSI_CYAN;\n    case 'INFO':\n      return ANSI_BLUE;\n    case 'WARN':\n      return ANSI_YELLOW;\n    case 'ERROR':\n      return ANSI_RED;\n    default:\n      return '';\n  }\n}\n\n/**\n * Mask sensitive information in data structures\n * @param data Data to mask (dict, str, list, etc.)\n * @param fields Additional sensitive field names\n * @returns Masked data (deep copy)\n */\nexport function maskSensitiveData(data: any, fields?: string[]): any {\n  const sensitiveFields = fields || SENSITIVE_FIELDS;\n  const visitedObjects = new WeakSet();\n\n  function mask(obj: any): any {\n    if (obj === null || obj === undefined) {\n      return obj;\n    }\n\n    if (typeof obj === 'object') {\n      if (visitedObjects.has(obj)) {\n        return '[Circular]';\n      }\n      visitedObjects.add(obj);\n\n      if (Array.isArray(obj)) {\n        return obj.map(item => mask(item));\n      }\n\n      const masked: any = {};\n      for (const key in obj) {\n        if (Object.prototype.hasOwnProperty.call(obj, key)) {\n          const value = obj[key];\n          if (sensitiveFields.some(field => key.toLowerCase().includes(field.toLowerCase()))) {\n            if (typeof value === 'string' && value.length > 4) {\n              masked[key] = value.substring(0, 2) + '****' + value.substring(value.length - 2);\n            } else {\n              masked[key] = '****';\n            }\n          } else {\n            masked[key] = mask(value);\n          }\n        }\n      }\n      return masked;\n    }\n\n    if (typeof obj === 'string') {\n      return obj;\n    }\n\n    return obj;\n  }\n\n  return mask(data);\n}\n\n/**\n * Set the log level\n * @param level The log level to set\n */\nexport function setLogLevel(level: LogLevel): void {\n  if (LOG_LEVEL_VALUES[level] !== undefined) {\n    currentLogLevel = level;\n  }\n}\n\n/**\n * Get the current log level\n * @returns The current log level\n */\nexport function getLogLevel(): LogLevel {\n  return currentLogLevel;\n}\n\n/**\n * Set the RequestID for tracking\n * @param requestId The RequestID to set\n */\nexport function setRequestId(requestId: string): void {\n  currentRequestId = requestId;\n}\n\n/**\n * Get the current RequestID\n * @returns The current RequestID or empty string\n */\nexport function getRequestId(): string {\n  return currentRequestId;\n}\n\n/**\n * Clear the current RequestID\n */\nexport function clearRequestId(): void {\n  currentRequestId = '';\n}\n\n/**\n * Parse file size string to bytes (e.g., \"10 MB\" -> 10485760)\n * @param sizeStr Size string like \"10 MB\", \"100 MB\", \"1 GB\"\n * @returns Size in bytes\n */\nfunction parseFileSize(sizeStr: string): number {\n  const match = sizeStr.match(/^(\\d+)\\s*(MB|GB|KB)?$/i);\n  if (!match) {\n    return 10 * 1024 * 1024; // Default 10MB\n  }\n\n  const value = parseInt(match[1], 10);\n  const unit = (match[2] || 'MB').toUpperCase();\n\n  switch (unit) {\n    case 'KB':\n      return value * 1024;\n    case 'MB':\n      return value * 1024 * 1024;\n    case 'GB':\n      return value * 1024 * 1024 * 1024;\n    default:\n      return value * 1024 * 1024;\n  }\n}\n\n/**\n * Write log message to file\n * @param message The formatted log message\n */\nfunction writeToFile(message: string): void {\n  if (!fileLoggingEnabled || !logFilePath) {\n    return;\n  }\n\n  try {\n    // Check file size and rotate if necessary\n    if (fs.existsSync(logFilePath)) {\n      const stats = fs.statSync(logFilePath);\n      if (stats.size >= logFileMaxSize) {\n        // Rotate: rename current file to .log.1\n        const rotatedPath = `${logFilePath}.1`;\n        if (fs.existsSync(rotatedPath)) {\n          fs.unlinkSync(rotatedPath);\n        }\n        fs.renameSync(logFilePath, rotatedPath);\n      }\n    }\n\n    // Append to file (create if doesn't exist)\n    fs.appendFileSync(logFilePath, message + '\\n', 'utf8');\n  } catch (error) {\n    // Silently fail to avoid infinite loop\n    if (consoleLoggingEnabled) {\n      process.stderr.write(`Failed to write to log file: ${error}\\n`);\n    }\n  }\n}\n\n/**\n * Setup logger configuration\n * @param config Logger configuration options\n *\n * @example\n * ```typescript\n * import { AgentBay, setupLogger } from 'wuying-agentbay-sdk';\n *\n * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n *\n * async function demonstrateLogging() {\n *     try {\n *         // Configure logging to file only\n *         setupLogger({\n *             level: 'DEBUG',\n *             logFile: '/var/log/myapp.log',\n *             maxFileSize: '100 MB',\n *             enableConsole: false\n *         });\n *\n *         // Create a session - logs will only be written to file\n *         const result = await agentBay.create({ imageId: 'browser_latest' });\n *         if (result.success) {\n *             const session = result.session;\n *             // All operations will be logged to file\n *             await session.delete();\n *         }\n *     } catch (error) {\n *         console.error('Error:', error);\n *     }\n * }\n *\n * demonstrateLogging().catch(console.error);\n * ```\n */\nexport function setupLogger(config: LoggerConfig): void {\n  if (config.level) {\n    setLogLevel(config.level);\n  }\n\n  if (config.logFile !== undefined) {\n    if (config.logFile) {\n      logFilePath = config.logFile;\n      fileLoggingEnabled = true;\n\n      // Ensure directory exists\n      const dir = path.dirname(logFilePath);\n      if (!fs.existsSync(dir)) {\n        fs.mkdirSync(dir, { recursive: true });\n      }\n\n      // Parse max file size\n      if (config.maxFileSize) {\n        logFileMaxSize = parseFileSize(config.maxFileSize);\n      }\n    } else {\n      fileLoggingEnabled = false;\n      logFilePath = null;\n    }\n  }\n\n  if (config.enableConsole !== undefined) {\n    consoleLoggingEnabled = config.enableConsole;\n  }\n}\n\n/**\n * Log a message without the log prefix and file location\n * Treated as INFO level - will be filtered if log level is WARN or higher\n * @param message The message to log\n * @param args Optional arguments to log\n */\nexport function log(message: string, ...args: any[]): void {\n  if (!shouldLog('INFO')) return;\n\n  if (consoleLoggingEnabled) {\n    process.stdout.write(message + \"\\n\");\n  }\n\n  // Write to file without colors\n  writeToFile(message);\n\n  if (args.length > 0) {\n    for (const arg of args) {\n      const argStr = typeof arg === \"object\" && arg !== null\n        ? JSON.stringify(arg, null, 2)\n        : String(arg);\n\n      if (consoleLoggingEnabled) {\n        process.stdout.write(argStr + \"\\n\");\n      }\n      writeToFile(argStr);\n    }\n  }\n}\n\n/**\n * Log a debug level message\n * @param message The message to log\n * @param args Optional arguments to log\n */\nexport function logDebug(message: string, ...args: any[]): void {\n  if (!shouldLog('DEBUG')) return;\n\n  const formattedMessage = formatLogMessage('DEBUG', message);\n  const fileMessage = formatLogMessage('DEBUG', message, true);\n\n  if (consoleLoggingEnabled) {\n    process.stdout.write(formattedMessage + \"\\n\");\n  }\n  writeToFile(fileMessage);\n\n  if (args.length > 0) {\n    for (const arg of args) {\n      const argStr = typeof arg === \"object\" && arg !== null\n        ? JSON.stringify(arg, null, 2)\n        : String(arg);\n\n      if (consoleLoggingEnabled) {\n        process.stdout.write(argStr + \"\\n\");\n      }\n      writeToFile(argStr);\n    }\n  }\n}\n\n/**\n * Log an info level message\n * @param message The message to log\n * @param args Optional arguments to log\n */\nexport function logInfo(message: string, ...args: any[]): void {\n  if (!shouldLog('INFO')) return;\n\n  const formattedMessage = formatLogMessage('INFO', message);\n  const fileMessage = formatLogMessage('INFO', message, true);\n\n  if (consoleLoggingEnabled) {\n    process.stdout.write(formattedMessage + \"\\n\");\n  }\n  writeToFile(fileMessage);\n\n  if (args.length > 0) {\n    for (const arg of args) {\n      const argStr = typeof arg === \"object\" && arg !== null\n        ? JSON.stringify(arg, null, 2)\n        : String(arg);\n\n      if (consoleLoggingEnabled) {\n        process.stdout.write(argStr + \"\\n\");\n      }\n      writeToFile(argStr);\n    }\n  }\n}\n\n/**\n * Log a warning level message (outputs to stderr)\n * @param message The message to log\n * @param args Optional arguments to log\n */\nexport function logWarn(message: string, ...args: any[]): void {\n  if (!shouldLog('WARN')) return;\n\n  const formattedMessage = formatLogMessage('WARN', message);\n  const fileMessage = formatLogMessage('WARN', message, true);\n\n  if (consoleLoggingEnabled) {\n    process.stderr.write(formattedMessage + \"\\n\");\n  }\n  writeToFile(fileMessage);\n\n  if (args.length > 0) {\n    for (const arg of args) {\n      const argStr = typeof arg === \"object\" && arg !== null\n        ? JSON.stringify(arg, null, 2)\n        : String(arg);\n\n      if (consoleLoggingEnabled) {\n        process.stderr.write(argStr + \"\\n\");\n      }\n      writeToFile(argStr);\n    }\n  }\n}\n\n/**\n * Log an error message with optional error object (outputs to stderr)\n * @param message The error message to log\n * @param error Optional error object\n */\nexport function logError(message: string, error?: any): void {\n  if (!shouldLog('ERROR')) return;\n\n  const formattedMessage = formatLogMessage('ERROR', message);\n  const fileMessage = formatLogMessage('ERROR', message, true);\n\n  if (consoleLoggingEnabled) {\n    process.stderr.write(formattedMessage + \"\\n\");\n  }\n  writeToFile(fileMessage);\n\n  if (error) {\n    let errorStr = '';\n    if (error instanceof Error) {\n      errorStr = error.message;\n      if (error.stack) {\n        errorStr += `\\nStack Trace:\\n${error.stack}`;\n      }\n    } else if (typeof error === \"object\") {\n      errorStr = JSON.stringify(error, null, 2);\n    } else {\n      errorStr = String(error);\n    }\n\n    if (consoleLoggingEnabled) {\n      process.stderr.write(errorStr + \"\\n\");\n    }\n    writeToFile(errorStr);\n  }\n}\n\n/**\n * Log an API call\n * @param apiName Name of the API being called\n * @param requestData Optional request data to log at DEBUG level\n */\nexport function logAPICall(apiName: string, requestData?: any): void {\n  if (!shouldLog('INFO')) return;\n  const message = `🔗 API Call: ${apiName}`;\n\n  // Temporarily clear RequestId since it's not available until API response\n  const savedRequestId = currentRequestId;\n  currentRequestId = '';\n\n  if (useColors) {\n    // Use cyan/bright blue for API calls\n    process.stdout.write(`${ANSI_CYAN}ℹ️  INFO: ${message}${ANSI_RESET}\\n`);\n  } else {\n    logInfo(message);\n  }\n\n  currentRequestId = savedRequestId;\n\n  if (requestData && shouldLog('DEBUG')) {\n    const maskedData = maskSensitiveData(requestData);\n    logDebug(`📤 Request: ${JSON.stringify(maskedData)}`);\n  }\n}\n\n/**\n * Log an API response with key details at INFO level\n * @param apiName Name of the API being called\n * @param requestId Request ID from the response\n * @param success Whether the API call was successful\n * @param keyFields Dictionary of key business fields to log\n * @param fullResponse Full response body (logged at DEBUG level)\n */\nexport function logAPIResponseWithDetails(\n  apiName: string,\n  requestId?: string,\n  success = true,\n  keyFields?: Record<string, any>,\n  fullResponse?: string\n): void {\n\n  if (success) {\n    if (shouldLog('INFO')) {\n      let mainMessage = `✅ API Response: ${apiName}`;\n      if (requestId) {\n        mainMessage += `, RequestId=${requestId}`;\n      }\n\n      if (useColors) {\n        // Use green for successful API responses\n        process.stdout.write(`${ANSI_GREEN}ℹ️  INFO: ${mainMessage}${ANSI_RESET}\\n`);\n      } else {\n        logInfo(mainMessage);\n      }\n\n      if (keyFields) {\n        for (const [key, value] of Object.entries(keyFields)) {\n          const maskedValue = maskSensitiveData({ [key]: value });\n          const keyMessage = `  └─ ${key}=${maskedValue[key]}`;\n          if (useColors) {\n            process.stdout.write(`${ANSI_GREEN}ℹ️  INFO: ${keyMessage}${ANSI_RESET}\\n`);\n          } else {\n            logInfo(keyMessage);\n          }\n        }\n      }\n    }\n\n    if (fullResponse && shouldLog('DEBUG')) {\n      logDebug(`📥 Full Response: ${fullResponse}`);\n    }\n  } else {\n    if (shouldLog('ERROR')) {\n      let errorMessage = `❌ API Response Failed: ${apiName}`;\n      if (requestId) {\n        errorMessage += `, RequestId=${requestId}`;\n      }\n\n      if (useColors) {\n        // Use red for failed API responses\n        process.stderr.write(`${ANSI_RED}❌ ERROR: ${errorMessage}${ANSI_RESET}\\n`);\n      } else {\n        logError(errorMessage);\n      }\n\n      if (fullResponse) {\n        if (useColors) {\n          process.stderr.write(`${ANSI_RED}ℹ️  INFO: 📥 Response: ${fullResponse}${ANSI_RESET}\\n`);\n        } else {\n          logError(`📥 Response: ${fullResponse}`);\n        }\n      }\n    }\n  }\n}\n\n/**\n * Extract and log the actual code execution output from run_code response\n * @param requestId Request ID from the API response\n * @param rawOutput Raw JSON output from the MCP tool\n */\nexport function logCodeExecutionOutput(requestId: string, rawOutput: string): void {\n  if (!shouldLog('INFO')) return;\n\n  try {\n    // Parse the JSON response to extract the actual code output\n    const response = JSON.parse(rawOutput);\n\n    // Extract text from all content items\n    const texts: string[] = [];\n    if (response && typeof response === 'object' && 'content' in response) {\n      const content = response.content;\n      if (Array.isArray(content)) {\n        for (const item of content) {\n          if (item && typeof item === 'object' && item.type === 'text' && item.text) {\n            texts.push(item.text);\n          }\n        }\n      }\n    }\n\n    if (texts.length === 0) {\n      return;\n    }\n\n    const actualOutput = texts.join('');\n\n    // Format the output with a clear separator\n    const header = `📋 Code Execution Output (RequestID: ${requestId}):`;\n\n    if (useColors) {\n      process.stdout.write(`${ANSI_GREEN}ℹ️  INFO: ${header}${ANSI_RESET}\\n`);\n    } else {\n      logInfo(header);\n    }\n\n    // Print each line with indentation\n    const lines = actualOutput.trimEnd().split('\\n');\n    for (const line of lines) {\n      if (useColors) {\n        process.stdout.write(`${ANSI_GREEN}ℹ️  INFO:    ${line}${ANSI_RESET}\\n`);\n      } else {\n        logInfo(`   ${line}`);\n      }\n    }\n\n    // Also write to file if enabled\n    if (fileLoggingEnabled && logFilePath) {\n      writeToFile(header);\n      for (const line of lines) {\n        writeToFile(`   ${line}`);\n      }\n    }\n  } catch (error) {\n    // If parsing fails, just return without logging\n    return;\n  }\n}\n\n/**\n * Log operation start\n * @param operation Name of the operation\n * @param details Optional operation details\n */\nexport function logOperationStart(operation: string, details?: string): void {\n  if (!shouldLog('INFO')) return;\n  const message = `🚀 Starting: ${operation}`;\n  logInfo(message);\n\n  if (details && shouldLog('DEBUG')) {\n    logDebug(`📋 Details: ${details}`);\n  }\n}\n\n/**\n * Log operation success\n * @param operation Name of the operation\n * @param result Optional operation result\n */\nexport function logOperationSuccess(operation: string, result?: string): void{\n  if (!shouldLog('INFO')) return;\n  const message = `✅ Completed: ${operation}`;\n  logInfo(message);\n\n  if (result && shouldLog('DEBUG')) {\n    logDebug(`📊 Result: ${result}`);\n  }\n}\n\n/**\n * Log operation error\n * @param operation Name of the operation that failed\n * @param error Error message or error object\n * @param includeStackTrace Whether to include stack trace\n */\nexport function logOperationError(\n  operation: string,\n  error: string | Error,\n  includeStackTrace = false\n): void {\n  if (!shouldLog('ERROR')) return;\n  const message = `❌ Failed: ${operation}`;\n\n  if (typeof error === 'string') {\n    logError(message, new Error(error));\n  } else if (error instanceof Error) {\n    if (includeStackTrace) {\n      logError(message, error);\n    } else {\n      logError(message, error.message);\n    }\n  } else {\n    logError(message, String(error));\n  }\n}\n\n/**\n * Log an info level message with custom color\n * @param message The message to log\n * @param color ANSI color code (defaults to red)\n */\nexport function logInfoWithColor(message: string, color: string = ANSI_RED): void {\n  if (!shouldLog('INFO')) return;\n\n  const emoji = 'ℹ️  INFO';\n  const fullMessage = `${emoji}: ${message}`;\n\n  if (useColors) {\n    if (consoleLoggingEnabled) {\n      process.stdout.write(`${color}${fullMessage}${ANSI_RESET}\\n`);\n    }\n  } else {\n    if (consoleLoggingEnabled) {\n      process.stdout.write(fullMessage + \"\\n\");\n    }\n  }\n\n  // Write to file without colors\n  writeToFile(fullMessage);\n}\n","/**\n * Base interface for API responses\n */\nexport interface ApiResponse {\n  /** Optional request identifier for tracking API calls */\n  requestId?: string;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n  /** Optional status code if the operation failed */\n  success?: boolean;\n}\n\n/**\n * Generic interface for API responses that include data payload\n * @template T The type of the data being returned\n */\nexport interface ApiResponseWithData<T> extends ApiResponse {\n  /** The actual data payload returned by the API */\n  session?: T;\n  data?: T;\n}\n\n/**\n * Interface for delete operation responses\n */\nexport interface DeleteResult extends ApiResponse {\n  /** Whether the delete operation was successful */\n  success: boolean;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for GetSession data\n */\nexport interface GetSessionData {\n  /** Application instance ID */\n  appInstanceId: string;\n  /** Resource ID */\n  resourceId: string;\n  /** Session ID */\n  sessionId: string;\n  /** Success status */\n  success: boolean;\n  /** HTTP port for VPC sessions */\n  httpPort: string;\n  /** Network interface IP for VPC sessions */\n  networkInterfaceIp: string;\n  /** Token for VPC sessions */\n  token: string;\n  /** Whether this session uses VPC resources */\n  vpcResource: boolean;\n  /** Resource URL for accessing the session */\n  resourceUrl: string;\n}\n\n/**\n * Interface for GetSession operation responses\n */\nexport interface GetSessionResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** HTTP status code */\n  httpStatusCode: number;\n  /** Response code */\n  code: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Session data */\n  data?: GetSessionData;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for session creation operation responses\n * Corresponds to Python's SessionResult type\n */\nexport interface SessionResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the session creation was successful */\n  success: boolean;\n  /** The created session object (only present if successful) */\n  session?: any; // Will be Session type, avoiding circular import\n  /** Error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for operation results\n * Corresponds to Python's OperationResult type\n */\nexport interface OperationResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Optional data payload */\n  data?: any;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for session info operation responses\n * Corresponds to Go's InfoResult type\n */\nexport interface InfoResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Session information object */\n  info?: any; // Will be SessionInfo type, avoiding circular import\n}\n\n/**\n * Interface for label operation responses\n * Corresponds to Go's LabelResult type\n */\nexport interface LabelResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Labels string (JSON format) */\n  labels: string;\n}\n\n/**\n * Interface for link operation responses\n * Corresponds to Go's LinkResult type\n */\nexport interface LinkResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Link string */\n  link: string;\n}\n\n/**\n * Interface for process list operation responses\n * Corresponds to Python's ProcessListResult type\n */\nexport interface ProcessListResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** The list of process objects */\n  data: any[]; // Will be Process[] type, avoiding circular import\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for installed app list operation responses\n * Corresponds to Python's InstalledAppListResult type\n */\nexport interface InstalledAppListResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** The list of installed app objects */\n  data: any[]; // Will be InstalledApp[] type, avoiding circular import\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for application operation responses\n * Corresponds to Python's AppOperationResult type\n */\nexport interface AppOperationResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for application info operation responses\n * Corresponds to Python's AppInfoResult type\n */\nexport interface AppInfoResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Application information */\n  appInfo: Record<string, any>;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for application list operation responses\n * Corresponds to Python's AppListResult type\n */\nexport interface AppListResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** List of applications */\n  apps: Record<string, any>[];\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for application installation responses\n * Corresponds to Python's AppInstallResult type\n */\nexport interface AppInstallResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the installation was successful */\n  success: boolean;\n  /** Result description or error message */\n  message: string;\n}\n\n/**\n * Interface for command execution operation responses\n * Corresponds to Python's CommandResult type\n */\nexport interface CommandResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the command execution was successful */\n  success: boolean;\n  /** The command output */\n  output: string;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for code execution operation responses\n * Corresponds to Python's CodeExecutionResult type\n */\nexport interface CodeExecutionResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the code execution was successful */\n  success: boolean;\n  /** The execution result */\n  result: string;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for boolean operation responses\n * Corresponds to Python's BoolResult type\n */\nexport interface BoolResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Boolean data result */\n  data?: boolean;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for file info operation responses\n * Corresponds to Python's FileInfoResult type\n */\nexport interface FileInfoResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** File information object */\n  fileInfo?: Record<string, any>;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for directory list operation responses\n * Corresponds to Python's DirectoryListResult type\n */\nexport interface DirectoryListResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Directory entries */\n  entries: Record<string, any>[];\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for file content operation responses\n * Corresponds to Python's FileContentResult type\n */\nexport interface FileContentResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** File content */\n  content: string;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for multiple file content operation responses\n * Corresponds to Python's MultipleFileContentResult type\n */\nexport interface MultipleFileContentResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Multiple file contents */\n  contents: Record<string, string>;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for file search operation responses\n * Corresponds to Python's FileSearchResult type\n */\nexport interface FileSearchResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Matching file paths */\n  matches: string[];\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for OSS client creation operation responses\n * Corresponds to Python's OSSClientResult type\n */\nexport interface OSSClientResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** OSS client configuration */\n  clientConfig: Record<string, any>;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for OSS upload operation responses\n * Corresponds to Python's OSSUploadResult type\n */\nexport interface OSSUploadResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Result of the upload operation */\n  content: string;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for OSS download operation responses\n * Corresponds to Python's OSSDownloadResult type\n */\nexport interface OSSDownloadResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Result of the download operation */\n  content: string;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for UI element list operation responses\n * Corresponds to Python's UIElementListResult type\n */\nexport interface UIElementListResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** UI elements */\n  elements: Record<string, any>[];\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for window list operation responses\n * Corresponds to Python's WindowListResult type\n */\nexport interface WindowListResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** List of windows */\n  windows: any[]; // Will be Window[] type, avoiding circular import\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for window info operation responses\n * Corresponds to Python's WindowInfoResult type\n */\nexport interface WindowInfoResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Window object */\n  window?: any; // Will be Window type, avoiding circular import\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for context operation responses\n * Corresponds to Python's ContextResult type\n */\nexport interface ContextResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** The context ID */\n  contextId: string;\n  /** The context object (only present if successful) */\n  context?: any; // Will be Context type, avoiding circular import\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Interface for context list operation responses\n * Corresponds to Python's ContextListResult type\n */\nexport interface ContextListResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** List of contexts */\n  contexts: any[]; // Will be Context[] type, avoiding circular import\n  /** Token for the next page of results */\n  nextToken?: string;\n  /** Maximum number of results per page */\n  maxResults?: number;\n  /** Total number of contexts available */\n  totalCount?: number;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Result of a presigned URL request\n */\nexport interface FileUrlResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** The presigned URL */\n  url: string;\n  /** Optional expire time (epoch seconds) */\n  expireTime?: number;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Represents a file item in a context\n */\nexport interface ContextFileEntry {\n  fileId?: string;\n  fileName?: string;\n  filePath: string;\n  fileType?: string;\n  gmtCreate?: string;\n  gmtModified?: string;\n  size?: number;\n  status?: string;\n}\n\n/**\n * Result of context file listing\n */\nexport interface ContextFileListResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** File entries under a folder */\n  entries: ContextFileEntry[];\n  /** Optional total count returned by backend */\n  count?: number;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n\n/**\n * Helper function to extract request ID from API responses\n */\nexport function extractRequestId(response: any): string | undefined {\n  if (!response) return undefined;\n\n  // If response is a string (like body?.requestId), return it directly\n  if (typeof response === \"string\" && response.length > 0) {\n    return response;\n  }\n\n  // Check for requestId in response.body first\n  if (response.body && response.body.requestId) {\n    return response.body.requestId;\n  }\n  // Check for requestId directly on response\n  if (response.requestId) {\n    return response.requestId;\n  }\n\n  return undefined;\n}\n\n/**\n * Result of context clear operations, including the real-time status.\n * Corresponds to Python's ClearContextResult type\n */\nexport interface ClearContextResult extends ApiResponse {\n  /** Request identifier for tracking API calls */\n  requestId: string;\n  /** Whether the operation was successful */\n  success: boolean;\n  /** Current status of the clearing task. This corresponds to the\n      context's state field. Possible values:\n      - \"clearing\": Context data is being cleared (in progress)\n      - \"available\": Clearing completed successfully\n      - Other values may indicate the context state after clearing */\n  status?: string;\n  /** The unique identifier of the context being cleared */\n  contextId?: string;\n  /** Optional error message if the operation failed */\n  errorMessage?: string;\n}\n","// UploadStrategy defines the upload strategy for context synchronization\nexport enum UploadStrategy {\n  UploadBeforeResourceRelease = \"UploadBeforeResourceRelease\",\n}\n\n// DownloadStrategy defines the download strategy for context synchronization\nexport enum DownloadStrategy {\n  DownloadAsync = \"DownloadAsync\",\n}\n\n// UploadMode defines the upload mode for context synchronization\nexport enum UploadMode {\n  File = \"File\",\n  Archive = \"Archive\",\n}\n\n// Lifecycle defines the lifecycle options for recycle policy\nexport enum Lifecycle {\n  Lifecycle_1Day = \"Lifecycle_1Day\",\n  Lifecycle_3Days = \"Lifecycle_3Days\", \n  Lifecycle_5Days = \"Lifecycle_5Days\",\n  Lifecycle_10Days = \"Lifecycle_10Days\",\n  Lifecycle_15Days = \"Lifecycle_15Days\",\n  Lifecycle_30Days = \"Lifecycle_30Days\",\n  Lifecycle_90Days = \"Lifecycle_90Days\",\n  Lifecycle_180Days = \"Lifecycle_180Days\",\n  Lifecycle_360Days = \"Lifecycle_360Days\",\n  Lifecycle_Forever = \"Lifecycle_Forever\",\n}\n\n// UploadPolicy defines the upload policy for context synchronization\nexport interface UploadPolicy {\n  autoUpload: boolean;\n  uploadStrategy: UploadStrategy;\n  uploadMode: UploadMode;\n}\n\n// DownloadPolicy defines the download policy for context synchronization\nexport interface DownloadPolicy {\n  autoDownload: boolean;\n  downloadStrategy: DownloadStrategy;\n}\n\n// DeletePolicy defines the delete policy for context synchronization\nexport interface DeletePolicy {\n  syncLocalFile: boolean;\n}\n\n// ExtractPolicy defines the extract policy for context synchronization\nexport interface ExtractPolicy {\n  extract: boolean;\n  deleteSrcFile: boolean;\n  extractToCurrentFolder: boolean;\n}\n\n// RecyclePolicy defines the recycle policy for context synchronization\nexport interface RecyclePolicy {\n  lifecycle: Lifecycle;\n  paths: string[];\n}\n\n// ExtractPolicyClass provides a class-based implementation with default values\nexport class ExtractPolicyClass implements ExtractPolicy {\n  extract = true;\n  deleteSrcFile = true;\n  extractToCurrentFolder = false;\n\n  constructor(extract = true, deleteSrcFile = true, extractToCurrentFolder = false) {\n    this.extract = extract;\n    this.deleteSrcFile = deleteSrcFile;\n    this.extractToCurrentFolder = extractToCurrentFolder;\n  }\n\n  /**\n   * Creates a new extract policy with default values\n   */\n  static default(): ExtractPolicyClass {\n    return new ExtractPolicyClass();\n  }\n\n  /**\n   * Converts to plain object for JSON serialization\n   */\n  toDict(): Record<string, any> {\n    return {\n      extract: this.extract,\n      deleteSrcFile: this.deleteSrcFile,\n      extractToCurrentFolder: this.extractToCurrentFolder\n    };\n  }\n}\n\n// WhiteList defines the white list configuration\nexport interface WhiteList {\n  path: string;\n  excludePaths?: string[];\n}\n\nexport class WhiteListValidator {\n  private static containsWildcard(path: string): boolean {\n    return /[*?\\[\\]]/.test(path);\n  }\n\n  static validate(whitelist: WhiteList): void {\n    if (this.containsWildcard(whitelist.path)) {\n      throw new Error(\n        `Wildcard patterns are not supported in path. Got: ${whitelist.path}. ` +\n        \"Please use exact directory paths instead.\"\n      );\n    }\n\n    if (whitelist.excludePaths) {\n      for (const excludePath of whitelist.excludePaths) {\n        if (this.containsWildcard(excludePath)) {\n          throw new Error(\n            `Wildcard patterns are not supported in exclude_paths. Got: ${excludePath}. ` +\n            \"Please use exact directory paths instead.\"\n          );\n        }\n      }\n    }\n  }\n}\n\n// BWList defines the black and white list configuration\nexport interface BWList {\n  whiteLists?: WhiteList[];\n}\n\n// MappingPolicy defines the mapping policy for cross-platform context synchronization\nexport interface MappingPolicy {\n  path: string;\n}\n\n// SyncPolicy defines the synchronization policy\nexport interface SyncPolicy {\n  uploadPolicy?: UploadPolicy;\n  downloadPolicy?: DownloadPolicy;\n  deletePolicy?: DeletePolicy;\n  extractPolicy?: ExtractPolicy;\n  recyclePolicy?: RecyclePolicy;\n  bwList?: BWList;\n  mappingPolicy?: MappingPolicy;\n}\n\n// SyncPolicyImpl provides a class-based implementation with default value handling\nexport class SyncPolicyImpl implements SyncPolicy {\n  uploadPolicy?: UploadPolicy;\n  downloadPolicy?: DownloadPolicy;\n  deletePolicy?: DeletePolicy;\n  extractPolicy?: ExtractPolicy;\n  recyclePolicy?: RecyclePolicy;\n  bwList?: BWList;\n\n  constructor(policy?: Partial<SyncPolicy>) {\n    if (policy) {\n      this.uploadPolicy = policy.uploadPolicy;\n      this.downloadPolicy = policy.downloadPolicy;\n      this.deletePolicy = policy.deletePolicy;\n      this.extractPolicy = policy.extractPolicy;\n      this.recyclePolicy = policy.recyclePolicy;\n      this.bwList = policy.bwList;\n    }\n    this.ensureDefaults();\n  }\n\n  private ensureDefaults(): void {\n    if (!this.uploadPolicy) {\n      this.uploadPolicy = newUploadPolicy();\n    }\n    if (!this.downloadPolicy) {\n      this.downloadPolicy = newDownloadPolicy();\n    }\n    if (!this.deletePolicy) {\n      this.deletePolicy = newDeletePolicy();\n    }\n    if (!this.extractPolicy) {\n      this.extractPolicy = newExtractPolicy();\n    }\n    if (!this.recyclePolicy) {\n      this.recyclePolicy = newRecyclePolicy();\n    }\n    if (!this.bwList) {\n      this.bwList = {\n        whiteLists: [\n          {\n            path: \"\",\n            excludePaths: [],\n          },\n        ],\n      };\n    }\n  }\n\n  toJSON(): SyncPolicy {\n    this.ensureDefaults();\n    return {\n      uploadPolicy: this.uploadPolicy,\n      downloadPolicy: this.downloadPolicy,\n      deletePolicy: this.deletePolicy,\n      extractPolicy: this.extractPolicy,\n      recyclePolicy: this.recyclePolicy,\n      bwList: this.bwList,\n    };\n  }\n}\n\n// ContextSync defines the context synchronization configuration\nexport class ContextSync {\n  contextId: string;\n  path: string;\n  policy?: SyncPolicy;\n\n  constructor(contextId: string, path: string, policy?: SyncPolicy) {\n    if (policy) {\n      validateSyncPolicy(policy);\n    }\n    this.contextId = contextId;\n    this.path = path;\n    this.policy = policy;\n  }\n\n  // WithPolicy sets the policy and returns the context sync for chaining\n  withPolicy(policy: SyncPolicy): ContextSync {\n    validateSyncPolicy(policy);\n    this.policy = policy;\n    return this;\n  }\n}\n\n// NewUploadPolicy creates a new upload policy with default values\nexport function newUploadPolicy(): UploadPolicy {\n  return {\n    autoUpload: true,\n    uploadStrategy: UploadStrategy.UploadBeforeResourceRelease,\n    uploadMode: UploadMode.File,\n  };\n}\n\n// NewDownloadPolicy creates a new download policy with default values\nexport function newDownloadPolicy(): DownloadPolicy {\n  return {\n    autoDownload: true,\n    downloadStrategy: DownloadStrategy.DownloadAsync,\n  };\n}\n\n// NewDeletePolicy creates a new delete policy with default values\nexport function newDeletePolicy(): DeletePolicy {\n  return {\n    syncLocalFile: true,\n  };\n}\n\n// NewExtractPolicy creates a new extract policy with default values\nexport function newExtractPolicy(): ExtractPolicy {\n  return {\n    extract: true,\n    deleteSrcFile: true,\n    extractToCurrentFolder: false,\n  };\n}\n\n// NewRecyclePolicy creates a new recycle policy with default values\nexport function newRecyclePolicy(): RecyclePolicy {\n  return {\n    lifecycle: Lifecycle.Lifecycle_Forever,\n    paths: [\"\"],\n  };\n}\n\n// NewMappingPolicy creates a new mapping policy with default values\nexport function newMappingPolicy(): MappingPolicy {\n  return {\n    path: \"\",\n  };\n}\n\n// NewSyncPolicy creates a new sync policy with default values\nexport function newSyncPolicy(): SyncPolicy {\n  return {\n    uploadPolicy: newUploadPolicy(),\n    downloadPolicy: newDownloadPolicy(),\n    deletePolicy: newDeletePolicy(),\n    extractPolicy: newExtractPolicy(),\n    recyclePolicy: newRecyclePolicy(),\n    bwList: {\n      whiteLists: [\n        {\n          path: \"\",\n          excludePaths: [],\n        },\n      ],\n    },\n  };\n}\n\n// isValidLifecycle checks if the given lifecycle value is valid\nfunction isValidLifecycle(lifecycle: Lifecycle): boolean {\n  return Object.values(Lifecycle).includes(lifecycle);\n}\n\n// isValidUploadMode checks if the given uploadMode value is valid\nfunction isValidUploadMode(uploadMode: UploadMode): boolean {\n  return uploadMode === UploadMode.File || uploadMode === UploadMode.Archive;\n}\n\nfunction validateSyncPolicy(policy?: SyncPolicy): void {\n  if (policy?.bwList?.whiteLists) {\n    for (const whitelist of policy.bwList.whiteLists) {\n      WhiteListValidator.validate(whitelist);\n    }\n  }\n\n  if (policy?.uploadPolicy?.uploadMode) {\n    // Validate uploadMode value\n    if (!isValidUploadMode(policy.uploadPolicy.uploadMode)) {\n      throw new Error(\n        `Invalid uploadMode value: ${policy.uploadPolicy.uploadMode}. ` +\n        `Valid values are: ${UploadMode.File}, ${UploadMode.Archive}`\n      );\n    }\n  }\n\n  if (policy?.recyclePolicy) {\n    // Validate lifecycle value\n    if (!isValidLifecycle(policy.recyclePolicy.lifecycle)) {\n      const validValues = Object.values(Lifecycle).join(', ');\n      throw new Error(\n        `Invalid lifecycle value: ${policy.recyclePolicy.lifecycle}. ` +\n        `Valid values are: ${validValues}`\n      );\n    }\n\n    // Validate paths don't contain wildcard patterns\n    if (policy.recyclePolicy.paths) {\n      for (const path of policy.recyclePolicy.paths) {\n        if (path && path.trim() !== \"\") {\n          // Create a temporary WhiteList object for validation\n          const tempWhiteList: WhiteList = { path: path };\n          WhiteListValidator.validate(tempWhiteList);\n        }\n      }\n    }\n  }\n}\n\n// NewSyncPolicyWithDefaults creates a new sync policy with partial parameters and fills defaults\nexport function newSyncPolicyWithDefaults(policy?: Partial<SyncPolicy>): SyncPolicy {\n  return new SyncPolicyImpl(policy).toJSON();\n}\n\n// NewContextSync creates a new context sync configuration\nexport function newContextSync(contextId: string, path: string, policy?: SyncPolicy): ContextSync {\n  if (policy) {\n    validateSyncPolicy(policy);\n  }\n  return new ContextSync(contextId, path, policy);\n}","import { AgentBay } from \"./agent-bay\";\nimport { Agent } from \"./agent/agent\";\nimport { Client } from \"./api/client\";\nimport {\n  CallMcpToolRequest,\n  GetLabelRequest,\n  GetLinkRequest,\n  GetMcpResourceRequest,\n  ListMcpToolsRequest,\n  ReleaseMcpSessionRequest,\n  SetLabelRequest,\n} from \"./api/models/model\";\nimport { Browser } from \"./browser\";\nimport { Code } from \"./code\";\nimport { Command } from \"./command\";\nimport { Computer } from \"./computer\";\nimport {\n  ContextManager,\n  ContextSyncResult,\n  newContextManager,\n} from \"./context-manager\";\nimport { FileSystem } from \"./filesystem\";\nimport { Mobile } from \"./mobile\";\nimport { Oss } from \"./oss\";\nimport {\n  DeleteResult,\n  extractRequestId,\n  OperationResult,\n} from \"./types/api-response\";\nimport {\n  log,\n  logError,\n  logInfo,\n  logDebug,\n  logAPICall,\n  logAPIResponseWithDetails,\n  logCodeExecutionOutput,\n  logInfoWithColor,\n  setRequestId,\n  getRequestId,\n} from \"./utils/logger\";\n\n/**\n * Represents an MCP tool with complete information.\n */\nexport interface McpTool {\n  name: string;\n  description: string;\n  inputSchema: Record<string, any>;\n  server: string;\n  tool: string;\n}\n\n/**\n * Result containing MCP tools list and request ID.\n */\nexport interface McpToolsResult extends OperationResult {\n  tools: McpTool[];\n}\n\n/**\n * Result containing MCP resource information and request ID.\n */\nexport interface McpResourceResult extends OperationResult {\n  uri: string;\n  name: string;\n  description: string;\n  mimeType: string;\n}\n\n/**\n * Result containing MCP resource content and request ID.\n */\nexport interface McpResourceContentResult extends OperationResult {\n  contents: Array<{\n    uri: string;\n    mimeType: string;\n    text?: string;\n    blob?: string;\n  }>;\n}\n\n/**\n * Contains information about a session.\n */\nexport interface SessionInfo {\n  sessionId: string;\n  resourceUrl: string;\n  appId?: string;\n  authCode?: string;\n  connectionProperties?: string;\n  resourceId?: string;\n  resourceType?: string;\n  ticket?: string;\n}\n\n/**\n * SessionInfo class to match Python version structure\n */\nexport class SessionInfoClass {\n  sessionId: string;\n  resourceUrl: string;\n  appId: string;\n  authCode: string;\n  connectionProperties: string;\n  resourceId: string;\n  resourceType: string;\n  ticket: string;\n\n  constructor(\n    sessionId = \"\",\n    resourceUrl = \"\",\n    appId = \"\",\n    authCode = \"\",\n    connectionProperties = \"\",\n    resourceId = \"\",\n    resourceType = \"\",\n    ticket = \"\"\n  ) {\n    this.sessionId = sessionId;\n    this.resourceUrl = resourceUrl;\n    this.appId = appId;\n    this.authCode = authCode;\n    this.connectionProperties = connectionProperties;\n    this.resourceId = resourceId;\n    this.resourceType = resourceType;\n    this.ticket = ticket;\n  }\n}\n\n/**\n * Represents a session in the AgentBay cloud environment.\n */\nexport class Session {\n  private agentBay: AgentBay;\n  public sessionId: string;\n\n  // File transfer context ID\n  public fileTransferContextId: string | null = null;\n\n  // Browser recording context ID\n  public recordContextId: string | null = null;\n\n  // VPC-related information\n  public isVpc = false; // Whether this session uses VPC resources\n  public networkInterfaceIp = \"\"; // Network interface IP for VPC sessions\n  public httpPort = \"\"; // HTTP port for VPC sessions\n  public token = \"\"; // Token for VPC sessions\n\n  // Resource URL for accessing the session\n  public resourceUrl = \"\";\n\n  // Recording functionality\n  public enableBrowserReplay = false; // Whether browser recording is enabled for this session\n\n  // File, command, code, and oss handlers (matching Python naming)\n  public fileSystem: FileSystem; // file_system in Python\n  public command: Command;\n  public code: Code;\n  public oss: Oss;\n\n  // Computer and Mobile automation (new modules)\n  public computer: Computer;\n  public mobile: Mobile;\n\n  // Agent for task execution\n  public agent: Agent;\n\n  // Browser for web automation\n  public browser: Browser;\n\n  // Context management (matching Go version)\n  public context: ContextManager;\n\n  // MCP tools available for this session\n  public mcpTools: McpTool[] = [];\n\n  /**\n   * Initialize a Session object.\n   *\n   * @param agentBay - The AgentBay instance that created this session.\n   * @param sessionId - The ID of this session.\n   */\n  constructor(agentBay: AgentBay, sessionId: string) {\n    this.agentBay = agentBay;\n    this.sessionId = sessionId;\n\n    // Initialize file system, command and code handlers (matching Python naming)\n    this.fileSystem = new FileSystem(this);\n    this.command = new Command(this);\n    this.code = new Code(this);\n    this.oss = new Oss(this);\n\n    // Initialize Computer and Mobile modules\n    this.computer = new Computer(this);\n    this.mobile = new Mobile(this);\n\n    // Initialize Agent\n    this.agent = new Agent(this);\n\n    // Initialize Browser\n    this.browser = new Browser(this);\n\n    // Initialize context manager (matching Go version)\n    this.context = newContextManager(this);\n  }\n\n  /**\n   * Return the AgentBay instance that created this session.\n   *\n   * @returns The AgentBay client instance\n   * @internal\n   */\n  getAgentBay(): AgentBay {\n    return this.agentBay;\n  }\n\n  /**\n   * Return the API key for this session.\n   * Note: This method is used internally by SDK modules (Code, Computer, Mobile, Agent).\n   *\n   * @returns The API key string\n   * @internal\n   */\n  getAPIKey(): string {\n    return this.agentBay.getAPIKey();\n  }\n\n  /**\n   * Return the HTTP client for this session.\n   *\n   * @returns The Client instance used for API communication\n   * @internal\n   */\n  getClient(): Client {\n    return this.agentBay.getClient();\n  }\n\n  /**\n   * Return the session_id for this session.\n   * Note: This method is used internally by SDK modules. Users should prefer using the `sessionId` property.\n   *\n   * @returns The session ID string\n   * @internal\n   */\n  getSessionId(): string {\n    return this.sessionId;\n  }\n\n  /**\n   * Return whether this session uses VPC resources.\n   *\n   * @returns boolean indicating if VPC is enabled for this session\n   * @internal\n   */\n  private isVpcEnabled(): boolean {\n    return this.isVpc;\n  }\n\n  /**\n   * Return the network interface IP for VPC sessions.\n   *\n   * @returns The network interface IP string for VPC sessions\n   * @internal\n   */\n  private getNetworkInterfaceIp(): string {\n    return this.networkInterfaceIp;\n  }\n\n  /**\n   * Return the HTTP port for VPC sessions.\n   *\n   * @returns The HTTP port string for VPC sessions\n   * @internal\n   */\n  private getHttpPort(): string {\n    return this.httpPort;\n  }\n\n  /**\n   * Return the token for VPC sessions.\n   *\n   * @returns The token string for VPC sessions\n   * @internal\n   */\n  private getToken(): string {\n    return this.token;\n  }\n\n  /**\n   * Find the server that provides the given tool.\n   *\n   * @param toolName - Name of the tool to find\n   * @returns The server name that provides the tool, or empty string if not found\n   * @internal\n   */\n  private findServerForTool(toolName: string): string {\n    for (const tool of this.mcpTools) {\n      if (tool.name === toolName) {\n        return tool.server;\n      }\n    }\n    return \"\";\n  }\n\n  /**\n   * Delete this session.\n   *\n   * @param syncContext - Whether to sync context data (trigger file uploads) before deleting the session. Defaults to false.\n   * @returns DeleteResult indicating success or failure and request ID\n   */\n  /**\n   * Deletes the session and releases all associated resources.\n   *\n   * @param syncContext - Whether to synchronize context data before deletion.\n   *                      If true, uploads all context data to OSS.\n   *                      If false but browser replay is enabled, syncs only the recording context.\n   *                      Defaults to false.\n   *\n   * @returns Promise resolving to DeleteResult containing:\n   *          - success: Whether deletion succeeded\n   *          - requestId: Unique identifier for this API request\n   *          - errorMessage: Error description if deletion failed\n   *\n   * @throws Error if the API call fails or network issues occur.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   await result.session.fileSystem.writeFile('/tmp/data.txt', 'data');\n   *   const deleteResult = await result.session.delete(true);\n   *   console.log('Session deleted:', deleteResult.success);\n   * }\n   * ```\n   *\n   * @remarks\n   * **Behavior:**\n   * - If `syncContext=true`: Uploads all context data to OSS before deletion\n   * - If `syncContext=false` but browser replay enabled: Syncs only recording context\n   * - If `syncContext=false` and no browser replay: Deletes immediately without sync\n   * - Continues with deletion even if context sync fails\n   * - Releases all associated resources (browser, computer, mobile, etc.)\n   *\n   * **Best Practices:**\n   * - Use `syncContext=true` when you need to preserve context data for later retrieval\n   * - For temporary sessions, use `syncContext=false` for faster cleanup\n   * - Always call `delete()` when done to avoid resource leaks\n   * - Handle deletion errors gracefully in production code\n   *\n   * @see {@link info}, {@link ContextManager.sync}\n   */\n  async delete(syncContext = false): Promise<DeleteResult> {\n    try {\n      // Determine sync behavior based on enableBrowserReplay and syncContext\n      let shouldSync = false;\n      let syncContextId: string | null = null;\n\n      if (syncContext) {\n        // User explicitly requested sync - sync all contexts\n        shouldSync = true;\n        logInfo(\"🔄 User requested context synchronization\");\n      } else if (this.enableBrowserReplay && this.recordContextId) {\n        // Browser replay enabled but no explicit sync - sync only browser recording context\n        shouldSync = true;\n        syncContextId = this.recordContextId;\n        logInfo(\n          `🎥 Browser replay enabled - syncing recording context: ${syncContextId}`\n        );\n      }\n\n      // If syncContext is true, trigger file uploads first\n      if (shouldSync) {\n        logDebug(\n          \"Triggering context synchronization before session deletion...\"\n        );\n\n        // Use the new sync method without callback (sync mode)\n        const syncStartTime = Date.now();\n\n        try {\n          let syncResult: ContextSyncResult;\n          if (syncContextId) {\n            // Sync specific context (browser recording)\n            syncResult = await this.context.sync(syncContextId);\n            logInfo(`🎥 Synced browser recording context: ${syncContextId}`);\n          } else {\n            // Sync all contexts\n            syncResult = await this.context.sync();\n            logInfo(\"🔄 Synced all contexts\");\n          }\n\n          const syncDuration = Date.now() - syncStartTime;\n\n          if (syncResult.success) {\n            logInfo(`Context sync completed in ${syncDuration}ms`);\n          } else {\n            logInfo(\n              `Context sync completed with failures after ${syncDuration}ms`\n            );\n          }\n        } catch (error) {\n          const syncDuration = Date.now() - syncStartTime;\n          logError(\n            `Failed to trigger context sync after ${syncDuration}ms:`,\n            error\n          );\n          // Continue with deletion even if sync fails\n        }\n      }\n\n      // Proceed with session deletion\n      const request = new ReleaseMcpSessionRequest({\n        authorization: `Bearer ${this.getAPIKey()}`,\n        sessionId: this.sessionId,\n      });\n\n      const response = await this.getClient().releaseMcpSession(request);\n      logDebug(\n        `Response from release_mcp_session: ${JSON.stringify(response)}`\n      );\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Check if the response is success (matching Python logic)\n      const responseBody = response.body;\n      const success = responseBody?.success !== false; // Note: capital S to match Python\n\n      if (!success) {\n        const errorMessage = `[${responseBody?.code || \"Unknown\"}] ${\n          responseBody?.message || \"Failed to delete session\"\n        }`;\n        return {\n          requestId,\n          success: false,\n          errorMessage,\n        };\n      }\n\n      // Return success result with request ID\n      return {\n        requestId,\n        success: true,\n      };\n    } catch (error) {\n      logError(\"Error calling release_mcp_session:\", error);\n      // In case of error, return failure result with error message (matching Python)\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to delete session ${this.sessionId}: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Validates labels parameter for label operations.\n   *\n   * @param labels - The labels to validate\n   * @returns null if validation passes, or OperationResult with error if validation fails\n   */\n  private validateLabels(\n    labels: Record<string, string>\n  ): OperationResult | null {\n    // Check if labels is null, undefined, or invalid type\n    if (!labels || typeof labels !== \"object\") {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage:\n          \"Labels cannot be null, undefined, or invalid type. Please provide a valid labels object.\",\n      };\n    }\n\n    // Check if labels is an array or other non-plain object\n    if (Array.isArray(labels)) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage:\n          \"Labels cannot be an array. Please provide a valid labels object.\",\n      };\n    }\n\n    // Check if labels is a Date, RegExp, or other built-in object types\n    if (\n      labels instanceof Date ||\n      labels instanceof RegExp ||\n      labels instanceof Error ||\n      labels instanceof Map ||\n      labels instanceof Set ||\n      labels instanceof WeakMap ||\n      labels instanceof WeakSet ||\n      labels instanceof Promise\n    ) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage:\n          \"Labels must be a plain object. Built-in object types are not allowed.\",\n      };\n    }\n\n    // Check if labels object is empty\n    if (Object.keys(labels).length === 0) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage:\n          \"Labels cannot be empty. Please provide at least one label.\",\n      };\n    }\n\n    for (const [key, value] of Object.entries(labels)) {\n      // Check key validity\n      if (!key || key.trim() === \"\") {\n        return {\n          requestId: \"\",\n          success: false,\n          errorMessage: \"Label keys cannot be empty Please provide valid keys.\",\n        };\n      }\n\n      // Check value is not null or undefined\n      if (!value || value.trim() === \"\") {\n        return {\n          requestId: \"\",\n          success: false,\n          errorMessage:\n            \"Label values cannot be empty Please provide valid values.\",\n        };\n      }\n    }\n\n    // Validation passed\n    return null;\n  }\n\n  /**\n   * Sets the labels for this session.\n   *\n   * @param labels - The labels to set for the session.\n   * @returns OperationResult indicating success or failure with request ID\n   * @throws Error if the operation fails (matching Python SessionError)\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const setResult = await result.session.setLabels({ project: 'demo', env: 'test' });\n   *   console.log('Labels set:', setResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async setLabels(labels: Record<string, string>): Promise<OperationResult> {\n    try {\n      // Validate labels using the extracted validation function\n      const validationResult = this.validateLabels(labels);\n      if (validationResult !== null) {\n        return validationResult;\n      }\n\n      // Convert labels to JSON string\n      const labelsJSON = JSON.stringify(labels);\n\n      const request = new SetLabelRequest({\n        authorization: `Bearer ${this.getAPIKey()}`,\n        sessionId: this.sessionId,\n        labels: labelsJSON,\n      });\n\n      const response = await this.getClient().setLabel(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      return {\n        requestId,\n        success: true,\n      };\n    } catch (error) {\n      logError(\"Error calling set_label:\", error);\n      throw new Error(\n        `Failed to set labels for session ${this.sessionId}: ${error}`\n      );\n    }\n  }\n\n  /**\n   * Gets the labels for this session.\n   *\n   * @returns OperationResult containing the labels as data and request ID\n   * @throws Error if the operation fails (matching Python SessionError)\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   await result.session.setLabels({ project: 'demo', env: 'test' });\n   *   const getResult = await result.session.getLabels();\n   *   console.log('Labels:', JSON.stringify(getResult.data));\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async getLabels(): Promise<OperationResult> {\n    try {\n      const request = new GetLabelRequest({\n        authorization: `Bearer ${this.getAPIKey()}`,\n        sessionId: this.sessionId,\n      });\n\n      const response = await this.getClient().getLabel(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Extract labels from response (matching Python structure)\n      const responseBody = response?.body;\n      const data = responseBody?.data; // Capital D to match Python\n      const labelsJSON = data?.labels; // Capital L to match Python\n\n      let labels = {};\n      if (labelsJSON) {\n        labels = JSON.parse(labelsJSON);\n      }\n\n      return {\n        requestId,\n        success: true,\n        data: labels,\n      };\n    } catch (error) {\n      logError(\"Error calling get_label:\", error);\n      throw new Error(\n        `Failed to get labels for session ${this.sessionId}: ${error}`\n      );\n    }\n  }\n\n  /**\n   * Retrieves detailed information about the current session.\n   *\n   * @returns Promise resolving to OperationResult containing:\n   *          - success: Whether the operation succeeded (always true if no exception)\n   *          - data: SessionInfo object with the following fields:\n   *            - sessionId (string): The session identifier\n   *            - resourceUrl (string): URL for accessing the session\n   *            - appId (string): Application ID (for desktop sessions)\n   *            - authCode (string): Authentication code\n   *            - connectionProperties (string): Connection configuration\n   *            - resourceId (string): Resource identifier\n   *            - resourceType (string): Type of resource (e.g., \"Desktop\")\n   *            - ticket (string): Access ticket\n   *          - requestId: Unique identifier for this API request\n   *          - errorMessage: Error description if operation failed\n   *\n   * @throws Error if the API request fails or response is invalid.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const infoResult = await result.session.info();\n   *   console.log(`Session ID: ${infoResult.data.sessionId}`);\n   *   console.log(`Resource URL: ${infoResult.data.resourceUrl}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   *\n   * @remarks\n   * **Behavior:**\n   * - This method calls the GetMcpResource API to retrieve session metadata\n   * - The returned SessionInfo contains:\n   *   - sessionId: The session identifier\n   *   - resourceUrl: URL for accessing the session\n   *   - Desktop-specific fields (appId, authCode, connectionProperties, etc.)\n   *     are populated from the DesktopInfo section of the API response\n   * - Session info is retrieved from the AgentBay API in real-time\n   * - The resourceUrl can be used for browser-based access\n   * - Desktop-specific fields (appId, authCode) are only populated for desktop sessions\n   * - This method does not modify the session state\n   *\n   * @see {@link delete}, {@link getLink}\n   */\n  async info(): Promise<OperationResult> {\n    try {\n      const request = new GetMcpResourceRequest({\n        authorization: `Bearer ${this.getAPIKey()}`,\n        sessionId: this.sessionId,\n      });\n\n      logAPICall(\"GetMcpResource\");\n      logDebug(`Request: SessionId=${this.sessionId}`);\n\n      const response = await this.getClient().getMcpResource(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Check for API-level errors\n      if (response?.body?.success === false && response.body?.code) {\n        const errorMessage = `[${response.body.code}] ${\n          response.body.message || \"Unknown error\"\n        }`;\n        const fullResponse = response.body\n          ? JSON.stringify(response.body, null, 2)\n          : \"\";\n        logAPIResponseWithDetails(\n          \"GetMcpResource\",\n          requestId,\n          false,\n          undefined,\n          fullResponse\n        );\n        return {\n          requestId,\n          success: false,\n          errorMessage,\n        };\n      }\n\n      // Extract session info from response (matching Python structure)\n      const responseBody = response.body;\n      const data = responseBody?.data; // Capital D to match Python\n\n      const sessionInfo = new SessionInfoClass();\n\n      if (data?.sessionId) {\n        // Capital S and I to match Python\n        sessionInfo.sessionId = data.sessionId;\n      }\n\n      if (data?.resourceUrl) {\n        // Capital R and U to match Python\n        sessionInfo.resourceUrl = data.resourceUrl;\n      }\n\n      // Transfer DesktopInfo fields to SessionInfo (matching Python structure)\n      if (data?.desktopInfo) {\n        // Capital D and I to match Python\n        const desktopInfo = data.desktopInfo;\n        if (desktopInfo.appId) {\n          sessionInfo.appId = desktopInfo.appId;\n        }\n        if (desktopInfo.authCode) {\n          sessionInfo.authCode = desktopInfo.authCode;\n        }\n        if (desktopInfo.connectionProperties) {\n          sessionInfo.connectionProperties = desktopInfo.connectionProperties;\n        }\n        if (desktopInfo.resourceId) {\n          sessionInfo.resourceId = desktopInfo.resourceId;\n        }\n        if (desktopInfo.resourceType) {\n          sessionInfo.resourceType = desktopInfo.resourceType;\n        }\n        if (desktopInfo.ticket) {\n          sessionInfo.ticket = desktopInfo.ticket;\n        }\n      }\n\n      // Log API response with key fields\n      const keyFields: Record<string, any> = {\n        session_id: this.sessionId,\n      };\n      if (sessionInfo.resourceUrl) {\n        keyFields.resource_url = sessionInfo.resourceUrl;\n      }\n      if (sessionInfo.appId) {\n        keyFields.app_id = sessionInfo.appId;\n      }\n      const fullResponse = responseBody\n        ? JSON.stringify(responseBody, null, 2)\n        : \"\";\n      logAPIResponseWithDetails(\n        \"GetMcpResource\",\n        requestId,\n        true,\n        keyFields,\n        fullResponse\n      );\n\n      return {\n        requestId,\n        success: true,\n        data: sessionInfo,\n      };\n    } catch (error: any) {\n      // Check if this is an expected business error (e.g., session not found)\n      const errorStr = String(error);\n      const errorCode = error?.data?.Code || error?.code || \"\";\n\n      if (\n        errorCode === \"InvalidMcpSession.NotFound\" ||\n        errorStr.includes(\"NotFound\")\n      ) {\n        // This is an expected error - session doesn't exist\n        // Use info level logging without stack trace, but with red color for visibility\n        logInfoWithColor(`Session not found: ${this.sessionId}`);\n        logDebug(`GetMcpResource error details: ${errorStr}`);\n        return {\n          requestId: \"\",\n          success: false,\n          errorMessage: `Session ${this.sessionId} not found`,\n        };\n      } else {\n        // This is an unexpected error - log with full error\n        logError(\n          `❌ Failed to get session info for session ${this.sessionId}`,\n          error\n        );\n        throw new Error(\n          `Failed to get session info for session ${this.sessionId}: ${error}`\n        );\n      }\n    }\n  }\n\n  /**\n   * Retrieves an access link for the session.\n   *\n   * @param protocolType - Protocol type for the link (optional, reserved for future use)\n   * @param port - Specific port number to access (must be in range [30100, 30199]).\n   *               If not specified, returns the default session link.\n   *\n   * @returns Promise resolving to OperationResult containing:\n   *          - success: Whether the operation succeeded\n   *          - data: String URL for accessing the session\n   *          - requestId: Unique identifier for this API request\n   *          - errorMessage: Error description if operation failed\n   *\n   * @throws Error if the API call fails or port is out of valid range.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const linkResult = await result.session.getLink();\n   *   console.log(`Session link: ${linkResult.data}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   *\n   * @remarks\n   * **Behavior:**\n   * - Without port: Returns the default session access URL\n   * - With port: Returns URL for accessing specific port-mapped service\n   * - Port must be in range [30100, 30199] for port forwarding\n   * - For ADB connections, use `session.mobile.getAdbUrl()` with appropriate ADB public key\n   *\n   * **Best Practices:**\n   * - Use default link for general session access\n   * - Use port-specific links when you've started services on specific ports\n   * - Validate port range before calling to avoid errors\n   *\n   * @see {@link info}\n   */\n  async getLink(\n    protocolType?: string,\n    port?: number,\n    options?: string\n  ): Promise<OperationResult> {\n    try {\n      // Validate port range if port is provided\n      if (port) {\n        if (!Number.isInteger(port) || port < 30100 || port > 30199) {\n          throw new Error(\n            `Invalid port value: ${port}. Port must be an integer in the range [30100, 30199].`\n          );\n        }\n      }\n\n      // Log API call\n      let requestParams = `SessionId=${this.getSessionId()}, ProtocolType=${\n        protocolType || \"default\"\n      }, Port=${port || \"default\"}`;\n      if (options) {\n        requestParams += \", Options=provided\";\n      }\n      logAPICall(\"GetLink\", requestParams);\n\n      const request = new GetLinkRequest({\n        authorization: `Bearer ${this.getAPIKey()}`,\n        sessionId: this.getSessionId(),\n        protocolType,\n        port,\n        option: options,\n      });\n\n      const response = await this.agentBay.getClient().getLink(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      const responseBody = response.body;\n\n      if (typeof responseBody !== \"object\") {\n        throw new Error(\n          \"Invalid response format: expected a dictionary from response body\"\n        );\n      }\n\n      let data = responseBody.data || {}; // Capital D to match Python\n      logDebug(`Data: ${JSON.stringify(data)}`);\n\n      if (typeof data !== \"object\") {\n        try {\n          data = typeof data === \"string\" ? JSON.parse(data) : {};\n        } catch (jsonError) {\n          data = {};\n        }\n      }\n\n      const url = (data as any).Url || (data as any).url;\n\n      // Log API response\n      const keyFields: Record<string, any> = {};\n      if (url) {\n        keyFields.url = url;\n      }\n      logAPIResponseWithDetails(\"GetLink\", requestId, true, keyFields);\n\n      return {\n        requestId,\n        success: true,\n        data: url,\n      };\n    } catch (error) {\n      if (\n        error instanceof Error &&\n        error.message.includes(\"Invalid response format\")\n      ) {\n        throw error;\n      }\n      throw new Error(`Failed to get link: ${error}`);\n    }\n  }\n\n  /**\n   * Asynchronously get a link associated with the current session.\n   *\n   * @param protocolType - Optional protocol type to use for the link\n   * @param port - Optional port to use for the link (must be in range [30100, 30199])\n   * @returns OperationResult containing the link as data and request ID\n   * @throws Error if the operation fails (matching Python SessionError)\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const linkResult = await result.session.getLinkAsync(undefined, 30150);\n   *   console.log(`Port link: ${linkResult.data}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async getLinkAsync(\n    protocolType?: string,\n    port?: number,\n    options?: string\n  ): Promise<OperationResult> {\n    try {\n      // Validate port range if port is provided\n      if (port !== undefined) {\n        if (!Number.isInteger(port) || port < 30100 || port > 30199) {\n          throw new Error(\n            `Invalid port value: ${port}. Port must be an integer in the range [30100, 30199].`\n          );\n        }\n      }\n\n      // Log API call\n      let requestParams = `SessionId=${this.getSessionId()}, ProtocolType=${\n        protocolType || \"default\"\n      }, Port=${port || \"default\"}`;\n      if (options) {\n        requestParams += \", Options=provided\";\n      }\n      logAPICall(\"GetLink\", requestParams);\n\n      const request = new GetLinkRequest({\n        authorization: `Bearer ${this.getAPIKey()}`,\n        sessionId: this.getSessionId(),\n        protocolType,\n        port,\n        option: options,\n      });\n\n      // Note: In TypeScript, async is the default, but keeping this method for API compatibility\n      const response = await this.agentBay.getClient().getLink(request);\n\n      // Extract request ID\n      const requestId = extractRequestId(response) || \"\";\n\n      const responseBody = response?.body;\n\n      if (typeof responseBody !== \"object\") {\n        throw new Error(\n          \"Invalid response format: expected a dictionary from response body\"\n        );\n      }\n\n      let data = responseBody?.data || {}; // Capital D to match Python\n      logDebug(`Data: ${JSON.stringify(data)}`);\n\n      if (typeof data !== \"object\") {\n        try {\n          data = typeof data === \"string\" ? JSON.parse(data) : {};\n        } catch (jsonError) {\n          data = {};\n        }\n      }\n\n      const url = (data as any).Url || (data as any).url;\n\n      // Log API response\n      const keyFields: Record<string, any> = {};\n      if (url) {\n        keyFields.url = url;\n      }\n      logAPIResponseWithDetails(\"GetLink\", requestId, true, keyFields);\n\n      return {\n        requestId,\n        success: true,\n        data: url,\n      };\n    } catch (error) {\n      if (\n        error instanceof Error &&\n        error.message.includes(\"Invalid response format\")\n      ) {\n        throw error;\n      }\n      throw new Error(`Failed to get link asynchronously: ${error}`);\n    }\n  }\n\n  /**\n   * List MCP tools available for this session.\n   *\n   * @param imageId Optional image ID, defaults to session's imageId or \"linux_latest\"\n   * @returns McpToolsResult containing tools list and request ID\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const toolsResult = await result.session.listMcpTools();\n   *   console.log(`Found ${toolsResult.tools.length} MCP tools`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async listMcpTools(imageId?: string): Promise<McpToolsResult> {\n    // Use provided imageId, session's imageId, or default\n    if (!imageId) {\n      imageId = (this as any).imageId || \"linux_latest\";\n    }\n\n    const request = new ListMcpToolsRequest({\n      authorization: `Bearer ${this.getAPIKey()}`,\n      imageId: imageId,\n    });\n\n    logAPICall(\"ListMcpTools\");\n    logDebug(`Request: ImageId=${imageId}`);\n\n    const response = await this.getClient().listMcpTools(request);\n\n    // Extract request ID\n    const requestId = extractRequestId(response) || \"\";\n\n    // Check for API-level errors\n    if (response?.body?.success === false && response.body?.code) {\n      const errorMessage = `[${response.body.code}] ${\n        response.body.message || \"Unknown error\"\n      }`;\n      const fullResponse = response.body\n        ? JSON.stringify(response.body, null, 2)\n        : \"\";\n      logAPIResponseWithDetails(\n        \"ListMcpTools\",\n        requestId,\n        false,\n        undefined,\n        fullResponse\n      );\n      return {\n        requestId,\n        success: false,\n        tools: [],\n        errorMessage,\n      };\n    }\n\n    // Parse the response data\n    const tools: McpTool[] = [];\n    if (response && response.body && response.body.data) {\n      try {\n        const toolsData = JSON.parse(response.body.data as string);\n        for (const toolData of toolsData) {\n          const tool: McpTool = {\n            name: toolData.name || \"\",\n            description: toolData.description || \"\",\n            inputSchema: toolData.inputSchema || {},\n            server: toolData.server || \"\",\n            tool: toolData.tool || \"\",\n          };\n          tools.push(tool);\n        }\n      } catch (error) {\n        logError(`Error unmarshaling tools data: ${error}`);\n      }\n    }\n\n    this.mcpTools = tools; // Update the session's mcpTools field\n\n    // Log API response with key fields\n    const keyFields: Record<string, any> = {\n      image_id: imageId,\n      tool_count: tools.length,\n    };\n    const fullResponse = response.body\n      ? JSON.stringify(response.body, null, 2)\n      : \"\";\n    logAPIResponseWithDetails(\n      \"ListMcpTools\",\n      requestId,\n      true,\n      keyFields,\n      fullResponse\n    );\n\n    return {\n      requestId,\n      success: true,\n      tools,\n    };\n  }\n\n  /**\n   * Call an MCP tool and return the result in a format compatible with Agent.\n   *\n   * @param toolName - Name of the MCP tool to call\n   * @param args - Arguments to pass to the tool\n   * @param autoGenSession - Whether to automatically generate session if not exists (default: false)\n   * @returns McpToolResult containing the response data\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const shellResult = await result.session.callMcpTool('shell', { command: \"echo 'Hello'\" });\n   *   console.log(`Output: ${shellResult.data}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   *\n   * @remarks\n   * For press_keys tool, key names are automatically normalized to correct case format.\n   * This improves case compatibility (e.g., \"CTRL\" -> \"Ctrl\", \"tab\" -> \"Tab\").\n   */\n  async callMcpTool(\n    toolName: string,\n    args: any,\n    autoGenSession = false\n  ): Promise<import(\"./agent/agent\").McpToolResult> {\n    try {\n      // Normalize press_keys arguments for better case compatibility\n      if (toolName === \"press_keys\" && args && Array.isArray(args.keys)) {\n        const { normalizeKeys } = await import(\"./key-normalizer\");\n        args = { ...args }; // Don't modify the original args\n        args.keys = normalizeKeys(args.keys);\n        logDebug(`Normalized press_keys arguments: ${JSON.stringify(args)}`);\n      }\n\n      const argsJSON = JSON.stringify(args);\n\n      // Check if this is a VPC session\n      if (this.isVpcEnabled()) {\n        // VPC mode: Use HTTP request to the VPC endpoint\n        const server = this.findServerForTool(toolName);\n        if (!server) {\n          return {\n            success: false,\n            data: \"\",\n            errorMessage: `Server not found for tool: ${toolName}`,\n            requestId: \"\",\n          };\n        }\n\n        if (!this.networkInterfaceIp || !this.httpPort) {\n          return {\n            success: false,\n            data: \"\",\n            errorMessage: `VPC network configuration incomplete: networkInterfaceIp=${this.networkInterfaceIp}, httpPort=${this.httpPort}. This may indicate the VPC session was not properly configured with network parameters.`,\n            requestId: \"\",\n          };\n        }\n\n        const baseURL = `http://${this.networkInterfaceIp}:${this.httpPort}/callTool`;\n        const url = new URL(baseURL);\n        url.searchParams.append(\"server\", server);\n        url.searchParams.append(\"tool\", toolName);\n        url.searchParams.append(\"args\", argsJSON);\n        url.searchParams.append(\"token\", this.getToken());\n        // Add requestId for debugging purposes\n        const requestId = `vpc-${Date.now()}-${Math.random()\n          .toString(36)\n          .substr(2, 9)}`;\n        url.searchParams.append(\"requestId\", requestId);\n\n        const response = await fetch(url.toString(), {\n          method: \"GET\",\n          headers: {\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\n          },\n        });\n\n        if (!response.ok) {\n          return {\n            success: false,\n            data: \"\",\n            errorMessage: `VPC request failed: ${response.statusText}`,\n            requestId: \"\",\n          };\n        }\n\n        const responseData = (await response.json()) as any;\n\n        // Extract the actual result from the nested VPC response structure\n        let actualResult: any = responseData;\n        if (typeof responseData.data === \"string\") {\n          try {\n            const dataMap = JSON.parse(responseData.data);\n            if (dataMap.result) {\n              actualResult = dataMap.result;\n            }\n          } catch (err) {\n            // Keep original response if parsing fails\n          }\n        } else if (responseData.data && responseData.data.result) {\n          actualResult = responseData.data.result;\n        }\n\n        // Extract text content from the result\n        let textContent = \"\";\n        if (\n          actualResult.content &&\n          Array.isArray(actualResult.content) &&\n          actualResult.content.length > 0\n        ) {\n          const contentItem = actualResult.content[0];\n          if (contentItem && contentItem.text) {\n            textContent = contentItem.text;\n          }\n        }\n\n        // For run_code tool, extract and log the actual code execution output\n        if (toolName === \"run_code\" && actualResult) {\n          const dataStr =\n            typeof actualResult === \"string\"\n              ? actualResult\n              : JSON.stringify(actualResult);\n          logCodeExecutionOutput(requestId, dataStr);\n        }\n\n        return {\n          success: true,\n          data: textContent || JSON.stringify(actualResult),\n          errorMessage: \"\",\n          requestId: \"\",\n        };\n      } else {\n        // Non-VPC mode: use traditional API call\n        const callToolRequest = new CallMcpToolRequest({\n          authorization: `Bearer ${this.getAPIKey()}`,\n          sessionId: this.getSessionId(),\n          name: toolName,\n          args: argsJSON,\n          autoGenSession: autoGenSession,\n        });\n\n        const response = await this.getClient().callMcpTool(callToolRequest);\n\n        if (!response.body?.data) {\n          return {\n            success: false,\n            data: \"\",\n            errorMessage: \"Invalid response data format\",\n            requestId: extractRequestId(response) || \"\",\n          };\n        }\n\n        // Check for API-level errors before parsing Data\n        if (response.body.success === false && response.body.code) {\n          const errorMessage = `[${response.body.code}] ${\n            response.body.message || \"Unknown error\"\n          }`;\n          return {\n            success: false,\n            data: \"\",\n            errorMessage,\n            requestId: extractRequestId(response) || \"\",\n          };\n        }\n\n        const data = response.body.data as Record<string, any>;\n\n        // Check if there's an error in the response\n        if (data.isError) {\n          const errorContent = data.content || [];\n          const errorMessage = errorContent\n            .map((item: any) => item.text || \"Unknown error\")\n            .join(\"; \");\n\n          return {\n            success: false,\n            data: \"\",\n            errorMessage,\n            requestId: extractRequestId(response) || \"\",\n          };\n        }\n\n        // Extract text content from content array\n        const content = data.content || [];\n        let textContent = \"\";\n        if (content.length > 0 && content[0].text !== undefined) {\n          textContent = content[0].text;\n        }\n\n        // For run_code tool, extract and log the actual code execution output\n        const reqId = extractRequestId(response) || \"\";\n        if (toolName === \"run_code\" && data) {\n          const dataStr =\n            typeof response.body.data === \"string\"\n              ? response.body.data\n              : JSON.stringify(response.body.data);\n          logCodeExecutionOutput(reqId, dataStr);\n        }\n\n        return {\n          success: true,\n          data: textContent,\n          errorMessage: \"\",\n          requestId: reqId,\n        };\n      }\n    } catch (error) {\n      return {\n        success: false,\n        data: \"\",\n        errorMessage: error instanceof Error ? error.message : String(error),\n        requestId: \"\",\n      };\n    }\n  }\n}\n","import { ApiResponse } from \"../types/api-response\";\nimport { logDebug } from \"../utils/logger\";\n\n/**\n * Result of task execution.\n */\nexport interface ExecutionResult extends ApiResponse {\n  success: boolean;\n  errorMessage: string;\n  taskId: string;\n  taskStatus: string;\n}\n\n/**\n * Result of query operations.\n */\nexport interface QueryResult extends ApiResponse {\n  success: boolean;\n  output: string;\n  errorMessage: string;\n}\n\n/**\n * Result of an MCP tool call.\n */\nexport interface McpToolResult {\n  success: boolean;\n  data: string;\n  errorMessage: string;\n  requestId: string;\n}\n\n/**\n * Interface defining the methods needed by Agent from Session.\n */\nexport interface McpSession {\n  getAPIKey(): string;\n  getSessionId(): string;\n  callMcpTool(toolName: string, args: any): Promise<McpToolResult>;\n}\n\n/**\n * An Agent to manipulate applications to complete specific tasks.\n */\nexport class Agent {\n  private session: McpSession;\n\n  /**\n   * Initialize an Agent object.\n   *\n   * @param session - The Session instance that this Agent belongs to.\n   */\n  constructor(session: McpSession) {\n    this.session = session;\n  }\n\n  /**\n   * Execute a specific task described in human language.\n   *\n   * @param task - Task description in human language.\n   * @param maxTryTimes - Maximum number of retry attempts.\n   * @returns ExecutionResult containing success status, task output, and error message if any.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const taskResult = await result.session.agent.executeTask('Open notepad', 10);\n   *   console.log(`Task status: ${taskResult.taskStatus}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async executeTask(task: string, maxTryTimes: number): Promise<ExecutionResult> {\n    try {\n      const args = { task, max_try_times: maxTryTimes };\n      const result = await this.session.callMcpTool(\"flux_execute_task\", args);\n      \n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          errorMessage: result.errorMessage,\n          taskStatus: \"failed\",\n          taskId: \"\",\n        };\n      }\n\n      // Parse task ID from response\n      let content: any;\n      try {\n        content = JSON.parse(result.data);\n      } catch (err) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          errorMessage: `Failed to parse response: ${err}`,\n          taskStatus: \"failed\",\n          taskId: \"\",\n        };\n      }\n\n      const taskId = content.task_id;\n      if (!taskId) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          errorMessage: \"Task ID not found in response\",\n          taskStatus: \"failed\",\n          taskId: \"\",\n        };\n      }\n\n      // Poll for task completion\n      let triedTime = 0;\n      while (triedTime < maxTryTimes) {\n        const query = await this.getTaskStatus(taskId);\n        if (!query.success) {\n          return {\n            requestId: result.requestId,\n            success: false,\n            errorMessage: query.errorMessage,\n            taskStatus: \"failed\",\n            taskId,\n          };\n        }\n\n        let statusContent: any;\n        try {\n          statusContent = JSON.parse(query.output);\n        } catch (err) {\n          return {\n            requestId: result.requestId,\n            success: false,\n            errorMessage: `Failed to parse status response: ${err}`,\n            taskStatus: \"failed\",\n            taskId,\n          };\n        }\n\n        const taskStatus = statusContent.status;\n        if (!taskStatus) {\n          return {\n            requestId: result.requestId,\n            success: false,\n            errorMessage: \"Task status not found in response\",\n            taskStatus: \"failed\",\n            taskId,\n          };\n        }\n\n        switch (taskStatus) {\n          case \"finished\":\n            return {\n              requestId: result.requestId,\n              success: true,\n              errorMessage: \"\",\n              taskId,\n              taskStatus,\n            };\n          case \"failed\":\n            return {\n              requestId: result.requestId,\n              success: false,\n              errorMessage: \"Failed to execute task.\",\n              taskId,\n              taskStatus,\n            };\n          case \"unsupported\":\n            return {\n              requestId: result.requestId,\n              success: false,\n              errorMessage: \"Unsupported task.\",\n              taskId,\n              taskStatus,\n            };\n        }\n\n        logDebug(`Task ${taskId} is still running, please wait for a while.`);\n        await new Promise(resolve => setTimeout(resolve, 3000));\n        triedTime++;\n      }\n\n      return {\n        requestId: result.requestId,\n        success: false,\n        errorMessage: \"Task execution timed out\",\n        taskStatus: \"timeout\",\n        taskId,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to execute: ${error}`,\n        taskStatus: \"failed\",\n        taskId: \"\",\n      };\n    }\n  }\n\n  /**\n   * Get the status of the task with the given task ID.\n   *\n   * @param taskId - Task ID\n   * @returns QueryResult containing the task status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const taskResult = await result.session.agent.executeTask('Open calculator', 10);\n   *   const statusResult = await result.session.agent.getTaskStatus(taskResult.taskId);\n   *   console.log(`Status: ${JSON.parse(statusResult.output).status}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async getTaskStatus(taskId: string): Promise<QueryResult> {\n    try {\n      const args = { task_id: taskId };\n      const result = await this.session.callMcpTool(\"flux_get_task_status\", args);\n      \n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          errorMessage: result.errorMessage,\n          output: \"\",\n        };\n      }\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        output: result.data,\n        errorMessage: \"\",\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to get task status: ${error}`,\n        output: \"\",\n      };\n    }\n  }\n\n  /**\n   * Terminate a task with a specified task ID.\n   *\n   * @param taskId - The ID of the running task.\n   * @returns ExecutionResult containing success status, task output, and error message if any.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const taskResult = await result.session.agent.executeTask('Open notepad', 5);\n   *   const terminateResult = await result.session.agent.terminateTask(taskResult.taskId);\n   *   console.log(`Terminated: ${terminateResult.taskStatus}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async terminateTask(taskId: string): Promise<ExecutionResult> {\n            logDebug(\"Terminating task\");\n    \n    try {\n      const args = { task_id: taskId };\n      const result = await this.session.callMcpTool(\"flux_terminate_task\", args);\n\n      let content: any;\n      try {\n        content = JSON.parse(result.data);\n      } catch (err) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          errorMessage: `Failed to parse response: ${err}`,\n          taskId,\n          taskStatus: \"failed\",\n        };\n      }\n\n      const terminatedTaskId = content.task_id || taskId;\n      const status = content.status || \"unknown\";\n\n      if (result.success) {\n        return {\n          requestId: result.requestId,\n          success: true,\n          errorMessage: \"\",\n          taskId: terminatedTaskId,\n          taskStatus: status,\n        };\n      }\n\n      return {\n        requestId: result.requestId,\n        success: false,\n        errorMessage: result.errorMessage,\n        taskId: terminatedTaskId,\n        taskStatus: status,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to terminate: ${error}`,\n        taskId,\n        taskStatus: \"failed\",\n      };\n    }\n  }\n} ","export { Browser, BrowserViewport, BrowserScreen, BrowserFingerprint, BrowserFingerprintContext, BrowserProxy, BrowserProxyClass, BrowserOption, BrowserOptionClass } from './browser';\nexport { BrowserAgent } from './browser_agent';\nexport type { ActOptions, ActResult, ObserveOptions, ObserveResult, ExtractOptions } from './browser_agent';\nexport { \n  FingerprintFormat, \n  BrowserFingerprintGenerator,\n  type ScreenFingerprint,\n  type Brand,\n  type UserAgentData,\n  type ExtraProperties,\n  type NavigatorFingerprint,\n  type VideoCard,\n  type Fingerprint\n} from './fingerprint'; ","import { Session } from '../session';\nimport { BrowserAgent } from './browser_agent';\nimport { BrowserError } from '../exceptions';\nimport { InitBrowserRequest } from '../api/models/InitBrowserRequest';\nimport { FingerprintFormat } from './fingerprint';\n\n// Browser data path constant (moved from config.ts)\nconst BROWSER_DATA_PATH = \"/tmp/agentbay_browser\";\n// Browser fingerprint persistent path constant (moved from config.ts)\nconst BROWSER_FINGERPRINT_PERSIST_PATH = \"/tmp/browser_fingerprint\";\nimport {\n  log,\n  logError,\n  logInfo,\n  logDebug,\n  logAPICall,\n  logAPIResponseWithDetails,\n  setRequestId,\n} from '../utils/logger';\n\nexport interface BrowserViewport {\n  width: number;\n  height: number;\n}\n\nexport interface BrowserScreen {\n  width: number;\n  height: number;\n}\n\nexport interface BrowserFingerprint {\n  devices?: Array<'desktop' | 'mobile'>;\n  operatingSystems?: Array<'windows' | 'macos' | 'linux' | 'android' | 'ios'>;\n  locales?: string[];\n}\n\n/**\n * Browser fingerprint context configuration.\n */\nexport class BrowserFingerprintContext {\n  /** ID of the fingerprint context for browser fingerprint */\n  fingerprintContextId: string;\n\n  /**\n   * Initialize BrowserFingerprintContext with context id.\n   * \n   * @param fingerprintContextId - ID of the fingerprint context for browser fingerprint.\n   * \n   * @throws {Error} If fingerprintContextId is empty.\n   */\n  constructor(fingerprintContextId: string) {\n    if (!fingerprintContextId || !fingerprintContextId.trim()) {\n      throw new Error(\"fingerprintContextId cannot be empty\");\n    }\n\n    this.fingerprintContextId = fingerprintContextId;\n  }\n}\n\nexport interface BrowserProxy {\n  type: 'custom' | 'wuying';\n  server?: string;\n  username?: string;\n  password?: string;\n  strategy?: 'restricted' | 'polling';\n  pollsize?: number;\n  toMap(): Record<string, any>;\n}\n\nexport class BrowserProxyClass implements BrowserProxy {\n  type: 'custom' | 'wuying';\n  server?: string;\n  username?: string;\n  password?: string;\n  strategy?: 'restricted' | 'polling';\n  pollsize?: number;\n\n  constructor(\n    proxyType: 'custom' | 'wuying',\n    server?: string,\n    username?: string,\n    password?: string,\n    strategy?: 'restricted' | 'polling',\n    pollsize?: number\n  ) {\n    this.type = proxyType;\n    this.server = server;\n    this.username = username;\n    this.password = password;\n    this.strategy = strategy;\n    this.pollsize = pollsize;\n\n    // Validation\n    if (proxyType !== 'custom' && proxyType !== 'wuying') {\n      throw new Error('proxy_type must be custom or wuying');\n    }\n\n    if (proxyType === 'custom' && !server) {\n      throw new Error('server is required for custom proxy type');\n    }\n\n    if (proxyType === 'wuying' && !strategy) {\n      throw new Error('strategy is required for wuying proxy type');\n    }\n\n    if (proxyType === 'wuying' && strategy !== 'restricted' && strategy !== 'polling') {\n      throw new Error('strategy must be restricted or polling for wuying proxy type');\n    }\n\n    if (proxyType === 'wuying' && strategy === 'polling' && pollsize !== undefined && pollsize <= 0) {\n      throw new Error('pollsize must be greater than 0 for polling strategy');\n    }\n  }\n\n  toMap(): Record<string, any> {\n    const proxyMap: Record<string, any> = {\n      type: this.type\n    };\n\n    if (this.type === 'custom') {\n      proxyMap.server = this.server;\n      if (this.username) {\n        proxyMap.username = this.username;\n      }\n      if (this.password) {\n        proxyMap.password = this.password;\n      }\n    } else if (this.type === 'wuying') {\n      proxyMap.strategy = this.strategy;\n      if (this.strategy === 'polling') {\n        proxyMap.pollsize = this.pollsize;\n      }\n    }\n\n    return proxyMap;\n  }\n\n  static fromMap(m: Record<string, any> | null | undefined): BrowserProxyClass | null {\n    if (!m || typeof m !== 'object') {\n      return null;\n    }\n\n    const proxyType = m.type;\n    if (!proxyType) {\n      return null;\n    }\n\n    if (proxyType === 'custom') {\n      return new BrowserProxyClass(\n        proxyType,\n        m.server,\n        m.username,\n        m.password\n      );\n    } else if (proxyType === 'wuying') {\n      return new BrowserProxyClass(\n        proxyType,\n        undefined,\n        undefined,\n        undefined,\n        m.strategy,\n        m.pollsize || 10\n      );\n    } else {\n      throw new Error(`Unsupported proxy type: ${proxyType}`);\n    }\n  }\n}\n\nexport interface BrowserOption {\n  persistentPath?: string;\n  useStealth?: boolean;\n  userAgent?: string;\n  viewport?: BrowserViewport;\n  screen?: BrowserScreen;\n  fingerprint?: BrowserFingerprint;\n  /** Browser fingerprint format data for detailed fingerprint configuration */\n  fingerprintFormat?: FingerprintFormat;\n  /** Whether to enable fingerprint persistence across sessions */\n  fingerprintPersistent?: boolean;\n  solveCaptchas?: boolean;\n  proxies?: BrowserProxy[];\n  /** Path to the extensions directory. Defaults to \"/tmp/extensions/\" */\n  extensionPath?: string;\n  /** Additional command line arguments for the browser */\n  cmdArgs?: string[];\n  /** Default URL to navigate to when browser starts */\n  defaultNavigateUrl?: string;\n  /** Browser type: 'chrome' or 'chromium'. Defaults to undefined */\n  browserType?: 'chrome' | 'chromium' | undefined;\n}\n\nexport class BrowserOptionClass implements BrowserOption {\n  persistentPath?: string;\n  useStealth?: boolean;\n  userAgent?: string;\n  viewport?: BrowserViewport;\n  screen?: BrowserScreen;\n  fingerprint?: BrowserFingerprint;\n  fingerprintFormat?: FingerprintFormat;\n  fingerprintPersistent?: boolean;\n  fingerprintPersistPath?: string;\n  solveCaptchas?: boolean;\n  proxies?: BrowserProxy[];\n  extensionPath?: string;\n  cmdArgs?: string[];\n  defaultNavigateUrl?: string;\n  browserType?: 'chrome' | 'chromium' | undefined;\n\n  constructor(\n    useStealth = false,\n    userAgent?: string,\n    viewport?: BrowserViewport,\n    screen?: BrowserScreen,\n    fingerprint?: BrowserFingerprint,\n    fingerprintFormat?: FingerprintFormat,\n    fingerprintPersistent = false,\n    solveCaptchas = false,\n    proxies?: BrowserProxy[],\n    cmdArgs?: string[],\n    defaultNavigateUrl?: string,\n    browserType?: 'chrome' | 'chromium',\n  ) {\n    this.useStealth = useStealth;\n    this.userAgent = userAgent;\n    this.viewport = viewport;\n    this.screen = screen;\n    this.fingerprint = fingerprint;\n    this.fingerprintFormat = fingerprintFormat;\n    this.fingerprintPersistent = fingerprintPersistent;\n    this.solveCaptchas = solveCaptchas;\n    this.extensionPath = \"/tmp/extensions/\";\n    this.cmdArgs = cmdArgs;\n    this.defaultNavigateUrl = defaultNavigateUrl;\n    this.browserType = browserType;\n\n    // Check fingerprint persistent if provided\n    if (fingerprintPersistent) {\n      // Currently only support persistent fingerprint in docker env\n      this.fingerprintPersistPath = `${BROWSER_FINGERPRINT_PERSIST_PATH}/fingerprint.json`;\n    } else {\n      this.fingerprintPersistPath = undefined;\n    }\n\n    // Validate proxies list items\n    if (proxies !== undefined) {\n      if (!Array.isArray(proxies)) {\n        throw new Error('proxies must be a list');\n      }\n      if (proxies.length > 1) {\n        throw new Error('proxies list length must be limited to 1');\n      }\n    }\n\n    // Set proxies after validation\n    this.proxies = proxies;\n\n    // Validate cmdArgs\n    if (cmdArgs !== undefined && !Array.isArray(cmdArgs)) {\n      throw new Error('cmdArgs must be a list');\n    }\n\n    // Validate browser_type\n    if (browserType !== undefined && browserType !== 'chrome' && browserType !== 'chromium') {\n      throw new Error(\"browserType must be 'chrome' or 'chromium'\");\n    }\n  }\n\n  toMap(): Record<string, any> {\n    const optionMap: Record<string, any> = {};\n    if (process.env.AGENTBAY_BROWSER_BEHAVIOR_SIMULATE) {\n      optionMap['behaviorSimulate'] = (process.env.AGENTBAY_BROWSER_BEHAVIOR_SIMULATE !== \"0\") as boolean;\n    }\n    if (this.useStealth !== undefined) {\n      optionMap['useStealth'] = this.useStealth;\n    }\n    if (this.userAgent !== undefined) {\n      optionMap['userAgent'] = this.userAgent;\n    }\n    if (this.viewport !== undefined) {\n      optionMap['viewport'] = { width: this.viewport.width, height: this.viewport.height };\n    }\n    if (this.screen !== undefined) {\n      optionMap['screen'] = { width: this.screen.width, height: this.screen.height };\n    }\n    if (this.fingerprint !== undefined) {\n      const fp: Record<string, any> = {};\n      if (this.fingerprint.devices) fp['devices'] = this.fingerprint.devices;\n      if (this.fingerprint.operatingSystems) fp['operatingSystems'] = this.fingerprint.operatingSystems;\n      if (this.fingerprint.locales) fp['locales'] = this.fingerprint.locales;\n      optionMap['fingerprint'] = fp;\n    }\n    if (this.fingerprintFormat !== undefined) {\n      // Encode fingerprint format to base64 string\n      const jsonStr = this.fingerprintFormat.toJson();\n      optionMap['fingerprintRawData'] = Buffer.from(jsonStr, 'utf-8').toString('base64');\n    }\n    if (this.fingerprintPersistent) {\n      this.fingerprintPersistPath = `${BROWSER_FINGERPRINT_PERSIST_PATH}/fingerprint.json`;\n      optionMap['fingerprintPersistPath'] = this.fingerprintPersistPath;\n    }\n    if (this.solveCaptchas !== undefined) {\n      optionMap['solveCaptchas'] = this.solveCaptchas;\n    }\n    if (this.proxies !== undefined) {\n      optionMap['proxies'] = this.proxies.map(proxy => proxy.toMap());\n    }\n    if (this.extensionPath !== undefined) {\n      optionMap['extensionPath'] = this.extensionPath;\n    }\n    if (this.cmdArgs !== undefined) {\n      optionMap['cmdArgs'] = this.cmdArgs;\n    }\n    if (this.defaultNavigateUrl !== undefined) {\n      optionMap['defaultNavigateUrl'] = this.defaultNavigateUrl;\n    }\n    if (this.browserType !== undefined) {\n      optionMap['browserType'] = this.browserType;\n    }\n    return optionMap;\n  }\n\n  fromMap(m: Record<string, any> | null | undefined): BrowserOptionClass {\n    const map = m || {};\n    if (map.useStealth !== undefined) {\n      this.useStealth = map.useStealth;\n    } else {\n      this.useStealth = false;\n    }\n    if (map.userAgent !== undefined) {\n      this.userAgent = map.userAgent;\n    }\n    if (map.viewport !== undefined) {\n      this.viewport = { width: map.viewport.width, height: map.viewport.height };\n    }\n    if (map.screen !== undefined) {\n      this.screen = { width: map.screen.width, height: map.screen.height };\n    }\n    if (map.fingerprint !== undefined) {\n      const fp: BrowserFingerprint = {};\n      if (map.fingerprint.devices) fp.devices = map.fingerprint.devices;\n      if (map.fingerprint.operatingSystems) fp.operatingSystems = map.fingerprint.operatingSystems;\n      if (map.fingerprint.locales) fp.locales = map.fingerprint.locales;\n      this.fingerprint = fp;\n    }\n    if (map.fingerprintFormat !== undefined) {\n      // Handle direct FingerprintFormat object\n      if (map.fingerprintFormat instanceof FingerprintFormat) {\n        this.fingerprintFormat = map.fingerprintFormat;\n      } else {\n        // Convert from plain object to FingerprintFormat\n        try {\n          this.fingerprintFormat = FingerprintFormat.fromDict(map.fingerprintFormat);\n        } catch (error) {\n          logError('Failed to convert fingerprintFormat from object:', error);\n          this.fingerprintFormat = undefined;\n        }\n      }\n    } else if (map.fingerprintRawData !== undefined) {\n      // Decode base64 string to fingerprint format\n      try {\n        const jsonStr = Buffer.from(map.fingerprintRawData, 'base64').toString('utf-8');\n        this.fingerprintFormat = FingerprintFormat.fromJson(jsonStr);\n      } catch (error) {\n        logError('Failed to decode fingerprint raw data:', error);\n        this.fingerprintFormat = undefined;\n      }\n    }\n    if (map.fingerprintPersistent !== undefined) {\n      this.fingerprintPersistent = map.fingerprintPersistent;\n    } else if (map.fingerprintPersistPath !== undefined) {\n      this.fingerprintPersistPath = map.fingerprintPersistPath;\n      this.fingerprintPersistent = true;\n    } else {\n      this.fingerprintPersistent = false;\n    }\n    if (map.solveCaptchas !== undefined) {\n      this.solveCaptchas = map.solveCaptchas;\n    }\n    if (map.proxies !== undefined) {\n      const proxyList = map.proxies;\n      if (proxyList.length > 1) {\n        throw new Error('proxies list length must be limited to 1');\n      }\n      this.proxies = proxyList.map((proxyData: any) => {\n        // If it's already a BrowserProxyClass instance, return it\n        if (proxyData instanceof BrowserProxyClass) {\n          return proxyData;\n        }\n        // Otherwise, convert from map\n        return BrowserProxyClass.fromMap(proxyData);\n      }).filter(Boolean) as BrowserProxy[];\n    }\n    if (map.extensionPath !== undefined) {\n      this.extensionPath = map.extensionPath;\n    }\n    if (map.cmdArgs !== undefined) {\n      this.cmdArgs = map.cmdArgs;\n    }\n    if (map.defaultNavigateUrl !== undefined) {\n      this.defaultNavigateUrl = map.defaultNavigateUrl;\n    }\n    if (map.browserType !== undefined) {\n      this.browserType = map.browserType;\n    }\n    return this;\n  }\n}\n\nexport class Browser {\n  private session: Session;\n  private _endpointUrl: string | null = null;\n  private _initialized = false;\n  private _option: BrowserOptionClass | null = null;\n  public agent: BrowserAgent;\n\n  constructor(session: Session) {\n    this.session = session;\n    this.agent = new BrowserAgent(this.session, this);\n  }\n\n  /**\n   * Initialize the browser instance with the given options.\n   * Returns true if successful, false otherwise.\n   */\n  initialize(option: BrowserOptionClass | BrowserOption): boolean {\n    if (this.isInitialized()) {\n      return true;\n    }\n\n    try {\n      logDebug(`Initializing browser with option: ${JSON.stringify(option)}`);\n      // Use direct API call to initialize browser\n      const request = new InitBrowserRequest();\n      request.authorization = `Bearer ${this.session.getAPIKey()}`;\n      request.persistentPath = BROWSER_DATA_PATH;\n      request.sessionId = this.session.getSessionId();\n\n      // Ensure option is a BrowserOptionClass instance\n      let browserOption: BrowserOptionClass;\n      if (option instanceof BrowserOptionClass) {\n        browserOption = option;\n      } else {\n        // Convert plain object to BrowserOptionClass instance\n        browserOption = new BrowserOptionClass();\n        browserOption.fromMap(option as Record<string, any>);\n      }\n\n      // Map BrowserOption to API BrowserOption payload\n      const browserOptionMap = browserOption.toMap();\n\n      // Enable record if session.enableBrowserReplay is true\n      if (this.session.enableBrowserReplay) {\n        browserOptionMap['enableRecord'] = true;\n      }\n\n      if (Object.keys(browserOptionMap).length > 0) {\n        request.browserOption = browserOptionMap;\n      }\n\n      const response = this.session.getClient().initBrowserSync(request);\n      logDebug(`Response from init_browser data:`, response.body?.data);\n\n      const success = response.body?.data?.port !== null && response.body?.data?.port !== undefined;\n      if (success) {\n        this._initialized = true;\n        this._option = browserOption;\n        logInfo(\"Browser instance was successfully initialized.\");\n      }\n\n      return success;\n    } catch (error) {\n      console.error(\"Failed to initialize browser instance:\", error);\n      this._initialized = false;\n      this._endpointUrl = null;\n      this._option = null;\n      return false;\n    }\n  }\n\n  /**\n   * Initialize the browser instance with the given options asynchronously.\n   * Returns true if successful, false otherwise.\n   *\n   * @param option - Browser configuration options\n   * @returns Promise resolving to true if successful, false otherwise\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'browser_latest' });\n   * if (result.success) {\n   *   const success = await result.session.browser.initializeAsync(new BrowserOptionClass());\n   *   console.log('Browser initialized:', success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async initializeAsync(option: BrowserOptionClass | BrowserOption): Promise<boolean> {\n    if (this.isInitialized()) {\n      return true;\n    }\n\n    try {\n      logDebug(`Initializing browser asynchronously with option: ${JSON.stringify(option)}`);\n      // Use direct API call to initialize browser\n      const request = new InitBrowserRequest();\n      request.authorization = `Bearer ${this.session.getAPIKey()}`;\n      request.persistentPath = BROWSER_DATA_PATH;\n      request.sessionId = this.session.getSessionId();\n\n      // Ensure option is a BrowserOptionClass instance\n      let browserOption: BrowserOptionClass;\n      if (option instanceof BrowserOptionClass) {\n        browserOption = option;\n      } else {\n        // Convert plain object to BrowserOptionClass instance\n        browserOption = new BrowserOptionClass();\n        browserOption.fromMap(option as Record<string, any>);\n      }\n\n      // Map BrowserOption to API BrowserOption payload\n      const browserOptionMap = browserOption.toMap();\n\n      // Enable record if session.enableBrowserReplay is true\n      if (this.session.enableBrowserReplay) {\n        browserOptionMap['enableRecord'] = true;\n      }\n\n      if (Object.keys(browserOptionMap).length > 0) {\n        request.browserOption = browserOptionMap;\n      }\n\n      const response = await this.session.getClient().initBrowser(request);\n      const requestId = response.body?.requestId || \"\";\n      setRequestId(requestId);\n\n      const success = response.body?.data?.port !== null && response.body?.data?.port !== undefined;\n      if (success) {\n        logAPIResponseWithDetails(\n          \"InitBrowser\",\n          requestId,\n          true,\n          {\n            port: response.body?.data?.port,\n            endpoint: response.body?.data?.endpoint,\n          }\n        );\n        this._initialized = true;\n        this._option = browserOption;\n      } else {\n        logAPIResponseWithDetails(\n          \"InitBrowser\",\n          requestId,\n          false,\n          {},\n          \"Port not found in response\"\n        );\n      }\n\n      return success;\n    } catch (error) {\n      logError(\"Failed to initialize browser instance:\", error);\n      this._initialized = false;\n      this._endpointUrl = null;\n      this._option = null;\n      return false;\n    }\n  }\n\n  /**\n   * Destroy the browser instance.\n   */\n  async destroy(): Promise<void> {\n    if (this.isInitialized()) {\n      await this.session.callMcpTool(\"stopChrome\", {});\n    } else {\n      throw new BrowserError(\"Browser is not initialized. Cannot destroy browser.\");\n    }\n  }\n\n  /**\n   * Returns the endpoint URL if the browser is initialized, otherwise throws an exception.\n   * When initialized, always fetches the latest CDP url from session.getLink().\n   *\n   * @returns Promise resolving to the CDP endpoint URL\n   * @throws {BrowserError} If browser is not initialized\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'browser_latest' });\n   * if (result.success) {\n   *   await result.session.browser.initializeAsync(new BrowserOptionClass());\n   *   const endpointUrl = await result.session.browser.getEndpointUrl();\n   *   const browser = await chromium.connectOverCDP(endpointUrl);\n   *   await browser.close();\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async getEndpointUrl(): Promise<string> {\n    if (!this.isInitialized()) {\n      throw new BrowserError(\"Browser is not initialized. Cannot access endpoint URL.\");\n    }\n\n    try {\n      if (this.session.isVpc) {\n        logDebug(`VPC mode, endpoint_router_port: ${this.session.httpPort}`);\n        this._endpointUrl = `ws://${this.session.networkInterfaceIp}:${this.session.httpPort}`;\n      } else {\n        const { GetCdpLinkRequest } = await import('../api/models/model');\n        const request = new GetCdpLinkRequest({\n          authorization: `Bearer ${this.session.getAPIKey()}`,\n          sessionId: this.session.sessionId\n        });\n        const response = await this.session.getAgentBay().getClient().getCdpLink(request);\n        if (response.body && response.body.success && response.body.data) {\n          this._endpointUrl = response.body.data.url || null;\n        } else {\n          const errorMsg = response.body?.message || \"Unknown error\";\n          throw new BrowserError(`Failed to get CDP link: ${errorMsg}`);\n        }\n      }\n      return this._endpointUrl!;\n    } catch (error) {\n      throw new BrowserError(`Failed to get endpoint URL from session: ${error}`);\n    }\n  }\n\n  /**\n   * Returns the current BrowserOption used to initialize the browser, or null if not set.\n   */\n  getOption(): BrowserOptionClass | null {\n    return this._option;\n  }\n\n  /**\n   * Returns true if the browser was initialized, false otherwise.\n   */\n  isInitialized(): boolean {\n    return this._initialized;\n  }\n\n  /**\n   * Stop the browser instance, internal use only.\n   */\n  private _stopBrowser(): void {\n    if (this.isInitialized()) {\n      this.session.callMcpTool(\"stopChrome\", {});\n    } else {\n      throw new BrowserError(\"Browser is not initialized. Cannot stop browser.\");\n    }\n  }\n\n  /**\n   * Takes a screenshot of the specified page with enhanced options and error handling.\n   * This method requires the caller to connect to the browser via Playwright or similar\n   * and pass the page object to this method.\n   *\n   * Note: This is a placeholder method that indicates where screenshot functionality\n   * should be implemented. In a complete implementation, this would use Playwright's\n   * page.screenshot() method or similar browser automation API.\n   *\n   * @param page The Playwright Page object to take a screenshot of. This is a required parameter.\n   * @param fullPage Whether to capture the full scrollable page. Defaults to false.\n   * @param options Additional screenshot options that will override defaults.\n   *                Common options include:\n   *                - type: Image type, either 'png' or 'jpeg' (default: 'png')\n   *                - quality: Quality of the image, between 0-100 (jpeg only)\n   *                - timeout: Maximum time in milliseconds (default: 60000)\n   *                - animations: How to handle animations (default: 'disabled')\n   *                - caret: How to handle the caret (default: 'hide')\n   *                - scale: Scale setting (default: 'css')\n   * @returns Screenshot data as Uint8Array.\n   * @throws BrowserError If browser is not initialized.\n   * @throws Error If screenshot capture fails.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'browser_latest' });\n   * if (result.success) {\n   *   await result.session.browser.initializeAsync(new BrowserOptionClass());\n   *   const browser = await chromium.connectOverCDP(await result.session.browser.getEndpointUrl());\n   *   const page = await browser.contexts()[0].newPage();\n   *   await page.goto('https://example.com');\n   *   const screenshot = await result.session.browser.screenshot(page);\n   *   await writeFile('screenshot.png', Buffer.from(screenshot));\n   *   await browser.close();\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async screenshot(page: any, fullPage = false, options: Record<string, any> = {}): Promise<Uint8Array> {\n    // Check if browser is initialized\n    if (!this.isInitialized()) {\n      throw new BrowserError(\"Browser must be initialized before calling screenshot.\");\n    }\n\n    if (page === null || page === undefined) {\n      throw new Error(\"Page cannot be null or undefined\");\n    }\n\n    // Set default enhanced options\n    const enhancedOptions: Record<string, any> = {\n      animations: \"disabled\",\n      caret: \"hide\",\n      scale: \"css\",\n      timeout: options.timeout || 60000,\n      fullPage: fullPage, // Use the function parameter, not options\n      type: options.type || \"png\",\n    };\n\n    // Update with user-provided options (but fullPage is already set from function parameter)\n    Object.assign(enhancedOptions, options);\n\n    try {\n      // Wait for page to load\n      // await page.waitForLoadState(\"networkidle\");\n      await page.evaluate(\"window.scrollTo(0, document.body.scrollHeight)\");\n      await page.waitForLoadState(\"domcontentloaded\");\n\n      // Scroll to load all content (especially for lazy-loaded elements)\n      await this._scrollToLoadAllContent(page);\n\n      // Ensure images with data-src attributes are loaded\n      await page.evaluate(`\n        () => {\n          document.querySelectorAll('img[data-src]').forEach(img => {\n            if (!img.src && img.dataset.src) {\n              img.src = img.dataset.src;\n            }\n          });\n          // Also handle background-image[data-bg]\n          document.querySelectorAll('[data-bg]').forEach(el => {\n            if (!el.style.backgroundImage) {\n              el.style.backgroundImage = \\`url(\\${el.dataset.bg})\\`;\n            }\n          });\n        }\n      `);\n\n      // Wait a bit for images to load\n      await page.waitForTimeout(1500);\n      const finalHeight = await page.evaluate(\"document.body.scrollHeight\");\n      await page.setViewportSize({ width: 1920, height: Math.min(finalHeight, 10000) });\n\n      // Take the screenshot\n      const screenshotBuffer = await page.screenshot(enhancedOptions);\n      logInfo(\"Screenshot captured successfully.\");\n      \n      // Convert Buffer to Uint8Array\n      return new Uint8Array(screenshotBuffer);\n    } catch (error) {\n      // Convert error to string safely to avoid comparison issues\n      let errorStr: string;\n      try {\n        errorStr = String(error);\n      } catch {\n        errorStr = \"Unknown error occurred\";\n      }\n      const errorMsg = `Failed to capture screenshot: ${errorStr}`;\n      throw new Error(errorMsg);\n    }\n  }\n\n  /**\n   * Scrolls the page to load all content (especially for lazy-loaded elements)\n   */\n  private async _scrollToLoadAllContent(page: any, maxScrolls = 8, delayMs = 1200): Promise<void> {\n    let lastHeight = 0;\n    for (let i = 0; i < maxScrolls; i++) {\n      await page.evaluate(\"window.scrollTo(0, document.body.scrollHeight)\");\n      await page.waitForTimeout(delayMs);\n      const newHeight = await page.evaluate(\"Math.max(document.body.scrollHeight, document.documentElement.scrollHeight)\");\n      if (newHeight === lastHeight) {\n        break;\n      }\n      lastHeight = newHeight;\n    }\n  }\n}\n","import { Session } from '../session';\nimport { Browser } from './browser';\nimport { BrowserError } from '../exceptions';\nimport {\n  log,\n  logError,\n  logInfo,\n  logDebug,\n  logWarn,\n  logAPICall,\n  logAPIResponseWithDetails,\n  setRequestId,\n} from '../utils/logger';\n\n// Options interfaces\nexport interface ActOptions {\n  action: string;\n  timeoutMS?: number;\n  iframes?: boolean;\n  domSettleTimeoutMS?: number;\n  variables?: Record<string, string>;\n  use_vision?: boolean;\n}\n\nexport interface ObserveOptions {\n  instruction: string;\n  iframes?: boolean;\n  domSettleTimeoutMS?: number;\n  use_vision?: boolean;\n}\n\nexport interface ExtractOptions<T = any> {\n  instruction: string;\n  schema: new (...args: any[]) => T;\n  use_text_extract?: boolean;\n  selector?: string;\n  iframe?: boolean;\n  domSettleTimeoutMS?: number;\n  use_vision?: boolean;\n}\n\nexport class ActResult {\n  success: boolean;\n  message: string;\n  action?: string;\n  constructor(success: boolean, message: string, action?: string) {\n    this.success = success;\n    this.message = message;\n    this.action = action;\n  }\n}\n\nexport class ObserveResult {\n  selector: string;\n  description: string;\n  method: string;\n  args: Record<string, any>;\n  constructor(selector: string, description: string, method: string, args: Record<string, any>) {\n    this.selector = selector;\n    this.description = description;\n    this.method = method;\n    this.args = args;\n  }\n}\n\nexport class BrowserAgent {\n  private session: Session;\n  private browser: Browser;\n\n  constructor(session: Session, browser: Browser) {\n    this.session = session;\n    this.browser = browser;\n  }\n\n  /** ------------------ ACT ------------------ **/\n  async act(options: ActOptions, page: any): Promise<ActResult> {\n    if (!this.browser.isInitialized()) throw new BrowserError(\"Browser must be initialized before calling act.\");\n    const [pageId, contextId] = await this._getPageAndContextIndexAsync(page);\n\n    const args: Record<string, any> = {\n      context_id: contextId,\n      page_id: pageId,\n      action: options.action,\n      variables: options.variables,\n      timeout_ms: options.timeoutMS,\n      iframes: options.iframes,\n      dom_settle_timeout_ms: options.domSettleTimeoutMS,\n      use_vision: options.use_vision\n    };\n    const task_name = options.action;\n    logDebug(`${task_name}`);\n  \n    const response = await this._callMcpTool(\"page_use_act\", args);\n\n    if (response.success && response.data) {\n      const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;\n      return new ActResult(true, JSON.stringify(data), options.action);\n    }\n    return new ActResult(false, response.errorMessage || \"\");\n  }\n\n  async actAsync(options: ActOptions, page: any): Promise<ActResult> {\n    if (!this.browser.isInitialized()) throw new BrowserError(\"Browser must be initialized before calling actAsync.\");\n\n    const [pageId, contextId] = await this._getPageAndContextIndexAsync(page);\n    const args: Record<string, any> = {\n      context_id: contextId,\n      page_id: pageId,\n      action: options.action,\n      variables: options.variables,\n      timeout_ms: options.timeoutMS,\n      iframes: options.iframes,\n      dom_settle_timeout_ms: options.domSettleTimeoutMS,\n      use_vision: options.use_vision\n    };\n    const task_name = options.action;\n    logDebug(`${task_name}`);\n\n    const startResp = await this._callMcpTool(\"page_use_act_async\", args);\n    if (!startResp.success) throw new BrowserError(\"Failed to start act task\");\n\n    const { task_id } = JSON.parse(startResp.data);\n    let retries = 30;\n\n    while (retries-- > 0) {\n      await this._delay(5000);\n      const pollResp = await this._callMcpTool(\"page_use_get_act_result\", { task_id });\n\n      if (pollResp.success && pollResp.data) {\n        const data = typeof pollResp.data === 'string' ? JSON.parse(pollResp.data) : pollResp.data;\n        const steps = data.steps || [];\n        const is_done = data.is_done || false;\n        const success = !!data.success;\n        if (is_done) {\n          const msg = steps.length ? JSON.stringify(steps) : \"No actions have been executed.\";\n          logInfo(`Task ${task_id}:${task_name} is done. Success=${success}. ${msg}`);\n          return new ActResult(success, msg, options.action);\n        } else {\n          if (steps.length) {\n            logDebug(`Task ${task_id}:${task_name} progress: ${steps.length} steps done. Details: ${JSON.stringify(steps)}`);\n          } else {\n            logDebug(`Task ${task_id}:${task_name} No actions have been executed yet.`);\n          }\n        }\n      }\n    }\n    throw new BrowserError(`Task ${task_id}: Act timed out`);\n  }\n\n  /** ------------------ OBSERVE ------------------ **/\n  async observe(options: ObserveOptions, page: any): Promise<[boolean, ObserveResult[]]> {\n    if (!this.browser.isInitialized()) throw new BrowserError(\"Browser must be initialized before calling observe.\");\n    const [pageId, contextId] = await this._getPageAndContextIndexAsync(page);\n\n    const args: Record<string, any> = {\n      context_id: contextId,\n      page_id: pageId,\n      instruction: options.instruction,\n      iframes: options.iframes,\n      dom_settle_timeout_ms: options.domSettleTimeoutMS,\n      use_vision: options.use_vision\n    };\n\n    const response = await this._callMcpTool(\"page_use_observe\", args);\n    if (response.success && response.data) {\n      const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;\n      const results: ObserveResult[] = [];\n\n      for (const item of data) {\n        let argsParsed: any;\n        try {\n          argsParsed = typeof item.arguments === 'string' ? JSON.parse(item.arguments) : item.arguments;\n        } catch {\n          logWarn(`Warning: Could not parse arguments JSON: ${item.arguments}`);\n          argsParsed = item.arguments;\n        }\n        results.push(new ObserveResult(item.selector || \"\", item.description || \"\", item.method || \"\", argsParsed));\n      }\n      return [true, results];\n    }\n    return [false, []];\n  }\n\n  async observeAsync(options: ObserveOptions, page: any): Promise<[boolean, ObserveResult[]]> {\n    return this.observe(options, page);\n  }\n\n  /** ------------------ EXTRACT ------------------ **/\n  async extract<T>(options: ExtractOptions<T>, page: any): Promise<[boolean, T | null]> {\n    if (!this.browser.isInitialized()) throw new BrowserError(\"Browser must be initialized before calling extract.\");\n    const [pageId, contextId] = await this._getPageAndContextIndexAsync(page);\n\n    const args: Record<string, any> = {\n      context_id: contextId,\n      page_id: pageId,\n      instruction: options.instruction,\n      field_schema: `schema: ${JSON.stringify({ name: options.schema.name })}`,\n      use_text_extract: options.use_text_extract,\n      use_vision: options.use_vision,\n      selector: options.selector,\n      iframe: options.iframe,\n      dom_settle_timeout_ms: options.domSettleTimeoutMS\n    };\n\n    const response = await this._callMcpTool(\"page_use_extract\", args);\n    if (response.success && response.data) {\n      const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;\n      return [true, data as T];\n    }\n    return [false, null];\n  }\n\n  async extractAsync<T>(options: ExtractOptions<T>, page: any): Promise<[boolean, T | null]> {\n    if (!this.browser.isInitialized()) throw new BrowserError(\"Browser must be initialized before calling extractAsync.\");\n    const [pageId, contextId] = await this._getPageAndContextIndexAsync(page);\n\n    const args: Record<string, any> = {\n      context_id: contextId,\n      page_id: pageId,\n      instruction: options.instruction,\n      field_schema: `schema: ${JSON.stringify({ name: options.schema.name })}`,\n      use_text_extract: options.use_text_extract,\n      use_vision: options.use_vision,\n      selector: options.selector,\n      iframe: options.iframe,\n      dom_settle_timeout_ms: options.domSettleTimeoutMS\n    };\n\n    const startResp = await this._callMcpTool(\"page_use_extract_async\", args);\n    if (!startResp.success) throw new BrowserError(\"Failed to start extract task\");\n\n    const { task_id } = JSON.parse(startResp.data);\n    let retries = 20;\n\n    while (retries-- > 0) {\n      await this._delay(8000);\n      const pollResp = await this._callMcpTool(\"page_use_get_extract_result\", { task_id });\n\n      if (pollResp.success && pollResp.data) {\n        const data = typeof pollResp.data === 'string' ? JSON.parse(pollResp.data) : pollResp.data;\n        return [true, data as T];\n      }\n      logDebug(`Task ${task_id}: No extract result yet (attempt ${20 - retries}/20)`);\n    }\n    throw new BrowserError(`Task ${task_id}: Extract timed out`);\n  }\n\n  private async _getPageAndContextIndexAsync(page: any): Promise<[string, number]> {\n    if (!page) {\n      throw new BrowserError(\"Page is null\");\n    }\n\n    // Try to use Playwright CDP if available\n    try {\n      if (page.context && typeof page.context === 'function') {\n        const context = page.context();\n        if (context && typeof context.newCDPSession === 'function') {\n          const cdpSession = await context.newCDPSession(page);\n          const targetInfo = await cdpSession.send('Target.getTargetInfo');\n          const pageIndex = (targetInfo && targetInfo.targetInfo && targetInfo.targetInfo.targetId) ? targetInfo.targetInfo.targetId : 'default-page-id';\n          if (typeof cdpSession.detach === 'function') {\n            await cdpSession.detach();\n          }\n\n          let contextIndex = 0;\n          if (typeof context.browser === 'function') {\n            const browserObj = context.browser();\n            if (browserObj && typeof browserObj.contexts === 'function') {\n              const contexts = browserObj.contexts();\n              const idx = contexts.indexOf(context);\n              if (idx >= 0) {\n                contextIndex = idx;\n              }\n            }\n          }\n          return [pageIndex, contextIndex];\n        }\n      }\n    } catch (error) {\n      logWarn(`CDP targetId retrieval failed, fallback to defaults: ${error}`);\n    }\n\n    // Fallback to defaults if CDP is not available\n    const pageIndex = \"default-page-id\";\n    const contextIndex = 0;\n    return [pageIndex, contextIndex];\n  }\n\n  private async _callMcpTool(toolName: string, args: Record<string, any>) {\n    return this.session.callMcpTool(toolName, args);\n  }\n\n  private _delay(ms: number) {\n    return new Promise(res => setTimeout(res, ms));\n  }\n}\n","import { chromium, Page } from 'playwright';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Screen fingerprint data structure.\n */\nexport interface ScreenFingerprint {\n  availHeight: number;\n  availWidth: number;\n  availTop: number;\n  availLeft: number;\n  colorDepth: number;\n  height: number;\n  pixelDepth: number;\n  width: number;\n  devicePixelRatio: number;\n  pageXOffset: number;\n  pageYOffset: number;\n  innerHeight: number;\n  outerHeight: number;\n  outerWidth: number;\n  innerWidth: number;\n  screenX: number;\n  clientWidth: number;\n  clientHeight: number;\n  hasHDR: boolean;\n}\n\n/**\n * Brand information data structure.\n */\nexport interface Brand {\n  brand: string;\n  version: string;\n}\n\n/**\n * User agent data structure.\n */\nexport interface UserAgentData {\n  brands: Brand[];\n  mobile: boolean;\n  platform: string;\n  architecture: string;\n  bitness: string;\n  fullVersionList: Brand[];\n  model: string;\n  platformVersion: string;\n  uaFullVersion: string;\n}\n\n/**\n * Navigator extra properties data structure.\n */\nexport interface ExtraProperties {\n  vendorFlavors: string[];\n  isBluetoothSupported: boolean;\n  globalPrivacyControl?: any;\n  pdfViewerEnabled: boolean;\n  installedApps: any[];\n}\n\n/**\n * Navigator fingerprint data structure.\n */\nexport interface NavigatorFingerprint {\n  userAgent: string;\n  userAgentData: UserAgentData;\n  doNotTrack: string;\n  appCodeName: string;\n  appName: string;\n  appVersion: string;\n  oscpu: string;\n  webdriver: string;\n  language: string;\n  languages: string[];\n  platform: string;\n  deviceMemory?: number;\n  hardwareConcurrency: number;\n  product: string;\n  productSub: string;\n  vendor: string;\n  vendorSub: string;\n  maxTouchPoints?: number;\n  extraProperties: ExtraProperties;\n}\n\n/**\n * Video card information data structure.\n */\nexport interface VideoCard {\n  renderer: string;\n  vendor: string;\n}\n\n/**\n * Main fingerprint data structure.\n */\nexport interface Fingerprint {\n  screen: ScreenFingerprint;\n  navigator: NavigatorFingerprint;\n  videoCodecs: Record<string, string>;\n  audioCodecs: Record<string, string>;\n  pluginsData: Record<string, string>;\n  battery?: Record<string, string>;\n  videoCard: VideoCard;\n  multimediaDevices: string[];\n  fonts: string[];\n  mockWebRTC: boolean;\n  slim?: boolean;\n}\n\n/**\n * Complete fingerprint format including fingerprint data and headers.\n */\nexport class FingerprintFormat {\n  public fingerprint: Fingerprint;\n  public headers: Record<string, string>;\n\n  constructor(fingerprint: Fingerprint, headers: Record<string, string>) {\n    this.fingerprint = fingerprint;\n    this.headers = headers;\n  }\n\n  /**\n   * Load fingerprint format from dict or JSON string.\n   *\n   * @param data - Dictionary or JSON string containing fingerprint data\n   * @returns FingerprintFormat instance\n   *\n   * @example\n   * ```typescript\n   * // From dict\n   * const fp = FingerprintFormat.load({fingerprint: {...}, headers: {...}});\n   * // From JSON file\n   * const data = fs.readFileSync('fingerprint.json', 'utf8');\n   * const fp2 = FingerprintFormat.load(data);\n   * ```\n   */\n  static load(data: string | Record<string, any>): FingerprintFormat {\n    if (typeof data === 'string') {\n      return this.fromJson(data);\n    }\n    return this.fromDict(data);\n  }\n\n  /**\n   * Convert to dictionary format.\n   * Note: Used internally by SDK modules.\n   */\n  toDict(): Record<string, any> {\n    return {\n      fingerprint: this.fingerprint,\n      headers: this.headers\n    };\n  }\n\n  /**\n   * Convert to JSON string format.\n   * Note: Used internally by SDK modules.\n   */\n  toJson(indent = 2): string {\n    return JSON.stringify(this.toDict(), null, indent);\n  }\n\n  /**\n   * Create FingerprintFormat from dictionary data.\n   * Note: Used internally by SDK modules.\n   */\n  static fromDict(data: Record<string, any>): FingerprintFormat {\n    if (!data || typeof data !== 'object') {\n      throw new Error('Invalid data: expected an object');\n    }\n\n    const fingerprintDict = data.fingerprint || {};\n    const headersDict = data.headers || {};\n\n    // Convert nested objects to proper interfaces\n    const screenDict = fingerprintDict.screen || {};\n    const screen: ScreenFingerprint = {\n      availHeight: screenDict.availHeight || 0,\n      availWidth: screenDict.availWidth || 0,\n      availTop: screenDict.availTop || 0,\n      availLeft: screenDict.availLeft || 0,\n      colorDepth: screenDict.colorDepth || 24,\n      height: screenDict.height || 0,\n      pixelDepth: screenDict.pixelDepth || 24,\n      width: screenDict.width || 0,\n      devicePixelRatio: screenDict.devicePixelRatio || 1,\n      pageXOffset: screenDict.pageXOffset || 0,\n      pageYOffset: screenDict.pageYOffset || 0,\n      innerHeight: screenDict.innerHeight || 0,\n      outerHeight: screenDict.outerHeight || 0,\n      outerWidth: screenDict.outerWidth || 0,\n      innerWidth: screenDict.innerWidth || 0,\n      screenX: screenDict.screenX || 0,\n      clientWidth: screenDict.clientWidth || 0,\n      clientHeight: screenDict.clientHeight || 0,\n      hasHDR: screenDict.hasHDR || false\n    };\n\n    // Handle UserAgentData - safely get navigator data\n    const navDict = fingerprintDict.navigator || {};\n    const userAgentDataDict = navDict.userAgentData || {};\n\n    // Handle brands and fullVersionList safely\n    const brandsData = userAgentDataDict.brands || [];\n    const brands: Brand[] = [];\n    if (Array.isArray(brandsData)) {\n      for (const brandData of brandsData) {\n        if (typeof brandData === 'object' && brandData !== null) {\n          brands.push({\n            brand: brandData.brand || '',\n            version: brandData.version || ''\n          });\n        }\n      }\n    }\n\n    const fullVersionListData = userAgentDataDict.fullVersionList || [];\n    const fullVersionList: Brand[] = [];\n    if (Array.isArray(fullVersionListData)) {\n      for (const brandData of fullVersionListData) {\n        if (typeof brandData === 'object' && brandData !== null) {\n          fullVersionList.push({\n            brand: brandData.brand || '',\n            version: brandData.version || ''\n          });\n        }\n      }\n    }\n\n    const userAgentData: UserAgentData = {\n      brands: brands,\n      mobile: userAgentDataDict.mobile || false,\n      platform: userAgentDataDict.platform || '',\n      architecture: userAgentDataDict.architecture || '',\n      bitness: userAgentDataDict.bitness || '',\n      fullVersionList: fullVersionList,\n      model: userAgentDataDict.model || '',\n      platformVersion: userAgentDataDict.platformVersion || '',\n      uaFullVersion: userAgentDataDict.uaFullVersion || ''\n    };\n\n    // Handle ExtraProperties\n    const extraPropsDict = navDict.extraProperties || {};\n    const extraProps: ExtraProperties = {\n      vendorFlavors: extraPropsDict.vendorFlavors || [],\n      isBluetoothSupported: extraPropsDict.isBluetoothSupported || false,\n      globalPrivacyControl: extraPropsDict.globalPrivacyControl,\n      pdfViewerEnabled: extraPropsDict.pdfViewerEnabled !== false,\n      installedApps: extraPropsDict.installedApps || []\n    };\n\n    // Create NavigatorFingerprint\n    const navigator: NavigatorFingerprint = {\n      userAgent: navDict.userAgent || '',\n      userAgentData: userAgentData,\n      doNotTrack: navDict.doNotTrack || '',\n      appCodeName: navDict.appCodeName || '',\n      appName: navDict.appName || '',\n      appVersion: navDict.appVersion || '',\n      oscpu: navDict.oscpu || '',\n      webdriver: navDict.webdriver || '',\n      language: navDict.language || '',\n      languages: navDict.languages || [],\n      platform: navDict.platform || '',\n      deviceMemory: navDict.deviceMemory,\n      hardwareConcurrency: navDict.hardwareConcurrency || 8,\n      product: navDict.product || '',\n      productSub: navDict.productSub || '',\n      vendor: navDict.vendor || '',\n      vendorSub: navDict.vendorSub || '',\n      maxTouchPoints: navDict.maxTouchPoints,\n      extraProperties: extraProps\n    };\n\n    // Create VideoCard\n    const videoCardDict = fingerprintDict.videoCard || {};\n    const videoCard: VideoCard = {\n      renderer: videoCardDict.renderer || 'Unknown',\n      vendor: videoCardDict.vendor || 'Unknown'\n    };\n\n    // Create main Fingerprint\n    const fingerprint: Fingerprint = {\n      screen: screen,\n      navigator: navigator,\n      videoCodecs: fingerprintDict.videoCodecs || {},\n      audioCodecs: fingerprintDict.audioCodecs || {},\n      pluginsData: fingerprintDict.pluginsData || {},\n      battery: fingerprintDict.battery,\n      videoCard: videoCard,\n      multimediaDevices: fingerprintDict.multimediaDevices || [],\n      fonts: fingerprintDict.fonts || [],\n      mockWebRTC: fingerprintDict.mockWebRTC || false,\n      slim: fingerprintDict.slim\n    };\n\n    return new FingerprintFormat(fingerprint, headersDict);\n  }\n\n  /**\n   * Create FingerprintFormat from JSON string.\n   */\n  static fromJson(jsonStr: string): FingerprintFormat {\n    const data = JSON.parse(jsonStr);\n    return FingerprintFormat.fromDict(data);\n  }\n\n  /**\n   * Create FingerprintFormat directly using component interfaces.\n   */\n  static create(\n    screen: ScreenFingerprint,\n    navigator: NavigatorFingerprint,\n    videoCard: VideoCard,\n    headers: Record<string, string>,\n    videoCodecs?: Record<string, string>,\n    audioCodecs?: Record<string, string>,\n    pluginsData?: Record<string, string>,\n    battery?: Record<string, string>,\n    multimediaDevices?: string[],\n    fonts?: string[],\n    mockWebRTC = false,\n    slim?: boolean\n  ): FingerprintFormat {\n    const fingerprint: Fingerprint = {\n      screen: screen,\n      navigator: navigator,\n      videoCodecs: videoCodecs || {},\n      audioCodecs: audioCodecs || {},\n      pluginsData: pluginsData || {},\n      battery: battery,\n      videoCard: videoCard,\n      multimediaDevices: multimediaDevices || [],\n      fonts: fonts || [],\n      mockWebRTC: mockWebRTC,\n      slim: slim\n    };\n\n    return new FingerprintFormat(fingerprint, headers);\n  }\n}\n\n/**\n * Browser fingerprint generator class.\n */\nexport class BrowserFingerprintGenerator {\n  private headless: boolean;\n  private useChromeChannel: boolean;\n\n  constructor(options: { headless?: boolean; useChromeChannel?: boolean } = {}) {\n    this.headless = options.headless ?? false;\n    this.useChromeChannel = options.useChromeChannel ?? true;\n  }\n\n  /**\n   * Extract comprehensive browser fingerprint using Playwright.\n   */\n  async generateFingerprint(): Promise<FingerprintFormat | null> {\n    try {\n      console.log('Starting fingerprint generation');\n\n      const launchOptions: any = {\n        headless: this.headless,\n        args: ['--start-maximized']\n      };\n\n      if (this.useChromeChannel) {\n        launchOptions.channel = 'chrome';\n      }\n\n      const browser = await chromium.launch(launchOptions);\n      const context = await browser.newContext({ viewport: null });\n      const page = await context.newPage();\n\n      // Navigate to a test page to ensure proper loading\n      await page.goto('about:blank');\n\n      console.log('Extracting comprehensive browser fingerprint...');\n\n      // Extract comprehensive fingerprint data\n      const fingerprintData = await this.extractFingerprintData(page);\n\n      // Get request headers\n      const headersData = await this.extractHeadersData(page);\n\n      await browser.close();\n\n      // Combine fingerprint and headers using FingerprintFormat\n      const fingerprintFormat = FingerprintFormat.fromDict({\n        fingerprint: fingerprintData,\n        headers: headersData\n      });\n\n      console.log('Fingerprint generation completed successfully!');\n      return fingerprintFormat;\n\n    } catch (error) {\n      console.error(`Error generating fingerprint: ${error}`);\n      return null;\n    }\n  }\n\n  /**\n   * Extract comprehensive browser fingerprint and save to file.\n   */\n  async generateFingerprintToFile(outputFilename = 'fingerprint_output.json'): Promise<boolean> {\n    try {\n      console.log(`Starting fingerprint generation, output file: ${outputFilename}`);\n\n      // Generate fingerprint data (FingerprintFormat object)\n      const fingerprintFormat = await this.generateFingerprint();\n\n      if (fingerprintFormat === null) {\n        console.error('Failed to generate fingerprint data');\n        return false;\n      }\n\n      // Convert to JSON string and save to file\n      const fingerprintJson = fingerprintFormat.toJson(2);\n      const success = await this.saveToFile(fingerprintJson, outputFilename);\n\n      if (success) {\n        console.log(`Fingerprint generation completed successfully! Saved to ${outputFilename}`);\n        return true;\n      } else {\n        console.error('Failed to save fingerprint data');\n        return false;\n      }\n\n    } catch (error) {\n      console.error(`Error generating fingerprint to file: ${error}`);\n      return false;\n    }\n  }\n\n  /**\n   * Extract fingerprint data from the page.\n   */\n  private async extractFingerprintData(page: Page): Promise<any> {\n    // Define the fingerprint extraction function as a string to avoid bundling issues\n    const extractionFunction = `\n      async function() {\n        // Helper function to get audio codec support\n        function getAudioCodecs() {\n          const audio = document.createElement('audio');\n          return {\n            ogg: audio.canPlayType('audio/ogg; codecs=\"vorbis\"') || '',\n            mp3: audio.canPlayType('audio/mpeg') || '',\n            wav: audio.canPlayType('audio/wav; codecs=\"1\"') || '',\n            m4a: audio.canPlayType('audio/x-m4a') || '',\n            aac: audio.canPlayType('audio/aac') || ''\n          };\n        }\n\n        // Helper function to get video codec support\n        function getVideoCodecs() {\n          const video = document.createElement('video');\n          return {\n            ogg: video.canPlayType('video/ogg; codecs=\"theora\"') || '',\n            h264: video.canPlayType('video/mp4; codecs=\"avc1.42E01E\"') || '',\n            webm: video.canPlayType('video/webm; codecs=\"vp8, vorbis\"') || ''\n          };\n        }\n\n        // Helper function to get plugins data\n        function getPluginsData() {\n          const plugins = [];\n          const mimeTypes = [];\n\n          for (let i = 0; i < navigator.plugins.length; i++) {\n            const plugin = navigator.plugins[i];\n            const pluginData = {\n              name: plugin.name,\n              description: plugin.description,\n              filename: plugin.filename,\n              mimeTypes: []\n            };\n\n            for (let j = 0; j < plugin.length; j++) {\n              const mimeType = plugin[j];\n              pluginData.mimeTypes.push({\n                type: mimeType.type,\n                suffixes: mimeType.suffixes,\n                description: mimeType.description,\n                enabledPlugin: plugin.name\n              });\n\n              mimeTypes.push(mimeType.description + '~~' + mimeType.type + '~~' + mimeType.suffixes);\n            }\n\n            plugins.push(pluginData);\n          }\n\n          return { plugins, mimeTypes };\n        }\n\n        // Helper function to get battery info\n        async function getBatteryInfo() {\n          try {\n            if ('getBattery' in navigator) {\n              const battery = await navigator.getBattery();\n              return {\n                charging: battery.charging,\n                chargingTime: battery.chargingTime,\n                dischargingTime: battery.dischargingTime,\n                level: battery.level\n              };\n            }\n          } catch (e) {\n            console.log('Battery API not supported');\n          }\n\n          return {\n            charging: true,\n            chargingTime: 0,\n            dischargingTime: null,\n            level: 1\n          };\n        }\n\n        // Helper function to get WebGL info\n        function getWebGLInfo() {\n          try {\n            const canvas = document.createElement('canvas');\n            const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');\n\n            if (gl) {\n              const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');\n              return {\n                renderer: debugInfo ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : gl.getParameter(gl.RENDERER),\n                vendor: debugInfo ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : gl.getParameter(gl.VENDOR)\n              };\n            }\n          } catch (e) {\n            console.log('WebGL not supported');\n          }\n\n          return {\n            renderer: \"Adreno (TM) 735\",\n            vendor: \"Qualcomm\"\n          };\n        }\n\n        // Helper function to get multimedia devices\n        async function getMultimediaDevices() {\n          try {\n            if ('mediaDevices' in navigator && 'enumerateDevices' in navigator.mediaDevices) {\n              const devices = await navigator.mediaDevices.enumerateDevices();\n              const speakers = [];\n              const micros = [];\n              const webcams = [];\n\n              devices.forEach(function(device) {\n                const deviceInfo = {\n                  deviceId: device.deviceId || '',\n                  kind: device.kind,\n                  label: device.label || '',\n                  groupId: device.groupId || ''\n                };\n\n                if (device.kind === 'audiooutput') {\n                  speakers.push(deviceInfo);\n                } else if (device.kind === 'audioinput') {\n                  micros.push(deviceInfo);\n                } else if (device.kind === 'videoinput') {\n                  webcams.push(deviceInfo);\n                }\n              });\n\n              return { speakers: speakers, micros: micros, webcams: webcams };\n            }\n          } catch (e) {\n            console.log('Media devices not accessible');\n          }\n\n          return {\n            speakers: [{ deviceId: '', kind: 'audiooutput', label: '', groupId: '' }],\n            micros: [{ deviceId: '', kind: 'audioinput', label: '', groupId: '' }],\n            webcams: []\n          };\n        }\n\n        // Helper function to get available fonts\n        function getFonts() {\n          const testFonts = [\n            'Arial', 'Helvetica', 'Times New Roman', 'Courier New', 'Verdana',\n            'Georgia', 'Palatino', 'Garamond', 'Bookman', 'Comic Sans MS',\n            'Trebuchet MS', 'Arial Black', 'Impact'\n          ];\n\n          const availableFonts = [];\n          const testString = 'mmmmmmmmmmlli';\n          const testSize = '72px';\n          const baseWidth = {};\n          const baseHeight = {};\n\n          const canvas = document.createElement('canvas');\n          const context = canvas.getContext('2d');\n          if (!context) return availableFonts;\n\n          const defaultFonts = ['monospace', 'sans-serif', 'serif'];\n          defaultFonts.forEach(function(font) {\n            context.font = testSize + ' ' + font;\n            const metrics = context.measureText(testString);\n            baseWidth[font] = metrics.width;\n            baseHeight[font] = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n          });\n\n          testFonts.forEach(function(font) {\n            let detected = false;\n            defaultFonts.forEach(function(baseFont) {\n              context.font = testSize + ' ' + font + ', ' + baseFont;\n              const metrics = context.measureText(testString);\n              const width = metrics.width;\n              const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n              if (width !== baseWidth[baseFont] || height !== baseHeight[baseFont]) {\n                detected = true;\n              }\n            });\n\n            if (detected) {\n              availableFonts.push(font);\n            }\n          });\n\n          return availableFonts;\n        }\n\n        // Get battery info\n        const batteryInfo = await getBatteryInfo();\n\n        // Get multimedia devices\n        const multimediaDevices = await getMultimediaDevices();\n\n        // Build the complete fingerprint object\n        const fingerprint = {\n          screen: {\n            availTop: screen.availTop,\n            availLeft: screen.availLeft,\n            pageXOffset: window.pageXOffset,\n            pageYOffset: window.pageYOffset,\n            screenX: window.screenX,\n            hasHDR: screen.colorDepth > 24,\n            width: screen.width,\n            height: screen.height,\n            availWidth: screen.availWidth,\n            availHeight: screen.availHeight,\n            clientWidth: document.documentElement.clientWidth,\n            clientHeight: document.documentElement.clientHeight,\n            innerWidth: window.innerWidth,\n            innerHeight: window.innerHeight,\n            outerWidth: window.outerWidth,\n            outerHeight: window.outerHeight,\n            colorDepth: screen.colorDepth,\n            pixelDepth: screen.pixelDepth,\n            devicePixelRatio: window.devicePixelRatio\n          },\n          navigator: {\n            userAgent: navigator.userAgent,\n            userAgentData: navigator.userAgentData ? {\n              brands: navigator.userAgentData.brands || [],\n              mobile: navigator.userAgentData.mobile || false,\n              platform: navigator.userAgentData.platform || ''\n            } : null,\n            language: navigator.language,\n            languages: navigator.languages || [],\n            platform: navigator.platform,\n            deviceMemory: navigator.deviceMemory || 8,\n            hardwareConcurrency: navigator.hardwareConcurrency || 8,\n            maxTouchPoints: navigator.maxTouchPoints || 0,\n            product: navigator.product,\n            productSub: navigator.productSub,\n            vendor: navigator.vendor,\n            vendorSub: navigator.vendorSub,\n            doNotTrack: navigator.doNotTrack,\n            appCodeName: navigator.appCodeName,\n            appName: navigator.appName,\n            appVersion: navigator.appVersion,\n            oscpu: navigator.oscpu,\n            extraProperties: {\n              vendorFlavors: ['chrome'],\n              globalPrivacyControl: navigator.globalPrivacyControl || null,\n              pdfViewerEnabled: navigator.pdfViewerEnabled || true,\n              installedApps: []\n            },\n            webdriver: false\n          },\n          audioCodecs: getAudioCodecs(),\n          videoCodecs: getVideoCodecs(),\n          pluginsData: getPluginsData(),\n          battery: batteryInfo,\n          videoCard: getWebGLInfo(),\n          multimediaDevices: multimediaDevices,\n          fonts: getFonts(),\n          mockWebRTC: false,\n          slim: false\n        };\n\n        return fingerprint;\n      }\n    `;\n\n    return await page.evaluate(extractionFunction);\n  }\n\n  /**\n   * Extract headers data from httpbin.\n   */\n  private async extractHeadersData(page: Page): Promise<Record<string, string>> {\n    try {\n      console.log('Getting request headers...');\n      await page.goto('https://httpbin.org/headers', { waitUntil: 'networkidle' });\n\n      // Extract headers from the response\n      const allHeaders = await page.evaluate(() => {\n        try {\n          // @ts-expect-error - document is available in browser context\n          const preElement = document.querySelector('pre');\n          if (preElement) {\n            const data = JSON.parse(preElement.textContent || '{}');\n            return data.headers || {};\n          }\n        } catch (e) {\n          console.log('Failed to parse headers:', e);\n        }\n        return {};\n      });\n\n      // Filter only the key headers from the example\n      const keyHeaders = [\n        'sec-ch-ua',\n        'sec-ch-ua-mobile',\n        'sec-ch-ua-platform',\n        'upgrade-insecure-requests',\n        'user-agent',\n        'accept',\n        'sec-fetch-site',\n        'sec-fetch-mode',\n        'sec-fetch-user',\n        'sec-fetch-dest',\n        'accept-encoding',\n        'accept-language'\n      ];\n\n      const headersData: Record<string, string> = {};\n      // Convert all_headers keys to lowercase for case-insensitive matching\n      const allHeadersLower: Record<string, string> = {};\n      for (const [key, value] of Object.entries(allHeaders)) {\n        allHeadersLower[key.toLowerCase()] = value as string;\n      }\n\n      for (const header of keyHeaders) {\n        const headerLower = header.toLowerCase();\n        if (headerLower in allHeadersLower) {\n          headersData[header] = allHeadersLower[headerLower];\n        }\n      }\n\n      return headersData;\n\n    } catch (error) {\n      console.warn(`Failed to extract headers: ${error}`);\n      return {};\n    }\n  }\n\n  /**\n   * Save JSON string data to a file.\n   */\n  private async saveToFile(jsonData: string, filename: string): Promise<boolean> {\n    try {\n      // In Node.js environment\n      if (typeof require !== 'undefined') {\n        // eslint-disable-next-line @typescript-eslint/no-var-requires\n        const fs = require('fs');\n        fs.writeFileSync(filename, jsonData, 'utf8');\n        console.log(`Fingerprint data saved to ${filename}`);\n        return true;\n      } else {\n        // In browser environment, we can't save files directly\n        console.warn('File saving not supported in browser environment');\n        return false;\n      }\n    } catch (error) {\n      console.error(`Failed to save fingerprint data: ${error}`);\n      return false;\n    }\n  }\n}\n","export { Code } from \"./code\";\nexport type { CodeExecutionResult } from \"../types/api-response\"; ","import {\n  CodeExecutionResult,\n} from \"../types/api-response\";\n\n/**\n * Handles code execution operations in the AgentBay cloud environment.\n */\nexport class Code {\n  private session: {\n    getAPIKey(): string;\n    getSessionId(): string;\n    callMcpTool(toolName: string, args: any): Promise<{\n      success: boolean;\n      data: string;\n      errorMessage: string;\n      requestId: string;\n    }>;\n  };\n\n  /**\n   * Initialize a Code object.\n   *\n   * @param session - The Session instance that this Code belongs to.\n   */\n  constructor(session: {\n    getAPIKey(): string;\n    getSessionId(): string;\n    callMcpTool(toolName: string, args: any): Promise<{\n      success: boolean;\n      data: string;\n      errorMessage: string;\n      requestId: string;\n    }>;\n  }) {\n    this.session = session;\n  }\n\n  /**\n   * Execute code in the specified language with a timeout.\n   * Corresponds to Python's run_code() method\n   *\n   * @param code - The code to execute.\n   * @param language - The programming language of the code. Must be either 'python' or 'javascript'.\n   * @param timeoutS - The timeout for the code execution in seconds. Default is 60s.\n   *                   Note: Due to gateway limitations, each request cannot exceed 60 seconds.\n   * @returns CodeExecutionResult with code execution output and requestId\n   * @throws Error if an unsupported language is specified.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: \"code_latest\" });\n   * if (result.success) {\n   *   const codeResult = await result.session.code.runCode('print(\"Hello\")', \"python\");\n   *   console.log(codeResult.result);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async runCode(\n    code: string,\n    language: string,\n    timeoutS = 60\n  ): Promise<CodeExecutionResult> {\n    try {\n      // Validate language\n      if (language !== \"python\" && language !== \"javascript\") {\n        return {\n          requestId: \"\",\n          success: false,\n          result: \"\",\n          errorMessage: `Unsupported language: ${language}. Supported languages are 'python' and 'javascript'`,\n        };\n      }\n\n      const args = {\n        code,\n        language,\n        timeout_s: timeoutS,\n      };\n\n      const response = await this.session.callMcpTool(\n        \"run_code\",\n        args\n      );\n\n      if (!response.success) {\n        return {\n          requestId: response.requestId,\n          success: false,\n          result: \"\",\n          errorMessage: response.errorMessage,\n        };\n      }\n\n      return {\n        requestId: response.requestId,\n        success: true,\n        result: response.data || \"\",\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        result: \"\",\n        errorMessage: `Failed to run code: ${error}`,\n      };\n    }\n  }\n} ","import { Session } from \"../session\";\nimport {\n  CommandResult,\n} from \"../types/api-response\";\n\n\n/**\n * Handles command execution operations in the AgentBay cloud environment.\n */\nexport class Command {\n  private session: Session;\n\n  /**\n   * Initialize a Command object.\n   *\n   * @param session - The Session instance that this Command belongs to.\n   */\n  constructor(session: Session) {\n    this.session = session;\n  }\n\n  /**\n   * Sanitizes error messages to remove sensitive information like API keys.\n   *\n   * @param error - The error to sanitize\n   * @returns The sanitized error\n   */\n  private sanitizeError(error: any): any {\n    if (!error) {\n      return error;\n    }\n\n    const errorString = String(error);\n    return errorString.replace(/Bearer\\s+[^\\s]+/g, \"Bearer [REDACTED]\");\n  }\n\n  /**\n   * Executes a shell command in the session environment.\n   *\n   * @param command - The shell command to execute.\n   * @param timeoutMs - Timeout in milliseconds. Defaults to 1000ms.\n   *\n   * @returns Promise resolving to CommandResult containing:\n   *          - success: Whether the command executed successfully\n   *          - output: Combined stdout and stderr output\n   *          - requestId: Unique identifier for this API request\n   *          - errorMessage: Error description if execution failed\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const cmdResult = await result.session.command.executeCommand('echo \"Hello\"', 3000);\n   *   console.log('Command output:', cmdResult.output);\n   *   await result.session.delete();\n   * }\n   * ```\n   *\n   * @remarks\n   * **Behavior:**\n   * - Executes in a Linux shell environment\n   * - Combines stdout and stderr in the output\n   * - Default timeout is 1000ms (1 second)\n   * - Command runs with session user permissions\n   *\n   * @see {@link FileSystem.readFile}, {@link FileSystem.writeFile}\n   */\n  async executeCommand(\n    command: string,\n    timeoutMs = 1000\n  ): Promise<CommandResult> {\n    try {\n      const args = {\n        command,\n        timeout_ms: timeoutMs,\n      };\n      const result = await this.session.callMcpTool(\"shell\", args);\n\n      return {\n        requestId: result.requestId,\n        success: result.success,\n        output: result.data,\n        errorMessage: result.errorMessage,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        output: \"\",\n        errorMessage: `Failed to execute command: ${error}`,\n      };\n    }\n  }\n}\n","/**\n * Command templates for various AgentBay operations.\n *\n * This module contains shell command templates used by different modules\n * to execute operations in remote environments.\n *\n * Template naming convention:\n * - Use descriptive names that clearly indicate the operation\n * - Group templates by functionality (mobile, desktop, network, etc.)\n * - Use consistent parameter naming across similar templates\n *\n * Parameter conventions:\n * - Use snake_case for parameter names\n * - Include descriptive parameter names (e.g., lock_switch, package_list)\n * - Document expected parameter types and values\n */\n\n// ================================\n// Mobile Device Command Templates\n// ================================\n\n/**\n * Resolution lock template\n * Parameters:\n *   lock_switch (string): \"1\" to enable lock, \"0\" to disable lock\n */\nexport const RESOLUTION_LOCK_TEMPLATE = \"setprop sys.wuying.lockres {lock_switch}\";\n\n/**\n * Application whitelist template\n * Parameters:\n *   package_list (string): Newline-separated list of package names\n */\nexport const APP_WHITELIST_TEMPLATE = `cat > /data/system/pm_whitelist.txt << 'EOF'\n{package_list}\nEOF\nchmod 644 /data/system/pm_whitelist.txt\nsetprop rw.wy.pm_whitelist.refresh 1\nsetprop persist.wy.pm_blacklist.switch 2`;\n\n/**\n * Application blacklist template\n * Parameters:\n *   package_list (string): Newline-separated list of package names\n */\nexport const APP_BLACKLIST_TEMPLATE = `cat > /data/system/pm_blacklist.txt << 'EOF'\n{package_list}\nEOF\nchmod 644 /data/system/pm_blacklist.txt\nsetprop rw.wy.pm_blacklist.refresh 1\nsetprop persist.wy.pm_blacklist.switch 1`;\n\n/**\n * Hide navigation bar template\n * Hides the system navigation bar by setting system property and restarting SystemUI\n */\nexport const HIDE_NAVIGATION_BAR_TEMPLATE = \"setprop persist.wy.hasnavibar false; killall com.android.systemui\";\n\n/**\n * Show navigation bar template\n * Shows the system navigation bar by setting system property and restarting SystemUI\n */\nexport const SHOW_NAVIGATION_BAR_TEMPLATE = \"setprop persist.wy.hasnavibar true; killall com.android.systemui\";\n\n/**\n * Uninstall blacklist template\n * Parameters:\n *   package_list (string): Newline-separated list of package names\n *   timestamp (string): Current timestamp for trigger property\n */\nexport const UNINSTALL_BLACKLIST_TEMPLATE = `cat > /data/system/pm_lock.conf << 'EOF'\n{package_list}\nEOF\nchmod 644 /data/system/pm_lock.conf\nsetprop persist.wy.pm_lock.trigger {timestamp}`;\n\n/**\n * Mobile command templates mapping for easy access\n */\nexport const MOBILE_COMMAND_TEMPLATES: Record<string, string> = {\n  \"resolution_lock_enable\": \"setprop sys.wuying.lockres 1\",\n  \"resolution_lock_disable\": \"setprop sys.wuying.lockres 0\",\n  \"app_whitelist\": APP_WHITELIST_TEMPLATE,\n  \"app_blacklist\": APP_BLACKLIST_TEMPLATE,\n  \"hide_navigation_bar\": HIDE_NAVIGATION_BAR_TEMPLATE,\n  \"show_navigation_bar\": SHOW_NAVIGATION_BAR_TEMPLATE,\n  \"uninstall_blacklist\": UNINSTALL_BLACKLIST_TEMPLATE,\n};\n\n/**\n * Get a mobile command template by name\n * \n * @param templateName - The name of the template to retrieve\n * @returns The template string if found, undefined otherwise\n */\nexport function getMobileCommandTemplate(templateName: string): string | undefined {\n  return MOBILE_COMMAND_TEMPLATES[templateName];\n}\n\n/**\n * Check if a mobile command template exists\n * \n * @param templateName - The name of the template to check\n * @returns True if the template exists, false otherwise\n */\nexport function hasMobileCommandTemplate(templateName: string): boolean {\n  return templateName in MOBILE_COMMAND_TEMPLATES;\n}\n\n/**\n * Replace placeholders in a template with actual values\n * \n * @param template - The template string with placeholders\n * @param replacements - Object containing placeholder-value pairs\n * @returns The template with placeholders replaced\n */\nexport function replaceTemplatePlaceholders(\n  template: string, \n  replacements: Record<string, string>\n): string {\n  let result = template;\n  for (const [placeholder, value] of Object.entries(replacements)) {\n    const placeholderPattern = new RegExp(`\\\\{${placeholder}\\\\}`, 'g');\n    result = result.replace(placeholderPattern, value);\n  }\n  return result;\n}\n","export { Computer, BoolResult, CursorPosition, ScreenSize, ScreenshotResult } from './computer'; ","/**\n * Computer module for desktop UI automation.\n * Provides mouse, keyboard, and screen operations for desktop environments.\n */\n\nimport { OperationResult, WindowListResult, WindowInfoResult, BoolResult as WindowBoolResult } from \"../types/api-response\";\n\nexport enum MouseButton {\n  LEFT = 'left',\n  RIGHT = 'right',\n  MIDDLE = 'middle',\n  DOUBLE_LEFT = 'double_left'\n}\n\nexport enum ScrollDirection {\n  UP = 'up',\n  DOWN = 'down',\n  LEFT = 'left',\n  RIGHT = 'right'\n}\n\nexport interface BoolResult extends OperationResult {\n  data?: boolean;\n}\n\nexport interface CursorPosition extends OperationResult {\n  x: number;\n  y: number;\n}\n\nexport interface ScreenSize extends OperationResult {\n  width: number;\n  height: number;\n  dpiScalingFactor: number;\n}\n\nexport interface ScreenshotResult extends OperationResult {\n  data: string; // Screenshot URL\n}\n\n// Session interface for Computer module\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ninterface ComputerSession {\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  callMcpTool(toolName: string, args: Record<string, any>): Promise<any>;\n  sessionId: string;\n  getAPIKey(): string;\n  getSessionId(): string;\n}\n\nexport class Computer {\n  private session: ComputerSession;\n\n  constructor(session: ComputerSession) {\n    this.session = session;\n  }\n\n  /**\n   * Click mouse at specified coordinates.\n   *\n   * @param x - X coordinate for the click\n   * @param y - Y coordinate for the click\n   * @param button - Mouse button to click (default: 'left'). Valid values: 'left', 'right', 'middle', 'double_left'\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const clickResult = await result.session.computer.clickMouse(100, 100, 'left');\n   *   console.log('Clicked:', clickResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async clickMouse(x: number, y: number, button: MouseButton | string = MouseButton.LEFT): Promise<BoolResult> {\n    const buttonStr = typeof button === 'string' ? button : button;\n    const validButtons = Object.values(MouseButton);\n    if (!validButtons.includes(buttonStr as MouseButton)) {\n      throw new Error(`Invalid button '${buttonStr}'. Must be one of ${validButtons.join(', ')}`);\n    }\n\n    const args = { x, y, button: buttonStr };\n    try {\n      const result = await this.session.callMcpTool('click_mouse', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to click mouse: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Move mouse to specified coordinates.\n   *\n   * @param x - X coordinate to move to\n   * @param y - Y coordinate to move to\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.moveMouse(300, 400);\n   *   const pos = await result.session.computer.getCursorPosition();\n   *   console.log(`Position: (${pos.x}, ${pos.y})`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async moveMouse(x: number, y: number): Promise<BoolResult> {\n    const args = { x, y };\n    try {\n      const result = await this.session.callMcpTool('move_mouse', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to move mouse: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Drag mouse from one position to another.\n   *\n   * @param fromX - Starting X coordinate\n   * @param fromY - Starting Y coordinate\n   * @param toX - Ending X coordinate\n   * @param toY - Ending Y coordinate\n   * @param button - Mouse button to use for drag (default: 'left'). Valid values: 'left', 'right', 'middle'\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const dragResult = await result.session.computer.dragMouse(100, 100, 300, 300, 'left');\n   *   console.log('Dragged:', dragResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async dragMouse(fromX: number, fromY: number, toX: number, toY: number, button: MouseButton | string = MouseButton.LEFT): Promise<BoolResult> {\n    const buttonStr = typeof button === 'string' ? button : button;\n    const validButtons = [MouseButton.LEFT, MouseButton.RIGHT, MouseButton.MIDDLE];\n    if (!validButtons.includes(buttonStr as MouseButton)) {\n      throw new Error(`Invalid button '${buttonStr}'. Must be one of ${validButtons.join(', ')}`);\n    }\n\n    const args = { from_x: fromX, from_y: fromY, to_x: toX, to_y: toY, button: buttonStr };\n    try {\n      const result = await this.session.callMcpTool('drag_mouse', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to drag mouse: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Scroll at specified coordinates.\n   *\n   * @param x - X coordinate to scroll at\n   * @param y - Y coordinate to scroll at\n   * @param direction - Scroll direction (default: 'up'). Valid values: 'up', 'down', 'left', 'right'\n   * @param amount - Scroll amount (default: 1)\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.scroll(400, 300, 'up', 3);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async scroll(x: number, y: number, direction: ScrollDirection | string = ScrollDirection.UP, amount = 1): Promise<BoolResult> {\n    const directionStr = typeof direction === 'string' ? direction : direction;\n    const validDirections = Object.values(ScrollDirection);\n    if (!validDirections.includes(directionStr as ScrollDirection)) {\n      throw new Error(`Invalid direction '${directionStr}'. Must be one of ${validDirections.join(', ')}`);\n    }\n\n    const args = { x, y, direction: directionStr, amount };\n    try {\n      const result = await this.session.callMcpTool('scroll', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to scroll: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Input text at the current cursor position.\n   *\n   * @param text - Text to input\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.inputText('Hello AgentBay!');\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async inputText(text: string): Promise<BoolResult> {\n    const args = { text };\n    try {\n      const result = await this.session.callMcpTool('input_text', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to input text: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Press one or more keys.\n   *\n   * @param keys - Array of key names to press\n   * @param hold - Whether to hold the keys down (default: false)\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.pressKeys(['Ctrl', 'c'], true);\n   *   await result.session.computer.releaseKeys(['Ctrl', 'c']);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async pressKeys(keys: string[], hold = false): Promise<BoolResult> {\n    const args = { keys, hold };\n    try {\n      const result = await this.session.callMcpTool('press_keys', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to press keys: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Release previously pressed keys.\n   *\n   * @param keys - Array of key names to release\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.pressKeys(['Ctrl'], true);\n   *   await result.session.computer.releaseKeys(['Ctrl']);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async releaseKeys(keys: string[]): Promise<BoolResult> {\n    const args = { keys };\n    try {\n      const result = await this.session.callMcpTool('release_keys', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to release keys: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Get cursor position.\n   *\n   * @returns Promise resolving to result containing cursor coordinates\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const pos = await result.session.computer.getCursorPosition();\n   *   console.log(`Cursor: (${pos.x}, ${pos.y})`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async getCursorPosition(): Promise<CursorPosition> {\n    try {\n      const result = await this.session.callMcpTool('get_cursor_position', {});\n      \n      if (!result.success) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: result.errorMessage || 'Failed to get cursor position',\n          x: 0,\n          y: 0\n        };\n      }\n\n      // Parse JSON response from data field (callMcpTool already extracts content[0].text to data)\n      const content = result.data;\n      if (!content) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: 'No content in response',\n          x: 0,\n          y: 0\n        };\n      }\n\n      try {\n        const position = JSON.parse(content);\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: '',\n          x: position.x || 0,\n          y: position.y || 0\n        };\n      } catch (parseError) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: `Failed to parse cursor position: ${parseError}`,\n          x: 0,\n          y: 0\n        };\n      }\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to get cursor position: ${error instanceof Error ? error.message : String(error)}`,\n        x: 0,\n        y: 0\n      };\n    }\n  }\n\n  /**\n   * Get screen size.\n   *\n   * @returns Promise resolving to result containing screen dimensions and DPI scaling\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const size = await result.session.computer.getScreenSize();\n   *   console.log(`Screen: ${size.width}x${size.height}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async getScreenSize(): Promise<ScreenSize> {\n    try {\n      const result = await this.session.callMcpTool('get_screen_size', {});\n      \n      if (!result.success) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: result.errorMessage || 'Failed to get screen size',\n          width: 0,\n          height: 0,\n          dpiScalingFactor: 1.0\n        };\n      }\n\n      // Parse JSON response from data field (callMcpTool already extracts content[0].text to data)\n      const content = result.data;\n      if (!content) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: 'No content in response',\n          width: 0,\n          height: 0,\n          dpiScalingFactor: 1.0\n        };\n      }\n\n      try {\n        const screenInfo = JSON.parse(content);\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: '',\n          width: screenInfo.width || 0,\n          height: screenInfo.height || 0,\n          dpiScalingFactor: screenInfo.dpiScalingFactor || 1.0\n        };\n      } catch (parseError) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: `Failed to parse screen size: ${parseError}`,\n          width: 0,\n          height: 0,\n          dpiScalingFactor: 1.0\n        };\n      }\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to get screen size: ${error instanceof Error ? error.message : String(error)}`,\n        width: 0,\n        height: 0,\n        dpiScalingFactor: 1.0\n      };\n    }\n  }\n\n  /**\n   * Take a screenshot.\n   *\n   * @returns Promise resolving to result containing screenshot URL\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const screenshot = await result.session.computer.screenshot();\n   *   console.log('Screenshot URL:', screenshot.data);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async screenshot(): Promise<ScreenshotResult> {\n    try {\n      const result = await this.session.callMcpTool('system_screenshot', {});\n      \n      if (!result.success) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: result.errorMessage || 'Failed to take screenshot',\n          data: ''\n        };\n      }\n\n      const screenshotUrl = result.content?.[0]?.text || '';\n      return {\n        success: true,\n        requestId: result.requestId || '',\n        errorMessage: '',\n        data: screenshotUrl\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to take screenshot: ${error instanceof Error ? error.message : String(error)}`,\n        data: ''\n      };\n    }\n  }\n\n  /**\n   * Lists all root windows.\n   *\n   * @param timeoutMs - Timeout in milliseconds (default: 3000)\n   * @returns Promise resolving to result containing array of root windows\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const windows = await result.session.computer.listRootWindows();\n   *   console.log(`Found ${windows.windows.length} windows`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async listRootWindows(timeoutMs = 3000): Promise<WindowListResult> {\n    try {\n      const args = { timeout_ms: timeoutMs };\n      const response = await this.session.callMcpTool('list_root_windows', args);\n\n      if (!response.success) {\n        return {\n          requestId: response.requestId,\n          success: false,\n          windows: [],\n          errorMessage: response.errorMessage,\n        };\n      }\n\n      const windows = response.data ? JSON.parse(response.data) : [];\n      return {\n        requestId: response.requestId,\n        success: true,\n        windows,\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        windows: [],\n        errorMessage: `Failed to list root windows: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Gets the currently active window.\n   *\n   * @param timeoutMs - Timeout in milliseconds (default: 3000)\n   * @returns Promise resolving to result containing active window information\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const activeWindow = await result.session.computer.getActiveWindow();\n   *   console.log(`Active: ${activeWindow.window?.title}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async getActiveWindow(timeoutMs = 3000): Promise<WindowInfoResult> {\n    try {\n      const args = { timeout_ms: timeoutMs };\n      const response = await this.session.callMcpTool('get_active_window', args);\n\n      if (!response.success) {\n        return {\n          requestId: response.requestId,\n          success: false,\n          window: null,\n          errorMessage: response.errorMessage,\n        };\n      }\n\n      const window = response.data ? JSON.parse(response.data) : null;\n      return {\n        requestId: response.requestId,\n        success: true,\n        window,\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        window: null,\n        errorMessage: `Failed to get active window: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Activates the specified window.\n   *\n   * @param windowId - ID of the window to activate\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const windows = await result.session.computer.listRootWindows();\n   *   await result.session.computer.activateWindow(windows.windows[0].id);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async activateWindow(windowId: number): Promise<WindowBoolResult> {\n    try {\n      const args = { window_id: windowId };\n      const response = await this.session.callMcpTool('activate_window', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to activate window: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Closes the specified window.\n   *\n   * @param windowId - ID of the window to close\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.startApp('notepad.exe');\n   *   const win = await result.session.computer.getActiveWindow();\n   *   await result.session.computer.closeWindow(win.window!.id);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async closeWindow(windowId: number): Promise<WindowBoolResult> {\n    try {\n      const args = { window_id: windowId };\n      const response = await this.session.callMcpTool('close_window', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to close window: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Maximizes the specified window.\n   *\n   * @param windowId - ID of the window to maximize\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.startApp('notepad.exe');\n   *   const win = await result.session.computer.getActiveWindow();\n   *   await result.session.computer.maximizeWindow(win.window!.id);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async maximizeWindow(windowId: number): Promise<WindowBoolResult> {\n    try {\n      const args = { window_id: windowId };\n      const response = await this.session.callMcpTool('maximize_window', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to maximize window: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Minimizes the specified window.\n   *\n   * @param windowId - ID of the window to minimize\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.startApp('notepad.exe');\n   *   const win = await result.session.computer.getActiveWindow();\n   *   await result.session.computer.minimizeWindow(win.window!.id);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async minimizeWindow(windowId: number): Promise<WindowBoolResult> {\n    try {\n      const args = { window_id: windowId };\n      const response = await this.session.callMcpTool('minimize_window', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to minimize window: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Restores the specified window.\n   *\n   * @param windowId - ID of the window to restore\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.startApp('notepad.exe');\n   *   const win = await result.session.computer.getActiveWindow();\n   *   await result.session.computer.minimizeWindow(win.window!.id);\n   *   await result.session.computer.restoreWindow(win.window!.id);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async restoreWindow(windowId: number): Promise<WindowBoolResult> {\n    try {\n      const args = { window_id: windowId };\n      const response = await this.session.callMcpTool('restore_window', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to restore window: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Resizes the specified window.\n   *\n   * @param windowId - ID of the window to resize\n   * @param width - New width of the window\n   * @param height - New height of the window\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.startApp('notepad.exe');\n   *   const win = await result.session.computer.getActiveWindow();\n   *   await result.session.computer.resizeWindow(win.window!.id, 800, 600);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async resizeWindow(windowId: number, width: number, height: number): Promise<WindowBoolResult> {\n    try {\n      const args = { window_id: windowId, width, height };\n      const response = await this.session.callMcpTool('resize_window', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to resize window: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Makes the specified window fullscreen.\n   *\n   * @param windowId - ID of the window to make fullscreen\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.startApp('notepad.exe');\n   *   const win = await result.session.computer.getActiveWindow();\n   *   await result.session.computer.fullscreenWindow(win.window!.id);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async fullscreenWindow(windowId: number): Promise<WindowBoolResult> {\n    try {\n      const args = { window_id: windowId };\n      const response = await this.session.callMcpTool('fullscreen_window', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to make window fullscreen: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Toggles focus mode on or off.\n   *\n   * @param on - Whether to enable (true) or disable (false) focus mode\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.focusMode(true);\n   *   await result.session.computer.focusMode(false);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async focusMode(on: boolean): Promise<WindowBoolResult> {\n    try {\n      const args = { on };\n      const response = await this.session.callMcpTool('focus_mode', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to toggle focus mode: ${error}`,\n      };\n    }\n  }\n\n  // Application Management Operations\n\n  /**\n   * Gets the list of installed applications.\n   *\n   * @param startMenu - Whether to include applications from start menu (default: true)\n   * @param desktop - Whether to include applications from desktop (default: false)\n   * @param ignoreSystemApps - Whether to exclude system applications (default: true)\n   * @returns Promise resolving to result containing array of installed applications\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const apps = await result.session.computer.getInstalledApps();\n   *   console.log(`Found ${apps.data.length} apps`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  async getInstalledApps(startMenu = true, desktop = false, ignoreSystemApps = true): Promise<any> {\n    try {\n      const args = {\n        start_menu: startMenu,\n        desktop,\n        ignore_system_apps: ignoreSystemApps,\n      };\n\n      const response = await this.session.callMcpTool('get_installed_apps', args);\n\n      if (!response.success) {\n        return {\n          requestId: response.requestId,\n          success: false,\n          data: [],\n          errorMessage: response.errorMessage,\n        };\n      }\n\n      const apps = response.data ? JSON.parse(response.data) : [];\n      return {\n        requestId: response.requestId,\n        success: true,\n        data: apps,\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        data: [],\n        errorMessage: `Failed to get installed apps: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Starts the specified application.\n   *\n   * @param startCmd - The command to start the application (e.g., 'notepad.exe', 'calculator:')\n   * @param workDirectory - The working directory for the application (optional)\n   * @param activity - The activity parameter (optional, primarily for mobile use)\n   * @returns Promise resolving to result containing array of started processes\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const startResult = await result.session.computer.startApp('notepad.exe');\n   *   console.log(`Started ${startResult.data.length} process(es)`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  async startApp(startCmd: string, workDirectory = \"\", activity = \"\"): Promise<any> {\n    try {\n      const args: Record<string, string> = { start_cmd: startCmd };\n      if (workDirectory) args.work_directory = workDirectory;\n      if (activity) args.activity = activity;\n\n      const response = await this.session.callMcpTool('start_app', args);\n\n      if (!response.success) {\n        return {\n          requestId: response.requestId,\n          success: false,\n          data: [],\n          errorMessage: response.errorMessage,\n        };\n      }\n\n      const processes = response.data ? JSON.parse(response.data) : [];\n      return {\n        requestId: response.requestId,\n        success: true,\n        data: processes,\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        data: [],\n        errorMessage: `Failed to start app: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Stops an application by process name.\n   *\n   * @param pname - The process name to stop (e.g., 'notepad.exe', 'chrome.exe')\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.startApp('notepad.exe');\n   *   await result.session.computer.stopAppByPName('notepad.exe');\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  async stopAppByPName(pname: string): Promise<any> {\n    try {\n      const args = { pname };\n      const response = await this.session.callMcpTool('stop_app_by_pname', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to stop app by pname: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Stops an application by process ID.\n   *\n   * @param pid - The process ID to stop\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const startResult = await result.session.computer.startApp('notepad.exe');\n   *   const pid = startResult.data[0].pid;\n   *   await result.session.computer.stopAppByPID(pid);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  async stopAppByPID(pid: number): Promise<any> {\n    try {\n      const args = { pid };\n      const response = await this.session.callMcpTool('stop_app_by_pid', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to stop app by pid: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Stops an application by stop command.\n   *\n   * @param cmd - The command to stop the application\n   * @returns Promise resolving to result with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   await result.session.computer.startApp('notepad.exe');\n   *   await result.session.computer.stopAppByCmd('taskkill /IM notepad.exe /F');\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  async stopAppByCmd(cmd: string): Promise<any> {\n    try {\n      const args = { stop_cmd: cmd };\n      const response = await this.session.callMcpTool('stop_app_by_cmd', args);\n\n      return {\n        requestId: response.requestId,\n        success: response.success,\n        errorMessage: response.errorMessage || '',\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        errorMessage: `Failed to stop app by cmd: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Lists all visible applications.\n   *\n   * @returns Promise resolving to result containing array of visible application processes\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'windows_latest' });\n   * if (result.success) {\n   *   const apps = await result.session.computer.listVisibleApps();\n   *   console.log(`Found ${apps.data.length} visible apps`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  async listVisibleApps(): Promise<any> {\n    try {\n      const response = await this.session.callMcpTool('list_visible_apps', {});\n\n      if (!response.success) {\n        return {\n          requestId: response.requestId,\n          success: false,\n          data: [],\n          errorMessage: response.errorMessage,\n        };\n      }\n\n      const processes = response.data ? JSON.parse(response.data) : [];\n      return {\n        requestId: response.requestId,\n        success: true,\n        data: processes,\n      };\n    } catch (error) {\n      return {\n        requestId: '',\n        success: false,\n        data: [],\n        errorMessage: `Failed to list visible apps: ${error}`,\n      };\n    }\n  }\n} ","import { Client } from \"./api/client\";\nimport { GetContextInfoRequest, SyncContextRequest } from \"./api/models/model\";\nimport { ApiResponse, extractRequestId } from \"./types/api-response\";\nimport {\n  log,\n  logError,\n  logInfo,\n  logDebug,\n  logAPICall,\n  logAPIResponseWithDetails,\n  setRequestId,\n  getRequestId,\n} from \"./utils/logger\";\n\nexport interface ContextStatusData {\n  contextId: string;\n  path: string;\n  errorMessage: string;\n  status: string;\n  startTime: number;\n  finishTime: number;\n  taskType: string;\n}\n\nexport interface ContextStatusItem {\n  type: string;\n  data: string;\n}\n\nexport interface ContextInfoResult extends ApiResponse {\n  success?: boolean;\n  contextStatusData: ContextStatusData[];\n  errorMessage?: string;\n}\n\nexport interface ContextSyncResult extends ApiResponse {\n  success: boolean;\n  errorMessage?: string;\n}\n\nexport type SyncCallback = (success: boolean) => void;\n\nexport interface SessionInterface {\n  getAPIKey(): string;\n  getClient(): Client;\n  getSessionId(): string;\n}\n\nexport class ContextManager {\n  private session: SessionInterface;\n\n  constructor(session: SessionInterface) {\n    this.session = session;\n  }\n\n  /**\n   * Gets information about context synchronization status for the current session.\n   *\n   * @returns Promise resolving to ContextInfoResult containing context status data and request ID\n   * @throws Error if the API call fails\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const info = await result.session.context.info();\n   *   console.log(`Context count: ${info.contextStatusData.length}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async info(): Promise<ContextInfoResult> {\n    return this.infoWithParams();\n  }\n\n  /**\n   * Gets information about context synchronization status with optional filter parameters.\n   *\n   * @param contextId - Optional context ID to filter results\n   * @param path - Optional path to filter results\n   * @param taskType - Optional task type to filter results (e.g., \"upload\", \"download\")\n   * @returns Promise resolving to ContextInfoResult containing filtered context status data and request ID\n   * @throws Error if the API call fails\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const info = await result.session.context.infoWithParams('SdkCtx-xxx', '/mnt/persistent');\n   *   console.log(`Context status: ${info.contextStatusData[0]?.status}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async infoWithParams(\n    contextId?: string,\n    path?: string,\n    taskType?: string\n  ): Promise<ContextInfoResult> {\n    const request = new GetContextInfoRequest({\n      authorization: `Bearer ${this.session.getAPIKey()}`,\n      sessionId: this.session.getSessionId(),\n    });\n\n    // Set optional parameters if provided\n    if (contextId) {\n      request.contextId = contextId;\n    }\n    if (path) {\n      request.path = path;\n    }\n    if (taskType) {\n      request.taskType = taskType;\n    }\n\n    // Log API request (matching Go version format)\n    logAPICall(\"GetContextInfo\");\n    let requestLog = `Request: SessionId=${request.sessionId}`;\n    if (request.contextId) {\n      requestLog += `, ContextId=${request.contextId}`;\n    }\n    if (request.path) {\n      requestLog += `, Path=${request.path}`;\n    }\n    if (request.taskType) {\n      requestLog += `, TaskType=${request.taskType}`;\n    }\n    logDebug(requestLog);\n\n    try {\n      const response = await this.session.getClient().getContextInfo(request);\n\n      // Extract RequestID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Check for API-level errors\n      if (response?.body?.success === false && response.body.code) {\n        const errorMsg = `[${response.body.code}] ${response.body.message || 'Unknown error'}`;\n        const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n        logAPIResponseWithDetails(\"GetContextInfo\", requestId, false, undefined, fullResponse);\n        return {\n          requestId,\n          success: false,\n          contextStatusData: [],\n          errorMessage: errorMsg,\n        };\n      }\n\n      // Parse the context status data\n      const contextStatusData: ContextStatusData[] = [];\n      if (response?.body?.data?.contextStatus) {\n        try {\n          // First, parse the outer array\n          const contextStatusStr = response.body.data.contextStatus;\n          const statusItems: ContextStatusItem[] = JSON.parse(contextStatusStr);\n\n          // Process each item in the array\n          for (const item of statusItems) {\n            if (item.type === \"data\") {\n              // Parse the inner data string\n              const dataItems: ContextStatusData[] = JSON.parse(item.data);\n              contextStatusData.push(...dataItems);\n            }\n          }\n        } catch (error) {\n          logError(\"Error parsing context status:\", error);\n        }\n      }\n\n      // Log API response with key fields\n      const keyFields: Record<string, any> = {\n        session_id: request.sessionId,\n        context_count: contextStatusData.length,\n      };\n      if (request.contextId) {\n        keyFields.context_id = request.contextId;\n      }\n      if (request.path) {\n        keyFields.path = request.path;\n      }\n      if (request.taskType) {\n        keyFields.task_type = request.taskType;\n      }\n      const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"GetContextInfo\", requestId, true, keyFields, fullResponse);\n\n      return {\n        requestId,\n        success: true,\n        contextStatusData,\n        errorMessage: undefined,\n      };\n    } catch (error) {\n      logError(\"Error calling GetContextInfo:\", error);\n      throw new Error(`Failed to get context info: ${error}`);\n    }\n  }\n\n  /**\n   * Synchronizes a context with the session. Supports both async and callback modes.\n   *\n   * @param contextId - Optional context ID to synchronize\n   * @param path - Optional path where the context should be mounted\n   * @param mode - Optional synchronization mode (e.g., \"upload\", \"download\")\n   * @param callback - Optional callback function. If provided, runs in background and calls callback when complete\n   * @param maxRetries - Maximum number of retries for polling completion status (default: 150)\n   * @param retryInterval - Milliseconds to wait between retries (default: 1500)\n   * @returns Promise resolving to ContextSyncResult with success status and request ID\n   * @throws Error if the API call fails\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const ctxResult = await agentBay.context.get('my-context', true);\n   *   const syncResult = await result.session.context.sync(ctxResult.context!.id, '/mnt/persistent', 'upload');\n   *   console.log(`Sync: ${syncResult.success}`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async sync(\n    contextId?: string,\n    path?: string,\n    mode?: string,\n    callback?: SyncCallback,\n    maxRetries = 150,\n    retryInterval = 1500\n  ): Promise<ContextSyncResult> {\n    const request = new SyncContextRequest({\n      authorization: `Bearer ${this.session.getAPIKey()}`,\n      sessionId: this.session.getSessionId(),\n    });\n\n    // Set optional parameters if provided\n    if (contextId) {\n      request.contextId = contextId;\n    }\n    if (path) {\n      request.path = path;\n    }\n    if (mode) {\n      request.mode = mode;\n    }\n\n    // Log API request (matching Go version format)\n    logAPICall(\"SyncContext\");\n    let requestLog = `Request: SessionId=${request.sessionId}`;\n    if (request.contextId) {\n      requestLog += `, ContextId=${request.contextId}`;\n    }\n    if (request.path) {\n      requestLog += `, Path=${request.path}`;\n    }\n    if (request.mode) {\n      requestLog += `, Mode=${request.mode}`;\n    }\n    logDebug(requestLog);\n\n    try {\n      const response = await this.session.getClient().syncContext(request);\n\n      // Extract RequestID\n      const requestId = extractRequestId(response) || \"\";\n\n      // Check for API-level errors\n      if (response?.body?.success === false && response.body.code) {\n        const errorMsg = `[${response.body.code}] ${response.body.message || 'Unknown error'}`;\n        const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n        logAPIResponseWithDetails(\"SyncContext\", requestId, false, undefined, fullResponse);\n        return {\n          requestId,\n          success: false,\n          errorMessage: errorMsg,\n        };\n      }\n\n      let success = false;\n      if (response?.body?.success !== undefined) {\n        success = response.body.success;\n      }\n\n      // Log API response with key fields\n      const keyFields: Record<string, any> = {\n        session_id: request.sessionId,\n        success: success,\n      };\n      if (request.contextId) {\n        keyFields.context_id = request.contextId;\n      }\n      if (request.path) {\n        keyFields.path = request.path;\n      }\n      if (request.mode) {\n        keyFields.mode = request.mode;\n      }\n      const fullResponse = response.body ? JSON.stringify(response.body, null, 2) : \"\";\n      logAPIResponseWithDetails(\"SyncContext\", requestId, success, keyFields, fullResponse);\n\n      // If callback is provided, start polling in background (async mode)\n      if (callback && success) {\n        // Start polling in background without blocking\n        this.pollForCompletion(callback, contextId, path, maxRetries, retryInterval)\n          .catch((error) => {\n            logError(\"Error in background polling:\", error);\n            callback(false);\n          });\n        return {\n          requestId,\n          success,\n        };\n      }\n\n      // If no callback, wait for completion (sync mode)\n      if (success) {\n        const finalSuccess = await this.pollForCompletionAsync(\n          contextId,\n          path,\n          maxRetries,\n          retryInterval\n        );\n        return {\n          requestId,\n          success: finalSuccess,\n        };\n      }\n\n      return {\n        requestId,\n        success,\n      };\n    } catch (error) {\n      logError(\"Error calling SyncContext:\", error);\n      throw new Error(`Failed to sync context: ${error}`);\n    }\n  }\n\n  /**\n   * Polls the info interface to check if sync is completed and calls callback.\n   */\n  private async pollForCompletion(\n    callback: SyncCallback,\n    contextId?: string,\n    path?: string,\n    maxRetries = 150,\n    retryInterval = 1500\n  ): Promise<void> {\n    for (let retry = 0; retry < maxRetries; retry++) {\n      try {\n        // Get context status data\n        const infoResult = await this.infoWithParams(contextId, path);\n\n        // Check if all sync tasks are completed\n        let allCompleted = true;\n        let hasFailure = false;\n        let hasSyncTasks = false;\n\n        for (const item of infoResult.contextStatusData) {\n          // We only care about sync tasks (upload/download)\n          if (item.taskType !== \"upload\" && item.taskType !== \"download\") {\n            continue;\n          }\n\n          hasSyncTasks = true;\n          logDebug(`Sync task ${item.contextId} status: ${item.status}, path: ${item.path}`);\n\n          if (item.status !== \"Success\" && item.status !== \"Failed\") {\n            allCompleted = false;\n            break;\n          }\n\n          if (item.status === \"Failed\") {\n            hasFailure = true;\n            logError(`Sync failed for context ${item.contextId}: ${item.errorMessage}`);\n          }\n        }\n\n        if (allCompleted || !hasSyncTasks) {\n          // All tasks completed or no sync tasks found\n          if (hasFailure) {\n            logInfo(\"Context sync completed with failures\");\n            callback(false);\n          } else if (hasSyncTasks) {\n            logInfo(\"Context sync completed successfully\");\n            callback(true);\n          } else {\n            logDebug(\"No sync tasks found\");\n            callback(true);\n          }\n          return; // Exit the function immediately after calling callback\n        }\n\n        logDebug(`Waiting for context sync to complete, attempt ${retry + 1}/${maxRetries}`);\n        await this.sleep(retryInterval);\n      } catch (error) {\n        logError(`Error checking context status on attempt ${retry + 1}:`, error);\n        await this.sleep(retryInterval);\n      }\n    }\n\n    // If we've exhausted all retries, call callback with failure\n    logError(`Context sync polling timed out after ${maxRetries} attempts`);\n    callback(false);\n  }\n\n  /**\n   * Async version of polling for sync completion.\n   */\n  private async pollForCompletionAsync(\n    contextId?: string,\n    path?: string,\n    maxRetries = 150,\n    retryInterval = 1500\n  ): Promise<boolean> {\n    for (let retry = 0; retry < maxRetries; retry++) {\n      try {\n        // Get context status data\n        const infoResult = await this.infoWithParams(contextId, path);\n\n        // Check if all sync tasks are completed\n        let allCompleted = true;\n        let hasFailure = false;\n        let hasSyncTasks = false;\n\n        for (const item of infoResult.contextStatusData) {\n          // We only care about sync tasks (upload/download)\n          if (item.taskType !== \"upload\" && item.taskType !== \"download\") {\n            continue;\n          }\n\n          hasSyncTasks = true;\n          logDebug(`Sync task ${item.contextId} status: ${item.status}, path: ${item.path}`);\n\n          if (item.status !== \"Success\" && item.status !== \"Failed\") {\n            allCompleted = false;\n            break;\n          }\n\n          if (item.status === \"Failed\") {\n            hasFailure = true;\n            logError(`Sync failed for context ${item.contextId}: ${item.errorMessage}`);\n          }\n        }\n\n        if (allCompleted || !hasSyncTasks) {\n          // All tasks completed or no sync tasks found\n          if (hasFailure) {\n            logInfo(\"Context sync completed with failures\");\n            return false;\n          } else if (hasSyncTasks) {\n            logInfo(\"Context sync completed successfully\");\n            return true;\n          } else {\n            logDebug(\"No sync tasks found\");\n            return true;\n          }\n        }\n\n        logDebug(`Waiting for context sync to complete, attempt ${retry + 1}/${maxRetries}`);\n        await this.sleep(retryInterval);\n      } catch (error) {\n        logError(`Error checking context status on attempt ${retry + 1}:`, error);\n        await this.sleep(retryInterval);\n      }\n    }\n\n    // If we've exhausted all retries, return failure\n    logError(`Context sync polling timed out after ${maxRetries} attempts`);\n    return false;\n  }\n\n  /**\n   * Sleep utility function for TypeScript\n   */\n  private sleep(ms: number): Promise<void> {\n    return new Promise((resolve) => setTimeout(resolve, ms));\n  }\n}\n\nexport function newContextManager(session: SessionInterface): ContextManager {\n  return new ContextManager(session);\n}\n","import {\n  BoolResult,\n  FileInfoResult,\n  DirectoryListResult,\n  FileContentResult,\n  MultipleFileContentResult,\n  FileSearchResult,\n  ApiResponse,\n} from \"../types/api-response\";\nimport { UploadResult, DownloadResult } from \"./file-transfer\";\nimport { FileTransfer } from \"./file-transfer\";\nimport { Session } from \"../session\";\nimport { log, logWarn } from \"../utils/logger\";\n\n// Default chunk size for large file operations (60KB)\nconst DEFAULT_CHUNK_SIZE = 60 * 1024;\n\n/**\n * Represents a single file change event\n */\nexport interface FileChangeEvent {\n  eventType: string; // \"create\", \"modify\", \"delete\"\n  path: string;\n  pathType: string;  // \"file\", \"directory\"\n}\n\n/**\n * Result of file change detection operations\n */\nexport interface FileChangeResult extends ApiResponse {\n  events: FileChangeEvent[];\n  rawData: string;\n}\n\n/**\n * Helper functions for FileChangeEvent\n */\nexport class FileChangeEventHelper {\n  static toString(event: FileChangeEvent): string {\n    return `FileChangeEvent(eventType='${event.eventType}', path='${event.path}', pathType='${event.pathType}')`;\n  }\n\n  static toDict(event: FileChangeEvent): Record<string, string> {\n    return {\n      eventType: event.eventType,\n      path: event.path,\n      pathType: event.pathType,\n    };\n  }\n\n  static fromDict(data: Record<string, any>): FileChangeEvent {\n    return {\n      eventType: data.eventType || \"\",\n      path: data.path || \"\",\n      pathType: data.pathType || \"\",\n    };\n  }\n}\n\n/**\n * Helper functions for FileChangeResult\n */\nexport class FileChangeResultHelper {\n  static hasChanges(result: FileChangeResult): boolean {\n    return result.events.length > 0;\n  }\n\n  static getModifiedFiles(result: FileChangeResult): string[] {\n    return result.events\n      .filter(event => event.eventType === \"modify\" && event.pathType === \"file\")\n      .map(event => event.path);\n  }\n\n  static getCreatedFiles(result: FileChangeResult): string[] {\n    return result.events\n      .filter(event => event.eventType === \"create\" && event.pathType === \"file\")\n      .map(event => event.path);\n  }\n\n  static getDeletedFiles(result: FileChangeResult): string[] {\n    return result.events\n      .filter(event => event.eventType === \"delete\" && event.pathType === \"file\")\n      .map(event => event.path);\n  }\n}\n\n/**\n * FileInfo represents information about a file or directory\n */\nexport interface FileInfo {\n  name: string;\n  path: string;\n  size: number;\n  isDirectory: boolean;\n  modTime: string;\n  mode: string;\n  owner?: string;\n  group?: string;\n}\n\n/**\n * DirectoryEntry represents an entry in a directory listing\n */\nexport interface DirectoryEntry {\n  name: string;\n  isDirectory: boolean;\n}\n\n/**\n * Parse a file info string into a FileInfo object\n *\n * @param fileInfoStr - The file info string to parse\n * @returns A FileInfo object\n */\nfunction parseFileInfo(fileInfoStr: string): FileInfo {\n  const result: FileInfo = {\n    name: \"\",\n    path: \"\",\n    size: 0,\n    isDirectory: false,\n    modTime: \"\",\n    mode: \"\",\n  };\n\n  const lines = fileInfoStr.split(\"\\n\");\n  for (const line of lines) {\n    if (line.includes(\":\")) {\n      const [key, value] = line.split(\":\", 2).map((part) => part.trim());\n\n      switch (key) {\n        case \"name\":\n          result.name = value;\n          break;\n        case \"path\":\n          result.path = value;\n          break;\n        case \"size\":\n          result.size = parseInt(value, 10);\n          break;\n        case \"isDirectory\":\n          result.isDirectory = value === \"true\";\n          break;\n        case \"modTime\":\n          result.modTime = value;\n          break;\n        case \"mode\":\n          result.mode = value;\n          break;\n        case \"owner\":\n          result.owner = value;\n          break;\n        case \"group\":\n          result.group = value;\n          break;\n      }\n    }\n  }\n\n  return result;\n}\n\n/**\n * Parse a directory listing string into an array of DirectoryEntry objects\n *\n * @param text - The directory listing text to parse\n * @returns An array of DirectoryEntry objects\n */\nfunction parseDirectoryListing(text: string): DirectoryEntry[] {\n  const result: DirectoryEntry[] = [];\n  const lines = text.split(\"\\n\");\n\n  for (const line of lines) {\n    const trimmedLine = line.trim();\n    if (trimmedLine === \"\") {\n      continue;\n    }\n\n    if (trimmedLine.startsWith(\"[DIR]\")) {\n      result.push({\n        isDirectory: true,\n        name: trimmedLine.replace(\"[DIR]\", \"\").trim(),\n      });\n    } else if (trimmedLine.startsWith(\"[FILE]\")) {\n      result.push({\n        isDirectory: false,\n        name: trimmedLine.replace(\"[FILE]\", \"\").trim(),\n      });\n    }\n  }\n\n  return result;\n}\n\n/**\n * Handles file operations in the AgentBay cloud environment.\n */\nexport class FileSystem {\n  private session: Session;\n  \n  private _fileTransfer: FileTransfer | null = null;\n\n  /**\n   * Initialize a FileSystem object.\n   *\n   * @param session - The Session instance that this FileSystem belongs to.\n   */\n  constructor(session: Session) {\n    this.session = session;\n  }\n\n  /**\n   * Ensure FileTransfer is initialized with the current session.\n   * \n   * @returns The FileTransfer instance\n   */\n  private _ensureFileTransfer(): FileTransfer {\n    if (this._fileTransfer === null) {\n      // Get the agent_bay instance from the session\n      const agentBay = this.session.getAgentBay();\n      if (agentBay === undefined) {\n        throw new Error(\"FileTransfer requires an AgentBay instance\");\n      }\n      \n      this._fileTransfer = new FileTransfer(agentBay, this.session);\n    }\n    \n    return this._fileTransfer;\n  }\n\n  /**\n   * Creates a new directory at the specified path.\n   * Corresponds to Python's create_directory() method\n   *\n   * @param path - Path to the directory to create.\n   * @returns BoolResult with creation result and requestId\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const createResult = await result.session.fileSystem.createDirectory('/tmp/mydir');\n   *   console.log('Directory created:', createResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async createDirectory(path: string): Promise<BoolResult> {\n    try {\n      const args = {\n        path,\n      };\n\n      const result = await this.session.callMcpTool(\n        \"create_directory\",\n        args\n      );\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        data: true,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to create directory: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Edits a file by replacing occurrences of oldText with newText.\n   * Corresponds to Python's edit_file() method\n   *\n   * @param path - Path to the file to edit.\n   * @param edits - Array of edit operations, each containing oldText and newText.\n   * @param dryRun - Optional: If true, preview changes without applying them.\n   * @returns BoolResult with edit result and requestId\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   await result.session.fileSystem.writeFile('/tmp/config.txt', 'DEBUG=false');\n   *   const edits = [{ oldText: 'DEBUG=false', newText: 'DEBUG=true' }];\n   *   const editResult = await result.session.fileSystem.editFile('/tmp/config.txt', edits);\n   *   console.log('File edited:', editResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async editFile(\n    path: string,\n    edits: Array<{ oldText: string; newText: string }>,\n    dryRun = false\n  ): Promise<BoolResult> {\n    try {\n      const args = {\n        path,\n        edits,\n        dryRun,\n      };\n\n      const result = await this.session.callMcpTool(\n        \"edit_file\",\n        args\n      );\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        data: true,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to edit file: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Gets information about a file or directory.\n   * Corresponds to Python's get_file_info() method\n   *\n   * @param path - Path to the file or directory to inspect.\n   * @returns FileInfoResult with file info and requestId\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   await result.session.fileSystem.writeFile('/tmp/test.txt', 'Sample content');\n   *   const infoResult = await result.session.fileSystem.getFileInfo('/tmp/test.txt');\n   *   console.log(`Size: ${infoResult.fileInfo.size} bytes`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async getFileInfo(path: string): Promise<FileInfoResult> {\n    try {\n      const args = {\n        path,\n      };\n\n      const result = await this.session.callMcpTool(\n        \"get_file_info\",\n        args\n      );\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          fileInfo: {},\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      // Parse and return the file info\n      if (!result.data) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          fileInfo: {},\n          errorMessage: \"Empty response from get_file_info\",\n        };\n      }\n\n      const fileInfo = parseFileInfo(result.data);\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        fileInfo,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to get file info: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Lists the contents of a directory.\n   * Corresponds to Python's list_directory() method\n   *\n   * @param path - Path to the directory to list.\n   * @returns DirectoryListResult with directory entries and requestId\n   */\n  /**\n   * Lists the contents of a directory.\n   *\n   * @param path - Absolute path to the directory to list.\n   *\n   * @returns Promise resolving to DirectoryListResult containing array of entries.\n   *\n   * @example\n   * ```typescript\n   * import { AgentBay } from 'wuying-agentbay-sdk';\n   *\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   *\n   * if (result.success) {\n   *   const session = result.session;\n   *\n   *   // List directory contents\n   *   const listResult = await session.fileSystem.listDirectory('/tmp');\n   *   if (listResult.success) {\n   *     console.log(`Found ${listResult.entries.length} entries`);\n   *     for (const entry of listResult.entries) {\n   *       console.log(`${entry.name} (${entry.isDirectory ? 'dir' : 'file'})`);\n   *     }\n   *   }\n   *\n   *   await session.delete();\n   * }\n   * ```\n   *\n   * @see {@link readFile}, {@link writeFile}\n   */\n  async listDirectory(path: string): Promise<DirectoryListResult> {\n    try {\n      const args = {\n        path,\n      };\n\n      const result = await this.session.callMcpTool(\n        \"list_directory\",\n        args\n      );\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          entries: [],\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      // Parse the text content into directory entries\n      const entries = result.data\n        ? parseDirectoryListing(result.data)\n        : [];\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        entries,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        entries: [],\n        errorMessage: `Failed to list directory: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Moves a file or directory from source to destination.\n   * Corresponds to Python's move_file() method\n   *\n   * @param source - Path to the source file or directory.\n   * @param destination - Path to the destination file or directory.\n   * @returns BoolResult with move result and requestId\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   await result.session.fileSystem.writeFile('/tmp/original.txt', 'Test content');\n   *   const moveResult = await result.session.fileSystem.moveFile('/tmp/original.txt', '/tmp/moved.txt');\n   *   console.log('File moved:', moveResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async moveFile(source: string, destination: string): Promise<BoolResult> {\n    try {\n      const args = {\n        source,\n        destination,\n      };\n\n      const result = await this.session.callMcpTool(\n        \"move_file\",\n        args\n      );\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        data: true,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to move file: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Internal method to read a file chunk. Used for chunked file operations.\n   *\n   * @param path - Path to the file to read.\n   * @param offset - Optional: Byte offset to start reading from (0-based).\n   * @param length - Optional: Number of bytes to read. If 0, reads the entire file from offset.\n   * @returns FileContentResult with file content and requestId\n   */\n  private async readFileChunk(\n    path: string,\n    offset = 0,\n    length = 0\n  ): Promise<FileContentResult> {\n    try {\n      const args: any = {\n        path,\n      };\n\n      if (offset > 0) {\n        args.offset = offset;\n      }\n\n      if (length > 0) {\n        args.length = length;\n      }\n\n      const result = await this.session.callMcpTool(\n        \"read_file\",\n        args\n      );\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          content: \"\",\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        content: result.data || \"\",\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        content: \"\",\n        errorMessage: `Failed to read file: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Reads the content of multiple files.\n   * Corresponds to Python's read_multiple_files() method\n   *\n   * @param paths - Array of file paths to read.\n   * @returns MultipleFileContentResult with file contents and requestId\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   await result.session.fileSystem.writeFile('/tmp/file1.txt', 'Content 1');\n   *   await result.session.fileSystem.writeFile('/tmp/file2.txt', 'Content 2');\n   *   const readResult = await result.session.fileSystem.readMultipleFiles(['/tmp/file1.txt', '/tmp/file2.txt']);\n   *   console.log(`Read ${Object.keys(readResult.contents).length} files`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async readMultipleFiles(paths: string[]): Promise<MultipleFileContentResult> {\n    try {\n      const args = {\n        paths,\n      };\n\n      const result = await this.session.callMcpTool(\n        \"read_multiple_files\",\n        args\n      );\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          contents: {},\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      const fileContents: Record<string, string> = {};\n\n      if (result.data) {\n        // Parse the response into a map of file paths to contents\n        const lines = result.data.split(\"\\n\");\n        let currentPath = \"\";\n        let currentContent: string[] = [];\n\n        for (const line of lines) {\n          // Check if this line contains a file path (ends with a colon)\n          const colonIndex = line.indexOf(\":\");\n          if (\n            colonIndex > 0 &&\n            currentPath === \"\" &&\n            !line.substring(0, colonIndex).includes(\" \")\n          ) {\n            // Extract path (everything before the first colon)\n            const path = line.substring(0, colonIndex).trim();\n\n            // Start collecting content (everything after the colon)\n            currentPath = path;\n\n            // If there's content on the same line after the colon, add it\n            if (line.length > colonIndex + 1) {\n              const contentStart = line.substring(colonIndex + 1).trim();\n              if (contentStart) {\n                currentContent.push(contentStart);\n              }\n            }\n          } else if (line === \"---\") {\n            // Save the current file content\n            if (currentPath) {\n              fileContents[currentPath] = currentContent.join(\"\\n\");\n              currentPath = \"\";\n              currentContent = [];\n            }\n          } else if (currentPath) {\n            // If we're collecting content for a path, add this line\n            currentContent.push(line);\n          }\n        }\n\n        // Save the last file content if exists\n        if (currentPath) {\n          fileContents[currentPath] = currentContent.join(\"\\n\");\n        }\n\n        // Trim trailing newlines from file contents to match expected test values\n        for (const path in fileContents) {\n          fileContents[path] = fileContents[path].replace(/\\n+$/, \"\");\n        }\n      }\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        contents: fileContents,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        contents: {},\n        errorMessage: `Failed to read multiple files: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Searches for files in a directory that match a wildcard pattern.\n   * Corresponds to Python's search_files() method\n   *\n   * @param path - Path to the directory to search in.\n   * @param pattern - Wildcard pattern to match against file names. Supports * (any characters)\n   *                  and ? (single character). Examples: \"*.py\", \"test_*\", \"*config*\".\n   * @param excludePatterns - Optional: Array of wildcard patterns to exclude.\n   * @returns FileSearchResult with search results and requestId\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   await result.session.fileSystem.createDirectory('/tmp/test');\n   *   await result.session.fileSystem.writeFile('/tmp/test/file1.py', \"print('hello')\");\n   *   const searchResult = await result.session.fileSystem.searchFiles('/tmp/test', '*.py');\n   *   console.log(`Found ${searchResult.matches.length} Python files`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async searchFiles(\n    path: string,\n    pattern: string,\n    excludePatterns: string[] = []\n  ): Promise<FileSearchResult> {\n    try {\n      const args: any = {\n        path,\n        pattern,\n      };\n\n      if (excludePatterns.length > 0) {\n        args.excludePatterns = excludePatterns;\n      }\n\n      const result = await this.session.callMcpTool(\n        \"search_files\",\n        args\n      );\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          matches: [],\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      // Parse the text content into search results\n      let searchResults: string[] = [];\n      if (result.data) {\n        // Split by newlines and filter out empty lines\n        searchResults = result.data\n          .split(\"\\n\")\n          .map((line) => line.trim())\n          .filter((line) => line !== \"\");\n      }\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        matches: searchResults,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        matches: [],\n        errorMessage: `Failed to search files: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Internal method to write a file chunk. Used for chunked file operations.\n   *\n   * @param path - Path to the file to write.\n   * @param content - Content to write to the file.\n   * @param mode - Optional: Write mode. One of \"overwrite\", \"append\", or \"create_new\". Default is \"overwrite\".\n   * @returns BoolResult with write result and requestId\n   */\n  private async writeFileChunk(\n    path: string,\n    content: string,\n    mode = \"overwrite\"\n  ): Promise<BoolResult> {\n    try {\n      // Validate mode\n      const validModes = [\"overwrite\", \"append\", \"create_new\"];\n      if (!validModes.includes(mode)) {\n        return {\n          requestId: \"\",\n          success: false,\n          errorMessage: `Invalid mode: ${mode}. Must be one of ${validModes.join(\n            \", \"\n          )}`,\n        };\n      }\n\n      const args = {\n        path,\n        content,\n        mode,\n      };\n\n      const result = await this.session.callMcpTool(\n        \"write_file\",\n        args\n      );\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        data: true,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to write file: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Reads the contents of a file. Automatically handles large files by chunking.\n   *\n   * @param path - Path to the file to read.\n   * @returns FileContentResult with complete file content and requestId\n   */\n  /**\n   * Reads the entire content of a file.\n   *\n   * @param path - Absolute path to the file to read.\n   *\n   * @returns Promise resolving to FileContentResult containing:\n   *          - success: Whether the read operation succeeded\n   *          - content: String content of the file\n   *          - requestId: Unique identifier for this API request\n   *          - errorMessage: Error description if read failed\n   *\n   * @throws Error if the API call fails.\n   *\n   * @example\n   * ```typescript\n   * import { AgentBay } from 'wuying-agentbay-sdk';\n   *\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   *\n   * if (result.success) {\n   *   const session = result.session;\n   *\n   *   // Read a text file\n   *   const fileResult = await session.fileSystem.readFile('/etc/hostname');\n   *   if (fileResult.success) {\n   *     console.log(`Content: ${fileResult.content}`);\n   *     // Output: Content: agentbay-session-xyz\n   *   }\n   *\n   *   await session.delete();\n   * }\n   * ```\n   *\n   * @remarks\n   * **Behavior:**\n   * - Automatically handles large files by reading in 60KB chunks\n   * - Returns empty string for empty files\n   * - Fails if path is a directory or doesn't exist\n   * - Content is returned as UTF-8 string\n   *\n   * @see {@link writeFile}, {@link listDirectory}\n   */\n  async readFile(\n    path: string\n  ): Promise<FileContentResult> {\n    const chunkSize = DEFAULT_CHUNK_SIZE;\n    try {\n      // First get the file info\n      const fileInfoResult = await this.getFileInfo(path);\n\n      if (!fileInfoResult.success) {\n        return {\n          requestId: fileInfoResult.requestId,\n          success: false,\n          content: \"\",\n          errorMessage: fileInfoResult.errorMessage,\n        };\n      }\n\n      // Check if file exists and is a file (not a directory)\n      if (!fileInfoResult.fileInfo || fileInfoResult.fileInfo.isDirectory) {\n        return {\n          requestId: fileInfoResult.requestId,\n          success: false,\n          content: \"\",\n          errorMessage: `Path does not exist or is a directory: ${path}`,\n        };\n      }\n\n      // Get size from the fileInfo object\n      const fileSize = fileInfoResult.fileInfo.size || 0;\n\n      if (fileSize === 0) {\n        return {\n          requestId: fileInfoResult.requestId,\n          success: true,\n          content: \"\",\n        };\n      }\n\n      // Read the file in chunks\n      let result = \"\";\n      let offset = 0;\n      let chunkCount = 0;\n\n      while (offset < fileSize) {\n        // Calculate how much to read in this chunk\n        let length = chunkSize;\n        if (offset + length > fileSize) {\n          length = fileSize - offset;\n        }\n\n        try {\n          // Read the chunk\n          const chunkResult = await this.readFileChunk(path, offset, length);\n\n          if (!chunkResult.success) {\n            return chunkResult; // Return the error\n          }\n\n          // Extract the actual content from the response\n          result += chunkResult.content;\n\n          // Move to the next chunk\n          offset += length;\n          chunkCount++;\n        } catch (error) {\n          return {\n            requestId: fileInfoResult.requestId,\n            success: false,\n            content: \"\",\n            errorMessage: `Error reading chunk at offset ${offset}: ${error}`,\n          };\n        }\n      }\n\n      return {\n        requestId: fileInfoResult.requestId,\n        success: true,\n        content: result,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        content: \"\",\n        errorMessage: `Failed to read large file: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Writes content to a file. Automatically handles large files by chunking.\n   *\n   * @param path - Path to the file to write.\n   * @param content - Content to write to the file.\n   * @param mode - Optional: Write mode. One of \"overwrite\", \"append\", or \"create_new\". Default is \"overwrite\".\n   * @returns BoolResult indicating success or failure with requestId\n   */\n  /**\n   * Writes content to a file.\n   *\n   * @param path - Absolute path to the file to write.\n   * @param content - String content to write to the file.\n   * @param mode - Write mode: \"overwrite\" (default) or \"append\".\n   *\n   * @returns Promise resolving to BoolResult with success status.\n   *\n   * @example\n   * ```typescript\n   * import { AgentBay } from 'wuying-agentbay-sdk';\n   *\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   *\n   * if (result.success) {\n   *   const session = result.session;\n   *\n   *   // Write to a file (overwrite mode)\n   *   const writeResult = await session.fileSystem.writeFile(\n   *     '/tmp/test.txt',\n   *     'Hello, AgentBay!'\n   *   );\n   *   if (writeResult.success) {\n   *     console.log('File written successfully');\n   *   }\n   *\n   *   // Append to a file\n   *   const appendResult = await session.fileSystem.writeFile(\n   *     '/tmp/test.txt',\n   *     '\\nNew line',\n   *     'append'\n   *   );\n   *\n   *   await session.delete();\n   * }\n   * ```\n   *\n   * @remarks\n   * **Behavior:**\n   * - Automatically handles large files by writing in 60KB chunks\n   * - Creates parent directories if they don't exist\n   * - \"overwrite\" mode replaces existing file content\n   * - \"append\" mode adds content to the end of the file\n   *\n   * @see {@link readFile}, {@link listDirectory}\n   */\n  async writeFile(\n    path: string,\n    content: string,\n    mode = \"overwrite\"\n  ): Promise<BoolResult> {\n    const chunkSize = DEFAULT_CHUNK_SIZE;\n    try {\n      const contentLen = content.length;\n\n      // If content is small enough, use the regular writeFileChunk method\n      if (contentLen <= chunkSize) {\n        return await this.writeFileChunk(path, content, mode);\n      }\n\n      // Write the first chunk with the specified mode\n      const firstChunkEnd = Math.min(chunkSize, contentLen);\n\n      const firstResult = await this.writeFileChunk(\n        path,\n        content.substring(0, firstChunkEnd),\n        mode\n      );\n\n      if (!firstResult.success) {\n        return firstResult;\n      }\n\n      // Write the remaining chunks with \"append\" mode\n      let chunkCount = 1; // Already wrote first chunk\n      for (let offset = firstChunkEnd; offset < contentLen; ) {\n        const end = Math.min(offset + chunkSize, contentLen);\n\n        const chunkResult = await this.writeFileChunk(\n          path,\n          content.substring(offset, end),\n          \"append\"\n        );\n\n        if (!chunkResult.success) {\n          return chunkResult;\n        }\n\n        offset = end;\n        chunkCount++;\n      }\n\n      return {\n        requestId: firstResult.requestId,\n        success: true,\n        data: true,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        errorMessage: `Failed to write large file: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Get file change information for the specified directory path\n   *\n   * @param path - Directory path to monitor\n   * @returns Promise resolving to result containing detected file changes\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   await result.session.fileSystem.createDirectory('/tmp/watch_dir');\n   *   const changeResult = await result.session.fileSystem.getFileChange('/tmp/watch_dir');\n   *   console.log(`Detected ${changeResult.events.length} changes`);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async getFileChange(path: string): Promise<FileChangeResult> {\n    try {\n      const args = { path };\n      const result = await this.session.callMcpTool(\"get_file_change\", args);\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          events: [],\n          rawData: result.data || \"\",\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      // Parse the file change events\n      const events = this.parseFileChangeData(result.data);\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        events,\n        rawData: result.data,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        events: [],\n        rawData: \"\",\n        errorMessage: `Failed to get file change: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Parse raw JSON data into FileChangeEvent array\n   */\n  private parseFileChangeData(rawData: string): FileChangeEvent[] {\n    const events: FileChangeEvent[] = [];\n    \n    try {\n      const changeData = JSON.parse(rawData);\n      if (Array.isArray(changeData)) {\n        for (const eventDict of changeData) {\n          if (typeof eventDict === \"object\" && eventDict !== null) {\n            const event = FileChangeEventHelper.fromDict(eventDict);\n            events.push(event);\n          }\n        }\n      }\n    } catch (error) {\n      console.warn(`Failed to parse JSON data: ${error}`);\n    }\n    \n    return events;\n  }\n\n  /**\n   * Watch a directory for file changes and call the callback function when changes occur\n   *\n   * @param path - Directory path to monitor\n   * @param callback - Function called when changes are detected\n   * @param interval - Polling interval in milliseconds (default: 500, minimum: 100)\n   * @param signal - Signal to abort the monitoring\n   * @returns Promise that resolves when monitoring stops\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create();\n   * if (result.success) {\n   *   const testDir = '/tmp/watch_test';\n   *   await result.session.fileSystem.createDirectory(testDir);\n   *   const controller = new AbortController();\n   *   const callback = (events) => console.log(`Detected ${events.length} changes`);\n   *   await result.session.fileSystem.watchDirectory(testDir, callback, 1000, controller.signal);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async watchDirectory(\n    path: string,\n    callback: (events: FileChangeEvent[]) => void,\n    interval = 500,\n    signal?: AbortSignal\n  ): Promise<void> {\n    console.log(`Starting directory monitoring for: ${path}`);\n    console.log(`Polling interval: ${interval} ms`);\n\n    const monitor = async () => {\n      while (!signal?.aborted) {\n        try {\n          // Check if session is still valid\n          if ((this.session as any)._isExpired && (this.session as any)._isExpired()) {\n            console.log(`Session expired, stopping directory monitoring for: ${path}`);\n            break;\n          }\n\n          const result = await this.getFileChange(path);\n\n          if (result.success && result.events.length > 0) {\n            console.log(`Detected ${result.events.length} file changes:`);\n            for (const event of result.events) {\n              console.log(`  - ${FileChangeEventHelper.toString(event)}`);\n            }\n\n            try {\n              callback(result.events);\n            } catch (error) {\n              console.error(`Error in callback function: ${error}`);\n            }\n          } else if (!result.success) {\n            // Check if error is due to session expiry\n            const errorMsg = (result.errorMessage || \"\").toLowerCase();\n            if (errorMsg.includes(\"session\") && (errorMsg.includes(\"expired\") || errorMsg.includes(\"invalid\"))) {\n              console.log(`Session expired, stopping directory monitoring for: ${path}`);\n              break;\n            }\n            console.error(`Error monitoring directory: ${result.errorMessage}`);\n          }\n\n          // Wait for next poll\n          await new Promise((resolve) => {\n            const timeoutId = setTimeout(resolve, interval);\n            signal?.addEventListener(\"abort\", () => {\n              clearTimeout(timeoutId);\n              resolve(void 0);\n            });\n          });\n        } catch (error) {\n          console.error(`Unexpected error in directory monitoring: ${error}`);\n          // Check if exception indicates session expiry\n          const errorStr = String(error).toLowerCase();\n          if (errorStr.includes(\"session\") && (errorStr.includes(\"expired\") || errorStr.includes(\"invalid\"))) {\n            console.log(`Session expired, stopping directory monitoring for: ${path}`);\n            break;\n          }\n          await new Promise((resolve) => setTimeout(resolve, interval));\n        }\n      }\n\n      console.log(`Stopped monitoring directory: ${path}`);\n    };\n\n    return monitor();\n  }\n\n  /**\n   * Upload a file from local to remote path using pre-signed URLs.\n   * This is a synchronous wrapper around the FileTransfer.upload method.\n   *\n   * @param localPath - Local file path to upload\n   * @param remotePath - Remote file path to upload to\n   * @param options - Optional parameters\n   * @returns UploadResult with upload result and requestId\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'code_latest' });\n   * if (result.success) {\n   *   const uploadResult = await result.session.fileSystem.uploadFile('/tmp/local.txt', '/workspace/remote.txt');\n   *   console.log('Upload success:', uploadResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async uploadFile(\n    localPath: string,\n    remotePath: string,\n    options?: {\n      contentType?: string;\n      wait?: boolean;\n      waitTimeout?: number;\n      pollInterval?: number;\n      progressCb?: (bytesTransferred: number) => void;\n    }\n  ): Promise<any> {\n    try {\n      // Ensure FileTransfer is initialized\n      const fileTransfer = this._ensureFileTransfer();\n      \n      // Perform upload\n      const result = await fileTransfer.upload(localPath, remotePath, options);\n      \n      // If upload was successful, delete the file from OSS\n      if (result.success && (this.session as any).fileTransferContextId) {\n        const contextId = (this.session as any).fileTransferContextId;\n        if (contextId) {\n          try {\n            // Delete the uploaded file from OSS\n            const deleteResult = await (this.session as any).agentBay.context.deleteFile(contextId, remotePath);\n            if (!deleteResult.success) {\n              logWarn(`Warning: Failed to delete uploaded file from OSS: ${deleteResult}`);\n            }\n          } catch (deleteError: any) {\n            logWarn(`Warning: Error deleting uploaded file from OSS: ${deleteError}`);\n          }\n        }\n      }\n      \n      return result;\n    } catch (error) {\n      return {\n        success: false,\n        bytesSent: 0,\n        path: remotePath,\n        error: `Upload failed: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Download a file from remote path to local path using pre-signed URLs.\n   * This is a synchronous wrapper around the FileTransfer.download method.\n   *\n   * @param remotePath - Remote file path to download from\n   * @param localPath - Local file path to download to\n   * @param options - Optional parameters\n   * @returns DownloadResult with download result and requestId\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const result = await agentBay.create({ imageId: 'code_latest' });\n   * if (result.success) {\n   *   await result.session.fileSystem.writeFile('/workspace/remote.txt', 'Content to download');\n   *   const downloadResult = await result.session.fileSystem.downloadFile('/workspace/remote.txt', '/tmp/local.txt');\n   *   console.log('Download success:', downloadResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async downloadFile(\n    remotePath: string,\n    localPath: string,\n    options?: {\n      overwrite?: boolean;\n      wait?: boolean;\n      waitTimeout?: number;\n      pollInterval?: number;\n      progressCb?: (bytesReceived: number) => void;\n    }\n  ): Promise<any> {\n    try {\n      // Ensure FileTransfer is initialized\n      const fileTransfer = this._ensureFileTransfer();\n      \n      // Perform download\n      const result = await fileTransfer.download(remotePath, localPath, options);\n      \n      // If download was successful, delete the file from OSS\n      if (result.success && (this.session as any).fileTransferContextId) {\n        const contextId = (this.session as any).fileTransferContextId;\n        if (contextId) {\n          try {\n            // Delete the downloaded file from OSS\n            const deleteResult = await (this.session as any).agentBay.context.deleteFile(contextId, remotePath);\n            if (!deleteResult.success) {\n              logWarn(`Warning: Failed to delete downloaded file from OSS: ${deleteResult}`);\n            }\n          } catch (deleteError: any) {\n            logWarn(`Warning: Error deleting downloaded file from OSS: ${deleteError}`);\n          }\n        }\n      }\n      \n      return result;\n    } catch (error) {\n      return {\n        success: false,\n        bytesReceived: 0,\n        path: remotePath,\n        localPath,\n        error: `Download failed: ${error}`,\n      };\n    }\n  }\n}\n","import { AgentBay } from \"../agent-bay\";\nimport { Session } from \"../session\";\nimport { ContextService } from \"../context\";\nimport { FileUrlResult, OperationResult } from \"../types/api-response\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport fetch from \"node-fetch\";\nimport { log, logDebug, logInfo } from \"../utils/logger\";\n\n/**\n * Result structure for file upload operations.\n */\nexport interface UploadResult {\n  /** Whether the upload was successful */\n  success: boolean;\n  /** Request ID for getting the upload URL */\n  requestIdUploadUrl?: string;\n  /** Request ID for sync operation */\n  requestIdSync?: string;\n  /** HTTP status code */\n  httpStatus?: number;\n  /** ETag of the uploaded file */\n  etag?: string;\n  /** Number of bytes sent */\n  bytesSent: number;\n  /** Remote file path */\n  path: string;\n  /** Error message if upload failed */\n  error?: string;\n}\n\n/**\n * Result structure for file download operations.\n */\nexport interface DownloadResult {\n  /** Whether the download was successful */\n  success: boolean;\n  /** Request ID for getting the download URL */\n  requestIdDownloadUrl?: string;\n  /** Request ID for sync operation */\n  requestIdSync?: string;\n  /** HTTP status code */\n  httpStatus?: number;\n  /** Number of bytes received */\n  bytesReceived: number;\n  /** Remote file path */\n  path: string;\n  /** Local file path where file was saved */\n  localPath: string;\n  /** Error message if download failed */\n  error?: string;\n}\n\n/**\n * FileTransfer provides pre-signed URL upload/download functionality between local and OSS,\n * with integration to Session Context synchronization.\n * \n * Prerequisites and Constraints:\n * - Session must be associated with the corresponding context_id and path through \n *   CreateSessionParams.contextSyncs, and remotePath should fall within that \n *   synchronization path (or conform to backend path rules).\n * - Requires available AgentBay context service (agentBay.context) and session context.\n */\nexport class FileTransfer {\n  private agentBay: AgentBay;\n  private contextSvc: ContextService;\n  private session: Session;\n  private httpTimeout: number;\n  private followRedirects: boolean;\n  private contextId: string;\n\n  // Task completion states (for compatibility)\n  private finishedStates = new Set([\"success\", \"successful\", \"ok\", \"finished\", \"done\", \"completed\", \"complete\"]);\n\n  /**\n   * Initialize FileTransfer with AgentBay client and session.\n   * \n   * @param agentBay - AgentBay instance for context service access\n   * @param session - Created session object for context operations\n   * @param httpTimeout - HTTP request timeout in seconds (default: 60.0)\n   * @param followRedirects - Whether to follow HTTP redirects (default: true)\n   */\n  constructor(\n    agentBay: AgentBay,\n    session: Session,\n    httpTimeout = 60.0,\n    followRedirects = true\n  ) {\n    this.agentBay = agentBay;\n    this.contextSvc = agentBay.context;\n    this.session = session;\n    this.httpTimeout = httpTimeout;\n    this.followRedirects = followRedirects;\n    this.contextId = session.fileTransferContextId || \"\";\n  }\n\n  /**\n   * Upload workflow:\n   * 1) Get OSS pre-signed URL via context.getFileUploadUrl\n   * 2) Upload local file to OSS using the URL (HTTP PUT)\n   * 3) Trigger session.context.sync(mode=\"download\") to sync OSS objects to cloud disk\n   * 4) If wait=true, poll session.context.info until upload task reaches completion or timeout\n   *\n   * Returns UploadResult containing request_ids, HTTP status, ETag and other information.\n   */\n  async upload(\n    localPath: string,\n    remotePath: string,\n    options?: {\n      contentType?: string;\n      wait?: boolean;\n      waitTimeout?: number;\n      pollInterval?: number;\n      progressCb?: (bytesTransferred: number) => void;\n    }\n  ): Promise<UploadResult> {\n    const {\n      contentType = null,\n      wait = true,\n      waitTimeout = 30.0,\n      pollInterval = 1.5,\n      progressCb = undefined\n    } = options || {};\n\n    try {\n      // 0. Parameter validation\n      if (!fs.existsSync(localPath)) {\n        return {\n          success: false,\n          bytesSent: 0,\n          path: remotePath,\n          error: `Local file not found: ${localPath}`\n        };\n      }\n\n      if (!this.contextId) {\n        return {\n          success: false,\n          bytesSent: 0,\n          path: remotePath,\n          error: \"No context ID\"\n        };\n      }\n\n      // 1. Get pre-signed upload URL\n      const urlRes = await this.contextSvc.getFileUploadUrl(this.contextId, remotePath);\n      if (!urlRes.success || !urlRes.url) {\n        return {\n          success: false,\n          requestIdUploadUrl: urlRes.requestId,\n          bytesSent: 0,\n          path: remotePath,\n          error: `getFileUploadUrl failed: ${urlRes.url || \"unknown error\"}`\n        };\n      }\n\n      const uploadUrl = urlRes.url;\n      const reqIdUpload = urlRes.requestId;\n\n      logDebug(`Uploading ${localPath} to ${uploadUrl}`);\n\n      // 2. PUT upload to pre-signed URL\n      try {\n        const { httpStatus, etag, bytesSent } = await this.putFile(\n          uploadUrl,\n          localPath,\n          contentType,\n          progressCb\n        );\n        \n        logInfo(`Upload completed with HTTP ${httpStatus}`);\n        if (httpStatus && ![200, 201, 204].includes(httpStatus)) {\n          return {\n            success: false,\n            requestIdUploadUrl: reqIdUpload,\n            httpStatus,\n            etag,\n            bytesSent,\n            path: remotePath,\n            error: `Upload failed with HTTP ${httpStatus}`\n          };\n        }\n      } catch (e: any) {\n        return {\n          success: false,\n          requestIdUploadUrl: reqIdUpload,\n          bytesSent: 0,\n          path: remotePath,\n          error: `Upload exception: ${e.message || e}`\n        };\n      }\n\n      // 3. Trigger sync to cloud disk (download mode), download from oss to cloud disk\n      let reqIdSync: string | undefined;\n      try {\n        logDebug(\"Triggering sync to cloud disk\");\n        reqIdSync = await this.awaitSync(\"download\", remotePath, this.contextId);\n      } catch (e: any) {\n        return {\n          success: false,\n          requestIdUploadUrl: reqIdUpload,\n          requestIdSync: reqIdSync,\n          httpStatus: 200, // Assuming previous step succeeded\n          etag: \"\", // Assuming previous step succeeded\n          bytesSent: fs.statSync(localPath).size, // Assuming previous step succeeded\n          path: remotePath,\n          error: `session.context.sync(upload) failed: ${e.message || e}`\n        };\n      }\n\n      logInfo(`Sync request ID: ${reqIdSync}`);\n      \n      // 4. Optionally wait for task completion\n      if (wait) {\n        const { success, error } = await this.waitForTask({\n          contextId: this.contextId,\n          remotePath,\n          taskType: \"download\",\n          timeout: waitTimeout,\n          interval: pollInterval\n        });\n        \n        if (!success) {\n          return {\n            success: false,\n            requestIdUploadUrl: reqIdUpload,\n            requestIdSync: reqIdSync,\n            httpStatus: 200, // Assuming previous step succeeded\n            etag: \"\", // Assuming previous step succeeded\n            bytesSent: fs.statSync(localPath).size, // Assuming previous step succeeded\n            path: remotePath,\n            error: `Upload sync not finished: ${error || \"timeout or unknown\"}`\n          };\n        }\n      }\n\n      return {\n        success: true,\n        requestIdUploadUrl: reqIdUpload,\n        requestIdSync: reqIdSync,\n        httpStatus: 200,\n        etag: \"\",\n        bytesSent: fs.statSync(localPath).size,\n        path: remotePath\n      };\n    } catch (e: any) {\n      return {\n        success: false,\n        bytesSent: 0,\n        path: remotePath,\n        error: `Upload failed: ${e.message || e}`\n      };\n    }\n  }\n\n  /**\n   * Download workflow:\n   * 1) Trigger session.context.sync(mode=\"upload\") to sync cloud disk data to OSS\n   * 2) Get pre-signed download URL via context.getFileDownloadUrl\n   * 3) Download the file and save to local localPath\n   * 4) If wait=true, wait for download task to reach completion after step 1 \n   *    (ensuring backend has prepared the download object)\n   *\n   * Returns DownloadResult containing sync and download request_ids, HTTP status, byte count, etc.\n   */\n  async download(\n    remotePath: string,\n    localPath: string,\n    options?: {\n      overwrite?: boolean;\n      wait?: boolean;\n      waitTimeout?: number;\n      pollInterval?: number;\n      progressCb?: (bytesReceived: number) => void;\n    }\n  ): Promise<DownloadResult> {\n    const {\n      overwrite = true,\n      wait = true,\n      waitTimeout = 30.0,\n      pollInterval = 1.5,\n      progressCb = undefined\n    } = options || {};\n\n    try {\n      // Use default context if none provided\n      if (!this.contextId) {\n        return {\n          success: false,\n          bytesReceived: 0,\n          path: remotePath,\n          localPath,\n          error: \"No context ID\"\n        };\n      }\n\n      // 1. Trigger cloud disk to OSS download sync\n      let reqIdSync: string | undefined;\n      try {\n        reqIdSync = await this.awaitSync(\"upload\", remotePath, this.contextId);\n      } catch (e: any) {\n        return {\n          success: false,\n          requestIdSync: reqIdSync,\n          bytesReceived: 0,\n          path: remotePath,\n          localPath,\n          error: `session.context.sync(download) failed: ${e.message || e}`\n        };\n      }\n\n      // Optionally wait for task completion (ensure object is ready in OSS)\n      if (wait) {\n        const { success, error } = await this.waitForTask({\n          contextId: this.contextId,\n          remotePath,\n          taskType: \"upload\",\n          timeout: waitTimeout,\n          interval: pollInterval\n        });\n        \n        if (!success) {\n          return {\n            success: false,\n            requestIdSync: reqIdSync,\n            bytesReceived: 0,\n            path: remotePath,\n            localPath,\n            error: `Download sync not finished: ${error || \"timeout or unknown\"}`\n          };\n        }\n      }\n\n      // 2. Get pre-signed download URL\n      const urlRes = await this.contextSvc.getFileDownloadUrl(this.contextId, remotePath);\n      if (!urlRes.success || !urlRes.url) {\n        return {\n          success: false,\n          requestIdDownloadUrl: urlRes.requestId,\n          requestIdSync: reqIdSync,\n          bytesReceived: 0,\n          path: remotePath,\n          localPath,\n          error: `getFileDownloadUrl failed: ${urlRes.url || \"unknown error\"}`\n        };\n      }\n\n      const downloadUrl = urlRes.url;\n      const reqIdDownload = urlRes.requestId;\n\n      // 3. Download and save to local\n      try {\n        // Ensure directory exists\n        const dir = path.dirname(localPath);\n        if (!fs.existsSync(dir)) {\n          fs.mkdirSync(dir, { recursive: true });\n        }\n\n        if (fs.existsSync(localPath) && !overwrite) {\n          return {\n            success: false,\n            requestIdDownloadUrl: reqIdDownload,\n            requestIdSync: reqIdSync,\n            bytesReceived: 0,\n            path: remotePath,\n            localPath,\n            error: `Destination exists and overwrite=false: ${localPath}`\n          };\n        }\n\n        const bytesReceived = await this.getFile(\n          downloadUrl,\n          localPath,\n          progressCb\n        );\n\n        if (fs.existsSync(localPath)) {\n          return {\n            success: true,\n            requestIdDownloadUrl: reqIdDownload,\n            requestIdSync: reqIdSync,\n            httpStatus: 200,\n            bytesReceived: fs.statSync(localPath).size,\n            path: remotePath,\n            localPath\n          };\n        } else {\n          return {\n            success: false,\n            requestIdDownloadUrl: reqIdDownload,\n            requestIdSync: reqIdSync,\n            bytesReceived,\n            path: remotePath,\n            localPath,\n            error: \"Download completed but file not found\"\n          };\n        }\n      } catch (e: any) {\n        return {\n          success: false,\n          requestIdDownloadUrl: reqIdDownload,\n          requestIdSync: reqIdSync,\n          bytesReceived: 0,\n          path: remotePath,\n          localPath,\n          error: `Download exception: ${e.message || e}`\n        };\n      }\n    } catch (e: any) {\n      return {\n        success: false,\n        bytesReceived: 0,\n        path: remotePath,\n        localPath,\n        error: `Download failed: ${e.message || e}`\n      };\n    }\n  }\n\n  // ========== Internal Utilities ==========\n\n  /**\n   * Compatibility wrapper for session.context.sync which may be sync or async:\n   * - Try async call first\n   * - Fall back to sync call\n   * Returns request_id if available\n   */\n  private async awaitSync(mode: string, remotePath = \"\", contextId = \"\"): Promise<string | undefined> {\n    mode = mode.toLowerCase().trim();\n\n    // Check if session has context property\n    if (!this.session.context) {\n      throw new Error(\"Session does not have context property\");\n    }\n\n    const syncFn = this.session.context.sync.bind(this.session.context);\n    logDebug(`session.context.sync(mode=${mode}, path=${remotePath}, contextId=${contextId})`);\n    \n    // Try calling with all parameters\n    try {\n      const result = await syncFn(contextId || undefined, remotePath || undefined, mode);\n      logDebug(`   Result: ${result.success}`);\n      return result.requestId;\n    } catch (e1) {\n      // Backend may not support all parameters, try with mode and path only\n      try {\n        const result = await syncFn(undefined, remotePath || undefined, mode);\n        logDebug(`   Result: ${result.success}`);\n        return result.requestId;\n      } catch (e2) {\n        // Backend may not support mode or path parameter\n        try {\n          const result = await syncFn(undefined, undefined, mode);\n          logDebug(`   Result: ${result.success}`);\n          return result.requestId;\n        } catch (e3) {\n          // Backend may not support mode parameter\n          const result = await syncFn();\n          logDebug(`   Result: ${result.success}`);\n          return result.requestId;\n        }\n      }\n    }\n  }\n\n  /**\n   * Poll session.context.info within timeout to check if specified task is completed.\n   * Returns { success, error } object.\n   */\n  private async waitForTask(options: {\n    contextId: string;\n    remotePath: string;\n    taskType?: string;\n    timeout: number;\n    interval: number;\n  }): Promise<{ success: boolean; error?: string }> {\n    const { contextId, remotePath, taskType, timeout, interval } = options;\n    const deadline = Date.now() + timeout * 1000;\n    let lastErr: string | null = null;\n\n    while (Date.now() < deadline) {\n      try {\n        // Check if session has context property\n        if (!this.session.context) {\n          throw new Error(\"Session does not have context property\");\n        }\n\n        const infoFn = this.session.context.infoWithParams.bind(this.session.context);\n        // Try calling with filter parameters\n        let res;\n        try {\n          res = await infoFn(contextId, remotePath, taskType);\n        } catch (e1) {\n          try {\n            res = await infoFn();\n          } catch (e2) {\n            // If all attempts fail, re-throw the last error\n            throw e2;\n          }\n        }\n\n        // Parse response\n        const statusList = res.contextStatusData || [];\n        for (const item of statusList) {\n          const cid = item.contextId;\n          const path = item.path;\n          const ttype = item.taskType;\n          const status = item.status;\n          const err = item.errorMessage;\n\n          if (cid === contextId && path === remotePath && (taskType === undefined || ttype === taskType)) {\n            if (err) {\n              return { success: false, error: `Task error: ${err}` };\n            }\n            if (status && this.finishedStates.has(status.toLowerCase())) {\n              return { success: true };\n            }\n            // Otherwise continue waiting\n          }\n        }\n        lastErr = \"task not finished\";\n      } catch (e: any) {\n        lastErr = `info error: ${e.message || e}`;\n      }\n\n      // Wait before next poll\n      await new Promise(resolve => setTimeout(resolve, interval * 1000));\n    }\n\n    return { success: false, error: lastErr || \"timeout\" };\n  }\n\n  /**\n   * Synchronously PUT file using node-fetch.\n   * Returns { status, etag, bytesSent }\n   */\n  private async putFile(\n    url: string,\n    filePath: string,\n    contentType: string | null,\n    progressCb?: (bytesTransferred: number) => void\n  ): Promise<{ httpStatus: number; etag?: string; bytesSent: number }> {\n    const headers: Record<string, string> = {};\n    if (contentType) {\n      headers[\"Content-Type\"] = contentType;\n    }\n\n    const fileBuffer = fs.readFileSync(filePath);\n    const response = await fetch(url, {\n      method: \"PUT\",\n      body: fileBuffer,\n      headers\n    });\n\n    const status = response.status;\n    const etag = response.headers.get(\"ETag\") || undefined;\n    const bytesSent = fileBuffer.length;\n\n    if (progressCb) {\n      progressCb(bytesSent);\n    }\n\n    return { httpStatus: status, etag, bytesSent };\n  }\n\n  /**\n   * Synchronously GET download to local file using node-fetch.\n   * Returns bytesReceived\n   */\n  private async getFile(\n    url: string,\n    destPath: string,\n    progressCb?: (bytesReceived: number) => void\n  ): Promise<number> {\n    let bytesReceived = 0;\n    \n    const response = await fetch(url);\n    const status = response.status;\n    \n    if (status !== 200) {\n      throw new Error(`HTTP ${status}`);\n    }\n\n    const buffer = await response.buffer();\n    bytesReceived = buffer.length;\n    \n    // Save to file\n    fs.writeFileSync(destPath, buffer);\n    \n    if (progressCb) {\n      progressCb(bytesReceived);\n    }\n\n    return bytesReceived;\n  }\n}","export { \n  Mobile, \n  BoolResult, \n  UIElement, \n  UIElementsResult, \n  InstalledApp, \n  InstalledAppsResult, \n  Process, \n  ProcessResult, \n  ScreenshotResult \n} from './mobile'; ","/**\n * Mobile module for mobile UI automation.\n * Provides touch, input, and app management operations for mobile environments.\n */\n\nimport { OperationResult } from \"../types/api-response\";\nimport { MobileExtraConfig } from \"../types/extra-configs\";\nimport { log, logError } from \"../utils/logger\";\nimport { getMobileCommandTemplate, replaceTemplatePlaceholders } from \"../command/command-templates\";\n\nexport interface BoolResult extends OperationResult {\n  data?: boolean;\n}\n\nexport interface UIBounds {\n  left: number;\n  top: number;\n  right: number;\n  bottom: number;\n}\n\nexport interface UIElement {\n  text: string;\n  className: string;\n  bounds: UIBounds | string;  // Can be either object or string format \"left,top,right,bottom\"\n}\n\nexport interface UIElementsResult extends OperationResult {\n  elements: UIElement[];\n}\n\nexport interface InstalledApp {\n  name: string;\n  startCmd: string;\n  workDirectory: string;\n}\n\nexport interface InstalledAppsResult extends OperationResult {\n  apps: InstalledApp[];\n}\n\nexport interface Process {\n  pid: number;\n  pname: string;\n}\n\nexport interface ProcessResult extends OperationResult {\n  processes: Process[];\n}\n\nexport interface ScreenshotResult extends OperationResult {\n  data: string; // Screenshot URL\n}\n\nexport interface AdbUrlResult extends OperationResult {\n  data?: string; // ADB connection URL (e.g., \"adb connect xx.xx.xx.xx:xxxxx\")\n  url?: string; // Alternative field name for compatibility\n}\n\n// Session interface for Mobile module\ninterface MobileSession {\n  callMcpTool(toolName: string, args: Record<string, any>): Promise<any>;\n  sessionId: string;\n  getAPIKey(): string;\n  getAgentBay(): any;\n  imageId?: string;\n  getLink(protocolType?: string, port?: number, options?: string): Promise<any>;\n}\n\n/**\n * Parse bounds string format \"left,top,right,bottom\" to UIBounds object\n */\nfunction parseBoundsString(boundsStr: string): UIBounds | null {\n  const parts = boundsStr.split(',');\n  if (parts.length !== 4) {\n    return null;\n  }\n\n  const [left, top, right, bottom] = parts.map(p => parseInt(p.trim(), 10));\n  if (isNaN(left) || isNaN(top) || isNaN(right) || isNaN(bottom)) {\n    return null;\n  }\n\n  return { left, top, right, bottom };\n}\n\n/**\n * Normalize UIElement bounds field from string to object format if needed\n */\nfunction normalizeUIElement(element: any): UIElement {\n  if (element.bounds && typeof element.bounds === 'string') {\n    const parsedBounds = parseBoundsString(element.bounds);\n    if (parsedBounds) {\n      element.bounds = parsedBounds;\n    }\n  }\n\n  // Recursively normalize children if present\n  if (element.children && Array.isArray(element.children)) {\n    element.children = element.children.map(normalizeUIElement);\n  }\n\n  return element;\n}\n\nexport class Mobile {\n  private session: MobileSession;\n\n  constructor(session: MobileSession) {\n    this.session = session;\n  }\n\n\n  /**\n   * Tap at specified coordinates on the mobile screen.\n   *\n   * @param x - X coordinate for the tap\n   * @param y - Y coordinate for the tap\n   * @returns Promise resolving to BoolResult with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'mobile_latest' });\n   * if (result.success) {\n   *   const tapResult = await result.session.mobile.tap(100, 100);\n   *   console.log('Tap success:', tapResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async tap(x: number, y: number): Promise<BoolResult> {\n    const args = { x, y };\n    try {\n      const result = await this.session.callMcpTool('tap', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to tap: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Swipe from one position to another on the mobile screen.\n   *\n   * @param startX - Starting X coordinate\n   * @param startY - Starting Y coordinate\n   * @param endX - Ending X coordinate\n   * @param endY - Ending Y coordinate\n   * @param durationMs - Swipe duration in milliseconds. Default is 300\n   * @returns Promise resolving to BoolResult with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'mobile_latest' });\n   * if (result.success) {\n   *   const swipeResult = await result.session.mobile.swipe(200, 400, 200, 100, 300);\n   *   console.log('Swipe success:', swipeResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async swipe(startX: number, startY: number, endX: number, endY: number, durationMs = 300): Promise<BoolResult> {\n    const args = { start_x: startX, start_y: startY, end_x: endX, end_y: endY, duration_ms: durationMs };\n    try {\n      const result = await this.session.callMcpTool('swipe', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to swipe: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Input text at the current focus position.\n   *\n   * @param text - Text to input\n   * @returns Promise resolving to BoolResult with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'mobile_latest' });\n   * if (result.success) {\n   *   const inputResult = await result.session.mobile.inputText('Hello Mobile');\n   *   console.log('Text input successfully:', inputResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async inputText(text: string): Promise<BoolResult> {\n    const args = { text };\n    try {\n      const result = await this.session.callMcpTool('input_text', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to input text: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Send Android key code.\n   *\n   * @param key - Android key code (e.g., 4 for BACK, 3 for HOME, 24 for VOLUME_UP)\n   * @returns Promise resolving to BoolResult with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'mobile_latest' });\n   * if (result.success) {\n   *   const keyResult = await result.session.mobile.sendKey(4);\n   *   console.log('BACK key sent:', keyResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async sendKey(key: number): Promise<BoolResult> {\n    const args = { key };\n    try {\n      const result = await this.session.callMcpTool('send_key', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to send key: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Get clickable UI elements.\n   */\n  async getClickableUIElements(timeoutMs = 5000): Promise<UIElementsResult> {\n    const args = { timeout_ms: timeoutMs };\n    try {\n      const result = await this.session.callMcpTool('get_clickable_ui_elements', args);\n\n      if (!result.success) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: result.errorMessage || 'Failed to get clickable UI elements',\n          elements: []\n        };\n      }\n\n      if (!result.data) {\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: '',\n          elements: []\n        };\n      }\n\n      try {\n        const elements = JSON.parse(result.data);\n        // Normalize bounds from string to object format if needed\n        const normalizedElements = (elements || []).map(normalizeUIElement);\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: '',\n          elements: normalizedElements\n        };\n      } catch (parseError) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: `Failed to parse UI elements: ${parseError}`,\n          elements: []\n        };\n      }\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to get clickable UI elements: ${error instanceof Error ? error.message : String(error)}`,\n        elements: []\n      };\n    }\n  }\n\n  /**\n   * Get all UI elements.\n   */\n  async getAllUIElements(timeoutMs = 3000): Promise<UIElementsResult> {\n    const args = { timeout_ms: timeoutMs };\n    try {\n      const result = await this.session.callMcpTool('get_all_ui_elements', args);\n\n      if (!result.success) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: result.errorMessage || 'Failed to get all UI elements',\n          elements: []\n        };\n      }\n\n      if (!result.data) {\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: '',\n          elements: []\n        };\n      }\n\n      try {\n        const elements = JSON.parse(result.data);\n        // Normalize bounds from string to object format if needed\n        const normalizedElements = (elements || []).map(normalizeUIElement);\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: '',\n          elements: normalizedElements\n        };\n      } catch (parseError) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: `Failed to parse UI elements: ${parseError}`,\n          elements: []\n        };\n      }\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to get all UI elements: ${error instanceof Error ? error.message : String(error)}`,\n        elements: []\n      };\n    }\n  }\n\n  /**\n   * Get installed apps.\n   */\n  async getInstalledApps(startMenu = false, desktop = true, ignoreSystemApps = true): Promise<InstalledAppsResult> {\n    const args = { start_menu: startMenu, desktop, ignore_system_apps: ignoreSystemApps };\n    try {\n      const result = await this.session.callMcpTool('get_installed_apps', args);\n      \n      if (!result.success) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: result.errorMessage || 'Failed to get installed apps',\n          apps: []\n        };\n      }\n\n      if (!result.data) {\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: '',\n          apps: []\n        };\n      }\n\n      try {\n        const apps = JSON.parse(result.data);\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: '',\n          apps: apps || []\n        };\n      } catch (parseError) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: `Failed to parse installed apps: ${parseError}`,\n          apps: []\n        };\n      }\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to get installed apps: ${error instanceof Error ? error.message : String(error)}`,\n        apps: []\n      };\n    }\n  }\n\n  /**\n   * Start an app.\n   *\n   * @param startCmd - Start command using \"monkey -p\" format (e.g., 'monkey -p com.android.settings')\n   * @param workDirectory - Optional working directory for the app\n   * @param activity - Optional activity name to launch\n   * @returns Promise resolving to ProcessResult containing launched process information\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'mobile_latest' });\n   * if (result.success) {\n   *   const startResult = await result.session.mobile.startApp('monkey -p com.android.settings');\n   *   console.log('App started:', startResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async startApp(startCmd: string, workDirectory = '', activity = ''): Promise<ProcessResult> {\n    const args = { start_cmd: startCmd, work_directory: workDirectory, activity };\n    try {\n      const result = await this.session.callMcpTool('start_app', args);\n      \n      if (!result.success) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: result.errorMessage || 'Failed to start app',\n          processes: []\n        };\n      }\n\n      if (!result.data) {\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: '',\n          processes: []\n        };\n      }\n\n      try {\n        const processes = JSON.parse(result.data);\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: '',\n          processes: processes || []\n        };\n      } catch (parseError) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: `Failed to parse process result: ${parseError}`,\n          processes: []\n        };\n      }\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to start app: ${error instanceof Error ? error.message : String(error)}`,\n        processes: []\n      };\n    }\n  }\n\n  /**\n   * Stop app by command.\n   *\n   * @param stopCmd - Package name of the app to stop (e.g., 'com.android.settings')\n   * @returns Promise resolving to BoolResult with success status\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'mobile_latest' });\n   * if (result.success) {\n   *   await result.session.mobile.startApp('monkey -p com.android.settings');\n   *   const stopResult = await result.session.mobile.stopAppByCmd('com.android.settings');\n   *   console.log('App stopped:', stopResult.success);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async stopAppByCmd(stopCmd: string): Promise<BoolResult> {\n    const args = { stop_cmd: stopCmd };\n    try {\n      const result = await this.session.callMcpTool('stop_app_by_cmd', args);\n      return {\n        success: result.success || false,\n        requestId: result.requestId || '',\n        errorMessage: result.errorMessage || '',\n        data: result.success || false\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to stop app: ${error instanceof Error ? error.message : String(error)}`,\n        data: false\n      };\n    }\n  }\n\n  /**\n   * Take a screenshot of the current mobile screen.\n   *\n   * @returns Promise resolving to ScreenshotResult containing screenshot URL\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: process.env.AGENTBAY_API_KEY });\n   * const result = await agentBay.create({ imageId: 'mobile_latest' });\n   * if (result.success) {\n   *   const screenshotResult = await result.session.mobile.screenshot();\n   *   console.log('Screenshot URL:', screenshotResult.data);\n   *   await result.session.delete();\n   * }\n   * ```\n   */\n  async screenshot(): Promise<ScreenshotResult> {\n    try {\n      const result = await this.session.callMcpTool('system_screenshot', {});\n      \n      if (!result.success) {\n        return {\n          success: false,\n          requestId: result.requestId || '',\n          errorMessage: result.errorMessage || 'Failed to take screenshot',\n          data: ''\n        };\n      }\n\n      return {\n        success: true,\n        requestId: result.requestId || '',\n        errorMessage: '',\n        data: result.data || ''\n      };\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to take screenshot: ${error instanceof Error ? error.message : String(error)}`,\n        data: ''\n      };\n    }\n  }\n\n  /**\n   * Retrieves the ADB connection URL for the mobile environment.\n   * This method is only supported in mobile environments (mobile_latest image).\n   * It uses the provided ADB public key to establish the connection and returns\n   * the ADB connect URL.\n   * \n   * @param adbkeyPub - ADB public key for authentication\n   * @returns AdbUrlResult containing the ADB connection URL\n   */\n  async getAdbUrl(adbkeyPub: string): Promise<AdbUrlResult> {\n    try {\n      // Build options JSON with adbkey_pub\n      const optionsJson = JSON.stringify({ adbkey_pub: adbkeyPub });\n\n      // Call get_adb_link API\n      const { GetAdbLinkRequest } = await import('../api/models/model');\n      const request = new GetAdbLinkRequest({\n        authorization: `Bearer ${this.session.getAPIKey()}`,\n        sessionId: this.session.sessionId,\n        option: optionsJson\n      });\n\n      const response = await this.session.getAgentBay().getClient().getAdbLink(request);\n\n      // Check response\n      if (response.body && response.body.success && response.body.data) {\n        const adbUrl = response.body.data.url;\n        const requestId = response.body.requestId || '';\n        log(`✅ get_adb_url completed successfully. RequestID: ${requestId}`);\n        return {\n          success: true,\n          requestId: requestId,\n          errorMessage: '',\n          data: adbUrl,\n          url: adbUrl\n        };\n      } else {\n        const errorMsg = response.body?.message || 'Unknown error';\n        const requestId = response.body?.requestId || '';\n        logError(`❌ Failed to get ADB URL: ${errorMsg}`);\n        return {\n          success: false,\n          requestId: requestId,\n          errorMessage: errorMsg,\n          data: undefined,\n          url: undefined\n        };\n      }\n    } catch (error) {\n      const errorMsg = `Failed to get ADB URL: ${error instanceof Error ? error.message : String(error)}`;\n      logError(`❌ ${errorMsg}`);\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: errorMsg,\n        data: undefined,\n        url: undefined\n      };\n    }\n  }\n\n  /**\n   * Configure mobile device settings based on MobileExtraConfig.\n   * This method applies various mobile configuration settings including\n   * resolution lock and app access management.\n   * \n   * @param config - The mobile configuration to apply\n   * @returns OperationResult indicating success or failure\n   */\n  async configure(config: MobileExtraConfig): Promise<OperationResult> {\n    try {\n      if (!config) {\n        return {\n          success: false,\n          requestId: '',\n          errorMessage: 'No mobile configuration provided'\n        };\n      }\n\n      // Configure resolution lock\n      const resolutionResult = await this.setResolutionLock(config.lockResolution);\n      if (!resolutionResult.success) {\n        return {\n          success: false,\n          requestId: resolutionResult.requestId,\n          errorMessage: `Failed to set resolution lock: ${resolutionResult.errorMessage}`\n        };\n      }\n\n      // Configure app management rules\n      if (config.appManagerRule && config.appManagerRule.ruleType) {\n        const appRule = config.appManagerRule;\n        const packageNames = appRule.appPackageNameList;\n\n        if (packageNames && packageNames.length > 0 && \n            (appRule.ruleType === \"White\" || appRule.ruleType === \"Black\")) {\n          \n          let appResult: OperationResult;\n          if (appRule.ruleType === \"White\") {\n            appResult = await this.setAppWhitelist(packageNames);\n          } else {\n            appResult = await this.setAppBlacklist(packageNames);\n          }\n\n          if (!appResult.success) {\n            return {\n              success: false,\n              requestId: appResult.requestId,\n              errorMessage: `Failed to set app ${appRule.ruleType.toLowerCase()}list: ${appResult.errorMessage}`\n            };\n          }\n        } else if (packageNames && packageNames.length === 0) {\n          return {\n            success: false,\n            requestId: '',\n            errorMessage: `No package names provided for ${appRule.ruleType} list`\n          };\n        }\n      }\n\n      // Configure navigation bar visibility\n      if (config.hideNavigationBar !== undefined) {\n        const navResult = await this.setNavigationBarVisibility(config.hideNavigationBar);\n        if (!navResult.success) {\n          return {\n            success: false,\n            requestId: navResult.requestId,\n            errorMessage: `Failed to set navigation bar visibility: ${navResult.errorMessage}`\n          };\n        }\n      }\n\n      // Configure uninstall blacklist\n      if (config.uninstallBlacklist && config.uninstallBlacklist.length > 0) {\n        const uninstallResult = await this.setUninstallBlacklist(config.uninstallBlacklist);\n        if (!uninstallResult.success) {\n          return {\n            success: false,\n            requestId: uninstallResult.requestId,\n            errorMessage: `Failed to set uninstall blacklist: ${uninstallResult.errorMessage}`\n          };\n        }\n      }\n\n      log(\"Mobile configuration applied successfully\");\n      return {\n        success: true,\n        requestId: '',\n        errorMessage: ''\n      };\n    } catch (error) {\n      const errorMsg = `Failed to configure mobile: ${error instanceof Error ? error.message : String(error)}`;\n      logError(errorMsg);\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: errorMsg\n      };\n    }\n  }\n\n  /**\n   * Set display resolution lock for mobile devices.\n   * \n   * @param enable - Whether to enable resolution lock\n   * @returns OperationResult indicating success or failure\n   */\n  async setResolutionLock(enable: boolean): Promise<OperationResult> {\n    try {\n      const templateName = enable ? \"resolution_lock_enable\" : \"resolution_lock_disable\";\n      const template = getMobileCommandTemplate(templateName);\n      \n      if (!template) {\n        return {\n          success: false,\n          requestId: '',\n          errorMessage: `Resolution lock template not found: ${templateName}`\n        };\n      }\n\n      const description = `Resolution lock ${enable ? 'enable' : 'disable'}`;\n      return await this.executeCommand(template, description);\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to set resolution lock: ${error instanceof Error ? error.message : String(error)}`\n      };\n    }\n  }\n\n  /**\n   * Set app whitelist configuration.\n   * \n   * @param packageNames - List of package names to whitelist\n   * @returns OperationResult indicating success or failure\n   */\n  async setAppWhitelist(packageNames: string[]): Promise<OperationResult> {\n    try {\n      const template = getMobileCommandTemplate(\"app_whitelist\");\n      if (!template) {\n        return {\n          success: false,\n          requestId: '',\n          errorMessage: \"App whitelist template not found\"\n        };\n      }\n\n      // Replace placeholder with actual package names (newline-separated for file content)\n      const packageList = packageNames.join('\\n');\n      const command = replaceTemplatePlaceholders(template, { package_list: packageList });\n      \n      const description = `App whitelist configuration (${packageNames.length} packages)`;\n      return await this.executeCommand(command, description);\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to set app whitelist: ${error instanceof Error ? error.message : String(error)}`\n      };\n    }\n  }\n\n  /**\n   * Set app blacklist configuration.\n   * \n   * @param packageNames - List of package names to blacklist\n   * @returns OperationResult indicating success or failure\n   */\n  async setAppBlacklist(packageNames: string[]): Promise<OperationResult> {\n    try {\n      const template = getMobileCommandTemplate(\"app_blacklist\");\n      if (!template) {\n        return {\n          success: false,\n          requestId: '',\n          errorMessage: \"App blacklist template not found\"\n        };\n      }\n\n      // Replace placeholder with actual package names (newline-separated for file content)\n      const packageList = packageNames.join('\\n');\n      const command = replaceTemplatePlaceholders(template, { package_list: packageList });\n      \n      const description = `App blacklist configuration (${packageNames.length} packages)`;\n      return await this.executeCommand(command, description);\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to set app blacklist: ${error instanceof Error ? error.message : String(error)}`\n      };\n    }\n  }\n\n  /**\n   * Set navigation bar visibility for mobile devices.\n   * \n   * @param hide - True to hide navigation bar, false to show navigation bar\n   * @returns OperationResult indicating success or failure\n   */\n  async setNavigationBarVisibility(hide: boolean): Promise<OperationResult> {\n    try {\n      const templateName = hide ? \"hide_navigation_bar\" : \"show_navigation_bar\";\n      const template = getMobileCommandTemplate(templateName);\n      \n      if (!template) {\n        return {\n          success: false,\n          requestId: '',\n          errorMessage: `Navigation bar template not found: ${templateName}`\n        };\n      }\n\n      const description = `Navigation bar visibility (hide: ${hide})`;\n      return await this.executeCommand(template, description);\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to set navigation bar visibility: ${error instanceof Error ? error.message : String(error)}`\n      };\n    }\n  }\n\n  /**\n   * Set uninstall protection blacklist for mobile devices.\n   * \n   * @param packageNames - List of Android package names to protect from uninstallation\n   * @returns OperationResult indicating success or failure\n   */\n  async setUninstallBlacklist(packageNames: string[]): Promise<OperationResult> {\n    try {\n      const template = getMobileCommandTemplate(\"uninstall_blacklist\");\n      if (!template) {\n        return {\n          success: false,\n          requestId: '',\n          errorMessage: \"Uninstall blacklist template not found\"\n        };\n      }\n\n      // Use newline-separated format for uninstall blacklist file content\n      const packageList = packageNames.join('\\n');\n      const timestamp = Math.floor(Date.now() / 1000).toString();\n      const command = replaceTemplatePlaceholders(template, { \n        package_list: packageList,\n        timestamp: timestamp\n      });\n      \n      const description = `Uninstall blacklist configuration (${packageNames.length} packages)`;\n      return await this.executeCommand(command, description);\n    } catch (error) {\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: `Failed to set uninstall blacklist: ${error instanceof Error ? error.message : String(error)}`\n      };\n    }\n  }\n\n  /**\n   * Execute a command template for mobile configuration.\n   * \n   * @param command - The command to execute\n   * @param description - Description of the operation\n   * @returns OperationResult indicating success or failure\n   */\n  private async executeCommand(command: string, description: string): Promise<OperationResult> {\n    try {\n      log(`Executing ${description}`);\n      \n      // Use the session's command module to execute the command with longer timeout for mobile operations\n      const result = await (this.session as any).command.executeCommand(command, 10000);\n      \n      if (result && result.success) {\n        log(`✅ ${description} completed successfully`);\n        if (result.output) {\n          log(`Command output: ${result.output}`);\n        }\n        return {\n          success: true,\n          requestId: result.requestId || '',\n          errorMessage: ''\n        };\n      } else {\n        const errorMessage = result?.errorMessage || `Failed to execute ${description}`;\n        logError(`Failed to execute ${description}: ${errorMessage}`);\n        return {\n          success: false,\n          requestId: result?.requestId || '',\n          errorMessage: errorMessage\n        };\n      }\n    } catch (error) {\n      const errorMsg = `Failed to execute ${description}: ${error instanceof Error ? error.message : String(error)}`;\n      logError(errorMsg);\n      return {\n        success: false,\n        requestId: '',\n        errorMessage: errorMsg\n      };\n    }\n  }\n} ","export { Oss } from \"./oss\";\n","import { Session } from \"../session\";\nimport {\n  OSSClientResult,\n  OSSUploadResult,\n  OSSDownloadResult,\n} from \"../types/api-response\";\n\n\n/**\n * Handles OSS operations in the AgentBay cloud environment.\n */\nexport class Oss {\n  private session: Session;\n\n  /**\n   * Initialize an Oss object.\n   *\n   * @param session - The Session instance that this Oss belongs to.\n   */\n  constructor(session: Session) {\n    this.session = session;\n  }\n\n  /**\n   * Sanitizes error messages to remove sensitive information like API keys.\n   *\n   * @param error - The error to sanitize\n   * @returns The sanitized error\n   */\n  private sanitizeError(error: any): any {\n    if (!error) {\n      return error;\n    }\n\n    const errorString = String(error);\n    return errorString.replace(/Bearer\\s+[^\\s]+/g, \"Bearer [REDACTED]\");\n  }\n\n  /**\n   * Initialize OSS environment variables with the specified STS temporary credentials.\n   * Corresponds to Python's env_init() method\n   *\n   * @param accessKeyId - The access key ID from STS temporary credentials\n   * @param accessKeySecret - The access key secret from STS temporary credentials\n   * @param securityToken - The security token from STS temporary credentials (required)\n   * @param endpoint - The OSS endpoint (optional)\n   * @param region - The OSS region (optional)\n   * @returns OSSClientResult with client configuration and requestId\n   * @throws APIError if the operation fails.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const createResult = await agentBay.create();\n   * if (createResult.success) {\n   *   const result = await createResult.session.oss.envInit('sts_key_id', 'sts_key_secret', 'sts_token', 'oss-cn-hangzhou.aliyuncs.com', 'cn-hangzhou');\n   *   console.log('OSS initialized:', result.success);\n   *   await createResult.session.delete();\n   * }\n   * ```\n   */\n  async envInit(\n    accessKeyId: string,\n    accessKeySecret: string,\n    securityToken: string,\n    endpoint?: string,\n    region?: string\n  ): Promise<OSSClientResult> {\n    try {\n      const args = {\n        access_key_id: accessKeyId,\n        access_key_secret: accessKeySecret,\n        security_token: securityToken,\n        endpoint: endpoint || \"\",\n        region: region || \"\",\n      };\n      const result = await this.session.callMcpTool(\"oss_env_init\", args);\n\n      if (!result.success) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          clientConfig: {},\n          errorMessage: result.errorMessage,\n        };\n      }\n\n      let clientConfig: Record<string, any> = {};\n      try {\n        clientConfig = JSON.parse(result.data);\n      } catch (err) {\n        return {\n          requestId: result.requestId,\n          success: false,\n          clientConfig: {},\n          errorMessage: `Failed to parse client config: ${err}`,\n        };\n      }\n\n      return {\n        requestId: result.requestId,\n        success: true,\n        clientConfig,\n        errorMessage: \"\",\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        clientConfig: {},\n        errorMessage: `Failed to initialize OSS environment: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Upload a file to OSS.\n   * Corresponds to Python's upload() method\n   *\n   * @param bucket - The OSS bucket name\n   * @param object - The OSS object key\n   * @param path - The local file path to upload\n   * @returns OSSUploadResult with upload result and requestId\n   * @throws APIError if the operation fails.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const createResult = await agentBay.create();\n   * if (createResult.success) {\n   *   await createResult.session.oss.envInit('key_id', 'key_secret', 'token', 'oss-cn-hangzhou.aliyuncs.com', 'cn-hangzhou');\n   *   const result = await createResult.session.oss.upload('my-bucket', 'my-folder/file.txt', '/path/to/local/file.txt');\n   *   console.log('Upload success:', result.success);\n   *   await createResult.session.delete();\n   * }\n   * ```\n   */\n  async upload(\n    bucket: string,\n    object: string,\n    path: string\n  ): Promise<OSSUploadResult> {\n    try {\n      const args = {\n        bucket,\n        object,\n        path,\n      };\n      const result = await this.session.callMcpTool(\"oss_upload\", args);\n\n      return {\n        requestId: result.requestId,\n        success: result.success,\n        content: result.data,\n        errorMessage: result.errorMessage,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        content: \"\",\n        errorMessage: `Failed to upload file: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Upload a file to OSS using an anonymous URL.\n   * Corresponds to Python's upload_anonymous() method\n   *\n   * @param url - The anonymous upload URL\n   * @param path - The local file path to upload\n   * @returns OSSUploadResult with upload result and requestId\n   * @throws APIError if the operation fails.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const createResult = await agentBay.create();\n   * if (createResult.success) {\n   *   const result = await createResult.session.oss.uploadAnonymous('https://example.com/upload', '/path/to/local/file.txt');\n   *   console.log('Anonymous upload success:', result.success);\n   *   await createResult.session.delete();\n   * }\n   * ```\n   */\n  async uploadAnonymous(url: string, path: string): Promise<OSSUploadResult> {\n    try {\n      const args = {\n        url,\n        path,\n      };\n      const result = await this.session.callMcpTool(\"oss_upload_annon\", args);\n\n      return {\n        requestId: result.requestId,\n        success: result.success,\n        content: result.data,\n        errorMessage: result.errorMessage,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        content: \"\",\n        errorMessage: `Failed to upload file anonymously: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Download a file from OSS.\n   * Corresponds to Python's download() method\n   *\n   * @param bucket - The OSS bucket name\n   * @param object - The OSS object key\n   * @param path - The local file path to save the downloaded file\n   * @returns OSSDownloadResult with download result and requestId\n   * @throws APIError if the operation fails.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const createResult = await agentBay.create();\n   * if (createResult.success) {\n   *   await createResult.session.oss.envInit('key_id', 'key_secret', 'token', 'oss-cn-hangzhou.aliyuncs.com', 'cn-hangzhou');\n   *   const result = await createResult.session.oss.download('my-bucket', 'my-folder/file.txt', '/path/to/save/file.txt');\n   *   console.log('Download success:', result.success);\n   *   await createResult.session.delete();\n   * }\n   * ```\n   */\n  async download(\n    bucket: string,\n    object: string,\n    path: string\n  ): Promise<OSSDownloadResult> {\n    try {\n      const args = {\n        bucket,\n        object,\n        path,\n      };\n      const result = await this.session.callMcpTool(\"oss_download\", args);\n\n      return {\n        requestId: result.requestId,\n        success: result.success,\n        content: result.data,\n        errorMessage: result.errorMessage,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        content: \"\",\n        errorMessage: `Failed to download file: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * Download a file from OSS using an anonymous URL.\n   * Corresponds to Python's download_anonymous() method\n   *\n   * @param url - The anonymous download URL\n   * @param path - The local file path to save the downloaded file\n   * @returns OSSDownloadResult with download result and requestId\n   * @throws APIError if the operation fails.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const createResult = await agentBay.create();\n   * if (createResult.success) {\n   *   const result = await createResult.session.oss.downloadAnonymous('https://example.com/file.txt', '/path/to/save/file.txt');\n   *   console.log('Anonymous download success:', result.success);\n   *   await createResult.session.delete();\n   * }\n   * ```\n   */\n  async downloadAnonymous(\n    url: string,\n    path: string\n  ): Promise<OSSDownloadResult> {\n    try {\n      const args = {\n        url,\n        path,\n      };\n      const result = await this.session.callMcpTool(\"oss_download_annon\", args);\n\n      return {\n        requestId: result.requestId,\n        success: result.success,\n        content: result.data,\n        errorMessage: result.errorMessage,\n      };\n    } catch (error) {\n      return {\n        requestId: \"\",\n        success: false,\n        content: \"\",\n        errorMessage: `Failed to download file anonymously: ${error}`,\n      };\n    }\n  }\n}\n","export { Agent, ExecutionResult, QueryResult, McpToolResult, McpSession } from \"./agent\"; ","import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as crypto from \"crypto\";\nimport fetch from \"node-fetch\";\nimport { AgentBay } from \"./agent-bay\";\nimport { ContextService, Context } from \"./context\";\nimport { AgentBayError } from \"./exceptions\";\nimport { OperationResult, ContextFileListResult, FileUrlResult } from \"./types/api-response\";\nimport { log, logError, logInfo, logDebug } from \"./utils/logger\";\n\n// ==============================================================================\n// Constants\n// ==============================================================================\nconst EXTENSIONS_BASE_PATH = \"/tmp/extensions\";\n\n// ==============================================================================\n// 1. Data Models\n// ==============================================================================\n\n/**\n * Represents a browser extension as a cloud resource.\n */\nexport class Extension {\n  /**\n   * The unique identifier of the extension.\n   */\n  id: string;\n\n  /**\n   * The name of the extension.\n   */\n  name: string;\n\n  /**\n   * Date and time when the extension was created.\n   */\n  createdAt?: string;\n\n  /**\n   * Initialize an Extension object.\n   *\n   * @param id - The unique identifier of the extension.\n   * @param name - The name of the extension.\n   * @param createdAt - Date and time when the extension was created.\n   */\n  constructor(id: string, name: string, createdAt?: string) {\n    this.id = id;\n    this.name = name;\n    this.createdAt = createdAt;\n  }\n}\n\n/**\n * Configuration options for browser extension integration.\n * \n * This class encapsulates the necessary parameters for setting up\n * browser extension synchronization and context management.\n */\nexport class ExtensionOption {\n  /**\n   * ID of the extension context for browser extensions.\n   */\n  contextId: string;\n\n  /**\n   * List of extension IDs to be loaded/synchronized.\n   */\n  extensionIds: string[];\n\n  /**\n   * Initialize ExtensionOption with context and extension configuration.\n   * \n   * @param contextId - ID of the extension context for browser extensions.\n   *                   This should match the context where extensions are stored.\n   * @param extensionIds - List of extension IDs to be loaded in the browser session.\n   *                      Each ID should correspond to a valid extension in the context.\n   * \n   * @throws {Error} If contextId is empty or extensionIds is empty.\n   */\n  constructor(contextId: string, extensionIds: string[]) {\n    if (!contextId || !contextId.trim()) {\n      throw new Error(\"contextId cannot be empty\");\n    }\n\n    if (!extensionIds || extensionIds.length === 0) {\n      throw new Error(\"extensionIds cannot be empty\");\n    }\n\n    this.contextId = contextId;\n    this.extensionIds = extensionIds;\n  }\n\n  /**\n   * String representation of ExtensionOption.\n   */\n  toString(): string {\n    return `ExtensionOption(contextId='${this.contextId}', extensionIds=${JSON.stringify(this.extensionIds)})`;\n  }\n\n  /**\n   * Human-readable string representation.\n   */\n  toDisplayString(): string {\n    return `Extension Config: ${this.extensionIds.length} extension(s) in context '${this.contextId}'`;\n  }\n\n  /**\n   * Validate the extension option configuration.\n   * \n   * @returns True if configuration is valid, false otherwise.\n   */\n  validate(): boolean {\n    try {\n      // Check contextId\n      if (!this.contextId || !this.contextId.trim()) {\n        return false;\n      }\n\n      // Check extensionIds\n      if (!this.extensionIds || this.extensionIds.length === 0) {\n        return false;\n      }\n\n      // Check that all extension IDs are non-empty strings\n      for (const extId of this.extensionIds) {\n        if (typeof extId !== 'string' || !extId.trim()) {\n          return false;\n        }\n      }\n\n      return true;\n    } catch (error) {\n      return false;\n    }\n  }\n}\n\n// ==============================================================================\n// 2. Core Service Class (Scoped Stateless Model)\n// ==============================================================================\n\n/**\n * Provides methods to manage user browser extensions.\n * This service integrates with the existing context functionality for file operations.\n * \n * **Usage** (Simplified - Auto-detection):\n * ```typescript\n * // Service automatically detects if context exists and creates if needed\n * const extensionsService = new ExtensionsService(agentBay, \"browser_extensions\");\n * \n * // Or use with empty contextId to auto-generate context name\n * const extensionsService = new ExtensionsService(agentBay);  // Uses default generated name\n * \n * // Use the service immediately - initialization happens automatically\n * const extension = await extensionsService.create(\"/path/to/plugin.zip\");\n * ```\n * \n * **Integration with ExtensionOption (Simplified)**:\n * ```typescript\n * // Create extensions and configure for browser sessions\n * const extensionsService = new ExtensionsService(agentBay, \"my_extensions\");\n * const ext1 = await extensionsService.create(\"/path/to/ext1.zip\");\n * const ext2 = await extensionsService.create(\"/path/to/ext2.zip\");\n * \n * // Create extension option for browser integration (no contextId needed!)\n * const extOption = extensionsService.createExtensionOption([ext1.id, ext2.id]);\n * \n * // Use with BrowserContext for session creation\n * const browserContext = new BrowserContext({\n *   contextId: \"browser_session\",\n *   autoUpload: true,\n *   extensionOption: extOption  // All extension config encapsulated\n * });\n * ```\n * \n * **Context Management**:\n * - If contextId provided and exists: Uses the existing context\n * - If contextId provided but doesn't exist: Creates context with provided name\n * - If contextId empty or not provided: Generates default name and creates context\n * - No need to manually manage context creation or call initialize()\n * - Context initialization happens automatically on first method call\n */\nexport class ExtensionsService {\n  private agentBay: AgentBay;\n  private contextService: ContextService;\n  private extensionContext!: Context;\n  private contextId!: string;\n  private contextName: string;\n  private autoCreated: boolean;\n  private _initializationPromise: Promise<void> | null = null;\n\n  /**\n   * Initializes the ExtensionsService with a context.\n   *\n   * @param agentBay - The AgentBay client instance.\n   * @param contextId - The context ID or name. If empty or not provided,\n   *                   a default context name will be generated automatically.\n   *                   If the context doesn't exist, it will be automatically created.\n   *                   \n   * Note:\n   *   The service automatically detects if the context exists. If not,\n   *   it creates a new context with the provided name or a generated default name.\n   *   Context initialization is handled lazily on first use.\n   */\n  constructor(agentBay: AgentBay, contextId = \"\") {\n    if (!agentBay) {\n      throw new AgentBayError(\"AgentBay instance is required\");\n    }\n    if (!agentBay.context) {\n      throw new AgentBayError(\"AgentBay instance must have a context service\");\n    }\n    \n    this.agentBay = agentBay;\n    this.contextService = agentBay.context;\n    this.autoCreated = true;\n\n    // Generate default context name if contextId is empty\n    if (!contextId || contextId.trim() === \"\") {\n      contextId = `extensions-${Math.floor(Date.now() / 1000)}`;\n      logDebug(`Generated default context name: ${contextId}`);\n    }\n\n    this.contextName = contextId;\n    \n    // Initialize context lazily - will be set on first method call\n    this._initializationPromise = this._initializeContext();\n  }\n\n  /**\n   * Internal method to initialize the context.\n   * This ensures the context is ready before any operations.\n   */\n  private async _initializeContext(): Promise<void> {\n    try {\n      // Context doesn't exist, create it\n      const contextResult = await this.contextService.get(this.contextName, true);\n      if (!contextResult.success || !contextResult.context) {\n        throw new AgentBayError(`Failed to create extension repository context: ${this.contextName}`);\n      }\n\n      this.extensionContext = contextResult.context;\n      this.contextId = this.extensionContext.id;\n    } catch (error) {\n      throw new AgentBayError(`Failed to initialize ExtensionsService: ${error instanceof Error ? error.message : String(error)}`);\n    }\n  }\n\n  /**\n   * Ensures the service is initialized before performing operations.\n   */\n  private async _ensureInitialized(): Promise<void> {\n    if (this._initializationPromise) {\n      await this._initializationPromise;\n      this._initializationPromise = null;\n    }\n  }\n\n  /**\n   * An internal helper method that encapsulates the flow of \"get upload URL for a specific path and upload\".\n   * Uses the existing context service for file operations.\n   *\n   * @param localPath - The path to the local file.\n   * @param remotePath - The path of the file in context storage.\n   * \n   * @throws {AgentBayError} If getting the credential or uploading fails.\n   */\n  private async _uploadToCloud(localPath: string, remotePath: string): Promise<void> {\n    try {\n      // 1. Get upload URL using context service\n      const urlResult = await this.contextService.getFileUploadUrl(this.contextId, remotePath);\n      if (!urlResult.success || !urlResult.url) {\n        throw new AgentBayError(`Failed to get upload URL: ${urlResult.url || 'No URL returned'}`);\n      }\n\n      const preSignedUrl = urlResult.url;\n\n      // 2. Use the presigned URL to upload the file directly\n      const fileBuffer = fs.readFileSync(localPath);\n      \n      const response = await fetch(preSignedUrl, {\n        method: 'PUT',\n        body: fileBuffer,\n      });\n\n      if (!response.ok) {\n        throw new AgentBayError(`HTTP error uploading file: ${response.status} ${response.statusText}`);\n      }\n    } catch (error) {\n      if (error instanceof AgentBayError) {\n        throw error;\n      }\n      throw new AgentBayError(`An error occurred while uploading the file: ${error instanceof Error ? error.message : String(error)}`);\n    }\n  }\n\n  /**\n   * Lists all available browser extensions within this context from the cloud.\n   * Uses the context service to list files under the extensions directory.\n   *\n   * @returns Promise that resolves to an array of Extension objects.\n   * @throws {AgentBayError} If listing extensions fails.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const service = new ExtensionsService(agentBay, 'my_extensions');\n   * await service.create('/path/to/ext1.zip');\n   * const extensions = await service.list();\n   * console.log(`Found ${extensions.length} extensions`);\n   * await service.cleanup();\n   * ```\n   */\n  async list(): Promise<Extension[]> {\n    await this._ensureInitialized();\n    \n    try {\n      // Use context service to list files in the extensions directory\n      const fileListResult = await this.contextService.listFiles(\n        this.contextId,\n        EXTENSIONS_BASE_PATH,\n        1, // pageNumber\n        100 // pageSize - reasonable limit for extensions\n      );\n\n      if (!fileListResult.success) {\n        throw new AgentBayError(\"Failed to list extensions: Context file listing failed.\");\n      }\n\n      const extensions: Extension[] = [];\n      for (const fileEntry of fileListResult.entries) {\n        // Extract the extension ID from the file name\n        const extensionId = fileEntry.fileName || fileEntry.filePath;\n        extensions.push(new Extension(\n          extensionId,\n          fileEntry.fileName || extensionId,\n          fileEntry.gmtCreate\n        ));\n      }\n      return extensions;\n    } catch (error) {\n      if (error instanceof AgentBayError) {\n        throw error;\n      }\n      throw new AgentBayError(`An error occurred while listing browser extensions: ${error instanceof Error ? error.message : String(error)}`);\n    }\n  }\n\n  /**\n   * Uploads a new browser extension from a local path into the current context.\n   *\n   * @param localPath - Path to the local extension file (must be a .zip file).\n   * @returns Promise that resolves to an Extension object.\n   * @throws {Error} If the local file doesn't exist.\n   * @throws {Error} If the file format is not supported (only .zip is supported).\n   * @throws {AgentBayError} If upload fails.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const service = new ExtensionsService(agentBay);\n   * const extension = await service.create('/path/to/my-extension.zip');\n   * console.log(`Extension ID: ${extension.id}`);\n   * await service.cleanup();\n   * ```\n   */\n  async create(localPath: string): Promise<Extension> {\n    await this._ensureInitialized();\n    \n    if (!fs.existsSync(localPath)) {\n      throw new Error(`The specified local file was not found: ${localPath}`);\n    }\n\n    // Determine the ID and cloud path before uploading\n    // Validate file type - only ZIP format is supported\n    const fileExtension = path.extname(localPath).toLowerCase();\n    if (fileExtension !== '.zip') {\n      throw new Error(`Unsupported plugin format '${fileExtension}'. Only ZIP format (.zip) is supported.`);\n    }\n\n    const extensionId = `ext_${crypto.randomBytes(16).toString('hex')}${fileExtension}`;\n    const extensionName = path.basename(localPath);\n    const remotePath = `${EXTENSIONS_BASE_PATH}/${extensionId}`;\n\n    // Use the helper method to perform the cloud upload\n    await this._uploadToCloud(localPath, remotePath);\n\n    // Upload implies creation. Return a locally constructed object with basic info.\n    return new Extension(extensionId, extensionName);\n  }\n\n  /**\n   * Updates an existing browser extension in the current context with a new file.\n   *\n   * @param extensionId - ID of the extension to update.\n   * @param newLocalPath - Path to the new local extension file.\n   * @returns Promise that resolves to an Extension object.\n   * @throws {Error} If the new local file doesn't exist.\n   * @throws {Error} If the extension doesn't exist in the context.\n   * @throws {AgentBayError} If update fails.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const service = new ExtensionsService(agentBay, 'my_extensions');\n   * const ext = await service.create('/path/to/ext-v1.zip');\n   * const updated = await service.update(ext.id, '/path/to/ext-v2.zip');\n   * console.log('Extension updated:', updated.name);\n   * await service.cleanup();\n   * ```\n   */\n  async update(extensionId: string, newLocalPath: string): Promise<Extension> {\n    await this._ensureInitialized();\n    \n    if (!fs.existsSync(newLocalPath)) {\n      throw new Error(`The specified new local file was not found: ${newLocalPath}`);\n    }\n\n    // Validate that the extension exists by checking the file list\n    const existingExtensions = await this.list();\n    const extensionExists = existingExtensions.some(ext => ext.id === extensionId);\n    \n    if (!extensionExists) {\n      throw new Error(`Browser extension with ID '${extensionId}' not found in the context. Cannot update.`);\n    }\n\n    const remotePath = `${EXTENSIONS_BASE_PATH}/${extensionId}`;\n\n    // Use the helper method to perform the cloud upload (overwrite)\n    await this._uploadToCloud(newLocalPath, remotePath);\n\n    return new Extension(extensionId, path.basename(newLocalPath));\n  }\n\n  /**\n   * Gets detailed information about a specific browser extension.\n   * \n   * @param extensionId - The ID of the extension to get info for.\n   * @returns Promise that resolves to an Extension object if found, undefined otherwise.\n   */\n  private async _getExtensionInfo(extensionId: string): Promise<Extension | undefined> {\n    await this._ensureInitialized();\n    \n    try {\n      const extensions = await this.list();\n      return extensions.find(ext => ext.id === extensionId);\n    } catch (error) {\n      logError(`An error occurred while getting extension info for '${extensionId}':`, error);\n      return undefined;\n    }\n  }\n\n  /**\n   * Cleans up the auto-created context if it was created by this service.\n   *\n   * @returns Promise that resolves to true if cleanup was successful or not needed, false if cleanup failed.\n   *\n   * Note:\n   *   This method only works if the context was auto-created by this service.\n   *   For existing contexts, no cleanup is performed.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const service = new ExtensionsService(agentBay);\n   * await service.create('/path/to/my-extension.zip');\n   * const success = await service.cleanup();\n   * console.log('Cleanup success:', success);\n   * ```\n   */\n  async cleanup(): Promise<boolean> {\n    await this._ensureInitialized();\n    \n    if (!this.autoCreated) {\n      // Context was not auto-created by this service, no cleanup needed\n      return true;\n    }\n\n    try {\n      const deleteResult = await this.contextService.delete(this.extensionContext);\n      if (deleteResult) {\n        logInfo(`Extension context deleted: ${this.contextName} (ID: ${this.contextId})`);\n        return true;\n      } else {\n        logError(`Warning: Failed to delete extension context: ${this.contextName}`, new Error('Delete operation returned false'));\n        return false;\n      }\n    } catch (error) {\n      logError(`Warning: Failed to delete extension context:`, error);\n      return false;\n    }\n  }\n\n  /**\n   * Deletes a browser extension from the current context.\n   *\n   * @param extensionId - ID of the extension to delete.\n   * @returns Promise that resolves to true if deletion was successful, false otherwise.\n   *\n   * @example\n   * ```typescript\n   * const agentBay = new AgentBay({ apiKey: 'your_api_key' });\n   * const service = new ExtensionsService(agentBay, 'my_extensions');\n   * const ext = await service.create('/path/to/my-extension.zip');\n   * const success = await service.delete(ext.id);\n   * console.log('Delete success:', success);\n   * await service.cleanup();\n   * ```\n   */\n  async delete(extensionId: string): Promise<boolean> {\n    await this._ensureInitialized();\n    \n    const remotePath = `${EXTENSIONS_BASE_PATH}/${extensionId}`;\n    try {\n      // Use context service to delete the file\n      const deleteResult = await this.contextService.deleteFile(this.contextId, remotePath);\n\n      return deleteResult.success;\n    } catch (error) {\n      logError(`An error occurred while deleting browser extension '${extensionId}':`, error);\n      return false;\n    }\n  }\n\n  /**\n   * Create an ExtensionOption for the current context with specified extension IDs.\n   * \n   * This is a convenience method that creates an ExtensionOption using the current\n   * service's contextId and the provided extension IDs. This option can then be\n   * used with BrowserContext for browser session creation.\n   * \n   * @param extensionIds - List of extension IDs to include in the option.\n   *                      These should be extensions that exist in the current context.\n   * @returns ExtensionOption configuration object for browser extension integration.\n   * @throws {Error} If extensionIds is empty or invalid.\n   * \n   * @example\n   * ```typescript\n   * // Create extensions\n   * const ext1 = await extensionsService.create(\"/path/to/ext1.zip\");\n   * const ext2 = await extensionsService.create(\"/path/to/ext2.zip\");\n   * \n   * // Create extension option for browser integration\n   * const extOption = extensionsService.createExtensionOption([ext1.id, ext2.id]);\n   * \n   * // Use with BrowserContext\n   * const browserContext = new BrowserContext({\n   *   contextId: \"browser_session\",\n   *   autoUpload: true,\n   *   extensionContextId: extOption.contextId,\n   *   extensionIds: extOption.extensionIds\n   * });\n   * ```\n   */\n  createExtensionOption(extensionIds: string[]): ExtensionOption {\n    // Note: This method is synchronous like in Python, but contextId might not be available yet\n    // In practice, this should be called after the service has been used at least once\n    if (!this.contextId) {\n      throw new Error(\"Service not initialized. Please call an async method first or ensure context is created.\");\n    }\n    \n    return new ExtensionOption(\n      this.contextId,\n      extensionIds\n    );\n  }\n}","export {\n  createListSessionParams,\n  type ListSessionParams,\n  type SessionListResult,\n} from \"./list-session-params\";\n\nexport {\n  extractRequestId,\n  type ApiResponse,\n  type ApiResponseWithData,\n  type DeleteResult,\n} from \"./api-response\";\n\nexport {\n  type AppManagerRule,\n  type MobileExtraConfig,\n  type ExtraConfigs,\n  validateAppManagerRule,\n  validateMobileExtraConfig,\n  validateExtraConfigs,\n  extraConfigsToJSON,\n  extraConfigsFromJSON,\n} from \"./extra-configs\";\n","import { Session } from \"../session\";\nimport { ApiResponse } from \"./api-response\";\n\n/**\n * Parameters for listing sessions with pagination support\n */\nexport interface ListSessionParams {\n  /** Number of results per page (default: 10) */\n  maxResults?: number;\n\n  /** Token for the next page */\n  nextToken?: string;\n\n  /** Labels to filter by */\n  labels: Record<string, string>;\n}\n\n/**\n * Result type for session listing with pagination information\n */\nexport interface SessionListResult extends ApiResponse {\n  /** Array of session IDs */\n  sessionIds: string[];\n\n  /** Token for the next page (if available) */\n  nextToken?: string;\n\n  /** Number of results per page */\n  maxResults?: number;\n\n  /** Total number of results */\n  totalCount?: number;\n}\n\n/**\n * Helper function to create ListSessionParams with default values\n */\nexport function createListSessionParams(\n  labels: Record<string, string> = {}\n): ListSessionParams {\n  return {\n    maxResults: 10,\n    labels,\n  };\n}\n","/**\n * App manager rule for mobile device configuration.\n * \n * Defines rules for managing app access on mobile devices through whitelist or blacklist modes.\n */\nexport interface AppManagerRule {\n  /**\n   * Type of rule to apply.\n   * - \"White\": Whitelist mode - only allow specified apps\n   * - \"Black\": Blacklist mode - block specified apps\n   */\n  ruleType: \"White\" | \"Black\";\n  \n  /**\n   * List of Android package names to apply the rule to.\n   * Example: [\"com.android.settings\", \"com.example.app\"]\n   */\n  appPackageNameList: string[];\n}\n\n/**\n * Mobile-specific configuration settings for session creation.\n * \n * These settings allow control over mobile device behavior including\n * resolution locking, app access management, navigation bar visibility, and uninstall protection.\n */\nexport interface MobileExtraConfig {\n  /**\n   * Whether to lock the screen resolution.\n   * - true: Locks resolution for consistent mobile testing environments\n   * - false: Allows adaptive resolution for different device types\n   */\n  lockResolution: boolean;\n  \n  /**\n   * Optional app manager rule for controlling app access.\n   * Defines which apps are allowed (whitelist) or blocked (blacklist).\n   */\n  appManagerRule?: AppManagerRule;\n  \n  /**\n   * Whether to hide the system navigation bar for immersive full-screen experience.\n   * - true: Hide navigation bar\n   * - false: Show navigation bar (default)\n   */\n  hideNavigationBar?: boolean;\n  \n  /**\n   * List of package names to protect from uninstallation.\n   * Prevents accidental or malicious removal of critical apps.\n   * Example: [\"com.android.systemui\", \"com.android.settings\"]\n   */\n  uninstallBlacklist?: string[];\n}\n\n/**\n * Extra configuration settings for different session types.\n * \n * This container holds specialized configurations for various\n * session environments (mobile, desktop, etc.).\n */\nexport interface ExtraConfigs {\n  /**\n   * Mobile-specific configuration settings.\n   * Only applicable when creating mobile sessions.\n   */\n  mobile?: MobileExtraConfig;\n}\n\n/**\n * Serializes ExtraConfigs to JSON string format.\n * Returns empty string if extraConfigs is null or undefined.\n * \n * @param extraConfigs - The extra configs to serialize\n * @returns JSON string representation\n */\nexport function extraConfigsToJSON(extraConfigs?: ExtraConfigs | null): string {\n  if (!extraConfigs) {\n    return \"\";\n  }\n  \n  return JSON.stringify(extraConfigs);\n}\n\n/**\n * Deserializes ExtraConfigs from JSON string format.\n * Returns null if jsonStr is empty or invalid.\n * \n * @param jsonStr - JSON string to deserialize\n * @returns Parsed ExtraConfigs object or null\n */\nexport function extraConfigsFromJSON(jsonStr: string): ExtraConfigs | null {\n  if (!jsonStr || jsonStr.trim() === \"\") {\n    return null;\n  }\n  \n  try {\n    return JSON.parse(jsonStr) as ExtraConfigs;\n  } catch (error) {\n    throw new Error(`Failed to parse ExtraConfigs JSON: ${error}`);\n  }\n}\n\n/**\n * Validates an AppManagerRule object.\n * Throws an error if validation fails.\n * \n * @param rule - The rule to validate\n */\nexport function validateAppManagerRule(rule: AppManagerRule): void {\n  if (!rule.ruleType) {\n    throw new Error(\"AppManagerRule ruleType is required\");\n  }\n  \n  if (rule.ruleType !== \"White\" && rule.ruleType !== \"Black\") {\n    throw new Error(`Invalid ruleType: ${rule.ruleType}. Must be \"White\" or \"Black\"`);\n  }\n  \n  if (!Array.isArray(rule.appPackageNameList)) {\n    throw new Error(\"AppManagerRule appPackageNameList must be an array\");\n  }\n  \n  for (const packageName of rule.appPackageNameList) {\n    if (typeof packageName !== \"string\") {\n      throw new Error(\"AppManagerRule appPackageNameList items must be strings\");\n    }\n  }\n}\n\n/**\n * Validates a MobileExtraConfig object.\n * Throws an error if validation fails.\n * \n * @param config - The config to validate\n */\nexport function validateMobileExtraConfig(config: MobileExtraConfig): void {\n  if (typeof config.lockResolution !== \"boolean\") {\n    throw new Error(\"MobileExtraConfig lockResolution must be a boolean\");\n  }\n  \n  if (config.appManagerRule) {\n    validateAppManagerRule(config.appManagerRule);\n  }\n  \n  if (config.hideNavigationBar !== undefined && typeof config.hideNavigationBar !== \"boolean\") {\n    throw new Error(\"MobileExtraConfig hideNavigationBar must be a boolean\");\n  }\n  \n  if (config.uninstallBlacklist) {\n    if (!Array.isArray(config.uninstallBlacklist)) {\n      throw new Error(\"MobileExtraConfig uninstallBlacklist must be an array\");\n    }\n    \n    for (const packageName of config.uninstallBlacklist) {\n      if (typeof packageName !== \"string\" || packageName.trim() === \"\") {\n        throw new Error(\"MobileExtraConfig uninstallBlacklist items must be non-empty strings\");\n      }\n    }\n  }\n}\n\n/**\n * Validates an ExtraConfigs object.\n * Throws an error if validation fails.\n * \n * @param extraConfigs - The extra configs to validate\n */\nexport function validateExtraConfigs(extraConfigs: ExtraConfigs): void {\n  if (extraConfigs.mobile) {\n    validateMobileExtraConfig(extraConfigs.mobile);\n  }\n}\n","import { ContextSync, SyncPolicy, newUploadPolicy, newExtractPolicy, newRecyclePolicy, WhiteList, BWList, newDeletePolicy } from \"./context-sync\";\nimport { ExtensionOption } from \"./extension\";\nimport { BrowserFingerprintContext } from \"./browser\";\nimport { ExtraConfigs, extraConfigsToJSON } from \"./types/extra-configs\";\n\n// Browser fingerprint persistent path constant (moved from config.ts)\nconst BROWSER_FINGERPRINT_PERSIST_PATH = \"/tmp/browser_fingerprint\";\nimport {\n  log,\n  logError,\n  logInfo,\n  logDebug,\n  logAPICall,\n  logAPIResponseWithDetails,\n  maskSensitiveData,\n  setRequestId,\n  getRequestId,\n} from \"./utils/logger\";\n\n/**\n * Browser context configuration for session with optional extension support.\n *\n * This class provides browser context configuration for cloud sessions and supports\n * automatic extension synchronization when ExtensionOption is provided.\n *\n * Key Features:\n * - Browser context binding for sessions\n * - Automatic browser data upload on session end\n * - Optional browser fingerprint integration with automatic context sync generation\n * - Optional extension integration with automatic context sync generation\n * - Clean API with ExtensionOption encapsulation\n *\n * Extension Configuration:\n * - **ExtensionOption**: Pass an ExtensionOption object with contextId and extensionIds\n * - **No Extensions**: Don't provide extensionOption parameter (extensionContextSyncs will be undefined)\n *\n * Usage Examples:\n * ```typescript\n * // With extensions using ExtensionOption\n * import { ExtensionOption } from \"./extension\";\n * import { BrowserFingerprintContext } from \"./browser\";\n *\n * const extOption = new ExtensionOption(\n *   \"my_extensions\",\n *   [\"ext1\", \"ext2\"]\n * );\n *\n * const fingerprintContext = new BrowserFingerprintContext(\n *   \"my_fingerprint_context\"\n * );\n *\n * const browserContext = new BrowserContext(\n *   \"browser_session\",\n *   true,\n *   extOption,\n *   fingerprintContext\n * );\n *\n * // Without extensions (minimal configuration)\n * const browserContext = new BrowserContext(\n *   \"browser_session\",\n *   true\n * );\n * // extensionContextSyncs and fingerprintContextSync will be undefined\n *\n * // With fingerprint only\n * const browserContextWithFingerprint = new BrowserContext(\n *   \"browser_session\",\n *   true,\n *   undefined,\n *   fingerprintContext\n * );\n * ```\n */\nexport class BrowserContext {\n  /** ID of the browser context to bind to the session */\n  contextId: string;\n  /** Whether to automatically upload browser data when the session ends */\n  autoUpload: boolean;\n  /** Optional browser fingerprint context configuration object containing fingerprintContextId */\n  fingerprintContext?: BrowserFingerprintContext;\n  /** ID of the fingerprint context for browser fingerprint. Set automatically from fingerprint_context. */\n  fingerprintContextId?: string;\n  /** Auto-generated context sync for fingerprint. None if no fingerprint configuration provided. */\n  fingerprintContextSync?: ContextSync;\n  /** Optional extension configuration object containing context_id and extension_ids */\n  extensionOption?: ExtensionOption;\n  /** ID of the extension context for browser extensions. Set automatically from extension_option. */\n  extensionContextId?: string;\n  /** List of extension IDs to synchronize. Set automatically from extension_option. */\n  extensionIds?: string[];\n  /** Auto-generated context syncs for extensions. None if no extension configuration provided. */\n  extensionContextSyncs?: ContextSync[];\n\n  /**\n   * Initialize BrowserContextImpl with optional extension and fingerprint support.\n   *\n   * @param contextId - ID of the browser context to bind to the session.\n   *                   This identifies the browser instance for the session.\n   * @param autoUpload - Whether to automatically upload browser data\n   *                    when the session ends. Defaults to true.\n   * @param extensionOption - Extension configuration object containing\n   *                         contextId and extensionIds. This encapsulates\n   *                         all extension-related configuration.\n   *                         Defaults to undefined.\n   * @param fingerprintContext - Browser fingerprint context configuration object containing\n   *                            fingerprintContextId. This encapsulates\n   *                            all fingerprint-related configuration.\n   *                            Defaults to undefined.\n   *\n   * Extension Configuration:\n   * - **ExtensionOption**: Use extensionOption parameter with an ExtensionOption object\n   * - **No Extensions**: Don't provide extensionOption parameter\n   *\n   * Fingerprint Configuration:\n   * - **BrowserFingerprintContext**: Use fingerprintContext parameter with a BrowserFingerprintContext object\n   * - **No Fingerprint**: Don't provide fingerprintContext parameter\n   *\n   * Auto-generation:\n   * - extensionContextSyncs is automatically generated when extensionOption is provided\n   * - extensionContextSyncs will be undefined if no extensionOption is provided\n   * - extensionContextSyncs will be a ContextSync[] if extensionOption is valid\n   * - fingerprintContextSync is automatically generated when fingerprintContext is provided\n   * - fingerprintContextSync will be undefined if no fingerprintContext is provided\n   * - fingerprintContextSync will be a ContextSync if fingerprintContext is valid\n   */\n  constructor(\n    contextId: string,\n    autoUpload = true,\n    extensionOption?: ExtensionOption,\n    fingerprintContext?: BrowserFingerprintContext\n  ) {\n    this.contextId = contextId;\n    this.autoUpload = autoUpload;\n    this.extensionOption = extensionOption;\n    this.fingerprintContext = fingerprintContext;\n\n    // Handle fingerprint configuration from BrowserFingerprintContext\n    if (fingerprintContext) {\n      // Extract fingerprint information from BrowserFingerprintContext\n      this.fingerprintContextId = fingerprintContext.fingerprintContextId;\n      // Auto-generate fingerprint context sync\n      this.fingerprintContextSync = this._createFingerprintContextSync();\n    } else {\n      // No fingerprint configuration provided\n      this.fingerprintContextId = undefined;\n      this.fingerprintContextSync = undefined;\n    }\n\n    // Handle extension configuration from ExtensionOption\n    if (extensionOption) {\n      // Extract extension information from ExtensionOption\n      this.extensionContextId = extensionOption.contextId;\n      this.extensionIds = extensionOption.extensionIds;\n      // Auto-generate extension context syncs\n      this.extensionContextSyncs = this._createExtensionContextSyncs();\n    } else {\n      // No extension configuration provided\n      this.extensionContextId = undefined;\n      this.extensionIds = [];\n      this.extensionContextSyncs = undefined;\n    }\n  }\n\n  /**\n   * Create ContextSync configurations for browser extensions.\n   *\n   * This method is called only when extensionOption is provided and contains\n   * valid extension configuration (contextId and extensionIds).\n   *\n   * @returns ContextSync[] - List of context sync configurations for extensions.\n   *                         Returns empty list if extension configuration is invalid.\n   */\n  private _createExtensionContextSyncs(): ContextSync[] {\n    if (!this.extensionIds || this.extensionIds.length === 0 || !this.extensionContextId) {\n      return [];\n    }\n\n    // Create whitelist for each extension ID\n    const whiteLists: WhiteList[] = this.extensionIds.map(extId => ({\n      path: extId,\n      excludePaths: []\n    }));\n\n    // Create sync policy for extensions\n    const syncPolicy: SyncPolicy = {\n      uploadPolicy: {\n        ...newUploadPolicy(),\n        autoUpload: false\n      },\n      extractPolicy: {\n        ...newExtractPolicy(),\n        extract: true,\n        deleteSrcFile: true\n      },\n      deletePolicy: {\n        ...newDeletePolicy(),\n        syncLocalFile: false\n      },\n      recyclePolicy: newRecyclePolicy(),\n      bwList: {\n        whiteLists: whiteLists\n      }\n    };\n\n    // Create context sync for extensions\n    const extensionSync = new ContextSync(\n      this.extensionContextId,\n      \"/tmp/extensions/\",\n      syncPolicy\n    );\n\n    return [extensionSync];\n  }\n\n  /**\n   * Create ContextSync configuration for browser fingerprint.\n   *\n   * This method is called only when fingerprintContext is provided and contains\n   * valid fingerprint configuration (fingerprintContextId).\n   *\n   * @returns ContextSync - Context sync configuration for fingerprint.\n   *                       Returns undefined if fingerprint configuration is invalid.\n   */\n  private _createFingerprintContextSync(): ContextSync | undefined {\n    if (!this.fingerprintContextId || !this.fingerprintContextId.trim()) {\n      return undefined;\n    }\n\n    // Create sync policy for fingerprint\n    const syncPolicy: SyncPolicy = {\n      uploadPolicy: {\n        ...newUploadPolicy(),\n        autoUpload: false\n      },\n      extractPolicy: {\n        ...newExtractPolicy(),\n        extract: true,\n        deleteSrcFile: true\n      },\n      deletePolicy: {\n        ...newDeletePolicy(),\n        syncLocalFile: false\n      },\n      recyclePolicy: newRecyclePolicy(),\n      bwList: {\n        whiteLists: []\n      }\n    };\n\n    // Create context sync for fingerprint\n    const fingerprintSync = new ContextSync(\n      this.fingerprintContextId,\n      BROWSER_FINGERPRINT_PERSIST_PATH,\n      syncPolicy\n    );\n\n    return fingerprintSync;\n  }\n\n  /**\n   * Get context syncs for extensions.\n   *\n   * @returns ContextSync[] - Context sync configurations for extensions. Returns empty list if no extensions configured.\n   */\n  getExtensionContextSyncs(): ContextSync[] {\n    return this.extensionContextSyncs || [];\n  }\n\n  /**\n   * Get context sync for fingerprint.\n   *\n   * @returns ContextSync - Context sync configuration for fingerprint.\n   *                       Returns undefined if fingerprint configuration is invalid.\n   */\n  getFingerprintContextSync(): ContextSync | undefined {\n    return this.fingerprintContextSync;\n  }\n}\n\n/**\n * Configuration interface for CreateSessionParams\n */\nexport interface CreateSessionParamsConfig {\n  labels: Record<string, string>;\n  imageId?: string;\n  contextSync: ContextSync[];\n  /** Optional configuration for browser data synchronization */\n  browserContext?: BrowserContext;\n  /** Whether to create a VPC-based session. Defaults to false. */\n  isVpc?: boolean;\n  /** Policy id to apply when creating the session. */\n  policyId?: string;\n  /** Whether to enable browser recording for the session. Defaults to false. */\n  enableBrowserReplay?: boolean;\n  /** Extra configuration settings for different session types (e.g., mobile) */\n  extraConfigs?: ExtraConfigs;\n}\n\n/**\n * CreateSessionParams provides a way to configure the parameters for creating a new session\n * in the AgentBay cloud environment.\n */\nexport class CreateSessionParams implements CreateSessionParamsConfig {\n  /** Custom labels for the Session. These can be used for organizing and filtering sessions. */\n  public labels: Record<string, string>;\n\n  /** Image ID to use for the session. */\n  public imageId?: string;\n\n  /**\n   * List of context synchronization configurations.\n   * These configurations define how contexts should be synchronized and mounted.\n   */\n  public contextSync: ContextSync[];\n\n  /** Optional configuration for browser data synchronization. */\n  public browserContext?: BrowserContext;\n\n  /** Whether to create a VPC-based session. Defaults to false. */\n  public isVpc: boolean;\n\n  /** Policy id to apply when creating the session. */\n  public policyId?: string;\n\n  /** Whether to enable browser recording for the session. Defaults to false. */\n  public enableBrowserReplay: boolean;\n\n  /** Extra configuration settings for different session types (e.g., mobile) */\n  public extraConfigs?: ExtraConfigs;\n\n  constructor() {\n    this.labels = {};\n    this.contextSync = [];\n    this.isVpc = false;\n    this.enableBrowserReplay = false;\n  }\n\n  /**\n   * WithLabels sets the labels for the session parameters and returns the updated parameters.\n   */\n  withLabels(labels: Record<string, string>): CreateSessionParams {\n    this.labels = labels;\n    return this;\n  }\n\n\n  /**\n   * WithImageId sets the image ID for the session parameters and returns the updated parameters.\n   */\n  withImageId(imageId: string): CreateSessionParams {\n    this.imageId = imageId;\n    return this;\n  }\n\n  /**\n   * WithBrowserContext sets the browser context for the session parameters and returns the updated parameters.\n   */\n  withBrowserContext(browserContext: BrowserContext): CreateSessionParams {\n    this.browserContext = browserContext;\n    // Add extension and fingerprint context syncs if browser context has them\n    if (this.browserContext && 'getExtensionContextSyncs' in this.browserContext) {\n      const contextSyncs = this.browserContext.getExtensionContextSyncs();\n      this.contextSync.push(...contextSyncs);\n      logDebug(`Added ${contextSyncs.length} extension context syncs from browser context`);\n    }\n    // Add fingerprint context sync if browser context has it\n    if (this.browserContext && 'getFingerprintContextSync' in this.browserContext) {\n      const fingerprintContextSync = this.browserContext.getFingerprintContextSync();\n      if (fingerprintContextSync) {\n        this.contextSync.push(fingerprintContextSync);\n        logDebug(`Added fingerprint context sync from browser context`);\n      }\n    }\n    return this;\n  }\n\n  /**\n   * WithIsVpc sets the VPC flag for the session parameters and returns the updated parameters.\n   */\n  withIsVpc(isVpc: boolean): CreateSessionParams {\n    this.isVpc = isVpc;\n    return this;\n  }\n\n  /**\n   * WithPolicyId sets the policy id for the session parameters and returns the updated parameters.\n   */\n  withPolicyId(policyId: string): CreateSessionParams {\n    this.policyId = policyId;\n    return this;\n  }\n\n  /**\n   * WithenableBrowserReplay sets the browser recording flag for the session parameters and returns the updated parameters.\n   */\n  withEnableBrowserReplay(enableBrowserReplay: boolean): CreateSessionParams {\n    this.enableBrowserReplay = enableBrowserReplay;\n    return this;\n  }\n\n  /**\n   * Alias for withEnableBrowserReplay for backward compatibility.\n   */\n  withEnableRecord(enableRecord: boolean): CreateSessionParams {\n    return this.withEnableBrowserReplay(enableRecord);\n  }\n\n  /**\n   * WithExtraConfigs sets the extra configurations for the session parameters and returns the updated parameters.\n   */\n  withExtraConfigs(extraConfigs: ExtraConfigs): CreateSessionParams {\n    this.extraConfigs = extraConfigs;\n    return this;\n  }\n\n  /**\n   * GetLabelsJSON returns the labels as a JSON string.\n   * Returns an object with success status and result/error message to match Go version behavior.\n   */\n  private getLabelsJSON(): { result: string; error?: string } {\n    if (Object.keys(this.labels).length === 0) {\n      return { result: \"\" };\n    }\n\n    try {\n      const labelsJSON = JSON.stringify(this.labels);\n      return { result: labelsJSON };\n    } catch (error) {\n      return {\n        result: \"\",\n        error: `Failed to marshal labels to JSON: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * GetExtraConfigsJSON returns the extra configs as a JSON string.\n   * Returns an object with result and optional error message to match Go version behavior.\n   */\n  private getExtraConfigsJSON(): { result: string; error?: string } {\n    if (!this.extraConfigs) {\n      return { result: \"\" };\n    }\n\n    try {\n      const extraConfigsJSON = extraConfigsToJSON(this.extraConfigs);\n      return { result: extraConfigsJSON };\n    } catch (error) {\n      return {\n        result: \"\",\n        error: `Failed to marshal extra configs to JSON: ${error}`,\n      };\n    }\n  }\n\n  /**\n   * AddContextSync adds a context sync configuration to the session parameters.\n   */\n  addContextSync(\n    contextId: string,\n    path: string,\n    policy?: SyncPolicy\n  ): CreateSessionParams {\n    const contextSync = new ContextSync(contextId, path, policy);\n    this.contextSync.push(contextSync);\n    return this;\n  }\n\n  /**\n   * AddContextSyncConfig adds a pre-configured context sync to the session parameters.\n   */\n  addContextSyncConfig(contextSync: ContextSync): CreateSessionParams {\n    this.contextSync.push(contextSync);\n    return this;\n  }\n\n  /**\n   * WithContextSync sets the context sync configurations for the session parameters.\n   */\n  withContextSync(contextSyncs: ContextSync[]): CreateSessionParams {\n    this.contextSync = contextSyncs;\n    // Add extension and fingerprint context syncs if browser context has them\n    if (this.browserContext && 'getExtensionContextSyncs' in this.browserContext) {\n      const contextSyncs = this.browserContext.getExtensionContextSyncs();\n      this.contextSync.push(...contextSyncs);\n      logDebug(`Added ${contextSyncs.length} extension context syncs from browser context`);\n    }\n    // Add fingerprint context sync if browser context has it\n    if (this.browserContext && 'getFingerprintContextSync' in this.browserContext) {\n      const fingerprintContextSync = this.browserContext.getFingerprintContextSync();\n      if (fingerprintContextSync) {\n        this.contextSync.push(fingerprintContextSync);\n        logDebug(`Added fingerprint context sync from browser context`);\n      }\n    }\n    return this;\n  }\n\n  /**\n   * Convert to plain object for JSON serialization\n   */\n  toJSON(): CreateSessionParamsConfig {\n    // Get base context syncs\n    let allContextSyncs = [...this.contextSync];\n\n    // Add extension context syncs if browser context has them\n    if (this.browserContext && 'getExtensionContextSyncs' in this.browserContext) {\n      const extensionSyncs = this.browserContext.getExtensionContextSyncs();\n      allContextSyncs = allContextSyncs.concat(extensionSyncs);\n      logDebug(`Added ${extensionSyncs.length} extension context syncs from browser context`);\n    }\n    // Add fingerprint context sync if browser context has it\n    if (this.browserContext && 'getFingerprintContextSync' in this.browserContext) {\n      const fingerprintContextSync = this.browserContext.getFingerprintContextSync();\n      if (fingerprintContextSync) {\n        allContextSyncs.push(fingerprintContextSync);\n        logDebug(`Added fingerprint context sync from browser context`);\n      }\n    }\n\n    return {\n      labels: this.labels,\n      imageId: this.imageId,\n      contextSync: allContextSyncs,\n      browserContext: this.browserContext,\n      isVpc: this.isVpc,\n      policyId: this.policyId,\n      enableBrowserReplay: this.enableBrowserReplay,\n      extraConfigs: this.extraConfigs,\n    };\n  }\n\n  /**\n   * Create from plain object\n   */\n  static fromJSON(config: CreateSessionParamsConfig): CreateSessionParams {\n    const params = new CreateSessionParams();\n    params.labels = config.labels || {};\n    params.imageId = config.imageId;\n    params.contextSync = config.contextSync || [];\n\n    // Handle browser context - convert to BrowserContext class if needed\n    if (config.browserContext) {\n      if ('getAllContextSyncs' in config.browserContext) {\n        // It's already a BrowserContext instance\n        params.browserContext = config.browserContext;\n      } else {\n        // It's a plain object, convert to BrowserContext class\n        const bc = config.browserContext as any; // Type assertion for plain object\n        params.browserContext = new BrowserContext(\n          bc.contextId,\n          bc.autoUpload,\n          bc.extensionOption,\n          bc.fingerprintContext\n        );\n      }\n    }\n\n    params.isVpc = config.isVpc || false;\n    params.policyId = config.policyId;\n    params.enableBrowserReplay = config.enableBrowserReplay || false;\n    params.extraConfigs = config.extraConfigs;\n    return params;\n  }\n}\n\n/**\n * NewCreateSessionParams creates a new CreateSessionParams with default values.\n */\nexport function newCreateSessionParams(): CreateSessionParams {\n  return new CreateSessionParams();\n}"]}