imprint-mcp 0.5.0 → 0.6.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.
Files changed (202) hide show
  1. package/README.md +6 -4
  2. package/examples/discoverandgo/README.md +1 -1
  3. package/examples/discoverandgo/book_discoverandgo_museum_pass/workflow.json +7 -0
  4. package/examples/google-flights/README.md +41 -14
  5. package/examples/google-flights/_shared/google_batchexecute_parser.ts +165 -0
  6. package/examples/google-flights/_shared/google_flights_transport.ts +70 -0
  7. package/examples/google-flights/_shared/google_flights_types.ts +56 -0
  8. package/examples/google-flights/get_flight_booking_options/backends.json +43 -0
  9. package/examples/google-flights/get_flight_booking_options/index.ts +129 -0
  10. package/examples/google-flights/get_flight_booking_options/parser.ts +209 -0
  11. package/examples/google-flights/get_flight_booking_options/playbook.yaml +46 -0
  12. package/examples/google-flights/get_flight_booking_options/request-transform.ts +210 -0
  13. package/examples/google-flights/get_flight_booking_options/workflow.json +85 -0
  14. package/examples/google-flights/get_flight_calendar_prices/backends.json +27 -0
  15. package/examples/google-flights/get_flight_calendar_prices/index.ts +44 -29
  16. package/examples/google-flights/get_flight_calendar_prices/parser.ts +76 -69
  17. package/examples/google-flights/get_flight_calendar_prices/playbook.yaml +67 -40
  18. package/examples/google-flights/get_flight_calendar_prices/workflow.json +29 -17
  19. package/examples/google-flights/get_flight_location_details/backends.json +27 -0
  20. package/examples/google-flights/{lookup_airport → get_flight_location_details}/index.ts +30 -20
  21. package/examples/google-flights/get_flight_location_details/parser.ts +103 -0
  22. package/examples/google-flights/get_flight_location_details/playbook.yaml +59 -0
  23. package/examples/google-flights/{lookup_airport → get_flight_location_details}/workflow.json +19 -12
  24. package/examples/google-flights/lookup_flight_locations/backends.json +27 -0
  25. package/examples/google-flights/lookup_flight_locations/index.ts +111 -0
  26. package/examples/google-flights/lookup_flight_locations/package.json +9 -0
  27. package/examples/google-flights/lookup_flight_locations/parser.ts +135 -0
  28. package/examples/google-flights/lookup_flight_locations/playbook.yaml +47 -0
  29. package/examples/google-flights/lookup_flight_locations/request-transform.ts +53 -0
  30. package/examples/google-flights/lookup_flight_locations/workflow.json +64 -0
  31. package/examples/google-flights/search_flights/backends.json +42 -0
  32. package/examples/google-flights/search_flights/index.ts +105 -69
  33. package/examples/google-flights/search_flights/parser.ts +186 -183
  34. package/examples/google-flights/search_flights/playbook.yaml +295 -88
  35. package/examples/google-flights/search_flights/request-transform.ts +247 -92
  36. package/examples/google-flights/search_flights/workflow.json +66 -40
  37. package/examples/google-flights/validate_flight_itinerary/backends.json +27 -0
  38. package/examples/google-flights/validate_flight_itinerary/index.ts +103 -0
  39. package/examples/google-flights/validate_flight_itinerary/package.json +9 -0
  40. package/examples/google-flights/validate_flight_itinerary/parser.ts +101 -0
  41. package/examples/google-flights/validate_flight_itinerary/playbook.yaml +84 -0
  42. package/examples/google-flights/validate_flight_itinerary/request-transform.ts +92 -0
  43. package/examples/google-flights/validate_flight_itinerary/workflow.json +59 -0
  44. package/examples/google-hotels/README.md +40 -15
  45. package/examples/google-hotels/get_hotel_details/backends.json +16 -0
  46. package/examples/google-hotels/get_hotel_details/index.ts +122 -0
  47. package/examples/google-hotels/get_hotel_details/parser.ts +275 -0
  48. package/examples/google-hotels/get_hotel_details/playbook.yaml +40 -0
  49. package/examples/google-hotels/get_hotel_details/request-transform.ts +67 -0
  50. package/examples/google-hotels/get_hotel_details/workflow.json +67 -0
  51. package/examples/google-hotels/get_hotel_photos/backends.json +16 -0
  52. package/examples/google-hotels/get_hotel_photos/index.ts +90 -0
  53. package/examples/google-hotels/get_hotel_photos/parser.ts +75 -0
  54. package/examples/google-hotels/get_hotel_photos/playbook.yaml +23 -0
  55. package/examples/google-hotels/get_hotel_photos/request-transform.ts +27 -0
  56. package/examples/google-hotels/get_hotel_photos/workflow.json +44 -0
  57. package/examples/google-hotels/get_hotel_reviews/backends.json +16 -0
  58. package/examples/google-hotels/get_hotel_reviews/index.ts +14 -17
  59. package/examples/google-hotels/get_hotel_reviews/parser.ts +177 -105
  60. package/examples/google-hotels/get_hotel_reviews/playbook.yaml +9 -54
  61. package/examples/google-hotels/get_hotel_reviews/workflow.json +10 -13
  62. package/examples/google-hotels/get_travel_area_boundaries/backends.json +16 -0
  63. package/examples/google-hotels/get_travel_area_boundaries/index.ts +104 -0
  64. package/examples/google-hotels/get_travel_area_boundaries/package.json +9 -0
  65. package/examples/google-hotels/get_travel_area_boundaries/parser.ts +127 -0
  66. package/examples/google-hotels/get_travel_area_boundaries/playbook.yaml +38 -0
  67. package/examples/google-hotels/get_travel_area_boundaries/request-transform.ts +52 -0
  68. package/examples/google-hotels/get_travel_area_boundaries/workflow.json +54 -0
  69. package/examples/google-hotels/list_hotel_booking_options/backends.json +16 -0
  70. package/examples/google-hotels/list_hotel_booking_options/index.ts +123 -0
  71. package/examples/google-hotels/list_hotel_booking_options/package.json +9 -0
  72. package/examples/google-hotels/list_hotel_booking_options/parser.ts +308 -0
  73. package/examples/google-hotels/list_hotel_booking_options/playbook.yaml +95 -0
  74. package/examples/google-hotels/list_hotel_booking_options/request-transform.ts +77 -0
  75. package/examples/google-hotels/list_hotel_booking_options/workflow.json +67 -0
  76. package/examples/google-hotels/search_hotel_web_links/backends.json +16 -0
  77. package/examples/google-hotels/search_hotel_web_links/index.ts +90 -0
  78. package/examples/google-hotels/search_hotel_web_links/package.json +9 -0
  79. package/examples/google-hotels/search_hotel_web_links/parser.ts +96 -0
  80. package/examples/google-hotels/search_hotel_web_links/playbook.yaml +53 -0
  81. package/examples/google-hotels/search_hotel_web_links/request-transform.ts +17 -0
  82. package/examples/google-hotels/search_hotel_web_links/workflow.json +43 -0
  83. package/examples/google-hotels/search_hotels/backends.json +16 -0
  84. package/examples/google-hotels/search_hotels/index.ts +32 -102
  85. package/examples/google-hotels/search_hotels/parser.ts +237 -216
  86. package/examples/google-hotels/search_hotels/playbook.yaml +177 -42
  87. package/examples/google-hotels/search_hotels/request-transform.ts +101 -170
  88. package/examples/google-hotels/search_hotels/workflow.json +24 -73
  89. package/examples/google-hotels/suggest_hotel_destinations/backends.json +16 -0
  90. package/examples/google-hotels/{autocomplete_hotel_location → suggest_hotel_destinations}/index.ts +30 -19
  91. package/examples/google-hotels/suggest_hotel_destinations/package.json +9 -0
  92. package/examples/google-hotels/suggest_hotel_destinations/parser.ts +138 -0
  93. package/examples/google-hotels/suggest_hotel_destinations/playbook.yaml +47 -0
  94. package/examples/google-hotels/suggest_hotel_destinations/request-transform.ts +33 -0
  95. package/examples/google-hotels/suggest_hotel_destinations/workflow.json +44 -0
  96. package/examples/southwest/README.md +31 -70
  97. package/examples/southwest/get_account_details/backends.json +22 -0
  98. package/examples/southwest/get_account_details/credentials.manifest.json +17 -0
  99. package/examples/southwest/get_account_details/index.ts +138 -0
  100. package/examples/{google-hotels/_shared → southwest/get_account_details}/package.json +2 -2
  101. package/examples/southwest/get_account_details/parser.ts +136 -0
  102. package/examples/southwest/get_account_details/playbook.yaml +19 -0
  103. package/examples/southwest/get_account_details/workflow.json +93 -0
  104. package/examples/southwest/get_flight_status/backends.json +21 -0
  105. package/examples/southwest/get_flight_status/index.ts +130 -0
  106. package/examples/southwest/get_flight_status/package.json +9 -0
  107. package/examples/southwest/get_flight_status/parser.ts +138 -0
  108. package/examples/southwest/get_flight_status/playbook.yaml +58 -0
  109. package/examples/southwest/get_flight_status/workflow.json +77 -0
  110. package/examples/southwest/get_low_fare_calendar/backends.json +21 -0
  111. package/examples/southwest/get_low_fare_calendar/index.ts +134 -0
  112. package/examples/southwest/get_low_fare_calendar/package.json +9 -0
  113. package/examples/southwest/get_low_fare_calendar/parser.ts +107 -0
  114. package/examples/southwest/get_low_fare_calendar/playbook.yaml +41 -0
  115. package/examples/southwest/get_low_fare_calendar/workflow.json +81 -0
  116. package/examples/southwest/list_upcoming_trips/backends.json +22 -0
  117. package/examples/southwest/list_upcoming_trips/credentials.manifest.json +17 -0
  118. package/examples/southwest/list_upcoming_trips/index.ts +123 -0
  119. package/examples/southwest/list_upcoming_trips/package.json +9 -0
  120. package/examples/southwest/list_upcoming_trips/parser.ts +160 -0
  121. package/examples/southwest/list_upcoming_trips/playbook.yaml +24 -0
  122. package/examples/southwest/list_upcoming_trips/workflow.json +78 -0
  123. package/examples/southwest/search_flights/backends.json +21 -0
  124. package/examples/southwest/search_flights/index.ts +150 -0
  125. package/examples/southwest/search_flights/package.json +9 -0
  126. package/examples/southwest/search_flights/parser.ts +214 -0
  127. package/examples/southwest/search_flights/playbook.yaml +52 -0
  128. package/examples/southwest/search_flights/workflow.json +88 -0
  129. package/package.json +1 -1
  130. package/prompts/audit-agent.md +2 -1
  131. package/prompts/build-planning.md +22 -1
  132. package/prompts/compile-agent.md +66 -53
  133. package/prompts/prereq-builder.md +3 -1
  134. package/prompts/tool-planning.md +2 -0
  135. package/src/cli.ts +8 -3
  136. package/src/imprint/audit.ts +337 -44
  137. package/src/imprint/backend-cache.ts +114 -0
  138. package/src/imprint/backend-ladder.ts +74 -24
  139. package/src/imprint/bot-defense.ts +12 -0
  140. package/src/imprint/build-plan.ts +346 -53
  141. package/src/imprint/cdp-browser-fetch.ts +96 -43
  142. package/src/imprint/cdp-jar-cache.ts +6 -5
  143. package/src/imprint/chromium.ts +34 -0
  144. package/src/imprint/codex-cli-compile.ts +13 -2
  145. package/src/imprint/compile-agent.ts +3 -1
  146. package/src/imprint/compile-tools.ts +715 -138
  147. package/src/imprint/compile.ts +41 -9
  148. package/src/imprint/concurrency.ts +26 -3
  149. package/src/imprint/cron.ts +0 -3
  150. package/src/imprint/endpoint-key.ts +23 -0
  151. package/src/imprint/llm-url.ts +69 -0
  152. package/src/imprint/llm.ts +366 -47
  153. package/src/imprint/login.ts +83 -75
  154. package/src/imprint/mcp-compile-server.ts +4 -1
  155. package/src/imprint/mcp-server.ts +51 -16
  156. package/src/imprint/param-grounding.ts +61 -52
  157. package/src/imprint/probe-backends.ts +21 -115
  158. package/src/imprint/redact.ts +17 -15
  159. package/src/imprint/request-capture.ts +94 -0
  160. package/src/imprint/runtime.ts +1 -78
  161. package/src/imprint/session-diff.ts +14 -1
  162. package/src/imprint/stealth-fetch.ts +30 -20
  163. package/src/imprint/stealth-token-cache.ts +2 -2
  164. package/src/imprint/teach-plan.ts +2 -0
  165. package/src/imprint/teach-state.ts +14 -9
  166. package/src/imprint/teach.ts +41 -7
  167. package/src/imprint/telemetry.ts +3 -0
  168. package/src/imprint/tool-candidates.ts +8 -7
  169. package/src/imprint/tool-plan.ts +29 -3
  170. package/src/imprint/tracing.ts +18 -2
  171. package/examples/google-flights/_shared/batchexecute.ts +0 -63
  172. package/examples/google-flights/_shared/flights_request.ts +0 -97
  173. package/examples/google-flights/get_flight_booking_details/index.ts +0 -159
  174. package/examples/google-flights/get_flight_booking_details/parser.ts +0 -182
  175. package/examples/google-flights/get_flight_booking_details/playbook.yaml +0 -138
  176. package/examples/google-flights/get_flight_booking_details/request-transform.ts +0 -86
  177. package/examples/google-flights/get_flight_booking_details/workflow.json +0 -98
  178. package/examples/google-flights/get_flight_calendar_prices/request-transform.ts +0 -31
  179. package/examples/google-flights/lookup_airport/parser.ts +0 -66
  180. package/examples/google-flights/lookup_airport/playbook.yaml +0 -47
  181. package/examples/google-flights/lookup_airport/request-transform.ts +0 -20
  182. package/examples/google-hotels/_shared/batchexecute.ts +0 -73
  183. package/examples/google-hotels/_shared/freq.ts +0 -158
  184. package/examples/google-hotels/autocomplete_hotel_location/parser.ts +0 -71
  185. package/examples/google-hotels/autocomplete_hotel_location/playbook.yaml +0 -36
  186. package/examples/google-hotels/autocomplete_hotel_location/request-transform.ts +0 -37
  187. package/examples/google-hotels/autocomplete_hotel_location/workflow.json +0 -36
  188. package/examples/google-hotels/get_hotel_booking_options/index.ts +0 -143
  189. package/examples/google-hotels/get_hotel_booking_options/parser.ts +0 -271
  190. package/examples/google-hotels/get_hotel_booking_options/playbook.yaml +0 -154
  191. package/examples/google-hotels/get_hotel_booking_options/request-transform.ts +0 -154
  192. package/examples/google-hotels/get_hotel_booking_options/workflow.json +0 -84
  193. package/examples/google-hotels/get_hotel_reviews/request-transform.ts +0 -42
  194. package/examples/southwest/search_southwest_flights/backends.json +0 -23
  195. package/examples/southwest/search_southwest_flights/cron.json +0 -19
  196. package/examples/southwest/search_southwest_flights/index.ts +0 -127
  197. package/examples/southwest/search_southwest_flights/playbook.yaml +0 -46
  198. package/examples/southwest/search_southwest_flights/workflow.json +0 -71
  199. /package/examples/google-flights/{get_flight_booking_details → get_flight_booking_options}/package.json +0 -0
  200. /package/examples/google-flights/{lookup_airport → get_flight_location_details}/package.json +0 -0
  201. /package/examples/google-hotels/{autocomplete_hotel_location → get_hotel_details}/package.json +0 -0
  202. /package/examples/google-hotels/{get_hotel_booking_options → get_hotel_photos}/package.json +0 -0
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * GENERATED by `imprint emit` — DO NOT EDIT BY HAND.
3
3
  *
