@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.
- package/dist/{chunk-YJ354BA2.js → chunk-2AMEQAO2.js} +2 -2
- package/dist/chunk-2AMEQAO2.js.map +1 -0
- package/dist/{chunk-EI4UKP24.js → chunk-2HMEZZKK.js} +2 -2
- package/dist/{chunk-EI4UKP24.js.map → chunk-2HMEZZKK.js.map} +1 -1
- package/dist/{chunk-UJ7LVT5G.js → chunk-2VERUKO2.js} +1 -1
- package/dist/chunk-2VERUKO2.js.map +1 -0
- package/dist/{chunk-VHOWYK66.js → chunk-43LQLGP6.js} +23 -33
- package/dist/chunk-43LQLGP6.js.map +1 -0
- package/dist/{chunk-R7WN43L2.js → chunk-4KMBU6T3.js} +4 -4
- package/dist/{chunk-BVVDDTI7.js → chunk-73RV6EXR.js} +2 -2
- package/dist/{chunk-IH3KN4AT.js → chunk-7D6TXC7X.js} +2 -2
- package/dist/{chunk-FIA6LBW2.js → chunk-O7LDZA52.js} +2 -2
- package/dist/{chunk-5QIWYEHJ.js → chunk-PE4KGDA6.js} +1 -8
- package/dist/chunk-PE4KGDA6.js.map +1 -0
- package/dist/{chunk-SQN4DUQS.js → chunk-Q6Y4R236.js} +26 -2
- package/dist/chunk-Q6Y4R236.js.map +1 -0
- package/dist/{chunk-TXYCM4NP.js → chunk-QAT5UVPX.js} +3 -3
- package/dist/{chunk-M7GFXRKL.js → chunk-QJP62BXH.js} +157 -72
- package/dist/chunk-QJP62BXH.js.map +1 -0
- package/dist/{chunk-AV6YV2MH.js → chunk-R7IQWNSA.js} +8 -8
- package/dist/chunk-R7IQWNSA.js.map +1 -0
- package/dist/{chunk-ESRZJRTQ.js → chunk-SI2CKRKJ.js} +86 -56
- package/dist/chunk-SI2CKRKJ.js.map +1 -0
- package/dist/{chunk-FMWNVTJJ.js → chunk-TSENDJQI.js} +6 -6
- package/dist/chunk-TSENDJQI.js.map +1 -0
- package/dist/{chunk-IUG2FHNN.js → chunk-XH7P7HVT.js} +1 -8
- package/dist/chunk-XH7P7HVT.js.map +1 -0
- package/dist/{chunk-WSQMWVSD.js → chunk-YZ5NCWO2.js} +6 -6
- package/dist/chunk-YZ5NCWO2.js.map +1 -0
- package/dist/{chunk-4WO7PJNT.js → chunk-ZYTAKEBW.js} +8 -8
- package/dist/client.cjs +280 -162
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +18 -18
- package/dist/index.cjs +280 -162
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +18 -18
- package/dist/tools/browser/anthropic.cjs +54 -23
- package/dist/tools/browser/anthropic.cjs.map +1 -1
- package/dist/tools/browser/anthropic.js +7 -7
- package/dist/tools/browser/core.cjs +262 -124
- package/dist/tools/browser/core.cjs.map +1 -1
- package/dist/tools/browser/core.d.ts +24 -24
- package/dist/tools/browser/core.js +5 -5
- package/dist/tools/browser/errors.cjs.map +1 -1
- package/dist/tools/browser/errors.d.ts +1 -1
- package/dist/tools/browser/errors.js +1 -1
- package/dist/tools/browser/index.cjs +277 -139
- package/dist/tools/browser/index.cjs.map +1 -1
- package/dist/tools/browser/index.d.ts +3 -3
- package/dist/tools/browser/index.js +12 -12
- package/dist/tools/browser/index.js.map +1 -1
- package/dist/tools/browser/live.cjs +25 -1
- package/dist/tools/browser/live.cjs.map +1 -1
- package/dist/tools/browser/live.js +1 -1
- package/dist/tools/browser/openai.cjs +54 -23
- package/dist/tools/browser/openai.cjs.map +1 -1
- package/dist/tools/browser/openai.js +7 -7
- package/dist/tools/browser/profiles/core.cjs +85 -54
- package/dist/tools/browser/profiles/core.cjs.map +1 -1
- package/dist/tools/browser/profiles/core.d.ts +33 -25
- package/dist/tools/browser/profiles/core.js +5 -3
- package/dist/tools/browser/profiles/index.cjs +85 -54
- package/dist/tools/browser/profiles/index.cjs.map +1 -1
- package/dist/tools/browser/profiles/index.d.ts +2 -2
- package/dist/tools/browser/profiles/index.js +5 -3
- package/dist/tools/browser/profiles/types.cjs +1 -1
- package/dist/tools/browser/profiles/types.cjs.map +1 -1
- package/dist/tools/browser/profiles/types.d.ts +28 -9
- package/dist/tools/browser/profiles/types.js +1 -1
- package/dist/tools/browser/prompts.cjs +1 -1
- package/dist/tools/browser/prompts.cjs.map +1 -1
- package/dist/tools/browser/prompts.d.ts +1 -1
- package/dist/tools/browser/prompts.js +1 -1
- package/dist/tools/browser/types.cjs.map +1 -1
- package/dist/tools/browser/types.d.ts +54 -52
- package/dist/tools/browser/vercel.cjs +56 -25
- package/dist/tools/browser/vercel.cjs.map +1 -1
- package/dist/tools/browser/vercel.d.ts +1 -1
- package/dist/tools/browser/vercel.js +7 -7
- package/dist/tools/fastapply/anthropic.cjs +0 -7
- package/dist/tools/fastapply/anthropic.cjs.map +1 -1
- package/dist/tools/fastapply/anthropic.js +1 -1
- package/dist/tools/fastapply/index.cjs +0 -14
- package/dist/tools/fastapply/index.cjs.map +1 -1
- package/dist/tools/fastapply/index.js +2 -2
- package/dist/tools/fastapply/openai.cjs +0 -7
- package/dist/tools/fastapply/openai.cjs.map +1 -1
- package/dist/tools/fastapply/openai.js +1 -1
- package/dist/tools/index.cjs +0 -14
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.js +2 -2
- package/dist/tools/warp_grep/agent/runner.cjs +18 -98
- package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/runner.js +2 -3
- package/dist/tools/warp_grep/anthropic.cjs +18 -98
- package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
- package/dist/tools/warp_grep/anthropic.js +8 -9
- package/dist/tools/warp_grep/client.cjs +18 -98
- package/dist/tools/warp_grep/client.cjs.map +1 -1
- package/dist/tools/warp_grep/client.js +7 -8
- package/dist/tools/warp_grep/gemini.cjs +18 -98
- package/dist/tools/warp_grep/gemini.cjs.map +1 -1
- package/dist/tools/warp_grep/gemini.js +7 -8
- package/dist/tools/warp_grep/gemini.js.map +1 -1
- package/dist/tools/warp_grep/harness.js +10 -10
- package/dist/tools/warp_grep/index.cjs +18 -98
- package/dist/tools/warp_grep/index.cjs.map +1 -1
- package/dist/tools/warp_grep/index.js +10 -11
- package/dist/tools/warp_grep/openai.cjs +18 -98
- package/dist/tools/warp_grep/openai.cjs.map +1 -1
- package/dist/tools/warp_grep/openai.js +8 -9
- package/dist/tools/warp_grep/providers/local.js +2 -2
- package/dist/tools/warp_grep/vercel.cjs +18 -98
- package/dist/tools/warp_grep/vercel.cjs.map +1 -1
- package/dist/tools/warp_grep/vercel.js +8 -9
- package/dist/{vercel-CsnNSdze.d.ts → vercel-CVF27qFK.d.ts} +10 -10
- package/package.json +1 -1
- package/dist/chunk-5QIWYEHJ.js.map +0 -1
- package/dist/chunk-AV6YV2MH.js.map +0 -1
- package/dist/chunk-ESRZJRTQ.js.map +0 -1
- package/dist/chunk-FMWNVTJJ.js.map +0 -1
- package/dist/chunk-IUG2FHNN.js.map +0 -1
- package/dist/chunk-M7GFXRKL.js.map +0 -1
- package/dist/chunk-SQN4DUQS.js.map +0 -1
- package/dist/chunk-UJ7LVT5G.js.map +0 -1
- package/dist/chunk-VHOWYK66.js.map +0 -1
- package/dist/chunk-WSQMWVSD.js.map +0 -1
- package/dist/chunk-YJ354BA2.js.map +0 -1
- /package/dist/{chunk-R7WN43L2.js.map → chunk-4KMBU6T3.js.map} +0 -0
- /package/dist/{chunk-BVVDDTI7.js.map → chunk-73RV6EXR.js.map} +0 -0
- /package/dist/{chunk-IH3KN4AT.js.map → chunk-7D6TXC7X.js.map} +0 -0
- /package/dist/{chunk-FIA6LBW2.js.map → chunk-O7LDZA52.js.map} +0 -0
- /package/dist/{chunk-TXYCM4NP.js.map → chunk-QAT5UVPX.js.map} +0 -0
- /package/dist/{chunk-4WO7PJNT.js.map → chunk-ZYTAKEBW.js.map} +0 -0
|
@@ -120,7 +120,8 @@ function buildLiveUrl(debugUrl, options = {}) {
|
|
|
120
120
|
"debugUrl is required. Ensure your backend returns debugUrl in the task response. Contact support@morphllm.com if you need help."
|
|
121
121
|
);
|
|
122
122
|
}
|
|
123
|
-
const
|
|
123
|
+
const normalized = normalizeLiveUrl(debugUrl);
|
|
124
|
+
const url = new URL(normalized);
|
|
124
125
|
if (options.interactive !== void 0) {
|
|
125
126
|
url.searchParams.set("interactive", String(options.interactive));
|
|
126
127
|
}
|
|
@@ -138,6 +139,29 @@ function buildLiveUrl(debugUrl, options = {}) {
|
|
|
138
139
|
}
|
|
139
140
|
return url.toString();
|
|
140
141
|
}
|
|
142
|
+
function normalizeLiveUrl(debugUrl) {
|
|
143
|
+
const trimmed = debugUrl.trim();
|
|
144
|
+
if (!trimmed) {
|
|
145
|
+
return trimmed;
|
|
146
|
+
}
|
|
147
|
+
if (trimmed.startsWith("wss://") || trimmed.startsWith("ws://")) {
|
|
148
|
+
return `https://live.browser-use.com?wss=${encodeURIComponent(trimmed)}`;
|
|
149
|
+
}
|
|
150
|
+
let url;
|
|
151
|
+
try {
|
|
152
|
+
url = new URL(trimmed);
|
|
153
|
+
} catch {
|
|
154
|
+
return trimmed;
|
|
155
|
+
}
|
|
156
|
+
if (url.protocol === "wss:" || url.protocol === "ws:") {
|
|
157
|
+
return `https://live.browser-use.com?wss=${encodeURIComponent(trimmed)}`;
|
|
158
|
+
}
|
|
159
|
+
const wssParam = url.searchParams.get("wss");
|
|
160
|
+
if (wssParam && (wssParam.startsWith("wss://") || wssParam.startsWith("ws://"))) {
|
|
161
|
+
url.searchParams.set("wss", wssParam);
|
|
162
|
+
}
|
|
163
|
+
return url.toString();
|
|
164
|
+
}
|
|
141
165
|
function buildLiveIframe(debugUrl, options = {}) {
|
|
142
166
|
const {
|
|
143
167
|
width = "100%",
|
|
@@ -368,7 +392,7 @@ function transformCreateInput(input) {
|
|
|
368
392
|
function transformSession(api) {
|
|
369
393
|
return {
|
|
370
394
|
sessionId: api.session_id,
|
|
371
|
-
debugUrl: api.debug_url ||
|
|
395
|
+
debugUrl: api.debug_url || ""
|
|
372
396
|
};
|
|
373
397
|
}
|
|
374
398
|
function transformSaveInput(input) {
|
|
@@ -393,22 +417,24 @@ var ProfilesClient = class {
|
|
|
393
417
|
this.config = config;
|
|
394
418
|
}
|
|
395
419
|
/**
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
420
|
+
* Create a new browser profile and immediately start a live session.
|
|
421
|
+
*
|
|
422
|
+
* @param input - Profile creation parameters
|
|
423
|
+
* @returns Profile setup handle with live URL + save()
|
|
424
|
+
* @throws {MorphValidationError} If input validation fails
|
|
425
|
+
* @throws {MorphProfileLimitError} If profile limit is exceeded
|
|
426
|
+
* @throws {MorphAuthenticationError} If API key is missing or invalid
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```typescript
|
|
430
|
+
* const setup = await morph.browser.profiles.createProfile({
|
|
431
|
+
* name: 'LinkedIn Production',
|
|
432
|
+
* repoId: 'owner/repo'
|
|
433
|
+
* });
|
|
434
|
+
* console.log(setup.session.debugUrl);
|
|
435
|
+
* await setup.save();
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
412
438
|
async createProfile(input) {
|
|
413
439
|
return createProfile(input, this.config);
|
|
414
440
|
}
|
|
@@ -424,7 +450,7 @@ var ProfilesClient = class {
|
|
|
424
450
|
* const allProfiles = await morph.browser.profiles.listProfiles();
|
|
425
451
|
*
|
|
426
452
|
* // List profiles for a specific repo
|
|
427
|
-
* const repoProfiles = await morph.browser.profiles.listProfiles('repo
|
|
453
|
+
* const repoProfiles = await morph.browser.profiles.listProfiles('owner/repo');
|
|
428
454
|
* ```
|
|
429
455
|
*/
|
|
430
456
|
async listProfiles(repoId) {
|
|
@@ -448,14 +474,13 @@ var ProfilesClient = class {
|
|
|
448
474
|
return getProfile(id, this.config);
|
|
449
475
|
}
|
|
450
476
|
/**
|
|
451
|
-
* Update a profile
|
|
477
|
+
* Update a profile by opening a live session (no rename).
|
|
452
478
|
*
|
|
453
479
|
* @param id - Profile ID
|
|
454
|
-
* @
|
|
455
|
-
* @returns Updated profile
|
|
480
|
+
* @returns Profile setup handle with live URL + save()
|
|
456
481
|
*/
|
|
457
|
-
async updateProfile(id
|
|
458
|
-
return updateProfile(id,
|
|
482
|
+
async updateProfile(id) {
|
|
483
|
+
return updateProfile(id, this.config);
|
|
459
484
|
}
|
|
460
485
|
/**
|
|
461
486
|
* Delete a profile.
|
|
@@ -499,6 +524,14 @@ var ProfilesClient = class {
|
|
|
499
524
|
async saveSession(sessionId, profileId) {
|
|
500
525
|
return saveProfileSession({ sessionId, profileId }, this.config);
|
|
501
526
|
}
|
|
527
|
+
/**
|
|
528
|
+
* List available repo IDs (discovery).
|
|
529
|
+
*
|
|
530
|
+
* @returns Repo summaries with profile counts
|
|
531
|
+
*/
|
|
532
|
+
async listRepos() {
|
|
533
|
+
return listRepos(this.config);
|
|
534
|
+
}
|
|
502
535
|
/**
|
|
503
536
|
* Get the presigned URL for a profile's state.
|
|
504
537
|
*
|
|
@@ -557,7 +590,9 @@ async function createProfile(input, config = {}) {
|
|
|
557
590
|
throw parseAPIError(response.status, errorText, requestId);
|
|
558
591
|
}
|
|
559
592
|
const apiProfile = await response.json();
|
|
560
|
-
|
|
593
|
+
const profile = transformProfile(apiProfile);
|
|
594
|
+
const session = await startProfileSession(config, { profileId: profile.id });
|
|
595
|
+
return buildProfileSetup(profile, session, config);
|
|
561
596
|
}
|
|
562
597
|
async function listProfiles(config = {}, repoId) {
|
|
563
598
|
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
@@ -598,37 +633,11 @@ async function getProfile(id, config = {}) {
|
|
|
598
633
|
delete: () => deleteProfile(id, config)
|
|
599
634
|
};
|
|
600
635
|
}
|
|
601
|
-
async function updateProfile(id,
|
|
636
|
+
async function updateProfile(id, config = {}) {
|
|
602
637
|
validateId(id, "id");
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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);
|
|
638
|
+
const profile = await fetchProfile(id, config);
|
|
639
|
+
const session = await startProfileSession(config, { profileId: profile.id });
|
|
640
|
+
return buildProfileSetup(profile, session, config);
|
|
632
641
|
}
|
|
633
642
|
async function deleteProfile(id, config = {}) {
|
|
634
643
|
validateId(id, "id");
|
|
@@ -691,6 +700,27 @@ async function saveProfileSession(input, config = {}) {
|
|
|
691
700
|
const apiProfile = await response.json();
|
|
692
701
|
return transformProfile(apiProfile);
|
|
693
702
|
}
|
|
703
|
+
async function listRepos(config = {}) {
|
|
704
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
705
|
+
const headers = buildHeaders(config);
|
|
706
|
+
const response = await fetchWithRetry(
|
|
707
|
+
`${apiUrl}/repos`,
|
|
708
|
+
{ method: "GET", headers },
|
|
709
|
+
config.retryConfig
|
|
710
|
+
);
|
|
711
|
+
if (!response.ok) {
|
|
712
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
713
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
714
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
715
|
+
}
|
|
716
|
+
const data = await response.json();
|
|
717
|
+
const repos = Array.isArray(data?.repos) ? data.repos : [];
|
|
718
|
+
return repos.map((repo) => ({
|
|
719
|
+
repoId: repo.repo_id,
|
|
720
|
+
repoFullName: repo.repo_full_name,
|
|
721
|
+
profileCount: repo.profile_count ?? 0
|
|
722
|
+
}));
|
|
723
|
+
}
|
|
694
724
|
async function getProfileState(profileId, config = {}) {
|
|
695
725
|
validateId(profileId, "profileId");
|
|
696
726
|
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
@@ -715,6 +745,29 @@ function buildHeaders(config) {
|
|
|
715
745
|
}
|
|
716
746
|
return headers;
|
|
717
747
|
}
|
|
748
|
+
async function fetchProfile(id, config) {
|
|
749
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
750
|
+
const headers = buildHeaders(config);
|
|
751
|
+
const response = await fetchWithRetry(
|
|
752
|
+
`${apiUrl}/profiles/${encodeURIComponent(id)}`,
|
|
753
|
+
{ method: "GET", headers },
|
|
754
|
+
config.retryConfig
|
|
755
|
+
);
|
|
756
|
+
if (!response.ok) {
|
|
757
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
758
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
759
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
760
|
+
}
|
|
761
|
+
const apiProfile = await response.json();
|
|
762
|
+
return transformProfile(apiProfile);
|
|
763
|
+
}
|
|
764
|
+
function buildProfileSetup(profile, session, config) {
|
|
765
|
+
return {
|
|
766
|
+
profile,
|
|
767
|
+
session,
|
|
768
|
+
save: () => saveProfileSession({ sessionId: session.sessionId, profileId: profile.id }, config)
|
|
769
|
+
};
|
|
770
|
+
}
|
|
718
771
|
|
|
719
772
|
// tools/browser/core.ts
|
|
720
773
|
var DEFAULT_CONFIG = {
|
|
@@ -757,20 +810,21 @@ var BrowserClient = class {
|
|
|
757
810
|
body: JSON.stringify({
|
|
758
811
|
task: input.task,
|
|
759
812
|
url: input.url,
|
|
760
|
-
max_steps: input.
|
|
813
|
+
max_steps: input.maxSteps ?? 10,
|
|
761
814
|
model: input.model ?? "morph-computer-use-v0",
|
|
762
|
-
viewport_width: input.
|
|
763
|
-
viewport_height: input.
|
|
764
|
-
external_id: input.
|
|
765
|
-
repo_id: input.
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
815
|
+
viewport_width: input.viewportWidth ?? 1280,
|
|
816
|
+
viewport_height: input.viewportHeight ?? 720,
|
|
817
|
+
external_id: input.externalId,
|
|
818
|
+
repo_id: input.repoId,
|
|
819
|
+
repo_full_name: input.repoFullName,
|
|
820
|
+
commit_id: input.commitId,
|
|
821
|
+
record_video: input.recordVideo ?? false,
|
|
822
|
+
video_width: input.videoWidth ?? input.viewportWidth ?? 1280,
|
|
823
|
+
video_height: input.videoHeight ?? input.viewportHeight ?? 720,
|
|
824
|
+
allow_resizing: input.allowResizing ?? false,
|
|
771
825
|
structured_output: "schema" in input ? stringifyStructuredOutput(input.schema) : void 0,
|
|
772
826
|
auth: input.auth,
|
|
773
|
-
profile_id: input.
|
|
827
|
+
profile_id: input.profileId
|
|
774
828
|
})
|
|
775
829
|
});
|
|
776
830
|
if (!response.ok) {
|
|
@@ -778,9 +832,10 @@ var BrowserClient = class {
|
|
|
778
832
|
if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
|
|
779
833
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
780
834
|
}
|
|
781
|
-
const result = await response.json();
|
|
835
|
+
const result = mapTaskResult(await response.json());
|
|
782
836
|
if (debug) {
|
|
783
|
-
|
|
837
|
+
const debugUrl = result.debugUrl;
|
|
838
|
+
console.log(`[Browser] \u2705 Task created: recordingId=${result.recordingId ?? "none"} debugUrl=${debugUrl ? "available" : "none"}`);
|
|
784
839
|
}
|
|
785
840
|
if ("schema" in input) {
|
|
786
841
|
return wrapTaskResponseWithSchema(result, this.config, input.schema);
|
|
@@ -835,15 +890,15 @@ async function executeBrowserTask(input, config = {}) {
|
|
|
835
890
|
error: 'Task description is required. Example: "Go to example.com and click the login button"'
|
|
836
891
|
};
|
|
837
892
|
}
|
|
838
|
-
if (input.
|
|
893
|
+
if (input.maxSteps !== void 0 && (input.maxSteps < 1 || input.maxSteps > 50)) {
|
|
839
894
|
return {
|
|
840
895
|
success: false,
|
|
841
|
-
error: "
|
|
896
|
+
error: "maxSteps must be between 1 and 50. Use more steps for complex multi-page flows."
|
|
842
897
|
};
|
|
843
898
|
}
|
|
844
899
|
if (debug) {
|
|
845
|
-
console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.
|
|
846
|
-
console.log(`[Browser] Recording: ${input.
|
|
900
|
+
console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.maxSteps ?? 10}`);
|
|
901
|
+
console.log(`[Browser] Recording: ${input.recordVideo ? "yes" : "no"} | Calling ${apiUrl}/browser-task`);
|
|
847
902
|
}
|
|
848
903
|
const startTime = Date.now();
|
|
849
904
|
try {
|
|
@@ -857,20 +912,20 @@ async function executeBrowserTask(input, config = {}) {
|
|
|
857
912
|
body: JSON.stringify({
|
|
858
913
|
task: input.task,
|
|
859
914
|
url: input.url,
|
|
860
|
-
max_steps: input.
|
|
915
|
+
max_steps: input.maxSteps ?? 10,
|
|
861
916
|
model: input.model ?? "morph-computer-use-v0",
|
|
862
|
-
viewport_width: input.
|
|
863
|
-
viewport_height: input.
|
|
864
|
-
external_id: input.
|
|
865
|
-
repo_id: input.
|
|
866
|
-
commit_id: input.
|
|
867
|
-
record_video: input.
|
|
868
|
-
video_width: input.
|
|
869
|
-
video_height: input.
|
|
870
|
-
allow_resizing: input.
|
|
871
|
-
structured_output: input.
|
|
917
|
+
viewport_width: input.viewportWidth ?? 1280,
|
|
918
|
+
viewport_height: input.viewportHeight ?? 720,
|
|
919
|
+
external_id: input.externalId,
|
|
920
|
+
repo_id: input.repoId,
|
|
921
|
+
commit_id: input.commitId,
|
|
922
|
+
record_video: input.recordVideo ?? false,
|
|
923
|
+
video_width: input.videoWidth ?? input.viewportWidth ?? 1280,
|
|
924
|
+
video_height: input.videoHeight ?? input.viewportHeight ?? 720,
|
|
925
|
+
allow_resizing: input.allowResizing ?? false,
|
|
926
|
+
structured_output: input.structuredOutput,
|
|
872
927
|
auth: input.auth,
|
|
873
|
-
profile_id: input.
|
|
928
|
+
profile_id: input.profileId
|
|
874
929
|
})
|
|
875
930
|
},
|
|
876
931
|
config.retryConfig
|
|
@@ -878,17 +933,17 @@ async function executeBrowserTask(input, config = {}) {
|
|
|
878
933
|
const response = await withTimeout(
|
|
879
934
|
fetchPromise,
|
|
880
935
|
timeout,
|
|
881
|
-
`Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing
|
|
936
|
+
`Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing maxSteps.`
|
|
882
937
|
);
|
|
883
938
|
if (!response.ok) {
|
|
884
939
|
const errorText = await response.text().catch(() => response.statusText);
|
|
885
940
|
if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
|
|
886
941
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
887
942
|
}
|
|
888
|
-
const result = await response.json();
|
|
943
|
+
const result = mapTaskResult(await response.json());
|
|
889
944
|
const elapsed = Date.now() - startTime;
|
|
890
945
|
if (debug) {
|
|
891
|
-
console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.
|
|
946
|
+
console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.stepsTaken ?? 0} recordingId=${result.recordingId ?? "none"}`);
|
|
892
947
|
}
|
|
893
948
|
return result;
|
|
894
949
|
} catch (error) {
|
|
@@ -926,7 +981,7 @@ async function getRecording(recordingId, config = {}) {
|
|
|
926
981
|
if (debug) console.error(`[Browser] getRecording error: ${response.status} - ${errorText}`);
|
|
927
982
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
928
983
|
}
|
|
929
|
-
const data = await response.json();
|
|
984
|
+
const data = mapRecordingStatus(await response.json());
|
|
930
985
|
if (debug) console.log(`[Browser] Recording status: ${data.status}`);
|
|
931
986
|
return {
|
|
932
987
|
...data,
|
|
@@ -949,10 +1004,10 @@ async function waitForRecording(recordingId, config = {}, options = {}) {
|
|
|
949
1004
|
}
|
|
950
1005
|
async function executeWithRecording(input, config = {}) {
|
|
951
1006
|
const taskResult = await executeBrowserTask(input, config);
|
|
952
|
-
if (taskResult.
|
|
1007
|
+
if (taskResult.recordingId) {
|
|
953
1008
|
try {
|
|
954
1009
|
const recording = await waitForRecording(
|
|
955
|
-
taskResult.
|
|
1010
|
+
taskResult.recordingId,
|
|
956
1011
|
config,
|
|
957
1012
|
{ timeout: 6e4, pollInterval: 2e3 }
|
|
958
1013
|
);
|
|
@@ -962,12 +1017,12 @@ async function executeWithRecording(input, config = {}) {
|
|
|
962
1017
|
};
|
|
963
1018
|
} catch (error) {
|
|
964
1019
|
const errorRecording = {
|
|
965
|
-
id: taskResult.
|
|
1020
|
+
id: taskResult.recordingId,
|
|
966
1021
|
status: "ERROR",
|
|
967
1022
|
error: error instanceof Error ? error.message : String(error),
|
|
968
|
-
|
|
969
|
-
getWebp: (options) => getWebp(taskResult.
|
|
970
|
-
getErrors: () => getErrors(taskResult.
|
|
1023
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1024
|
+
getWebp: (options) => getWebp(taskResult.recordingId, config, options),
|
|
1025
|
+
getErrors: () => getErrors(taskResult.recordingId, config)
|
|
971
1026
|
};
|
|
972
1027
|
return {
|
|
973
1028
|
...taskResult,
|
|
@@ -993,8 +1048,8 @@ async function getErrors(recordingId, config = {}) {
|
|
|
993
1048
|
if (debug) console.error(`[Browser] getErrors error: ${response.status} - ${errorText}`);
|
|
994
1049
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
995
1050
|
}
|
|
996
|
-
const errors = await response.json();
|
|
997
|
-
if (debug) console.log(`[Browser] Found ${errors.
|
|
1051
|
+
const errors = mapErrorsResponse(await response.json());
|
|
1052
|
+
if (debug) console.log(`[Browser] Found ${errors.totalErrors} errors`);
|
|
998
1053
|
return errors;
|
|
999
1054
|
}
|
|
1000
1055
|
function stringifyStructuredOutput(schema) {
|
|
@@ -1027,6 +1082,84 @@ function parseStructuredTaskOutput(result, schema) {
|
|
|
1027
1082
|
throw error;
|
|
1028
1083
|
}
|
|
1029
1084
|
}
|
|
1085
|
+
function mapTaskResult(api) {
|
|
1086
|
+
if (!api || typeof api !== "object") {
|
|
1087
|
+
return api;
|
|
1088
|
+
}
|
|
1089
|
+
return {
|
|
1090
|
+
success: api.success,
|
|
1091
|
+
result: api.result,
|
|
1092
|
+
error: api.error,
|
|
1093
|
+
stepsTaken: api.steps_taken,
|
|
1094
|
+
executionTimeMs: api.execution_time_ms,
|
|
1095
|
+
urls: api.urls,
|
|
1096
|
+
actionNames: api.action_names,
|
|
1097
|
+
errors: api.errors,
|
|
1098
|
+
modelActions: api.model_actions,
|
|
1099
|
+
isDone: api.is_done,
|
|
1100
|
+
actionHistory: api.action_history,
|
|
1101
|
+
actionResults: api.action_results,
|
|
1102
|
+
hasErrors: api.has_errors,
|
|
1103
|
+
numberOfSteps: api.number_of_steps,
|
|
1104
|
+
judgement: api.judgement,
|
|
1105
|
+
isValidated: api.is_validated,
|
|
1106
|
+
replayId: api.replay_id,
|
|
1107
|
+
replayUrl: api.replay_url,
|
|
1108
|
+
recordingId: api.recording_id,
|
|
1109
|
+
recordingStatus: api.recording_status,
|
|
1110
|
+
taskId: api.task_id,
|
|
1111
|
+
status: api.status,
|
|
1112
|
+
output: api.output,
|
|
1113
|
+
debugUrl: api.debug_url
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
function mapRecordingStatus(api) {
|
|
1117
|
+
return {
|
|
1118
|
+
id: api.id,
|
|
1119
|
+
status: api.status,
|
|
1120
|
+
replayUrl: api.replay_url,
|
|
1121
|
+
networkUrl: api.network_url,
|
|
1122
|
+
consoleUrl: api.console_url,
|
|
1123
|
+
videoUrl: api.video_url,
|
|
1124
|
+
totalEvents: api.total_events,
|
|
1125
|
+
fileSize: api.file_size,
|
|
1126
|
+
duration: api.duration,
|
|
1127
|
+
error: api.error,
|
|
1128
|
+
createdAt: api.created_at
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
function mapBrowserError(api) {
|
|
1132
|
+
return {
|
|
1133
|
+
type: api.type,
|
|
1134
|
+
message: api.message,
|
|
1135
|
+
url: api.url,
|
|
1136
|
+
timestamp: api.timestamp,
|
|
1137
|
+
screenshotUrl: api.screenshot_url,
|
|
1138
|
+
capturedAt: api.captured_at,
|
|
1139
|
+
status: api.status
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
function mapErrorsResponse(api) {
|
|
1143
|
+
return {
|
|
1144
|
+
recordingId: api.recording_id,
|
|
1145
|
+
totalErrors: api.total_errors,
|
|
1146
|
+
errors: Array.isArray(api.errors) ? api.errors.map(mapBrowserError) : []
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
function mapWebpResponse(api) {
|
|
1150
|
+
return {
|
|
1151
|
+
webpUrl: api.webp_url,
|
|
1152
|
+
cached: api.cached,
|
|
1153
|
+
width: api.width,
|
|
1154
|
+
fps: api.fps,
|
|
1155
|
+
maxDuration: api.max_duration,
|
|
1156
|
+
fileSize: api.file_size,
|
|
1157
|
+
maxSizeMb: api.max_size_mb,
|
|
1158
|
+
budgetMet: api.budget_met,
|
|
1159
|
+
qualityUsed: api.quality_used,
|
|
1160
|
+
attempts: api.attempts
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1030
1163
|
async function getTaskStatus(taskId, config) {
|
|
1031
1164
|
const apiUrl = config.apiUrl || DEFAULT_CONFIG.apiUrl;
|
|
1032
1165
|
const debug = config.debug || false;
|
|
@@ -1042,7 +1175,7 @@ async function getTaskStatus(taskId, config) {
|
|
|
1042
1175
|
if (debug) console.error(`[Browser] getTaskStatus error: ${response.status} - ${errorText}`);
|
|
1043
1176
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
1044
1177
|
}
|
|
1045
|
-
const result = await response.json();
|
|
1178
|
+
const result = mapTaskResult(await response.json());
|
|
1046
1179
|
if (debug) console.log(`[Browser] Task status: ${result.status}`);
|
|
1047
1180
|
return result;
|
|
1048
1181
|
}
|
|
@@ -1065,42 +1198,44 @@ async function pollTaskUntilComplete(taskId, config, pollConfig = {}) {
|
|
|
1065
1198
|
throw new Error(`Task polling timeout after ${timeout}ms`);
|
|
1066
1199
|
}
|
|
1067
1200
|
function wrapTaskResponse(result, config) {
|
|
1201
|
+
const debugUrl = result.debugUrl ?? "";
|
|
1068
1202
|
const wrapped = {
|
|
1069
1203
|
...result,
|
|
1070
|
-
|
|
1071
|
-
|
|
1204
|
+
debugUrl,
|
|
1205
|
+
taskId: result.taskId || "",
|
|
1206
|
+
liveUrl: result.taskId ? generateLiveUrl(result.taskId, config) : debugUrl,
|
|
1072
1207
|
complete: async (pollConfig) => {
|
|
1073
|
-
if (result.
|
|
1074
|
-
return pollTaskUntilComplete(result.
|
|
1208
|
+
if (result.taskId) {
|
|
1209
|
+
return pollTaskUntilComplete(result.taskId, config, pollConfig);
|
|
1075
1210
|
}
|
|
1076
|
-
if (result.
|
|
1211
|
+
if (result.recordingId) {
|
|
1077
1212
|
const recording = await waitForRecording(
|
|
1078
|
-
result.
|
|
1213
|
+
result.recordingId,
|
|
1079
1214
|
config,
|
|
1080
1215
|
pollConfig
|
|
1081
1216
|
);
|
|
1082
1217
|
return {
|
|
1083
1218
|
...result,
|
|
1084
|
-
|
|
1219
|
+
recordingStatus: recording.status
|
|
1085
1220
|
};
|
|
1086
1221
|
}
|
|
1087
|
-
throw new Error("Cannot poll completion: no
|
|
1222
|
+
throw new Error("Cannot poll completion: no taskId or recordingId available");
|
|
1088
1223
|
},
|
|
1089
1224
|
// Add Steel live session helpers - either functional or error-throwing
|
|
1090
|
-
getLiveUrl:
|
|
1225
|
+
getLiveUrl: debugUrl ? (options) => buildLiveUrl(debugUrl, options) : () => {
|
|
1091
1226
|
throw new Error(
|
|
1092
1227
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
|
|
1093
1228
|
);
|
|
1094
1229
|
},
|
|
1095
|
-
getLiveIframe:
|
|
1230
|
+
getLiveIframe: debugUrl ? (optionsOrPreset) => {
|
|
1096
1231
|
const options = resolvePreset(optionsOrPreset);
|
|
1097
|
-
return buildLiveIframe(
|
|
1232
|
+
return buildLiveIframe(debugUrl, options);
|
|
1098
1233
|
} : () => {
|
|
1099
1234
|
throw new Error(
|
|
1100
1235
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
|
|
1101
1236
|
);
|
|
1102
1237
|
},
|
|
1103
|
-
getEmbedCode:
|
|
1238
|
+
getEmbedCode: debugUrl ? () => buildEmbedCode(debugUrl) : () => {
|
|
1104
1239
|
throw new Error(
|
|
1105
1240
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
|
|
1106
1241
|
);
|
|
@@ -1109,44 +1244,46 @@ function wrapTaskResponse(result, config) {
|
|
|
1109
1244
|
return wrapped;
|
|
1110
1245
|
}
|
|
1111
1246
|
function wrapTaskResponseWithSchema(result, config, schema) {
|
|
1247
|
+
const debugUrl = result.debugUrl ?? "";
|
|
1112
1248
|
const parsed = result.output ? parseStructuredTaskOutput(result, schema) : { ...result, parsed: null };
|
|
1113
1249
|
const wrapped = {
|
|
1114
1250
|
...parsed,
|
|
1115
|
-
|
|
1116
|
-
|
|
1251
|
+
debugUrl,
|
|
1252
|
+
taskId: result.taskId || "",
|
|
1253
|
+
liveUrl: result.taskId ? generateLiveUrl(result.taskId, config) : debugUrl,
|
|
1117
1254
|
complete: async (pollConfig) => {
|
|
1118
|
-
if (result.
|
|
1119
|
-
const finalResult = await pollTaskUntilComplete(result.
|
|
1255
|
+
if (result.taskId) {
|
|
1256
|
+
const finalResult = await pollTaskUntilComplete(result.taskId, config, pollConfig);
|
|
1120
1257
|
return parseStructuredTaskOutput(finalResult, schema);
|
|
1121
1258
|
}
|
|
1122
|
-
if (result.
|
|
1259
|
+
if (result.recordingId) {
|
|
1123
1260
|
const recording = await waitForRecording(
|
|
1124
|
-
result.
|
|
1261
|
+
result.recordingId,
|
|
1125
1262
|
config,
|
|
1126
1263
|
pollConfig
|
|
1127
1264
|
);
|
|
1128
1265
|
return {
|
|
1129
1266
|
...parsed,
|
|
1130
|
-
|
|
1267
|
+
recordingStatus: recording.status
|
|
1131
1268
|
};
|
|
1132
1269
|
}
|
|
1133
|
-
throw new Error("Cannot poll completion: no
|
|
1270
|
+
throw new Error("Cannot poll completion: no taskId or recordingId available");
|
|
1134
1271
|
},
|
|
1135
1272
|
// Add Steel live session helpers - either functional or error-throwing
|
|
1136
|
-
getLiveUrl:
|
|
1273
|
+
getLiveUrl: debugUrl ? (options) => buildLiveUrl(debugUrl, options) : () => {
|
|
1137
1274
|
throw new Error(
|
|
1138
1275
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions. "
|
|
1139
1276
|
);
|
|
1140
1277
|
},
|
|
1141
|
-
getLiveIframe:
|
|
1278
|
+
getLiveIframe: debugUrl ? (optionsOrPreset) => {
|
|
1142
1279
|
const options = resolvePreset(optionsOrPreset);
|
|
1143
|
-
return buildLiveIframe(
|
|
1280
|
+
return buildLiveIframe(debugUrl, options);
|
|
1144
1281
|
} : () => {
|
|
1145
1282
|
throw new Error(
|
|
1146
1283
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions."
|
|
1147
1284
|
);
|
|
1148
1285
|
},
|
|
1149
|
-
getEmbedCode:
|
|
1286
|
+
getEmbedCode: debugUrl ? () => buildEmbedCode(debugUrl) : () => {
|
|
1150
1287
|
throw new Error(
|
|
1151
1288
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions."
|
|
1152
1289
|
);
|
|
@@ -1161,10 +1298,11 @@ async function getWebp(recordingId, config = {}, options = {}) {
|
|
|
1161
1298
|
throw new Error("API key required for getWebp");
|
|
1162
1299
|
}
|
|
1163
1300
|
const params = new URLSearchParams();
|
|
1164
|
-
if (options.
|
|
1301
|
+
if (options.maxDuration !== void 0) params.set("max_duration", String(options.maxDuration));
|
|
1165
1302
|
if (options.fps !== void 0) params.set("fps", String(options.fps));
|
|
1166
1303
|
if (options.width !== void 0) params.set("width", String(options.width));
|
|
1167
1304
|
if (options.quality !== void 0) params.set("quality", String(options.quality));
|
|
1305
|
+
if (options.maxSizeMb !== void 0) params.set("max_size_mb", String(options.maxSizeMb));
|
|
1168
1306
|
const url = `${apiUrl}/recordings/${recordingId}/webp${params.toString() ? "?" + params.toString() : ""}`;
|
|
1169
1307
|
if (debug) console.log(`[Browser] getWebp: ${url}`);
|
|
1170
1308
|
const response = await fetch(url, {
|
|
@@ -1176,8 +1314,8 @@ async function getWebp(recordingId, config = {}, options = {}) {
|
|
|
1176
1314
|
if (debug) console.error(`[Browser] getWebp error: ${response.status} - ${errorText}`);
|
|
1177
1315
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
1178
1316
|
}
|
|
1179
|
-
const result = await response.json();
|
|
1180
|
-
if (debug) console.log(`[Browser] WebP ready: ${result.
|
|
1317
|
+
const result = mapWebpResponse(await response.json());
|
|
1318
|
+
if (debug) console.log(`[Browser] WebP ready: ${result.webpUrl} (cached: ${result.cached})`);
|
|
1181
1319
|
return result;
|
|
1182
1320
|
}
|
|
1183
1321
|
async function checkHealth(config = {}) {
|