@modelrelay/sdk 1.28.0 → 1.36.0
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/api-jqOwvH6O.d.cts +4761 -0
- package/dist/api-jqOwvH6O.d.ts +4761 -0
- package/dist/billing/index.cjs +151 -0
- package/dist/billing/index.d.cts +178 -0
- package/dist/billing/index.d.ts +178 -0
- package/dist/billing/index.js +128 -0
- package/dist/chunk-ADQ74R7T.js +1194 -0
- package/dist/{chunk-BL7GWXRZ.js → chunk-CV3DTA6P.js} +1 -1
- package/dist/{chunk-G5H7EY4F.js → chunk-EMQKOEF3.js} +1 -1
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-ZBQKY2W2.js +1194 -0
- package/dist/index.cjs +295 -1246
- package/dist/index.d.cts +163 -4760
- package/dist/index.d.ts +163 -4760
- package/dist/index.js +9 -955
- package/dist/node.cjs +603 -9
- package/dist/node.d.cts +201 -2
- package/dist/node.d.ts +201 -2
- package/dist/node.js +582 -2
- package/dist/tools-Cnl2MgVa.d.cts +1174 -0
- package/dist/tools-Cnl2MgVa.d.ts +1174 -0
- package/dist/{tools-Db-F5rIL.d.cts → tools-DAqAFZ8F.d.cts} +9 -6
- package/dist/{tools-Db-F5rIL.d.ts → tools-DAqAFZ8F.d.ts} +9 -6
- package/package.json +14 -9
package/dist/index.js
CHANGED
|
@@ -28,7 +28,6 @@ import {
|
|
|
28
28
|
TransportError,
|
|
29
29
|
WebToolIntents,
|
|
30
30
|
WorkflowValidationError,
|
|
31
|
-
__export,
|
|
32
31
|
asModelId,
|
|
33
32
|
asProviderId,
|
|
34
33
|
asTierCode,
|
|
@@ -70,7 +69,10 @@ import {
|
|
|
70
69
|
toolResultMessage,
|
|
71
70
|
tryParseToolArgs,
|
|
72
71
|
zodToJsonSchema
|
|
73
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-ADQ74R7T.js";
|
|
73
|
+
import {
|
|
74
|
+
__export
|
|
75
|
+
} from "./chunk-MLKGABMK.js";
|
|
74
76
|
|
|
75
77
|
// src/api_keys.ts
|
|
76
78
|
var PUBLISHABLE_PREFIX = "mr_pk_";
|
|
@@ -298,7 +300,7 @@ var AuthClient = class {
|
|
|
298
300
|
projectId: apiResp.project_id,
|
|
299
301
|
customerId: apiResp.customer_id,
|
|
300
302
|
customerExternalId: apiResp.customer_external_id,
|
|
301
|
-
tierCode: apiResp.tier_code
|
|
303
|
+
tierCode: apiResp.tier_code ? asTierCode(apiResp.tier_code) : void 0
|
|
302
304
|
};
|
|
303
305
|
}
|
|
304
306
|
/**
|
|
@@ -330,7 +332,7 @@ var AuthClient = class {
|
|
|
330
332
|
projectId: apiResp.project_id,
|
|
331
333
|
customerId: apiResp.customer_id,
|
|
332
334
|
customerExternalId: apiResp.customer_external_id,
|
|
333
|
-
tierCode: apiResp.tier_code
|
|
335
|
+
tierCode: apiResp.tier_code ? asTierCode(apiResp.tier_code) : void 0
|
|
334
336
|
};
|
|
335
337
|
}
|
|
336
338
|
/**
|
|
@@ -442,7 +444,7 @@ var AuthClient = class {
|
|
|
442
444
|
projectId: apiResp.project_id,
|
|
443
445
|
customerId: apiResp.customer_id,
|
|
444
446
|
customerExternalId: apiResp.customer_external_id,
|
|
445
|
-
tierCode: apiResp.tier_code
|
|
447
|
+
tierCode: apiResp.tier_code ? asTierCode(apiResp.tier_code) : void 0
|
|
446
448
|
}
|
|
447
449
|
};
|
|
448
450
|
} catch (err) {
|
|
@@ -2779,370 +2781,6 @@ var WorkflowsClient = class {
|
|
|
2779
2781
|
}
|
|
2780
2782
|
};
|
|
2781
2783
|
|
|
2782
|
-
// src/customers.ts
|
|
2783
|
-
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2784
|
-
function isValidEmail(email) {
|
|
2785
|
-
return EMAIL_REGEX.test(email);
|
|
2786
|
-
}
|
|
2787
|
-
var CustomersClient = class {
|
|
2788
|
-
constructor(http, cfg) {
|
|
2789
|
-
this.http = http;
|
|
2790
|
-
this.apiKey = cfg.apiKey ? parseApiKey(cfg.apiKey) : void 0;
|
|
2791
|
-
this.hasSecretKey = this.apiKey ? isSecretKey(this.apiKey) : false;
|
|
2792
|
-
this.accessToken = cfg.accessToken;
|
|
2793
|
-
this.tokenProvider = cfg.tokenProvider;
|
|
2794
|
-
}
|
|
2795
|
-
ensureSecretKey() {
|
|
2796
|
-
if (!this.apiKey || !this.hasSecretKey) {
|
|
2797
|
-
throw new ConfigError(
|
|
2798
|
-
"Secret key (mr_sk_*) required for customer operations"
|
|
2799
|
-
);
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
ensureApiKey() {
|
|
2803
|
-
if (!this.apiKey) {
|
|
2804
|
-
throw new ConfigError(
|
|
2805
|
-
"API key (mr_pk_* or mr_sk_*) required for claim operation"
|
|
2806
|
-
);
|
|
2807
|
-
}
|
|
2808
|
-
}
|
|
2809
|
-
async customerAccessToken() {
|
|
2810
|
-
if (this.accessToken?.trim()) {
|
|
2811
|
-
return this.accessToken.trim();
|
|
2812
|
-
}
|
|
2813
|
-
if (this.tokenProvider) {
|
|
2814
|
-
const token = (await this.tokenProvider.getToken())?.trim();
|
|
2815
|
-
if (!token) {
|
|
2816
|
-
throw new ConfigError("tokenProvider returned an empty token");
|
|
2817
|
-
}
|
|
2818
|
-
return token;
|
|
2819
|
-
}
|
|
2820
|
-
throw new ConfigError("Access token or tokenProvider required for customers.me()");
|
|
2821
|
-
}
|
|
2822
|
-
/**
|
|
2823
|
-
* Get the authenticated customer from a customer-scoped bearer token.
|
|
2824
|
-
*
|
|
2825
|
-
* This endpoint requires a customer bearer token. API keys are not accepted.
|
|
2826
|
-
*/
|
|
2827
|
-
async me() {
|
|
2828
|
-
const token = await this.customerAccessToken();
|
|
2829
|
-
const response = await this.http.json("/customers/me", {
|
|
2830
|
-
method: "GET",
|
|
2831
|
-
accessToken: token
|
|
2832
|
-
});
|
|
2833
|
-
if (!response.customer) {
|
|
2834
|
-
throw new ConfigError("missing customer in response");
|
|
2835
|
-
}
|
|
2836
|
-
return response.customer;
|
|
2837
|
-
}
|
|
2838
|
-
/**
|
|
2839
|
-
* Get the authenticated customer's subscription details.
|
|
2840
|
-
*
|
|
2841
|
-
* This endpoint requires a customer bearer token. API keys are not accepted.
|
|
2842
|
-
*/
|
|
2843
|
-
async meSubscription() {
|
|
2844
|
-
const token = await this.customerAccessToken();
|
|
2845
|
-
const response = await this.http.json("/customers/me/subscription", {
|
|
2846
|
-
method: "GET",
|
|
2847
|
-
accessToken: token
|
|
2848
|
-
});
|
|
2849
|
-
if (!response.subscription) {
|
|
2850
|
-
throw new ConfigError("missing subscription in response");
|
|
2851
|
-
}
|
|
2852
|
-
return response.subscription;
|
|
2853
|
-
}
|
|
2854
|
-
/**
|
|
2855
|
-
* Get the authenticated customer's usage metrics for the current billing window.
|
|
2856
|
-
*
|
|
2857
|
-
* This endpoint requires a customer bearer token. API keys are not accepted.
|
|
2858
|
-
*/
|
|
2859
|
-
async meUsage() {
|
|
2860
|
-
const token = await this.customerAccessToken();
|
|
2861
|
-
const response = await this.http.json("/customers/me/usage", {
|
|
2862
|
-
method: "GET",
|
|
2863
|
-
accessToken: token
|
|
2864
|
-
});
|
|
2865
|
-
if (!response.usage) {
|
|
2866
|
-
throw new ConfigError("missing usage in response");
|
|
2867
|
-
}
|
|
2868
|
-
return response.usage;
|
|
2869
|
-
}
|
|
2870
|
-
/**
|
|
2871
|
-
* List all customers in the project.
|
|
2872
|
-
*/
|
|
2873
|
-
async list() {
|
|
2874
|
-
this.ensureSecretKey();
|
|
2875
|
-
const response = await this.http.json("/customers", {
|
|
2876
|
-
method: "GET",
|
|
2877
|
-
apiKey: this.apiKey
|
|
2878
|
-
});
|
|
2879
|
-
return response.customers;
|
|
2880
|
-
}
|
|
2881
|
-
/**
|
|
2882
|
-
* Create a new customer in the project.
|
|
2883
|
-
*/
|
|
2884
|
-
async create(request) {
|
|
2885
|
-
this.ensureSecretKey();
|
|
2886
|
-
if (!request.external_id?.trim()) {
|
|
2887
|
-
throw new ConfigError("external_id is required");
|
|
2888
|
-
}
|
|
2889
|
-
if (!request.email?.trim()) {
|
|
2890
|
-
throw new ConfigError("email is required");
|
|
2891
|
-
}
|
|
2892
|
-
if (!isValidEmail(request.email)) {
|
|
2893
|
-
throw new ConfigError("invalid email format");
|
|
2894
|
-
}
|
|
2895
|
-
const response = await this.http.json("/customers", {
|
|
2896
|
-
method: "POST",
|
|
2897
|
-
body: request,
|
|
2898
|
-
apiKey: this.apiKey
|
|
2899
|
-
});
|
|
2900
|
-
return response.customer;
|
|
2901
|
-
}
|
|
2902
|
-
/**
|
|
2903
|
-
* Get a customer by ID.
|
|
2904
|
-
*/
|
|
2905
|
-
async get(customerId) {
|
|
2906
|
-
this.ensureSecretKey();
|
|
2907
|
-
if (!customerId?.trim()) {
|
|
2908
|
-
throw new ConfigError("customerId is required");
|
|
2909
|
-
}
|
|
2910
|
-
const response = await this.http.json(
|
|
2911
|
-
`/customers/${customerId}`,
|
|
2912
|
-
{
|
|
2913
|
-
method: "GET",
|
|
2914
|
-
apiKey: this.apiKey
|
|
2915
|
-
}
|
|
2916
|
-
);
|
|
2917
|
-
return response.customer;
|
|
2918
|
-
}
|
|
2919
|
-
/**
|
|
2920
|
-
* Upsert a customer by external_id.
|
|
2921
|
-
* If a customer with the given external_id exists, it is updated.
|
|
2922
|
-
* Otherwise, a new customer is created.
|
|
2923
|
-
*/
|
|
2924
|
-
async upsert(request) {
|
|
2925
|
-
this.ensureSecretKey();
|
|
2926
|
-
if (!request.external_id?.trim()) {
|
|
2927
|
-
throw new ConfigError("external_id is required");
|
|
2928
|
-
}
|
|
2929
|
-
if (!request.email?.trim()) {
|
|
2930
|
-
throw new ConfigError("email is required");
|
|
2931
|
-
}
|
|
2932
|
-
if (!isValidEmail(request.email)) {
|
|
2933
|
-
throw new ConfigError("invalid email format");
|
|
2934
|
-
}
|
|
2935
|
-
const response = await this.http.json("/customers", {
|
|
2936
|
-
method: "PUT",
|
|
2937
|
-
body: request,
|
|
2938
|
-
apiKey: this.apiKey
|
|
2939
|
-
});
|
|
2940
|
-
return response.customer;
|
|
2941
|
-
}
|
|
2942
|
-
/**
|
|
2943
|
-
* Link a customer identity (provider + subject) to a customer found by email.
|
|
2944
|
-
* Used when a customer subscribes via Stripe Checkout (email only) and later authenticates to the app.
|
|
2945
|
-
*
|
|
2946
|
-
* This is a user self-service operation that works with publishable keys,
|
|
2947
|
-
* allowing CLI tools and frontends to link subscriptions to user identities.
|
|
2948
|
-
*
|
|
2949
|
-
* Works with both publishable keys (mr_pk_*) and secret keys (mr_sk_*).
|
|
2950
|
-
*
|
|
2951
|
-
* @throws {APIError} with status 404 if customer not found by email
|
|
2952
|
-
* @throws {APIError} with status 409 if the identity is already linked to a different customer
|
|
2953
|
-
*/
|
|
2954
|
-
async claim(request) {
|
|
2955
|
-
this.ensureApiKey();
|
|
2956
|
-
if (!request.email?.trim()) {
|
|
2957
|
-
throw new ConfigError("email is required");
|
|
2958
|
-
}
|
|
2959
|
-
if (!isValidEmail(request.email)) {
|
|
2960
|
-
throw new ConfigError("invalid email format");
|
|
2961
|
-
}
|
|
2962
|
-
if (!request.provider?.trim()) {
|
|
2963
|
-
throw new ConfigError("provider is required");
|
|
2964
|
-
}
|
|
2965
|
-
if (!request.subject?.trim()) {
|
|
2966
|
-
throw new ConfigError("subject is required");
|
|
2967
|
-
}
|
|
2968
|
-
await this.http.request("/customers/claim", {
|
|
2969
|
-
method: "POST",
|
|
2970
|
-
body: request,
|
|
2971
|
-
apiKey: this.apiKey
|
|
2972
|
-
});
|
|
2973
|
-
}
|
|
2974
|
-
/**
|
|
2975
|
-
* Delete a customer by ID.
|
|
2976
|
-
*/
|
|
2977
|
-
async delete(customerId) {
|
|
2978
|
-
this.ensureSecretKey();
|
|
2979
|
-
if (!customerId?.trim()) {
|
|
2980
|
-
throw new ConfigError("customerId is required");
|
|
2981
|
-
}
|
|
2982
|
-
await this.http.request(`/customers/${customerId}`, {
|
|
2983
|
-
method: "DELETE",
|
|
2984
|
-
apiKey: this.apiKey
|
|
2985
|
-
});
|
|
2986
|
-
}
|
|
2987
|
-
/**
|
|
2988
|
-
* Create a Stripe checkout session for a customer subscription.
|
|
2989
|
-
*/
|
|
2990
|
-
async subscribe(customerId, request) {
|
|
2991
|
-
this.ensureSecretKey();
|
|
2992
|
-
if (!customerId?.trim()) {
|
|
2993
|
-
throw new ConfigError("customerId is required");
|
|
2994
|
-
}
|
|
2995
|
-
if (!request.tier_id?.trim()) {
|
|
2996
|
-
throw new ConfigError("tier_id is required");
|
|
2997
|
-
}
|
|
2998
|
-
if (!request.success_url?.trim() || !request.cancel_url?.trim()) {
|
|
2999
|
-
throw new ConfigError("success_url and cancel_url are required");
|
|
3000
|
-
}
|
|
3001
|
-
return await this.http.json(
|
|
3002
|
-
`/customers/${customerId}/subscribe`,
|
|
3003
|
-
{
|
|
3004
|
-
method: "POST",
|
|
3005
|
-
body: request,
|
|
3006
|
-
apiKey: this.apiKey
|
|
3007
|
-
}
|
|
3008
|
-
);
|
|
3009
|
-
}
|
|
3010
|
-
/**
|
|
3011
|
-
* Get the subscription details for a customer.
|
|
3012
|
-
*/
|
|
3013
|
-
async getSubscription(customerId) {
|
|
3014
|
-
this.ensureSecretKey();
|
|
3015
|
-
if (!customerId?.trim()) {
|
|
3016
|
-
throw new ConfigError("customerId is required");
|
|
3017
|
-
}
|
|
3018
|
-
const response = await this.http.json(
|
|
3019
|
-
`/customers/${customerId}/subscription`,
|
|
3020
|
-
{
|
|
3021
|
-
method: "GET",
|
|
3022
|
-
apiKey: this.apiKey
|
|
3023
|
-
}
|
|
3024
|
-
);
|
|
3025
|
-
return response.subscription;
|
|
3026
|
-
}
|
|
3027
|
-
/**
|
|
3028
|
-
* Cancel a customer's subscription at period end.
|
|
3029
|
-
*/
|
|
3030
|
-
async unsubscribe(customerId) {
|
|
3031
|
-
this.ensureSecretKey();
|
|
3032
|
-
if (!customerId?.trim()) {
|
|
3033
|
-
throw new ConfigError("customerId is required");
|
|
3034
|
-
}
|
|
3035
|
-
await this.http.request(`/customers/${customerId}/subscription`, {
|
|
3036
|
-
method: "DELETE",
|
|
3037
|
-
apiKey: this.apiKey
|
|
3038
|
-
});
|
|
3039
|
-
}
|
|
3040
|
-
};
|
|
3041
|
-
|
|
3042
|
-
// src/tiers.ts
|
|
3043
|
-
var TiersClient = class {
|
|
3044
|
-
constructor(http, cfg) {
|
|
3045
|
-
this.http = http;
|
|
3046
|
-
this.apiKey = cfg.apiKey ? parseApiKey(cfg.apiKey) : void 0;
|
|
3047
|
-
this.hasSecretKey = this.apiKey ? isSecretKey(this.apiKey) : false;
|
|
3048
|
-
}
|
|
3049
|
-
ensureApiKey() {
|
|
3050
|
-
if (!this.apiKey) {
|
|
3051
|
-
throw new ConfigError(
|
|
3052
|
-
"API key (mr_pk_* or mr_sk_*) required for tier operations"
|
|
3053
|
-
);
|
|
3054
|
-
}
|
|
3055
|
-
}
|
|
3056
|
-
ensureSecretKey() {
|
|
3057
|
-
if (!this.apiKey || !this.hasSecretKey) {
|
|
3058
|
-
throw new ConfigError(
|
|
3059
|
-
"Secret key (mr_sk_*) required for checkout operations"
|
|
3060
|
-
);
|
|
3061
|
-
}
|
|
3062
|
-
}
|
|
3063
|
-
/**
|
|
3064
|
-
* List all tiers in the project.
|
|
3065
|
-
*/
|
|
3066
|
-
async list() {
|
|
3067
|
-
this.ensureApiKey();
|
|
3068
|
-
const response = await this.http.json("/tiers", {
|
|
3069
|
-
method: "GET",
|
|
3070
|
-
apiKey: this.apiKey
|
|
3071
|
-
});
|
|
3072
|
-
return response.tiers;
|
|
3073
|
-
}
|
|
3074
|
-
/**
|
|
3075
|
-
* Get a tier by ID.
|
|
3076
|
-
*/
|
|
3077
|
-
async get(tierId) {
|
|
3078
|
-
this.ensureApiKey();
|
|
3079
|
-
if (!tierId?.trim()) {
|
|
3080
|
-
throw new ConfigError("tierId is required");
|
|
3081
|
-
}
|
|
3082
|
-
const response = await this.http.json(`/tiers/${tierId}`, {
|
|
3083
|
-
method: "GET",
|
|
3084
|
-
apiKey: this.apiKey
|
|
3085
|
-
});
|
|
3086
|
-
return response.tier;
|
|
3087
|
-
}
|
|
3088
|
-
/**
|
|
3089
|
-
* Create a Stripe checkout session for a tier (Stripe-first flow).
|
|
3090
|
-
*
|
|
3091
|
-
* This enables users to subscribe before authenticating. Stripe collects
|
|
3092
|
-
* the customer's email during checkout. After checkout completes, a
|
|
3093
|
-
* customer record is created with the email from Stripe. The customer
|
|
3094
|
-
* can later be linked to an identity via POST /customers/claim.
|
|
3095
|
-
*
|
|
3096
|
-
* Requires a secret key (mr_sk_*).
|
|
3097
|
-
*
|
|
3098
|
-
* @param tierId - The tier ID to create a checkout session for
|
|
3099
|
-
* @param request - Checkout session request with redirect URLs
|
|
3100
|
-
* @returns Checkout session with Stripe URL
|
|
3101
|
-
*/
|
|
3102
|
-
async checkout(tierId, request) {
|
|
3103
|
-
this.ensureSecretKey();
|
|
3104
|
-
if (!tierId?.trim()) {
|
|
3105
|
-
throw new ConfigError("tierId is required");
|
|
3106
|
-
}
|
|
3107
|
-
if (!request.success_url?.trim()) {
|
|
3108
|
-
throw new ConfigError("success_url is required");
|
|
3109
|
-
}
|
|
3110
|
-
if (!request.cancel_url?.trim()) {
|
|
3111
|
-
throw new ConfigError("cancel_url is required");
|
|
3112
|
-
}
|
|
3113
|
-
return await this.http.json(
|
|
3114
|
-
`/tiers/${tierId}/checkout`,
|
|
3115
|
-
{
|
|
3116
|
-
method: "POST",
|
|
3117
|
-
apiKey: this.apiKey,
|
|
3118
|
-
body: request
|
|
3119
|
-
}
|
|
3120
|
-
);
|
|
3121
|
-
}
|
|
3122
|
-
};
|
|
3123
|
-
|
|
3124
|
-
// src/models.ts
|
|
3125
|
-
var ModelsClient = class {
|
|
3126
|
-
constructor(http) {
|
|
3127
|
-
this.http = http;
|
|
3128
|
-
}
|
|
3129
|
-
/**
|
|
3130
|
-
* List active models with rich metadata.
|
|
3131
|
-
*/
|
|
3132
|
-
async list(params = {}) {
|
|
3133
|
-
const qs = new URLSearchParams();
|
|
3134
|
-
if (params.provider?.trim()) {
|
|
3135
|
-
qs.set("provider", params.provider.trim());
|
|
3136
|
-
}
|
|
3137
|
-
if (params.capability) {
|
|
3138
|
-
qs.set("capability", params.capability);
|
|
3139
|
-
}
|
|
3140
|
-
const path = qs.toString() ? `/models?${qs.toString()}` : "/models";
|
|
3141
|
-
const resp = await this.http.json(path, { method: "GET" });
|
|
3142
|
-
return resp.models;
|
|
3143
|
-
}
|
|
3144
|
-
};
|
|
3145
|
-
|
|
3146
2784
|
// src/images.ts
|
|
3147
2785
|
var IMAGES_PATH = "/images/generate";
|
|
3148
2786
|
var ImagesClient = class {
|
|
@@ -3439,8 +3077,8 @@ function getModelContextCacheEntry(client) {
|
|
|
3439
3077
|
async function populateModelContextCache(client, entry) {
|
|
3440
3078
|
if (!entry.listPromise) {
|
|
3441
3079
|
entry.listPromise = (async () => {
|
|
3442
|
-
const
|
|
3443
|
-
for (const model of models) {
|
|
3080
|
+
const response = await client.http.json("/models");
|
|
3081
|
+
for (const model of response.models) {
|
|
3444
3082
|
entry.byId.set(String(model.model_id), {
|
|
3445
3083
|
contextWindow: model.context_window,
|
|
3446
3084
|
maxOutputTokens: model.max_output_tokens
|
|
@@ -7692,579 +7330,6 @@ function createMockFetchQueue(responses) {
|
|
|
7692
7330
|
return { fetch: fetchImpl, calls };
|
|
7693
7331
|
}
|
|
7694
7332
|
|
|
7695
|
-
// src/tools_browser.ts
|
|
7696
|
-
var BrowserToolNames = {
|
|
7697
|
-
/** Navigate to a URL and return accessibility tree */
|
|
7698
|
-
NAVIGATE: "browser.navigate",
|
|
7699
|
-
/** Click an element by accessible name/role */
|
|
7700
|
-
CLICK: "browser.click",
|
|
7701
|
-
/** Type text into an input field */
|
|
7702
|
-
TYPE: "browser.type",
|
|
7703
|
-
/** Get current accessibility tree */
|
|
7704
|
-
SNAPSHOT: "browser.snapshot",
|
|
7705
|
-
/** Scroll the page */
|
|
7706
|
-
SCROLL: "browser.scroll",
|
|
7707
|
-
/** Capture a screenshot */
|
|
7708
|
-
SCREENSHOT: "browser.screenshot",
|
|
7709
|
-
/** Extract data using CSS selectors */
|
|
7710
|
-
EXTRACT: "browser.extract"
|
|
7711
|
-
};
|
|
7712
|
-
var BrowserDefaults = {
|
|
7713
|
-
/** Navigation timeout in milliseconds */
|
|
7714
|
-
NAVIGATION_TIMEOUT_MS: 3e4,
|
|
7715
|
-
/** Action timeout in milliseconds */
|
|
7716
|
-
ACTION_TIMEOUT_MS: 5e3,
|
|
7717
|
-
/** Maximum nodes to include in accessibility tree output */
|
|
7718
|
-
MAX_SNAPSHOT_NODES: 500,
|
|
7719
|
-
/** Maximum screenshot size in bytes */
|
|
7720
|
-
MAX_SCREENSHOT_BYTES: 5e6
|
|
7721
|
-
};
|
|
7722
|
-
var BrowserToolPack = class {
|
|
7723
|
-
constructor(options = {}) {
|
|
7724
|
-
this.browser = null;
|
|
7725
|
-
this.context = null;
|
|
7726
|
-
this.page = null;
|
|
7727
|
-
this.cdpSession = null;
|
|
7728
|
-
this.ownsBrowser = false;
|
|
7729
|
-
this.ownsContext = false;
|
|
7730
|
-
this.cfg = {
|
|
7731
|
-
allowedDomains: options.allowedDomains ?? [],
|
|
7732
|
-
blockedDomains: options.blockedDomains ?? [],
|
|
7733
|
-
navigationTimeoutMs: options.navigationTimeoutMs ?? BrowserDefaults.NAVIGATION_TIMEOUT_MS,
|
|
7734
|
-
actionTimeoutMs: options.actionTimeoutMs ?? BrowserDefaults.ACTION_TIMEOUT_MS,
|
|
7735
|
-
maxSnapshotNodes: options.maxSnapshotNodes ?? BrowserDefaults.MAX_SNAPSHOT_NODES,
|
|
7736
|
-
headless: options.headless ?? true
|
|
7737
|
-
};
|
|
7738
|
-
if (options.browser) {
|
|
7739
|
-
this.browser = options.browser;
|
|
7740
|
-
this.ownsBrowser = false;
|
|
7741
|
-
}
|
|
7742
|
-
if (options.context) {
|
|
7743
|
-
this.context = options.context;
|
|
7744
|
-
this.ownsContext = false;
|
|
7745
|
-
}
|
|
7746
|
-
}
|
|
7747
|
-
/**
|
|
7748
|
-
* Initialize the browser. Must be called before using any tools.
|
|
7749
|
-
*/
|
|
7750
|
-
async initialize() {
|
|
7751
|
-
if (this.page) {
|
|
7752
|
-
return;
|
|
7753
|
-
}
|
|
7754
|
-
const { chromium } = await import("playwright");
|
|
7755
|
-
if (!this.browser) {
|
|
7756
|
-
this.browser = await chromium.launch({
|
|
7757
|
-
headless: this.cfg.headless
|
|
7758
|
-
});
|
|
7759
|
-
this.ownsBrowser = true;
|
|
7760
|
-
}
|
|
7761
|
-
if (!this.context) {
|
|
7762
|
-
this.context = await this.browser.newContext();
|
|
7763
|
-
this.ownsContext = true;
|
|
7764
|
-
}
|
|
7765
|
-
this.page = await this.context.newPage();
|
|
7766
|
-
}
|
|
7767
|
-
/**
|
|
7768
|
-
* Close the browser and clean up resources.
|
|
7769
|
-
*/
|
|
7770
|
-
async close() {
|
|
7771
|
-
if (this.cdpSession) {
|
|
7772
|
-
await this.cdpSession.detach().catch(() => {
|
|
7773
|
-
});
|
|
7774
|
-
this.cdpSession = null;
|
|
7775
|
-
}
|
|
7776
|
-
if (this.page) {
|
|
7777
|
-
await this.page.close().catch(() => {
|
|
7778
|
-
});
|
|
7779
|
-
this.page = null;
|
|
7780
|
-
}
|
|
7781
|
-
if (this.ownsContext && this.context) {
|
|
7782
|
-
await this.context.close().catch(() => {
|
|
7783
|
-
});
|
|
7784
|
-
this.context = null;
|
|
7785
|
-
}
|
|
7786
|
-
if (this.ownsBrowser && this.browser) {
|
|
7787
|
-
await this.browser.close().catch(() => {
|
|
7788
|
-
});
|
|
7789
|
-
this.browser = null;
|
|
7790
|
-
}
|
|
7791
|
-
}
|
|
7792
|
-
/**
|
|
7793
|
-
* Get tool definitions for use with LLM APIs.
|
|
7794
|
-
*/
|
|
7795
|
-
getToolDefinitions() {
|
|
7796
|
-
return [
|
|
7797
|
-
{
|
|
7798
|
-
type: ToolTypes.Function,
|
|
7799
|
-
function: {
|
|
7800
|
-
name: BrowserToolNames.NAVIGATE,
|
|
7801
|
-
description: "Navigate to a URL and return the page's accessibility tree. The tree shows interactive elements (buttons, links, inputs) with their accessible names.",
|
|
7802
|
-
parameters: {
|
|
7803
|
-
type: "object",
|
|
7804
|
-
properties: {
|
|
7805
|
-
url: {
|
|
7806
|
-
type: "string",
|
|
7807
|
-
description: "The URL to navigate to (must be http/https)"
|
|
7808
|
-
},
|
|
7809
|
-
waitUntil: {
|
|
7810
|
-
type: "string",
|
|
7811
|
-
enum: ["load", "domcontentloaded", "networkidle"],
|
|
7812
|
-
description: "When to consider navigation complete. Default: domcontentloaded"
|
|
7813
|
-
}
|
|
7814
|
-
},
|
|
7815
|
-
required: ["url"]
|
|
7816
|
-
}
|
|
7817
|
-
}
|
|
7818
|
-
},
|
|
7819
|
-
{
|
|
7820
|
-
type: ToolTypes.Function,
|
|
7821
|
-
function: {
|
|
7822
|
-
name: BrowserToolNames.CLICK,
|
|
7823
|
-
description: "Click an element by its accessible name. Returns updated accessibility tree.",
|
|
7824
|
-
parameters: {
|
|
7825
|
-
type: "object",
|
|
7826
|
-
properties: {
|
|
7827
|
-
name: {
|
|
7828
|
-
type: "string",
|
|
7829
|
-
description: "The accessible name of the element (from button text, aria-label, etc.)"
|
|
7830
|
-
},
|
|
7831
|
-
role: {
|
|
7832
|
-
type: "string",
|
|
7833
|
-
enum: [
|
|
7834
|
-
"button",
|
|
7835
|
-
"link",
|
|
7836
|
-
"menuitem",
|
|
7837
|
-
"checkbox",
|
|
7838
|
-
"radio",
|
|
7839
|
-
"tab"
|
|
7840
|
-
],
|
|
7841
|
-
description: "ARIA role to match. If omitted, searches buttons, links, and menuitems."
|
|
7842
|
-
}
|
|
7843
|
-
},
|
|
7844
|
-
required: ["name"]
|
|
7845
|
-
}
|
|
7846
|
-
}
|
|
7847
|
-
},
|
|
7848
|
-
{
|
|
7849
|
-
type: ToolTypes.Function,
|
|
7850
|
-
function: {
|
|
7851
|
-
name: BrowserToolNames.TYPE,
|
|
7852
|
-
description: "Type text into an input field identified by accessible name.",
|
|
7853
|
-
parameters: {
|
|
7854
|
-
type: "object",
|
|
7855
|
-
properties: {
|
|
7856
|
-
name: {
|
|
7857
|
-
type: "string",
|
|
7858
|
-
description: "The accessible name of the input (from label, aria-label, placeholder)"
|
|
7859
|
-
},
|
|
7860
|
-
text: {
|
|
7861
|
-
type: "string",
|
|
7862
|
-
description: "The text to type"
|
|
7863
|
-
},
|
|
7864
|
-
role: {
|
|
7865
|
-
type: "string",
|
|
7866
|
-
enum: ["textbox", "searchbox", "combobox"],
|
|
7867
|
-
description: "ARIA role. Default: textbox"
|
|
7868
|
-
}
|
|
7869
|
-
},
|
|
7870
|
-
required: ["name", "text"]
|
|
7871
|
-
}
|
|
7872
|
-
}
|
|
7873
|
-
},
|
|
7874
|
-
{
|
|
7875
|
-
type: ToolTypes.Function,
|
|
7876
|
-
function: {
|
|
7877
|
-
name: BrowserToolNames.SNAPSHOT,
|
|
7878
|
-
description: "Get the current page's accessibility tree without navigating.",
|
|
7879
|
-
parameters: {
|
|
7880
|
-
type: "object",
|
|
7881
|
-
properties: {}
|
|
7882
|
-
}
|
|
7883
|
-
}
|
|
7884
|
-
},
|
|
7885
|
-
{
|
|
7886
|
-
type: ToolTypes.Function,
|
|
7887
|
-
function: {
|
|
7888
|
-
name: BrowserToolNames.SCROLL,
|
|
7889
|
-
description: "Scroll the page in a given direction.",
|
|
7890
|
-
parameters: {
|
|
7891
|
-
type: "object",
|
|
7892
|
-
properties: {
|
|
7893
|
-
direction: {
|
|
7894
|
-
type: "string",
|
|
7895
|
-
enum: ["up", "down"],
|
|
7896
|
-
description: "Scroll direction"
|
|
7897
|
-
},
|
|
7898
|
-
amount: {
|
|
7899
|
-
type: "string",
|
|
7900
|
-
enum: ["page", "half", "toTop", "toBottom"],
|
|
7901
|
-
description: "How much to scroll. Default: page"
|
|
7902
|
-
}
|
|
7903
|
-
},
|
|
7904
|
-
required: ["direction"]
|
|
7905
|
-
}
|
|
7906
|
-
}
|
|
7907
|
-
},
|
|
7908
|
-
{
|
|
7909
|
-
type: ToolTypes.Function,
|
|
7910
|
-
function: {
|
|
7911
|
-
name: BrowserToolNames.SCREENSHOT,
|
|
7912
|
-
description: "Capture a PNG screenshot of the current page. Use sparingly - prefer accessibility tree for decisions.",
|
|
7913
|
-
parameters: {
|
|
7914
|
-
type: "object",
|
|
7915
|
-
properties: {
|
|
7916
|
-
fullPage: {
|
|
7917
|
-
type: "boolean",
|
|
7918
|
-
description: "Capture full scrollable page. Default: false (viewport only)"
|
|
7919
|
-
}
|
|
7920
|
-
}
|
|
7921
|
-
}
|
|
7922
|
-
}
|
|
7923
|
-
},
|
|
7924
|
-
{
|
|
7925
|
-
type: ToolTypes.Function,
|
|
7926
|
-
function: {
|
|
7927
|
-
name: BrowserToolNames.EXTRACT,
|
|
7928
|
-
description: "Extract structured data from the page using CSS selectors.",
|
|
7929
|
-
parameters: {
|
|
7930
|
-
type: "object",
|
|
7931
|
-
properties: {
|
|
7932
|
-
selector: {
|
|
7933
|
-
type: "string",
|
|
7934
|
-
description: "CSS selector for elements to extract"
|
|
7935
|
-
},
|
|
7936
|
-
attribute: {
|
|
7937
|
-
type: "string",
|
|
7938
|
-
description: "Attribute to extract (textContent, href, src, etc.). Default: textContent"
|
|
7939
|
-
},
|
|
7940
|
-
multiple: {
|
|
7941
|
-
type: "boolean",
|
|
7942
|
-
description: "Return all matches as JSON array. Default: false (first match only)"
|
|
7943
|
-
}
|
|
7944
|
-
},
|
|
7945
|
-
required: ["selector"]
|
|
7946
|
-
}
|
|
7947
|
-
}
|
|
7948
|
-
}
|
|
7949
|
-
];
|
|
7950
|
-
}
|
|
7951
|
-
/**
|
|
7952
|
-
* Register tool handlers into an existing registry.
|
|
7953
|
-
*/
|
|
7954
|
-
registerInto(registry) {
|
|
7955
|
-
registry.register(BrowserToolNames.NAVIGATE, this.navigate.bind(this));
|
|
7956
|
-
registry.register(BrowserToolNames.CLICK, this.click.bind(this));
|
|
7957
|
-
registry.register(BrowserToolNames.TYPE, this.type.bind(this));
|
|
7958
|
-
registry.register(BrowserToolNames.SNAPSHOT, this.snapshot.bind(this));
|
|
7959
|
-
registry.register(BrowserToolNames.SCROLL, this.scroll.bind(this));
|
|
7960
|
-
registry.register(BrowserToolNames.SCREENSHOT, this.screenshot.bind(this));
|
|
7961
|
-
registry.register(BrowserToolNames.EXTRACT, this.extract.bind(this));
|
|
7962
|
-
return registry;
|
|
7963
|
-
}
|
|
7964
|
-
/**
|
|
7965
|
-
* Create a new registry with just this pack's tools.
|
|
7966
|
-
*/
|
|
7967
|
-
toRegistry() {
|
|
7968
|
-
return this.registerInto(new ToolRegistry());
|
|
7969
|
-
}
|
|
7970
|
-
// ========================================================================
|
|
7971
|
-
// Private: Helpers
|
|
7972
|
-
// ========================================================================
|
|
7973
|
-
ensureInitialized() {
|
|
7974
|
-
if (!this.page) {
|
|
7975
|
-
throw new Error(
|
|
7976
|
-
"BrowserToolPack not initialized. Call initialize() first."
|
|
7977
|
-
);
|
|
7978
|
-
}
|
|
7979
|
-
}
|
|
7980
|
-
parseArgs(call, required) {
|
|
7981
|
-
const func = call.function;
|
|
7982
|
-
if (!func) {
|
|
7983
|
-
throw new ToolArgumentError({
|
|
7984
|
-
message: "tool call missing function",
|
|
7985
|
-
toolCallId: call.id,
|
|
7986
|
-
toolName: "",
|
|
7987
|
-
rawArguments: ""
|
|
7988
|
-
});
|
|
7989
|
-
}
|
|
7990
|
-
const rawArgs = func.arguments || "{}";
|
|
7991
|
-
let parsed;
|
|
7992
|
-
try {
|
|
7993
|
-
parsed = JSON.parse(rawArgs);
|
|
7994
|
-
} catch (err) {
|
|
7995
|
-
throw new ToolArgumentError({
|
|
7996
|
-
message: `invalid JSON arguments: ${err.message}`,
|
|
7997
|
-
toolCallId: call.id,
|
|
7998
|
-
toolName: func.name,
|
|
7999
|
-
rawArguments: rawArgs
|
|
8000
|
-
});
|
|
8001
|
-
}
|
|
8002
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
8003
|
-
throw new ToolArgumentError({
|
|
8004
|
-
message: "arguments must be an object",
|
|
8005
|
-
toolCallId: call.id,
|
|
8006
|
-
toolName: func.name,
|
|
8007
|
-
rawArguments: rawArgs
|
|
8008
|
-
});
|
|
8009
|
-
}
|
|
8010
|
-
const args = parsed;
|
|
8011
|
-
for (const key of required) {
|
|
8012
|
-
const value = args[key];
|
|
8013
|
-
if (value === void 0 || value === null || value === "") {
|
|
8014
|
-
throw new ToolArgumentError({
|
|
8015
|
-
message: `${key} is required`,
|
|
8016
|
-
toolCallId: call.id,
|
|
8017
|
-
toolName: func.name,
|
|
8018
|
-
rawArguments: rawArgs
|
|
8019
|
-
});
|
|
8020
|
-
}
|
|
8021
|
-
}
|
|
8022
|
-
return args;
|
|
8023
|
-
}
|
|
8024
|
-
validateUrl(url, call) {
|
|
8025
|
-
let parsed;
|
|
8026
|
-
try {
|
|
8027
|
-
parsed = new URL(url);
|
|
8028
|
-
} catch {
|
|
8029
|
-
throw new ToolArgumentError({
|
|
8030
|
-
message: `Invalid URL: ${url}`,
|
|
8031
|
-
toolCallId: call.id,
|
|
8032
|
-
toolName: call.function?.name ?? "",
|
|
8033
|
-
rawArguments: call.function?.arguments ?? ""
|
|
8034
|
-
});
|
|
8035
|
-
}
|
|
8036
|
-
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
8037
|
-
throw new ToolArgumentError({
|
|
8038
|
-
message: `Invalid protocol: ${parsed.protocol}. Only http/https allowed.`,
|
|
8039
|
-
toolCallId: call.id,
|
|
8040
|
-
toolName: call.function?.name ?? "",
|
|
8041
|
-
rawArguments: call.function?.arguments ?? ""
|
|
8042
|
-
});
|
|
8043
|
-
}
|
|
8044
|
-
const domain = parsed.hostname;
|
|
8045
|
-
if (this.cfg.blockedDomains.some((d) => domain.endsWith(d))) {
|
|
8046
|
-
throw new ToolArgumentError({
|
|
8047
|
-
message: `Domain blocked: ${domain}`,
|
|
8048
|
-
toolCallId: call.id,
|
|
8049
|
-
toolName: call.function?.name ?? "",
|
|
8050
|
-
rawArguments: call.function?.arguments ?? ""
|
|
8051
|
-
});
|
|
8052
|
-
}
|
|
8053
|
-
if (this.cfg.allowedDomains.length > 0) {
|
|
8054
|
-
if (!this.cfg.allowedDomains.some((d) => domain.endsWith(d))) {
|
|
8055
|
-
throw new ToolArgumentError({
|
|
8056
|
-
message: `Domain not in allowlist: ${domain}`,
|
|
8057
|
-
toolCallId: call.id,
|
|
8058
|
-
toolName: call.function?.name ?? "",
|
|
8059
|
-
rawArguments: call.function?.arguments ?? ""
|
|
8060
|
-
});
|
|
8061
|
-
}
|
|
8062
|
-
}
|
|
8063
|
-
}
|
|
8064
|
-
/**
|
|
8065
|
-
* Validates the current page URL against allowlist/blocklist.
|
|
8066
|
-
* Called after navigation and before any action to catch redirects
|
|
8067
|
-
* and in-session navigation to blocked domains.
|
|
8068
|
-
*/
|
|
8069
|
-
ensureCurrentUrlAllowed() {
|
|
8070
|
-
if (!this.page) return;
|
|
8071
|
-
const currentUrl = this.page.url();
|
|
8072
|
-
if (currentUrl === "about:blank") return;
|
|
8073
|
-
let parsed;
|
|
8074
|
-
try {
|
|
8075
|
-
parsed = new URL(currentUrl);
|
|
8076
|
-
} catch {
|
|
8077
|
-
throw new Error(`Current page has invalid URL: ${currentUrl}`);
|
|
8078
|
-
}
|
|
8079
|
-
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
8080
|
-
throw new Error(
|
|
8081
|
-
`Current page protocol not allowed: ${parsed.protocol}. Only http/https allowed.`
|
|
8082
|
-
);
|
|
8083
|
-
}
|
|
8084
|
-
const domain = parsed.hostname;
|
|
8085
|
-
if (this.cfg.blockedDomains.some((d) => domain.endsWith(d))) {
|
|
8086
|
-
throw new Error(`Current page domain is blocked: ${domain}`);
|
|
8087
|
-
}
|
|
8088
|
-
if (this.cfg.allowedDomains.length > 0) {
|
|
8089
|
-
if (!this.cfg.allowedDomains.some((d) => domain.endsWith(d))) {
|
|
8090
|
-
throw new Error(`Current page domain not in allowlist: ${domain}`);
|
|
8091
|
-
}
|
|
8092
|
-
}
|
|
8093
|
-
}
|
|
8094
|
-
async getAccessibilityTree() {
|
|
8095
|
-
this.ensureInitialized();
|
|
8096
|
-
if (!this.cdpSession) {
|
|
8097
|
-
this.cdpSession = await this.page.context().newCDPSession(this.page);
|
|
8098
|
-
await this.cdpSession.send("Accessibility.enable");
|
|
8099
|
-
}
|
|
8100
|
-
const response = await this.cdpSession.send(
|
|
8101
|
-
"Accessibility.getFullAXTree"
|
|
8102
|
-
);
|
|
8103
|
-
return response.nodes;
|
|
8104
|
-
}
|
|
8105
|
-
formatAXTree(nodes) {
|
|
8106
|
-
const lines = [];
|
|
8107
|
-
let count = 0;
|
|
8108
|
-
for (const node of nodes) {
|
|
8109
|
-
if (count >= this.cfg.maxSnapshotNodes) {
|
|
8110
|
-
lines.push(`[truncated at ${this.cfg.maxSnapshotNodes} nodes]`);
|
|
8111
|
-
break;
|
|
8112
|
-
}
|
|
8113
|
-
if (node.ignored) {
|
|
8114
|
-
continue;
|
|
8115
|
-
}
|
|
8116
|
-
const role = node.role?.value || "unknown";
|
|
8117
|
-
const name = node.name?.value || "";
|
|
8118
|
-
if (!name && ["generic", "none", "text"].includes(role)) {
|
|
8119
|
-
continue;
|
|
8120
|
-
}
|
|
8121
|
-
const states = [];
|
|
8122
|
-
if (node.properties) {
|
|
8123
|
-
for (const prop of node.properties) {
|
|
8124
|
-
if (prop.value?.value === true) {
|
|
8125
|
-
const stateName = prop.name;
|
|
8126
|
-
if (["focused", "checked", "disabled", "expanded", "selected"].includes(
|
|
8127
|
-
stateName
|
|
8128
|
-
)) {
|
|
8129
|
-
states.push(stateName);
|
|
8130
|
-
}
|
|
8131
|
-
}
|
|
8132
|
-
}
|
|
8133
|
-
}
|
|
8134
|
-
const stateStr = states.length ? " " + states.join(" ") : "";
|
|
8135
|
-
const nameStr = name ? ` "${name}"` : "";
|
|
8136
|
-
lines.push(`[${role}${nameStr}${stateStr}]`);
|
|
8137
|
-
count++;
|
|
8138
|
-
}
|
|
8139
|
-
return lines.join("\n");
|
|
8140
|
-
}
|
|
8141
|
-
// ========================================================================
|
|
8142
|
-
// Private: Tool Handlers
|
|
8143
|
-
// ========================================================================
|
|
8144
|
-
async navigate(_args, call) {
|
|
8145
|
-
const args = this.parseArgs(call, ["url"]);
|
|
8146
|
-
this.validateUrl(args.url, call);
|
|
8147
|
-
this.ensureInitialized();
|
|
8148
|
-
const waitUntil = args.waitUntil ?? "domcontentloaded";
|
|
8149
|
-
await this.page.goto(args.url, {
|
|
8150
|
-
timeout: this.cfg.navigationTimeoutMs,
|
|
8151
|
-
waitUntil
|
|
8152
|
-
});
|
|
8153
|
-
this.ensureCurrentUrlAllowed();
|
|
8154
|
-
const tree = await this.getAccessibilityTree();
|
|
8155
|
-
return this.formatAXTree(tree);
|
|
8156
|
-
}
|
|
8157
|
-
async click(_args, call) {
|
|
8158
|
-
const args = this.parseArgs(call, ["name"]);
|
|
8159
|
-
this.ensureInitialized();
|
|
8160
|
-
this.ensureCurrentUrlAllowed();
|
|
8161
|
-
let locator;
|
|
8162
|
-
if (args.role) {
|
|
8163
|
-
locator = this.page.getByRole(args.role, {
|
|
8164
|
-
name: args.name
|
|
8165
|
-
});
|
|
8166
|
-
} else {
|
|
8167
|
-
locator = this.page.getByRole("button", { name: args.name }).or(this.page.getByRole("link", { name: args.name })).or(this.page.getByRole("menuitem", { name: args.name }));
|
|
8168
|
-
}
|
|
8169
|
-
await locator.click({ timeout: this.cfg.actionTimeoutMs });
|
|
8170
|
-
const tree = await this.getAccessibilityTree();
|
|
8171
|
-
return this.formatAXTree(tree);
|
|
8172
|
-
}
|
|
8173
|
-
async type(_args, call) {
|
|
8174
|
-
const args = this.parseArgs(call, ["name", "text"]);
|
|
8175
|
-
this.ensureInitialized();
|
|
8176
|
-
this.ensureCurrentUrlAllowed();
|
|
8177
|
-
const role = args.role ?? "textbox";
|
|
8178
|
-
const locator = this.page.getByRole(role, { name: args.name });
|
|
8179
|
-
await locator.fill(args.text, { timeout: this.cfg.actionTimeoutMs });
|
|
8180
|
-
return `Typed "${args.text}" into ${role} "${args.name}"`;
|
|
8181
|
-
}
|
|
8182
|
-
async snapshot(_args, _call) {
|
|
8183
|
-
this.ensureInitialized();
|
|
8184
|
-
this.ensureCurrentUrlAllowed();
|
|
8185
|
-
const tree = await this.getAccessibilityTree();
|
|
8186
|
-
return this.formatAXTree(tree);
|
|
8187
|
-
}
|
|
8188
|
-
async scroll(_args, call) {
|
|
8189
|
-
const args = this.parseArgs(call, ["direction"]);
|
|
8190
|
-
this.ensureInitialized();
|
|
8191
|
-
this.ensureCurrentUrlAllowed();
|
|
8192
|
-
const amount = args.amount ?? "page";
|
|
8193
|
-
if (amount === "toTop") {
|
|
8194
|
-
await this.page.evaluate(() => window.scrollTo(0, 0));
|
|
8195
|
-
} else if (amount === "toBottom") {
|
|
8196
|
-
await this.page.evaluate(
|
|
8197
|
-
() => window.scrollTo(0, document.body.scrollHeight)
|
|
8198
|
-
);
|
|
8199
|
-
} else {
|
|
8200
|
-
const viewport = this.page.viewportSize();
|
|
8201
|
-
const height = viewport?.height ?? 800;
|
|
8202
|
-
const scrollAmount = amount === "half" ? height / 2 : height;
|
|
8203
|
-
const delta = args.direction === "down" ? scrollAmount : -scrollAmount;
|
|
8204
|
-
await this.page.evaluate((d) => window.scrollBy(0, d), delta);
|
|
8205
|
-
}
|
|
8206
|
-
const tree = await this.getAccessibilityTree();
|
|
8207
|
-
return this.formatAXTree(tree);
|
|
8208
|
-
}
|
|
8209
|
-
async screenshot(_args, call) {
|
|
8210
|
-
const args = this.parseArgs(call, []);
|
|
8211
|
-
this.ensureInitialized();
|
|
8212
|
-
this.ensureCurrentUrlAllowed();
|
|
8213
|
-
const buffer = await this.page.screenshot({
|
|
8214
|
-
fullPage: args.fullPage ?? false,
|
|
8215
|
-
type: "png"
|
|
8216
|
-
});
|
|
8217
|
-
if (buffer.length > BrowserDefaults.MAX_SCREENSHOT_BYTES) {
|
|
8218
|
-
throw new Error(
|
|
8219
|
-
`Screenshot size (${buffer.length} bytes) exceeds maximum allowed (${BrowserDefaults.MAX_SCREENSHOT_BYTES} bytes). Try capturing viewport only.`
|
|
8220
|
-
);
|
|
8221
|
-
}
|
|
8222
|
-
const base64 = buffer.toString("base64");
|
|
8223
|
-
return `data:image/png;base64,${base64}`;
|
|
8224
|
-
}
|
|
8225
|
-
async extract(_args, call) {
|
|
8226
|
-
const args = this.parseArgs(call, ["selector"]);
|
|
8227
|
-
this.ensureInitialized();
|
|
8228
|
-
this.ensureCurrentUrlAllowed();
|
|
8229
|
-
const attribute = args.attribute ?? "textContent";
|
|
8230
|
-
const multiple = args.multiple ?? false;
|
|
8231
|
-
if (multiple) {
|
|
8232
|
-
const elements = this.page.locator(args.selector);
|
|
8233
|
-
const count = await elements.count();
|
|
8234
|
-
const results = [];
|
|
8235
|
-
for (let i = 0; i < count; i++) {
|
|
8236
|
-
const el = elements.nth(i);
|
|
8237
|
-
let value;
|
|
8238
|
-
if (attribute === "textContent") {
|
|
8239
|
-
value = await el.textContent();
|
|
8240
|
-
} else {
|
|
8241
|
-
value = await el.getAttribute(attribute);
|
|
8242
|
-
}
|
|
8243
|
-
if (value !== null) {
|
|
8244
|
-
results.push(value.trim());
|
|
8245
|
-
}
|
|
8246
|
-
}
|
|
8247
|
-
return JSON.stringify(results);
|
|
8248
|
-
} else {
|
|
8249
|
-
const el = this.page.locator(args.selector).first();
|
|
8250
|
-
let value;
|
|
8251
|
-
if (attribute === "textContent") {
|
|
8252
|
-
value = await el.textContent();
|
|
8253
|
-
} else {
|
|
8254
|
-
value = await el.getAttribute(attribute);
|
|
8255
|
-
}
|
|
8256
|
-
return value?.trim() ?? "";
|
|
8257
|
-
}
|
|
8258
|
-
}
|
|
8259
|
-
};
|
|
8260
|
-
function createBrowserToolPack(options = {}) {
|
|
8261
|
-
return new BrowserToolPack(options);
|
|
8262
|
-
}
|
|
8263
|
-
function createBrowserTools(options = {}) {
|
|
8264
|
-
const pack = new BrowserToolPack(options);
|
|
8265
|
-
return { pack, registry: pack.toRegistry() };
|
|
8266
|
-
}
|
|
8267
|
-
|
|
8268
7333
|
// src/tools_runner.ts
|
|
8269
7334
|
var ToolRunner = class {
|
|
8270
7335
|
constructor(options) {
|
|
@@ -8732,9 +7797,6 @@ var ModelRelay = class _ModelRelay {
|
|
|
8732
7797
|
trace: cfg.trace
|
|
8733
7798
|
});
|
|
8734
7799
|
this.images = new ImagesClient(this.http, auth);
|
|
8735
|
-
this.customers = new CustomersClient(this.http, { apiKey, accessToken, tokenProvider });
|
|
8736
|
-
this.tiers = new TiersClient(this.http, { apiKey });
|
|
8737
|
-
this.models = new ModelsClient(this.http);
|
|
8738
7800
|
this.sessions = new SessionsClient(this, this.http, auth);
|
|
8739
7801
|
}
|
|
8740
7802
|
forCustomer(customerId) {
|
|
@@ -8750,9 +7812,6 @@ export {
|
|
|
8750
7812
|
AuthClient,
|
|
8751
7813
|
BillingProviders,
|
|
8752
7814
|
BindingTargetError,
|
|
8753
|
-
BrowserDefaults,
|
|
8754
|
-
BrowserToolNames,
|
|
8755
|
-
BrowserToolPack,
|
|
8756
7815
|
Chain,
|
|
8757
7816
|
ChainBuilder,
|
|
8758
7817
|
ConfigError,
|
|
@@ -8760,7 +7819,6 @@ export {
|
|
|
8760
7819
|
CustomerResponsesClient,
|
|
8761
7820
|
CustomerScopedModelRelay,
|
|
8762
7821
|
CustomerTokenProvider,
|
|
8763
|
-
CustomersClient,
|
|
8764
7822
|
DEFAULT_BASE_URL,
|
|
8765
7823
|
DEFAULT_CLIENT_HEADER,
|
|
8766
7824
|
DEFAULT_CONNECT_TIMEOUT_MS,
|
|
@@ -8796,7 +7854,6 @@ export {
|
|
|
8796
7854
|
MessageRoles,
|
|
8797
7855
|
ModelRelay,
|
|
8798
7856
|
ModelRelayError,
|
|
8799
|
-
ModelsClient,
|
|
8800
7857
|
OIDCExchangeTokenProvider,
|
|
8801
7858
|
OutputFormatTypes,
|
|
8802
7859
|
OutputItemTypes,
|
|
@@ -8816,7 +7873,6 @@ export {
|
|
|
8816
7873
|
StructuredExhaustedError,
|
|
8817
7874
|
StructuredJSONStream,
|
|
8818
7875
|
SubscriptionStatuses,
|
|
8819
|
-
TiersClient,
|
|
8820
7876
|
ToolArgsError,
|
|
8821
7877
|
ToolArgumentError,
|
|
8822
7878
|
ToolCallAccumulator,
|
|
@@ -8845,8 +7901,6 @@ export {
|
|
|
8845
7901
|
createAccessTokenAuth,
|
|
8846
7902
|
createApiKeyAuth,
|
|
8847
7903
|
createAssistantMessage,
|
|
8848
|
-
createBrowserToolPack,
|
|
8849
|
-
createBrowserTools,
|
|
8850
7904
|
createFunctionCall,
|
|
8851
7905
|
createFunctionTool,
|
|
8852
7906
|
createFunctionToolFromSchema,
|