mcp-google-map 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -18
- package/dist/{chunk-NAUIQEK4.js → chunk-OAML6T2D.js} +2 -2
- package/dist/chunk-OAML6T2D.js.map +1 -0
- package/dist/cli.js +197 -65
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/docs/CONFIGURATION.md +8 -8
- package/package.json +1 -1
- package/src/resources/skills/{venue-discovery → eatspot-discovery}/SKILL.md +3 -3
- package/src/resources/skills/manifest.json +3 -3
- package/dist/chunk-NAUIQEK4.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# mcp-google-map (0.
|
|
1
|
+
# mcp-google-map (0.8.0)
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<img src="https://img.shields.io/badge/node-%3E%3D22-339933?style=flat-square&logo=nodedotjs&logoColor=white" alt="Node.js >= 22">
|
|
@@ -13,7 +13,7 @@ A [Model Context Protocol](https://modelcontextprotocol.io) server for **Google
|
|
|
13
13
|
|
|
14
14
|
Supported transports: **stdio** (local MCP clients) and **Streamable HTTP**.
|
|
15
15
|
|
|
16
|
-
> Ships the **
|
|
16
|
+
> Ships the **eatspot-discovery** agent skill (`src/resources/skills/`) for budget-, quality-, category-, and schedule-validated venue search.
|
|
17
17
|
|
|
18
18
|
</p>
|
|
19
19
|
|
|
@@ -43,7 +43,7 @@ Supported transports: **stdio** (local MCP clients) and **Streamable HTTP**.
|
|
|
43
43
|
- **Per-request credentials** — key resolution from `X-Google-Maps-API-Key` or `Authorization: Bearer` headers, session key, or `GOOGLE_MAPS_API_KEY` environment.
|
|
44
44
|
- **Standalone CLI** — `doctor` self-checks, `exec <tool>` one-shot calls, `batch-geocode` with bounded concurrency.
|
|
45
45
|
- **Automatic update check** — at server startup the server compares its version against the latest npm release and prints an upgrade notice on stderr; it never blocks startup and can be disabled with `MCP_AUTO_UPDATE_DISABLED=1` (see [Configuration prerequisites](#configuration-prerequisites)).
|
|
46
|
-
- **
|
|
46
|
+
- **EatSpot Discovery skill** — server-side `priceLevels`/`minRating`/`strictTypeFiltering` filters and raw payloads back the bundled `eatspot-discovery` skill (`src/resources/skills/`).
|
|
47
47
|
|
|
48
48
|
## Requirements
|
|
49
49
|
|
|
@@ -81,7 +81,7 @@ gcloud services enable places-backend-new.googleapis.com \
|
|
|
81
81
|
|
|
82
82
|
### Notes
|
|
83
83
|
|
|
84
|
-
- For minimal
|
|
84
|
+
- For minimal eatspot-discovery use, **Places API (New)** + **Geocoding API** suffice; enable the others only for the corresponding tools (or limit exposure with `GOOGLE_MAPS_ENABLED_TOOLS`).
|
|
85
85
|
- **Maps Grounding Lite** and **Geolocation API** are **not required** by this server: the former belongs to Google's hosted MCP (`mapstools.googleapis.com`), the latter is used by no tool (there is no `ip_geolocate` in this fork).
|
|
86
86
|
- The legacy **Places API** is not needed: the server uses Places API (New) exclusively.
|
|
87
87
|
- Weather API and Air Quality API have limited regional coverage: outside coverage, the tool returns an unsupported-location error message.
|
|
@@ -98,11 +98,11 @@ npm install -g mcp-google-map
|
|
|
98
98
|
mcp-google-map setup --write-env # scaffold .env in the current directory (never overwrites)
|
|
99
99
|
$EDITOR .env # set GOOGLE_MAPS_API_KEY
|
|
100
100
|
mcp-google-map doctor # local self-check: Node, package, key, config (no network)
|
|
101
|
-
mcp-google-map --stdio
|
|
102
|
-
mcp-google-map
|
|
101
|
+
mcp-google-map start --stdio # MCP server over stdio
|
|
102
|
+
mcp-google-map start # MCP server over Streamable HTTP at http://127.0.0.1:3000/mcp
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
One-shot alternative without a global install: `npx -y mcp-google-map --stdio`.
|
|
105
|
+
One-shot alternative without a global install: `npx -y mcp-google-map start --stdio`.
|
|
106
106
|
|
|
107
107
|
### From source (this repository) — cross-platform, no bash required
|
|
108
108
|
|
|
@@ -111,8 +111,8 @@ git clone https://github.com/Ogekuri/mcp-google-map.git
|
|
|
111
111
|
cd mcp-google-map
|
|
112
112
|
npm ci # install dependencies from the lockfile
|
|
113
113
|
npm run build # tsup -> dist/ (ESM)
|
|
114
|
-
node dist/cli.js --stdio # MCP server over stdio
|
|
115
|
-
node dist/cli.js --host 127.0.0.1 --port 3000 # Streamable HTTP at /mcp
|
|
114
|
+
node dist/cli.js start --stdio # MCP server over stdio
|
|
115
|
+
node dist/cli.js start --host 127.0.0.1 --port 3000 # Streamable HTTP at /mcp
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
On Linux/macOS the bash helpers in `scripts/` wrap the same commands (`scripts/setup.sh`, `scripts/build.sh`, `scripts/start-mcp.sh`, `scripts/start-http.sh`, `scripts/test.sh`); each helper fails with remediation guidance instead of chaining implicit installs or builds. On Windows use the `npm`/`node` commands above.
|
|
@@ -124,7 +124,7 @@ MCP client registration over stdio works cross-platform with plain `node`:
|
|
|
124
124
|
"mcpServers": {
|
|
125
125
|
"mcp-google-map": {
|
|
126
126
|
"command": "node",
|
|
127
|
-
"args": ["/absolute/path/to/mcp-google-map/dist/cli.js", "--stdio"],
|
|
127
|
+
"args": ["/absolute/path/to/mcp-google-map/dist/cli.js", "start", "--stdio"],
|
|
128
128
|
"env": { "GOOGLE_MAPS_API_KEY": "AIza..." }
|
|
129
129
|
}
|
|
130
130
|
}
|
|
@@ -155,8 +155,8 @@ mcp-google-map snippet --transport http --port 3100 # HTTP URL snip
|
|
|
155
155
|
All commands work through the `mcp-google-map` binary (npm install) or `node dist/cli.js` (from source).
|
|
156
156
|
|
|
157
157
|
```bash
|
|
158
|
-
mcp-google-map --stdio
|
|
159
|
-
mcp-google-map --host 127.0.0.1 --port 3000
|
|
158
|
+
mcp-google-map start --stdio # MCP server over stdio
|
|
159
|
+
mcp-google-map start --host 127.0.0.1 --port 3000 # MCP server over Streamable HTTP at /mcp (loopback default)
|
|
160
160
|
mcp-google-map setup --write-env # onboarding: readiness checks + .env scaffold (no network, no prompts)
|
|
161
161
|
mcp-google-map doctor # local self-check: Node, package, key, config, tool profiles (no network)
|
|
162
162
|
mcp-google-map doctor --live --apikey "AIza..." # also probes Geocoding, Places (New), Routes (billable calls)
|
|
@@ -178,7 +178,7 @@ mcp-google-map snippet --client cursor # print an MCP client re
|
|
|
178
178
|
- `snippet` prints a valid `mcpServers` JSON registration to stdout (guidance notes on stderr) and never writes or edits configuration files; by default it embeds a literal placeholder, and `--env-key NAME` emits a `${NAME}` reference whose expansion is client-specific.
|
|
179
179
|
- `--env-file <path>` loads exactly the given environment file for any command and exits 1 with a JSON error when the file is missing (no implicit `.env` fallback).
|
|
180
180
|
- `enable <cli>` / `disable <cli>` manage the persisted enabled-CLI set at `~/.config/mcp-google-map/config.json`; accepted names (case-insensitive): `pi`, `openclaw`, `codex`, `antigravity`, `claude`; unsupported names exit 2 listing the supported ones.
|
|
181
|
-
- `install` registers for every enabled CLI the stdio server entry `mcp-google-map` (`npx -y mcp-google-map --stdio`, embedding the resolved `GOOGLE_MAPS_API_KEY` when set) and installs every bundled skill; `uninstall` is the reciprocal removal. Per-CLI targets: pi `~/.pi/agent/mcp.json` + `~/.pi/agent/skills`, openclaw `~/.openclaw/openclaw.json` + `~/.openclaw/workspace/skills`, codex `~/.codex/config.toml` (`mcp_servers` table) + `~/.codex/skills`, antigravity `~/.gemini/settings.json` + `~/.gemini/config/skills`, claude `~/.claude.json` + `~/.claude/skills`. Existing unrelated configuration entries are always preserved.
|
|
181
|
+
- `install` registers for every enabled CLI the stdio server entry `mcp-google-map` (`npx -y mcp-google-map start --stdio`, embedding the resolved `GOOGLE_MAPS_API_KEY` when set) and installs every bundled skill; `uninstall` is the reciprocal removal. Per-CLI targets: pi `~/.pi/agent/mcp.json` + `~/.pi/agent/skills`, openclaw `~/.openclaw/openclaw.json` + `~/.openclaw/workspace/skills`, codex `~/.codex/config.toml` (`mcp_servers` table) + `~/.codex/skills`, antigravity `~/.gemini/settings.json` + `~/.gemini/config/skills`, claude `~/.claude.json` + `~/.claude/skills`. Existing unrelated configuration entries are always preserved.
|
|
182
182
|
- `show` prints two human-readable tables: CLI Configuration (CLI, Enabled, MCP Config Path, Skills Config Path for all five CLIs) and Installation Status (Type, Name, CLI, Enabled, Installed with OK/MISSING/STALE per enabled CLI).
|
|
183
183
|
- HTTP mode starts even without an API key and logs a warning; stdio mode and the CLI commands require a key (`--apikey` or `GOOGLE_MAPS_API_KEY`). When no HTTP instance can start (invalid or taken port), the process exits non-zero.
|
|
184
184
|
|
|
@@ -220,14 +220,14 @@ pi reads MCP servers from `~/.pi/agent/mcp.json` (user) or `.pi/mcp.json` (proje
|
|
|
220
220
|
"mcpServers": {
|
|
221
221
|
"maps": {
|
|
222
222
|
"command": "node",
|
|
223
|
-
"args": ["/absolute/path/to/mcp-google-map/dist/cli.js", "--stdio"],
|
|
223
|
+
"args": ["/absolute/path/to/mcp-google-map/dist/cli.js", "start", "--stdio"],
|
|
224
224
|
"env": { "GOOGLE_MAPS_API_KEY": "${GOOGLE_MAPS_API_KEY}" }
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
```
|
|
229
229
|
|
|
230
|
-
- From a terminal: `pi mcp add maps -- node /absolute/path/to/mcp-google-map/dist/cli.js --stdio` (add `--env GOOGLE_MAPS_API_KEY=...` to pass the key).
|
|
230
|
+
- From a terminal: `pi mcp add maps -- node /absolute/path/to/mcp-google-map/dist/cli.js start --stdio` (add `--env GOOGLE_MAPS_API_KEY=...` to pass the key).
|
|
231
231
|
- `${GOOGLE_MAPS_API_KEY}` expands the environment variable on the fly: the key never stays hard-coded in the configuration file.
|
|
232
232
|
- After editing `mcp.json` outside the session, run `/reload` in pi; `/mcp` inspects status, tools, and connection errors.
|
|
233
233
|
- Tools appear with the `mcp__<server>__` prefix: `mcp__maps__maps_geocode`, `mcp__maps__maps_search_places`, ...
|
|
@@ -237,10 +237,10 @@ pi reads MCP servers from `~/.pi/agent/mcp.json` (user) or `.pi/mcp.json` (proje
|
|
|
237
237
|
OpenClaw manages MCP servers in `~/.openclaw/openclaw.json` through its CLI (verified on OpenClaw 2026.4.21):
|
|
238
238
|
|
|
239
239
|
```bash
|
|
240
|
-
openclaw mcp set maps '{"command":"node","args":["/absolute/path/to/mcp-google-map/dist/cli.js","--stdio"],"env":{"GOOGLE_MAPS_API_KEY":"AIza..."},"description":"Google Maps: Places API (New), geocoding, routes, weather"}'
|
|
240
|
+
openclaw mcp set maps '{"command":"node","args":["/absolute/path/to/mcp-google-map/dist/cli.js","start","--stdio"],"env":{"GOOGLE_MAPS_API_KEY":"AIza..."},"description":"Google Maps: Places API (New), geocoding, routes, weather"}'
|
|
241
241
|
```
|
|
242
242
|
|
|
243
|
-
- Without a local install: use `"command":"npx","args":["-y","mcp-google-map","--stdio"]` (runs the published npm package).
|
|
243
|
+
- Without a local install: use `"command":"npx","args":["-y","mcp-google-map","start","--stdio"]` (runs the published npm package).
|
|
244
244
|
- After the change, reload the gateway or restart the agent session so the new server gets connected.
|
|
245
245
|
- Inspect and remove: `openclaw mcp list`, `openclaw mcp show maps`, `openclaw mcp unset maps`.
|
|
246
246
|
- The OpenClaw agent can install and configure the server on its own (shell + `openclaw mcp set`); creating the API key in Google Cloud Console remains a human task.
|
|
@@ -273,7 +273,7 @@ mcp-google-map/
|
|
|
273
273
|
|
|
274
274
|
- [docs/TOOLS.md](docs/TOOLS.md) — tool catalog and parameters
|
|
275
275
|
- [docs/CONFIGURATION.md](docs/CONFIGURATION.md) — environment variables, transports, API key
|
|
276
|
-
- [src/resources/skills/](src/resources/skills/) — bundled `
|
|
276
|
+
- [src/resources/skills/](src/resources/skills/) — bundled `eatspot-discovery` agent skill (budget filters, FieldMask, raw payload)
|
|
277
277
|
|
|
278
278
|
## Development
|
|
279
279
|
|
|
@@ -817,7 +817,7 @@ var NewPlacesService = class {
|
|
|
817
817
|
return result.places;
|
|
818
818
|
}
|
|
819
819
|
/**
|
|
820
|
-
* Text Search (New) with the full
|
|
820
|
+
* Text Search (New) with the full eatspot-discovery filter surface (REQ-027, REQ-109):
|
|
821
821
|
* - priceLevels: cumulative server-side economic filter (enum strings)
|
|
822
822
|
* - strictTypeFiltering: strict place-type filtering
|
|
823
823
|
* - minRating, includedType, openNow: server-side filters
|
|
@@ -1857,4 +1857,4 @@ export {
|
|
|
1857
1857
|
PlacesSearcher,
|
|
1858
1858
|
Logger
|
|
1859
1859
|
};
|
|
1860
|
-
//# sourceMappingURL=chunk-
|
|
1860
|
+
//# sourceMappingURL=chunk-OAML6T2D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/toolclass.ts","../src/services/RoutesService.ts","../src/services/NewPlacesService.ts","../src/services/PlacesSearcher.ts","../src/index.ts"],"sourcesContent":["import { Client, Language } from \"@googlemaps/google-maps-services-js\";\nimport dotenv from \"dotenv\";\nimport { Logger } from \"../index.js\";\nimport { RoutesService } from \"./RoutesService.js\";\n\ndotenv.config();\n\ninterface GeocodeResult {\n lat: number;\n lng: number;\n formatted_address?: string;\n place_id?: string;\n}\n\n/**\n * Extracts a meaningful, actionable error message from Google Maps API errors.\n */\nfunction extractErrorMessage(error: any): string {\n const statusCode = error?.response?.status;\n const apiError = error?.response?.data?.error_message;\n const apiStatus = error?.response?.data?.status;\n\n // Map common HTTP status codes to actionable messages\n if (statusCode === 403) {\n return \"API key invalid or required API not enabled. Check: console.cloud.google.com → APIs & Services → Enable the relevant API (Places, Geocoding, etc.)\";\n }\n if (statusCode === 429) {\n return \"API quota exceeded. Wait and retry, or check quota at console.cloud.google.com → Quotas\";\n }\n\n // Map Google Maps API status codes\n if (apiStatus === \"ZERO_RESULTS\") {\n return \"No results found. Try broader search terms or a larger radius.\";\n }\n if (apiStatus === \"OVER_QUERY_LIMIT\") {\n return \"API quota exceeded. Wait and retry, or upgrade your billing plan.\";\n }\n if (apiStatus === \"REQUEST_DENIED\") {\n return `Request denied by Google Maps API. ${apiError || \"Check your API key and enabled APIs.\"}`;\n }\n if (apiStatus === \"INVALID_REQUEST\") {\n return `Invalid request parameters. ${apiError || \"Check your input values.\"}`;\n }\n\n if (apiError) {\n return `${apiError} (HTTP ${statusCode})`;\n }\n\n return error instanceof Error ? error.message : String(error);\n}\n\nexport class GoogleMapsTools {\n private client: Client;\n private readonly defaultLanguage: Language = Language.en;\n private apiKey: string;\n\n constructor(apiKey?: string) {\n this.client = new Client({});\n // Use provided API key, or fall back to environment variable\n this.apiKey = apiKey || process.env.GOOGLE_MAPS_API_KEY || \"\";\n if (!this.apiKey) {\n throw new Error(\"Google Maps API Key is required\");\n }\n }\n\n private async geocodeAddress(address: string): Promise<GeocodeResult> {\n try {\n const response = await this.client.geocode({\n params: {\n address: address,\n key: this.apiKey,\n language: this.defaultLanguage,\n },\n });\n\n if (response.data.results.length === 0) {\n throw new Error(`No location found for address: \"${address}\"`);\n }\n\n const result = response.data.results[0];\n const location = result.geometry.location;\n return {\n lat: location.lat,\n lng: location.lng,\n formatted_address: result.formatted_address,\n place_id: result.place_id,\n };\n } catch (error: any) {\n Logger.error(\"Error in geocodeAddress:\", error);\n throw new Error(`Failed to geocode address \"${address}\": ${extractErrorMessage(error)}`);\n }\n }\n\n private parseCoordinates(coordString: string): GeocodeResult {\n const coords = coordString.split(\",\").map((c) => parseFloat(c.trim()));\n if (coords.length !== 2 || isNaN(coords[0]) || isNaN(coords[1])) {\n throw new Error(\n `Invalid coordinate format: \"${coordString}\". Please use \"latitude,longitude\" format (e.g., \"25.033,121.564\"`\n );\n }\n return { lat: coords[0], lng: coords[1] };\n }\n\n async getLocation(center: { value: string; isCoordinates: boolean }): Promise<GeocodeResult> {\n if (center.isCoordinates) {\n return this.parseCoordinates(center.value);\n }\n return this.geocodeAddress(center.value);\n }\n\n async geocode(address: string): Promise<{\n location: { lat: number; lng: number };\n formatted_address: string;\n place_id: string;\n }> {\n try {\n const result = await this.geocodeAddress(address);\n return {\n location: { lat: result.lat, lng: result.lng },\n formatted_address: result.formatted_address || \"\",\n place_id: result.place_id || \"\",\n };\n } catch (error: any) {\n Logger.error(\"Error in geocode:\", error);\n throw new Error(`Failed to geocode address \"${address}\": ${extractErrorMessage(error)}`);\n }\n }\n\n async reverseGeocode(\n latitude: number,\n longitude: number\n ): Promise<{\n formatted_address: string;\n place_id: string;\n address_components: any[];\n }> {\n try {\n const response = await this.client.reverseGeocode({\n params: {\n latlng: { lat: latitude, lng: longitude },\n language: this.defaultLanguage,\n key: this.apiKey,\n },\n });\n\n if (response.data.results.length === 0) {\n throw new Error(`No address found for coordinates: (${latitude}, ${longitude})`);\n }\n\n const result = response.data.results[0];\n return {\n formatted_address: result.formatted_address,\n place_id: result.place_id,\n address_components: result.address_components,\n };\n } catch (error: any) {\n Logger.error(\"Error in reverseGeocode:\", error);\n throw new Error(\n `Failed to reverse geocode coordinates (${latitude}, ${longitude}): ${extractErrorMessage(error)}`\n );\n }\n }\n\n async searchAlongRoute(params: {\n textQuery: string;\n origin: string;\n destination: string;\n mode?: string;\n maxResults?: number;\n }): Promise<{ places: any[]; route: { distance: string; duration: string; polyline: string } }> {\n try {\n // Step 1: Get directions via Routes API to obtain the encoded polyline\n const routesService = new RoutesService(this.apiKey);\n const directions = await routesService.computeRoutes({\n origin: params.origin,\n destination: params.destination,\n mode: params.mode || \"walking\",\n });\n\n const polyline = directions.routes[0]?.polyline?.encodedPolyline;\n if (!polyline) {\n throw new Error(\"Could not get route polyline\");\n }\n\n // Step 2: Call Places searchText REST API with searchAlongRouteParameters\n const maxResults = Math.min(params.maxResults || 5, 20);\n const fieldMask =\n \"places.displayName,places.id,places.formattedAddress,places.location,places.rating,places.userRatingCount,places.currentOpeningHours.openNow\";\n\n const response = await fetch(\"https://places.googleapis.com/v1/places:searchText\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Goog-Api-Key\": this.apiKey,\n \"X-Goog-FieldMask\": fieldMask,\n },\n body: JSON.stringify({\n textQuery: params.textQuery,\n searchAlongRouteParameters: {\n polyline: {\n encodedPolyline: polyline,\n },\n },\n maxResultCount: maxResults,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(errorData?.error?.message || `HTTP ${response.status}`);\n }\n\n const data = await response.json();\n const places = (data.places || []).map((place: any) => ({\n name: place.displayName?.text || \"\",\n place_id: place.id || \"\",\n formatted_address: place.formattedAddress || \"\",\n location: {\n lat: place.location?.latitude || 0,\n lng: place.location?.longitude || 0,\n },\n rating: place.rating || 0,\n user_ratings_total: place.userRatingCount || 0,\n open_now: place.currentOpeningHours?.openNow ?? null,\n }));\n\n return {\n places,\n route: {\n distance: directions.total_distance.text,\n duration: directions.total_duration.text,\n polyline,\n },\n };\n } catch (error: any) {\n Logger.error(\"Error in searchAlongRoute:\", error);\n throw new Error(error.message || \"Failed to search along route\");\n }\n }\n\n async getWeather(\n latitude: number,\n longitude: number,\n type: \"current\" | \"forecast_daily\" | \"forecast_hourly\" = \"current\",\n forecastDays?: number,\n forecastHours?: number\n ): Promise<any> {\n try {\n const baseParams = `key=${this.apiKey}&location.latitude=${latitude}&location.longitude=${longitude}`;\n let url: string;\n\n switch (type) {\n case \"forecast_daily\": {\n const days = Math.min(Math.max(forecastDays || 5, 1), 10);\n url = `https://weather.googleapis.com/v1/forecast/days:lookup?${baseParams}&days=${days}`;\n break;\n }\n case \"forecast_hourly\": {\n const hours = Math.min(Math.max(forecastHours || 24, 1), 240);\n url = `https://weather.googleapis.com/v1/forecast/hours:lookup?${baseParams}&hours=${hours}`;\n break;\n }\n default:\n url = `https://weather.googleapis.com/v1/currentConditions:lookup?${baseParams}`;\n }\n\n const response = await fetch(url);\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const msg = errorData?.error?.message || `HTTP ${response.status}`;\n\n if (msg.includes(\"not supported for this location\")) {\n throw new Error(\n `Weather data is not available for this location (${latitude}, ${longitude}). ` +\n \"The Google Weather API has limited coverage — China, Japan, South Korea, Cuba, Iran, North Korea, and Syria are unsupported. \" +\n \"Try a location in North America, Europe, or Oceania.\"\n );\n }\n\n throw new Error(msg);\n }\n\n const data = await response.json();\n\n if (type === \"current\") {\n return {\n temperature: data.temperature,\n feelsLike: data.feelsLikeTemperature,\n humidity: data.relativeHumidity,\n wind: data.wind,\n conditions: data.weatherCondition?.description?.text || data.weatherCondition?.type,\n uvIndex: data.uvIndex,\n precipitation: data.precipitation,\n visibility: data.visibility,\n pressure: data.airPressure,\n cloudCover: data.cloudCover,\n isDayTime: data.isDaytime,\n };\n }\n\n // forecast_daily or forecast_hourly — return as-is with light cleanup\n return data;\n } catch (error: any) {\n Logger.error(\"Error in getWeather:\", error);\n throw new Error(error.message || `Failed to get weather for (${latitude}, ${longitude})`);\n }\n }\n\n async getAirQuality(\n latitude: number,\n longitude: number,\n includeHealthRecommendations: boolean = true,\n includePollutants: boolean = false\n ): Promise<any> {\n try {\n const url = `https://airquality.googleapis.com/v1/currentConditions:lookup?key=${this.apiKey}`;\n\n const extraComputations: string[] = [];\n if (includeHealthRecommendations) {\n extraComputations.push(\"HEALTH_RECOMMENDATIONS\");\n }\n if (includePollutants) {\n extraComputations.push(\"POLLUTANT_CONCENTRATION\");\n }\n\n const body: any = {\n location: { latitude, longitude },\n };\n if (extraComputations.length > 0) {\n body.extraComputations = extraComputations;\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const msg = errorData?.error?.message || `HTTP ${response.status}`;\n throw new Error(msg);\n }\n\n const data = await response.json();\n\n // Extract the primary index\n const indexes = data.indexes || [];\n const primaryIndex = indexes[0];\n\n const result: any = {\n dateTime: data.dateTime,\n regionCode: data.regionCode,\n aqi: primaryIndex?.aqi,\n category: primaryIndex?.category,\n dominantPollutant: primaryIndex?.dominantPollutant,\n color: primaryIndex?.color,\n };\n\n // Include all available indexes (universal + local)\n if (indexes.length > 1) {\n result.indexes = indexes.map((idx: any) => ({\n code: idx.code,\n displayName: idx.displayName,\n aqi: idx.aqi,\n category: idx.category,\n dominantPollutant: idx.dominantPollutant,\n }));\n }\n\n // Health recommendations\n if (data.healthRecommendations) {\n result.healthRecommendations = data.healthRecommendations;\n }\n\n // Pollutants\n if (data.pollutants) {\n result.pollutants = data.pollutants.map((p: any) => ({\n code: p.code,\n displayName: p.displayName,\n concentration: p.concentration,\n additionalInfo: p.additionalInfo,\n }));\n }\n\n return result;\n } catch (error: any) {\n Logger.error(\"Error in getAirQuality:\", error);\n throw new Error(error.message || `Failed to get air quality for (${latitude}, ${longitude})`);\n }\n }\n\n async getStaticMap(params: {\n center?: string;\n zoom?: number;\n size?: string;\n maptype?: string;\n markers?: string[];\n path?: string[];\n }): Promise<{ base64: string; size: number; dimensions: string }> {\n try {\n const dimensions = params.size || \"600x400\";\n const queryParts: string[] = [\n `key=${this.apiKey}`,\n `size=${dimensions}`,\n `maptype=${params.maptype || \"roadmap\"}`,\n ];\n\n if (params.center) {\n queryParts.push(`center=${encodeURIComponent(params.center)}`);\n }\n if (params.zoom !== undefined) {\n queryParts.push(`zoom=${params.zoom}`);\n }\n if (params.markers) {\n for (const marker of params.markers) {\n queryParts.push(`markers=${encodeURIComponent(marker)}`);\n }\n }\n if (params.path) {\n for (const p of params.path) {\n queryParts.push(`path=${encodeURIComponent(p)}`);\n }\n }\n\n const url = `https://maps.googleapis.com/maps/api/staticmap?${queryParts.join(\"&\")}`;\n\n // Check URL length limit\n if (url.length > 16384) {\n throw new Error(`URL exceeds 16,384 character limit (${url.length}). Reduce markers or path points.`);\n }\n\n const response = await fetch(url);\n\n if (!response.ok) {\n const contentType = response.headers.get(\"content-type\") || \"\";\n if (contentType.includes(\"application/json\") || contentType.includes(\"text/\")) {\n const errorText = await response.text();\n throw new Error(`Static Maps API error: ${errorText}`);\n }\n throw new Error(`Static Maps API returned HTTP ${response.status}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const buffer = Buffer.from(arrayBuffer);\n const base64 = buffer.toString(\"base64\");\n\n return {\n base64,\n size: buffer.length,\n dimensions,\n };\n } catch (error: any) {\n Logger.error(\"Error in getStaticMap:\", error);\n throw new Error(error.message || \"Failed to generate static map\");\n }\n }\n\n async getTimezone(\n latitude: number,\n longitude: number,\n timestamp?: number\n ): Promise<{ timeZoneId: string; timeZoneName: string; utcOffset: number; dstOffset: number; localTime: string }> {\n try {\n const ts = timestamp ? Math.floor(timestamp / 1000) : Math.floor(Date.now() / 1000);\n\n const response = await this.client.timezone({\n params: {\n location: { lat: latitude, lng: longitude },\n timestamp: ts,\n key: this.apiKey,\n },\n });\n\n const result = response.data;\n\n if (result.status !== \"OK\") {\n throw new Error(`Timezone API returned status: ${result.status}`);\n }\n\n const totalOffset = (result.rawOffset + result.dstOffset) * 1000;\n const localTime = new Date(ts * 1000 + totalOffset).toISOString().replace(\"Z\", \"\");\n\n return {\n timeZoneId: result.timeZoneId,\n timeZoneName: result.timeZoneName,\n utcOffset: result.rawOffset,\n dstOffset: result.dstOffset,\n localTime,\n };\n } catch (error: any) {\n Logger.error(\"Error in getTimezone:\", error);\n throw new Error(`Failed to get timezone for (${latitude}, ${longitude}): ${extractErrorMessage(error)}`);\n }\n }\n\n async getElevation(\n locations: Array<{ latitude: number; longitude: number }>\n ): Promise<Array<{ elevation: number; location: { lat: number; lng: number } }>> {\n try {\n const formattedLocations = locations.map((loc) => ({\n lat: loc.latitude,\n lng: loc.longitude,\n }));\n\n const response = await this.client.elevation({\n params: {\n locations: formattedLocations,\n key: this.apiKey,\n },\n });\n\n const result = response.data;\n\n if (result.status !== \"OK\") {\n throw new Error(`Failed to get elevation data with status: ${result.status}`);\n }\n\n return result.results.map((item: any, index: number) => ({\n elevation: item.elevation,\n location: formattedLocations[index],\n }));\n } catch (error: any) {\n Logger.error(\"Error in getElevation:\", error);\n throw new Error(\n `Failed to get elevation data for ${locations.length} location(s): ${extractErrorMessage(error)}`\n );\n }\n }\n}\n","import { Logger } from \"../index.js\";\n\nconst ROUTES_API_BASE = \"https://routes.googleapis.com\";\n\n/** Travel mode mapping: legacy lowercase → Routes API uppercase */\nconst TRAVEL_MODE_MAP: Record<string, string> = {\n driving: \"DRIVE\",\n walking: \"WALK\",\n bicycling: \"BICYCLE\",\n transit: \"TRANSIT\",\n};\n\n/** FieldMask for computeRoutes */\nconst COMPUTE_ROUTES_FIELD_MASK = [\n \"routes.distanceMeters\",\n \"routes.duration\",\n \"routes.description\",\n \"routes.polyline.encodedPolyline\",\n \"routes.legs.distanceMeters\",\n \"routes.legs.duration\",\n \"routes.legs.startLocation\",\n \"routes.legs.endLocation\",\n \"routes.legs.steps.navigationInstruction\",\n \"routes.legs.steps.distanceMeters\",\n \"routes.legs.steps.staticDuration\",\n \"routes.legs.steps.startLocation\",\n \"routes.legs.steps.endLocation\",\n \"routes.legs.steps.transitDetails\",\n \"routes.legs.polyline\",\n \"routes.optimizedIntermediateWaypointIndex\",\n].join(\",\");\n\n/** FieldMask for computeRouteMatrix */\nconst COMPUTE_ROUTE_MATRIX_FIELD_MASK = \"originIndex,destinationIndex,distanceMeters,duration,status,condition\";\n\n/**\n * Parse Routes API duration string (\"1234s\") to seconds.\n */\nexport function parseDuration(duration: string | undefined): number {\n if (!duration) return 0;\n const match = duration.match(/^(\\d+)s$/);\n return match ? parseInt(match[1], 10) : 0;\n}\n\n/**\n * Format meters to human-readable distance.\n */\nexport function formatDistance(meters: number): string {\n if (meters >= 1000) {\n return `${(meters / 1000).toFixed(1)} km`;\n }\n return `${meters} m`;\n}\n\n/**\n * Format seconds to human-readable duration.\n */\nexport function formatDuration(seconds: number): string {\n if (seconds >= 3600) {\n const hours = Math.floor(seconds / 3600);\n const mins = Math.round((seconds % 3600) / 60);\n return mins > 0\n ? `${hours} hour${hours > 1 ? \"s\" : \"\"} ${mins} min${mins > 1 ? \"s\" : \"\"}`\n : `${hours} hour${hours > 1 ? \"s\" : \"\"}`;\n }\n const mins = Math.round(seconds / 60);\n return `${mins} min${mins !== 1 ? \"s\" : \"\"}`;\n}\n\nexport const COORDINATE_STRING_PATTERN = /^\\s*(-?\\d+\\.?\\d*)\\s*,\\s*(-?\\d+\\.?\\d*)\\s*$/;\n\nexport type RouteWaypoint = { latLng: { latitude: number; longitude: number } } | { placeId: string };\nexport type WaypointInput = string | RouteWaypoint;\n\n/**\n * Format a waypoint for human-readable error messages.\n */\nfunction describeWaypoint(waypoint: WaypointInput): string {\n if (typeof waypoint === \"string\") {\n return waypoint;\n }\n if (\"placeId\" in waypoint) {\n return `placeId:${waypoint.placeId}`;\n }\n return `${waypoint.latLng.latitude},${waypoint.latLng.longitude}`;\n}\n\n/**\n * Convert address/coordinates string or structured waypoint to Routes API Waypoint.\n *\n * NOTE: must stay synchronous and take exactly one argument. computeRoutes below\n * calls params.intermediates.map(toWaypoint), so a second parameter would silently\n * bind the array index, and returning a promise would serialise as {}.\n */\nfunction toWaypoint(location: WaypointInput): any {\n if (typeof location !== \"string\") {\n return \"placeId\" in location ? { placeId: location.placeId } : { location: { latLng: location.latLng } };\n }\n const coordMatch = location.match(COORDINATE_STRING_PATTERN);\n if (coordMatch) {\n return {\n location: {\n latLng: {\n latitude: parseFloat(coordMatch[1]),\n longitude: parseFloat(coordMatch[2]),\n },\n },\n };\n }\n return { address: location };\n}\n\nfunction buildRouteModifiers(params: { avoidTolls?: boolean; avoidHighways?: boolean }, travelMode: string): any {\n const routeModifiers: Record<string, boolean> = {};\n\n if (params.avoidTolls) {\n routeModifiers.avoidTolls = true;\n }\n if (params.avoidHighways) {\n routeModifiers.avoidHighways = true;\n }\n\n if (Object.keys(routeModifiers).length === 0) {\n return undefined;\n }\n\n if (travelMode !== \"DRIVE\") {\n throw new Error('Route modifiers \"avoid_tolls\" and \"avoid_highways\" are only supported with mode \"driving\".');\n }\n\n return routeModifiers;\n}\n\n/**\n * Routes API REST client.\n *\n * Uses native fetch() to call Google Routes API endpoints.\n * Follows the same pattern as getWeather/getAirQuality/searchAlongRoute.\n */\nexport class RoutesService {\n private apiKey: string;\n\n constructor(apiKey?: string) {\n this.apiKey = apiKey || process.env.GOOGLE_MAPS_API_KEY || \"\";\n if (!this.apiKey) {\n throw new Error(\"Google Maps API Key is required\");\n }\n }\n\n /**\n * Compute routes using Routes API.\n * Returns response compatible with existing DirectionsResponse.data interface.\n */\n async computeRoutes(params: {\n origin: WaypointInput;\n destination: WaypointInput;\n mode?: string;\n departureTime?: Date;\n arrivalTime?: Date;\n intermediates?: WaypointInput[];\n optimizeWaypointOrder?: boolean;\n avoidTolls?: boolean;\n avoidHighways?: boolean;\n /** What to call the endpoints in error messages. Defaults to the waypoints themselves. */\n originLabel?: string;\n destinationLabel?: string;\n }): Promise<{\n routes: any[];\n summary: string;\n total_distance: { value: number; text: string };\n total_duration: { value: number; text: string };\n arrival_time: string;\n departure_time: string;\n optimizedIntermediateWaypointIndex?: number[];\n }> {\n const travelMode = TRAVEL_MODE_MAP[params.mode || \"driving\"] || \"DRIVE\";\n\n const requestBody: any = {\n origin: toWaypoint(params.origin),\n destination: toWaypoint(params.destination),\n travelMode,\n computeAlternativeRoutes: false,\n };\n\n // routingPreference only valid for DRIVE mode\n if (travelMode === \"DRIVE\") {\n requestBody.routingPreference = \"TRAFFIC_AWARE\";\n }\n\n const routeModifiers = buildRouteModifiers(params, travelMode);\n if (routeModifiers) {\n requestBody.routeModifiers = routeModifiers;\n }\n\n // Departure/arrival time (only set if explicitly provided — Routes API rejects past timestamps)\n if (params.arrivalTime) {\n requestBody.arrivalTime = params.arrivalTime.toISOString();\n } else if (params.departureTime) {\n requestBody.departureTime = params.departureTime.toISOString();\n }\n\n // Intermediates for multi-stop\n if (params.intermediates && params.intermediates.length > 0) {\n requestBody.intermediates = params.intermediates.map(toWaypoint);\n }\n\n // Waypoint optimization (not supported for TRANSIT, requires at least 2 intermediates)\n if (\n params.optimizeWaypointOrder &&\n params.intermediates &&\n params.intermediates.length > 1 &&\n travelMode !== \"TRANSIT\"\n ) {\n requestBody.optimizeWaypointOrder = true;\n }\n\n const response = await fetch(`${ROUTES_API_BASE}/directions/v2:computeRoutes`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Goog-Api-Key\": this.apiKey,\n \"X-Goog-FieldMask\": COMPUTE_ROUTES_FIELD_MASK,\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const msg = errorData?.error?.message || `HTTP ${response.status}`;\n throw new Error(msg);\n }\n\n const data = await response.json();\n\n if (!data.routes || data.routes.length === 0) {\n const mode = params.mode || \"driving\";\n if (mode === \"transit\") {\n throw new Error(\n `No transit route found from \"${params.originLabel ?? describeWaypoint(params.origin)}\" ` +\n `to \"${params.destinationLabel ?? describeWaypoint(params.destination)}\". ` +\n `The Google Routes API does not support transit directions in some regions (notably Japan and India). ` +\n `Try using mode \"driving\" or \"walking\" instead, or use a regional transit service for public transportation details.`\n );\n }\n throw new Error(\n `No route found from \"${params.originLabel ?? describeWaypoint(params.origin)}\" ` +\n `to \"${params.destinationLabel ?? describeWaypoint(params.destination)}\" with mode: ${mode}`\n );\n }\n\n const route = data.routes[0];\n const totalDistanceMeters = route.distanceMeters || 0;\n const totalDurationSeconds = parseDuration(route.duration);\n\n return {\n routes: data.routes,\n summary: route.description || \"\",\n total_distance: {\n value: totalDistanceMeters,\n text: formatDistance(totalDistanceMeters),\n },\n total_duration: {\n value: totalDurationSeconds,\n text: formatDuration(totalDurationSeconds),\n },\n arrival_time: \"\",\n departure_time: \"\",\n ...(route.optimizedIntermediateWaypointIndex\n ? { optimizedIntermediateWaypointIndex: route.optimizedIntermediateWaypointIndex }\n : {}),\n };\n }\n\n /**\n * Compute route matrix using Routes API.\n * Returns response compatible with existing DistanceMatrixResponse.data interface.\n */\n async computeRouteMatrix(params: {\n origins: string[];\n destinations: string[];\n mode?: string;\n departureTime?: Date;\n avoidTolls?: boolean;\n avoidHighways?: boolean;\n }): Promise<{\n distances: any[][];\n durations: any[][];\n origin_addresses: string[];\n destination_addresses: string[];\n warning?: string;\n }> {\n const travelMode = TRAVEL_MODE_MAP[params.mode || \"driving\"] || \"DRIVE\";\n\n const requestBody: any = {\n origins: params.origins.map((o) => ({ waypoint: toWaypoint(o) })),\n destinations: params.destinations.map((d) => ({ waypoint: toWaypoint(d) })),\n travelMode,\n };\n\n // routingPreference only valid for DRIVE mode\n if (travelMode === \"DRIVE\") {\n requestBody.routingPreference = \"TRAFFIC_AWARE\";\n }\n\n const routeModifiers = buildRouteModifiers(params, travelMode);\n if (routeModifiers) {\n requestBody.routeModifiers = routeModifiers;\n }\n\n if (params.departureTime) {\n requestBody.departureTime = params.departureTime.toISOString();\n }\n\n const response = await fetch(`${ROUTES_API_BASE}/distanceMatrix/v2:computeRouteMatrix`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Goog-Api-Key\": this.apiKey,\n \"X-Goog-FieldMask\": COMPUTE_ROUTE_MATRIX_FIELD_MASK,\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const msg = errorData?.error?.message || `HTTP ${response.status}`;\n throw new Error(msg);\n }\n\n const elements: any[] = await response.json();\n\n // Rebuild 2D matrix from flat array\n const rowCount = params.origins.length;\n const colCount = params.destinations.length;\n const distances: any[][] = Array.from({ length: rowCount }, () => Array(colCount).fill(null));\n const durations: any[][] = Array.from({ length: rowCount }, () => Array(colCount).fill(null));\n\n let routeNotFoundCount = 0;\n for (const element of elements) {\n const i = element.originIndex;\n const j = element.destinationIndex;\n if (i === undefined || j === undefined) continue;\n if (element.condition === \"ROUTE_NOT_FOUND\") {\n routeNotFoundCount++;\n continue;\n }\n\n const distMeters = element.distanceMeters || 0;\n const durSeconds = parseDuration(element.duration);\n\n distances[i][j] = {\n value: distMeters,\n text: formatDistance(distMeters),\n };\n durations[i][j] = {\n value: durSeconds,\n text: formatDuration(durSeconds),\n };\n }\n\n const totalPairs = rowCount * colCount;\n\n // All pairs failed — likely a regional transit limitation\n if (routeNotFoundCount === totalPairs && travelMode === \"TRANSIT\") {\n throw new Error(\n `No transit routes found for any origin/destination pair. ` +\n `The Google Routes API does not support transit directions in some regions (notably Japan and India). ` +\n `Try using mode \"driving\" or \"walking\" instead, or use a regional transit service for public transportation details.`\n );\n }\n\n // Routes API doesn't return resolved addresses; use input strings\n return {\n distances,\n durations,\n origin_addresses: params.origins,\n destination_addresses: params.destinations,\n ...(routeNotFoundCount > 0 && travelMode === \"TRANSIT\"\n ? {\n warning: `${routeNotFoundCount} of ${totalPairs} origin/destination pairs returned no transit route. The Google Routes API has limited transit coverage in some regions.`,\n }\n : {}),\n };\n }\n}\n","import { PlacesClient } from \"@googlemaps/places\";\nimport { Logger } from \"../index.js\";\n\nexport class NewPlacesService {\n private client: PlacesClient;\n private readonly apiKey: string;\n private readonly defaultLanguage: string = \"en\";\n private readonly placeFieldMask: string = [\n \"displayName\",\n \"name\",\n \"id\",\n \"formattedAddress\",\n \"location\",\n \"utcOffsetMinutes\",\n \"primaryType\",\n \"types\",\n \"regularOpeningHours.periods\",\n \"regularOpeningHours.weekdayDescriptions\",\n \"currentOpeningHours.openNow\",\n \"currentOpeningHours.periods\",\n \"currentOpeningHours.weekdayDescriptions\",\n \"nationalPhoneNumber\",\n \"websiteUri\",\n \"priceLevel\",\n \"rating\",\n \"userRatingCount\",\n \"editorialSummary\",\n \"reviews.rating\",\n \"reviews.text\",\n \"reviews.publishTime\",\n \"reviews.authorAttribution.displayName\",\n \"reviews.authorAttribution.uri\",\n \"reviews.authorAttribution.photoUri\",\n \"reviews.googleMapsUri\",\n \"reviews.flagContentUri\",\n \"reviews.relativePublishTimeDescription\",\n \"photos.heightPx\",\n \"photos.widthPx\",\n \"photos.name\",\n \"photos.authorAttributions\",\n \"photos.googleMapsUri\",\n \"photos.flagContentUri\",\n \"googleMapsUri\",\n // Parking & Accessibility\n \"parkingOptions\",\n \"accessibilityOptions\",\n // Food & Drink\n \"servesVegetarianFood\",\n \"servesBeer\",\n \"servesWine\",\n \"servesCocktails\",\n \"servesBreakfast\",\n \"servesLunch\",\n \"servesDinner\",\n \"servesBrunch\",\n \"servesCoffee\",\n \"servesDessert\",\n // Dining options\n \"dineIn\",\n \"delivery\",\n \"takeout\",\n \"curbsidePickup\",\n \"reservable\",\n // Pricing (Money structure preserved as returned by the API)\n \"priceRange\",\n \"businessStatus\",\n // Atmosphere\n \"goodForGroups\",\n \"goodForChildren\",\n \"goodForWatchingSports\",\n \"liveMusic\",\n \"outdoorSeating\",\n \"allowsDogs\",\n \"menuForChildren\",\n \"restroom\",\n // Payment\n \"paymentOptions\",\n // AI Summaries (region-limited)\n \"reviewSummary\",\n \"generativeSummary\",\n ].join(\",\");\n\n private readonly searchNearbyFieldMask: string = [\n \"places.displayName\",\n \"places.name\",\n \"places.id\",\n \"places.formattedAddress\",\n \"places.location\",\n \"places.rating\",\n \"places.userRatingCount\",\n \"places.currentOpeningHours.openNow\",\n \"places.primaryType\",\n \"places.priceLevel\",\n \"places.types\",\n \"places.businessStatus\",\n \"places.googleMapsUri\",\n \"places.priceRange\",\n ].join(\",\");\n\n /**\n * FieldMask for Text Search (New). Includes every field required for\n * budget/quality/category/opening-hours validation (price levels, price range\n * with the original Money structure, types, business status, regular and\n * current opening hours) plus nextPageToken for pagination.\n */\n private readonly searchTextFieldMask: string = [\n \"places.id\",\n \"places.displayName\",\n \"places.formattedAddress\",\n \"places.location\",\n \"places.primaryType\",\n \"places.types\",\n \"places.businessStatus\",\n \"places.googleMapsUri\",\n \"places.priceLevel\",\n \"places.priceRange\",\n \"places.rating\",\n \"places.userRatingCount\",\n \"places.regularOpeningHours.periods\",\n \"places.regularOpeningHours.weekdayDescriptions\",\n \"places.currentOpeningHours.openNow\",\n \"places.currentOpeningHours.periods\",\n \"places.currentOpeningHours.weekdayDescriptions\",\n \"nextPageToken\",\n ].join(\",\");\n constructor(apiKey?: string) {\n this.apiKey = apiKey || process.env.GOOGLE_MAPS_API_KEY || \"\";\n this.client = new PlacesClient({ apiKey: this.apiKey });\n\n if (!this.apiKey) {\n throw new Error(\"Google Maps API Key is required\");\n }\n }\n\n async searchNearby(params: {\n location: { lat: number; lng: number };\n keyword?: string;\n radius?: number;\n maxResultCount?: number;\n }): Promise<any[]> {\n try {\n const request: any = {\n locationRestriction: {\n circle: {\n center: {\n latitude: params.location.lat,\n longitude: params.location.lng,\n },\n radius: params.radius || 1000,\n },\n },\n maxResultCount: Math.min(params.maxResultCount || 20, 20),\n languageCode: this.defaultLanguage,\n };\n\n if (params.keyword) {\n request.includedTypes = [params.keyword];\n }\n\n const [response] = await this.client.searchNearby(request, {\n otherArgs: {\n headers: {\n \"X-Goog-FieldMask\": this.searchNearbyFieldMask,\n },\n },\n });\n\n return (response.places || []).map((place: any) => this.transformSearchResult(place));\n } catch (error: any) {\n Logger.error(\"Error in searchNearby (New API):\", error);\n throw new Error(`Failed to search nearby places: ${this.extractErrorMessage(error)}`);\n }\n }\n\n /**\n * Backward-compatible Text Search (New) returning only the transformed\n * places array.\n */\n async searchText(params: {\n textQuery: string;\n locationBias?: { lat: number; lng: number; radius?: number };\n openNow?: boolean;\n minRating?: number;\n includedType?: string;\n strictTypeFiltering?: boolean;\n priceLevels?: string[];\n pageSize?: number;\n pageToken?: string;\n languageCode?: string;\n maxResultCount?: number;\n }): Promise<any[]> {\n const result = await this.searchTextFull(params);\n return result.places;\n }\n\n /**\n * Text Search (New) with the full eatspot-discovery filter surface (REQ-027, REQ-109):\n * - priceLevels: cumulative server-side economic filter (enum strings)\n * - strictTypeFiltering: strict place-type filtering\n * - minRating, includedType, openNow: server-side filters\n * - locationBias, pageSize/pageToken: geographic bias and pagination\n * - includeRaw: attaches the original API payload (raw_place per result and\n * the full raw_response) so callers can validate against real data.\n */\n async searchTextFull(params: {\n textQuery: string;\n locationBias?: { lat: number; lng: number; radius?: number };\n openNow?: boolean;\n minRating?: number;\n includedType?: string;\n strictTypeFiltering?: boolean;\n priceLevels?: string[];\n pageSize?: number;\n pageToken?: string;\n languageCode?: string;\n maxResultCount?: number;\n includeRaw?: boolean;\n }): Promise<{\n places: any[];\n nextPageToken?: string;\n fieldMask: string;\n raw?: any;\n }> {\n try {\n const request: any = {\n textQuery: params.textQuery,\n languageCode: params.languageCode || this.defaultLanguage,\n };\n\n // NOTE ON PAGINATION: the SearchTextRequest proto shipped with\n // @googlemaps/places (verified up to v3.1.0) does not include\n // page_size/page_token, so those fields are silently dropped during\n // protobuf serialization. maxResultCount (1-20) is the effective result\n // limiter available with the official SDK; pageSize is accepted as an\n // alias and translated to maxResultCount.\n const requestedCount = params.pageSize ?? params.maxResultCount ?? 10;\n request.maxResultCount = Math.min(Math.max(1, Math.round(requestedCount)), 20);\n if (params.pageToken) {\n request.pageToken = params.pageToken; // forwarded; dropped by current SDK protos\n }\n\n if (params.locationBias) {\n request.locationBias = {\n circle: {\n center: {\n latitude: params.locationBias.lat,\n longitude: params.locationBias.lng,\n },\n radius: params.locationBias.radius || 5000,\n },\n };\n }\n\n if (params.openNow) {\n request.openNow = true;\n }\n\n if (params.minRating != null) {\n request.minRating = params.minRating;\n }\n\n if (params.includedType) {\n request.includedType = params.includedType;\n }\n\n if (params.strictTypeFiltering != null) {\n request.strictTypeFiltering = params.strictTypeFiltering;\n }\n\n if (params.priceLevels?.length) {\n request.priceLevels = params.priceLevels;\n }\n\n if (params.pageToken) {\n request.pageToken = params.pageToken;\n }\n\n const [response] = await this.client.searchText(request, {\n otherArgs: {\n headers: {\n \"X-Goog-FieldMask\": this.searchTextFieldMask,\n },\n },\n });\n\n const places = (response.places || []).map((place: any) => {\n const transformed = this.transformSearchResult(place);\n return params.includeRaw ? { ...transformed, raw_place: place } : transformed;\n });\n\n // ISearchTextResponse typings in @googlemaps/places@2.x predate Text Search\n // pagination, but the REST API does return nextPageToken when requested in\n // the field mask — read it through an any-typed view of the response.\n const paginated = response as any;\n\n return {\n places,\n ...(paginated.nextPageToken ? { nextPageToken: paginated.nextPageToken } : {}),\n fieldMask: this.searchTextFieldMask,\n ...(params.includeRaw ? { raw: response } : {}),\n };\n } catch (error: any) {\n Logger.error(\"Error in searchText (New API):\", error);\n throw new Error(`Failed to search places: ${this.extractErrorMessage(error)}`);\n }\n }\n\n async getPhotoUri(photoName: string, maxWidthPx: number = 800): Promise<string> {\n try {\n const [response] = await this.client.getPhotoMedia({\n name: `${photoName}/media`,\n maxWidthPx,\n skipHttpRedirect: true,\n });\n return response.photoUri || \"\";\n } catch (error: any) {\n Logger.error(\"Error in getPhotoUri:\", error);\n throw new Error(`Failed to get photo URI: ${this.extractErrorMessage(error)}`);\n }\n }\n\n async getPlaceDetails(placeId: string, includeRaw: boolean = false): Promise<any> {\n try {\n const placeName = `places/${placeId}`;\n\n const [place] = await this.client.getPlace(\n {\n name: placeName,\n languageCode: this.defaultLanguage,\n },\n {\n otherArgs: {\n headers: {\n \"X-Goog-FieldMask\": this.placeFieldMask,\n },\n },\n }\n );\n\n // Use only Places (New) reviews: the legacy reviews endpoint does not\n // provide the per-review Google Maps source link required for display.\n const transformed = this.transformPlaceResponse(place);\n return includeRaw ? { ...transformed, raw_place: place, field_mask: this.placeFieldMask } : transformed;\n } catch (error: any) {\n Logger.error(\"Error in getPlaceDetails (New API):\", error);\n throw new Error(`Failed to get place details for ${placeId}: ${this.extractErrorMessage(error)}`);\n }\n }\n\n private transformSearchResult(place: any) {\n return {\n name: place.displayName?.text || \"\",\n place_id: this.extractLegacyPlaceId(place),\n formatted_address: place.formattedAddress || \"\",\n geometry: {\n location: {\n lat: place.location?.latitude || 0,\n lng: place.location?.longitude || 0,\n },\n },\n primary_type: place.primaryType || null,\n types: place.types || [],\n business_status: place.businessStatus || null,\n google_maps_uri: place.googleMapsUri || null,\n price_level: place.priceLevel || null,\n // Raw Money object preserved exactly as returned by the API\n // ({ startPrice/endPrice: { currencyCode, units, nanos } })\n price_range: place.priceRange || null,\n rating: place.rating || 0,\n user_ratings_total: place.userRatingCount || 0,\n opening_hours: {\n open_now: place.currentOpeningHours?.openNow ?? null,\n },\n regular_opening_hours: place.regularOpeningHours || null,\n current_opening_hours: place.currentOpeningHours || null,\n };\n }\n\n private transformPlaceResponse(place: any) {\n // Build parking info (only include truthy values)\n const parking = place.parkingOptions\n ? Object.fromEntries(Object.entries(place.parkingOptions).filter(([, v]) => v === true))\n : undefined;\n\n // Build accessibility info (only include truthy values)\n const accessibility = place.accessibilityOptions\n ? Object.fromEntries(Object.entries(place.accessibilityOptions).filter(([, v]) => v === true))\n : undefined;\n\n // Build dining_options (only include truthy values)\n const diningOptions: Record<string, boolean> = {};\n if (place.dineIn) diningOptions.dine_in = true;\n if (place.delivery) diningOptions.delivery = true;\n if (place.takeout) diningOptions.takeout = true;\n if (place.curbsidePickup) diningOptions.curbside_pickup = true;\n if (place.reservable) diningOptions.reservable = true;\n\n // Build serves (only include truthy values)\n const serves: Record<string, boolean> = {};\n if (place.servesVegetarianFood) serves.vegetarian_food = true;\n if (place.servesBeer) serves.beer = true;\n if (place.servesWine) serves.wine = true;\n if (place.servesCocktails) serves.cocktails = true;\n if (place.servesBreakfast) serves.breakfast = true;\n if (place.servesLunch) serves.lunch = true;\n if (place.servesDinner) serves.dinner = true;\n if (place.servesBrunch) serves.brunch = true;\n if (place.servesCoffee) serves.coffee = true;\n if (place.servesDessert) serves.dessert = true;\n\n // Build atmosphere (only include truthy values)\n const atmosphere: Record<string, boolean> = {};\n if (place.goodForGroups) atmosphere.good_for_groups = true;\n if (place.goodForChildren) atmosphere.good_for_children = true;\n if (place.goodForWatchingSports) atmosphere.good_for_watching_sports = true;\n if (place.liveMusic) atmosphere.live_music = true;\n if (place.outdoorSeating) atmosphere.outdoor_seating = true;\n if (place.allowsDogs) atmosphere.allows_dogs = true;\n if (place.menuForChildren) atmosphere.menu_for_children = true;\n if (place.restroom) atmosphere.restroom = true;\n\n return {\n name: place.displayName?.text || place.name || \"\",\n place_id: this.extractLegacyPlaceId(place),\n formatted_address: place.formattedAddress || \"\",\n google_maps_uri: place.googleMapsUri || \"\",\n geometry: {\n location: {\n lat: place.location?.latitude || 0,\n lng: place.location?.longitude || 0,\n },\n },\n primary_type: place.primaryType || null,\n types: place.types || [],\n business_status: place.businessStatus || null,\n rating: place.rating || 0,\n user_ratings_total: place.userRatingCount || 0,\n // Raw Money object preserved exactly as returned by the API\n // ({ startPrice/endPrice: { currencyCode, units, nanos } })\n price_range: place.priceRange || null,\n opening_hours: place.regularOpeningHours\n ? {\n open_now: this.isCurrentlyOpen(\n place.regularOpeningHours,\n place.utcOffsetMinutes,\n place.currentOpeningHours\n ),\n weekday_text: this.formatOpeningHours(place.regularOpeningHours),\n }\n : undefined,\n current_opening_hours: place.currentOpeningHours || null,\n formatted_phone_number: place.nationalPhoneNumber || \"\",\n website: place.websiteUri || \"\",\n price_level: place.priceLevel || 0,\n editorial_summary: place.editorialSummary?.text || null,\n ...(Object.keys(parking || {}).length > 0 ? { parking } : {}),\n ...(Object.keys(accessibility || {}).length > 0 ? { accessibility } : {}),\n ...(Object.keys(diningOptions).length > 0 ? { dining_options: diningOptions } : {}),\n ...(Object.keys(serves).length > 0 ? { serves } : {}),\n ...(Object.keys(atmosphere).length > 0 ? { atmosphere } : {}),\n ...(place.paymentOptions\n ? {\n payment_options: Object.fromEntries(\n Object.entries(place.paymentOptions).filter(([k]) => !k.startsWith(\"_\"))\n ),\n }\n : {}),\n ...(place.reviewSummary?.text?.text\n ? {\n review_summary: place.reviewSummary.text.text,\n review_summary_attribution: {\n disclosure_text: place.reviewSummary.disclosureText?.text || \"\",\n flag_content_uri: place.reviewSummary.flagContentUri || \"\",\n reviews_uri: place.reviewSummary.reviewsUri || \"\",\n },\n }\n : {}),\n ...(place.generativeSummary?.overview?.text\n ? {\n generative_summary: place.generativeSummary.overview.text,\n generative_summary_attribution: {\n disclosure_text: place.generativeSummary.disclosureText?.text || \"\",\n flag_content_uri: place.generativeSummary.flagContentUri || \"\",\n },\n }\n : {}),\n reviews:\n place.reviews?.map((review: any) => ({\n rating: review.rating || 0,\n text: review.text?.text || \"\",\n language: review.text?.languageCode || null,\n time: review.publishTime?.seconds || 0,\n author_name: review.authorAttribution?.displayName || \"\",\n author_uri: review.authorAttribution?.uri || \"\",\n author_photo_uri: review.authorAttribution?.photoUri || \"\",\n google_maps_uri: review.googleMapsUri || \"\",\n flag_content_uri: review.flagContentUri || \"\",\n relative_publish_time_description: review.relativePublishTimeDescription || \"\",\n })) || [],\n photos:\n place.photos?.map((photo: any) => ({\n photo_reference: photo.name || \"\",\n height: photo.heightPx || 0,\n width: photo.widthPx || 0,\n google_maps_uri: photo.googleMapsUri || \"\",\n flag_content_uri: photo.flagContentUri || \"\",\n author_attributions:\n photo.authorAttributions?.map((author: any) => ({\n display_name: author.displayName || \"\",\n uri: author.uri || \"\",\n photo_uri: author.photoUri || \"\",\n })) || [],\n })) || [],\n };\n }\n\n private extractLegacyPlaceId(place: any): string {\n const resourceName = place?.name;\n\n if (typeof resourceName === \"string\" && resourceName.startsWith(\"places/\")) {\n const legacyId = resourceName.substring(\"places/\".length);\n if (legacyId) {\n return legacyId;\n }\n }\n\n return place?.id || \"\";\n }\n\n private isCurrentlyOpen(openingHours: any, utcOffsetMinutes?: number, currentOpeningHours?: any): boolean {\n if (typeof currentOpeningHours?.openNow === \"boolean\") {\n return currentOpeningHours.openNow;\n }\n\n if (typeof openingHours?.openNow === \"boolean\") {\n return openingHours.openNow;\n }\n\n const periods = openingHours?.periods;\n\n if (!Array.isArray(periods) || periods.length === 0) {\n return false;\n }\n\n const minutesInDay = 24 * 60;\n const minutesInWeek = minutesInDay * 7;\n\n const { day: localDay, minutes: localMinutes } = this.getLocalTimeComponents(utcOffsetMinutes);\n const localTimeValue = localDay * minutesInDay + localMinutes;\n\n const dayMapping = {\n SUNDAY: 0,\n MONDAY: 1,\n TUESDAY: 2,\n WEDNESDAY: 3,\n THURSDAY: 4,\n FRIDAY: 5,\n SATURDAY: 6,\n };\n\n const toDayNumber = (value: any): number | undefined => {\n if (typeof value === \"number\" && value >= 0 && value <= 6) {\n return value;\n }\n if (typeof value === \"string\") {\n const normalized = value.toUpperCase();\n if (normalized in dayMapping) {\n return dayMapping[normalized as keyof typeof dayMapping];\n }\n }\n return undefined;\n };\n\n const toMinutes = (time: any): number | undefined => {\n if (!time) {\n return undefined;\n }\n\n const hours = typeof time.hours === \"number\" ? time.hours : Number(time.hours ?? NaN);\n const minutes = typeof time.minutes === \"number\" ? time.minutes : Number(time.minutes ?? NaN);\n\n if (!Number.isFinite(hours) || !Number.isFinite(minutes)) {\n return undefined;\n }\n\n return hours * 60 + minutes;\n };\n\n for (const period of periods) {\n const openDay = toDayNumber(period?.openDay);\n const closeDay = toDayNumber(period?.closeDay ?? period?.openDay);\n const openMinutes = toMinutes(period?.openTime);\n const closeMinutes = toMinutes(period?.closeTime);\n\n if (openDay === undefined || openMinutes === undefined) {\n continue;\n }\n\n const start = openDay * minutesInDay + openMinutes;\n let end: number;\n\n if (closeDay === undefined || closeMinutes === undefined) {\n end = start + minutesInDay;\n } else {\n end = closeDay * minutesInDay + closeMinutes;\n }\n\n if (end <= start) {\n end += minutesInWeek;\n }\n\n let comparableLocalTime = localTimeValue;\n while (comparableLocalTime < start) {\n comparableLocalTime += minutesInWeek;\n }\n\n if (comparableLocalTime >= start && comparableLocalTime < end) {\n return true;\n }\n }\n\n return false;\n }\n\n private getLocalTimeComponents(utcOffsetMinutes?: number): { day: number; minutes: number } {\n const now = new Date();\n\n if (typeof utcOffsetMinutes === \"number\" && Number.isFinite(utcOffsetMinutes)) {\n const localTime = new Date(now.getTime() + utcOffsetMinutes * 60000);\n\n return {\n day: localTime.getUTCDay(),\n minutes: localTime.getUTCHours() * 60 + localTime.getUTCMinutes(),\n };\n }\n\n return {\n day: now.getDay(),\n minutes: now.getHours() * 60 + now.getMinutes(),\n };\n }\n\n private formatOpeningHours(openingHours: any): string[] {\n return openingHours?.weekdayDescriptions || [];\n }\n\n private extractErrorMessage(error: any): string {\n const statusCode = error?.code;\n const message = error?.message || error?.details;\n\n if (statusCode === 7 || statusCode === 403) {\n return \"API key invalid or Places API (New) not enabled. Check: console.cloud.google.com → APIs & Services → Enable 'Places API (New)'\";\n }\n if (statusCode === 8 || statusCode === 429) {\n return \"API quota exceeded. Wait and retry, or check quota at console.cloud.google.com → Quotas\";\n }\n\n return message || (error instanceof Error ? error.message : String(error));\n }\n}\n","import { GoogleMapsTools } from \"./toolclass.js\";\nimport { NewPlacesService } from \"./NewPlacesService.js\";\nimport {\n RoutesService,\n parseDuration,\n formatDistance,\n formatDuration,\n COORDINATE_STRING_PATTERN,\n type RouteWaypoint,\n} from \"./RoutesService.js\";\n\ninterface SearchResponse {\n success: boolean;\n error?: string;\n data?: any[];\n location?: any;\n nextPageToken?: string;\n fieldMask?: string;\n raw_response?: any;\n}\n\ninterface PlaceDetailsResponse {\n success: boolean;\n error?: string;\n data?: any;\n fieldMask?: string;\n}\n\ninterface GeocodeResponse {\n success: boolean;\n error?: string;\n data?: {\n location: { lat: number; lng: number };\n formatted_address: string;\n place_id: string;\n };\n}\n\ninterface ReverseGeocodeResponse {\n success: boolean;\n error?: string;\n data?: {\n formatted_address: string;\n place_id: string;\n address_components: any[];\n };\n}\n\ninterface DistanceMatrixResponse {\n success: boolean;\n error?: string;\n data?: {\n distances: any[][];\n durations: any[][];\n origin_addresses: string[];\n destination_addresses: string[];\n };\n}\n\ninterface DirectionsResponse {\n success: boolean;\n error?: string;\n data?: {\n routes: any[];\n summary: string;\n total_distance: { value: number; text: string };\n total_duration: { value: number; text: string };\n };\n}\n\ninterface TimezoneResponse {\n success: boolean;\n error?: string;\n data?: {\n timeZoneId: string;\n timeZoneName: string;\n utcOffset: number;\n dstOffset: number;\n localTime: string;\n };\n}\n\ninterface WeatherResponse {\n success: boolean;\n error?: string;\n data?: any;\n}\n\ninterface StaticMapResponse {\n success: boolean;\n error?: string;\n data?: {\n base64: string;\n size: number;\n dimensions: string;\n };\n}\n\ninterface AirQualityResponse {\n success: boolean;\n error?: string;\n data?: any;\n}\n\ninterface ElevationResponse {\n success: boolean;\n error?: string;\n data?: Array<{\n elevation: number;\n location: { lat: number; lng: number };\n }>;\n}\n\ninterface ResolvedLocation {\n originalName: string;\n address: string;\n lat: number;\n lng: number;\n placeId: string;\n}\n\n/**\n * Parse a \"latitude,longitude\" input, or null when it is not one or is out of range.\n */\nfunction parseCoordinateInput(input: string): { lat: number; lng: number } | null {\n const match = input.match(COORDINATE_STRING_PATTERN);\n if (!match) {\n return null;\n }\n const lat = parseFloat(match[1]);\n const lng = parseFloat(match[2]);\n if (!Number.isFinite(lat) || !Number.isFinite(lng)) {\n return null;\n }\n if (lat < -90 || lat > 90 || lng < -180 || lng > 180) {\n return null;\n }\n return { lat, lng };\n}\n\n/**\n * Place ID over coordinates: Routes snaps coordinates to the nearest road, which\n * may not be an entrance. Text Search can return a result without a place ID.\n */\nfunction toRouteWaypoint(loc: ResolvedLocation): RouteWaypoint {\n if (loc.placeId) {\n return { placeId: loc.placeId };\n }\n return { latLng: { latitude: loc.lat, longitude: loc.lng } };\n}\n\nexport class PlacesSearcher {\n private mapsTools: GoogleMapsTools;\n private newPlacesService: NewPlacesService;\n private routesService: RoutesService;\n\n constructor(apiKey?: string) {\n this.mapsTools = new GoogleMapsTools(apiKey);\n this.newPlacesService = new NewPlacesService(apiKey);\n this.routesService = new RoutesService(apiKey);\n }\n\n async searchNearby(params: {\n center: { value: string; isCoordinates: boolean };\n keyword?: string;\n radius?: number;\n openNow?: boolean;\n minRating?: number;\n }): Promise<SearchResponse> {\n try {\n const location = await this.mapsTools.getLocation(params.center);\n const places = await this.newPlacesService.searchNearby({\n location,\n keyword: params.keyword,\n radius: params.radius,\n });\n\n let filteredPlaces = places;\n if (params.openNow) {\n filteredPlaces = filteredPlaces.filter((p: any) => p.opening_hours?.open_now === true);\n }\n if (params.minRating) {\n filteredPlaces = filteredPlaces.filter((p: any) => (p.rating || 0) >= (params.minRating || 0));\n }\n\n return {\n location: location,\n success: true,\n data: filteredPlaces.map((place: any) => ({\n name: place.name,\n place_id: place.place_id,\n address: place.formatted_address,\n location: place.geometry.location,\n primary_type: place.primary_type || null,\n price_level: place.price_level || null,\n rating: place.rating,\n total_ratings: place.user_ratings_total,\n open_now: place.opening_hours?.open_now,\n })),\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred during search\",\n };\n }\n }\n\n async searchText(params: {\n query: string;\n locationBias?: { latitude: number; longitude: number; radius?: number };\n openNow?: boolean;\n minRating?: number;\n includedType?: string;\n strictTypeFiltering?: boolean;\n priceLevels?: string[];\n pageSize?: number;\n pageToken?: string;\n languageCode?: string;\n includeRaw?: boolean;\n }): Promise<SearchResponse> {\n try {\n const result = await this.newPlacesService.searchTextFull({\n textQuery: params.query,\n locationBias: params.locationBias\n ? {\n lat: params.locationBias.latitude,\n lng: params.locationBias.longitude,\n radius: params.locationBias.radius,\n }\n : undefined,\n openNow: params.openNow,\n minRating: params.minRating,\n includedType: params.includedType,\n strictTypeFiltering: params.strictTypeFiltering,\n priceLevels: params.priceLevels,\n pageSize: params.pageSize,\n pageToken: params.pageToken,\n languageCode: params.languageCode,\n includeRaw: params.includeRaw,\n });\n\n return {\n success: true,\n data: result.places.map((place: any) => ({\n name: place.name,\n place_id: place.place_id,\n address: place.formatted_address,\n location: place.geometry.location,\n primary_type: place.primary_type || null,\n types: place.types || [],\n business_status: place.business_status || null,\n google_maps_uri: place.google_maps_uri || null,\n price_level: place.price_level || null,\n price_range: place.price_range || null,\n rating: place.rating,\n total_ratings: place.user_ratings_total,\n open_now: place.opening_hours?.open_now,\n regular_opening_hours: place.regular_opening_hours || null,\n current_opening_hours: place.current_opening_hours || null,\n ...(place.raw_place ? { raw_place: place.raw_place } : {}),\n })),\n ...(result.nextPageToken ? { nextPageToken: result.nextPageToken } : {}),\n fieldMask: result.fieldMask,\n ...(params.includeRaw && result.raw ? { raw_response: result.raw } : {}),\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred during text search\",\n };\n }\n }\n\n async getPlaceDetails(\n placeId: string,\n maxPhotos: number = 0,\n includeRaw: boolean = false\n ): Promise<PlaceDetailsResponse> {\n try {\n const details = await this.newPlacesService.getPlaceDetails(placeId, includeRaw);\n\n // Resolve photo URLs if requested\n let photos:\n | Array<{\n url: string;\n width: number;\n height: number;\n google_maps_uri: string;\n flag_content_uri: string;\n author_attributions: Array<{ display_name: string; uri: string; photo_uri: string }>;\n }>\n | undefined;\n if (maxPhotos > 0 && details.photos?.length > 0) {\n const photosToFetch = details.photos.slice(0, maxPhotos);\n photos = [];\n for (const photo of photosToFetch) {\n try {\n const url = await this.newPlacesService.getPhotoUri(photo.photo_reference);\n photos.push({\n url,\n width: photo.width,\n height: photo.height,\n google_maps_uri: photo.google_maps_uri,\n flag_content_uri: photo.flag_content_uri,\n author_attributions: photo.author_attributions,\n });\n } catch {\n // Skip failed photos silently\n }\n }\n }\n\n return {\n success: true,\n ...(details.field_mask ? { fieldMask: details.field_mask } : {}),\n data: {\n name: details.name,\n address: details.formatted_address,\n google_maps_uri: details.google_maps_uri,\n location: details.geometry?.location,\n primary_type: details.primary_type || null,\n types: details.types || [],\n business_status: details.business_status || null,\n rating: details.rating,\n total_ratings: details.user_ratings_total,\n // Raw Money object (startPrice/endPrice: currencyCode/units/nanos)\n price_range: details.price_range || null,\n current_opening_hours: details.current_opening_hours || null,\n opening_hours: details.opening_hours,\n phone: details.formatted_phone_number,\n website: details.website,\n price_level: details.price_level,\n editorial_summary: details.editorial_summary || null,\n ...(details.parking ? { parking: details.parking } : {}),\n ...(details.accessibility ? { accessibility: details.accessibility } : {}),\n ...(details.dining_options ? { dining_options: details.dining_options } : {}),\n ...(details.serves ? { serves: details.serves } : {}),\n ...(details.atmosphere ? { atmosphere: details.atmosphere } : {}),\n ...(details.payment_options ? { payment_options: details.payment_options } : {}),\n ...(details.review_summary ? { review_summary: details.review_summary } : {}),\n ...(details.review_summary_attribution\n ? { review_summary_attribution: details.review_summary_attribution }\n : {}),\n ...(details.generative_summary ? { generative_summary: details.generative_summary } : {}),\n ...(details.generative_summary_attribution\n ? { generative_summary_attribution: details.generative_summary_attribution }\n : {}),\n photo_count: details.photos?.length || 0,\n ...(photos && photos.length > 0 ? { photos } : {}),\n ...(details.raw_place ? { raw_place: details.raw_place } : {}),\n reviews: details.reviews?.map((review: any) => ({\n rating: review.rating,\n text: review.text,\n language: review.language || null,\n time: review.time,\n author_name: review.author_name,\n author_uri: review.author_uri,\n author_photo_uri: review.author_photo_uri,\n google_maps_uri: review.google_maps_uri,\n flag_content_uri: review.flag_content_uri,\n relative_publish_time_description: review.relative_publish_time_description,\n })),\n },\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred while getting place details\",\n };\n }\n }\n\n async geocode(address: string): Promise<GeocodeResponse> {\n try {\n const result = await this.mapsTools.geocode(address);\n\n return {\n success: true,\n data: result,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred while geocoding address\",\n };\n }\n }\n\n async reverseGeocode(latitude: number, longitude: number): Promise<ReverseGeocodeResponse> {\n try {\n const result = await this.mapsTools.reverseGeocode(latitude, longitude);\n\n return {\n success: true,\n data: result,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred during reverse geocoding\",\n };\n }\n }\n\n async calculateDistanceMatrix(\n origins: string[],\n destinations: string[],\n mode: \"driving\" | \"walking\" | \"bicycling\" | \"transit\" = \"driving\",\n departure_time?: string,\n avoid_tolls?: boolean,\n avoid_highways?: boolean\n ): Promise<DistanceMatrixResponse> {\n try {\n const result = await this.routesService.computeRouteMatrix({\n origins,\n destinations,\n mode,\n ...(departure_time ? { departureTime: new Date(departure_time) } : {}),\n ...(avoid_tolls !== undefined ? { avoidTolls: avoid_tolls } : {}),\n ...(avoid_highways !== undefined ? { avoidHighways: avoid_highways } : {}),\n });\n\n return {\n success: true,\n data: result,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred while calculating distance matrix\",\n };\n }\n }\n\n async getDirections(\n origin: string,\n destination: string,\n mode: \"driving\" | \"walking\" | \"bicycling\" | \"transit\" = \"driving\",\n departure_time?: string,\n arrival_time?: string,\n avoid_tolls?: boolean,\n avoid_highways?: boolean\n ): Promise<DirectionsResponse> {\n try {\n const departureTime = departure_time ? new Date(departure_time) : undefined;\n const arrivalTime = arrival_time ? new Date(arrival_time) : undefined;\n const result = await this.routesService.computeRoutes({\n origin,\n destination,\n mode,\n ...(departureTime ? { departureTime } : {}),\n ...(arrivalTime ? { arrivalTime } : {}),\n ...(avoid_tolls !== undefined ? { avoidTolls: avoid_tolls } : {}),\n ...(avoid_highways !== undefined ? { avoidHighways: avoid_highways } : {}),\n });\n\n return {\n success: true,\n data: result,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred while getting directions\",\n };\n }\n }\n\n async getTimezone(latitude: number, longitude: number, timestamp?: number): Promise<TimezoneResponse> {\n try {\n const result = await this.mapsTools.getTimezone(latitude, longitude, timestamp);\n return { success: true, data: result };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred while getting timezone\",\n };\n }\n }\n\n async getWeather(\n latitude: number,\n longitude: number,\n type: \"current\" | \"forecast_daily\" | \"forecast_hourly\" = \"current\",\n forecastDays?: number,\n forecastHours?: number\n ): Promise<WeatherResponse> {\n try {\n const result = await this.mapsTools.getWeather(latitude, longitude, type, forecastDays, forecastHours);\n return { success: true, data: result };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred while getting weather\",\n };\n }\n }\n\n async getAirQuality(\n latitude: number,\n longitude: number,\n includeHealthRecommendations?: boolean,\n includePollutants?: boolean\n ): Promise<AirQualityResponse> {\n try {\n const result = await this.mapsTools.getAirQuality(\n latitude,\n longitude,\n includeHealthRecommendations,\n includePollutants\n );\n return { success: true, data: result };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred while getting air quality\",\n };\n }\n }\n\n async getStaticMap(params: {\n center?: string;\n zoom?: number;\n size?: string;\n maptype?: string;\n markers?: string[];\n path?: string[];\n }): Promise<StaticMapResponse> {\n try {\n const result = await this.mapsTools.getStaticMap(params);\n return { success: true, data: result };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred while generating static map\",\n };\n }\n }\n\n async searchAlongRoute(params: {\n textQuery: string;\n origin: string;\n destination: string;\n mode?: string;\n maxResults?: number;\n }): Promise<{ success: boolean; error?: string; data?: any }> {\n try {\n const result = await this.mapsTools.searchAlongRoute(params);\n return { success: true, data: result };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred while searching along route\",\n };\n }\n }\n\n // --------------- Composite Tools ---------------\n\n /**\n * Resolve a location string to one concrete place, exactly once: geocoding\n * first, then Places Text Search for informal names it does not index.\n *\n * Throws rather than returning { success: false } — the plan_route and\n * explore_area actions JSON.stringify(result.data) without checking the flag,\n * so a failure object would surface as an empty success.\n */\n private async resolveLocation(input: string): Promise<ResolvedLocation> {\n if (COORDINATE_STRING_PATTERN.test(input)) {\n const coordinates = parseCoordinateInput(input);\n if (!coordinates) {\n throw new Error(`Failed to resolve location: ${input} (not a valid \"latitude,longitude\" pair)`);\n }\n // Already exact, so geocoding only supplies a display address and its failure\n // must not fail the stop. No place ID: the nearest addressable place would\n // silently move the caller's point. geocode() over reverseGeocode() because it\n // labels sparse areas better — \"Canada\" rather than a bare Plus Code.\n const displayGeocode = await this.geocode(input);\n return {\n originalName: input,\n address: displayGeocode.success && displayGeocode.data ? displayGeocode.data.formatted_address : input,\n lat: coordinates.lat,\n lng: coordinates.lng,\n placeId: \"\",\n };\n }\n\n const geo = await this.geocode(input);\n if (geo.success && geo.data) {\n return {\n originalName: input,\n address: geo.data.formatted_address,\n lat: geo.data.location.lat,\n lng: geo.data.location.lng,\n placeId: geo.data.place_id,\n };\n }\n\n // Any geocoding failure falls through, not just zero results: geocode() flattens\n // every error into { success: false } with no status code. A systemic failure\n // (bad key, exhausted quota) fails the text search too, and both errors are thrown.\n const textSearch = await this.searchText({ query: input });\n if (textSearch.success && textSearch.data && textSearch.data.length > 0) {\n const topMatch = textSearch.data[0];\n return {\n originalName: input,\n address: topMatch.address || topMatch.name,\n lat: topMatch.location.lat,\n lng: topMatch.location.lng,\n placeId: topMatch.place_id || \"\",\n };\n }\n\n throw new Error(\n `Failed to resolve location: ${input} (geocoding: ${geo.error || \"no result\"}; places text search: ${textSearch.error || \"no result\"})`\n );\n }\n\n async exploreArea(params: { location: string; types?: string[]; radius?: number; topN?: number }): Promise<any> {\n // \"tourist_attraction\" is the Places API (New) type name; a bare\n // \"attraction\" is rejected with INVALID_ARGUMENT: Unsupported types.\n const types = params.types || [\"restaurant\", \"cafe\", \"tourist_attraction\"];\n const radius = params.radius || 1000;\n const topN = params.topN || 3;\n\n // 1. Resolve location\n const resolved = await this.resolveLocation(params.location);\n const { lat, lng } = resolved;\n\n // 2. Search each type\n const categories: any[] = [];\n for (const type of types) {\n const search = await this.searchNearby({\n center: { value: `${lat},${lng}`, isCoordinates: true },\n keyword: type,\n radius,\n });\n if (!search.success || !search.data) continue;\n\n // 3. Get details for top N\n const topPlaces = search.data.slice(0, topN);\n const detailed = [];\n for (const place of topPlaces) {\n if (!place.place_id) continue;\n const details = await this.getPlaceDetails(place.place_id);\n detailed.push({\n name: place.name,\n address: place.address,\n rating: place.rating,\n total_ratings: place.total_ratings,\n open_now: place.open_now,\n phone: details.data?.phone,\n website: details.data?.website,\n });\n }\n categories.push({ type, count: search.data.length, top: detailed });\n }\n\n return {\n success: true,\n data: {\n location: { address: resolved.address, lat, lng },\n radius,\n categories,\n },\n };\n }\n\n async planRoute(params: {\n stops: string[];\n mode?: \"driving\" | \"walking\" | \"bicycling\" | \"transit\";\n optimize?: boolean;\n departure_time?: string;\n avoid_tolls?: boolean;\n avoid_highways?: boolean;\n }): Promise<any> {\n const mode = params.mode || \"driving\";\n const stops = params.stops;\n if (stops.length < 2) throw new Error(\"Need at least 2 stops\");\n\n // 1. Resolve all stops for display addresses and waypoints\n const resolvedStops: ResolvedLocation[] = [];\n for (const stop of stops) {\n resolvedStops.push(await this.resolveLocation(stop));\n }\n\n // 2. Single Routes API call handles optimization + all leg directions\n const origin = toRouteWaypoint(resolvedStops[0]);\n const destination = toRouteWaypoint(resolvedStops[resolvedStops.length - 1]);\n const intermediates = stops.length > 2 ? resolvedStops.slice(1, -1).map((s) => toRouteWaypoint(s)) : undefined;\n // Optimize if requested, > 3 stops (at least 2 intermediates), and not transit (transit doesn't support intermediates for optimization)\n const shouldOptimize = params.optimize !== false && stops.length > 3 && mode !== \"transit\";\n\n const routeResult = await this.routesService.computeRoutes({\n origin,\n destination,\n mode,\n intermediates,\n optimizeWaypointOrder: shouldOptimize,\n originLabel: stops[0],\n destinationLabel: stops[stops.length - 1],\n ...(params.departure_time ? { departureTime: new Date(params.departure_time) } : {}),\n ...(params.avoid_tolls !== undefined ? { avoidTolls: params.avoid_tolls } : {}),\n ...(params.avoid_highways !== undefined ? { avoidHighways: params.avoid_highways } : {}),\n });\n\n const route = routeResult.routes[0];\n const routeLegs = route?.legs || [];\n\n // 3. Determine ordered stops based on optimization result\n let orderedStops: typeof resolvedStops;\n if (shouldOptimize && routeResult.optimizedIntermediateWaypointIndex) {\n const optimizedOrder = routeResult.optimizedIntermediateWaypointIndex;\n const intermediateResolved = resolvedStops.slice(1, -1);\n orderedStops = [\n resolvedStops[0],\n ...optimizedOrder.map((i: number) => intermediateResolved[i]),\n resolvedStops[resolvedStops.length - 1],\n ];\n } else {\n orderedStops = resolvedStops;\n }\n\n // 4. Build legs from Routes API response\n const legs: any[] = [];\n let totalDistance = 0;\n let totalDuration = 0;\n\n for (let i = 0; i < orderedStops.length - 1; i++) {\n const leg = routeLegs[i];\n if (leg) {\n const distMeters = leg.distanceMeters || 0;\n const durSeconds = parseDuration(leg.duration);\n totalDistance += distMeters;\n totalDuration += durSeconds;\n legs.push({\n from: orderedStops[i].originalName,\n to: orderedStops[i + 1].originalName,\n distance: formatDistance(distMeters),\n duration: formatDuration(durSeconds),\n });\n } else {\n legs.push({\n from: orderedStops[i].originalName,\n to: orderedStops[i + 1].originalName,\n distance: \"unknown\",\n duration: \"unknown\",\n note: \"Directions unavailable for this segment\",\n });\n }\n }\n\n return {\n success: true,\n data: {\n mode,\n optimized: shouldOptimize,\n stops: orderedStops.map((s) => `${s.originalName} (${s.address})`),\n legs,\n total_distance: `${(totalDistance / 1000).toFixed(1)} km`,\n total_duration: `${Math.round(totalDuration / 60)} min`,\n },\n };\n }\n\n async comparePlaces(params: {\n query: string;\n userLocation?: { latitude: number; longitude: number };\n limit?: number;\n }): Promise<any> {\n const limit = params.limit || 5;\n\n // 1. Search\n const search = await this.searchText({ query: params.query });\n if (!search.success || !search.data) throw new Error(search.error || \"Search failed\");\n\n const places = search.data.slice(0, limit);\n\n // 2. Get details for each\n const compared: any[] = [];\n for (const place of places) {\n const details = await this.getPlaceDetails(place.place_id);\n compared.push({\n name: place.name,\n address: place.address,\n primary_type: details.data?.primary_type || place.primary_type || null,\n rating: place.rating,\n total_ratings: place.total_ratings,\n opening_hours: details.data?.opening_hours,\n phone: details.data?.phone,\n website: details.data?.website,\n price_level: details.data?.price_level,\n ...(details.data?.parking ? { parking: details.data.parking } : {}),\n ...(details.data?.serves ? { serves: details.data.serves } : {}),\n ...(details.data?.atmosphere ? { atmosphere: details.data.atmosphere } : {}),\n ...(details.data?.dining_options ? { dining_options: details.data.dining_options } : {}),\n });\n }\n\n // 3. Distance from user location (if provided)\n if (params.userLocation && compared.length > 0) {\n const origin = `${params.userLocation.latitude},${params.userLocation.longitude}`;\n const destinations = places.map((p: any) => `${p.location.lat},${p.location.lng}`);\n const matrix = await this.calculateDistanceMatrix([origin], destinations, \"driving\");\n if (matrix.success && matrix.data) {\n for (let i = 0; i < compared.length; i++) {\n compared[i].distance = matrix.data.distances[0]?.[i]?.text;\n compared[i].drive_time = matrix.data.durations[0]?.[i]?.text;\n }\n }\n }\n\n return { success: true, data: compared };\n }\n\n async getElevation(locations: Array<{ latitude: number; longitude: number }>): Promise<ElevationResponse> {\n try {\n const result = await this.mapsTools.getElevation(locations);\n\n return {\n success: true,\n data: result,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred while getting elevation data\",\n };\n }\n }\n\n async localRankTracker(params: {\n keywords: string[];\n placeId: string;\n center: { latitude: number; longitude: number };\n gridSize?: number;\n gridSpacing?: number;\n }): Promise<any> {\n try {\n const gridSize = params.gridSize || 3;\n const spacingMeters = params.gridSpacing || 1000;\n const { latitude: centerLat, longitude: centerLng } = params.center;\n\n // Generate grid coordinates\n const half = Math.floor(gridSize / 2);\n const metersPerDegreeLat = 111320;\n const metersPerDegreeLng = 111320 * Math.cos((centerLat * Math.PI) / 180);\n\n const gridPoints: Array<{ row: number; col: number; lat: number; lng: number }> = [];\n for (let row = 0; row < gridSize; row++) {\n for (let col = 0; col < gridSize; col++) {\n const rowOffset = (row - half) * spacingMeters;\n const colOffset = (col - half) * spacingMeters;\n gridPoints.push({\n row,\n col,\n lat: centerLat + rowOffset / metersPerDegreeLat,\n lng: centerLng + colOffset / metersPerDegreeLng,\n });\n }\n }\n\n // Get target business name\n let targetName = \"\";\n try {\n const details = await this.getPlaceDetails(params.placeId);\n if (details.success && details.data) {\n targetName = details.data.name;\n }\n } catch {\n // ignore\n }\n\n // Scan each keyword across the grid\n const keywordResults = [];\n for (const keyword of params.keywords) {\n const gridResults = await this.scanKeywordGrid(keyword, params.placeId, gridPoints, spacingMeters);\n keywordResults.push({ keyword, ...gridResults });\n }\n\n // Single keyword: flat response (backward compatible)\n if (keywordResults.length === 1) {\n const kr = keywordResults[0];\n return {\n success: true,\n data: {\n target: { name: targetName, place_id: params.placeId },\n grid_size: `${gridSize}x${gridSize}`,\n grid_spacing_m: spacingMeters,\n keyword: kr.keyword,\n metrics: kr.metrics,\n grid: kr.grid,\n },\n };\n }\n\n // Multi-keyword: array of results\n return {\n success: true,\n data: {\n target: { name: targetName, place_id: params.placeId },\n grid_size: `${gridSize}x${gridSize}`,\n grid_spacing_m: spacingMeters,\n keywords: keywordResults.map((kr) => ({\n keyword: kr.keyword,\n metrics: kr.metrics,\n grid: kr.grid,\n })),\n },\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"An error occurred during local rank tracking\",\n };\n }\n }\n\n private async scanKeywordGrid(\n keyword: string,\n placeId: string,\n gridPoints: Array<{ row: number; col: number; lat: number; lng: number }>,\n spacingMeters: number\n ) {\n const concurrency = 5;\n const gridResults: Array<{\n row: number;\n col: number;\n lat: number;\n lng: number;\n rank: number | null;\n top3: string[];\n }> = [];\n\n const searchOne = async (point: (typeof gridPoints)[0]) => {\n try {\n const places = await this.newPlacesService.searchText({\n textQuery: keyword,\n locationBias: { lat: point.lat, lng: point.lng, radius: spacingMeters / 2 },\n maxResultCount: 20,\n });\n\n const rank = places.findIndex((p: any) => p.place_id === placeId);\n const top3 = places.slice(0, 3).map((p: any) => p.name || \"\");\n\n return {\n row: point.row,\n col: point.col,\n lat: Math.round(point.lat * 1e6) / 1e6,\n lng: Math.round(point.lng * 1e6) / 1e6,\n rank: rank >= 0 ? rank + 1 : null,\n top3,\n };\n } catch {\n return {\n row: point.row,\n col: point.col,\n lat: Math.round(point.lat * 1e6) / 1e6,\n lng: Math.round(point.lng * 1e6) / 1e6,\n rank: null,\n top3: [] as string[],\n };\n }\n };\n\n for (let i = 0; i < gridPoints.length; i += concurrency) {\n const batch = gridPoints.slice(i, i + concurrency);\n const results = await Promise.all(batch.map(searchOne));\n gridResults.push(...results);\n }\n\n const rankedPoints = gridResults.filter((r) => r.rank !== null);\n const totalPoints = gridResults.length;\n const inTop3 = rankedPoints.filter((r) => r.rank! <= 3).length;\n\n const arp =\n rankedPoints.length > 0\n ? Math.round((rankedPoints.reduce((sum, r) => sum + r.rank!, 0) / rankedPoints.length) * 10) / 10\n : null;\n\n const atrp = Math.round((gridResults.reduce((sum, r) => sum + (r.rank ?? 21), 0) / totalPoints) * 10) / 10;\n\n const solv = Math.round((inTop3 / totalPoints) * 1000) / 10;\n\n return {\n metrics: {\n arp,\n atrp,\n solv,\n found_in: `${rankedPoints.length}/${totalPoints}`,\n },\n grid: gridResults,\n };\n }\n}\n","export const Logger = {\n log: (...args: any[]) => {\n console.error(\"[INFO]\", ...args);\n },\n error: (...args: any[]) => {\n console.error(\"[ERROR]\", ...args);\n },\n};\n\nexport { PlacesSearcher } from \"./services/PlacesSearcher.js\";\nexport { NewPlacesService } from \"./services/NewPlacesService.js\";\n"],"mappings":"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","names":["mins"]}
|