4
- * Tool: lookup_airport
4
+ * Tool: get_flight_location_details
5
5
  * Site: google-flights
6
- * Intent: Resolve a city or airport query into airport codes and structured airport details.
6
+ * Intent: Resolve a selected airport, city, or place into detailed Google Flights location metadata.
7
7
  *
8
- * To regenerate: imprint emit ~/.imprint/google-flights/lookup_airport/workflow.json --force
8
+ * To regenerate: imprint emit ~/.imprint/google-flights/get_flight_location_details/workflow.json --force
9
9
  */
10
10
 
11
11
  import { fileURLToPath } from 'node:url';
@@ -17,23 +17,31 @@ import {
17
17
  import type { ToolResult, Workflow } from 'imprint/types';
18
18
 
19
19
  const WORKFLOW: Workflow = {
20
- "toolName": "lookup_airport",
20
+ "toolName": "get_flight_location_details",
21
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"
22
+ "description": "Resolve a selected airport, city, or place into detailed Google Flights location metadata.",
23
+ "userSaid": "searched for a multi city flight; multi city from SJC to SAN to LAX to SFO"
24
24
  },
25
25
  "parameters": [
26
26
  {
27
- "name": "query",
27
+ "name": "location_id",
28
28
  "type": "string",
29
- "description": "City or airport search text, e.g. 'san jose' or 'SJC'",
29
+ "description": "Airport code such as SJC or Google place id such as /m/0d9jr.",
30
+ "default": "SJC",
31
+ "verified": true
32
+ },
33
+ {
34
+ "name": "location_type",
35
+ "type": "string",
36
+ "description": "Google Flights location type code: use 0 for airport_code or 5 for google_place_id/city place ids.",
37
+ "default": "0",
30
38
  "verified": true
31
39
  }
32
40
  ],
33
41
  "requests": [
34
42
  {
35
43
  "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",
44
+ "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=${generated.nonce}&rt=c",
37
45
  "headers": {
38
46
  "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
39
47
  "X-Same-Domain": "1",
@@ -41,7 +49,7 @@ const WORKFLOW: Workflow = {
41
49
  "Accept-Language": "en-US,en;q=0.9",
42
50
  "x-goog-ext-259736195-jspb": "[\"en-US\",\"US\",\"USD\",2,null,[420],null,null,7,[]]"
43
51
  },
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&",
52
+ "body": "f.req=%5B%5B%5B%22tDoGIe%22%2C%22%5Bnull%2C%5B%5B%5C%22${param.location_id}%5C%22%2C${param.location_type}%5D%5D%5D%22%2Cnull%2C%22generic%22%5D%5D%5D&",
45
53
  "effect": "safe"
46
54
  }
47
55
  ],
@@ -49,14 +57,13 @@ const WORKFLOW: Workflow = {
49
57
  "bootstrap": {
50
58
  "url": "https://www.google.com/travel/flights",
51
59
  "waitUntil": "domcontentloaded",
52
- "timeoutMs": 30000,
53
60
  "captures": [
54
61
  {
55
62
  "name": "f_sid",
56
63
  "required": true,
57
64
  "capability": "browser_bootstrap",
58
65
  "source": "html_regex",
59
- "pattern": "\"FdrFJe\":\"([^\"]+)\"",
66
+ "pattern": "\\\"FdrFJe\\\":\\\"([^\\\"]+)\\\"",
60
67
  "group": 1
61
68
  },
62
69
  {
@@ -64,28 +71,31 @@ const WORKFLOW: Workflow = {
64
71
  "required": true,
65
72
  "capability": "browser_bootstrap",
66
73
  "source": "html_regex",
67
- "pattern": "\"cfb2h\":\"([^\"]+)\"",
74
+ "pattern": "\\\"cfb2h\\\":\\\"([^\\\"]+)\\\"",
68
75
  "group": 1
69
76
  }
70
77
  ]
71
78
  },
72
79
  "parserModule": "./parser.ts",
73
- "requestTransformModule": "./request-transform.ts",
80
+ "requestTransformModule": "../_shared/google_flights_transport.ts",
74
81
  "liveVerified": true
75
82
  };
76
83
 
77
- export interface LookupAirportInput {
78
- /** City or airport search text, e.g. 'san jose' or 'SJC' */
79
- query: string;
84
+ export interface GetFlightLocationDetailsInput {
85
+ /** Airport code such as SJC or Google place id such as /m/0d9jr. */
86
+ location_id?: string;
87
+ /** Google Flights location type code: use 0 for airport_code or 5 for google_place_id/city place ids. */
88
+ location_type?: string;
80
89
  }
81
90
 
82
- export async function lookupAirport(
83
- input: LookupAirportInput,
91
+ export async function getFlightLocationDetails(
92
+ input: GetFlightLocationDetailsInput,
84
93
  opts: { credentials?: CredentialStore; fetchImpl?: typeof fetch; initialState?: Record<string, unknown> } = {},
85
94
  ): Promise<ToolResult> {
86
95
  const __dirname = dirname(fileURLToPath(import.meta.url));
87
96
  const params: Record<string, string | number | boolean> = {
88
- query: input.query,
97
+ location_id: input.location_id ?? "SJC",
98
+ location_type: input.location_type ?? "0",
89
99
 
90
100
  };
91
101
  return executeWorkflow({
@@ -0,0 +1,103 @@
1
+ import { extractWrbRecords, parseNestedPayload } from '../_shared/google_batchexecute_parser.ts';
2
+ import type { FlightLocation } from '../_shared/google_flights_types.ts';
3
+
4
+ type Output = {
5
+ locations: FlightLocation[];
6
+ location?: FlightLocation;
7
+ };
8
+
9
+ function asArray(value: unknown): unknown[] | undefined {
10
+ return Array.isArray(value) ? value : undefined;
11
+ }
12
+
13
+ function asString(value: unknown): string | undefined {
14
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
15
+ }
16
+
17
+ function asNumber(value: unknown): number | undefined {
18
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
19
+ }
20
+
21
+ function apiTypeToName(type: unknown): string {
22
+ if (type === 0) return 'airport_code';
23
+ if (type === 4) return 'city';
24
+ if (type === 5) return 'google_place_id';
25
+ return typeof type === 'number' ? `google_flights_type_${type}` : 'unknown';
26
+ }
27
+
28
+ function imageUrls(details: unknown[]): string[] {
29
+ const images = asArray(details[2]) ?? [];
30
+ const urls: string[] = [];
31
+ for (const image of images) {
32
+ const nested = asArray(image);
33
+ const url = nested ? asString(nested[0]) : asString(image);
34
+ if (url) urls.push(url);
35
+ }
36
+ return urls;
37
+ }
38
+
39
+ function parseLocation(record: unknown): FlightLocation | undefined {
40
+ const item = asArray(record);
41
+ if (!item) return undefined;
42
+
43
+ const idPair = asArray(item[0]) ?? [];
44
+ const id = asString(idPair[0]);
45
+ const apiType = idPair[1];
46
+ const displayName = asString(item[1]);
47
+ const details = asArray(item[2]) ?? [];
48
+ const coordinatesRaw = asArray(item[3]) ?? [];
49
+ const lat = asNumber(coordinatesRaw[0]);
50
+ const lng = asNumber(coordinatesRaw[1]);
51
+
52
+ if (!id && !displayName) return undefined;
53
+
54
+ const detailId = asString(details[0]);
55
+ const detailName = asString(details[1]);
56
+ const airportCode = apiType === 0 ? id : asString(details[5]);
57
+ const placeId = id?.startsWith('/m/') ? id : detailId?.startsWith('/m/') ? detailId : undefined;
58
+
59
+ const location: FlightLocation = {
60
+ id: id ?? detailId ?? displayName ?? '',
61
+ type: apiTypeToName(apiType),
62
+ displayName: displayName ?? detailName ?? id ?? '',
63
+ };
64
+
65
+ if (detailName && detailName !== location.displayName) location.city = detailName;
66
+ if (asString(item[6])) location.region = asString(item[6]);
67
+ if (asString(details[3])) location.description = asString(details[3]);
68
+ if (airportCode) location.airportCode = airportCode;
69
+ if (placeId) location.placeId = placeId;
70
+ if (lat !== undefined && lng !== undefined) location.coordinates = { lat, lng };
71
+
72
+ const urls = imageUrls(details);
73
+ if (urls.length > 0) location.imageUrls = urls;
74
+
75
+ return location;
76
+ }
77
+
78
+ function payloadFromRaw(rawResponse: unknown): unknown {
79
+ if (typeof rawResponse === 'string') {
80
+ const records = extractWrbRecords(rawResponse).filter((record) => record.rpcid === 'tDoGIe');
81
+ return parseNestedPayload(records[0]?.payload);
82
+ }
83
+ return parseNestedPayload(rawResponse);
84
+ }
85
+
86
+ export function extract(rawResponse: unknown): Output {
87
+ const payload = payloadFromRaw(rawResponse);
88
+ const root = asArray(payload) ?? [];
89
+ const locationsRaw = asArray(root[1]) ?? [];
90
+ const locations = locationsRaw
91
+ .map(parseLocation)
92
+ .filter((location): location is FlightLocation => Boolean(location && location.id && location.displayName));
93
+
94
+ if (locations.length > 1) {
95
+ const [primary, ...rest] = locations;
96
+ if (primary) primary.nestedAirports = rest;
97
+ }
98
+
99
+ return {
100
+ locations,
101
+ location: locations[0],
102
+ };
103
+ }
@@ -0,0 +1,59 @@
1
+ toolName: get_flight_location_details
2
+ summary: Resolve a selected Google Flights airport, city, or place into normalized location metadata.
3
+ parameters:
4
+ - name: location_id
5
+ type: string
6
+ description: Airport code such as SJC or place id such as /m/0d9jr.
7
+ - name: location_type
8
+ type: string
9
+ description: Location id type, such as airport_code or google_place_id.
10
+ steps:
11
+ - action: navigate
12
+ url: https://www.google.com/travel/flights
13
+ wait_for: networkidle
14
+ - action: click
15
+ locators:
16
+ - by: role
17
+ value: textbox
18
+ name: Where from?
19
+ - by: aria_label
20
+ value_pattern: Where from
21
+ - by: text
22
+ value_pattern: Where from
23
+ - by: css
24
+ value: input[aria-label^="Where from"]
25
+ wait_for:
26
+ sleep_ms: 300
27
+ - action: type
28
+ locators:
29
+ - by: role
30
+ value: textbox
31
+ name: Where else?
32
+ - by: aria_label
33
+ value_pattern: Where else
34
+ - by: aria_label
35
+ value_pattern: Where from
36
+ - by: css
37
+ value: input[type="text"]
38
+ value: ${location_id}
39
+ wait_for:
40
+ xhr: rpcids=H028ib
41
+ - action: click
42
+ locators:
43
+ - by: role
44
+ value: option
45
+ name: ${location_id}
46
+ - by: aria_label
47
+ value_pattern: ${location_id}
48
+ - by: text
49
+ value_pattern: ${location_id}
50
+ - by: css
51
+ value: ul.DFGgtd li
52
+ wait_for:
53
+ xhr: rpcids=tDoGIe
54
+ result:
55
+ source: xhr
56
+ url_pattern: rpcids=tDoGIe
57
+ extract: "0.2"
58
+ return_as: location_details
59
+ notes: The selected-location response is a Google batchexecute payload; downstream parsing should strip the JSON prefix, find the wrb.fr record for tDoGIe, then parse its inner JSON string for coordinates, image URLs, display names, descriptions, canonical IDs, and country metadata.
@@ -1,27 +1,34 @@
1
1
  {
2
- "toolName": "lookup_airport",
2
+ "toolName": "get_flight_location_details",
3
3
  "intent": {
4
- "description": "Resolve a city or airport query into airport codes and structured airport details.",
5
- "userSaid": "multi city from SJC to SAN to LAX to SFO"
4
+ "description": "Resolve a selected airport, city, or place into detailed Google Flights location metadata.",
5
+ "userSaid": "searched for a multi city flight; multi city from SJC to SAN to LAX to SFO"
6
6
  },
7
7
  "site": "google-flights",
8
8
  "parameters": [
9
9
  {
10
- "name": "query",
10
+ "name": "location_id",
11
11
  "type": "string",
12
- "description": "City or airport search text, e.g. 'san jose' or 'SJC'",
12
+ "description": "Airport code such as SJC or Google place id such as /m/0d9jr.",
13
+ "default": "SJC",
14
+ "verified": true
15
+ },
16
+ {
17
+ "name": "location_type",
18
+ "type": "string",
19
+ "description": "Google Flights location type code: use 0 for airport_code or 5 for google_place_id/city place ids.",
20
+ "default": "0",
13
21
  "verified": true
14
22
  }
15
23
  ],
16
24
  "bootstrap": {
17
25
  "url": "https://www.google.com/travel/flights",
18
26
  "waitUntil": "domcontentloaded",
19
- "timeoutMs": 30000,
20
27
  "captures": [
21
28
  {
22
29
  "source": "html_regex",
23
30
  "name": "f_sid",
24
- "pattern": "\"FdrFJe\":\"([^\"]+)\"",
31
+ "pattern": "\\\"FdrFJe\\\":\\\"([^\\\"]+)\\\"",
25
32
  "group": 1,
26
33
  "required": true,
27
34
  "capability": "browser_bootstrap"
@@ -29,17 +36,19 @@
29
36
  {
30
37
  "source": "html_regex",
31
38
  "name": "bl",
32
- "pattern": "\"cfb2h\":\"([^\"]+)\"",
39
+ "pattern": "\\\"cfb2h\\\":\\\"([^\\\"]+)\\\"",
33
40
  "group": 1,
34
41
  "required": true,
35
42
  "capability": "browser_bootstrap"
36
43
  }
37
44
  ]
38
45
  },
46
+ "requestTransformModule": "../_shared/google_flights_transport.ts",
47
+ "parserModule": "./parser.ts",
39
48
  "requests": [
40
49
  {
41
50
  "method": "POST",
42
- "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",
51
+ "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=${generated.nonce}&rt=c",
43
52
  "headers": {
44
53
  "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
45
54
  "X-Same-Domain": "1",
@@ -47,11 +56,9 @@
47
56
  "Accept-Language": "en-US,en;q=0.9",
48
57
  "x-goog-ext-259736195-jspb": "[\"en-US\",\"US\",\"USD\",2,null,[420],null,null,7,[]]"
49
58
  },
50
- "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&",
59
+ "body": "f.req=%5B%5B%5B%22tDoGIe%22%2C%22%5Bnull%2C%5B%5B%5C%22${param.location_id}%5C%22%2C${param.location_type}%5D%5D%5D%22%2Cnull%2C%22generic%22%5D%5D%5D&",
51
60
  "effect": "safe"
52
61
  }
53
62
  ],
54
- "parserModule": "./parser.ts",
55
- "requestTransformModule": "./request-transform.ts",
56
63
  "liveVerified": true
57
64
  }
@@ -0,0 +1,27 @@
1
+ {
2
+ "probedAt": "2026-07-01T01:16:34.053Z",
3
+ "imprintVersion": "0.5.0",
4
+ "schemaVersion": 2,
5
+ "workflowHash": "0540c5b796bac7278bf655a5f0dc27c2508e08483eed2cd9453be4356b8b69cf",
6
+ "capabilityHash": "5fc16811cd65aff7afee349ddca05c806a65c9c2c96948192ea1bafba9284c38",
7
+ "preferredOrder": [
8
+ "cdp-replay"
9
+ ],
10
+ "results": {
11
+ "fetch": {
12
+ "outcome": "failed",
13
+ "durationMs": 18,
14
+ "error": "STATE_MISSING",
15
+ "detail": "STATE_MISSING: Workflow placeholder ${state.bl} but state \"bl\" has not been captured yet"
16
+ },
17
+ "fetch-bootstrap": {
18
+ "outcome": "forbidden",
19
+ "durationMs": 30187,
20
+ "detail": "FORBIDDEN: fetch-bootstrap: cdp-minted jar did not validate; cdp-replay (in-page) required."
21
+ },
22
+ "cdp-replay": {
23
+ "outcome": "ok",
24
+ "durationMs": 470
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * GENERATED by `imprint emit` — DO NOT EDIT BY HAND.
3
+ *
4
+ * Tool: lookup_flight_locations
5
+ * Site: google-flights
6
+ * Intent: Search Google Flights location autocomplete for airports, cities, and travel places by text query.
7
+ *
8
+ * To regenerate: imprint emit ~/.imprint/google-flights/lookup_flight_locations/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_flight_locations",
21
+ "intent": {
22
+ "description": "Search Google Flights location autocomplete for airports, cities, and travel places by text query.",
23
+ "userSaid": "searched for a round trip flight; multi city from SJC to SAN to LAX to SFO; searched for a multi city flight"
24
+ },
25
+ "parameters": [
26
+ {
27
+ "name": "query",
28
+ "type": "string",
29
+ "description": "Partial or full airport, city, or place query typed by the user.",
30
+ "default": "san di",
31
+ "verified": true
32
+ },
33
+ {
34
+ "name": "location_types",
35
+ "type": "string",
36
+ "description": "Optional type filter. Use default for recorded Google Flights location types, or all to include the expanded region/county type.",
37
+ "default": "default",
38
+ "verified": true
39
+ }
40
+ ],
41
+ "requests": [
42
+ {
43
+ "method": "POST",
44
+ "url": "https://www.google.com/_/FlightsFrontendUi/data/batchexecute?rpcids=H028ib&source-path=%2Ftravel%2Fflights&f.sid=${state.f_sid}&bl=${state.bl}&hl=en-US&soc-app=162&soc-platform=1&soc-device=1&rt=c",
45
+ "headers": {
46
+ "X-Same-Domain": "1",
47
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
48
+ "Referer": "https://www.google.com/travel/flights",
49
+ "x-goog-ext-259736195-jspb": "[\"en-US\",\"US\",\"USD\",2,null,[420],null,null,7,[]]"
50
+ },
51
+ "body": "f.req=${param.query}&location_types=${param.location_types}",
52
+ "effect": "safe"
53
+ }
54
+ ],
55
+ "site": "google-flights",
56
+ "bootstrap": {
57
+ "url": "https://www.google.com/travel/flights",
58
+ "waitUntil": "load",
59
+ "waitMs": 1000,
60
+ "captures": [
61
+ {
62
+ "name": "f_sid",
63
+ "required": true,
64
+ "capability": "browser_bootstrap",
65
+ "source": "html_regex",
66
+ "pattern": "(?:\\\\?\"|&quot;)FdrFJe(?:\\\\?\"|&quot;)\\s*:\\s*(?:\\\\?\"|&quot;)(-?[0-9]+)",
67
+ "group": 1
68
+ },
69
+ {
70
+ "name": "bl",
71
+ "required": true,
72
+ "capability": "browser_bootstrap",
73
+ "source": "html_regex",
74
+ "pattern": "(?:\\\\?\"|&quot;)cfb2h(?:\\\\?\"|&quot;)\\s*:\\s*(?:\\\\?\"|&quot;)(boq_travel-frontend-flights-ui_[^\\\\\"&]+)",
75
+ "group": 1
76
+ }
77
+ ]
78
+ },
79
+ "parserModule": "./parser.ts",
80
+ "requestTransformModule": "./request-transform.ts",
81
+ "liveVerified": true
82
+ };
83
+
84
+ export interface LookupFlightLocationsInput {
85
+ /** Partial or full airport, city, or place query typed by the user. */
86
+ query?: string;
87
+ /** Optional type filter. Use default for recorded Google Flights location types, or all to include the expanded region/county type. */
88
+ location_types?: string;
89
+ }
90
+
91
+ export async function lookupFlightLocations(
92
+ input: LookupFlightLocationsInput,
93
+ opts: { credentials?: CredentialStore; fetchImpl?: typeof fetch; initialState?: Record<string, unknown> } = {},
94
+ ): Promise<ToolResult> {
95
+ const __dirname = dirname(fileURLToPath(import.meta.url));
96
+ const params: Record<string, string | number | boolean> = {
97
+ query: input.query ?? "san di",
98
+ location_types: input.location_types ?? "default",
99
+
100
+ };
101
+ return executeWorkflow({
102
+ workflow: WORKFLOW,
103
+ params,
104
+ credentials: opts.credentials,
105
+ fetchImpl: opts.fetchImpl,
106
+ initialState: opts.initialState,
107
+ workflowPath: join(__dirname, 'workflow.json'),
108
+ });
109
+ }
110
+
111
+ export { WORKFLOW };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "imprint-tool-google-flights",
3
+ "private": true,
4
+ "devDependencies": {
5
+ "@types/bun": "latest",
6
+ "@types/node": "latest",
7
+ "bun-types": "latest"
8
+ }
9
+ }
@@ -0,0 +1,135 @@
1
+ import { extractWrbRecords, parseNestedPayload } from '../_shared/google_batchexecute_parser.ts';
2
+ import type { FlightLocation } from '../_shared/google_flights_types.ts';
3
+
4
+ type LookupResult = {
5
+ query?: string;
6
+ items: FlightLocation[];
7
+ };
8
+
9
+ function asArray(value: unknown): unknown[] | undefined {
10
+ return Array.isArray(value) ? value : undefined;
11
+ }
12
+
13
+ function asString(value: unknown): string | undefined {
14
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
15
+ }
16
+
17
+ function asNumber(value: unknown): number | undefined {
18
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
19
+ }
20
+
21
+ function typeName(typeCode: unknown): string {
22
+ switch (typeCode) {
23
+ case 1:
24
+ return 'airport';
25
+ case 3:
26
+ return 'city';
27
+ case 4:
28
+ return 'region';
29
+ case 5:
30
+ return 'station';
31
+ default:
32
+ return `type_${String(typeCode ?? 'unknown')}`;
33
+ }
34
+ }
35
+
36
+ function parseTuple(tuple: unknown, distanceText?: unknown): (FlightLocation & { score?: number; distance?: string }) | null {
37
+ const values = asArray(tuple);
38
+ if (!values) return null;
39
+
40
+ const type = typeName(values[0]);
41
+ const displayName = asString(values[1]);
42
+ const secondaryName = asString(values[2]);
43
+ const description = asString(values[3]);
44
+ const placeId = asString(values[4]);
45
+ const airportCode = asString(values[5]) ?? asString(values[8]);
46
+ const id = airportCode ?? placeId ?? displayName;
47
+
48
+ if (!id || !displayName) return null;
49
+
50
+ const location: FlightLocation & { score?: number; distance?: string } = {
51
+ id,
52
+ type,
53
+ displayName,
54
+ };
55
+
56
+ if (type === 'region') {
57
+ if (secondaryName) location.region = secondaryName;
58
+ } else if (secondaryName) {
59
+ location.city = secondaryName;
60
+ }
61
+ if (description) location.description = description;
62
+ if (airportCode) location.airportCode = airportCode;
63
+ if (placeId) location.placeId = placeId;
64
+ const score = asNumber(values[12]);
65
+ if (score !== undefined) location.score = score;
66
+ const distance = asString(distanceText);
67
+ if (distance) location.distance = distance;
68
+
69
+ return location;
70
+ }
71
+
72
+ function parseNestedAirports(value: unknown): FlightLocation[] | undefined {
73
+ const options = asArray(value);
74
+ if (!options) return undefined;
75
+
76
+ const nested = options
77
+ .map((option) => {
78
+ const parts = asArray(option);
79
+ if (!parts) return null;
80
+ return parseTuple(parts[0], parts[1]);
81
+ })
82
+ .filter((item): item is FlightLocation => Boolean(item));
83
+
84
+ return nested.length > 0 ? nested : undefined;
85
+ }
86
+
87
+ function unwrapRows(payload: unknown): unknown[] {
88
+ let parsed = payload;
89
+ for (let i = 0; i < 4; i += 1) {
90
+ parsed = parseNestedPayload(parsed);
91
+ }
92
+
93
+ const top = asArray(parsed);
94
+ return asArray(top?.[0]) ?? [];
95
+ }
96
+
97
+ export function extract(
98
+ rawResponse: unknown,
99
+ context?: { params: Record<string, string | number | boolean>; responses: unknown[] },
100
+ ): LookupResult {
101
+ const query = typeof context?.params?.query === 'string' ? context.params.query : undefined;
102
+
103
+ if (typeof rawResponse !== 'string') {
104
+ const rows = unwrapRows(rawResponse);
105
+ return {
106
+ query,
107
+ items: rows.map(parseRow).filter((item): item is FlightLocation => Boolean(item)),
108
+ };
109
+ }
110
+
111
+ const records = extractWrbRecords(rawResponse);
112
+ const record = records.find((entry) => entry.rpcid === 'H028ib');
113
+ if (!record) {
114
+ throw new Error('Google Flights location lookup response did not include H028ib wrb.fr data');
115
+ }
116
+
117
+ const rows = unwrapRows(record.rawPayload ?? record.payload);
118
+ return {
119
+ query,
120
+ items: rows.map(parseRow).filter((item): item is FlightLocation => Boolean(item)),
121
+ };
122
+ }
123
+
124
+ function parseRow(row: unknown): FlightLocation | null {
125
+ const parts = asArray(row);
126
+ if (!parts) return null;
127
+
128
+ const location = parseTuple(parts[0]);
129
+ if (!location) return null;
130
+
131
+ const nestedAirports = parseNestedAirports(parts[1]);
132
+ if (nestedAirports) location.nestedAirports = nestedAirports;
133
+
134
+ return location;
135
+ }