@giselles-ai/browser-tool 0.1.17 → 0.1.19
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/mcp-server/index.js +15 -1
- package/dist/relay/index.d.ts +89 -1
- package/dist/relay/index.js +136 -0
- package/package.json +1 -1
package/dist/mcp-server/index.js
CHANGED
|
@@ -113,6 +113,13 @@ function requiredEnv(name) {
|
|
|
113
113
|
function trimTrailingSlash(input) {
|
|
114
114
|
return input.replace(/\/+$/, "");
|
|
115
115
|
}
|
|
116
|
+
function safeJsonStringify(value) {
|
|
117
|
+
try {
|
|
118
|
+
return JSON.stringify(value);
|
|
119
|
+
} catch {
|
|
120
|
+
return String(value);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
116
123
|
var RelayClient = class {
|
|
117
124
|
url;
|
|
118
125
|
sessionId;
|
|
@@ -194,7 +201,14 @@ var RelayClient = class {
|
|
|
194
201
|
}
|
|
195
202
|
const success = dispatchSuccessSchema.safeParse(body);
|
|
196
203
|
if (!success.success) {
|
|
197
|
-
throw new Error(
|
|
204
|
+
throw new Error(
|
|
205
|
+
[
|
|
206
|
+
"Relay dispatch returned an unexpected payload.",
|
|
207
|
+
`status=${response.status}`,
|
|
208
|
+
`url=${this.url}`,
|
|
209
|
+
`body=${safeJsonStringify(body)}`
|
|
210
|
+
].join(" ")
|
|
211
|
+
);
|
|
198
212
|
}
|
|
199
213
|
if (!response.ok) {
|
|
200
214
|
throw new Error(`Relay dispatch failed with HTTP ${response.status}.`);
|
package/dist/relay/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Redis from 'ioredis';
|
|
2
|
+
import { z } from 'zod';
|
|
2
3
|
|
|
3
4
|
declare function createRelayHandler(): {
|
|
4
5
|
GET: (request: Request) => Promise<Response>;
|
|
@@ -7,6 +8,79 @@ declare function createRelayHandler(): {
|
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
type RelayErrorCode = "UNAUTHORIZED" | "NO_BROWSER" | "TIMEOUT" | "INVALID_RESPONSE" | "NOT_FOUND" | "INTERNAL";
|
|
11
|
+
declare const relayRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
12
|
+
type: z.ZodLiteral<"snapshot_request">;
|
|
13
|
+
requestId: z.ZodString;
|
|
14
|
+
instruction: z.ZodString;
|
|
15
|
+
document: z.ZodOptional<z.ZodString>;
|
|
16
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
17
|
+
type: z.ZodLiteral<"execute_request">;
|
|
18
|
+
requestId: z.ZodString;
|
|
19
|
+
actions: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
20
|
+
action: z.ZodLiteral<"fill">;
|
|
21
|
+
fieldId: z.ZodString;
|
|
22
|
+
value: z.ZodString;
|
|
23
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
24
|
+
action: z.ZodLiteral<"click">;
|
|
25
|
+
fieldId: z.ZodString;
|
|
26
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
27
|
+
action: z.ZodLiteral<"select">;
|
|
28
|
+
fieldId: z.ZodString;
|
|
29
|
+
value: z.ZodString;
|
|
30
|
+
}, z.core.$strip>], "action">>;
|
|
31
|
+
fields: z.ZodArray<z.ZodObject<{
|
|
32
|
+
fieldId: z.ZodString;
|
|
33
|
+
selector: z.ZodString;
|
|
34
|
+
kind: z.ZodEnum<{
|
|
35
|
+
text: "text";
|
|
36
|
+
textarea: "textarea";
|
|
37
|
+
select: "select";
|
|
38
|
+
checkbox: "checkbox";
|
|
39
|
+
radio: "radio";
|
|
40
|
+
}>;
|
|
41
|
+
label: z.ZodString;
|
|
42
|
+
name: z.ZodOptional<z.ZodString>;
|
|
43
|
+
required: z.ZodBoolean;
|
|
44
|
+
placeholder: z.ZodOptional<z.ZodString>;
|
|
45
|
+
currentValue: z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>;
|
|
46
|
+
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
47
|
+
}, z.core.$strip>>;
|
|
48
|
+
}, z.core.$strip>], "type">;
|
|
49
|
+
declare const relayResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
50
|
+
type: z.ZodLiteral<"snapshot_response">;
|
|
51
|
+
requestId: z.ZodString;
|
|
52
|
+
fields: z.ZodArray<z.ZodObject<{
|
|
53
|
+
fieldId: z.ZodString;
|
|
54
|
+
selector: z.ZodString;
|
|
55
|
+
kind: z.ZodEnum<{
|
|
56
|
+
text: "text";
|
|
57
|
+
textarea: "textarea";
|
|
58
|
+
select: "select";
|
|
59
|
+
checkbox: "checkbox";
|
|
60
|
+
radio: "radio";
|
|
61
|
+
}>;
|
|
62
|
+
label: z.ZodString;
|
|
63
|
+
name: z.ZodOptional<z.ZodString>;
|
|
64
|
+
required: z.ZodBoolean;
|
|
65
|
+
placeholder: z.ZodOptional<z.ZodString>;
|
|
66
|
+
currentValue: z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>;
|
|
67
|
+
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
68
|
+
}, z.core.$strip>>;
|
|
69
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
70
|
+
type: z.ZodLiteral<"execute_response">;
|
|
71
|
+
requestId: z.ZodString;
|
|
72
|
+
report: z.ZodObject<{
|
|
73
|
+
applied: z.ZodNumber;
|
|
74
|
+
skipped: z.ZodNumber;
|
|
75
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
76
|
+
}, z.core.$strip>;
|
|
77
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
78
|
+
type: z.ZodLiteral<"error_response">;
|
|
79
|
+
requestId: z.ZodString;
|
|
80
|
+
message: z.ZodString;
|
|
81
|
+
}, z.core.$strip>], "type">;
|
|
82
|
+
type RelayRequest = z.infer<typeof relayRequestSchema>;
|
|
83
|
+
type RelayResponse = z.infer<typeof relayResponseSchema>;
|
|
10
84
|
|
|
11
85
|
declare global {
|
|
12
86
|
var __browserToolRelayRedis: Redis | undefined;
|
|
@@ -23,4 +97,18 @@ declare function createRelaySession(): Promise<{
|
|
|
23
97
|
}>;
|
|
24
98
|
declare function toRelayError(error: unknown): RelayStoreError;
|
|
25
99
|
|
|
26
|
-
|
|
100
|
+
type RelayRequestSubscription = {
|
|
101
|
+
nextRequest(): Promise<RelayRequest>;
|
|
102
|
+
close(): Promise<void>;
|
|
103
|
+
};
|
|
104
|
+
declare function createRelayRequestSubscription(input: {
|
|
105
|
+
sessionId: string;
|
|
106
|
+
token: string;
|
|
107
|
+
}): Promise<RelayRequestSubscription>;
|
|
108
|
+
declare function sendRelayResponse(input: {
|
|
109
|
+
sessionId: string;
|
|
110
|
+
token: string;
|
|
111
|
+
response: RelayResponse;
|
|
112
|
+
}): Promise<void>;
|
|
113
|
+
|
|
114
|
+
export { type RelayRequestSubscription, createRelayHandler, createRelayRequestSubscription, createRelaySession, sendRelayResponse, toRelayError };
|
package/dist/relay/index.js
CHANGED
|
@@ -581,6 +581,44 @@ var postBodySchema = z2.discriminatedUnion("type", [
|
|
|
581
581
|
dispatchSchema,
|
|
582
582
|
respondSchema
|
|
583
583
|
]);
|
|
584
|
+
function summarizeRelayRequestForLog(request) {
|
|
585
|
+
if (request.type === "snapshot_request") {
|
|
586
|
+
return {
|
|
587
|
+
type: request.type,
|
|
588
|
+
requestId: request.requestId,
|
|
589
|
+
instruction: request.instruction
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
return {
|
|
593
|
+
type: request.type,
|
|
594
|
+
requestId: request.requestId,
|
|
595
|
+
actionCount: request.actions.length,
|
|
596
|
+
fieldCount: request.fields.length
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
function summarizeRelayResponseForLog(response) {
|
|
600
|
+
if (response.type === "snapshot_response") {
|
|
601
|
+
return {
|
|
602
|
+
type: response.type,
|
|
603
|
+
requestId: response.requestId,
|
|
604
|
+
fieldCount: response.fields.length
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
if (response.type === "execute_response") {
|
|
608
|
+
return {
|
|
609
|
+
type: response.type,
|
|
610
|
+
requestId: response.requestId,
|
|
611
|
+
applied: response.report.applied,
|
|
612
|
+
skipped: response.report.skipped,
|
|
613
|
+
warningCount: response.report.warnings.length
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
type: response.type,
|
|
618
|
+
requestId: response.requestId,
|
|
619
|
+
message: response.message
|
|
620
|
+
};
|
|
621
|
+
}
|
|
584
622
|
function createRelayEventsRoute(request) {
|
|
585
623
|
const cors = corsHeaders(request);
|
|
586
624
|
const url = new URL(request.url);
|
|
@@ -751,6 +789,10 @@ async function createRelayPostRoute(request) {
|
|
|
751
789
|
);
|
|
752
790
|
}
|
|
753
791
|
if (parsed.data.type === "relay.dispatch") {
|
|
792
|
+
console.info(`${LOG_PREFIX} post.dispatch`, {
|
|
793
|
+
sessionId: parsed.data.sessionId,
|
|
794
|
+
request: summarizeRelayRequestForLog(parsed.data.request)
|
|
795
|
+
});
|
|
754
796
|
try {
|
|
755
797
|
const response = await dispatchRelayRequest({
|
|
756
798
|
sessionId: parsed.data.sessionId,
|
|
@@ -758,24 +800,46 @@ async function createRelayPostRoute(request) {
|
|
|
758
800
|
request: parsed.data.request,
|
|
759
801
|
timeoutMs: parsed.data.timeoutMs
|
|
760
802
|
});
|
|
803
|
+
console.info(`${LOG_PREFIX} post.dispatch.ok`, {
|
|
804
|
+
sessionId: parsed.data.sessionId,
|
|
805
|
+
response: summarizeRelayResponseForLog(response)
|
|
806
|
+
});
|
|
761
807
|
return Response.json({ ok: true, response }, { headers: cors });
|
|
762
808
|
} catch (error) {
|
|
763
809
|
const relayError = toRelayError(error);
|
|
810
|
+
console.error(`${LOG_PREFIX} post.dispatch.error`, {
|
|
811
|
+
sessionId: parsed.data.sessionId,
|
|
812
|
+
errorCode: relayError.code,
|
|
813
|
+
message: relayError.message
|
|
814
|
+
});
|
|
764
815
|
return Response.json(
|
|
765
816
|
{ ok: false, errorCode: relayError.code, message: relayError.message },
|
|
766
817
|
{ status: relayError.status, headers: cors }
|
|
767
818
|
);
|
|
768
819
|
}
|
|
769
820
|
}
|
|
821
|
+
console.info(`${LOG_PREFIX} post.respond`, {
|
|
822
|
+
sessionId: parsed.data.sessionId,
|
|
823
|
+
response: summarizeRelayResponseForLog(parsed.data.response)
|
|
824
|
+
});
|
|
770
825
|
try {
|
|
771
826
|
await resolveRelayResponse({
|
|
772
827
|
sessionId: parsed.data.sessionId,
|
|
773
828
|
token: parsed.data.token,
|
|
774
829
|
response: parsed.data.response
|
|
775
830
|
});
|
|
831
|
+
console.info(`${LOG_PREFIX} post.respond.ok`, {
|
|
832
|
+
sessionId: parsed.data.sessionId,
|
|
833
|
+
requestId: parsed.data.response.requestId
|
|
834
|
+
});
|
|
776
835
|
return Response.json({ ok: true }, { headers: cors });
|
|
777
836
|
} catch (error) {
|
|
778
837
|
const relayError = toRelayError(error);
|
|
838
|
+
console.error(`${LOG_PREFIX} post.respond.error`, {
|
|
839
|
+
sessionId: parsed.data.sessionId,
|
|
840
|
+
errorCode: relayError.code,
|
|
841
|
+
message: relayError.message
|
|
842
|
+
});
|
|
779
843
|
return Response.json(
|
|
780
844
|
{ ok: false, errorCode: relayError.code, message: relayError.message },
|
|
781
845
|
{ status: relayError.status, headers: cors }
|
|
@@ -789,8 +853,80 @@ function createRelayHandler() {
|
|
|
789
853
|
OPTIONS: (request) => new Response(null, { status: 204, headers: corsHeaders(request) })
|
|
790
854
|
};
|
|
791
855
|
}
|
|
856
|
+
|
|
857
|
+
// src/relay/request-subscription.ts
|
|
858
|
+
async function createRelayRequestSubscription(input) {
|
|
859
|
+
await assertRelaySession(input.sessionId, input.token);
|
|
860
|
+
const subscriber = createRelaySubscriber();
|
|
861
|
+
const channel = relayRequestChannel(input.sessionId);
|
|
862
|
+
let keepaliveId = null;
|
|
863
|
+
try {
|
|
864
|
+
await subscriber.subscribe(channel);
|
|
865
|
+
await markBrowserConnected(input.sessionId, input.token);
|
|
866
|
+
keepaliveId = setInterval(() => {
|
|
867
|
+
void touchBrowserConnected(input.sessionId).catch(() => void 0);
|
|
868
|
+
}, RELAY_SSE_KEEPALIVE_INTERVAL_MS);
|
|
869
|
+
} catch (error) {
|
|
870
|
+
await subscriber.unsubscribe(channel).catch(() => void 0);
|
|
871
|
+
await subscriber.quit().catch(() => {
|
|
872
|
+
subscriber.disconnect();
|
|
873
|
+
});
|
|
874
|
+
throw error;
|
|
875
|
+
}
|
|
876
|
+
const nextRequest = () => {
|
|
877
|
+
return new Promise((resolve, reject) => {
|
|
878
|
+
const onMessage = (_channel, message) => {
|
|
879
|
+
if (_channel !== channel) {
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
let parsed = null;
|
|
883
|
+
try {
|
|
884
|
+
const raw = JSON.parse(message);
|
|
885
|
+
const safe = relayRequestSchema.safeParse(raw);
|
|
886
|
+
if (!safe.success) {
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
parsed = safe.data;
|
|
890
|
+
} catch {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
cleanup();
|
|
894
|
+
resolve(parsed);
|
|
895
|
+
};
|
|
896
|
+
const onError = (error) => {
|
|
897
|
+
cleanup();
|
|
898
|
+
reject(error);
|
|
899
|
+
};
|
|
900
|
+
const cleanup = () => {
|
|
901
|
+
subscriber.off("message", onMessage);
|
|
902
|
+
subscriber.off("error", onError);
|
|
903
|
+
};
|
|
904
|
+
subscriber.on("message", onMessage);
|
|
905
|
+
subscriber.on("error", onError);
|
|
906
|
+
});
|
|
907
|
+
};
|
|
908
|
+
const close = async () => {
|
|
909
|
+
if (keepaliveId) {
|
|
910
|
+
clearInterval(keepaliveId);
|
|
911
|
+
keepaliveId = null;
|
|
912
|
+
}
|
|
913
|
+
await subscriber.unsubscribe(channel).catch(() => void 0);
|
|
914
|
+
await subscriber.quit().catch(() => {
|
|
915
|
+
subscriber.disconnect();
|
|
916
|
+
});
|
|
917
|
+
};
|
|
918
|
+
return {
|
|
919
|
+
nextRequest,
|
|
920
|
+
close
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
async function sendRelayResponse(input) {
|
|
924
|
+
await resolveRelayResponse(input);
|
|
925
|
+
}
|
|
792
926
|
export {
|
|
793
927
|
createRelayHandler,
|
|
928
|
+
createRelayRequestSubscription,
|
|
794
929
|
createRelaySession,
|
|
930
|
+
sendRelayResponse,
|
|
795
931
|
toRelayError
|
|
796
932
|
};
|