@morphllm/morphsdk 0.2.94 → 0.2.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/{chunk-YJ354BA2.js → chunk-2AMEQAO2.js} +2 -2
  2. package/dist/chunk-2AMEQAO2.js.map +1 -0
  3. package/dist/{chunk-EI4UKP24.js → chunk-2HMEZZKK.js} +2 -2
  4. package/dist/{chunk-EI4UKP24.js.map → chunk-2HMEZZKK.js.map} +1 -1
  5. package/dist/{chunk-UJ7LVT5G.js → chunk-2VERUKO2.js} +1 -1
  6. package/dist/chunk-2VERUKO2.js.map +1 -0
  7. package/dist/{chunk-VHOWYK66.js → chunk-43LQLGP6.js} +23 -33
  8. package/dist/chunk-43LQLGP6.js.map +1 -0
  9. package/dist/{chunk-R7WN43L2.js → chunk-4KMBU6T3.js} +4 -4
  10. package/dist/{chunk-BVVDDTI7.js → chunk-73RV6EXR.js} +2 -2
  11. package/dist/{chunk-IH3KN4AT.js → chunk-7D6TXC7X.js} +2 -2
  12. package/dist/{chunk-FIA6LBW2.js → chunk-O7LDZA52.js} +2 -2
  13. package/dist/{chunk-5QIWYEHJ.js → chunk-PE4KGDA6.js} +1 -8
  14. package/dist/chunk-PE4KGDA6.js.map +1 -0
  15. package/dist/{chunk-SQN4DUQS.js → chunk-Q6Y4R236.js} +26 -2
  16. package/dist/chunk-Q6Y4R236.js.map +1 -0
  17. package/dist/{chunk-TXYCM4NP.js → chunk-QAT5UVPX.js} +3 -3
  18. package/dist/{chunk-M7GFXRKL.js → chunk-QJP62BXH.js} +157 -72
  19. package/dist/chunk-QJP62BXH.js.map +1 -0
  20. package/dist/{chunk-AV6YV2MH.js → chunk-R7IQWNSA.js} +8 -8
  21. package/dist/chunk-R7IQWNSA.js.map +1 -0
  22. package/dist/{chunk-ESRZJRTQ.js → chunk-SI2CKRKJ.js} +86 -56
  23. package/dist/chunk-SI2CKRKJ.js.map +1 -0
  24. package/dist/{chunk-FMWNVTJJ.js → chunk-TSENDJQI.js} +6 -6
  25. package/dist/chunk-TSENDJQI.js.map +1 -0
  26. package/dist/{chunk-IUG2FHNN.js → chunk-XH7P7HVT.js} +1 -8
  27. package/dist/chunk-XH7P7HVT.js.map +1 -0
  28. package/dist/{chunk-WSQMWVSD.js → chunk-YZ5NCWO2.js} +6 -6
  29. package/dist/chunk-YZ5NCWO2.js.map +1 -0
  30. package/dist/{chunk-4WO7PJNT.js → chunk-ZYTAKEBW.js} +8 -8
  31. package/dist/client.cjs +280 -162
  32. package/dist/client.cjs.map +1 -1
  33. package/dist/client.js +18 -18
  34. package/dist/index.cjs +280 -162
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.js +18 -18
  37. package/dist/tools/browser/anthropic.cjs +54 -23
  38. package/dist/tools/browser/anthropic.cjs.map +1 -1
  39. package/dist/tools/browser/anthropic.js +7 -7
  40. package/dist/tools/browser/core.cjs +262 -124
  41. package/dist/tools/browser/core.cjs.map +1 -1
  42. package/dist/tools/browser/core.d.ts +24 -24
  43. package/dist/tools/browser/core.js +5 -5
  44. package/dist/tools/browser/errors.cjs.map +1 -1
  45. package/dist/tools/browser/errors.d.ts +1 -1
  46. package/dist/tools/browser/errors.js +1 -1
  47. package/dist/tools/browser/index.cjs +277 -139
  48. package/dist/tools/browser/index.cjs.map +1 -1
  49. package/dist/tools/browser/index.d.ts +3 -3
  50. package/dist/tools/browser/index.js +12 -12
  51. package/dist/tools/browser/index.js.map +1 -1
  52. package/dist/tools/browser/live.cjs +25 -1
  53. package/dist/tools/browser/live.cjs.map +1 -1
  54. package/dist/tools/browser/live.js +1 -1
  55. package/dist/tools/browser/openai.cjs +54 -23
  56. package/dist/tools/browser/openai.cjs.map +1 -1
  57. package/dist/tools/browser/openai.js +7 -7
  58. package/dist/tools/browser/profiles/core.cjs +85 -54
  59. package/dist/tools/browser/profiles/core.cjs.map +1 -1
  60. package/dist/tools/browser/profiles/core.d.ts +33 -25
  61. package/dist/tools/browser/profiles/core.js +5 -3
  62. package/dist/tools/browser/profiles/index.cjs +85 -54
  63. package/dist/tools/browser/profiles/index.cjs.map +1 -1
  64. package/dist/tools/browser/profiles/index.d.ts +2 -2
  65. package/dist/tools/browser/profiles/index.js +5 -3
  66. package/dist/tools/browser/profiles/types.cjs +1 -1
  67. package/dist/tools/browser/profiles/types.cjs.map +1 -1
  68. package/dist/tools/browser/profiles/types.d.ts +28 -9
  69. package/dist/tools/browser/profiles/types.js +1 -1
  70. package/dist/tools/browser/prompts.cjs +1 -1
  71. package/dist/tools/browser/prompts.cjs.map +1 -1
  72. package/dist/tools/browser/prompts.d.ts +1 -1
  73. package/dist/tools/browser/prompts.js +1 -1
  74. package/dist/tools/browser/types.cjs.map +1 -1
  75. package/dist/tools/browser/types.d.ts +54 -52
  76. package/dist/tools/browser/vercel.cjs +56 -25
  77. package/dist/tools/browser/vercel.cjs.map +1 -1
  78. package/dist/tools/browser/vercel.d.ts +1 -1
  79. package/dist/tools/browser/vercel.js +7 -7
  80. package/dist/tools/fastapply/anthropic.cjs +0 -7
  81. package/dist/tools/fastapply/anthropic.cjs.map +1 -1
  82. package/dist/tools/fastapply/anthropic.js +1 -1
  83. package/dist/tools/fastapply/index.cjs +0 -14
  84. package/dist/tools/fastapply/index.cjs.map +1 -1
  85. package/dist/tools/fastapply/index.js +2 -2
  86. package/dist/tools/fastapply/openai.cjs +0 -7
  87. package/dist/tools/fastapply/openai.cjs.map +1 -1
  88. package/dist/tools/fastapply/openai.js +1 -1
  89. package/dist/tools/index.cjs +0 -14
  90. package/dist/tools/index.cjs.map +1 -1
  91. package/dist/tools/index.js +2 -2
  92. package/dist/tools/warp_grep/agent/runner.cjs +18 -98
  93. package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
  94. package/dist/tools/warp_grep/agent/runner.js +2 -3
  95. package/dist/tools/warp_grep/anthropic.cjs +18 -98
  96. package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
  97. package/dist/tools/warp_grep/anthropic.js +8 -9
  98. package/dist/tools/warp_grep/client.cjs +18 -98
  99. package/dist/tools/warp_grep/client.cjs.map +1 -1
  100. package/dist/tools/warp_grep/client.js +7 -8
  101. package/dist/tools/warp_grep/gemini.cjs +18 -98
  102. package/dist/tools/warp_grep/gemini.cjs.map +1 -1
  103. package/dist/tools/warp_grep/gemini.js +7 -8
  104. package/dist/tools/warp_grep/gemini.js.map +1 -1
  105. package/dist/tools/warp_grep/harness.js +10 -10
  106. package/dist/tools/warp_grep/index.cjs +18 -98
  107. package/dist/tools/warp_grep/index.cjs.map +1 -1
  108. package/dist/tools/warp_grep/index.js +10 -11
  109. package/dist/tools/warp_grep/openai.cjs +18 -98
  110. package/dist/tools/warp_grep/openai.cjs.map +1 -1
  111. package/dist/tools/warp_grep/openai.js +8 -9
  112. package/dist/tools/warp_grep/providers/local.js +2 -2
  113. package/dist/tools/warp_grep/vercel.cjs +18 -98
  114. package/dist/tools/warp_grep/vercel.cjs.map +1 -1
  115. package/dist/tools/warp_grep/vercel.js +8 -9
  116. package/dist/{vercel-CsnNSdze.d.ts → vercel-CVF27qFK.d.ts} +10 -10
  117. package/package.json +1 -1
  118. package/dist/chunk-5QIWYEHJ.js.map +0 -1
  119. package/dist/chunk-AV6YV2MH.js.map +0 -1
  120. package/dist/chunk-ESRZJRTQ.js.map +0 -1
  121. package/dist/chunk-FMWNVTJJ.js.map +0 -1
  122. package/dist/chunk-IUG2FHNN.js.map +0 -1
  123. package/dist/chunk-M7GFXRKL.js.map +0 -1
  124. package/dist/chunk-SQN4DUQS.js.map +0 -1
  125. package/dist/chunk-UJ7LVT5G.js.map +0 -1
  126. package/dist/chunk-VHOWYK66.js.map +0 -1
  127. package/dist/chunk-WSQMWVSD.js.map +0 -1
  128. package/dist/chunk-YJ354BA2.js.map +0 -1
  129. /package/dist/{chunk-R7WN43L2.js.map → chunk-4KMBU6T3.js.map} +0 -0
  130. /package/dist/{chunk-BVVDDTI7.js.map → chunk-73RV6EXR.js.map} +0 -0
  131. /package/dist/{chunk-IH3KN4AT.js.map → chunk-7D6TXC7X.js.map} +0 -0
  132. /package/dist/{chunk-FIA6LBW2.js.map → chunk-O7LDZA52.js.map} +0 -0
  133. /package/dist/{chunk-TXYCM4NP.js.map → chunk-QAT5UVPX.js.map} +0 -0
  134. /package/dist/{chunk-4WO7PJNT.js.map → chunk-ZYTAKEBW.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../tools/browser/profiles/core.ts","../../../../tools/utils/resilience.ts","../../../../tools/browser/errors.ts","../../../../tools/browser/profiles/types.ts"],"sourcesContent":["/**\n * ProfilesClient - Manage browser profiles for storing login state.\n *\n * @example\n * ```typescript\n * const morph = new MorphClient({ apiKey: '...' });\n *\n * // Create a profile\n * const profile = await morph.browser.profiles.createProfile({\n * name: 'LinkedIn',\n * repoId: 'repo-uuid'\n * });\n *\n * // Start session for user to sign in\n * const session = await morph.browser.profiles.startSession();\n * console.log('Sign in at:', session.debugUrl);\n *\n * // Save state after user signs in\n * await morph.browser.profiles.saveSession(session.sessionId, profile.id);\n *\n * // Use profile in browser tasks\n * await morph.browser.execute({\n * task: 'Check notifications',\n * url: 'https://linkedin.com',\n * profileId: profile.id\n * });\n * ```\n */\n\nimport { fetchWithRetry } from '../../utils/resilience.js';\nimport type { BrowserConfig } from '../types.js';\nimport {\n MorphValidationError,\n MorphAuthenticationError,\n parseAPIError,\n} from '../errors.js';\nimport type {\n Profile,\n ProfileWithMethods,\n CreateProfileInput,\n UpdateProfileInput,\n ProfileListResponse,\n ProfileSession,\n ProfileSessionInput,\n SaveProfileSessionInput,\n ProfileStateResponse,\n APIProfile,\n APIProfileSession,\n APIProfileStateResponse,\n} from './types.js';\nimport {\n transformProfile,\n transformCreateInput,\n transformSession,\n transformSaveInput,\n transformStateResponse,\n} from './types.js';\n\nconst DEFAULT_API_URL = process.env.MORPH_ENVIRONMENT === 'DEV'\n ? 'http://localhost:8000'\n : 'https://browser.morphllm.com';\n\n/**\n * ProfilesClient class for managing browser profiles.\n *\n * Access via `morph.browser.profiles` on a MorphClient instance.\n */\nexport class ProfilesClient {\n private config: BrowserConfig;\n\n constructor(config: BrowserConfig) {\n this.config = config;\n }\n\n /**\n * Create a new browser profile.\n *\n * @param input - Profile creation parameters\n * @returns The created profile\n * @throws {MorphValidationError} If input validation fails\n * @throws {MorphProfileLimitError} If profile limit is exceeded\n * @throws {MorphAuthenticationError} If API key is missing or invalid\n *\n * @example\n * ```typescript\n * const profile = await morph.browser.profiles.createProfile({\n * name: 'LinkedIn Production',\n * repoId: 'repo-uuid-here'\n * });\n * ```\n */\n async createProfile(input: CreateProfileInput): Promise<Profile> {\n return createProfile(input, this.config);\n }\n\n /**\n * List all profiles for the authenticated user.\n *\n * @param repoId - Optional repository ID to filter by\n * @returns Array of profiles\n *\n * @example\n * ```typescript\n * // List all profiles\n * const allProfiles = await morph.browser.profiles.listProfiles();\n *\n * // List profiles for a specific repo\n * const repoProfiles = await morph.browser.profiles.listProfiles('repo-uuid');\n * ```\n */\n async listProfiles(repoId?: string): Promise<Profile[]> {\n return listProfiles(this.config, repoId);\n }\n\n /**\n * Get a profile by ID with convenience methods.\n *\n * @param id - Profile ID\n * @returns Profile with attached methods\n * @throws {MorphNotFoundError} If profile is not found\n *\n * @example\n * ```typescript\n * const profile = await morph.browser.profiles.getProfile('profile-id');\n * const state = await profile.getState();\n * await profile.delete();\n * ```\n */\n async getProfile(id: string): Promise<ProfileWithMethods> {\n return getProfile(id, this.config);\n }\n\n /**\n * Update a profile's name.\n *\n * @param id - Profile ID\n * @param input - Update parameters\n * @returns Updated profile\n */\n async updateProfile(id: string, input: UpdateProfileInput): Promise<Profile> {\n return updateProfile(id, input, this.config);\n }\n\n /**\n * Delete a profile.\n *\n * @param id - Profile ID\n * @throws {MorphNotFoundError} If profile is not found\n */\n async deleteProfile(id: string): Promise<void> {\n return deleteProfile(id, this.config);\n }\n\n /**\n * Start a browser session for profile setup.\n *\n * Returns a live URL where the user can sign into accounts.\n * After signing in, call `saveSession` to persist the state.\n *\n * @param input - Optional session parameters\n * @returns Session with debug URL\n *\n * @example\n * ```typescript\n * const session = await morph.browser.profiles.startSession();\n * console.log('Sign in at:', session.debugUrl);\n * // Open debugUrl in browser, user signs in...\n * await morph.browser.profiles.saveSession(session.sessionId, profile.id);\n * ```\n */\n async startSession(input?: ProfileSessionInput): Promise<ProfileSession> {\n return startProfileSession(this.config, input);\n }\n\n /**\n * Save browser state from a session to a profile.\n *\n * Call this after the user is done signing into accounts.\n * Extracts cookies, localStorage, and sessionStorage.\n *\n * @param sessionId - Browser session ID from startSession\n * @param profileId - Profile ID to save state to\n * @returns Updated profile with cookie domains\n */\n async saveSession(sessionId: string, profileId: string): Promise<Profile> {\n return saveProfileSession({ sessionId, profileId }, this.config);\n }\n\n /**\n * Get the presigned URL for a profile's state.\n *\n * Use this to download the raw state JSON for debugging\n * or to restore state manually.\n *\n * @param profileId - Profile ID\n * @returns State URL with expiry information\n */\n async getProfileState(profileId: string): Promise<ProfileStateResponse> {\n return getProfileState(profileId, this.config);\n }\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nfunction validateCreateInput(input: CreateProfileInput): void {\n if (!input.name || typeof input.name !== 'string') {\n throw new MorphValidationError('name is required', 'name');\n }\n\n const trimmedName = input.name.trim();\n if (trimmedName.length === 0) {\n throw new MorphValidationError('name cannot be empty', 'name');\n }\n\n if (trimmedName.length > 100) {\n throw new MorphValidationError('name must be 100 characters or less', 'name');\n }\n\n if (!input.repoId || typeof input.repoId !== 'string') {\n throw new MorphValidationError('repoId is required', 'repoId');\n }\n\n if (input.repoId.trim().length === 0) {\n throw new MorphValidationError('repoId cannot be empty', 'repoId');\n }\n}\n\nfunction validateId(id: string, fieldName: string): void {\n if (!id || typeof id !== 'string') {\n throw new MorphValidationError(`${fieldName} is required`, fieldName);\n }\n\n if (id.trim().length === 0) {\n throw new MorphValidationError(`${fieldName} cannot be empty`, fieldName);\n }\n}\n\n// ============================================================================\n// Standalone Functions\n// ============================================================================\n\n/**\n * Create a new browser profile.\n */\nexport async function createProfile(\n input: CreateProfileInput,\n config: BrowserConfig = {}\n): Promise<Profile> {\n validateCreateInput(input);\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify(transformCreateInput(input)),\n },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiProfile: APIProfile = await response.json();\n return transformProfile(apiProfile);\n}\n\n/**\n * List all profiles for the authenticated user.\n */\nexport async function listProfiles(\n config: BrowserConfig = {},\n repoId?: string\n): Promise<Profile[]> {\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const url = repoId\n ? `${apiUrl}/profiles?repo_id=${encodeURIComponent(repoId)}`\n : `${apiUrl}/profiles`;\n\n const response = await fetchWithRetry(\n url,\n { method: 'GET', headers },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const data: { profiles: APIProfile[] } = await response.json();\n return data.profiles.map(transformProfile);\n}\n\n/**\n * Get a profile by ID with convenience methods.\n */\nexport async function getProfile(\n id: string,\n config: BrowserConfig = {}\n): Promise<ProfileWithMethods> {\n validateId(id, 'id');\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/${encodeURIComponent(id)}`,\n { method: 'GET', headers },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiProfile: APIProfile = await response.json();\n const profile = transformProfile(apiProfile);\n\n // Attach convenience methods\n return {\n ...profile,\n getState: () => getProfileState(id, config),\n delete: () => deleteProfile(id, config),\n };\n}\n\n/**\n * Update a profile.\n */\nexport async function updateProfile(\n id: string,\n input: UpdateProfileInput,\n config: BrowserConfig = {}\n): Promise<Profile> {\n validateId(id, 'id');\n\n if (input.name !== undefined) {\n if (typeof input.name !== 'string') {\n throw new MorphValidationError('name must be a string', 'name');\n }\n if (input.name.trim().length === 0) {\n throw new MorphValidationError('name cannot be empty', 'name');\n }\n if (input.name.length > 100) {\n throw new MorphValidationError('name must be 100 characters or less', 'name');\n }\n }\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/${encodeURIComponent(id)}`,\n {\n method: 'PATCH',\n headers,\n body: JSON.stringify(input),\n },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiProfile: APIProfile = await response.json();\n return transformProfile(apiProfile);\n}\n\n/**\n * Delete a profile.\n */\nexport async function deleteProfile(\n id: string,\n config: BrowserConfig = {}\n): Promise<void> {\n validateId(id, 'id');\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/${encodeURIComponent(id)}`,\n { method: 'DELETE', headers },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n}\n\n/**\n * Start a browser session for profile setup.\n */\nexport async function startProfileSession(\n config: BrowserConfig = {},\n input?: ProfileSessionInput\n): Promise<ProfileSession> {\n if (!config.apiKey) {\n throw new MorphAuthenticationError();\n }\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n // Transform input if provided\n const body = input?.profileId\n ? { profile_id: input.profileId }\n : {};\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/session/start`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiSession: APIProfileSession = await response.json();\n return transformSession(apiSession);\n}\n\n/**\n * Save browser state from a session to a profile.\n */\nexport async function saveProfileSession(\n input: SaveProfileSessionInput,\n config: BrowserConfig = {}\n): Promise<Profile> {\n validateId(input.sessionId, 'sessionId');\n validateId(input.profileId, 'profileId');\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/session/save`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify(transformSaveInput(input)),\n },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiProfile: APIProfile = await response.json();\n return transformProfile(apiProfile);\n}\n\n/**\n * Get the presigned URL for a profile's state.\n */\nexport async function getProfileState(\n profileId: string,\n config: BrowserConfig = {}\n): Promise<ProfileStateResponse> {\n validateId(profileId, 'profileId');\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/${encodeURIComponent(profileId)}/state`,\n { method: 'GET', headers },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiState: APIProfileStateResponse = await response.json();\n return transformStateResponse(apiState);\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction buildHeaders(config: BrowserConfig): Record<string, string> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n }\n return headers;\n}\n","/**\n * Resilience utilities for retry logic and timeout handling\n */\n\nexport interface RetryConfig {\n maxRetries?: number; // Default: 3\n initialDelay?: number; // Default: 1000ms\n maxDelay?: number; // Default: 30000ms\n backoffMultiplier?: number; // Default: 2\n retryableErrors?: string[]; // Default: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND']\n onRetry?: (attempt: number, error: Error) => void;\n}\n\nconst DEFAULT_RETRY_CONFIG: Required<Omit<RetryConfig, 'onRetry'>> = {\n maxRetries: 3,\n initialDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n retryableErrors: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND'],\n};\n\n/**\n * Retry a fetch request with exponential backoff\n * \n * @param url - Request URL\n * @param options - Fetch options\n * @param retryConfig - Retry configuration\n * @returns Response from fetch\n * \n * @example\n * ```typescript\n * const response = await fetchWithRetry(\n * 'https://api.example.com/data',\n * { method: 'POST', body: JSON.stringify(data) },\n * { maxRetries: 5, initialDelay: 500 }\n * );\n * ```\n */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n retryConfig: RetryConfig = {}\n): Promise<Response> {\n const {\n maxRetries = DEFAULT_RETRY_CONFIG.maxRetries,\n initialDelay = DEFAULT_RETRY_CONFIG.initialDelay,\n maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,\n backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,\n retryableErrors = DEFAULT_RETRY_CONFIG.retryableErrors,\n onRetry,\n } = retryConfig;\n\n let lastError: Error | null = null;\n let delay = initialDelay;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, options);\n \n // Retry on 429 (rate limit) or 503 (service unavailable)\n if (response.status === 429 || response.status === 503) {\n if (attempt < maxRetries) {\n // Check for Retry-After header\n const retryAfter = response.headers.get('Retry-After');\n const waitTime = retryAfter \n ? parseInt(retryAfter) * 1000 \n : Math.min(delay, maxDelay);\n \n const error = new Error(`HTTP ${response.status}: Retrying after ${waitTime}ms`);\n if (onRetry) {\n onRetry(attempt + 1, error);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n continue;\n }\n }\n\n return response;\n } catch (error) {\n lastError = error as Error;\n \n // Check if error is retryable\n const isRetryable = retryableErrors.some(errType => \n lastError?.message?.includes(errType)\n );\n\n if (!isRetryable || attempt === maxRetries) {\n throw lastError;\n }\n\n // Exponential backoff\n const waitTime = Math.min(delay, maxDelay);\n if (onRetry) {\n onRetry(attempt + 1, lastError);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n }\n }\n\n throw lastError || new Error('Max retries exceeded');\n}\n\n/**\n * Add timeout to any promise\n * \n * @param promise - Promise to wrap with timeout\n * @param timeoutMs - Timeout in milliseconds\n * @param errorMessage - Optional custom error message\n * @returns Promise that rejects if timeout is reached\n * \n * @example\n * ```typescript\n * const result = await withTimeout(\n * fetchData(),\n * 5000,\n * 'Data fetch timed out'\n * );\n * ```\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n errorMessage?: string\n): Promise<T> {\n let timeoutId: NodeJS.Timeout | number;\n \n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(errorMessage || `Operation timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n const result = await Promise.race([promise, timeoutPromise]);\n clearTimeout(timeoutId!);\n return result;\n } catch (error) {\n clearTimeout(timeoutId!);\n throw error;\n }\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Unified error type for all tools\n */\nexport class MorphError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public retryable: boolean = false\n ) {\n super(message);\n this.name = 'MorphError';\n }\n}\n\n\n","/**\n * Custom error classes for browser automation and profiles.\n *\n * @example\n * ```typescript\n * try {\n * await morph.browser.profiles.createProfile({ name: '', repoId: '...' });\n * } catch (e) {\n * if (e instanceof MorphValidationError) {\n * console.log('Validation failed:', e.field, e.message);\n * } else if (e instanceof MorphAPIError) {\n * console.log('API error:', e.code, e.statusCode);\n * }\n * }\n * ```\n */\n\n/**\n * Error codes for Morph Browser SDK\n */\nexport type MorphErrorCode =\n // Validation errors\n | 'validation_error'\n | 'invalid_parameter'\n | 'missing_required_field'\n // Authentication errors\n | 'authentication_required'\n | 'invalid_api_key'\n | 'insufficient_permissions'\n // Resource errors\n | 'profile_not_found'\n | 'session_not_found'\n | 'resource_not_found'\n // Limit errors\n | 'profile_limit_exceeded'\n | 'rate_limit_exceeded'\n // Session errors\n | 'session_expired'\n | 'session_save_failed'\n // Network errors\n | 'network_error'\n | 'timeout'\n | 'service_unavailable';\n\n/**\n * Base error class for all Morph SDK errors.\n */\nexport class MorphError extends Error {\n /** Error code for programmatic handling */\n readonly code: MorphErrorCode;\n /** Original cause of the error, if any */\n readonly cause?: Error;\n\n constructor(message: string, code: MorphErrorCode, cause?: Error) {\n super(message);\n this.name = 'MorphError';\n this.code = code;\n this.cause = cause;\n\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n\n /**\n * Returns a JSON representation of the error for logging.\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n cause: this.cause?.message,\n };\n }\n}\n\n/**\n * Error thrown when API request validation fails.\n */\nexport class MorphValidationError extends MorphError {\n /** The field that failed validation */\n readonly field?: string;\n\n constructor(message: string, field?: string) {\n super(message, 'validation_error');\n this.name = 'MorphValidationError';\n this.field = field;\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n field: this.field,\n };\n }\n}\n\n/**\n * Error thrown when an API request fails.\n */\nexport class MorphAPIError extends MorphError {\n /** HTTP status code */\n readonly statusCode: number;\n /** Request ID for debugging (if available) */\n readonly requestId?: string;\n /** Raw response body */\n readonly rawResponse?: string;\n\n constructor(\n message: string,\n code: MorphErrorCode,\n statusCode: number,\n options?: {\n requestId?: string;\n rawResponse?: string;\n cause?: Error;\n }\n ) {\n super(message, code, options?.cause);\n this.name = 'MorphAPIError';\n this.statusCode = statusCode;\n this.requestId = options?.requestId;\n this.rawResponse = options?.rawResponse;\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n statusCode: this.statusCode,\n requestId: this.requestId,\n };\n }\n}\n\n/**\n * Error thrown when authentication fails.\n */\nexport class MorphAuthenticationError extends MorphAPIError {\n constructor(message: string = 'Authentication required. Please provide a valid API key.') {\n super(message, 'authentication_required', 401);\n this.name = 'MorphAuthenticationError';\n }\n}\n\n/**\n * Error thrown when rate limit is exceeded.\n */\nexport class MorphRateLimitError extends MorphAPIError {\n /** When the rate limit resets (Unix timestamp) */\n readonly resetAt?: number;\n /** Number of seconds until reset */\n readonly retryAfter?: number;\n\n constructor(\n message: string = 'Rate limit exceeded. Please retry later.',\n options?: {\n resetAt?: number;\n retryAfter?: number;\n requestId?: string;\n }\n ) {\n super(message, 'rate_limit_exceeded', 429, { requestId: options?.requestId });\n this.name = 'MorphRateLimitError';\n this.resetAt = options?.resetAt;\n this.retryAfter = options?.retryAfter;\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n resetAt: this.resetAt,\n retryAfter: this.retryAfter,\n };\n }\n}\n\n/**\n * Error thrown when a resource is not found.\n */\nexport class MorphNotFoundError extends MorphAPIError {\n /** The type of resource that was not found */\n readonly resourceType: string;\n /** The ID of the resource that was not found */\n readonly resourceId?: string;\n\n constructor(resourceType: string, resourceId?: string) {\n const message = resourceId\n ? `${resourceType} '${resourceId}' not found`\n : `${resourceType} not found`;\n super(message, 'resource_not_found', 404);\n this.name = 'MorphNotFoundError';\n this.resourceType = resourceType;\n this.resourceId = resourceId;\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n resourceType: this.resourceType,\n resourceId: this.resourceId,\n };\n }\n}\n\n/**\n * Error thrown when profile limit is exceeded.\n */\nexport class MorphProfileLimitError extends MorphAPIError {\n /** Current number of profiles */\n readonly currentCount?: number;\n /** Maximum allowed profiles for the plan */\n readonly maxAllowed?: number;\n\n constructor(\n message: string = 'Profile limit exceeded for your plan.',\n options?: {\n currentCount?: number;\n maxAllowed?: number;\n requestId?: string;\n }\n ) {\n super(message, 'profile_limit_exceeded', 403, { requestId: options?.requestId });\n this.name = 'MorphProfileLimitError';\n this.currentCount = options?.currentCount;\n this.maxAllowed = options?.maxAllowed;\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n currentCount: this.currentCount,\n maxAllowed: this.maxAllowed,\n };\n }\n}\n\n/**\n * Parse an API error response and return the appropriate error class.\n */\nexport function parseAPIError(\n statusCode: number,\n responseText: string,\n requestId?: string\n): MorphAPIError {\n // Try to parse JSON error response\n let errorData: { detail?: string; code?: string; message?: string } = {};\n try {\n errorData = JSON.parse(responseText);\n } catch {\n // Not JSON, use raw text\n }\n\n const message = errorData.detail || errorData.message || responseText || 'Unknown error';\n const code = errorData.code;\n\n // Map status codes to specific error classes\n switch (statusCode) {\n case 401:\n return new MorphAuthenticationError(message);\n\n case 403:\n if (code === 'profile_limit_exceeded' || message.toLowerCase().includes('limit')) {\n return new MorphProfileLimitError(message, { requestId });\n }\n return new MorphAPIError(message, 'insufficient_permissions', statusCode, { requestId, rawResponse: responseText });\n\n case 404:\n if (message.toLowerCase().includes('profile')) {\n return new MorphNotFoundError('Profile', undefined);\n }\n if (message.toLowerCase().includes('session')) {\n return new MorphNotFoundError('Session', undefined);\n }\n return new MorphAPIError(message, 'resource_not_found', statusCode, { requestId, rawResponse: responseText });\n\n case 429:\n return new MorphRateLimitError(message, { requestId });\n\n case 422:\n return new MorphAPIError(message, 'validation_error', statusCode, { requestId, rawResponse: responseText });\n\n case 500:\n case 502:\n case 503:\n case 504:\n return new MorphAPIError(message, 'service_unavailable', statusCode, { requestId, rawResponse: responseText });\n\n default:\n return new MorphAPIError(message, 'network_error', statusCode, { requestId, rawResponse: responseText });\n }\n}\n","/**\n * Type definitions for browser profiles.\n *\n * All types use camelCase for consistency with TypeScript conventions.\n * API responses are automatically transformed from snake_case.\n */\n\n/**\n * A browser profile that stores login state (cookies, localStorage).\n *\n * @example\n * ```typescript\n * const profile = await morph.browser.profiles.getProfile('profile-id');\n * console.log(profile.cookieDomains); // ['linkedin.com', 'google.com']\n * ```\n */\nexport interface Profile {\n /** Unique profile identifier (UUID) */\n id: string;\n /** Human-readable profile name */\n name: string;\n /** Repository ID this profile is associated with */\n repoId: string;\n /** List of domains with stored cookies */\n cookieDomains?: string[];\n /** ISO 8601 timestamp of last use */\n lastUsedAt?: string;\n /** ISO 8601 timestamp of creation */\n createdAt: string;\n /** ISO 8601 timestamp of last update */\n updatedAt: string;\n}\n\n/**\n * Input for creating a new profile.\n *\n * @example\n * ```typescript\n * await morph.browser.profiles.createProfile({\n * name: 'LinkedIn Production',\n * repoId: 'repo-uuid-here'\n * });\n * ```\n */\nexport interface CreateProfileInput {\n /** Profile name (1-100 characters) */\n name: string;\n /** Repository ID - profiles are repo-specific */\n repoId: string;\n}\n\n/**\n * Input for updating a profile.\n */\nexport interface UpdateProfileInput {\n /** New profile name (1-100 characters) */\n name?: string;\n}\n\n/**\n * Response for listing profiles.\n */\nexport interface ProfileListResponse {\n profiles: Profile[];\n}\n\n/**\n * A browser session for profile setup.\n *\n * @example\n * ```typescript\n * const session = await morph.browser.profiles.startSession();\n * console.log('Sign in at:', session.debugUrl);\n * // After user signs in...\n * await morph.browser.profiles.saveSession(session.sessionId, profile.id);\n * ```\n */\nexport interface ProfileSession {\n /** Unique session identifier */\n sessionId: string;\n /** Live session URL for viewing/interacting with the browser */\n debugUrl: string;\n}\n\n/**\n * Input for starting a profile session.\n */\nexport interface ProfileSessionInput {\n /** Optional profile ID to update an existing profile */\n profileId?: string;\n}\n\n/**\n * Input for saving a profile session.\n */\nexport interface SaveProfileSessionInput {\n /** The browser session ID to extract state from */\n sessionId: string;\n /** The profile ID to save state to */\n profileId: string;\n}\n\n/**\n * Response for profile state URL.\n */\nexport interface ProfileStateResponse {\n /** Profile ID */\n profileId: string;\n /** Presigned URL to download the profile state (expires in `expiresIn` seconds) */\n stateUrl: string;\n /** URL expiry time in seconds */\n expiresIn: number;\n}\n\n/**\n * Profile with convenience methods attached.\n *\n * @example\n * ```typescript\n * const profile = await morph.browser.profiles.getProfile('id');\n * const state = await profile.getState();\n * await profile.delete();\n * ```\n */\nexport interface ProfileWithMethods extends Profile {\n /** Get presigned URL for profile state */\n getState: () => Promise<ProfileStateResponse>;\n /** Delete this profile */\n delete: () => Promise<void>;\n}\n\n// ============================================================================\n// Internal API types (snake_case - matches backend response)\n// ============================================================================\n\n/** @internal API response format */\nexport interface APIProfile {\n id: string;\n name: string;\n repo_id: string;\n cookie_domains?: string[];\n last_used_at?: string;\n created_at: string;\n updated_at: string;\n}\n\n/** @internal API response format */\nexport interface APIProfileSession {\n session_id: string;\n debug_url?: string;\n debugUrl?: string; // Backend may return either\n}\n\n/** @internal API response format */\nexport interface APIProfileStateResponse {\n profile_id: string;\n state_url: string;\n expires_in: number;\n}\n\n// ============================================================================\n// Transformers\n// ============================================================================\n\n/**\n * Transform API profile response to SDK format.\n * @internal\n */\nexport function transformProfile(api: APIProfile): Profile {\n return {\n id: api.id,\n name: api.name,\n repoId: api.repo_id,\n cookieDomains: api.cookie_domains,\n lastUsedAt: api.last_used_at,\n createdAt: api.created_at,\n updatedAt: api.updated_at,\n };\n}\n\n/**\n * Transform SDK input to API format.\n * @internal\n */\nexport function transformCreateInput(input: CreateProfileInput): { name: string; repo_id: string } {\n return {\n name: input.name,\n repo_id: input.repoId,\n };\n}\n\n/**\n * Transform API session response to SDK format.\n * @internal\n */\nexport function transformSession(api: APIProfileSession): ProfileSession {\n return {\n sessionId: api.session_id,\n debugUrl: api.debug_url || api.debugUrl || '',\n };\n}\n\n/**\n * Transform SDK save input to API format.\n * @internal\n */\nexport function transformSaveInput(input: SaveProfileSessionInput): { session_id: string; profile_id: string } {\n return {\n session_id: input.sessionId,\n profile_id: input.profileId,\n };\n}\n\n/**\n * Transform API state response to SDK format.\n * @internal\n */\nexport function transformStateResponse(api: APIProfileStateResponse): ProfileStateResponse {\n return {\n profileId: api.profile_id,\n stateUrl: api.state_url,\n expiresIn: api.expires_in,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../tools/browser/profiles/core.ts","../../../../tools/utils/resilience.ts","../../../../tools/browser/errors.ts","../../../../tools/browser/profiles/types.ts"],"sourcesContent":["/**\n * ProfilesClient - Manage browser profiles for storing login state.\n *\n * @example\n * ```typescript\n * const morph = new MorphClient({ apiKey: '...' });\n *\n * // Create a profile + live setup session\n * const setup = await morph.browser.profiles.createProfile({\n * name: 'LinkedIn',\n * repoId: 'owner/repo'\n * });\n *\n * console.log('Sign in at:', setup.session.debugUrl);\n * // User logs in, then explicitly save\n * await setup.save();\n *\n * // Use profile in browser tasks\n * await morph.browser.execute({\n * task: 'Check notifications',\n * url: 'https://linkedin.com',\n * profileId: profile.id\n * });\n * ```\n */\n\nimport { fetchWithRetry } from '../../utils/resilience.js';\nimport type { BrowserConfig } from '../types.js';\nimport {\n MorphValidationError,\n MorphAuthenticationError,\n parseAPIError,\n} from '../errors.js';\nimport type {\n Profile,\n ProfileSetup,\n ProfileWithMethods,\n CreateProfileInput,\n ProfileListResponse,\n ProfileSession,\n ProfileSessionInput,\n SaveProfileSessionInput,\n ProfileStateResponse,\n RepoSummary,\n RepoListResponse,\n APIProfile,\n APIProfileSession,\n APIProfileStateResponse,\n} from './types.js';\nimport {\n transformProfile,\n transformCreateInput,\n transformSession,\n transformSaveInput,\n transformStateResponse,\n} from './types.js';\n\nconst DEFAULT_API_URL = process.env.MORPH_ENVIRONMENT === 'DEV'\n ? 'http://localhost:8000'\n : 'https://browser.morphllm.com';\n\n/**\n * ProfilesClient class for managing browser profiles.\n *\n * Access via `morph.browser.profiles` on a MorphClient instance.\n */\nexport class ProfilesClient {\n private config: BrowserConfig;\n\n constructor(config: BrowserConfig) {\n this.config = config;\n }\n\n /**\n * Create a new browser profile and immediately start a live session.\n *\n * @param input - Profile creation parameters\n * @returns Profile setup handle with live URL + save()\n * @throws {MorphValidationError} If input validation fails\n * @throws {MorphProfileLimitError} If profile limit is exceeded\n * @throws {MorphAuthenticationError} If API key is missing or invalid\n *\n * @example\n * ```typescript\n * const setup = await morph.browser.profiles.createProfile({\n * name: 'LinkedIn Production',\n * repoId: 'owner/repo'\n * });\n * console.log(setup.session.debugUrl);\n * await setup.save();\n * ```\n */\n async createProfile(input: CreateProfileInput): Promise<ProfileSetup> {\n return createProfile(input, this.config);\n }\n\n /**\n * List all profiles for the authenticated user.\n *\n * @param repoId - Optional repository ID to filter by\n * @returns Array of profiles\n *\n * @example\n * ```typescript\n * // List all profiles\n * const allProfiles = await morph.browser.profiles.listProfiles();\n *\n * // List profiles for a specific repo\n * const repoProfiles = await morph.browser.profiles.listProfiles('owner/repo');\n * ```\n */\n async listProfiles(repoId?: string): Promise<Profile[]> {\n return listProfiles(this.config, repoId);\n }\n\n /**\n * Get a profile by ID with convenience methods.\n *\n * @param id - Profile ID\n * @returns Profile with attached methods\n * @throws {MorphNotFoundError} If profile is not found\n *\n * @example\n * ```typescript\n * const profile = await morph.browser.profiles.getProfile('profile-id');\n * const state = await profile.getState();\n * await profile.delete();\n * ```\n */\n async getProfile(id: string): Promise<ProfileWithMethods> {\n return getProfile(id, this.config);\n }\n\n /**\n * Update a profile by opening a live session (no rename).\n *\n * @param id - Profile ID\n * @returns Profile setup handle with live URL + save()\n */\n async updateProfile(id: string): Promise<ProfileSetup> {\n return updateProfile(id, this.config);\n }\n\n /**\n * Delete a profile.\n *\n * @param id - Profile ID\n * @throws {MorphNotFoundError} If profile is not found\n */\n async deleteProfile(id: string): Promise<void> {\n return deleteProfile(id, this.config);\n }\n\n /**\n * Start a browser session for profile setup.\n *\n * Returns a live URL where the user can sign into accounts.\n * After signing in, call `saveSession` to persist the state.\n *\n * @param input - Optional session parameters\n * @returns Session with debug URL\n *\n * @example\n * ```typescript\n * const session = await morph.browser.profiles.startSession();\n * console.log('Sign in at:', session.debugUrl);\n * // Open debugUrl in browser, user signs in...\n * await morph.browser.profiles.saveSession(session.sessionId, profile.id);\n * ```\n */\n async startSession(input?: ProfileSessionInput): Promise<ProfileSession> {\n return startProfileSession(this.config, input);\n }\n\n /**\n * Save browser state from a session to a profile.\n *\n * Call this after the user is done signing into accounts.\n * Extracts cookies, localStorage, and sessionStorage.\n *\n * @param sessionId - Browser session ID from startSession\n * @param profileId - Profile ID to save state to\n * @returns Updated profile with cookie domains\n */\n async saveSession(sessionId: string, profileId: string): Promise<Profile> {\n return saveProfileSession({ sessionId, profileId }, this.config);\n }\n\n /**\n * List available repo IDs (discovery).\n *\n * @returns Repo summaries with profile counts\n */\n async listRepos(): Promise<RepoSummary[]> {\n return listRepos(this.config);\n }\n\n /**\n * Get the presigned URL for a profile's state.\n *\n * Use this to download the raw state JSON for debugging\n * or to restore state manually.\n *\n * @param profileId - Profile ID\n * @returns State URL with expiry information\n */\n async getProfileState(profileId: string): Promise<ProfileStateResponse> {\n return getProfileState(profileId, this.config);\n }\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nfunction validateCreateInput(input: CreateProfileInput): void {\n if (!input.name || typeof input.name !== 'string') {\n throw new MorphValidationError('name is required', 'name');\n }\n\n const trimmedName = input.name.trim();\n if (trimmedName.length === 0) {\n throw new MorphValidationError('name cannot be empty', 'name');\n }\n\n if (trimmedName.length > 100) {\n throw new MorphValidationError('name must be 100 characters or less', 'name');\n }\n\n if (!input.repoId || typeof input.repoId !== 'string') {\n throw new MorphValidationError('repoId is required', 'repoId');\n }\n\n if (input.repoId.trim().length === 0) {\n throw new MorphValidationError('repoId cannot be empty', 'repoId');\n }\n}\n\nfunction validateId(id: string, fieldName: string): void {\n if (!id || typeof id !== 'string') {\n throw new MorphValidationError(`${fieldName} is required`, fieldName);\n }\n\n if (id.trim().length === 0) {\n throw new MorphValidationError(`${fieldName} cannot be empty`, fieldName);\n }\n}\n\n// ============================================================================\n// Standalone Functions\n// ============================================================================\n\n/**\n * Create a new browser profile.\n */\nexport async function createProfile(\n input: CreateProfileInput,\n config: BrowserConfig = {}\n): Promise<ProfileSetup> {\n validateCreateInput(input);\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify(transformCreateInput(input)),\n },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiProfile: APIProfile = await response.json();\n const profile = transformProfile(apiProfile);\n const session = await startProfileSession(config, { profileId: profile.id });\n return buildProfileSetup(profile, session, config);\n}\n\n/**\n * List all profiles for the authenticated user.\n */\nexport async function listProfiles(\n config: BrowserConfig = {},\n repoId?: string\n): Promise<Profile[]> {\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const url = repoId\n ? `${apiUrl}/profiles?repo_id=${encodeURIComponent(repoId)}`\n : `${apiUrl}/profiles`;\n\n const response = await fetchWithRetry(\n url,\n { method: 'GET', headers },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const data: { profiles: APIProfile[] } = await response.json();\n return data.profiles.map(transformProfile);\n}\n\n/**\n * Get a profile by ID with convenience methods.\n */\nexport async function getProfile(\n id: string,\n config: BrowserConfig = {}\n): Promise<ProfileWithMethods> {\n validateId(id, 'id');\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/${encodeURIComponent(id)}`,\n { method: 'GET', headers },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiProfile: APIProfile = await response.json();\n const profile = transformProfile(apiProfile);\n\n // Attach convenience methods\n return {\n ...profile,\n getState: () => getProfileState(id, config),\n delete: () => deleteProfile(id, config),\n };\n}\n\n/**\n * Update a profile.\n */\nexport async function updateProfile(\n id: string,\n config: BrowserConfig = {}\n): Promise<ProfileSetup> {\n validateId(id, 'id');\n const profile = await fetchProfile(id, config);\n const session = await startProfileSession(config, { profileId: profile.id });\n return buildProfileSetup(profile, session, config);\n}\n\n/**\n * Delete a profile.\n */\nexport async function deleteProfile(\n id: string,\n config: BrowserConfig = {}\n): Promise<void> {\n validateId(id, 'id');\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/${encodeURIComponent(id)}`,\n { method: 'DELETE', headers },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n}\n\n/**\n * Start a browser session for profile setup.\n */\nexport async function startProfileSession(\n config: BrowserConfig = {},\n input?: ProfileSessionInput\n): Promise<ProfileSession> {\n if (!config.apiKey) {\n throw new MorphAuthenticationError();\n }\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n // Transform input if provided\n const body = input?.profileId\n ? { profile_id: input.profileId }\n : {};\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/session/start`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiSession: APIProfileSession = await response.json();\n return transformSession(apiSession);\n}\n\n/**\n * Save browser state from a session to a profile.\n */\nexport async function saveProfileSession(\n input: SaveProfileSessionInput,\n config: BrowserConfig = {}\n): Promise<Profile> {\n validateId(input.sessionId, 'sessionId');\n validateId(input.profileId, 'profileId');\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/session/save`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify(transformSaveInput(input)),\n },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiProfile: APIProfile = await response.json();\n return transformProfile(apiProfile);\n}\n\n/**\n * List repo IDs available to the authenticated user/org.\n */\nexport async function listRepos(config: BrowserConfig = {}): Promise<RepoSummary[]> {\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/repos`,\n { method: 'GET', headers },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const data = await response.json();\n const repos = Array.isArray(data?.repos) ? data.repos : [];\n\n return repos.map((repo: any) => ({\n repoId: repo.repo_id,\n repoFullName: repo.repo_full_name,\n profileCount: repo.profile_count ?? 0,\n })) satisfies RepoSummary[];\n}\n\n/**\n * Get the presigned URL for a profile's state.\n */\nexport async function getProfileState(\n profileId: string,\n config: BrowserConfig = {}\n): Promise<ProfileStateResponse> {\n validateId(profileId, 'profileId');\n\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/${encodeURIComponent(profileId)}/state`,\n { method: 'GET', headers },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiState: APIProfileStateResponse = await response.json();\n return transformStateResponse(apiState);\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction buildHeaders(config: BrowserConfig): Record<string, string> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n }\n return headers;\n}\n\nasync function fetchProfile(id: string, config: BrowserConfig): Promise<Profile> {\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const headers = buildHeaders(config);\n\n const response = await fetchWithRetry(\n `${apiUrl}/profiles/${encodeURIComponent(id)}`,\n { method: 'GET', headers },\n config.retryConfig\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n const requestId = response.headers.get('x-request-id') || undefined;\n throw parseAPIError(response.status, errorText, requestId);\n }\n\n const apiProfile: APIProfile = await response.json();\n return transformProfile(apiProfile);\n}\n\nfunction buildProfileSetup(profile: Profile, session: ProfileSession, config: BrowserConfig): ProfileSetup {\n return {\n profile,\n session,\n save: () => saveProfileSession({ sessionId: session.sessionId, profileId: profile.id }, config),\n };\n}\n","/**\n * Resilience utilities for retry logic and timeout handling\n */\n\nexport interface RetryConfig {\n maxRetries?: number; // Default: 3\n initialDelay?: number; // Default: 1000ms\n maxDelay?: number; // Default: 30000ms\n backoffMultiplier?: number; // Default: 2\n retryableErrors?: string[]; // Default: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND']\n onRetry?: (attempt: number, error: Error) => void;\n}\n\nconst DEFAULT_RETRY_CONFIG: Required<Omit<RetryConfig, 'onRetry'>> = {\n maxRetries: 3,\n initialDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n retryableErrors: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND'],\n};\n\n/**\n * Retry a fetch request with exponential backoff\n * \n * @param url - Request URL\n * @param options - Fetch options\n * @param retryConfig - Retry configuration\n * @returns Response from fetch\n * \n * @example\n * ```typescript\n * const response = await fetchWithRetry(\n * 'https://api.example.com/data',\n * { method: 'POST', body: JSON.stringify(data) },\n * { maxRetries: 5, initialDelay: 500 }\n * );\n * ```\n */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n retryConfig: RetryConfig = {}\n): Promise<Response> {\n const {\n maxRetries = DEFAULT_RETRY_CONFIG.maxRetries,\n initialDelay = DEFAULT_RETRY_CONFIG.initialDelay,\n maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,\n backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,\n retryableErrors = DEFAULT_RETRY_CONFIG.retryableErrors,\n onRetry,\n } = retryConfig;\n\n let lastError: Error | null = null;\n let delay = initialDelay;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, options);\n \n // Retry on 429 (rate limit) or 503 (service unavailable)\n if (response.status === 429 || response.status === 503) {\n if (attempt < maxRetries) {\n // Check for Retry-After header\n const retryAfter = response.headers.get('Retry-After');\n const waitTime = retryAfter \n ? parseInt(retryAfter) * 1000 \n : Math.min(delay, maxDelay);\n \n const error = new Error(`HTTP ${response.status}: Retrying after ${waitTime}ms`);\n if (onRetry) {\n onRetry(attempt + 1, error);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n continue;\n }\n }\n\n return response;\n } catch (error) {\n lastError = error as Error;\n \n // Check if error is retryable\n const isRetryable = retryableErrors.some(errType => \n lastError?.message?.includes(errType)\n );\n\n if (!isRetryable || attempt === maxRetries) {\n throw lastError;\n }\n\n // Exponential backoff\n const waitTime = Math.min(delay, maxDelay);\n if (onRetry) {\n onRetry(attempt + 1, lastError);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n }\n }\n\n throw lastError || new Error('Max retries exceeded');\n}\n\n/**\n * Add timeout to any promise\n * \n * @param promise - Promise to wrap with timeout\n * @param timeoutMs - Timeout in milliseconds\n * @param errorMessage - Optional custom error message\n * @returns Promise that rejects if timeout is reached\n * \n * @example\n * ```typescript\n * const result = await withTimeout(\n * fetchData(),\n * 5000,\n * 'Data fetch timed out'\n * );\n * ```\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n errorMessage?: string\n): Promise<T> {\n let timeoutId: NodeJS.Timeout | number;\n \n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(errorMessage || `Operation timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n const result = await Promise.race([promise, timeoutPromise]);\n clearTimeout(timeoutId!);\n return result;\n } catch (error) {\n clearTimeout(timeoutId!);\n throw error;\n }\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Unified error type for all tools\n */\nexport class MorphError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public retryable: boolean = false\n ) {\n super(message);\n this.name = 'MorphError';\n }\n}\n\n\n","/**\n * Custom error classes for browser automation and profiles.\n *\n * @example\n * ```typescript\n * try {\n * await morph.browser.profiles.createProfile({ name: '', repoId: 'owner/repo' });\n * } catch (e) {\n * if (e instanceof MorphValidationError) {\n * console.log('Validation failed:', e.field, e.message);\n * } else if (e instanceof MorphAPIError) {\n * console.log('API error:', e.code, e.statusCode);\n * }\n * }\n * ```\n */\n\n/**\n * Error codes for Morph Browser SDK\n */\nexport type MorphErrorCode =\n // Validation errors\n | 'validation_error'\n | 'invalid_parameter'\n | 'missing_required_field'\n // Authentication errors\n | 'authentication_required'\n | 'invalid_api_key'\n | 'insufficient_permissions'\n // Resource errors\n | 'profile_not_found'\n | 'session_not_found'\n | 'resource_not_found'\n // Limit errors\n | 'profile_limit_exceeded'\n | 'rate_limit_exceeded'\n // Session errors\n | 'session_expired'\n | 'session_save_failed'\n // Network errors\n | 'network_error'\n | 'timeout'\n | 'service_unavailable';\n\n/**\n * Base error class for all Morph SDK errors.\n */\nexport class MorphError extends Error {\n /** Error code for programmatic handling */\n readonly code: MorphErrorCode;\n /** Original cause of the error, if any */\n readonly cause?: Error;\n\n constructor(message: string, code: MorphErrorCode, cause?: Error) {\n super(message);\n this.name = 'MorphError';\n this.code = code;\n this.cause = cause;\n\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n\n /**\n * Returns a JSON representation of the error for logging.\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n cause: this.cause?.message,\n };\n }\n}\n\n/**\n * Error thrown when API request validation fails.\n */\nexport class MorphValidationError extends MorphError {\n /** The field that failed validation */\n readonly field?: string;\n\n constructor(message: string, field?: string) {\n super(message, 'validation_error');\n this.name = 'MorphValidationError';\n this.field = field;\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n field: this.field,\n };\n }\n}\n\n/**\n * Error thrown when an API request fails.\n */\nexport class MorphAPIError extends MorphError {\n /** HTTP status code */\n readonly statusCode: number;\n /** Request ID for debugging (if available) */\n readonly requestId?: string;\n /** Raw response body */\n readonly rawResponse?: string;\n\n constructor(\n message: string,\n code: MorphErrorCode,\n statusCode: number,\n options?: {\n requestId?: string;\n rawResponse?: string;\n cause?: Error;\n }\n ) {\n super(message, code, options?.cause);\n this.name = 'MorphAPIError';\n this.statusCode = statusCode;\n this.requestId = options?.requestId;\n this.rawResponse = options?.rawResponse;\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n statusCode: this.statusCode,\n requestId: this.requestId,\n };\n }\n}\n\n/**\n * Error thrown when authentication fails.\n */\nexport class MorphAuthenticationError extends MorphAPIError {\n constructor(message: string = 'Authentication required. Please provide a valid API key.') {\n super(message, 'authentication_required', 401);\n this.name = 'MorphAuthenticationError';\n }\n}\n\n/**\n * Error thrown when rate limit is exceeded.\n */\nexport class MorphRateLimitError extends MorphAPIError {\n /** When the rate limit resets (Unix timestamp) */\n readonly resetAt?: number;\n /** Number of seconds until reset */\n readonly retryAfter?: number;\n\n constructor(\n message: string = 'Rate limit exceeded. Please retry later.',\n options?: {\n resetAt?: number;\n retryAfter?: number;\n requestId?: string;\n }\n ) {\n super(message, 'rate_limit_exceeded', 429, { requestId: options?.requestId });\n this.name = 'MorphRateLimitError';\n this.resetAt = options?.resetAt;\n this.retryAfter = options?.retryAfter;\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n resetAt: this.resetAt,\n retryAfter: this.retryAfter,\n };\n }\n}\n\n/**\n * Error thrown when a resource is not found.\n */\nexport class MorphNotFoundError extends MorphAPIError {\n /** The type of resource that was not found */\n readonly resourceType: string;\n /** The ID of the resource that was not found */\n readonly resourceId?: string;\n\n constructor(resourceType: string, resourceId?: string) {\n const message = resourceId\n ? `${resourceType} '${resourceId}' not found`\n : `${resourceType} not found`;\n super(message, 'resource_not_found', 404);\n this.name = 'MorphNotFoundError';\n this.resourceType = resourceType;\n this.resourceId = resourceId;\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n resourceType: this.resourceType,\n resourceId: this.resourceId,\n };\n }\n}\n\n/**\n * Error thrown when profile limit is exceeded.\n */\nexport class MorphProfileLimitError extends MorphAPIError {\n /** Current number of profiles */\n readonly currentCount?: number;\n /** Maximum allowed profiles for the plan */\n readonly maxAllowed?: number;\n\n constructor(\n message: string = 'Profile limit exceeded for your plan.',\n options?: {\n currentCount?: number;\n maxAllowed?: number;\n requestId?: string;\n }\n ) {\n super(message, 'profile_limit_exceeded', 403, { requestId: options?.requestId });\n this.name = 'MorphProfileLimitError';\n this.currentCount = options?.currentCount;\n this.maxAllowed = options?.maxAllowed;\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n currentCount: this.currentCount,\n maxAllowed: this.maxAllowed,\n };\n }\n}\n\n/**\n * Parse an API error response and return the appropriate error class.\n */\nexport function parseAPIError(\n statusCode: number,\n responseText: string,\n requestId?: string\n): MorphAPIError {\n // Try to parse JSON error response\n let errorData: { detail?: string; code?: string; message?: string } = {};\n try {\n errorData = JSON.parse(responseText);\n } catch {\n // Not JSON, use raw text\n }\n\n const message = errorData.detail || errorData.message || responseText || 'Unknown error';\n const code = errorData.code;\n\n // Map status codes to specific error classes\n switch (statusCode) {\n case 401:\n return new MorphAuthenticationError(message);\n\n case 403:\n if (code === 'profile_limit_exceeded' || message.toLowerCase().includes('limit')) {\n return new MorphProfileLimitError(message, { requestId });\n }\n return new MorphAPIError(message, 'insufficient_permissions', statusCode, { requestId, rawResponse: responseText });\n\n case 404:\n if (message.toLowerCase().includes('profile')) {\n return new MorphNotFoundError('Profile', undefined);\n }\n if (message.toLowerCase().includes('session')) {\n return new MorphNotFoundError('Session', undefined);\n }\n return new MorphAPIError(message, 'resource_not_found', statusCode, { requestId, rawResponse: responseText });\n\n case 429:\n return new MorphRateLimitError(message, { requestId });\n\n case 422:\n return new MorphAPIError(message, 'validation_error', statusCode, { requestId, rawResponse: responseText });\n\n case 500:\n case 502:\n case 503:\n case 504:\n return new MorphAPIError(message, 'service_unavailable', statusCode, { requestId, rawResponse: responseText });\n\n default:\n return new MorphAPIError(message, 'network_error', statusCode, { requestId, rawResponse: responseText });\n }\n}\n","/**\n * Type definitions for browser profiles.\n *\n * All types use camelCase for SDK ergonomics.\n * API responses are normalized from snake_case to camelCase.\n */\n\n/**\n * A browser profile that stores login state (cookies, localStorage).\n *\n * @example\n * ```typescript\n * const profile = await morph.browser.profiles.getProfile('profile-id');\n * console.log(profile.cookieDomains); // ['linkedin.com', 'google.com']\n * ```\n */\nexport interface Profile {\n /** Unique profile identifier (UUID) */\n id: string;\n /** Human-readable profile name */\n name: string;\n /** Repository ID this profile is associated with */\n repoId: string;\n /** List of domains with stored cookies */\n cookieDomains?: string[];\n /** ISO 8601 timestamp of last use */\n lastUsedAt?: string;\n /** ISO 8601 timestamp of creation */\n createdAt: string;\n /** ISO 8601 timestamp of last update */\n updatedAt: string;\n}\n\n/**\n * Input for creating a new profile.\n *\n * @example\n * ```typescript\n * await morph.browser.profiles.createProfile({\n * name: 'LinkedIn Production',\n * repoId: 'owner/repo'\n * });\n * ```\n */\nexport interface CreateProfileInput {\n /** Profile name (1-100 characters) */\n name: string;\n /** Repository ID - profiles are repo-specific */\n repoId: string;\n}\n\n/**\n * Input for updating a profile.\n */\n// Intentionally no UpdateProfileInput: update means \"open a live session\"\n\n/**\n * Response for listing profiles.\n */\nexport interface ProfileListResponse {\n profiles: Profile[];\n}\n\n/**\n * A browser session for profile setup.\n *\n * @example\n * ```typescript\n * const session = await morph.browser.profiles.startSession();\n * console.log('Sign in at:', session.debugUrl);\n * // After user signs in...\n * await morph.browser.profiles.saveSession(session.sessionId, profile.id);\n * ```\n */\nexport interface ProfileSession {\n /** Unique session identifier */\n sessionId: string;\n /** Live session URL for viewing/interacting with the browser */\n debugUrl: string;\n}\n\n/**\n * Profile setup handle returned by create/update.\n *\n * Provides a live URL for login and an explicit save() helper.\n */\nexport interface ProfileSetup {\n profile: Profile;\n session: ProfileSession;\n save: () => Promise<Profile>;\n}\n/**\n * Input for starting a profile session.\n */\nexport interface ProfileSessionInput {\n /** Optional profile ID to update an existing profile */\n profileId?: string;\n}\n\n/**\n * Input for saving a profile session.\n */\nexport interface SaveProfileSessionInput {\n /** The browser session ID to extract state from */\n sessionId: string;\n /** The profile ID to save state to */\n profileId: string;\n}\n\n/**\n * Response for profile state URL.\n */\nexport interface ProfileStateResponse {\n /** Profile ID */\n profileId: string;\n /** Presigned URL to download the profile state (expires in `expiresIn` seconds) */\n stateUrl: string;\n /** URL expiry time in seconds */\n expiresIn: number;\n}\n\n/**\n * Repo summary for profile discovery.\n */\nexport interface RepoSummary {\n repoId: string;\n repoFullName?: string;\n profileCount: number;\n}\n\n/**\n * Response for repo discovery.\n */\nexport interface RepoListResponse {\n repos: RepoSummary[];\n}\n\n/**\n * Profile with convenience methods attached.\n *\n * @example\n * ```typescript\n * const profile = await morph.browser.profiles.getProfile('id');\n * const state = await profile.getState();\n * await profile.delete();\n * ```\n */\nexport interface ProfileWithMethods extends Profile {\n /** Get presigned URL for profile state */\n getState: () => Promise<ProfileStateResponse>;\n /** Delete this profile */\n delete: () => Promise<void>;\n}\n\n// ============================================================================\n// Internal API types (snake_case - matches backend response)\n// ============================================================================\n\n/** @internal API response format */\nexport interface APIProfile {\n id: string;\n name: string;\n repo_id: string;\n cookie_domains?: string[];\n last_used_at?: string;\n created_at: string;\n updated_at: string;\n}\n\n/** @internal API response format */\nexport interface APIProfileSession {\n session_id: string;\n debug_url?: string;\n}\n\n/** @internal API response format */\nexport interface APIProfileStateResponse {\n profile_id: string;\n state_url: string;\n expires_in: number;\n}\n\n// ============================================================================\n// Transformers\n// ============================================================================\n\n/**\n * Transform API profile response to SDK format.\n * @internal\n */\nexport function transformProfile(api: APIProfile): Profile {\n return {\n id: api.id,\n name: api.name,\n repoId: api.repo_id,\n cookieDomains: api.cookie_domains,\n lastUsedAt: api.last_used_at,\n createdAt: api.created_at,\n updatedAt: api.updated_at,\n };\n}\n\n/**\n * Transform SDK input to API format.\n * @internal\n */\nexport function transformCreateInput(input: CreateProfileInput): { name: string; repo_id: string } {\n return {\n name: input.name,\n repo_id: input.repoId,\n };\n}\n\n/**\n * Transform API session response to SDK format.\n * @internal\n */\nexport function transformSession(api: APIProfileSession): ProfileSession {\n return {\n sessionId: api.session_id,\n debugUrl: api.debug_url || '',\n };\n}\n\n/**\n * Transform SDK save input to API format.\n * @internal\n */\nexport function transformSaveInput(input: SaveProfileSessionInput): { session_id: string; profile_id: string } {\n return {\n session_id: input.sessionId,\n profile_id: input.profileId,\n };\n}\n\n/**\n * Transform API state response to SDK format.\n * @internal\n */\nexport function transformStateResponse(api: APIProfileStateResponse): ProfileStateResponse {\n return {\n profileId: api.profile_id,\n stateUrl: api.state_url,\n expiresIn: api.expires_in,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
1
  import { BrowserConfig } from '../types.js';
2
- import { CreateProfileInput, Profile, ProfileWithMethods, UpdateProfileInput, ProfileSessionInput, ProfileSession, ProfileStateResponse, SaveProfileSessionInput } from './types.js';
2
+ import { CreateProfileInput, ProfileSetup, Profile, ProfileWithMethods, ProfileSessionInput, ProfileSession, RepoSummary, ProfileStateResponse, SaveProfileSessionInput } from './types.js';
3
3
  import '../../utils/resilience.js';
4
4
 
5
5
  /**
@@ -9,18 +9,15 @@ import '../../utils/resilience.js';
9
9
  * ```typescript
10
10
  * const morph = new MorphClient({ apiKey: '...' });
11
11
  *
12
- * // Create a profile
13
- * const profile = await morph.browser.profiles.createProfile({
12
+ * // Create a profile + live setup session
13
+ * const setup = await morph.browser.profiles.createProfile({
14
14
  * name: 'LinkedIn',
15
- * repoId: 'repo-uuid'
15
+ * repoId: 'owner/repo'
16
16
  * });
17
17
  *
18
- * // Start session for user to sign in
19
- * const session = await morph.browser.profiles.startSession();
20
- * console.log('Sign in at:', session.debugUrl);
21
- *
22
- * // Save state after user signs in
23
- * await morph.browser.profiles.saveSession(session.sessionId, profile.id);
18
+ * console.log('Sign in at:', setup.session.debugUrl);
19
+ * // User logs in, then explicitly save
20
+ * await setup.save();
24
21
  *
25
22
  * // Use profile in browser tasks
26
23
  * await morph.browser.execute({
@@ -40,23 +37,25 @@ declare class ProfilesClient {
40
37
  private config;
41
38
  constructor(config: BrowserConfig);
42
39
  /**
43
- * Create a new browser profile.
40
+ * Create a new browser profile and immediately start a live session.
44
41
  *
45
42
  * @param input - Profile creation parameters
46
- * @returns The created profile
43
+ * @returns Profile setup handle with live URL + save()
47
44
  * @throws {MorphValidationError} If input validation fails
48
45
  * @throws {MorphProfileLimitError} If profile limit is exceeded
49
46
  * @throws {MorphAuthenticationError} If API key is missing or invalid
50
47
  *
51
48
  * @example
52
49
  * ```typescript
53
- * const profile = await morph.browser.profiles.createProfile({
54
- * name: 'LinkedIn Production',
55
- * repoId: 'repo-uuid-here'
56
- * });
50
+ * const setup = await morph.browser.profiles.createProfile({
51
+ * name: 'LinkedIn Production',
52
+ * repoId: 'owner/repo'
53
+ * });
54
+ * console.log(setup.session.debugUrl);
55
+ * await setup.save();
57
56
  * ```
58
57
  */
59
- createProfile(input: CreateProfileInput): Promise<Profile>;
58
+ createProfile(input: CreateProfileInput): Promise<ProfileSetup>;
60
59
  /**
61
60
  * List all profiles for the authenticated user.
62
61
  *
@@ -69,7 +68,7 @@ declare class ProfilesClient {
69
68
  * const allProfiles = await morph.browser.profiles.listProfiles();
70
69
  *
71
70
  * // List profiles for a specific repo
72
- * const repoProfiles = await morph.browser.profiles.listProfiles('repo-uuid');
71
+ * const repoProfiles = await morph.browser.profiles.listProfiles('owner/repo');
73
72
  * ```
74
73
  */
75
74
  listProfiles(repoId?: string): Promise<Profile[]>;
@@ -89,13 +88,12 @@ declare class ProfilesClient {
89
88
  */
90
89
  getProfile(id: string): Promise<ProfileWithMethods>;
91
90
  /**
92
- * Update a profile's name.
91
+ * Update a profile by opening a live session (no rename).
93
92
  *
94
93
  * @param id - Profile ID
95
- * @param input - Update parameters
96
- * @returns Updated profile
94
+ * @returns Profile setup handle with live URL + save()
97
95
  */
98
- updateProfile(id: string, input: UpdateProfileInput): Promise<Profile>;
96
+ updateProfile(id: string): Promise<ProfileSetup>;
99
97
  /**
100
98
  * Delete a profile.
101
99
  *
@@ -132,6 +130,12 @@ declare class ProfilesClient {
132
130
  * @returns Updated profile with cookie domains
133
131
  */
134
132
  saveSession(sessionId: string, profileId: string): Promise<Profile>;
133
+ /**
134
+ * List available repo IDs (discovery).
135
+ *
136
+ * @returns Repo summaries with profile counts
137
+ */
138
+ listRepos(): Promise<RepoSummary[]>;
135
139
  /**
136
140
  * Get the presigned URL for a profile's state.
137
141
  *
@@ -146,7 +150,7 @@ declare class ProfilesClient {
146
150
  /**
147
151
  * Create a new browser profile.
148
152
  */
149
- declare function createProfile(input: CreateProfileInput, config?: BrowserConfig): Promise<Profile>;
153
+ declare function createProfile(input: CreateProfileInput, config?: BrowserConfig): Promise<ProfileSetup>;
150
154
  /**
151
155
  * List all profiles for the authenticated user.
152
156
  */
@@ -158,7 +162,7 @@ declare function getProfile(id: string, config?: BrowserConfig): Promise<Profile
158
162
  /**
159
163
  * Update a profile.
160
164
  */
161
- declare function updateProfile(id: string, input: UpdateProfileInput, config?: BrowserConfig): Promise<Profile>;
165
+ declare function updateProfile(id: string, config?: BrowserConfig): Promise<ProfileSetup>;
162
166
  /**
163
167
  * Delete a profile.
164
168
  */
@@ -171,9 +175,13 @@ declare function startProfileSession(config?: BrowserConfig, input?: ProfileSess
171
175
  * Save browser state from a session to a profile.
172
176
  */
173
177
  declare function saveProfileSession(input: SaveProfileSessionInput, config?: BrowserConfig): Promise<Profile>;
178
+ /**
179
+ * List repo IDs available to the authenticated user/org.
180
+ */
181
+ declare function listRepos(config?: BrowserConfig): Promise<RepoSummary[]>;
174
182
  /**
175
183
  * Get the presigned URL for a profile's state.
176
184
  */
177
185
  declare function getProfileState(profileId: string, config?: BrowserConfig): Promise<ProfileStateResponse>;
178
186
 
179
- export { ProfilesClient, createProfile, deleteProfile, getProfile, getProfileState, listProfiles, saveProfileSession, startProfileSession, updateProfile };
187
+ export { ProfilesClient, createProfile, deleteProfile, getProfile, getProfileState, listProfiles, listRepos, saveProfileSession, startProfileSession, updateProfile };
@@ -5,12 +5,13 @@ import {
5
5
  getProfile,
6
6
  getProfileState,
7
7
  listProfiles,
8
+ listRepos,
8
9
  saveProfileSession,
9
10
  startProfileSession,
10
11
  updateProfile
11
- } from "../../../chunk-ESRZJRTQ.js";
12
- import "../../../chunk-YJ354BA2.js";
13
- import "../../../chunk-UJ7LVT5G.js";
12
+ } from "../../../chunk-SI2CKRKJ.js";
13
+ import "../../../chunk-2AMEQAO2.js";
14
+ import "../../../chunk-2VERUKO2.js";
14
15
  import "../../../chunk-4VWJFZVS.js";
15
16
  import "../../../chunk-PZ5AY32C.js";
16
17
  export {
@@ -20,6 +21,7 @@ export {
20
21
  getProfile,
21
22
  getProfileState,
22
23
  listProfiles,
24
+ listRepos,
23
25
  saveProfileSession,
24
26
  startProfileSession,
25
27
  updateProfile
@@ -26,6 +26,7 @@ __export(profiles_exports, {
26
26
  getProfile: () => getProfile,
27
27
  getProfileState: () => getProfileState,
28
28
  listProfiles: () => listProfiles,
29
+ listRepos: () => listRepos,
29
30
  saveProfileSession: () => saveProfileSession,
30
31
  startProfileSession: () => startProfileSession,
31
32
  updateProfile: () => updateProfile
@@ -277,7 +278,7 @@ function transformCreateInput(input) {
277
278
  function transformSession(api) {
278
279
  return {
279
280
  sessionId: api.session_id,
280
- debugUrl: api.debug_url || api.debugUrl || ""
281
+ debugUrl: api.debug_url || ""
281
282
  };
282
283
  }
283
284
  function transformSaveInput(input) {
@@ -302,22 +303,24 @@ var ProfilesClient = class {
302
303
  this.config = config;
303
304
  }
304
305
  /**
305
- * Create a new browser profile.
306
- *
307
- * @param input - Profile creation parameters
308
- * @returns The created profile
309
- * @throws {MorphValidationError} If input validation fails
310
- * @throws {MorphProfileLimitError} If profile limit is exceeded
311
- * @throws {MorphAuthenticationError} If API key is missing or invalid
312
- *
313
- * @example
314
- * ```typescript
315
- * const profile = await morph.browser.profiles.createProfile({
316
- * name: 'LinkedIn Production',
317
- * repoId: 'repo-uuid-here'
318
- * });
319
- * ```
320
- */
306
+ * Create a new browser profile and immediately start a live session.
307
+ *
308
+ * @param input - Profile creation parameters
309
+ * @returns Profile setup handle with live URL + save()
310
+ * @throws {MorphValidationError} If input validation fails
311
+ * @throws {MorphProfileLimitError} If profile limit is exceeded
312
+ * @throws {MorphAuthenticationError} If API key is missing or invalid
313
+ *
314
+ * @example
315
+ * ```typescript
316
+ * const setup = await morph.browser.profiles.createProfile({
317
+ * name: 'LinkedIn Production',
318
+ * repoId: 'owner/repo'
319
+ * });
320
+ * console.log(setup.session.debugUrl);
321
+ * await setup.save();
322
+ * ```
323
+ */
321
324
  async createProfile(input) {
322
325
  return createProfile(input, this.config);
323
326
  }
@@ -333,7 +336,7 @@ var ProfilesClient = class {
333
336
  * const allProfiles = await morph.browser.profiles.listProfiles();
334
337
  *
335
338
  * // List profiles for a specific repo
336
- * const repoProfiles = await morph.browser.profiles.listProfiles('repo-uuid');
339
+ * const repoProfiles = await morph.browser.profiles.listProfiles('owner/repo');
337
340
  * ```
338
341
  */
339
342
  async listProfiles(repoId) {
@@ -357,14 +360,13 @@ var ProfilesClient = class {
357
360
  return getProfile(id, this.config);
358
361
  }
359
362
  /**
360
- * Update a profile's name.
363
+ * Update a profile by opening a live session (no rename).
361
364
  *
362
365
  * @param id - Profile ID
363
- * @param input - Update parameters
364
- * @returns Updated profile
366
+ * @returns Profile setup handle with live URL + save()
365
367
  */
366
- async updateProfile(id, input) {
367
- return updateProfile(id, input, this.config);
368
+ async updateProfile(id) {
369
+ return updateProfile(id, this.config);
368
370
  }
369
371
  /**
370
372
  * Delete a profile.
@@ -408,6 +410,14 @@ var ProfilesClient = class {
408
410
  async saveSession(sessionId, profileId) {
409
411
  return saveProfileSession({ sessionId, profileId }, this.config);
410
412
  }
413
+ /**
414
+ * List available repo IDs (discovery).
415
+ *
416
+ * @returns Repo summaries with profile counts
417
+ */
418
+ async listRepos() {
419
+ return listRepos(this.config);
420
+ }
411
421
  /**
412
422
  * Get the presigned URL for a profile's state.
413
423
  *
@@ -466,7 +476,9 @@ async function createProfile(input, config = {}) {
466
476
  throw parseAPIError(response.status, errorText, requestId);
467
477
  }
468
478
  const apiProfile = await response.json();
469
- return transformProfile(apiProfile);
479
+ const profile = transformProfile(apiProfile);
480
+ const session = await startProfileSession(config, { profileId: profile.id });
481
+ return buildProfileSetup(profile, session, config);
470
482
  }
471
483
  async function listProfiles(config = {}, repoId) {
472
484
  const apiUrl = config.apiUrl || DEFAULT_API_URL;
@@ -507,37 +519,11 @@ async function getProfile(id, config = {}) {
507
519
  delete: () => deleteProfile(id, config)
508
520
  };
509
521
  }
510
- async function updateProfile(id, input, config = {}) {
522
+ async function updateProfile(id, config = {}) {
511
523
  validateId(id, "id");
512
- if (input.name !== void 0) {
513
- if (typeof input.name !== "string") {
514
- throw new MorphValidationError("name must be a string", "name");
515
- }
516
- if (input.name.trim().length === 0) {
517
- throw new MorphValidationError("name cannot be empty", "name");
518
- }
519
- if (input.name.length > 100) {
520
- throw new MorphValidationError("name must be 100 characters or less", "name");
521
- }
522
- }
523
- const apiUrl = config.apiUrl || DEFAULT_API_URL;
524
- const headers = buildHeaders(config);
525
- const response = await fetchWithRetry(
526
- `${apiUrl}/profiles/${encodeURIComponent(id)}`,
527
- {
528
- method: "PATCH",
529
- headers,
530
- body: JSON.stringify(input)
531
- },
532
- config.retryConfig
533
- );
534
- if (!response.ok) {
535
- const errorText = await response.text().catch(() => response.statusText);
536
- const requestId = response.headers.get("x-request-id") || void 0;
537
- throw parseAPIError(response.status, errorText, requestId);
538
- }
539
- const apiProfile = await response.json();
540
- return transformProfile(apiProfile);
524
+ const profile = await fetchProfile(id, config);
525
+ const session = await startProfileSession(config, { profileId: profile.id });
526
+ return buildProfileSetup(profile, session, config);
541
527
  }
542
528
  async function deleteProfile(id, config = {}) {
543
529
  validateId(id, "id");
@@ -600,6 +586,27 @@ async function saveProfileSession(input, config = {}) {
600
586
  const apiProfile = await response.json();
601
587
  return transformProfile(apiProfile);
602
588
  }
589
+ async function listRepos(config = {}) {
590
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
591
+ const headers = buildHeaders(config);
592
+ const response = await fetchWithRetry(
593
+ `${apiUrl}/repos`,
594
+ { method: "GET", headers },
595
+ config.retryConfig
596
+ );
597
+ if (!response.ok) {
598
+ const errorText = await response.text().catch(() => response.statusText);
599
+ const requestId = response.headers.get("x-request-id") || void 0;
600
+ throw parseAPIError(response.status, errorText, requestId);
601
+ }
602
+ const data = await response.json();
603
+ const repos = Array.isArray(data?.repos) ? data.repos : [];
604
+ return repos.map((repo) => ({
605
+ repoId: repo.repo_id,
606
+ repoFullName: repo.repo_full_name,
607
+ profileCount: repo.profile_count ?? 0
608
+ }));
609
+ }
603
610
  async function getProfileState(profileId, config = {}) {
604
611
  validateId(profileId, "profileId");
605
612
  const apiUrl = config.apiUrl || DEFAULT_API_URL;
@@ -624,6 +631,29 @@ function buildHeaders(config) {
624
631
  }
625
632
  return headers;
626
633
  }
634
+ async function fetchProfile(id, config) {
635
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
636
+ const headers = buildHeaders(config);
637
+ const response = await fetchWithRetry(
638
+ `${apiUrl}/profiles/${encodeURIComponent(id)}`,
639
+ { method: "GET", headers },
640
+ config.retryConfig
641
+ );
642
+ if (!response.ok) {
643
+ const errorText = await response.text().catch(() => response.statusText);
644
+ const requestId = response.headers.get("x-request-id") || void 0;
645
+ throw parseAPIError(response.status, errorText, requestId);
646
+ }
647
+ const apiProfile = await response.json();
648
+ return transformProfile(apiProfile);
649
+ }
650
+ function buildProfileSetup(profile, session, config) {
651
+ return {
652
+ profile,
653
+ session,
654
+ save: () => saveProfileSession({ sessionId: session.sessionId, profileId: profile.id }, config)
655
+ };
656
+ }
627
657
  // Annotate the CommonJS export names for ESM import in node:
628
658
  0 && (module.exports = {
629
659
  ProfilesClient,
@@ -632,6 +662,7 @@ function buildHeaders(config) {
632
662
  getProfile,
633
663
  getProfileState,
634
664
  listProfiles,
665
+ listRepos,
635
666
  saveProfileSession,
636
667
  startProfileSession,
637
668
  updateProfile