@morphllm/morphsdk 0.2.92 → 0.2.94
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.
- package/dist/{chunk-IJ54DTJ3.js → chunk-4WO7PJNT.js} +11 -11
- package/dist/{chunk-EYGBUH2R.js → chunk-AV6YV2MH.js} +2 -2
- package/dist/{chunk-LMUZ3NGC.js → chunk-BVVDDTI7.js} +2 -2
- package/dist/chunk-ESRZJRTQ.js +359 -0
- package/dist/chunk-ESRZJRTQ.js.map +1 -0
- package/dist/{chunk-GU6DACME.js → chunk-FIA6LBW2.js} +2 -2
- package/dist/{chunk-4WLGDYWQ.js → chunk-FMWNVTJJ.js} +2 -2
- package/dist/{chunk-PBLPZ6AU.js → chunk-IH3KN4AT.js} +2 -2
- package/dist/{chunk-MIIJWDOQ.js → chunk-M7GFXRKL.js} +13 -3
- package/dist/chunk-M7GFXRKL.js.map +1 -0
- package/dist/{chunk-4KMBU6T3.js → chunk-R7WN43L2.js} +4 -4
- package/dist/{chunk-PUGIOVSP.js → chunk-TXYCM4NP.js} +2 -2
- package/dist/chunk-UJ7LVT5G.js +177 -0
- package/dist/chunk-UJ7LVT5G.js.map +1 -0
- package/dist/{chunk-FNLNDMIX.js → chunk-WSQMWVSD.js} +2 -2
- package/dist/chunk-YJ354BA2.js +46 -0
- package/dist/chunk-YJ354BA2.js.map +1 -0
- package/dist/client.cjs +546 -4
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.ts +2 -0
- package/dist/client.js +12 -9
- package/dist/index.cjs +546 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +12 -9
- package/dist/tools/browser/anthropic.cjs +5 -1
- package/dist/tools/browser/anthropic.cjs.map +1 -1
- package/dist/tools/browser/anthropic.js +5 -2
- package/dist/tools/browser/core.cjs +544 -2
- package/dist/tools/browser/core.cjs.map +1 -1
- package/dist/tools/browser/core.d.ts +6 -0
- package/dist/tools/browser/core.js +4 -1
- package/dist/tools/browser/errors.cjs +208 -0
- package/dist/tools/browser/errors.cjs.map +1 -0
- package/dist/tools/browser/errors.d.ts +158 -0
- package/dist/tools/browser/errors.js +22 -0
- package/dist/tools/browser/errors.js.map +1 -0
- package/dist/tools/browser/index.cjs +562 -2
- package/dist/tools/browser/index.cjs.map +1 -1
- package/dist/tools/browser/index.d.ts +3 -0
- package/dist/tools/browser/index.js +27 -4
- package/dist/tools/browser/index.js.map +1 -1
- package/dist/tools/browser/openai.cjs +5 -1
- package/dist/tools/browser/openai.cjs.map +1 -1
- package/dist/tools/browser/openai.js +5 -2
- package/dist/tools/browser/profiles/core.cjs +639 -0
- package/dist/tools/browser/profiles/core.cjs.map +1 -0
- package/dist/tools/browser/profiles/core.d.ts +179 -0
- package/dist/tools/browser/profiles/core.js +27 -0
- package/dist/tools/browser/profiles/core.js.map +1 -0
- package/dist/tools/browser/profiles/index.cjs +639 -0
- package/dist/tools/browser/profiles/index.cjs.map +1 -0
- package/dist/tools/browser/profiles/index.d.ts +4 -0
- package/dist/tools/browser/profiles/index.js +27 -0
- package/dist/tools/browser/profiles/index.js.map +1 -0
- package/dist/tools/browser/profiles/types.cjs +74 -0
- package/dist/tools/browser/profiles/types.cjs.map +1 -0
- package/dist/tools/browser/profiles/types.d.ts +176 -0
- package/dist/tools/browser/profiles/types.js +16 -0
- package/dist/tools/browser/profiles/types.js.map +1 -0
- package/dist/tools/browser/types.cjs.map +1 -1
- package/dist/tools/browser/types.d.ts +2 -0
- package/dist/tools/browser/vercel.cjs +5 -1
- package/dist/tools/browser/vercel.cjs.map +1 -1
- package/dist/tools/browser/vercel.js +5 -2
- package/dist/tools/fastapply/index.js +3 -3
- package/dist/tools/index.js +3 -3
- package/dist/tools/warp_grep/anthropic.js +4 -4
- package/dist/tools/warp_grep/client.js +3 -3
- package/dist/tools/warp_grep/gemini.js +3 -3
- package/dist/tools/warp_grep/harness.js +2 -2
- package/dist/tools/warp_grep/index.js +3 -3
- package/dist/tools/warp_grep/openai.js +4 -4
- package/dist/tools/warp_grep/providers/local.js +2 -2
- package/dist/tools/warp_grep/vercel.js +4 -4
- package/package.json +7 -2
- package/dist/chunk-MIIJWDOQ.js.map +0 -1
- /package/dist/{chunk-IJ54DTJ3.js.map → chunk-4WO7PJNT.js.map} +0 -0
- /package/dist/{chunk-EYGBUH2R.js.map → chunk-AV6YV2MH.js.map} +0 -0
- /package/dist/{chunk-LMUZ3NGC.js.map → chunk-BVVDDTI7.js.map} +0 -0
- /package/dist/{chunk-GU6DACME.js.map → chunk-FIA6LBW2.js.map} +0 -0
- /package/dist/{chunk-4WLGDYWQ.js.map → chunk-FMWNVTJJ.js.map} +0 -0
- /package/dist/{chunk-PBLPZ6AU.js.map → chunk-IH3KN4AT.js.map} +0 -0
- /package/dist/{chunk-4KMBU6T3.js.map → chunk-R7WN43L2.js.map} +0 -0
- /package/dist/{chunk-PUGIOVSP.js.map → chunk-TXYCM4NP.js.map} +0 -0
- /package/dist/{chunk-FNLNDMIX.js.map → chunk-WSQMWVSD.js.map} +0 -0
|
@@ -181,6 +181,541 @@ function resolvePreset(optionsOrPreset) {
|
|
|
181
181
|
return optionsOrPreset;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
// tools/browser/errors.ts
|
|
185
|
+
var MorphError = class extends Error {
|
|
186
|
+
/** Error code for programmatic handling */
|
|
187
|
+
code;
|
|
188
|
+
/** Original cause of the error, if any */
|
|
189
|
+
cause;
|
|
190
|
+
constructor(message, code, cause) {
|
|
191
|
+
super(message);
|
|
192
|
+
this.name = "MorphError";
|
|
193
|
+
this.code = code;
|
|
194
|
+
this.cause = cause;
|
|
195
|
+
if (Error.captureStackTrace) {
|
|
196
|
+
Error.captureStackTrace(this, this.constructor);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Returns a JSON representation of the error for logging.
|
|
201
|
+
*/
|
|
202
|
+
toJSON() {
|
|
203
|
+
return {
|
|
204
|
+
name: this.name,
|
|
205
|
+
message: this.message,
|
|
206
|
+
code: this.code,
|
|
207
|
+
cause: this.cause?.message
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
var MorphValidationError = class extends MorphError {
|
|
212
|
+
/** The field that failed validation */
|
|
213
|
+
field;
|
|
214
|
+
constructor(message, field) {
|
|
215
|
+
super(message, "validation_error");
|
|
216
|
+
this.name = "MorphValidationError";
|
|
217
|
+
this.field = field;
|
|
218
|
+
}
|
|
219
|
+
toJSON() {
|
|
220
|
+
return {
|
|
221
|
+
...super.toJSON(),
|
|
222
|
+
field: this.field
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
var MorphAPIError = class extends MorphError {
|
|
227
|
+
/** HTTP status code */
|
|
228
|
+
statusCode;
|
|
229
|
+
/** Request ID for debugging (if available) */
|
|
230
|
+
requestId;
|
|
231
|
+
/** Raw response body */
|
|
232
|
+
rawResponse;
|
|
233
|
+
constructor(message, code, statusCode, options) {
|
|
234
|
+
super(message, code, options?.cause);
|
|
235
|
+
this.name = "MorphAPIError";
|
|
236
|
+
this.statusCode = statusCode;
|
|
237
|
+
this.requestId = options?.requestId;
|
|
238
|
+
this.rawResponse = options?.rawResponse;
|
|
239
|
+
}
|
|
240
|
+
toJSON() {
|
|
241
|
+
return {
|
|
242
|
+
...super.toJSON(),
|
|
243
|
+
statusCode: this.statusCode,
|
|
244
|
+
requestId: this.requestId
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
var MorphAuthenticationError = class extends MorphAPIError {
|
|
249
|
+
constructor(message = "Authentication required. Please provide a valid API key.") {
|
|
250
|
+
super(message, "authentication_required", 401);
|
|
251
|
+
this.name = "MorphAuthenticationError";
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
var MorphRateLimitError = class extends MorphAPIError {
|
|
255
|
+
/** When the rate limit resets (Unix timestamp) */
|
|
256
|
+
resetAt;
|
|
257
|
+
/** Number of seconds until reset */
|
|
258
|
+
retryAfter;
|
|
259
|
+
constructor(message = "Rate limit exceeded. Please retry later.", options) {
|
|
260
|
+
super(message, "rate_limit_exceeded", 429, { requestId: options?.requestId });
|
|
261
|
+
this.name = "MorphRateLimitError";
|
|
262
|
+
this.resetAt = options?.resetAt;
|
|
263
|
+
this.retryAfter = options?.retryAfter;
|
|
264
|
+
}
|
|
265
|
+
toJSON() {
|
|
266
|
+
return {
|
|
267
|
+
...super.toJSON(),
|
|
268
|
+
resetAt: this.resetAt,
|
|
269
|
+
retryAfter: this.retryAfter
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
var MorphNotFoundError = class extends MorphAPIError {
|
|
274
|
+
/** The type of resource that was not found */
|
|
275
|
+
resourceType;
|
|
276
|
+
/** The ID of the resource that was not found */
|
|
277
|
+
resourceId;
|
|
278
|
+
constructor(resourceType, resourceId) {
|
|
279
|
+
const message = resourceId ? `${resourceType} '${resourceId}' not found` : `${resourceType} not found`;
|
|
280
|
+
super(message, "resource_not_found", 404);
|
|
281
|
+
this.name = "MorphNotFoundError";
|
|
282
|
+
this.resourceType = resourceType;
|
|
283
|
+
this.resourceId = resourceId;
|
|
284
|
+
}
|
|
285
|
+
toJSON() {
|
|
286
|
+
return {
|
|
287
|
+
...super.toJSON(),
|
|
288
|
+
resourceType: this.resourceType,
|
|
289
|
+
resourceId: this.resourceId
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
var MorphProfileLimitError = class extends MorphAPIError {
|
|
294
|
+
/** Current number of profiles */
|
|
295
|
+
currentCount;
|
|
296
|
+
/** Maximum allowed profiles for the plan */
|
|
297
|
+
maxAllowed;
|
|
298
|
+
constructor(message = "Profile limit exceeded for your plan.", options) {
|
|
299
|
+
super(message, "profile_limit_exceeded", 403, { requestId: options?.requestId });
|
|
300
|
+
this.name = "MorphProfileLimitError";
|
|
301
|
+
this.currentCount = options?.currentCount;
|
|
302
|
+
this.maxAllowed = options?.maxAllowed;
|
|
303
|
+
}
|
|
304
|
+
toJSON() {
|
|
305
|
+
return {
|
|
306
|
+
...super.toJSON(),
|
|
307
|
+
currentCount: this.currentCount,
|
|
308
|
+
maxAllowed: this.maxAllowed
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
function parseAPIError(statusCode, responseText, requestId) {
|
|
313
|
+
let errorData = {};
|
|
314
|
+
try {
|
|
315
|
+
errorData = JSON.parse(responseText);
|
|
316
|
+
} catch {
|
|
317
|
+
}
|
|
318
|
+
const message = errorData.detail || errorData.message || responseText || "Unknown error";
|
|
319
|
+
const code = errorData.code;
|
|
320
|
+
switch (statusCode) {
|
|
321
|
+
case 401:
|
|
322
|
+
return new MorphAuthenticationError(message);
|
|
323
|
+
case 403:
|
|
324
|
+
if (code === "profile_limit_exceeded" || message.toLowerCase().includes("limit")) {
|
|
325
|
+
return new MorphProfileLimitError(message, { requestId });
|
|
326
|
+
}
|
|
327
|
+
return new MorphAPIError(message, "insufficient_permissions", statusCode, { requestId, rawResponse: responseText });
|
|
328
|
+
case 404:
|
|
329
|
+
if (message.toLowerCase().includes("profile")) {
|
|
330
|
+
return new MorphNotFoundError("Profile", void 0);
|
|
331
|
+
}
|
|
332
|
+
if (message.toLowerCase().includes("session")) {
|
|
333
|
+
return new MorphNotFoundError("Session", void 0);
|
|
334
|
+
}
|
|
335
|
+
return new MorphAPIError(message, "resource_not_found", statusCode, { requestId, rawResponse: responseText });
|
|
336
|
+
case 429:
|
|
337
|
+
return new MorphRateLimitError(message, { requestId });
|
|
338
|
+
case 422:
|
|
339
|
+
return new MorphAPIError(message, "validation_error", statusCode, { requestId, rawResponse: responseText });
|
|
340
|
+
case 500:
|
|
341
|
+
case 502:
|
|
342
|
+
case 503:
|
|
343
|
+
case 504:
|
|
344
|
+
return new MorphAPIError(message, "service_unavailable", statusCode, { requestId, rawResponse: responseText });
|
|
345
|
+
default:
|
|
346
|
+
return new MorphAPIError(message, "network_error", statusCode, { requestId, rawResponse: responseText });
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// tools/browser/profiles/types.ts
|
|
351
|
+
function transformProfile(api) {
|
|
352
|
+
return {
|
|
353
|
+
id: api.id,
|
|
354
|
+
name: api.name,
|
|
355
|
+
repoId: api.repo_id,
|
|
356
|
+
cookieDomains: api.cookie_domains,
|
|
357
|
+
lastUsedAt: api.last_used_at,
|
|
358
|
+
createdAt: api.created_at,
|
|
359
|
+
updatedAt: api.updated_at
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function transformCreateInput(input) {
|
|
363
|
+
return {
|
|
364
|
+
name: input.name,
|
|
365
|
+
repo_id: input.repoId
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
function transformSession(api) {
|
|
369
|
+
return {
|
|
370
|
+
sessionId: api.session_id,
|
|
371
|
+
debugUrl: api.debug_url || api.debugUrl || ""
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function transformSaveInput(input) {
|
|
375
|
+
return {
|
|
376
|
+
session_id: input.sessionId,
|
|
377
|
+
profile_id: input.profileId
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function transformStateResponse(api) {
|
|
381
|
+
return {
|
|
382
|
+
profileId: api.profile_id,
|
|
383
|
+
stateUrl: api.state_url,
|
|
384
|
+
expiresIn: api.expires_in
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// tools/browser/profiles/core.ts
|
|
389
|
+
var DEFAULT_API_URL = process.env.MORPH_ENVIRONMENT === "DEV" ? "http://localhost:8000" : "https://browser.morphllm.com";
|
|
390
|
+
var ProfilesClient = class {
|
|
391
|
+
config;
|
|
392
|
+
constructor(config) {
|
|
393
|
+
this.config = config;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Create a new browser profile.
|
|
397
|
+
*
|
|
398
|
+
* @param input - Profile creation parameters
|
|
399
|
+
* @returns The created profile
|
|
400
|
+
* @throws {MorphValidationError} If input validation fails
|
|
401
|
+
* @throws {MorphProfileLimitError} If profile limit is exceeded
|
|
402
|
+
* @throws {MorphAuthenticationError} If API key is missing or invalid
|
|
403
|
+
*
|
|
404
|
+
* @example
|
|
405
|
+
* ```typescript
|
|
406
|
+
* const profile = await morph.browser.profiles.createProfile({
|
|
407
|
+
* name: 'LinkedIn Production',
|
|
408
|
+
* repoId: 'repo-uuid-here'
|
|
409
|
+
* });
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
async createProfile(input) {
|
|
413
|
+
return createProfile(input, this.config);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* List all profiles for the authenticated user.
|
|
417
|
+
*
|
|
418
|
+
* @param repoId - Optional repository ID to filter by
|
|
419
|
+
* @returns Array of profiles
|
|
420
|
+
*
|
|
421
|
+
* @example
|
|
422
|
+
* ```typescript
|
|
423
|
+
* // List all profiles
|
|
424
|
+
* const allProfiles = await morph.browser.profiles.listProfiles();
|
|
425
|
+
*
|
|
426
|
+
* // List profiles for a specific repo
|
|
427
|
+
* const repoProfiles = await morph.browser.profiles.listProfiles('repo-uuid');
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
async listProfiles(repoId) {
|
|
431
|
+
return listProfiles(this.config, repoId);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Get a profile by ID with convenience methods.
|
|
435
|
+
*
|
|
436
|
+
* @param id - Profile ID
|
|
437
|
+
* @returns Profile with attached methods
|
|
438
|
+
* @throws {MorphNotFoundError} If profile is not found
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```typescript
|
|
442
|
+
* const profile = await morph.browser.profiles.getProfile('profile-id');
|
|
443
|
+
* const state = await profile.getState();
|
|
444
|
+
* await profile.delete();
|
|
445
|
+
* ```
|
|
446
|
+
*/
|
|
447
|
+
async getProfile(id) {
|
|
448
|
+
return getProfile(id, this.config);
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Update a profile's name.
|
|
452
|
+
*
|
|
453
|
+
* @param id - Profile ID
|
|
454
|
+
* @param input - Update parameters
|
|
455
|
+
* @returns Updated profile
|
|
456
|
+
*/
|
|
457
|
+
async updateProfile(id, input) {
|
|
458
|
+
return updateProfile(id, input, this.config);
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Delete a profile.
|
|
462
|
+
*
|
|
463
|
+
* @param id - Profile ID
|
|
464
|
+
* @throws {MorphNotFoundError} If profile is not found
|
|
465
|
+
*/
|
|
466
|
+
async deleteProfile(id) {
|
|
467
|
+
return deleteProfile(id, this.config);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Start a browser session for profile setup.
|
|
471
|
+
*
|
|
472
|
+
* Returns a live URL where the user can sign into accounts.
|
|
473
|
+
* After signing in, call `saveSession` to persist the state.
|
|
474
|
+
*
|
|
475
|
+
* @param input - Optional session parameters
|
|
476
|
+
* @returns Session with debug URL
|
|
477
|
+
*
|
|
478
|
+
* @example
|
|
479
|
+
* ```typescript
|
|
480
|
+
* const session = await morph.browser.profiles.startSession();
|
|
481
|
+
* console.log('Sign in at:', session.debugUrl);
|
|
482
|
+
* // Open debugUrl in browser, user signs in...
|
|
483
|
+
* await morph.browser.profiles.saveSession(session.sessionId, profile.id);
|
|
484
|
+
* ```
|
|
485
|
+
*/
|
|
486
|
+
async startSession(input) {
|
|
487
|
+
return startProfileSession(this.config, input);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Save browser state from a session to a profile.
|
|
491
|
+
*
|
|
492
|
+
* Call this after the user is done signing into accounts.
|
|
493
|
+
* Extracts cookies, localStorage, and sessionStorage.
|
|
494
|
+
*
|
|
495
|
+
* @param sessionId - Browser session ID from startSession
|
|
496
|
+
* @param profileId - Profile ID to save state to
|
|
497
|
+
* @returns Updated profile with cookie domains
|
|
498
|
+
*/
|
|
499
|
+
async saveSession(sessionId, profileId) {
|
|
500
|
+
return saveProfileSession({ sessionId, profileId }, this.config);
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Get the presigned URL for a profile's state.
|
|
504
|
+
*
|
|
505
|
+
* Use this to download the raw state JSON for debugging
|
|
506
|
+
* or to restore state manually.
|
|
507
|
+
*
|
|
508
|
+
* @param profileId - Profile ID
|
|
509
|
+
* @returns State URL with expiry information
|
|
510
|
+
*/
|
|
511
|
+
async getProfileState(profileId) {
|
|
512
|
+
return getProfileState(profileId, this.config);
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
function validateCreateInput(input) {
|
|
516
|
+
if (!input.name || typeof input.name !== "string") {
|
|
517
|
+
throw new MorphValidationError("name is required", "name");
|
|
518
|
+
}
|
|
519
|
+
const trimmedName = input.name.trim();
|
|
520
|
+
if (trimmedName.length === 0) {
|
|
521
|
+
throw new MorphValidationError("name cannot be empty", "name");
|
|
522
|
+
}
|
|
523
|
+
if (trimmedName.length > 100) {
|
|
524
|
+
throw new MorphValidationError("name must be 100 characters or less", "name");
|
|
525
|
+
}
|
|
526
|
+
if (!input.repoId || typeof input.repoId !== "string") {
|
|
527
|
+
throw new MorphValidationError("repoId is required", "repoId");
|
|
528
|
+
}
|
|
529
|
+
if (input.repoId.trim().length === 0) {
|
|
530
|
+
throw new MorphValidationError("repoId cannot be empty", "repoId");
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function validateId(id, fieldName) {
|
|
534
|
+
if (!id || typeof id !== "string") {
|
|
535
|
+
throw new MorphValidationError(`${fieldName} is required`, fieldName);
|
|
536
|
+
}
|
|
537
|
+
if (id.trim().length === 0) {
|
|
538
|
+
throw new MorphValidationError(`${fieldName} cannot be empty`, fieldName);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function createProfile(input, config = {}) {
|
|
542
|
+
validateCreateInput(input);
|
|
543
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
544
|
+
const headers = buildHeaders(config);
|
|
545
|
+
const response = await fetchWithRetry(
|
|
546
|
+
`${apiUrl}/profiles`,
|
|
547
|
+
{
|
|
548
|
+
method: "POST",
|
|
549
|
+
headers,
|
|
550
|
+
body: JSON.stringify(transformCreateInput(input))
|
|
551
|
+
},
|
|
552
|
+
config.retryConfig
|
|
553
|
+
);
|
|
554
|
+
if (!response.ok) {
|
|
555
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
556
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
557
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
558
|
+
}
|
|
559
|
+
const apiProfile = await response.json();
|
|
560
|
+
return transformProfile(apiProfile);
|
|
561
|
+
}
|
|
562
|
+
async function listProfiles(config = {}, repoId) {
|
|
563
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
564
|
+
const headers = buildHeaders(config);
|
|
565
|
+
const url = repoId ? `${apiUrl}/profiles?repo_id=${encodeURIComponent(repoId)}` : `${apiUrl}/profiles`;
|
|
566
|
+
const response = await fetchWithRetry(
|
|
567
|
+
url,
|
|
568
|
+
{ method: "GET", headers },
|
|
569
|
+
config.retryConfig
|
|
570
|
+
);
|
|
571
|
+
if (!response.ok) {
|
|
572
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
573
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
574
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
575
|
+
}
|
|
576
|
+
const data = await response.json();
|
|
577
|
+
return data.profiles.map(transformProfile);
|
|
578
|
+
}
|
|
579
|
+
async function getProfile(id, config = {}) {
|
|
580
|
+
validateId(id, "id");
|
|
581
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
582
|
+
const headers = buildHeaders(config);
|
|
583
|
+
const response = await fetchWithRetry(
|
|
584
|
+
`${apiUrl}/profiles/${encodeURIComponent(id)}`,
|
|
585
|
+
{ method: "GET", headers },
|
|
586
|
+
config.retryConfig
|
|
587
|
+
);
|
|
588
|
+
if (!response.ok) {
|
|
589
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
590
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
591
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
592
|
+
}
|
|
593
|
+
const apiProfile = await response.json();
|
|
594
|
+
const profile = transformProfile(apiProfile);
|
|
595
|
+
return {
|
|
596
|
+
...profile,
|
|
597
|
+
getState: () => getProfileState(id, config),
|
|
598
|
+
delete: () => deleteProfile(id, config)
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
async function updateProfile(id, input, config = {}) {
|
|
602
|
+
validateId(id, "id");
|
|
603
|
+
if (input.name !== void 0) {
|
|
604
|
+
if (typeof input.name !== "string") {
|
|
605
|
+
throw new MorphValidationError("name must be a string", "name");
|
|
606
|
+
}
|
|
607
|
+
if (input.name.trim().length === 0) {
|
|
608
|
+
throw new MorphValidationError("name cannot be empty", "name");
|
|
609
|
+
}
|
|
610
|
+
if (input.name.length > 100) {
|
|
611
|
+
throw new MorphValidationError("name must be 100 characters or less", "name");
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
615
|
+
const headers = buildHeaders(config);
|
|
616
|
+
const response = await fetchWithRetry(
|
|
617
|
+
`${apiUrl}/profiles/${encodeURIComponent(id)}`,
|
|
618
|
+
{
|
|
619
|
+
method: "PATCH",
|
|
620
|
+
headers,
|
|
621
|
+
body: JSON.stringify(input)
|
|
622
|
+
},
|
|
623
|
+
config.retryConfig
|
|
624
|
+
);
|
|
625
|
+
if (!response.ok) {
|
|
626
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
627
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
628
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
629
|
+
}
|
|
630
|
+
const apiProfile = await response.json();
|
|
631
|
+
return transformProfile(apiProfile);
|
|
632
|
+
}
|
|
633
|
+
async function deleteProfile(id, config = {}) {
|
|
634
|
+
validateId(id, "id");
|
|
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: "DELETE", 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
|
+
}
|
|
648
|
+
async function startProfileSession(config = {}, input) {
|
|
649
|
+
if (!config.apiKey) {
|
|
650
|
+
throw new MorphAuthenticationError();
|
|
651
|
+
}
|
|
652
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
653
|
+
const headers = buildHeaders(config);
|
|
654
|
+
const body = input?.profileId ? { profile_id: input.profileId } : {};
|
|
655
|
+
const response = await fetchWithRetry(
|
|
656
|
+
`${apiUrl}/profiles/session/start`,
|
|
657
|
+
{
|
|
658
|
+
method: "POST",
|
|
659
|
+
headers,
|
|
660
|
+
body: JSON.stringify(body)
|
|
661
|
+
},
|
|
662
|
+
config.retryConfig
|
|
663
|
+
);
|
|
664
|
+
if (!response.ok) {
|
|
665
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
666
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
667
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
668
|
+
}
|
|
669
|
+
const apiSession = await response.json();
|
|
670
|
+
return transformSession(apiSession);
|
|
671
|
+
}
|
|
672
|
+
async function saveProfileSession(input, config = {}) {
|
|
673
|
+
validateId(input.sessionId, "sessionId");
|
|
674
|
+
validateId(input.profileId, "profileId");
|
|
675
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
676
|
+
const headers = buildHeaders(config);
|
|
677
|
+
const response = await fetchWithRetry(
|
|
678
|
+
`${apiUrl}/profiles/session/save`,
|
|
679
|
+
{
|
|
680
|
+
method: "POST",
|
|
681
|
+
headers,
|
|
682
|
+
body: JSON.stringify(transformSaveInput(input))
|
|
683
|
+
},
|
|
684
|
+
config.retryConfig
|
|
685
|
+
);
|
|
686
|
+
if (!response.ok) {
|
|
687
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
688
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
689
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
690
|
+
}
|
|
691
|
+
const apiProfile = await response.json();
|
|
692
|
+
return transformProfile(apiProfile);
|
|
693
|
+
}
|
|
694
|
+
async function getProfileState(profileId, config = {}) {
|
|
695
|
+
validateId(profileId, "profileId");
|
|
696
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
697
|
+
const headers = buildHeaders(config);
|
|
698
|
+
const response = await fetchWithRetry(
|
|
699
|
+
`${apiUrl}/profiles/${encodeURIComponent(profileId)}/state`,
|
|
700
|
+
{ method: "GET", headers },
|
|
701
|
+
config.retryConfig
|
|
702
|
+
);
|
|
703
|
+
if (!response.ok) {
|
|
704
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
705
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
706
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
707
|
+
}
|
|
708
|
+
const apiState = await response.json();
|
|
709
|
+
return transformStateResponse(apiState);
|
|
710
|
+
}
|
|
711
|
+
function buildHeaders(config) {
|
|
712
|
+
const headers = { "Content-Type": "application/json" };
|
|
713
|
+
if (config.apiKey) {
|
|
714
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
715
|
+
}
|
|
716
|
+
return headers;
|
|
717
|
+
}
|
|
718
|
+
|
|
184
719
|
// tools/browser/core.ts
|
|
185
720
|
var DEFAULT_CONFIG = {
|
|
186
721
|
apiUrl: process.env.MORPH_ENVIRONMENT === "DEV" ? "http://localhost:8000" : "https://browser.morphllm.com",
|
|
@@ -190,11 +725,16 @@ var DEFAULT_CONFIG = {
|
|
|
190
725
|
};
|
|
191
726
|
var BrowserClient = class {
|
|
192
727
|
config;
|
|
728
|
+
/**
|
|
729
|
+
* Profile management - create and manage browser profiles for storing login state.
|
|
730
|
+
*/
|
|
731
|
+
profiles;
|
|
193
732
|
constructor(config = {}) {
|
|
194
733
|
this.config = {
|
|
195
734
|
...DEFAULT_CONFIG,
|
|
196
735
|
...config
|
|
197
736
|
};
|
|
737
|
+
this.profiles = new ProfilesClient(this.config);
|
|
198
738
|
}
|
|
199
739
|
/**
|
|
200
740
|
* Execute a browser automation task
|
|
@@ -229,7 +769,8 @@ var BrowserClient = class {
|
|
|
229
769
|
video_height: input.video_height ?? input.viewport_height ?? 720,
|
|
230
770
|
allow_resizing: input.allow_resizing ?? false,
|
|
231
771
|
structured_output: "schema" in input ? stringifyStructuredOutput(input.schema) : void 0,
|
|
232
|
-
auth: input.auth
|
|
772
|
+
auth: input.auth,
|
|
773
|
+
profile_id: input.profile_id
|
|
233
774
|
})
|
|
234
775
|
});
|
|
235
776
|
if (!response.ok) {
|
|
@@ -328,7 +869,8 @@ async function executeBrowserTask(input, config = {}) {
|
|
|
328
869
|
video_height: input.video_height ?? input.viewport_height ?? 720,
|
|
329
870
|
allow_resizing: input.allow_resizing ?? false,
|
|
330
871
|
structured_output: input.structured_output,
|
|
331
|
-
auth: input.auth
|
|
872
|
+
auth: input.auth,
|
|
873
|
+
profile_id: input.profile_id
|
|
332
874
|
})
|
|
333
875
|
},
|
|
334
876
|
config.retryConfig
|