imprint-mcp 0.2.1 → 0.3.1
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/README.md +193 -189
- package/examples/discoverandgo/README.md +1 -1
- package/examples/echo/README.md +1 -1
- package/examples/google-flights/README.md +28 -0
- package/examples/google-flights/_shared/batchexecute.ts +63 -0
- package/examples/google-flights/_shared/flights_request.ts +95 -0
- package/examples/google-flights/_shared/package.json +9 -0
- package/examples/google-flights/get_flight_booking_details/index.ts +159 -0
- package/examples/google-flights/get_flight_booking_details/package.json +9 -0
- package/examples/google-flights/get_flight_booking_details/parser.ts +182 -0
- package/examples/google-flights/get_flight_booking_details/playbook.yaml +138 -0
- package/examples/google-flights/get_flight_booking_details/request-transform.ts +86 -0
- package/examples/google-flights/get_flight_booking_details/workflow.json +98 -0
- package/examples/google-flights/get_flight_calendar_prices/index.ts +131 -0
- package/examples/google-flights/get_flight_calendar_prices/package.json +9 -0
- package/examples/google-flights/get_flight_calendar_prices/parser.ts +86 -0
- package/examples/google-flights/get_flight_calendar_prices/playbook.yaml +97 -0
- package/examples/google-flights/get_flight_calendar_prices/request-transform.ts +31 -0
- package/examples/google-flights/get_flight_calendar_prices/workflow.json +78 -0
- package/examples/google-flights/lookup_airport/index.ts +101 -0
- package/examples/google-flights/lookup_airport/package.json +9 -0
- package/examples/google-flights/lookup_airport/parser.ts +66 -0
- package/examples/google-flights/lookup_airport/playbook.yaml +47 -0
- package/examples/google-flights/lookup_airport/request-transform.ts +20 -0
- package/examples/google-flights/lookup_airport/workflow.json +57 -0
- package/examples/google-flights/search_flights/index.ts +219 -0
- package/examples/google-flights/search_flights/package.json +9 -0
- package/examples/google-flights/search_flights/parser.ts +169 -0
- package/examples/google-flights/search_flights/playbook.yaml +184 -0
- package/examples/google-flights/search_flights/request-transform.ts +119 -0
- package/examples/google-flights/search_flights/workflow.json +143 -0
- package/examples/google-hotels/README.md +29 -0
- package/examples/google-hotels/_shared/batchexecute.ts +73 -0
- package/examples/google-hotels/_shared/freq.ts +158 -0
- package/examples/google-hotels/_shared/package.json +9 -0
- package/examples/google-hotels/autocomplete_hotel_location/index.ts +80 -0
- package/examples/google-hotels/autocomplete_hotel_location/package.json +9 -0
- package/examples/google-hotels/autocomplete_hotel_location/parser.ts +71 -0
- package/examples/google-hotels/autocomplete_hotel_location/playbook.yaml +36 -0
- package/examples/google-hotels/autocomplete_hotel_location/request-transform.ts +37 -0
- package/examples/google-hotels/autocomplete_hotel_location/workflow.json +36 -0
- package/examples/google-hotels/get_hotel_booking_options/index.ts +143 -0
- package/examples/google-hotels/get_hotel_booking_options/package.json +9 -0
- package/examples/google-hotels/get_hotel_booking_options/parser.ts +271 -0
- package/examples/google-hotels/get_hotel_booking_options/playbook.yaml +154 -0
- package/examples/google-hotels/get_hotel_booking_options/request-transform.ts +154 -0
- package/examples/google-hotels/get_hotel_booking_options/workflow.json +84 -0
- package/examples/google-hotels/get_hotel_reviews/index.ts +81 -0
- package/examples/google-hotels/get_hotel_reviews/package.json +9 -0
- package/examples/google-hotels/get_hotel_reviews/parser.ts +128 -0
- package/examples/google-hotels/get_hotel_reviews/playbook.yaml +64 -0
- package/examples/google-hotels/get_hotel_reviews/request-transform.ts +42 -0
- package/examples/google-hotels/get_hotel_reviews/workflow.json +37 -0
- package/examples/google-hotels/search_hotels/index.ts +207 -0
- package/examples/google-hotels/search_hotels/package.json +9 -0
- package/examples/google-hotels/search_hotels/parser.ts +260 -0
- package/examples/google-hotels/search_hotels/playbook.yaml +87 -0
- package/examples/google-hotels/search_hotels/request-transform.ts +197 -0
- package/examples/google-hotels/search_hotels/workflow.json +127 -0
- package/examples/southwest/README.md +3 -2
- package/examples/southwest/search_southwest_flights/index.ts +18 -1
- package/examples/southwest/search_southwest_flights/workflow.json +18 -1
- package/package.json +3 -2
- package/prompts/audit-agent.md +71 -0
- package/prompts/build-planning.md +74 -0
- package/prompts/compile-agent.md +131 -27
- package/prompts/prereq-builder.md +64 -0
- package/prompts/prereq-planner.md +34 -0
- package/prompts/tool-planning.md +39 -0
- package/src/cli.ts +116 -3
- package/src/imprint/agent.ts +5 -0
- package/src/imprint/audit.ts +996 -0
- package/src/imprint/backend-ladder.ts +1214 -184
- package/src/imprint/build-plan.ts +1051 -0
- package/src/imprint/cdp-browser-fetch.ts +592 -0
- package/src/imprint/cdp-jar-cache.ts +320 -0
- package/src/imprint/chromium.ts +414 -8
- package/src/imprint/claude-cli-compile.ts +125 -25
- package/src/imprint/codex-cli-compile.ts +26 -23
- package/src/imprint/compile-agent-types.ts +38 -0
- package/src/imprint/compile-agent.ts +63 -25
- package/src/imprint/compile-tools.ts +1666 -66
- package/src/imprint/compile.ts +13 -1
- package/src/imprint/concurrency.ts +87 -0
- package/src/imprint/cron.ts +4 -0
- package/src/imprint/doctor.ts +48 -3
- package/src/imprint/freeform-redact.ts +5 -4
- package/src/imprint/install.ts +79 -4
- package/src/imprint/integrations.ts +3 -3
- package/src/imprint/llm.ts +56 -8
- package/src/imprint/mcp-compile-server.ts +43 -10
- package/src/imprint/mcp-maintenance.ts +18 -102
- package/src/imprint/mcp-server.ts +73 -7
- package/src/imprint/multi-progress.ts +7 -2
- package/src/imprint/param-grounding.ts +367 -0
- package/src/imprint/paths.ts +29 -0
- package/src/imprint/playbook-runner.ts +101 -40
- package/src/imprint/prereq-builder.ts +651 -0
- package/src/imprint/probe-backends.ts +6 -3
- package/src/imprint/record.ts +10 -1
- package/src/imprint/redact.ts +30 -2
- package/src/imprint/replay-capture.ts +19 -18
- package/src/imprint/runtime.ts +19 -10
- package/src/imprint/session-diff.ts +79 -2
- package/src/imprint/session-merge.ts +9 -5
- package/src/imprint/stealth-chromium.ts +79 -0
- package/src/imprint/stealth-fetch.ts +309 -29
- package/src/imprint/stealth-token-cache.ts +88 -0
- package/src/imprint/teach-plan.ts +251 -0
- package/src/imprint/teach-state.ts +10 -0
- package/src/imprint/teach.ts +456 -142
- package/src/imprint/tool-candidates.ts +72 -14
- package/src/imprint/tool-plan.ts +313 -0
- package/src/imprint/tracing.ts +135 -6
- package/src/imprint/types.ts +61 -3
- package/examples/google-flights/search_google_flights/index.ts +0 -101
- package/examples/google-flights/search_google_flights/parser.test.ts +0 -140
- package/examples/google-flights/search_google_flights/parser.ts +0 -189
- package/examples/google-flights/search_google_flights/playbook.yaml +0 -130
- package/examples/google-flights/search_google_flights/workflow.json +0 -48
- package/examples/google-hotels/search_google_hotels/index.ts +0 -194
- package/examples/google-hotels/search_google_hotels/parser.test.ts +0 -168
- package/examples/google-hotels/search_google_hotels/parser.ts +0 -330
- package/examples/google-hotels/search_google_hotels/playbook.yaml +0 -125
- package/examples/google-hotels/search_google_hotels/workflow.json +0 -111
- package/examples/namecheap-domains/search_namecheap_domains/index.ts +0 -144
- package/examples/namecheap-domains/search_namecheap_domains/parser.ts +0 -380
- package/examples/namecheap-domains/search_namecheap_domains/playbook.yaml +0 -50
- package/examples/namecheap-domains/search_namecheap_domains/request-transform.ts +0 -136
- package/examples/namecheap-domains/search_namecheap_domains/workflow.json +0 -97
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"toolName": "get_flight_booking_details",
|
|
3
|
+
"site": "google-flights",
|
|
4
|
+
"intent": {
|
|
5
|
+
"description": "Get booking/fare details and bookable options for a specific selected flight itinerary on Google Flights.",
|
|
6
|
+
"userSaid": "clicked one of the one-way fares to find more details and booking details; saw the options for another flight; chose round trip fares; kept exploring round trip fare details"
|
|
7
|
+
},
|
|
8
|
+
"parameters": [
|
|
9
|
+
{
|
|
10
|
+
"name": "origin",
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Origin airport IATA code for the outbound leg, e.g. SJC",
|
|
13
|
+
"verified": false,
|
|
14
|
+
"verifyNote": "annotated"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "destination",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "Destination airport IATA code for the outbound leg, e.g. SAN",
|
|
20
|
+
"verified": false,
|
|
21
|
+
"verifyNote": "annotated"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "departure_date",
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Outbound date in YYYY-MM-DD",
|
|
27
|
+
"verified": false,
|
|
28
|
+
"verifyNote": "annotated"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "return_date",
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "Return date in YYYY-MM-DD. Empty/omit for a one-way booking.",
|
|
34
|
+
"default": "",
|
|
35
|
+
"verified": true
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "outbound_flight",
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Selected outbound flight as 'carrier number', e.g. 'WN 3489'",
|
|
41
|
+
"verified": false,
|
|
42
|
+
"verifyNote": "annotated"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "return_flight",
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Selected return flight as 'carrier number' (round trip only), e.g. 'WN 3540'. Empty for one-way.",
|
|
48
|
+
"default": "",
|
|
49
|
+
"verified": false,
|
|
50
|
+
"verifyNote": "annotated"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "flight_token",
|
|
54
|
+
"type": "string",
|
|
55
|
+
"description": "Opaque per-itinerary booking token minted by the search_flights tool's flight_token output for the selected itinerary."
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"bootstrap": {
|
|
59
|
+
"url": "https://www.google.com/travel/flights",
|
|
60
|
+
"waitUntil": "domcontentloaded",
|
|
61
|
+
"timeoutMs": 30000,
|
|
62
|
+
"captures": [
|
|
63
|
+
{
|
|
64
|
+
"source": "html_regex",
|
|
65
|
+
"name": "f_sid",
|
|
66
|
+
"pattern": "\"FdrFJe\":\"([^\"]+)\"",
|
|
67
|
+
"group": 1,
|
|
68
|
+
"required": false,
|
|
69
|
+
"capability": "browser_bootstrap"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"source": "html_regex",
|
|
73
|
+
"name": "bl",
|
|
74
|
+
"pattern": "\"cfb2h\":\"([^\"]+)\"",
|
|
75
|
+
"group": 1,
|
|
76
|
+
"required": false,
|
|
77
|
+
"capability": "browser_bootstrap"
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
"requests": [
|
|
82
|
+
{
|
|
83
|
+
"method": "POST",
|
|
84
|
+
"url": "https://www.google.com/_/FlightsFrontendUi/data/travel.frontend.flights.FlightsFrontendService/GetBookingResults?f.sid=${state.f_sid}&bl=${state.bl}&hl=en-US&soc-app=162&soc-platform=1&soc-device=1&_reqid=1659189&rt=c",
|
|
85
|
+
"headers": {
|
|
86
|
+
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
87
|
+
"X-Same-Domain": "1",
|
|
88
|
+
"Referer": "https://www.google.com/travel/flights",
|
|
89
|
+
"x-goog-ext-259736195-jspb": "[\"en-US\",\"US\",\"USD\",2,null,[420],null,null,7,[]]"
|
|
90
|
+
},
|
|
91
|
+
"body": "f.req=${param.origin}|${param.destination}|${param.departure_date}|${param.return_date}|${param.outbound_flight}|${param.return_flight}|${param.flight_token}&",
|
|
92
|
+
"effect": "safe"
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
"requestTransformModule": "./request-transform.ts",
|
|
96
|
+
"parserModule": "./parser.ts",
|
|
97
|
+
"liveVerified": true
|
|
98
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED by `imprint emit` — DO NOT EDIT BY HAND.
|
|
3
|
+
*
|
|
4
|
+
* Tool: get_flight_calendar_prices
|
|
5
|
+
* Site: google-flights
|
|
6
|
+
* Intent: Get a calendar grid of lowest round-trip prices by departure date for a route over a date window.
|
|
7
|
+
*
|
|
8
|
+
* To regenerate: imprint emit ~/.imprint/google-flights/get_flight_calendar_prices/workflow.json --force
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import {
|
|
14
|
+
executeWorkflow,
|
|
15
|
+
type CredentialStore,
|
|
16
|
+
} from 'imprint/runtime';
|
|
17
|
+
import type { ToolResult, Workflow } from 'imprint/types';
|
|
18
|
+
|
|
19
|
+
const WORKFLOW: Workflow = {
|
|
20
|
+
"toolName": "get_flight_calendar_prices",
|
|
21
|
+
"intent": {
|
|
22
|
+
"description": "Get a calendar grid of lowest round-trip prices by departure date for a route over a date window.",
|
|
23
|
+
"userSaid": "searched for a round trip flight; SJC to SAN"
|
|
24
|
+
},
|
|
25
|
+
"parameters": [
|
|
26
|
+
{
|
|
27
|
+
"name": "origin",
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Origin airport IATA code, e.g. SJC",
|
|
30
|
+
"verified": false,
|
|
31
|
+
"verifyNote": "annotated"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "destination",
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Destination airport IATA code, e.g. SAN",
|
|
37
|
+
"verified": false,
|
|
38
|
+
"verifyNote": "annotated"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "start_date",
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "Start of departure-date window, YYYY-MM-DD",
|
|
44
|
+
"verified": false,
|
|
45
|
+
"verifyNote": "annotated"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "end_date",
|
|
49
|
+
"type": "string",
|
|
50
|
+
"description": "End of departure-date window, YYYY-MM-DD",
|
|
51
|
+
"verified": false,
|
|
52
|
+
"verifyNote": "annotated"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"requests": [
|
|
56
|
+
{
|
|
57
|
+
"method": "POST",
|
|
58
|
+
"url": "https://www.google.com/_/FlightsFrontendUi/data/travel.frontend.flights.FlightsFrontendService/GetCalendarPicker?f.sid=${state.f_sid}&bl=${state.bl}&hl=en-US&soc-app=162&soc-platform=1&soc-device=1&_reqid=1508023&rt=c",
|
|
59
|
+
"headers": {
|
|
60
|
+
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
61
|
+
"X-Same-Domain": "1",
|
|
62
|
+
"x-goog-ext-259736195-jspb": "[\"en-US\",\"US\",\"USD\",2,null,[420],null,null,7,[]]",
|
|
63
|
+
"Referer": "https://www.google.com/travel/flights",
|
|
64
|
+
"Accept-Language": "en-US,en;q=0.9"
|
|
65
|
+
},
|
|
66
|
+
"body": "f.req=%5Bnull%2C%22%5Bnull%2C%5Bnull%2Cnull%2C1%2Cnull%2C%5B%5D%2C1%2C%5B1%2C0%2C0%2C0%5D%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2C%5B%5B%5B%5B%5B%5C%22${param.origin}%5C%22%2C0%5D%5D%5D%2C%5B%5B%5B%5C%22${param.destination}%5C%22%2C0%5D%5D%5D%2Cnull%2C0%5D%2C%5B%5B%5B%5B%5C%22${param.destination}%5C%22%2C0%5D%5D%5D%2C%5B%5B%5B%5C%22${param.origin}%5C%22%2C0%5D%5D%5D%2Cnull%2C0%5D%5D%2Cnull%2Cnull%2Cnull%2C1%5D%2C%5B%5C%22${param.start_date}%5C%22%2C%5C%22${param.end_date}%5C%22%5D%2Cnull%2C%5B7%2C7%5D%5D%22%5D&",
|
|
67
|
+
"effect": "safe"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"site": "google-flights",
|
|
71
|
+
"bootstrap": {
|
|
72
|
+
"url": "https://www.google.com/travel/flights?hl=en-US&curr=USD",
|
|
73
|
+
"waitUntil": "domcontentloaded",
|
|
74
|
+
"captures": [
|
|
75
|
+
{
|
|
76
|
+
"name": "f_sid",
|
|
77
|
+
"required": false,
|
|
78
|
+
"capability": "browser_bootstrap",
|
|
79
|
+
"source": "html_regex",
|
|
80
|
+
"pattern": "\"FdrFJe\":\"([^\"]+)\"",
|
|
81
|
+
"group": 1
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "bl",
|
|
85
|
+
"required": false,
|
|
86
|
+
"capability": "browser_bootstrap",
|
|
87
|
+
"source": "html_regex",
|
|
88
|
+
"pattern": "\"cfb2h\":\"([^\"]+)\"",
|
|
89
|
+
"group": 1
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
"parserModule": "./parser.ts",
|
|
94
|
+
"requestTransformModule": "./request-transform.ts",
|
|
95
|
+
"liveVerified": true
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export interface GetFlightCalendarPricesInput {
|
|
99
|
+
/** Origin airport IATA code, e.g. SJC */
|
|
100
|
+
origin: string;
|
|
101
|
+
/** Destination airport IATA code, e.g. SAN */
|
|
102
|
+
destination: string;
|
|
103
|
+
/** Start of departure-date window, YYYY-MM-DD */
|
|
104
|
+
start_date: string;
|
|
105
|
+
/** End of departure-date window, YYYY-MM-DD */
|
|
106
|
+
end_date: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function getFlightCalendarPrices(
|
|
110
|
+
input: GetFlightCalendarPricesInput,
|
|
111
|
+
opts: { credentials?: CredentialStore; fetchImpl?: typeof fetch; initialState?: Record<string, unknown> } = {},
|
|
112
|
+
): Promise<ToolResult> {
|
|
113
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
114
|
+
const params: Record<string, string | number | boolean> = {
|
|
115
|
+
origin: input.origin,
|
|
116
|
+
destination: input.destination,
|
|
117
|
+
start_date: input.start_date,
|
|
118
|
+
end_date: input.end_date,
|
|
119
|
+
|
|
120
|
+
};
|
|
121
|
+
return executeWorkflow({
|
|
122
|
+
workflow: WORKFLOW,
|
|
123
|
+
params,
|
|
124
|
+
credentials: opts.credentials,
|
|
125
|
+
fetchImpl: opts.fetchImpl,
|
|
126
|
+
initialState: opts.initialState,
|
|
127
|
+
workflowPath: join(__dirname, 'workflow.json'),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { WORKFLOW };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Parse a Google Flights GetCalendarPicker batchexecute response into a
|
|
2
|
+
// date->price calendar for the route. Decoding of the )]}' envelope is delegated
|
|
3
|
+
// to the shared batchexecute helper (imported per the build plan).
|
|
4
|
+
import { decodeBatchExecute } from '../_shared/batchexecute.ts';
|
|
5
|
+
|
|
6
|
+
type CalendarEntry = {
|
|
7
|
+
departureDate: string;
|
|
8
|
+
returnDate: string | null;
|
|
9
|
+
lowestPriceUSD: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
13
|
+
|
|
14
|
+
// The GetCalendarPicker wrb.fr payload is shaped:
|
|
15
|
+
// [ <metadata>, [ [depDate, retDate, [[null, price], token], 1], ... ] ]
|
|
16
|
+
// The metadata entry is skipped naturally because we only keep array items whose
|
|
17
|
+
// [0] is an ISO date string. We scan every nested array so we are robust to the
|
|
18
|
+
// list living at payload[1] (the recorded shape) or being flattened.
|
|
19
|
+
function collectEntries(payload: unknown): CalendarEntry[] {
|
|
20
|
+
const entries: CalendarEntry[] = [];
|
|
21
|
+
const seen = new Set<string>();
|
|
22
|
+
if (!Array.isArray(payload)) return entries;
|
|
23
|
+
|
|
24
|
+
const consider = (item: unknown) => {
|
|
25
|
+
if (!Array.isArray(item)) return;
|
|
26
|
+
const dep = item[0];
|
|
27
|
+
if (typeof dep !== 'string' || !ISO_DATE.test(dep)) return;
|
|
28
|
+
const ret = typeof item[1] === 'string' && ISO_DATE.test(item[1]) ? (item[1] as string) : null;
|
|
29
|
+
// price lives at item[2][0][1]
|
|
30
|
+
const priceContainer = item[2];
|
|
31
|
+
let price: unknown = null;
|
|
32
|
+
if (Array.isArray(priceContainer) && Array.isArray(priceContainer[0])) {
|
|
33
|
+
price = (priceContainer[0] as unknown[])[1];
|
|
34
|
+
}
|
|
35
|
+
if (typeof price !== 'number') return; // no fare found for that date -> omit
|
|
36
|
+
if (seen.has(dep)) return;
|
|
37
|
+
seen.add(dep);
|
|
38
|
+
entries.push({ departureDate: dep, returnDate: ret, lowestPriceUSD: price });
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
for (const top of payload) {
|
|
42
|
+
if (Array.isArray(top)) {
|
|
43
|
+
// top may itself be the list of date entries, or a single entry.
|
|
44
|
+
consider(top);
|
|
45
|
+
for (const inner of top) consider(inner);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return entries;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function extract(
|
|
52
|
+
rawResponse: unknown,
|
|
53
|
+
context?: { params?: Record<string, string | number | boolean>; responses?: unknown[] },
|
|
54
|
+
): unknown {
|
|
55
|
+
const raw = typeof rawResponse === 'string' ? rawResponse : JSON.stringify(rawResponse ?? '');
|
|
56
|
+
const frames = decodeBatchExecute(raw);
|
|
57
|
+
|
|
58
|
+
let payload: unknown = null;
|
|
59
|
+
for (const f of frames) {
|
|
60
|
+
const candidate = collectEntries(f.payload);
|
|
61
|
+
if (candidate.length > 0) {
|
|
62
|
+
payload = f.payload;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// If no frame produced entries, still attempt the first frame's payload so an
|
|
67
|
+
// empty (zero-result) response yields an empty calendar rather than throwing.
|
|
68
|
+
if (payload == null && frames.length > 0) payload = frames[0]?.payload ?? null;
|
|
69
|
+
|
|
70
|
+
const entries = collectEntries(payload).sort((a, b) =>
|
|
71
|
+
a.departureDate < b.departureDate ? -1 : a.departureDate > b.departureDate ? 1 : 0,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const prices: Record<string, number> = {};
|
|
75
|
+
for (const e of entries) prices[e.departureDate] = e.lowestPriceUSD;
|
|
76
|
+
|
|
77
|
+
const params = context?.params ?? {};
|
|
78
|
+
return {
|
|
79
|
+
origin: params.origin != null ? String(params.origin).toUpperCase() : null,
|
|
80
|
+
destination: params.destination != null ? String(params.destination).toUpperCase() : null,
|
|
81
|
+
currency: 'USD',
|
|
82
|
+
count: entries.length,
|
|
83
|
+
prices,
|
|
84
|
+
calendar: entries,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
toolName: get_flight_calendar_prices
|
|
2
|
+
summary: Open the Google Flights date picker for a route to capture the calendar grid of lowest prices by departure/return date.
|
|
3
|
+
parameters:
|
|
4
|
+
- name: origin
|
|
5
|
+
type: string
|
|
6
|
+
description: Origin airport code, e.g. SJC
|
|
7
|
+
- name: destination
|
|
8
|
+
type: string
|
|
9
|
+
description: Destination airport code, e.g. SAN
|
|
10
|
+
- name: start_date
|
|
11
|
+
type: string
|
|
12
|
+
description: Start of date window YYYY-MM-DD (used to filter the returned calendar range; the picker returns a fixed window starting near today)
|
|
13
|
+
- name: end_date
|
|
14
|
+
type: string
|
|
15
|
+
description: End of date window YYYY-MM-DD (used to filter the returned calendar range)
|
|
16
|
+
steps:
|
|
17
|
+
- action: navigate
|
|
18
|
+
url: https://www.google.com/travel/flights
|
|
19
|
+
wait_for: networkidle
|
|
20
|
+
- action: click
|
|
21
|
+
locators:
|
|
22
|
+
- by: role
|
|
23
|
+
value: combobox
|
|
24
|
+
name: "Where from?"
|
|
25
|
+
- by: aria_label
|
|
26
|
+
value: "Where from?"
|
|
27
|
+
wait_for:
|
|
28
|
+
sleep_ms: 300
|
|
29
|
+
- action: type
|
|
30
|
+
locators:
|
|
31
|
+
- by: aria_label
|
|
32
|
+
value: "Where from?"
|
|
33
|
+
- by: aria_label
|
|
34
|
+
value: "Where else?"
|
|
35
|
+
value: ${origin}
|
|
36
|
+
wait_for:
|
|
37
|
+
sleep_ms: 500
|
|
38
|
+
- action: click
|
|
39
|
+
locators:
|
|
40
|
+
- by: aria_label
|
|
41
|
+
value_pattern: ${origin}
|
|
42
|
+
- by: role
|
|
43
|
+
value: option
|
|
44
|
+
- by: css
|
|
45
|
+
value: "ul.DFGgtd > li.n4HaVc"
|
|
46
|
+
wait_for: visible
|
|
47
|
+
- action: click
|
|
48
|
+
locators:
|
|
49
|
+
- by: role
|
|
50
|
+
value: combobox
|
|
51
|
+
name: "Where to?"
|
|
52
|
+
- by: aria_label
|
|
53
|
+
value: "Where to? "
|
|
54
|
+
wait_for:
|
|
55
|
+
sleep_ms: 300
|
|
56
|
+
- action: type
|
|
57
|
+
locators:
|
|
58
|
+
- by: aria_label
|
|
59
|
+
value: "Where to? "
|
|
60
|
+
- by: aria_label
|
|
61
|
+
value: "Where else?"
|
|
62
|
+
value: ${destination}
|
|
63
|
+
wait_for:
|
|
64
|
+
sleep_ms: 500
|
|
65
|
+
- action: click
|
|
66
|
+
locators:
|
|
67
|
+
- by: aria_label
|
|
68
|
+
value_pattern: ${destination}
|
|
69
|
+
- by: role
|
|
70
|
+
value: option
|
|
71
|
+
- by: css
|
|
72
|
+
value: "ul.DFGgtd > li.n4HaVc"
|
|
73
|
+
wait_for: visible
|
|
74
|
+
- action: click
|
|
75
|
+
locators:
|
|
76
|
+
- by: aria_label
|
|
77
|
+
value: Departure
|
|
78
|
+
- by: css
|
|
79
|
+
value: "div.GYgkab.YICvqf > div.NA5Egc.ESCxub > input.TP4Lpb.eoY5cb"
|
|
80
|
+
wait_for:
|
|
81
|
+
xhr: /GetCalendarPicker
|
|
82
|
+
result:
|
|
83
|
+
source: xhr
|
|
84
|
+
url_pattern: /GetCalendarPicker
|
|
85
|
+
extract: "[1][]"
|
|
86
|
+
return_as: calendar_prices
|
|
87
|
+
notes: >-
|
|
88
|
+
The GetCalendarPicker response is a Google batchexecute payload: strip the )]}' prefix, split on
|
|
89
|
+
numeric length-prefix lines, and JSON.parse the "wrb.fr" envelope's third element (a JSON-encoded
|
|
90
|
+
string). Inside that decoded array, index [1] is the list of date entries; each entry is
|
|
91
|
+
[departureDate (YYYY-MM-DD), returnDate (YYYY-MM-DD), [[null, lowestPriceUSD], opaqueFlightToken], 1].
|
|
92
|
+
Build a departureDate->lowestPriceUSD map and filter to [start_date, end_date]. The extract path
|
|
93
|
+
"[1][]" is relative to the decoded wrb.fr payload, not the raw HTTP body. start_date/end_date are
|
|
94
|
+
client-side filters: the picker returns a fixed forward window (~from today) and is opened simply by
|
|
95
|
+
clicking the Departure field — no date is typed to drive the request. The request is authorized by a
|
|
96
|
+
per-page f.sid plus X-Goog-BatchExecute-Bgr header that must be captured fresh from the page bootstrap
|
|
97
|
+
(navigate step), not parameterized.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Local adapter: maps this tool's user-facing params (origin/destination/
|
|
2
|
+
// start_date/end_date) into the shape the shared FlightsFrontendService body
|
|
3
|
+
// builder consumes (legs[]/startDate/endDate/tripType), then delegates the
|
|
4
|
+
// byte-for-byte f.req encoding to the assigned shared request-transform.
|
|
5
|
+
import { transform as sharedTransform } from '../_shared/flights_request.ts';
|
|
6
|
+
|
|
7
|
+
export function transform(
|
|
8
|
+
method: string,
|
|
9
|
+
url: string,
|
|
10
|
+
responses: Record<string, any>,
|
|
11
|
+
params?: Record<string, any>,
|
|
12
|
+
): { url: string; body: string } {
|
|
13
|
+
const p: Record<string, any> = params ?? {};
|
|
14
|
+
const origin = String(p.origin ?? '').toUpperCase();
|
|
15
|
+
const destination = String(p.destination ?? '').toUpperCase();
|
|
16
|
+
|
|
17
|
+
// GetCalendarPicker for this tool is a round-trip (tripType 1), 1 adult,
|
|
18
|
+
// with a mirrored outbound/return leg pair — exactly as recorded in seq 97.
|
|
19
|
+
const adapted = {
|
|
20
|
+
tripType: 1,
|
|
21
|
+
adults: 1,
|
|
22
|
+
legs: [
|
|
23
|
+
{ origin, dest: destination },
|
|
24
|
+
{ origin: destination, dest: origin },
|
|
25
|
+
],
|
|
26
|
+
startDate: p.start_date ?? null,
|
|
27
|
+
endDate: p.end_date ?? null,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return sharedTransform(method, url, responses, adapted);
|
|
31
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"toolName": "get_flight_calendar_prices",
|
|
3
|
+
"intent": {
|
|
4
|
+
"description": "Get a calendar grid of lowest round-trip prices by departure date for a route over a date window.",
|
|
5
|
+
"userSaid": "searched for a round trip flight; SJC to SAN"
|
|
6
|
+
},
|
|
7
|
+
"site": "google-flights",
|
|
8
|
+
"parameters": [
|
|
9
|
+
{
|
|
10
|
+
"name": "origin",
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Origin airport IATA code, e.g. SJC",
|
|
13
|
+
"verified": false,
|
|
14
|
+
"verifyNote": "annotated"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "destination",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "Destination airport IATA code, e.g. SAN",
|
|
20
|
+
"verified": false,
|
|
21
|
+
"verifyNote": "annotated"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "start_date",
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Start of departure-date window, YYYY-MM-DD",
|
|
27
|
+
"verified": false,
|
|
28
|
+
"verifyNote": "annotated"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "end_date",
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "End of departure-date window, YYYY-MM-DD",
|
|
34
|
+
"verified": false,
|
|
35
|
+
"verifyNote": "annotated"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"bootstrap": {
|
|
39
|
+
"url": "https://www.google.com/travel/flights?hl=en-US&curr=USD",
|
|
40
|
+
"waitUntil": "domcontentloaded",
|
|
41
|
+
"captures": [
|
|
42
|
+
{
|
|
43
|
+
"source": "html_regex",
|
|
44
|
+
"name": "f_sid",
|
|
45
|
+
"pattern": "\"FdrFJe\":\"([^\"]+)\"",
|
|
46
|
+
"group": 1,
|
|
47
|
+
"required": false,
|
|
48
|
+
"capability": "browser_bootstrap"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"source": "html_regex",
|
|
52
|
+
"name": "bl",
|
|
53
|
+
"pattern": "\"cfb2h\":\"([^\"]+)\"",
|
|
54
|
+
"group": 1,
|
|
55
|
+
"required": false,
|
|
56
|
+
"capability": "browser_bootstrap"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
"requests": [
|
|
61
|
+
{
|
|
62
|
+
"method": "POST",
|
|
63
|
+
"url": "https://www.google.com/_/FlightsFrontendUi/data/travel.frontend.flights.FlightsFrontendService/GetCalendarPicker?f.sid=${state.f_sid}&bl=${state.bl}&hl=en-US&soc-app=162&soc-platform=1&soc-device=1&_reqid=1508023&rt=c",
|
|
64
|
+
"headers": {
|
|
65
|
+
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
66
|
+
"X-Same-Domain": "1",
|
|
67
|
+
"x-goog-ext-259736195-jspb": "[\"en-US\",\"US\",\"USD\",2,null,[420],null,null,7,[]]",
|
|
68
|
+
"Referer": "https://www.google.com/travel/flights",
|
|
69
|
+
"Accept-Language": "en-US,en;q=0.9"
|
|
70
|
+
},
|
|
71
|
+
"body": "f.req=%5Bnull%2C%22%5Bnull%2C%5Bnull%2Cnull%2C1%2Cnull%2C%5B%5D%2C1%2C%5B1%2C0%2C0%2C0%5D%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2C%5B%5B%5B%5B%5B%5C%22${param.origin}%5C%22%2C0%5D%5D%5D%2C%5B%5B%5B%5C%22${param.destination}%5C%22%2C0%5D%5D%5D%2Cnull%2C0%5D%2C%5B%5B%5B%5B%5C%22${param.destination}%5C%22%2C0%5D%5D%5D%2C%5B%5B%5B%5C%22${param.origin}%5C%22%2C0%5D%5D%5D%2Cnull%2C0%5D%5D%2Cnull%2Cnull%2Cnull%2C1%5D%2C%5B%5C%22${param.start_date}%5C%22%2C%5C%22${param.end_date}%5C%22%5D%2Cnull%2C%5B7%2C7%5D%5D%22%5D&",
|
|
72
|
+
"effect": "safe"
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"requestTransformModule": "./request-transform.ts",
|
|
76
|
+
"parserModule": "./parser.ts",
|
|
77
|
+
"liveVerified": true
|
|
78
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED by `imprint emit` — DO NOT EDIT BY HAND.
|
|
3
|
+
*
|
|
4
|
+
* Tool: lookup_airport
|
|
5
|
+
* Site: google-flights
|
|
6
|
+
* Intent: Resolve a city or airport query into airport codes and structured airport details.
|
|
7
|
+
*
|
|
8
|
+
* To regenerate: imprint emit ~/.imprint/google-flights/lookup_airport/workflow.json --force
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import {
|
|
14
|
+
executeWorkflow,
|
|
15
|
+
type CredentialStore,
|
|
16
|
+
} from 'imprint/runtime';
|
|
17
|
+
import type { ToolResult, Workflow } from 'imprint/types';
|
|
18
|
+
|
|
19
|
+
const WORKFLOW: Workflow = {
|
|
20
|
+
"toolName": "lookup_airport",
|
|
21
|
+
"intent": {
|
|
22
|
+
"description": "Resolve a city or airport query into airport codes and structured airport details.",
|
|
23
|
+
"userSaid": "multi city from SJC to SAN to LAX to SFO"
|
|
24
|
+
},
|
|
25
|
+
"parameters": [
|
|
26
|
+
{
|
|
27
|
+
"name": "query",
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "City or airport search text, e.g. 'san jose' or 'SJC'",
|
|
30
|
+
"verified": true
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"requests": [
|
|
34
|
+
{
|
|
35
|
+
"method": "POST",
|
|
36
|
+
"url": "https://www.google.com/_/FlightsFrontendUi/data/batchexecute?rpcids=tDoGIe&source-path=%2Ftravel%2Fflights&f.sid=${state.f_sid}&bl=${state.bl}&hl=en-US&soc-app=162&soc-platform=1&soc-device=1&_reqid=608023&rt=c",
|
|
37
|
+
"headers": {
|
|
38
|
+
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
39
|
+
"X-Same-Domain": "1",
|
|
40
|
+
"Referer": "https://www.google.com/travel/flights",
|
|
41
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
42
|
+
"x-goog-ext-259736195-jspb": "[\"en-US\",\"US\",\"USD\",2,null,[420],null,null,7,[]]"
|
|
43
|
+
},
|
|
44
|
+
"body": "f.req=%5B%5B%5B%22tDoGIe%22%2C%22%5Bnull%2C%5B%5B%5C%22${param.query}%5C%22%2C0%5D%5D%5D%22%2Cnull%2C%22generic%22%5D%5D%5D&",
|
|
45
|
+
"effect": "safe"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"site": "google-flights",
|
|
49
|
+
"bootstrap": {
|
|
50
|
+
"url": "https://www.google.com/travel/flights",
|
|
51
|
+
"waitUntil": "domcontentloaded",
|
|
52
|
+
"timeoutMs": 30000,
|
|
53
|
+
"captures": [
|
|
54
|
+
{
|
|
55
|
+
"name": "f_sid",
|
|
56
|
+
"required": true,
|
|
57
|
+
"capability": "browser_bootstrap",
|
|
58
|
+
"source": "html_regex",
|
|
59
|
+
"pattern": "\"FdrFJe\":\"([^\"]+)\"",
|
|
60
|
+
"group": 1
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "bl",
|
|
64
|
+
"required": true,
|
|
65
|
+
"capability": "browser_bootstrap",
|
|
66
|
+
"source": "html_regex",
|
|
67
|
+
"pattern": "\"cfb2h\":\"([^\"]+)\"",
|
|
68
|
+
"group": 1
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
"parserModule": "./parser.ts",
|
|
73
|
+
"requestTransformModule": "./request-transform.ts",
|
|
74
|
+
"liveVerified": true
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export interface LookupAirportInput {
|
|
78
|
+
/** City or airport search text, e.g. 'san jose' or 'SJC' */
|
|
79
|
+
query: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function lookupAirport(
|
|
83
|
+
input: LookupAirportInput,
|
|
84
|
+
opts: { credentials?: CredentialStore; fetchImpl?: typeof fetch; initialState?: Record<string, unknown> } = {},
|
|
85
|
+
): Promise<ToolResult> {
|
|
86
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
87
|
+
const params: Record<string, string | number | boolean> = {
|
|
88
|
+
query: input.query,
|
|
89
|
+
|
|
90
|
+
};
|
|
91
|
+
return executeWorkflow({
|
|
92
|
+
workflow: WORKFLOW,
|
|
93
|
+
params,
|
|
94
|
+
credentials: opts.credentials,
|
|
95
|
+
fetchImpl: opts.fetchImpl,
|
|
96
|
+
initialState: opts.initialState,
|
|
97
|
+
workflowPath: join(__dirname, 'workflow.json'),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export { WORKFLOW };
|