Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 204 additions & 0 deletions cli-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14594,6 +14594,210 @@
"modulePath": "oeis/sequence.js",
"sourceFile": "oeis/sequence.js"
},
{
"site": "open-meteo",
"name": "air-quality",
"description": "Hourly Open-Meteo air quality forecast for a city or lat,lon",
"access": "read",
"domain": "open-meteo.com",
"strategy": "public",
"browser": false,
"args": [
{
"name": "location",
"type": "str",
"required": true,
"positional": true,
"help": "City name or \"lat,lon\""
},
{
"name": "hours",
"type": "int",
"default": 24,
"required": false,
"help": "Forecast hours (1-168)"
}
],
"columns": [
"rank",
"location",
"region",
"country",
"time",
"usAqi",
"europeanAqi",
"pm10",
"pm25",
"nitrogenDioxide",
"ozone",
"uvIndex"
],
"type": "js",
"modulePath": "open-meteo/air-quality.js",
"sourceFile": "open-meteo/air-quality.js"
},
{
"site": "open-meteo",
"name": "current",
"description": "Current weather from Open-Meteo for a city or lat,lon",
"access": "read",
"domain": "open-meteo.com",
"strategy": "public",
"browser": false,
"args": [
{
"name": "location",
"type": "str",
"required": true,
"positional": true,
"help": "City name or \"lat,lon\""
},
{
"name": "units",
"type": "str",
"default": "metric",
"required": false,
"help": "metric or imperial units",
"choices": [
"metric",
"imperial"
]
}
],
"columns": [
"location",
"region",
"country",
"latitude",
"longitude",
"timezone",
"time",
"temperature",
"apparentTemperature",
"humidity",
"precipitation",
"cloudCover",
"windSpeed",
"windDirection",
"windGusts",
"isDay",
"weather"
],
"type": "js",
"modulePath": "open-meteo/current.js",
"sourceFile": "open-meteo/current.js"
},
{
"site": "open-meteo",
"name": "forecast",
"description": "Daily Open-Meteo forecast for a city or lat,lon",
"access": "read",
"domain": "open-meteo.com",
"strategy": "public",
"browser": false,
"args": [
{
"name": "location",
"type": "str",
"required": true,
"positional": true,
"help": "City name or \"lat,lon\""
},
{
"name": "days",
"type": "int",
"default": 7,
"required": false,
"help": "Forecast days (1-16)"
},
{
"name": "units",
"type": "str",
"default": "metric",
"required": false,
"help": "metric or imperial units",
"choices": [
"metric",
"imperial"
]
}
],
"columns": [
"rank",
"location",
"region",
"country",
"latitude",
"longitude",
"timezone",
"date",
"weather",
"tempMin",
"tempMax",
"apparentTempMin",
"apparentTempMax",
"precipitation",
"precipitationProbability",
"windSpeedMax",
"windGustsMax"
],
"type": "js",
"modulePath": "open-meteo/forecast.js",
"sourceFile": "open-meteo/forecast.js"
},
{
"site": "open-meteo",
"name": "hourly",
"description": "Hourly Open-Meteo forecast for a city or lat,lon",
"access": "read",
"domain": "open-meteo.com",
"strategy": "public",
"browser": false,
"args": [
{
"name": "location",
"type": "str",
"required": true,
"positional": true,
"help": "City name or \"lat,lon\""
},
{
"name": "hours",
"type": "int",
"default": 24,
"required": false,
"help": "Forecast hours (1-168)"
},
{
"name": "units",
"type": "str",
"default": "metric",
"required": false,
"help": "metric or imperial units",
"choices": [
"metric",
"imperial"
]
}
],
"columns": [
"rank",
"location",
"region",
"country",
"time",
"weather",
"temperature",
"humidity",
"precipitationProbability",
"precipitation",
"windSpeed",
"windGusts"
],
"type": "js",
"modulePath": "open-meteo/hourly.js",
"sourceFile": "open-meteo/hourly.js"
},
{
"site": "openalex",
"name": "search",
Expand Down
66 changes: 66 additions & 0 deletions clis/open-meteo/air-quality.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { EmptyResultError } from '@agentrhq/webcmd/errors';
import { cli, Strategy } from '@agentrhq/webcmd/registry';
import {
AIR_QUALITY_BASE,
fetchJson,
locationFields,
requireHours,
resolveLocation,
} from './utils.js';

cli({
site: 'open-meteo',
name: 'air-quality',
access: 'read',
description: 'Hourly Open-Meteo air quality forecast for a city or lat,lon',
domain: 'open-meteo.com',
strategy: Strategy.PUBLIC,
browser: false,
args: [
{ name: 'location', positional: true, required: true, help: 'City name or "lat,lon"' },
{ name: 'hours', type: 'int', default: 24, help: 'Forecast hours (1-168)' },
],
columns: [
'rank', 'location', 'region', 'country', 'time',
'usAqi', 'europeanAqi', 'pm10', 'pm25',
'nitrogenDioxide', 'ozone', 'uvIndex',
],
func: async (args) => {
const location = await resolveLocation(args.location);
const hours = requireHours(args.hours);
const url = new URL(AIR_QUALITY_BASE);
url.searchParams.set('latitude', String(location.latitude));
url.searchParams.set('longitude', String(location.longitude));
url.searchParams.set('timezone', 'auto');
url.searchParams.set('forecast_days', String(Math.ceil(hours / 24)));
url.searchParams.set('hourly', [
'us_aqi',
'european_aqi',
'pm10',
'pm2_5',
'nitrogen_dioxide',
'ozone',
'uv_index',
].join(','));

const body = await fetchJson(url, 'open-meteo air-quality');
const hourly = body?.hourly ?? {};
const times = Array.isArray(hourly.time) ? hourly.time : [];
const rows = times.slice(0, hours).map((time, i) => ({
rank: i + 1,
...locationFields(location),
time,
usAqi: hourly.us_aqi?.[i] ?? null,
europeanAqi: hourly.european_aqi?.[i] ?? null,
pm10: hourly.pm10?.[i] ?? null,
pm25: hourly.pm2_5?.[i] ?? null,
nitrogenDioxide: hourly.nitrogen_dioxide?.[i] ?? null,
ozone: hourly.ozone?.[i] ?? null,
uvIndex: hourly.uv_index?.[i] ?? null,
}));
if (!rows.length) {
throw new EmptyResultError('open-meteo air-quality', 'Open-Meteo returned no air quality rows.');
}
return rows.map(({ latitude, longitude, ...row }) => row);
},
});
69 changes: 69 additions & 0 deletions clis/open-meteo/current.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { cli, Strategy } from '@agentrhq/webcmd/registry';
import {
FORECAST_BASE,
applyUnitParams,
describeWeatherCode,
fetchJson,
locationFields,
requireUnits,
resolveLocation,
} from './utils.js';

cli({
site: 'open-meteo',
name: 'current',
access: 'read',
description: 'Current weather from Open-Meteo for a city or lat,lon',
domain: 'open-meteo.com',
strategy: Strategy.PUBLIC,
browser: false,
args: [
{ name: 'location', positional: true, required: true, help: 'City name or "lat,lon"' },
{ name: 'units', default: 'metric', choices: ['metric', 'imperial'], help: 'metric or imperial units' },
],
columns: [
'location', 'region', 'country', 'latitude', 'longitude', 'timezone',
'time', 'temperature', 'apparentTemperature', 'humidity',
'precipitation', 'cloudCover', 'windSpeed', 'windDirection',
'windGusts', 'isDay', 'weather',
],
func: async (args) => {
const location = await resolveLocation(args.location);
const units = requireUnits(args.units);
const url = new URL(FORECAST_BASE);
url.searchParams.set('latitude', String(location.latitude));
url.searchParams.set('longitude', String(location.longitude));
url.searchParams.set('timezone', 'auto');
url.searchParams.set('current', [
'temperature_2m',
'apparent_temperature',
'relative_humidity_2m',
'precipitation',
'weather_code',
'cloud_cover',
'wind_speed_10m',
'wind_direction_10m',
'wind_gusts_10m',
'is_day',
].join(','));
applyUnitParams(url, units);

const body = await fetchJson(url, 'open-meteo current');
const cur = body?.current ?? {};
return [{
...locationFields(location),
timezone: body?.timezone ?? null,
time: cur.time ?? null,
temperature: cur.temperature_2m ?? null,
apparentTemperature: cur.apparent_temperature ?? null,
humidity: cur.relative_humidity_2m ?? null,
precipitation: cur.precipitation ?? null,
cloudCover: cur.cloud_cover ?? null,
windSpeed: cur.wind_speed_10m ?? null,
windDirection: cur.wind_direction_10m ?? null,
windGusts: cur.wind_gusts_10m ?? null,
isDay: cur.is_day == null ? null : Boolean(cur.is_day),
weather: describeWeatherCode(cur.weather_code),
}];
},
});
Loading
Loading