imprint-mcp 0.2.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 (97) hide show
  1. package/CHANGELOG.md +168 -0
  2. package/LICENSE +21 -0
  3. package/README.md +322 -0
  4. package/examples/discoverandgo/README.md +57 -0
  5. package/examples/discoverandgo/book_discoverandgo_museum_pass/cron.json +8 -0
  6. package/examples/discoverandgo/book_discoverandgo_museum_pass/index.ts +89 -0
  7. package/examples/discoverandgo/book_discoverandgo_museum_pass/workflow.json +39 -0
  8. package/examples/echo/README.md +37 -0
  9. package/examples/echo/echo_test/index.ts +31 -0
  10. package/examples/google-flights/search_google_flights/index.ts +101 -0
  11. package/examples/google-flights/search_google_flights/parser.test.ts +140 -0
  12. package/examples/google-flights/search_google_flights/parser.ts +189 -0
  13. package/examples/google-flights/search_google_flights/playbook.yaml +130 -0
  14. package/examples/google-flights/search_google_flights/workflow.json +48 -0
  15. package/examples/google-hotels/search_google_hotels/index.ts +194 -0
  16. package/examples/google-hotels/search_google_hotels/parser.test.ts +168 -0
  17. package/examples/google-hotels/search_google_hotels/parser.ts +330 -0
  18. package/examples/google-hotels/search_google_hotels/playbook.yaml +125 -0
  19. package/examples/google-hotels/search_google_hotels/workflow.json +111 -0
  20. package/examples/namecheap-domains/search_namecheap_domains/index.ts +144 -0
  21. package/examples/namecheap-domains/search_namecheap_domains/parser.ts +380 -0
  22. package/examples/namecheap-domains/search_namecheap_domains/playbook.yaml +50 -0
  23. package/examples/namecheap-domains/search_namecheap_domains/request-transform.ts +136 -0
  24. package/examples/namecheap-domains/search_namecheap_domains/workflow.json +97 -0
  25. package/examples/southwest/README.md +81 -0
  26. package/examples/southwest/search_southwest_flights/backends.json +23 -0
  27. package/examples/southwest/search_southwest_flights/cron.json +19 -0
  28. package/examples/southwest/search_southwest_flights/index.ts +110 -0
  29. package/examples/southwest/search_southwest_flights/playbook.yaml +46 -0
  30. package/examples/southwest/search_southwest_flights/workflow.json +54 -0
  31. package/package.json +78 -0
  32. package/prompts/compile-agent.md +580 -0
  33. package/prompts/intent-detection.md +198 -0
  34. package/prompts/playbook-compilation.md +279 -0
  35. package/prompts/request-triage.md +74 -0
  36. package/prompts/tool-candidate-detection.md +104 -0
  37. package/src/cli.ts +1287 -0
  38. package/src/imprint/agent.ts +468 -0
  39. package/src/imprint/app-api-hosts.ts +53 -0
  40. package/src/imprint/backend-ladder.ts +568 -0
  41. package/src/imprint/check.ts +136 -0
  42. package/src/imprint/chromium.ts +211 -0
  43. package/src/imprint/claude-cli-compile.ts +640 -0
  44. package/src/imprint/cli-credential.ts +394 -0
  45. package/src/imprint/codex-cli-compile.ts +712 -0
  46. package/src/imprint/compile-agent-types.ts +40 -0
  47. package/src/imprint/compile-agent.ts +404 -0
  48. package/src/imprint/compile-tools.ts +1389 -0
  49. package/src/imprint/compile.ts +720 -0
  50. package/src/imprint/cookie-jar.ts +246 -0
  51. package/src/imprint/credential-bundle.ts +195 -0
  52. package/src/imprint/credential-extract.ts +290 -0
  53. package/src/imprint/credential-store.ts +707 -0
  54. package/src/imprint/cron.ts +312 -0
  55. package/src/imprint/doctor.ts +223 -0
  56. package/src/imprint/emit.ts +154 -0
  57. package/src/imprint/etld.ts +134 -0
  58. package/src/imprint/freeform-redact.ts +216 -0
  59. package/src/imprint/inject-listener.ts +137 -0
  60. package/src/imprint/install.ts +795 -0
  61. package/src/imprint/integrations.ts +385 -0
  62. package/src/imprint/is-compiled.ts +2 -0
  63. package/src/imprint/json-path.ts +100 -0
  64. package/src/imprint/llm.ts +998 -0
  65. package/src/imprint/load-json.ts +54 -0
  66. package/src/imprint/log.ts +33 -0
  67. package/src/imprint/login.ts +166 -0
  68. package/src/imprint/mcp-compile-server.ts +282 -0
  69. package/src/imprint/mcp-maintenance.ts +1790 -0
  70. package/src/imprint/mcp-server.ts +350 -0
  71. package/src/imprint/multi-progress.ts +69 -0
  72. package/src/imprint/notify.ts +155 -0
  73. package/src/imprint/paths.ts +64 -0
  74. package/src/imprint/playbook-parser.ts +21 -0
  75. package/src/imprint/playbook-runner.ts +465 -0
  76. package/src/imprint/probe-backends.ts +251 -0
  77. package/src/imprint/progress.ts +28 -0
  78. package/src/imprint/record.ts +470 -0
  79. package/src/imprint/redact.ts +550 -0
  80. package/src/imprint/replay-capture.ts +387 -0
  81. package/src/imprint/request-context.ts +66 -0
  82. package/src/imprint/runtime-link.ts +73 -0
  83. package/src/imprint/runtime.ts +942 -0
  84. package/src/imprint/sensitive-keys.ts +156 -0
  85. package/src/imprint/session-diff.ts +409 -0
  86. package/src/imprint/session-merge.ts +198 -0
  87. package/src/imprint/session-writer.ts +149 -0
  88. package/src/imprint/sites.ts +27 -0
  89. package/src/imprint/stealth-fetch.ts +434 -0
  90. package/src/imprint/teach-state.ts +235 -0
  91. package/src/imprint/teach.ts +2120 -0
  92. package/src/imprint/tool-candidates.ts +423 -0
  93. package/src/imprint/tool-loader.ts +186 -0
  94. package/src/imprint/tool-selection.ts +70 -0
  95. package/src/imprint/tracing.ts +508 -0
  96. package/src/imprint/types.ts +472 -0
  97. package/src/imprint/version.ts +21 -0
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Parser for Google Hotels search results (Travel Frontend UI batchexecute, rpcid AtySUc).
3
+ *
4
+ * The session captured a search for hotels in Tahoe City, June 19–27 2026,
5
+ * 1 room with adults + 2 children (ages 3 + 3). The raw response is Google's
6
+ * batchexecute envelope:
7
+ *
8
+ * )]}'
9
+ * <chunkSize>
10
+ * [["wrb.fr","AtySUc","<JSON-string of the actual JSPB payload>",null,null,null,"generic"]]
11
+ * <chunkSize>
12
+ * [["di",NNN], …]
13
+ *
14
+ * The payload is positional JSPB (no field names — values are addressed by
15
+ * array index). Hotels live in "section" sub-arrays under
16
+ * payload[0][0][0][1][i] = [section_type, { "<field_id>": <data> }]
17
+ * where section_type === 34 carries hotel records under key "397419284".
18
+ *
19
+ * Each hotel record is a 48-element array. Empirically observed indices:
20
+ * rec[1] string hotel name
21
+ * rec[2][0] [lat, lng]
22
+ * rec[3] ["3-star hotel", 3] star description + rating
23
+ * rec[6][1][4] [[Y,M,D],[Y,M,D],nights,rooms] search stay
24
+ * rec[6][2][1] [nightlyDisplay, nightlyDisplayHigher, nightlyValueA, null, nightlyValueB]
25
+ * rec[6][2][9] [totalDisplay, totalDisplayHigher]
26
+ * rec[7][0] [guestRating, reviewCount]
27
+ * rec[9] Maps internal place id "0x...:0x..."
28
+ * rec[11][0] description / tagline
29
+ * rec[12][0] primary photo URL
30
+ * rec[20] opaque hotel token "ChY..." (for hotel-detail fetches)
31
+ * rec[25] numeric Google hotel id
32
+ *
33
+ * Top-level metadata (search header + currency) is in section_type === 53
34
+ * under key "416343588" → [resultCount, …, destinationName, …].
35
+ */
36
+
37
+ export type Hotel = {
38
+ name: string;
39
+ latitude: number | null;
40
+ longitude: number | null;
41
+ starDescription: string | null;
42
+ starRating: number | null;
43
+ guestRating: number | null;
44
+ reviewCount: number | null;
45
+ nightlyPrice: string | null;
46
+ nightlyPriceValue: number | null;
47
+ totalPrice: string | null;
48
+ totalPriceValue: number | null;
49
+ description: string | null;
50
+ photoUrl: string | null;
51
+ hotelId: string | null;
52
+ hotelToken: string | null;
53
+ mapsFeatureId: string | null;
54
+ checkInDate: string | null;
55
+ checkOutDate: string | null;
56
+ nights: number | null;
57
+ };
58
+
59
+ export type ExtractResult = {
60
+ destination: string | null;
61
+ totalResults: number | null;
62
+ currency: string;
63
+ checkInDate: string | null;
64
+ checkOutDate: string | null;
65
+ nights: number | null;
66
+ hotelCount: number;
67
+ hotels: Hotel[];
68
+ };
69
+
70
+ // ─── helpers ───────────────────────────────────────────────────────────────
71
+
72
+ function dateTupleToIso(t: unknown): string | null {
73
+ if (!Array.isArray(t) || t.length < 3) return null;
74
+ const [y, m, d] = t as [unknown, unknown, unknown];
75
+ if (typeof y !== 'number' || typeof m !== 'number' || typeof d !== 'number') return null;
76
+ return `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
77
+ }
78
+
79
+ function asArray(x: unknown): unknown[] {
80
+ return Array.isArray(x) ? x : [];
81
+ }
82
+
83
+ function asNumberOrNull(x: unknown): number | null {
84
+ return typeof x === 'number' ? x : null;
85
+ }
86
+
87
+ function asStringOrNull(x: unknown): string | null {
88
+ return typeof x === 'string' && x.length > 0 ? x : null;
89
+ }
90
+
91
+ // ─── envelope parser ───────────────────────────────────────────────────────
92
+
93
+ /**
94
+ * Parse Google's batchexecute envelope. Yields the inner JSPB payload of the
95
+ * AtySUc wrb.fr row. Tolerant of off-by-N declared chunk lengths (Google's
96
+ * length counter occasionally counts something other than UTF-16 code units).
97
+ */
98
+ export function parseEnvelope(raw: string): { rpcid: unknown; payload: unknown } | null {
99
+ let s = raw;
100
+ if (s.startsWith(")]}'")) s = s.slice(4);
101
+ s = s.replace(/^\s+/, '');
102
+
103
+ let i = 0;
104
+ while (i < s.length) {
105
+ while (i < s.length && /\s/.test(s[i]!)) i++;
106
+ if (i >= s.length) break;
107
+ const lenStart = i;
108
+ while (i < s.length && /[0-9]/.test(s[i]!)) i++;
109
+ if (i === lenStart) break;
110
+ const declaredLen = parseInt(s.slice(lenStart, i), 10);
111
+ while (i < s.length && /\s/.test(s[i]!)) i++;
112
+ const chunkStart = i;
113
+
114
+ const chunk = sliceJsonChunk(s, chunkStart, declaredLen);
115
+ if (chunk === null) break;
116
+ i = chunkStart + chunk.length;
117
+
118
+ let parsed: unknown;
119
+ try {
120
+ parsed = JSON.parse(chunk);
121
+ } catch {
122
+ continue;
123
+ }
124
+
125
+ if (!Array.isArray(parsed)) continue;
126
+ for (const entry of parsed as unknown[]) {
127
+ if (!Array.isArray(entry)) continue;
128
+ const [tag, rpcid, payloadStr] = entry as unknown[];
129
+ if (tag === 'wrb.fr' && typeof payloadStr === 'string') {
130
+ try {
131
+ return { rpcid, payload: JSON.parse(payloadStr) };
132
+ } catch {
133
+ return { rpcid, payload: null };
134
+ }
135
+ }
136
+ }
137
+ }
138
+ return null;
139
+ }
140
+
141
+ function sliceJsonChunk(s: string, start: number, declaredLen: number): string | null {
142
+ const tryParse = (len: number): string | null => {
143
+ if (len < 1 || start + len > s.length) return null;
144
+ const c = s.slice(start, start + len);
145
+ try {
146
+ JSON.parse(c);
147
+ return c;
148
+ } catch {
149
+ return null;
150
+ }
151
+ };
152
+ const exact = tryParse(declaredLen);
153
+ if (exact !== null) return exact;
154
+ for (let d = 1; d <= 16; d++) {
155
+ const minus = tryParse(declaredLen - d);
156
+ if (minus !== null) return minus;
157
+ const plus = tryParse(declaredLen + d);
158
+ if (plus !== null) return plus;
159
+ }
160
+ return null;
161
+ }
162
+
163
+ // ─── hotel record extraction ───────────────────────────────────────────────
164
+
165
+ function extractHotel(rec: unknown[]): Hotel | null {
166
+ if (!Array.isArray(rec) || rec.length < 12) return null;
167
+ const name = asStringOrNull(rec[1]);
168
+ if (!name) return null;
169
+
170
+ // location
171
+ const locInfo = asArray(rec[2]);
172
+ const latLng = asArray(locInfo[0]);
173
+ const latitude = asNumberOrNull(latLng[0]);
174
+ const longitude = asNumberOrNull(latLng[1]);
175
+
176
+ // stars
177
+ const starInfo = asArray(rec[3]);
178
+ const starDescription = asStringOrNull(starInfo[0]);
179
+ const starRating = asNumberOrNull(starInfo[1]);
180
+
181
+ // pricing block — rec[6]
182
+ const priceBlock = asArray(rec[6]);
183
+ const stay = asArray(asArray(priceBlock[1])[4]);
184
+ const checkInDate = dateTupleToIso(stay[0]);
185
+ const checkOutDate = dateTupleToIso(stay[1]);
186
+ const nights = asNumberOrNull(stay[2]);
187
+
188
+ const priceArr = asArray(priceBlock[2]);
189
+ const nightlyTuple = asArray(priceArr[1]);
190
+ const nightlyPrice = asStringOrNull(nightlyTuple[0]);
191
+ const nightlyPriceValue =
192
+ asNumberOrNull(nightlyTuple[2]) ??
193
+ asNumberOrNull(nightlyTuple[4]);
194
+
195
+ const totalTuple = asArray(priceArr[9]);
196
+ const totalPrice = asStringOrNull(totalTuple[0]);
197
+ const totalPriceValue =
198
+ asNumberOrNull(totalTuple[2]) ?? asNumberOrNull(totalTuple[4]);
199
+
200
+ // ratings — rec[7][0] = [guestRating, reviewCount]
201
+ const ratingTuple = asArray(asArray(rec[7])[0]);
202
+ const guestRating = asNumberOrNull(ratingTuple[0]);
203
+ const reviewCount = asNumberOrNull(ratingTuple[1]);
204
+
205
+ // description / photo
206
+ const description = asStringOrNull(asArray(rec[11])[0]);
207
+ const photoUrl = asStringOrNull(asArray(rec[12])[0]);
208
+
209
+ // identifiers
210
+ const mapsFeatureId = asStringOrNull(rec[9]);
211
+ const hotelToken = asStringOrNull(rec[20]);
212
+ const hotelId = asStringOrNull(rec[25]);
213
+
214
+ return {
215
+ name,
216
+ latitude,
217
+ longitude,
218
+ starDescription,
219
+ starRating,
220
+ guestRating,
221
+ reviewCount,
222
+ nightlyPrice,
223
+ nightlyPriceValue,
224
+ totalPrice,
225
+ totalPriceValue,
226
+ description,
227
+ photoUrl,
228
+ hotelId,
229
+ hotelToken,
230
+ mapsFeatureId,
231
+ checkInDate,
232
+ checkOutDate,
233
+ nights,
234
+ };
235
+ }
236
+
237
+ /**
238
+ * Recursively walk the payload pulling out every "397419284" hotel record.
239
+ * The number of nesting levels is not perfectly stable across query types
240
+ * (recommended hotels vs additional hotels vs vacation rentals), so a key
241
+ * sweep is more robust than hard-coded paths.
242
+ */
243
+ function collectHotels(node: unknown, out: Hotel[], seen: Set<string>): void {
244
+ if (node === null || node === undefined) return;
245
+ if (Array.isArray(node)) {
246
+ for (const child of node) collectHotels(child, out, seen);
247
+ return;
248
+ }
249
+ if (typeof node !== 'object') return;
250
+ const obj = node as Record<string, unknown>;
251
+ for (const k of Object.keys(obj)) {
252
+ if (k === '397419284' && Array.isArray(obj[k])) {
253
+ const wrapper = obj[k] as unknown[];
254
+ for (const rec of wrapper) {
255
+ if (!Array.isArray(rec)) continue;
256
+ const h = extractHotel(rec);
257
+ if (!h) continue;
258
+ const key =
259
+ h.hotelId ??
260
+ h.hotelToken ??
261
+ `${h.name}|${h.latitude ?? ''}|${h.longitude ?? ''}`;
262
+ if (seen.has(key)) continue;
263
+ seen.add(key);
264
+ out.push(h);
265
+ }
266
+ } else {
267
+ collectHotels(obj[k], out, seen);
268
+ }
269
+ }
270
+ }
271
+
272
+ /** Find the search-meta section (type=53, key=416343588) anywhere in the tree. */
273
+ function findSearchMeta(
274
+ node: unknown,
275
+ ): { destination: string | null; totalResults: number | null } | null {
276
+ if (node === null || node === undefined) return null;
277
+ if (Array.isArray(node)) {
278
+ for (const child of node) {
279
+ const r = findSearchMeta(child);
280
+ if (r) return r;
281
+ }
282
+ return null;
283
+ }
284
+ if (typeof node !== 'object') return null;
285
+ const obj = node as Record<string, unknown>;
286
+ if (Array.isArray(obj['416343588'])) {
287
+ const arr = obj['416343588'] as unknown[];
288
+ return {
289
+ totalResults: asNumberOrNull(arr[0]),
290
+ destination: asStringOrNull(arr[2]),
291
+ };
292
+ }
293
+ for (const k of Object.keys(obj)) {
294
+ const r = findSearchMeta(obj[k]);
295
+ if (r) return r;
296
+ }
297
+ return null;
298
+ }
299
+
300
+ export function extract(rawResponse: unknown): ExtractResult {
301
+ let payload: unknown = rawResponse;
302
+ if (typeof rawResponse === 'string') {
303
+ const env = parseEnvelope(rawResponse);
304
+ payload = env?.payload;
305
+ }
306
+
307
+ const hotels: Hotel[] = [];
308
+ collectHotels(payload, hotels, new Set());
309
+
310
+ const meta = findSearchMeta(payload) ?? { destination: null, totalResults: null };
311
+
312
+ // Pull stay info from any hotel that has it (all hotels carry the same dates).
313
+ const sample = hotels.find(
314
+ (h) => h.checkInDate !== null && h.checkOutDate !== null,
315
+ );
316
+ const checkInDate = sample?.checkInDate ?? null;
317
+ const checkOutDate = sample?.checkOutDate ?? null;
318
+ const nights = sample?.nights ?? null;
319
+
320
+ return {
321
+ destination: meta.destination,
322
+ totalResults: meta.totalResults,
323
+ currency: 'USD',
324
+ checkInDate,
325
+ checkOutDate,
326
+ nights,
327
+ hotelCount: hotels.length,
328
+ hotels,
329
+ };
330
+ }
@@ -0,0 +1,125 @@
1
+ toolName: search_google_hotels
2
+ summary: Search Google Hotels for a destination, date range, and guest count, returning hotel names with stay-total prices.
3
+ parameters:
4
+ - name: destination
5
+ type: string
6
+ description: City or place name to search hotels in, e.g. "tahoe city"
7
+ - name: check_in_date
8
+ type: string
9
+ description: Check-in date as YYYY-MM-DD
10
+ - name: check_out_date
11
+ type: string
12
+ description: Check-out date as YYYY-MM-DD
13
+ - name: adults
14
+ type: number
15
+ description: Number of adult guests
16
+ default: 2
17
+ - name: children
18
+ type: number
19
+ description: Number of child guests
20
+ default: 0
21
+ steps:
22
+ - action: navigate
23
+ url: https://www.google.com/travel/search
24
+ wait_for: networkidle
25
+ - action: click
26
+ locators:
27
+ - by: aria_label
28
+ value: Search for places, hotels and more
29
+ - by: css
30
+ value: input.II2One.j0Ppje
31
+ wait_for:
32
+ sleep_ms: 300
33
+ - action: type
34
+ locators:
35
+ - by: aria_label
36
+ value: Search for places, hotels and more
37
+ - by: css
38
+ value: input.II2One.j0Ppje
39
+ value: ${destination}
40
+ wait_for:
41
+ sleep_ms: 800
42
+ - action: press
43
+ key: Enter
44
+ wait_for:
45
+ xhr: /TravelFrontendUi/data/batchexecute.*rpcids=AtySUc
46
+ - action: click
47
+ locators:
48
+ - by: aria_label
49
+ value: Check-in
50
+ - by: css
51
+ value: input.TP4Lpb.eoY5cb
52
+ wait_for:
53
+ sleep_ms: 500
54
+ - action: type
55
+ locators:
56
+ - by: aria_label
57
+ value: Check-in
58
+ value: ${check_in_date}
59
+ wait_for:
60
+ sleep_ms: 300
61
+ - action: type
62
+ locators:
63
+ - by: aria_label
64
+ value: Check-out
65
+ value: ${check_out_date}
66
+ wait_for:
67
+ sleep_ms: 300
68
+ - action: click
69
+ locators:
70
+ - by: text
71
+ value: Done
72
+ - by: role
73
+ value: button
74
+ name: Done
75
+ wait_for:
76
+ sleep_ms: 500
77
+ - action: click
78
+ locators:
79
+ - by: aria_label
80
+ value_pattern: guests
81
+ - by: css
82
+ value: div.cQnuXe.k0gFV button
83
+ wait_for:
84
+ sleep_ms: 500
85
+ - action: click
86
+ locators:
87
+ - by: aria_label
88
+ value: Set number of adults
89
+ - by: css
90
+ value: div.P634r.ZP29Te button
91
+ wait_for:
92
+ sleep_ms: 200
93
+ notes: Click adults increment N-2 times where N=${adults}; default capture used 2 extra clicks for 4 adults
94
+ - action: click
95
+ locators:
96
+ - by: text
97
+ value: Done
98
+ - by: role
99
+ value: button
100
+ name: Done
101
+ wait_for:
102
+ xhr: /TravelFrontendUi/data/batchexecute.*rpcids=AtySUc
103
+ - action: click
104
+ locators:
105
+ - by: text
106
+ value: Stay total
107
+ - by: css
108
+ value: label.LLYsl
109
+ wait_for:
110
+ sleep_ms: 300
111
+ - action: click
112
+ locators:
113
+ - by: aria_label
114
+ value: Done
115
+ - by: text
116
+ value: Done
117
+ wait_for:
118
+ xhr: /TravelFrontendUi/data/batchexecute.*rpcids=M0CRd
119
+ result:
120
+ source: xhr
121
+ url_pattern: /TravelFrontendUi/data/batchexecute.*rpcids=M0CRd
122
+ extract: '[].[].[].[].[].[].179305178.1'
123
+ return_as: hotels
124
+ notes: |
125
+ Google Travel uses the proprietary "batchexecute" RPC envelope; the relevant hotel results come back from the M0CRd RPC after the final search submission. The response is a deeply-nested JSON array (not a clean object), so extracting structured prices reliably requires custom parsing of the array indices in the response — the extract path above is approximate and the caller may need to walk the array manually. Date typing into Google's date inputs is finicky; if direct typing fails, fall back to clicking calendar day cells (text matches the day-of-month number). The guest count step requires multiple clicks on the +/- buttons; the playbook here assumes a fixed default (2 adults) and parameterizes via repeated clicks the agent must dispatch based on the adults/children parameter values. The "Stay total" toggle ensures prices reflect the full multi-night stay rather than the per-night rate. Filters (price range, hotel class, amenities, brands) shown in the recording were exploratory and are NOT codified as parameters — extend the playbook if a use case demands them.
@@ -0,0 +1,111 @@
1
+ {
2
+ "toolName": "search_google_hotels",
3
+ "intent": {
4
+ "description": "Search Google Hotels for lodging in a destination over a given date range and party composition, returning the list of nearby hotels with star rating, guest rating, nightly + total prices, and Google identifiers.",
5
+ "userSaid": "i just searched for htoels in tahoe city from june 19 to june 27 for 2 people | i added some adutls and some children | i clicked one of hte hotels | i clicked hte box that shows the stay total price (not nightly prices before taxes and fees) | i played around with all the filters | i played around with the brands"
6
+ },
7
+ "parameters": [
8
+ {
9
+ "name": "query",
10
+ "type": "string",
11
+ "description": "Free-text destination query the user typed into the search box (e.g. 'tahoe city').",
12
+ "default": "tahoe city"
13
+ },
14
+ {
15
+ "name": "place_mid",
16
+ "type": "string",
17
+ "description": "Google Knowledge Graph machine ID for the destination (the '/m/0gyvmkl' style identifier returned by the autocomplete request). Required to anchor the search to a specific place.",
18
+ "default": "/m/0gyvmkl"
19
+ },
20
+ {
21
+ "name": "place_ftid",
22
+ "type": "string",
23
+ "description": "Google Maps feature ID for the destination, of the form '0xHEX:0xHEX'. Comes from the autocomplete response alongside the place_mid.",
24
+ "default": "0x809bd62ecf1fa721:0x2a98b230816c9ed1"
25
+ },
26
+ {
27
+ "name": "place_name",
28
+ "type": "string",
29
+ "description": "Display name for the destination (e.g. 'Tahoe City').",
30
+ "default": "Tahoe City"
31
+ },
32
+ {
33
+ "name": "check_in_year",
34
+ "type": "number",
35
+ "description": "Check-in year (e.g. 2026).",
36
+ "default": 2026
37
+ },
38
+ {
39
+ "name": "check_in_month",
40
+ "type": "number",
41
+ "description": "Check-in month (1–12).",
42
+ "default": 6
43
+ },
44
+ {
45
+ "name": "check_in_day",
46
+ "type": "number",
47
+ "description": "Check-in day of month (1–31).",
48
+ "default": 19
49
+ },
50
+ {
51
+ "name": "check_out_year",
52
+ "type": "number",
53
+ "description": "Check-out year.",
54
+ "default": 2026
55
+ },
56
+ {
57
+ "name": "check_out_month",
58
+ "type": "number",
59
+ "description": "Check-out month (1–12).",
60
+ "default": 6
61
+ },
62
+ {
63
+ "name": "check_out_day",
64
+ "type": "number",
65
+ "description": "Check-out day of month (1–31).",
66
+ "default": 27
67
+ },
68
+ {
69
+ "name": "nights",
70
+ "type": "number",
71
+ "description": "Length of stay in nights. Must equal check_out - check_in.",
72
+ "default": 8
73
+ },
74
+ {
75
+ "name": "child_age_1",
76
+ "type": "number",
77
+ "description": "Age of the first child (omit by leaving default; the workflow currently always sends two children to mirror the recorded request).",
78
+ "default": 3
79
+ },
80
+ {
81
+ "name": "child_age_2",
82
+ "type": "number",
83
+ "description": "Age of the second child.",
84
+ "default": 3
85
+ },
86
+ {
87
+ "name": "currency",
88
+ "type": "string",
89
+ "description": "ISO currency code for displayed prices.",
90
+ "default": "USD"
91
+ }
92
+ ],
93
+ "requests": [
94
+ {
95
+ "method": "POST",
96
+ "url": "https://www.google.com/_/TravelFrontendUi/data/batchexecute?rpcids=AtySUc&source-path=%2Ftravel%2Fsearch&hl=en-US&soc-app=162&soc-platform=1&soc-device=1&rt=c",
97
+ "headers": {
98
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
99
+ "X-Same-Domain": "1",
100
+ "Origin": "https://www.google.com",
101
+ "Referer": "https://www.google.com/travel/search?q=${param.query}",
102
+ "Accept-Language": "en-US,en;q=0.9",
103
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
104
+ "x-goog-ext-259736195-jspb": "[\"en-US\",\"US\",\"${param.currency}\",2,null,[420],null,null,7,[]]"
105
+ },
106
+ "body": "f.req=%5B%5B%5B%22AtySUc%22%2C%22%5B%5C%22${param.query}%5C%22%2C%5B1%2C%5B%5B%5B${param.child_age_1}%5D%2C%5B${param.child_age_2}%5D%5D%2C0%5D%2C%5B%5Bnull%2C%5B%5B%5C%22${param.place_mid}%5C%22%2Cnull%2Cnull%2Cnull%2Cnull%2C%5C%22${param.place_ftid}%5C%22%2C%5C%22${param.place_name}%5C%22%5D%5D%2C%5B%5D%5D%2C%5Bnull%2C%5B%5B${param.check_in_year}%2C${param.check_in_month}%2C${param.check_in_day}%5D%2C%5B${param.check_out_year}%2C${param.check_out_month}%2C${param.check_out_day}%5D%2C${param.nights}%5D%2Cnull%2Cnull%2Cnull%2C%5B1%5D%5D%5D%2Cnull%2C%5B%5Bnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2C%5C%22${param.currency}%5C%22%5D%2Cnull%2C%5B%5D%5D%5D%2C%5B1%2Cnull%2Cnull%2C0%2C0%2Cnull%2C13%2Cnull%2C0%5D%5D%22%2Cnull%2C%221%22%5D%5D%5D&"
107
+ }
108
+ ],
109
+ "site": "google-hotels",
110
+ "parserModule": "./parser.ts"
111
+ }