@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
|
@@ -139,7 +139,8 @@ function buildLiveUrl(debugUrl, options = {}) {
|
|
|
139
139
|
"debugUrl is required. Ensure your backend returns debugUrl in the task response. Contact support@morphllm.com if you need help."
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
|
-
const
|
|
142
|
+
const normalized = normalizeLiveUrl(debugUrl);
|
|
143
|
+
const url = new URL(normalized);
|
|
143
144
|
if (options.interactive !== void 0) {
|
|
144
145
|
url.searchParams.set("interactive", String(options.interactive));
|
|
145
146
|
}
|
|
@@ -157,6 +158,29 @@ function buildLiveUrl(debugUrl, options = {}) {
|
|
|
157
158
|
}
|
|
158
159
|
return url.toString();
|
|
159
160
|
}
|
|
161
|
+
function normalizeLiveUrl(debugUrl) {
|
|
162
|
+
const trimmed = debugUrl.trim();
|
|
163
|
+
if (!trimmed) {
|
|
164
|
+
return trimmed;
|
|
165
|
+
}
|
|
166
|
+
if (trimmed.startsWith("wss://") || trimmed.startsWith("ws://")) {
|
|
167
|
+
return `https://live.browser-use.com?wss=${encodeURIComponent(trimmed)}`;
|
|
168
|
+
}
|
|
169
|
+
let url;
|
|
170
|
+
try {
|
|
171
|
+
url = new URL(trimmed);
|
|
172
|
+
} catch {
|
|
173
|
+
return trimmed;
|
|
174
|
+
}
|
|
175
|
+
if (url.protocol === "wss:" || url.protocol === "ws:") {
|
|
176
|
+
return `https://live.browser-use.com?wss=${encodeURIComponent(trimmed)}`;
|
|
177
|
+
}
|
|
178
|
+
const wssParam = url.searchParams.get("wss");
|
|
179
|
+
if (wssParam && (wssParam.startsWith("wss://") || wssParam.startsWith("ws://"))) {
|
|
180
|
+
url.searchParams.set("wss", wssParam);
|
|
181
|
+
}
|
|
182
|
+
return url.toString();
|
|
183
|
+
}
|
|
160
184
|
function buildLiveIframe(debugUrl, options = {}) {
|
|
161
185
|
const {
|
|
162
186
|
width = "100%",
|
|
@@ -387,7 +411,7 @@ function transformCreateInput(input) {
|
|
|
387
411
|
function transformSession(api) {
|
|
388
412
|
return {
|
|
389
413
|
sessionId: api.session_id,
|
|
390
|
-
debugUrl: api.debug_url ||
|
|
414
|
+
debugUrl: api.debug_url || ""
|
|
391
415
|
};
|
|
392
416
|
}
|
|
393
417
|
function transformSaveInput(input) {
|
|
@@ -412,22 +436,24 @@ var ProfilesClient = class {
|
|
|
412
436
|
this.config = config;
|
|
413
437
|
}
|
|
414
438
|
/**
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
439
|
+
* Create a new browser profile and immediately start a live session.
|
|
440
|
+
*
|
|
441
|
+
* @param input - Profile creation parameters
|
|
442
|
+
* @returns Profile setup handle with live URL + save()
|
|
443
|
+
* @throws {MorphValidationError} If input validation fails
|
|
444
|
+
* @throws {MorphProfileLimitError} If profile limit is exceeded
|
|
445
|
+
* @throws {MorphAuthenticationError} If API key is missing or invalid
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```typescript
|
|
449
|
+
* const setup = await morph.browser.profiles.createProfile({
|
|
450
|
+
* name: 'LinkedIn Production',
|
|
451
|
+
* repoId: 'owner/repo'
|
|
452
|
+
* });
|
|
453
|
+
* console.log(setup.session.debugUrl);
|
|
454
|
+
* await setup.save();
|
|
455
|
+
* ```
|
|
456
|
+
*/
|
|
431
457
|
async createProfile(input) {
|
|
432
458
|
return createProfile(input, this.config);
|
|
433
459
|
}
|
|
@@ -443,7 +469,7 @@ var ProfilesClient = class {
|
|
|
443
469
|
* const allProfiles = await morph.browser.profiles.listProfiles();
|
|
444
470
|
*
|
|
445
471
|
* // List profiles for a specific repo
|
|
446
|
-
* const repoProfiles = await morph.browser.profiles.listProfiles('repo
|
|
472
|
+
* const repoProfiles = await morph.browser.profiles.listProfiles('owner/repo');
|
|
447
473
|
* ```
|
|
448
474
|
*/
|
|
449
475
|
async listProfiles(repoId) {
|
|
@@ -467,14 +493,13 @@ var ProfilesClient = class {
|
|
|
467
493
|
return getProfile(id, this.config);
|
|
468
494
|
}
|
|
469
495
|
/**
|
|
470
|
-
* Update a profile
|
|
496
|
+
* Update a profile by opening a live session (no rename).
|
|
471
497
|
*
|
|
472
498
|
* @param id - Profile ID
|
|
473
|
-
* @
|
|
474
|
-
* @returns Updated profile
|
|
499
|
+
* @returns Profile setup handle with live URL + save()
|
|
475
500
|
*/
|
|
476
|
-
async updateProfile(id
|
|
477
|
-
return updateProfile(id,
|
|
501
|
+
async updateProfile(id) {
|
|
502
|
+
return updateProfile(id, this.config);
|
|
478
503
|
}
|
|
479
504
|
/**
|
|
480
505
|
* Delete a profile.
|
|
@@ -518,6 +543,14 @@ var ProfilesClient = class {
|
|
|
518
543
|
async saveSession(sessionId, profileId) {
|
|
519
544
|
return saveProfileSession({ sessionId, profileId }, this.config);
|
|
520
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* List available repo IDs (discovery).
|
|
548
|
+
*
|
|
549
|
+
* @returns Repo summaries with profile counts
|
|
550
|
+
*/
|
|
551
|
+
async listRepos() {
|
|
552
|
+
return listRepos(this.config);
|
|
553
|
+
}
|
|
521
554
|
/**
|
|
522
555
|
* Get the presigned URL for a profile's state.
|
|
523
556
|
*
|
|
@@ -576,7 +609,9 @@ async function createProfile(input, config = {}) {
|
|
|
576
609
|
throw parseAPIError(response.status, errorText, requestId);
|
|
577
610
|
}
|
|
578
611
|
const apiProfile = await response.json();
|
|
579
|
-
|
|
612
|
+
const profile = transformProfile(apiProfile);
|
|
613
|
+
const session = await startProfileSession(config, { profileId: profile.id });
|
|
614
|
+
return buildProfileSetup(profile, session, config);
|
|
580
615
|
}
|
|
581
616
|
async function listProfiles(config = {}, repoId) {
|
|
582
617
|
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
@@ -617,37 +652,11 @@ async function getProfile(id, config = {}) {
|
|
|
617
652
|
delete: () => deleteProfile(id, config)
|
|
618
653
|
};
|
|
619
654
|
}
|
|
620
|
-
async function updateProfile(id,
|
|
655
|
+
async function updateProfile(id, config = {}) {
|
|
621
656
|
validateId(id, "id");
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
}
|
|
626
|
-
if (input.name.trim().length === 0) {
|
|
627
|
-
throw new MorphValidationError("name cannot be empty", "name");
|
|
628
|
-
}
|
|
629
|
-
if (input.name.length > 100) {
|
|
630
|
-
throw new MorphValidationError("name must be 100 characters or less", "name");
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
634
|
-
const headers = buildHeaders(config);
|
|
635
|
-
const response = await fetchWithRetry(
|
|
636
|
-
`${apiUrl}/profiles/${encodeURIComponent(id)}`,
|
|
637
|
-
{
|
|
638
|
-
method: "PATCH",
|
|
639
|
-
headers,
|
|
640
|
-
body: JSON.stringify(input)
|
|
641
|
-
},
|
|
642
|
-
config.retryConfig
|
|
643
|
-
);
|
|
644
|
-
if (!response.ok) {
|
|
645
|
-
const errorText = await response.text().catch(() => response.statusText);
|
|
646
|
-
const requestId = response.headers.get("x-request-id") || void 0;
|
|
647
|
-
throw parseAPIError(response.status, errorText, requestId);
|
|
648
|
-
}
|
|
649
|
-
const apiProfile = await response.json();
|
|
650
|
-
return transformProfile(apiProfile);
|
|
657
|
+
const profile = await fetchProfile(id, config);
|
|
658
|
+
const session = await startProfileSession(config, { profileId: profile.id });
|
|
659
|
+
return buildProfileSetup(profile, session, config);
|
|
651
660
|
}
|
|
652
661
|
async function deleteProfile(id, config = {}) {
|
|
653
662
|
validateId(id, "id");
|
|
@@ -710,6 +719,27 @@ async function saveProfileSession(input, config = {}) {
|
|
|
710
719
|
const apiProfile = await response.json();
|
|
711
720
|
return transformProfile(apiProfile);
|
|
712
721
|
}
|
|
722
|
+
async function listRepos(config = {}) {
|
|
723
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
724
|
+
const headers = buildHeaders(config);
|
|
725
|
+
const response = await fetchWithRetry(
|
|
726
|
+
`${apiUrl}/repos`,
|
|
727
|
+
{ method: "GET", headers },
|
|
728
|
+
config.retryConfig
|
|
729
|
+
);
|
|
730
|
+
if (!response.ok) {
|
|
731
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
732
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
733
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
734
|
+
}
|
|
735
|
+
const data = await response.json();
|
|
736
|
+
const repos = Array.isArray(data?.repos) ? data.repos : [];
|
|
737
|
+
return repos.map((repo) => ({
|
|
738
|
+
repoId: repo.repo_id,
|
|
739
|
+
repoFullName: repo.repo_full_name,
|
|
740
|
+
profileCount: repo.profile_count ?? 0
|
|
741
|
+
}));
|
|
742
|
+
}
|
|
713
743
|
async function getProfileState(profileId, config = {}) {
|
|
714
744
|
validateId(profileId, "profileId");
|
|
715
745
|
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
@@ -734,6 +764,29 @@ function buildHeaders(config) {
|
|
|
734
764
|
}
|
|
735
765
|
return headers;
|
|
736
766
|
}
|
|
767
|
+
async function fetchProfile(id, config) {
|
|
768
|
+
const apiUrl = config.apiUrl || DEFAULT_API_URL;
|
|
769
|
+
const headers = buildHeaders(config);
|
|
770
|
+
const response = await fetchWithRetry(
|
|
771
|
+
`${apiUrl}/profiles/${encodeURIComponent(id)}`,
|
|
772
|
+
{ method: "GET", headers },
|
|
773
|
+
config.retryConfig
|
|
774
|
+
);
|
|
775
|
+
if (!response.ok) {
|
|
776
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
777
|
+
const requestId = response.headers.get("x-request-id") || void 0;
|
|
778
|
+
throw parseAPIError(response.status, errorText, requestId);
|
|
779
|
+
}
|
|
780
|
+
const apiProfile = await response.json();
|
|
781
|
+
return transformProfile(apiProfile);
|
|
782
|
+
}
|
|
783
|
+
function buildProfileSetup(profile, session, config) {
|
|
784
|
+
return {
|
|
785
|
+
profile,
|
|
786
|
+
session,
|
|
787
|
+
save: () => saveProfileSession({ sessionId: session.sessionId, profileId: profile.id }, config)
|
|
788
|
+
};
|
|
789
|
+
}
|
|
737
790
|
|
|
738
791
|
// tools/browser/core.ts
|
|
739
792
|
var DEFAULT_CONFIG = {
|
|
@@ -776,20 +829,21 @@ var BrowserClient = class {
|
|
|
776
829
|
body: JSON.stringify({
|
|
777
830
|
task: input.task,
|
|
778
831
|
url: input.url,
|
|
779
|
-
max_steps: input.
|
|
832
|
+
max_steps: input.maxSteps ?? 10,
|
|
780
833
|
model: input.model ?? "morph-computer-use-v0",
|
|
781
|
-
viewport_width: input.
|
|
782
|
-
viewport_height: input.
|
|
783
|
-
external_id: input.
|
|
784
|
-
repo_id: input.
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
834
|
+
viewport_width: input.viewportWidth ?? 1280,
|
|
835
|
+
viewport_height: input.viewportHeight ?? 720,
|
|
836
|
+
external_id: input.externalId,
|
|
837
|
+
repo_id: input.repoId,
|
|
838
|
+
repo_full_name: input.repoFullName,
|
|
839
|
+
commit_id: input.commitId,
|
|
840
|
+
record_video: input.recordVideo ?? false,
|
|
841
|
+
video_width: input.videoWidth ?? input.viewportWidth ?? 1280,
|
|
842
|
+
video_height: input.videoHeight ?? input.viewportHeight ?? 720,
|
|
843
|
+
allow_resizing: input.allowResizing ?? false,
|
|
790
844
|
structured_output: "schema" in input ? stringifyStructuredOutput(input.schema) : void 0,
|
|
791
845
|
auth: input.auth,
|
|
792
|
-
profile_id: input.
|
|
846
|
+
profile_id: input.profileId
|
|
793
847
|
})
|
|
794
848
|
});
|
|
795
849
|
if (!response.ok) {
|
|
@@ -797,9 +851,10 @@ var BrowserClient = class {
|
|
|
797
851
|
if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
|
|
798
852
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
799
853
|
}
|
|
800
|
-
const result = await response.json();
|
|
854
|
+
const result = mapTaskResult(await response.json());
|
|
801
855
|
if (debug) {
|
|
802
|
-
|
|
856
|
+
const debugUrl = result.debugUrl;
|
|
857
|
+
console.log(`[Browser] \u2705 Task created: recordingId=${result.recordingId ?? "none"} debugUrl=${debugUrl ? "available" : "none"}`);
|
|
803
858
|
}
|
|
804
859
|
if ("schema" in input) {
|
|
805
860
|
return wrapTaskResponseWithSchema(result, this.config, input.schema);
|
|
@@ -854,15 +909,15 @@ async function executeBrowserTask(input, config = {}) {
|
|
|
854
909
|
error: 'Task description is required. Example: "Go to example.com and click the login button"'
|
|
855
910
|
};
|
|
856
911
|
}
|
|
857
|
-
if (input.
|
|
912
|
+
if (input.maxSteps !== void 0 && (input.maxSteps < 1 || input.maxSteps > 50)) {
|
|
858
913
|
return {
|
|
859
914
|
success: false,
|
|
860
|
-
error: "
|
|
915
|
+
error: "maxSteps must be between 1 and 50. Use more steps for complex multi-page flows."
|
|
861
916
|
};
|
|
862
917
|
}
|
|
863
918
|
if (debug) {
|
|
864
|
-
console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.
|
|
865
|
-
console.log(`[Browser] Recording: ${input.
|
|
919
|
+
console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.maxSteps ?? 10}`);
|
|
920
|
+
console.log(`[Browser] Recording: ${input.recordVideo ? "yes" : "no"} | Calling ${apiUrl}/browser-task`);
|
|
866
921
|
}
|
|
867
922
|
const startTime = Date.now();
|
|
868
923
|
try {
|
|
@@ -876,20 +931,20 @@ async function executeBrowserTask(input, config = {}) {
|
|
|
876
931
|
body: JSON.stringify({
|
|
877
932
|
task: input.task,
|
|
878
933
|
url: input.url,
|
|
879
|
-
max_steps: input.
|
|
934
|
+
max_steps: input.maxSteps ?? 10,
|
|
880
935
|
model: input.model ?? "morph-computer-use-v0",
|
|
881
|
-
viewport_width: input.
|
|
882
|
-
viewport_height: input.
|
|
883
|
-
external_id: input.
|
|
884
|
-
repo_id: input.
|
|
885
|
-
commit_id: input.
|
|
886
|
-
record_video: input.
|
|
887
|
-
video_width: input.
|
|
888
|
-
video_height: input.
|
|
889
|
-
allow_resizing: input.
|
|
890
|
-
structured_output: input.
|
|
936
|
+
viewport_width: input.viewportWidth ?? 1280,
|
|
937
|
+
viewport_height: input.viewportHeight ?? 720,
|
|
938
|
+
external_id: input.externalId,
|
|
939
|
+
repo_id: input.repoId,
|
|
940
|
+
commit_id: input.commitId,
|
|
941
|
+
record_video: input.recordVideo ?? false,
|
|
942
|
+
video_width: input.videoWidth ?? input.viewportWidth ?? 1280,
|
|
943
|
+
video_height: input.videoHeight ?? input.viewportHeight ?? 720,
|
|
944
|
+
allow_resizing: input.allowResizing ?? false,
|
|
945
|
+
structured_output: input.structuredOutput,
|
|
891
946
|
auth: input.auth,
|
|
892
|
-
profile_id: input.
|
|
947
|
+
profile_id: input.profileId
|
|
893
948
|
})
|
|
894
949
|
},
|
|
895
950
|
config.retryConfig
|
|
@@ -897,17 +952,17 @@ async function executeBrowserTask(input, config = {}) {
|
|
|
897
952
|
const response = await withTimeout(
|
|
898
953
|
fetchPromise,
|
|
899
954
|
timeout,
|
|
900
|
-
`Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing
|
|
955
|
+
`Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing maxSteps.`
|
|
901
956
|
);
|
|
902
957
|
if (!response.ok) {
|
|
903
958
|
const errorText = await response.text().catch(() => response.statusText);
|
|
904
959
|
if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
|
|
905
960
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
906
961
|
}
|
|
907
|
-
const result = await response.json();
|
|
962
|
+
const result = mapTaskResult(await response.json());
|
|
908
963
|
const elapsed = Date.now() - startTime;
|
|
909
964
|
if (debug) {
|
|
910
|
-
console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.
|
|
965
|
+
console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.stepsTaken ?? 0} recordingId=${result.recordingId ?? "none"}`);
|
|
911
966
|
}
|
|
912
967
|
return result;
|
|
913
968
|
} catch (error) {
|
|
@@ -945,7 +1000,7 @@ async function getRecording(recordingId, config = {}) {
|
|
|
945
1000
|
if (debug) console.error(`[Browser] getRecording error: ${response.status} - ${errorText}`);
|
|
946
1001
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
947
1002
|
}
|
|
948
|
-
const data = await response.json();
|
|
1003
|
+
const data = mapRecordingStatus(await response.json());
|
|
949
1004
|
if (debug) console.log(`[Browser] Recording status: ${data.status}`);
|
|
950
1005
|
return {
|
|
951
1006
|
...data,
|
|
@@ -968,10 +1023,10 @@ async function waitForRecording(recordingId, config = {}, options = {}) {
|
|
|
968
1023
|
}
|
|
969
1024
|
async function executeWithRecording(input, config = {}) {
|
|
970
1025
|
const taskResult = await executeBrowserTask(input, config);
|
|
971
|
-
if (taskResult.
|
|
1026
|
+
if (taskResult.recordingId) {
|
|
972
1027
|
try {
|
|
973
1028
|
const recording = await waitForRecording(
|
|
974
|
-
taskResult.
|
|
1029
|
+
taskResult.recordingId,
|
|
975
1030
|
config,
|
|
976
1031
|
{ timeout: 6e4, pollInterval: 2e3 }
|
|
977
1032
|
);
|
|
@@ -981,12 +1036,12 @@ async function executeWithRecording(input, config = {}) {
|
|
|
981
1036
|
};
|
|
982
1037
|
} catch (error) {
|
|
983
1038
|
const errorRecording = {
|
|
984
|
-
id: taskResult.
|
|
1039
|
+
id: taskResult.recordingId,
|
|
985
1040
|
status: "ERROR",
|
|
986
1041
|
error: error instanceof Error ? error.message : String(error),
|
|
987
|
-
|
|
988
|
-
getWebp: (options) => getWebp(taskResult.
|
|
989
|
-
getErrors: () => getErrors(taskResult.
|
|
1042
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1043
|
+
getWebp: (options) => getWebp(taskResult.recordingId, config, options),
|
|
1044
|
+
getErrors: () => getErrors(taskResult.recordingId, config)
|
|
990
1045
|
};
|
|
991
1046
|
return {
|
|
992
1047
|
...taskResult,
|
|
@@ -1012,8 +1067,8 @@ async function getErrors(recordingId, config = {}) {
|
|
|
1012
1067
|
if (debug) console.error(`[Browser] getErrors error: ${response.status} - ${errorText}`);
|
|
1013
1068
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
1014
1069
|
}
|
|
1015
|
-
const errors = await response.json();
|
|
1016
|
-
if (debug) console.log(`[Browser] Found ${errors.
|
|
1070
|
+
const errors = mapErrorsResponse(await response.json());
|
|
1071
|
+
if (debug) console.log(`[Browser] Found ${errors.totalErrors} errors`);
|
|
1017
1072
|
return errors;
|
|
1018
1073
|
}
|
|
1019
1074
|
function stringifyStructuredOutput(schema) {
|
|
@@ -1046,6 +1101,84 @@ function parseStructuredTaskOutput(result, schema) {
|
|
|
1046
1101
|
throw error;
|
|
1047
1102
|
}
|
|
1048
1103
|
}
|
|
1104
|
+
function mapTaskResult(api) {
|
|
1105
|
+
if (!api || typeof api !== "object") {
|
|
1106
|
+
return api;
|
|
1107
|
+
}
|
|
1108
|
+
return {
|
|
1109
|
+
success: api.success,
|
|
1110
|
+
result: api.result,
|
|
1111
|
+
error: api.error,
|
|
1112
|
+
stepsTaken: api.steps_taken,
|
|
1113
|
+
executionTimeMs: api.execution_time_ms,
|
|
1114
|
+
urls: api.urls,
|
|
1115
|
+
actionNames: api.action_names,
|
|
1116
|
+
errors: api.errors,
|
|
1117
|
+
modelActions: api.model_actions,
|
|
1118
|
+
isDone: api.is_done,
|
|
1119
|
+
actionHistory: api.action_history,
|
|
1120
|
+
actionResults: api.action_results,
|
|
1121
|
+
hasErrors: api.has_errors,
|
|
1122
|
+
numberOfSteps: api.number_of_steps,
|
|
1123
|
+
judgement: api.judgement,
|
|
1124
|
+
isValidated: api.is_validated,
|
|
1125
|
+
replayId: api.replay_id,
|
|
1126
|
+
replayUrl: api.replay_url,
|
|
1127
|
+
recordingId: api.recording_id,
|
|
1128
|
+
recordingStatus: api.recording_status,
|
|
1129
|
+
taskId: api.task_id,
|
|
1130
|
+
status: api.status,
|
|
1131
|
+
output: api.output,
|
|
1132
|
+
debugUrl: api.debug_url
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
function mapRecordingStatus(api) {
|
|
1136
|
+
return {
|
|
1137
|
+
id: api.id,
|
|
1138
|
+
status: api.status,
|
|
1139
|
+
replayUrl: api.replay_url,
|
|
1140
|
+
networkUrl: api.network_url,
|
|
1141
|
+
consoleUrl: api.console_url,
|
|
1142
|
+
videoUrl: api.video_url,
|
|
1143
|
+
totalEvents: api.total_events,
|
|
1144
|
+
fileSize: api.file_size,
|
|
1145
|
+
duration: api.duration,
|
|
1146
|
+
error: api.error,
|
|
1147
|
+
createdAt: api.created_at
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
function mapBrowserError(api) {
|
|
1151
|
+
return {
|
|
1152
|
+
type: api.type,
|
|
1153
|
+
message: api.message,
|
|
1154
|
+
url: api.url,
|
|
1155
|
+
timestamp: api.timestamp,
|
|
1156
|
+
screenshotUrl: api.screenshot_url,
|
|
1157
|
+
capturedAt: api.captured_at,
|
|
1158
|
+
status: api.status
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
function mapErrorsResponse(api) {
|
|
1162
|
+
return {
|
|
1163
|
+
recordingId: api.recording_id,
|
|
1164
|
+
totalErrors: api.total_errors,
|
|
1165
|
+
errors: Array.isArray(api.errors) ? api.errors.map(mapBrowserError) : []
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
function mapWebpResponse(api) {
|
|
1169
|
+
return {
|
|
1170
|
+
webpUrl: api.webp_url,
|
|
1171
|
+
cached: api.cached,
|
|
1172
|
+
width: api.width,
|
|
1173
|
+
fps: api.fps,
|
|
1174
|
+
maxDuration: api.max_duration,
|
|
1175
|
+
fileSize: api.file_size,
|
|
1176
|
+
maxSizeMb: api.max_size_mb,
|
|
1177
|
+
budgetMet: api.budget_met,
|
|
1178
|
+
qualityUsed: api.quality_used,
|
|
1179
|
+
attempts: api.attempts
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1049
1182
|
async function getTaskStatus(taskId, config) {
|
|
1050
1183
|
const apiUrl = config.apiUrl || DEFAULT_CONFIG.apiUrl;
|
|
1051
1184
|
const debug = config.debug || false;
|
|
@@ -1061,7 +1194,7 @@ async function getTaskStatus(taskId, config) {
|
|
|
1061
1194
|
if (debug) console.error(`[Browser] getTaskStatus error: ${response.status} - ${errorText}`);
|
|
1062
1195
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
1063
1196
|
}
|
|
1064
|
-
const result = await response.json();
|
|
1197
|
+
const result = mapTaskResult(await response.json());
|
|
1065
1198
|
if (debug) console.log(`[Browser] Task status: ${result.status}`);
|
|
1066
1199
|
return result;
|
|
1067
1200
|
}
|
|
@@ -1084,42 +1217,44 @@ async function pollTaskUntilComplete(taskId, config, pollConfig = {}) {
|
|
|
1084
1217
|
throw new Error(`Task polling timeout after ${timeout}ms`);
|
|
1085
1218
|
}
|
|
1086
1219
|
function wrapTaskResponse(result, config) {
|
|
1220
|
+
const debugUrl = result.debugUrl ?? "";
|
|
1087
1221
|
const wrapped = {
|
|
1088
1222
|
...result,
|
|
1089
|
-
|
|
1090
|
-
|
|
1223
|
+
debugUrl,
|
|
1224
|
+
taskId: result.taskId || "",
|
|
1225
|
+
liveUrl: result.taskId ? generateLiveUrl(result.taskId, config) : debugUrl,
|
|
1091
1226
|
complete: async (pollConfig) => {
|
|
1092
|
-
if (result.
|
|
1093
|
-
return pollTaskUntilComplete(result.
|
|
1227
|
+
if (result.taskId) {
|
|
1228
|
+
return pollTaskUntilComplete(result.taskId, config, pollConfig);
|
|
1094
1229
|
}
|
|
1095
|
-
if (result.
|
|
1230
|
+
if (result.recordingId) {
|
|
1096
1231
|
const recording = await waitForRecording(
|
|
1097
|
-
result.
|
|
1232
|
+
result.recordingId,
|
|
1098
1233
|
config,
|
|
1099
1234
|
pollConfig
|
|
1100
1235
|
);
|
|
1101
1236
|
return {
|
|
1102
1237
|
...result,
|
|
1103
|
-
|
|
1238
|
+
recordingStatus: recording.status
|
|
1104
1239
|
};
|
|
1105
1240
|
}
|
|
1106
|
-
throw new Error("Cannot poll completion: no
|
|
1241
|
+
throw new Error("Cannot poll completion: no taskId or recordingId available");
|
|
1107
1242
|
},
|
|
1108
1243
|
// Add Steel live session helpers - either functional or error-throwing
|
|
1109
|
-
getLiveUrl:
|
|
1244
|
+
getLiveUrl: debugUrl ? (options) => buildLiveUrl(debugUrl, options) : () => {
|
|
1110
1245
|
throw new Error(
|
|
1111
1246
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
|
|
1112
1247
|
);
|
|
1113
1248
|
},
|
|
1114
|
-
getLiveIframe:
|
|
1249
|
+
getLiveIframe: debugUrl ? (optionsOrPreset) => {
|
|
1115
1250
|
const options = resolvePreset(optionsOrPreset);
|
|
1116
|
-
return buildLiveIframe(
|
|
1251
|
+
return buildLiveIframe(debugUrl, options);
|
|
1117
1252
|
} : () => {
|
|
1118
1253
|
throw new Error(
|
|
1119
1254
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
|
|
1120
1255
|
);
|
|
1121
1256
|
},
|
|
1122
|
-
getEmbedCode:
|
|
1257
|
+
getEmbedCode: debugUrl ? () => buildEmbedCode(debugUrl) : () => {
|
|
1123
1258
|
throw new Error(
|
|
1124
1259
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help."
|
|
1125
1260
|
);
|
|
@@ -1128,44 +1263,46 @@ function wrapTaskResponse(result, config) {
|
|
|
1128
1263
|
return wrapped;
|
|
1129
1264
|
}
|
|
1130
1265
|
function wrapTaskResponseWithSchema(result, config, schema) {
|
|
1266
|
+
const debugUrl = result.debugUrl ?? "";
|
|
1131
1267
|
const parsed = result.output ? parseStructuredTaskOutput(result, schema) : { ...result, parsed: null };
|
|
1132
1268
|
const wrapped = {
|
|
1133
1269
|
...parsed,
|
|
1134
|
-
|
|
1135
|
-
|
|
1270
|
+
debugUrl,
|
|
1271
|
+
taskId: result.taskId || "",
|
|
1272
|
+
liveUrl: result.taskId ? generateLiveUrl(result.taskId, config) : debugUrl,
|
|
1136
1273
|
complete: async (pollConfig) => {
|
|
1137
|
-
if (result.
|
|
1138
|
-
const finalResult = await pollTaskUntilComplete(result.
|
|
1274
|
+
if (result.taskId) {
|
|
1275
|
+
const finalResult = await pollTaskUntilComplete(result.taskId, config, pollConfig);
|
|
1139
1276
|
return parseStructuredTaskOutput(finalResult, schema);
|
|
1140
1277
|
}
|
|
1141
|
-
if (result.
|
|
1278
|
+
if (result.recordingId) {
|
|
1142
1279
|
const recording = await waitForRecording(
|
|
1143
|
-
result.
|
|
1280
|
+
result.recordingId,
|
|
1144
1281
|
config,
|
|
1145
1282
|
pollConfig
|
|
1146
1283
|
);
|
|
1147
1284
|
return {
|
|
1148
1285
|
...parsed,
|
|
1149
|
-
|
|
1286
|
+
recordingStatus: recording.status
|
|
1150
1287
|
};
|
|
1151
1288
|
}
|
|
1152
|
-
throw new Error("Cannot poll completion: no
|
|
1289
|
+
throw new Error("Cannot poll completion: no taskId or recordingId available");
|
|
1153
1290
|
},
|
|
1154
1291
|
// Add Steel live session helpers - either functional or error-throwing
|
|
1155
|
-
getLiveUrl:
|
|
1292
|
+
getLiveUrl: debugUrl ? (options) => buildLiveUrl(debugUrl, options) : () => {
|
|
1156
1293
|
throw new Error(
|
|
1157
1294
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions. "
|
|
1158
1295
|
);
|
|
1159
1296
|
},
|
|
1160
|
-
getLiveIframe:
|
|
1297
|
+
getLiveIframe: debugUrl ? (optionsOrPreset) => {
|
|
1161
1298
|
const options = resolvePreset(optionsOrPreset);
|
|
1162
|
-
return buildLiveIframe(
|
|
1299
|
+
return buildLiveIframe(debugUrl, options);
|
|
1163
1300
|
} : () => {
|
|
1164
1301
|
throw new Error(
|
|
1165
1302
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions."
|
|
1166
1303
|
);
|
|
1167
1304
|
},
|
|
1168
|
-
getEmbedCode:
|
|
1305
|
+
getEmbedCode: debugUrl ? () => buildEmbedCode(debugUrl) : () => {
|
|
1169
1306
|
throw new Error(
|
|
1170
1307
|
"Live sessions not available. Your backend must return a debugUrl in the response. Contact support@morphllm.com if you need help enabling live sessions."
|
|
1171
1308
|
);
|
|
@@ -1180,10 +1317,11 @@ async function getWebp(recordingId, config = {}, options = {}) {
|
|
|
1180
1317
|
throw new Error("API key required for getWebp");
|
|
1181
1318
|
}
|
|
1182
1319
|
const params = new URLSearchParams();
|
|
1183
|
-
if (options.
|
|
1320
|
+
if (options.maxDuration !== void 0) params.set("max_duration", String(options.maxDuration));
|
|
1184
1321
|
if (options.fps !== void 0) params.set("fps", String(options.fps));
|
|
1185
1322
|
if (options.width !== void 0) params.set("width", String(options.width));
|
|
1186
1323
|
if (options.quality !== void 0) params.set("quality", String(options.quality));
|
|
1324
|
+
if (options.maxSizeMb !== void 0) params.set("max_size_mb", String(options.maxSizeMb));
|
|
1187
1325
|
const url = `${apiUrl}/recordings/${recordingId}/webp${params.toString() ? "?" + params.toString() : ""}`;
|
|
1188
1326
|
if (debug) console.log(`[Browser] getWebp: ${url}`);
|
|
1189
1327
|
const response = await fetch(url, {
|
|
@@ -1195,8 +1333,8 @@ async function getWebp(recordingId, config = {}, options = {}) {
|
|
|
1195
1333
|
if (debug) console.error(`[Browser] getWebp error: ${response.status} - ${errorText}`);
|
|
1196
1334
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
1197
1335
|
}
|
|
1198
|
-
const result = await response.json();
|
|
1199
|
-
if (debug) console.log(`[Browser] WebP ready: ${result.
|
|
1336
|
+
const result = mapWebpResponse(await response.json());
|
|
1337
|
+
if (debug) console.log(`[Browser] WebP ready: ${result.webpUrl} (cached: ${result.cached})`);
|
|
1200
1338
|
return result;
|
|
1201
1339
|
}
|
|
1202
1340
|
async function checkHealth(config = {}) {
|
|
@@ -1263,7 +1401,7 @@ Include verification steps:
|
|
|
1263
1401
|
## Requirements
|
|
1264
1402
|
- **URL**: Must be publicly accessible (use tunnels like ngrok, Cloudflare, or deploy to staging)
|
|
1265
1403
|
- **Timing**: Use this after implementation, not during coding
|
|
1266
|
-
- **Complexity**: Set
|
|
1404
|
+
- **Complexity**: Set maxSteps higher (20-30) for multi-step user workflows`;
|
|
1267
1405
|
var BROWSER_SYSTEM_PROMPT = `You are an AI agent designed to automate browser tasks to accomplish the <user_request>. Respond with a valid JSON object in the format: {"thinking": "Reason step-by-step about your current state, history, and the user request to decide your next goal and action. Analyze the browser state and screenshot to confirm the outcome of your last action.", "evaluation_previous_goal": "A concise, one-sentence evaluation of your last action's outcome (e.g., Success, Failure, or Uncertain).", "memory": "1-3 sentences summarizing key information and progress so far. This helps you track progress across multiple steps (e.g., items collected, pages visited).", "next_goal": "A clear, one-sentence description of your immediate next objective.", "action": [{"action_name": {"parameter": "value"}}]}`;
|
|
1268
1406
|
|
|
1269
1407
|
// tools/browser/anthropic.ts
|
|
@@ -1286,7 +1424,7 @@ var browserTool = {
|
|
|
1286
1424
|
type: "string",
|
|
1287
1425
|
description: "Starting URL (e.g., https://3000-xyz.e2b.dev). Required if navigating to a specific page."
|
|
1288
1426
|
},
|
|
1289
|
-
|
|
1427
|
+
maxSteps: {
|
|
1290
1428
|
type: "number",
|
|
1291
1429
|
description: "Maximum number of browser actions to take (1-50). Default: 10. Use 15-30 for complex flows.",
|
|
1292
1430
|
default: 10
|
|
@@ -1305,8 +1443,8 @@ function formatResult(result) {
|
|
|
1305
1443
|
if (result.success) {
|
|
1306
1444
|
const parts = [
|
|
1307
1445
|
"\u2705 Browser task completed successfully",
|
|
1308
|
-
`Steps taken: ${result.
|
|
1309
|
-
result.
|
|
1446
|
+
`Steps taken: ${result.stepsTaken ?? 0}`,
|
|
1447
|
+
result.executionTimeMs ? `Execution time: ${result.executionTimeMs}ms` : null,
|
|
1310
1448
|
"",
|
|
1311
1449
|
"Result:",
|
|
1312
1450
|
result.result || "Task completed"
|
|
@@ -1354,7 +1492,7 @@ var browserTool2 = {
|
|
|
1354
1492
|
type: "string",
|
|
1355
1493
|
description: "Starting URL (e.g., https://3000-xyz.e2b.dev). Required if navigating to a specific page."
|
|
1356
1494
|
},
|
|
1357
|
-
|
|
1495
|
+
maxSteps: {
|
|
1358
1496
|
type: "number",
|
|
1359
1497
|
description: "Maximum number of browser actions to take (1-50). Default: 10. Use 15-30 for complex flows.",
|
|
1360
1498
|
default: 10
|
|
@@ -1378,8 +1516,8 @@ function formatResult2(result) {
|
|
|
1378
1516
|
if (result.success) {
|
|
1379
1517
|
const parts = [
|
|
1380
1518
|
"\u2705 Browser task completed successfully",
|
|
1381
|
-
`Steps taken: ${result.
|
|
1382
|
-
result.
|
|
1519
|
+
`Steps taken: ${result.stepsTaken ?? 0}`,
|
|
1520
|
+
result.executionTimeMs ? `Execution time: ${result.executionTimeMs}ms` : null,
|
|
1383
1521
|
"",
|
|
1384
1522
|
"Result:",
|
|
1385
1523
|
result.result || "Task completed"
|
|
@@ -1417,24 +1555,24 @@ function createBrowserTool3(config) {
|
|
|
1417
1555
|
const schema = import_zod.z.object({
|
|
1418
1556
|
task: import_zod.z.string().describe('Natural language description of what to do (e.g., "Test checkout flow for buying a pineapple")'),
|
|
1419
1557
|
url: import_zod.z.string().optional().describe("Starting URL (e.g., https://3000-xyz.e2b.dev)"),
|
|
1420
|
-
|
|
1558
|
+
maxSteps: import_zod.z.number().min(1).max(50).default(10).describe("Maximum number of browser actions to take"),
|
|
1421
1559
|
region: import_zod.z.enum(["sfo", "lon"]).default("sfo").describe("Browserless region: sfo (US West) or lon (Europe)")
|
|
1422
1560
|
});
|
|
1423
1561
|
return (0, import_ai.tool)({
|
|
1424
1562
|
description: BROWSER_TOOL_DESCRIPTION,
|
|
1425
1563
|
inputSchema: schema,
|
|
1426
1564
|
execute: async (params) => {
|
|
1427
|
-
const { task, url,
|
|
1565
|
+
const { task, url, maxSteps, region } = params;
|
|
1428
1566
|
const result = await executeBrowserTask(
|
|
1429
|
-
{ task, url,
|
|
1567
|
+
{ task, url, maxSteps, region },
|
|
1430
1568
|
config
|
|
1431
1569
|
);
|
|
1432
1570
|
if (result.success) {
|
|
1433
1571
|
return {
|
|
1434
1572
|
success: true,
|
|
1435
1573
|
result: result.result,
|
|
1436
|
-
|
|
1437
|
-
|
|
1574
|
+
stepsTaken: result.stepsTaken,
|
|
1575
|
+
executionTimeMs: result.executionTimeMs
|
|
1438
1576
|
};
|
|
1439
1577
|
}
|
|
1440
1578
|
return {
|
|
@@ -1469,7 +1607,7 @@ var TOOL_PARAMETERS = {
|
|
|
1469
1607
|
type: import_generative_ai.SchemaType.STRING,
|
|
1470
1608
|
description: "Starting URL (e.g., https://3000-xyz.e2b.dev). Required if navigating to a specific page."
|
|
1471
1609
|
},
|
|
1472
|
-
|
|
1610
|
+
maxSteps: {
|
|
1473
1611
|
type: import_generative_ai.SchemaType.NUMBER,
|
|
1474
1612
|
description: "Maximum number of browser actions to take (1-50). Default: 10. Use 15-30 for complex flows."
|
|
1475
1613
|
},
|
|
@@ -1493,8 +1631,8 @@ function formatResult3(result) {
|
|
|
1493
1631
|
if (result.success) {
|
|
1494
1632
|
const parts = [
|
|
1495
1633
|
"\u2705 Browser task completed successfully",
|
|
1496
|
-
`Steps taken: ${result.
|
|
1497
|
-
result.
|
|
1634
|
+
`Steps taken: ${result.stepsTaken ?? 0}`,
|
|
1635
|
+
result.executionTimeMs ? `Execution time: ${result.executionTimeMs}ms` : null,
|
|
1498
1636
|
"",
|
|
1499
1637
|
"Result:",
|
|
1500
1638
|
result.result || "Task completed"
|