diff --git a/skill/reference/axiom.md b/skill/reference/axiom.md index db9b12e..17545df 100644 --- a/skill/reference/axiom.md +++ b/skill/reference/axiom.md @@ -1,13 +1,12 @@ # Axiom API Capabilities -Summary of all operations available via Axiom API with a personal access token (PAT). +Summary of all operations available via Axiom API. -**Base URL:** `https://api.axiom.co` (for all endpoints except ingestion) -**Ingest URL:** Use edge deployment domain (e.g., `https://us-east-1.aws.edge.axiom.co`) +**Base URL:** `https://api.axiom.co` (for management endpoints) +**Query & Ingest URL:** Use edge deployment domain (e.g., `https://us-east-1.aws.edge.axiom.co`) **Authentication:** -- PAT: `Authorization: Bearer $PAT` + `x-axiom-org-id: $ORG_ID` -- API Token: `Authorization: Bearer $API_TOKEN` +- `Authorization: Bearer $API_TOKEN` — use an advanced API token with minimal privileges --- @@ -15,8 +14,8 @@ Summary of all operations available via Axiom API with a personal access token ( | Operation | Endpoint | Description | |-----------|----------|-------------| -| Run APL query | `POST /v1/datasets/_apl?format=tabular` | Execute APL query with tabular output | -| Run APL query (legacy) | `POST /v1/datasets/_apl?format=legacy` | Execute APL query with legacy output | +| Run APL query (edge) | `POST /v1/query/_apl?format=tabular` | Execute APL query with tabular output | +| Run APL query (legacy) | `POST /v1/query/_apl?format=legacy` | Execute APL query with legacy output | | Run query (legacy) | `POST /v1/datasets/{dataset_name}/query` | Legacy query endpoint with filter/aggregation model | **Query parameters:** `apl`, `startTime`, `endTime`, `cursor`, `includeCursor`, `queryOptions`, `variables` diff --git a/skill/scripts/axiom-api b/skill/scripts/axiom-api index f4bf71e..5e2752a 100755 --- a/skill/scripts/axiom-api +++ b/skill/scripts/axiom-api @@ -2,8 +2,8 @@ # Axiom API helper - uses unified config # Usage: axiom-api [body] # Examples: -# axiom-api dev POST "/v1/datasets/_apl?format=tabular" '{"apl": "..."}' # axiom-api dev GET "/v1/datasets" +# axiom-api dev GET "/v1/datasets/my-dataset" set -euo pipefail @@ -20,8 +20,22 @@ fi SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" eval "$("$SCRIPT_DIR/config" axiom "$DEPLOYMENT")" +# edge URL for ingest endpoints +REQUEST_URL="$AXIOM_URL" + +if [[ "$ENDPOINT" == /v1/ingest/* ]]; then + DATASET="${ENDPOINT#/v1/ingest/}" + DATASET="${DATASET%%[?/]*}" + if [[ -n "$DATASET" ]]; then + RESOLVED=$("$SCRIPT_DIR/resolve-url" "$DEPLOYMENT" "$DATASET" 2>/dev/null || true) + if [[ -n "$RESOLVED" && "$RESOLVED" != "$AXIOM_URL" ]]; then + REQUEST_URL="$RESOLVED" + fi + fi +fi + if [[ -n "$BODY" ]]; then - "$SCRIPT_DIR/curl-auth" axiom "$DEPLOYMENT" -X "$METHOD" -d "$BODY" "${AXIOM_URL}${ENDPOINT}" + "$SCRIPT_DIR/curl-auth" axiom "$DEPLOYMENT" -X "$METHOD" -d "$BODY" "${REQUEST_URL}${ENDPOINT}" else - "$SCRIPT_DIR/curl-auth" axiom "$DEPLOYMENT" -X "$METHOD" "${AXIOM_URL}${ENDPOINT}" + "$SCRIPT_DIR/curl-auth" axiom "$DEPLOYMENT" -X "$METHOD" "${REQUEST_URL}${ENDPOINT}" fi diff --git a/skill/scripts/axiom-link b/skill/scripts/axiom-link index 266ee62..25bc9cc 100755 --- a/skill/scripts/axiom-link +++ b/skill/scripts/axiom-link @@ -37,10 +37,10 @@ fi # Derive web UI URL from configured API URL # Replace "api." with "app." in the domain # Examples: +# https://api.axiom.co → https://app.axiom.co # https://api.staging.axiom.co → https://app.staging.axiom.co # https://api.dev.axiom.co → https://app.dev.axiom.co # https://cloud.axiom.co → https://app.axiom.co -# https://api.axiom.co → https://app.axiom.co if [[ "$URL" == *"cloud.axiom.co"* ]]; then BASE_URL="https://app.axiom.co" elif [[ "$URL" == https://api.* ]]; then diff --git a/skill/scripts/axiom-query b/skill/scripts/axiom-query index c15f737..0cfd233 100755 --- a/skill/scripts/axiom-query +++ b/skill/scripts/axiom-query @@ -141,6 +141,34 @@ PAYLOAD=$(jq -cn \ # shellcheck disable=SC1090 eval "$("$SCRIPT_DIR/config" axiom "$DEPLOYMENT")" +CACHE_DIR="${TMPDIR:-/tmp}/axiom-edge-cache" +REGIONS_CACHE="$CACHE_DIR/regions__${DEPLOYMENT}" +if [[ ! -f "$REGIONS_CACHE" ]]; then + if [[ "$AXIOM_URL" == *"cloud.axiom.co"* ]]; then + APP_URL="https://app.axiom.co" + else + APP_URL="${AXIOM_URL/api./app.}" + fi + REGIONS_JSON=$("$SCRIPT_DIR/curl-auth" axiom "$DEPLOYMENT" -X GET "${APP_URL}/api/internal/regions/axiom" 2>/dev/null) || true + if [[ -n "$REGIONS_JSON" ]]; then + mkdir -p "$CACHE_DIR" + echo "$REGIONS_JSON" > "$REGIONS_CACHE" + fi +fi + +LAST_EDGE_CACHE="$CACHE_DIR/last-edge__${DEPLOYMENT}" +if [[ -f "$LAST_EDGE_CACHE" ]]; then + FIRST_EDGE=$(cat "$LAST_EDGE_CACHE") +elif [[ -f "$REGIONS_CACHE" ]]; then + FIRST_EDGE=$(jq -r '.axiom[] | select(.environmentDefault == true) | .domain // empty' "$REGIONS_CACHE" 2>/dev/null) +fi +EDGE_URLS="${FIRST_EDGE:-}" +if [[ -f "$REGIONS_CACHE" ]]; then + OTHER_URLS=$(jq -r '.axiom[].domain // empty' "$REGIONS_CACHE" 2>/dev/null | grep -v "^${FIRST_EDGE:-}$" || true) + EDGE_URLS="$EDGE_URLS +$OTHER_URLS" +fi + RESP_HEADERS=$(mktemp) RESP_BODY=$(mktemp) cleanup() { @@ -148,21 +176,26 @@ cleanup() { } trap cleanup EXIT -# Execute query and pipe to formatter -HTTP_CODE=$(curl -sS -o "$RESP_BODY" -D "$RESP_HEADERS" -w "%{http_code}" \ - -X POST "$AXIOM_URL/v1/datasets/_apl?format=tabular" \ - -H "Authorization: Bearer $AXIOM_TOKEN" \ - -H "X-Axiom-Org-Id: $AXIOM_ORG_ID" \ - -H "Content-Type: application/json" \ - -d "$PAYLOAD") +QUERY_OK=false +for QUERY_URL in $EDGE_URLS; do + HTTP_CODE=$(curl -sS -o "$RESP_BODY" -D "$RESP_HEADERS" -w "%{http_code}" \ + -X POST "${QUERY_URL}/v1/query/_apl?format=tabular" \ + -H "Authorization: Bearer $AXIOM_TOKEN" \ + -H "X-Axiom-Org-Id: $AXIOM_ORG_ID" \ + -H "Content-Type: application/json" \ + -d "$PAYLOAD") + if [[ "$HTTP_CODE" -ge 200 && "$HTTP_CODE" -lt 300 ]]; then + echo "$QUERY_URL" > "$LAST_EDGE_CACHE" + QUERY_OK=true + break + fi +done -if [[ "$HTTP_CODE" -lt 200 || "$HTTP_CODE" -ge 300 ]]; then +if [[ "$QUERY_OK" != true ]]; then msg=$(jq -r '.message // empty' "$RESP_BODY" 2>/dev/null) trace=$(grep -i '^x-axiom-trace-id:' "$RESP_HEADERS" | tail -1 | awk '{print $2}' | tr -d '\r') echo "error: ${msg:-http $HTTP_CODE}" >&2 - if [[ -n "$trace" ]]; then - echo "trace_id: $trace" >&2 - fi + [[ -n "${trace:-}" ]] && echo "trace_id: $trace" >&2 exit 1 fi diff --git a/skill/scripts/resolve-url b/skill/scripts/resolve-url new file mode 100755 index 0000000..59b4274 --- /dev/null +++ b/skill/scripts/resolve-url @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# resolve-url: Resolve the regional edge URL for a dataset +# +# Usage: resolve-url +# +# Two-step resolution: +# 1. GET /v1/datasets → find dataset's region (e.g. cloud.us-east-1.aws) +# 2. GET /api/internal/regions/axiom → map region to edge domain +# +# Both steps are cached to avoid repeated API calls. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +DEPLOYMENT="${1:-}" +DATASET="${2:-}" + +if [[ -z "$DEPLOYMENT" || -z "$DATASET" ]]; then + echo "Usage: resolve-url " >&2 + exit 1 +fi + +CACHE_DIR="${TMPDIR:-/tmp}/axiom-edge-cache" + +# Check dataset edge URL cache (valid for 1 hour) +DATASET_CACHE="$CACHE_DIR/edge__${DEPLOYMENT}__${DATASET}" +if [[ -f "$DATASET_CACHE" ]]; then + if [[ "$(uname)" == "Darwin" ]]; then + AGE=$(( $(date +%s) - $(stat -f %m "$DATASET_CACHE") )) + else + AGE=$(( $(date +%s) - $(stat -c %Y "$DATASET_CACHE") )) + fi + if [[ $AGE -lt 3600 ]]; then + cat "$DATASET_CACHE" + exit 0 + fi +fi + +eval "$("$SCRIPT_DIR/config" axiom "$DEPLOYMENT")" + +# Step 1: get dataset region +REGION=$("$SCRIPT_DIR/curl-auth" axiom "$DEPLOYMENT" -X GET "${AXIOM_URL}/v1/datasets" \ + | jq -r --arg name "$DATASET" '.[] | select(.name == $name) | .edgeDeployment // .region // empty' 2>/dev/null) + +if [[ -z "$REGION" || "$REGION" == "null" ]]; then + mkdir -p "$CACHE_DIR" + echo "$AXIOM_URL" > "$DATASET_CACHE" + echo "$AXIOM_URL" + exit 0 +fi + +# Step 2: look up edge domain from regions API (cached 24h per deployment) +REGIONS_CACHE="$CACHE_DIR/regions__${DEPLOYMENT}" +REGIONS_JSON="" +if [[ -f "$REGIONS_CACHE" ]]; then + if [[ "$(uname)" == "Darwin" ]]; then + AGE=$(( $(date +%s) - $(stat -f %m "$REGIONS_CACHE") )) + else + AGE=$(( $(date +%s) - $(stat -c %Y "$REGIONS_CACHE") )) + fi + if [[ $AGE -lt 86400 ]]; then + REGIONS_JSON=$(cat "$REGIONS_CACHE") + fi +fi + +if [[ -z "$REGIONS_JSON" ]]; then + if [[ "$AXIOM_URL" == *"cloud.axiom.co"* ]]; then + APP_URL="https://app.axiom.co" + else + APP_URL="${AXIOM_URL/api./app.}" + fi + REGIONS_JSON=$("$SCRIPT_DIR/curl-auth" axiom "$DEPLOYMENT" -X GET "${APP_URL}/api/internal/regions/axiom" 2>/dev/null) || true + if [[ -n "$REGIONS_JSON" ]]; then + mkdir -p "$CACHE_DIR" + echo "$REGIONS_JSON" > "$REGIONS_CACHE" + fi +fi + +EDGE_DOMAIN="" +if [[ -n "$REGIONS_JSON" ]]; then + EDGE_DOMAIN=$(echo "$REGIONS_JSON" | jq -r --arg region "$REGION" '.axiom[] | select(.id == $region) | .domain // empty' 2>/dev/null) +fi + +if [[ -z "$EDGE_DOMAIN" ]]; then + mkdir -p "$CACHE_DIR" + echo "$AXIOM_URL" > "$DATASET_CACHE" + echo "$AXIOM_URL" + exit 0 +fi + +# Cache the resolved edge URL +mkdir -p "$CACHE_DIR" +if [[ "$EDGE_DOMAIN" != https://* ]]; then + EDGE_DOMAIN="https://${EDGE_DOMAIN}" +fi +echo "$EDGE_DOMAIN" > "$DATASET_CACHE" + +echo "$EDGE_DOMAIN"