From 45fa666c659b8ce2bbc1f37085fc859079652c51 Mon Sep 17 00:00:00 2001 From: sk Date: Mon, 25 May 2026 15:29:36 +0800 Subject: [PATCH] feat(amazon): add Perpetua CLI commands --- internal/command/root_sources.go | 6 ++- internal/command/schema_test_helper.go | 2 + internal/output/projection_rules.go | 43 ++++++++++++++++ internal/output/projection_test.go | 2 + skills/apimux-amazon/SKILL.md | 68 ++++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 2 deletions(-) diff --git a/internal/command/root_sources.go b/internal/command/root_sources.go index 06a7528..38d2183 100644 --- a/internal/command/root_sources.go +++ b/internal/command/root_sources.go @@ -29,8 +29,8 @@ func (r *Root) newAmazonCommand(runCtx *runContext) *cobra.Command { "amazon", "Amazon product, keyword, and sales data", "Amazon product, keyword, and sales data.\n\nUse this command for Amazon product lookup, keyword research, sales history, reviews, and category trend endpoints.", - " apimux amazon search_products --keyword 'desk lamp'\n apimux amazon get_product --asin B0EXAMPLE", - "expand_keywords, get_keyword_overview, get_keyword_trends, list_asin_keywords, query_aba_keywords, search_category, get_asin_sales_daily_trend, get_asins_sales_history, get_variant_sales_30d, get_product, search_products, get_product_reviews, get_category_best_sellers, get_category_trend", + " apimux amazon search_products --keyword 'desk lamp'\n apimux amazon get_product --asin B0EXAMPLE\n apimux amazon get_product_metrics --asin B0EXAMPLE", + "expand_keywords, get_keyword_overview, get_keyword_trends, list_asin_keywords, query_aba_keywords, search_category, get_asin_sales_daily_trend, get_asins_sales_history, get_variant_sales_30d, get_product, get_product_metrics, get_product_search_terms, search_products, get_product_reviews, get_category_best_sellers, get_category_trend", newSchemaBoundCapabilityCommand(runCtx, "amazon.expand_keywords", "expand_keywords", "Expand one Amazon keyword into related queries", "amazon expand_keywords"), newSchemaBoundCapabilityCommand(runCtx, "amazon.get_keyword_overview", "get_keyword_overview", "Fetch one Amazon keyword overview", "amazon get_keyword_overview"), newSchemaBoundCapabilityCommand(runCtx, "amazon.get_keyword_trends", "get_keyword_trends", "Fetch Amazon keyword search trends", "amazon get_keyword_trends"), @@ -41,6 +41,8 @@ func (r *Root) newAmazonCommand(runCtx *runContext) *cobra.Command { newSchemaBoundCapabilityCommand(runCtx, "amazon.get_asins_sales_history", "get_asins_sales_history", "Fetch monthly sales history for multiple Amazon ASINs", "amazon get_asins_sales_history"), newSchemaBoundCapabilityCommand(runCtx, "amazon.get_variant_sales_30d", "get_variant_sales_30d", "Fetch 30-day sales for Amazon variants", "amazon get_variant_sales_30d"), newSchemaBoundCapabilityCommand(runCtx, "amazon.get_product", "get_product", "Fetch one Amazon product", "amazon get_product"), + newSchemaBoundCapabilityCommand(runCtx, "amazon.get_product_metrics", "get_product_metrics", "Fetch Perpetua-backed Amazon product metrics", "amazon get_product_metrics"), + newSchemaBoundCapabilityCommand(runCtx, "amazon.get_product_search_terms", "get_product_search_terms", "Fetch Perpetua-backed Amazon product search terms", "amazon get_product_search_terms"), newSchemaBoundCapabilityCommand(runCtx, "amazon.search_products", "search_products", "Search Amazon products", "amazon search_products"), newSchemaBoundCapabilityCommand(runCtx, "amazon.get_product_reviews", "get_product_reviews", "Fetch canonical Amazon product reviews", "amazon get_product_reviews"), newSchemaBoundCapabilityCommand(runCtx, "amazon.get_category_best_sellers", "get_category_best_sellers", "Fetch Amazon category best sellers", "amazon get_category_best_sellers"), diff --git a/internal/command/schema_test_helper.go b/internal/command/schema_test_helper.go index c38faac..5d6936f 100644 --- a/internal/command/schema_test_helper.go +++ b/internal/command/schema_test_helper.go @@ -82,6 +82,8 @@ func testSchemas() map[string]schema.CapabilitySchema { "amazon.get_asin_sales_daily_trend": amazonSchema("amazon.get_asin_sales_daily_trend", []schema.CapabilityParam{{Name: "asin", Type: "string", Required: true}, {Name: "begin_date", Type: "string", FlagName: "begin-date"}, {Name: "market", Type: "string"}}), "amazon.get_asins_sales_history": amazonSchema("amazon.get_asins_sales_history", []schema.CapabilityParam{{Name: "asins", Type: "array", Required: true, ItemsType: "string", Encoding: "csv"}, {Name: "market", Type: "string"}}), "amazon.get_variant_sales_30d": amazonSchema("amazon.get_variant_sales_30d", []schema.CapabilityParam{{Name: "asin", Type: "string", Required: true}, {Name: "market", Type: "string"}}), + "amazon.get_product_metrics": amazonSchema("amazon.get_product_metrics", []schema.CapabilityParam{{Name: "asin", Type: "string", Required: true}, {Name: "market", Type: "string"}}), + "amazon.get_product_search_terms": amazonSchema("amazon.get_product_search_terms", []schema.CapabilityParam{{Name: "asin", Type: "string", Required: true}, {Name: "market", Type: "string"}}), "amazon.get_product_reviews": amazonSchema("amazon.get_product_reviews", []schema.CapabilityParam{{Name: "asin", Type: "string", Required: true}, {Name: "market", Type: "string"}, {Name: "start_date", Type: "string", FlagName: "start-date"}, {Name: "star", Type: "string", Enum: []string{"positive", "negative", "1", "2", "3", "4", "5"}}, {Name: "only_purchase", Type: "boolean", FlagName: "only-purchase"}, {Name: "page_index", Type: "integer", FlagName: "page-index"}}), "amazon.get_category_best_sellers": amazonSchema("amazon.get_category_best_sellers", []schema.CapabilityParam{{Name: "node_id", Type: "string", Required: true, FlagName: "node-id"}, {Name: "market", Type: "string"}, {Name: "query_start", Type: "string", FlagName: "query-start"}, {Name: "query_date", Type: "string", FlagName: "query-date"}, {Name: "query_days", Type: "integer", FlagName: "query-days"}}), "amazon.get_category_trend": amazonSchema("amazon.get_category_trend", []schema.CapabilityParam{{Name: "node_id", Type: "string", Required: true, FlagName: "node-id"}, {Name: "market", Type: "string"}, {Name: "trend_types", Type: "array", Required: true, ItemsType: "string", Encoding: "csv", FlagName: "trend-types"}}), diff --git a/internal/output/projection_rules.go b/internal/output/projection_rules.go index 510cc9e..bb6dc1d 100644 --- a/internal/output/projection_rules.go +++ b/internal/output/projection_rules.go @@ -88,6 +88,49 @@ var projectionRules = map[string]projectionRule{ }, }, }, + "amazon.get_product_metrics": { + Compact: projectionSpec{ + Tables: []tableRule{ + { + From: "$root", + To: "items", + Limit: 20, + Columns: []fieldRule{ + {From: "asin", To: "asin"}, + {From: "market", To: "market"}, + {From: "totalMetric.from", To: "from"}, + {From: "totalMetric.to", To: "to"}, + {From: "totalMetric.totals.totalSales.value", To: "total_sales"}, + {From: "totalMetric.totals.totalUnitsSold.value", To: "total_units_sold"}, + {From: "totalMetric.averages.averagePrice.value", To: "average_price"}, + {From: "totalMetric.averages.averageRatings.value", To: "average_rating"}, + {From: "totalMetric.averages.averageReview.value", To: "average_reviews"}, + }, + }, + }, + }, + }, + "amazon.get_product_search_terms": { + Compact: projectionSpec{ + Tables: []tableRule{ + { + From: "$root", + To: "items", + Limit: 25, + Columns: []fieldRule{ + {From: "searchTerm", To: "search_term"}, + {From: "searchFrequencyRank", To: "search_frequency_rank"}, + {From: "organicRank.rank", To: "organic_rank"}, + {From: "paidRank", To: "paid_rank"}, + {From: "proprietaryAsinOrganicRank", To: "proprietary_organic_rank"}, + {From: "organicRankDelta", To: "organic_rank_delta"}, + {From: "sales", To: "sales"}, + {From: "adSpend", To: "ad_spend"}, + }, + }, + }, + }, + }, "amazon.get_product_reviews": { Compact: projectionSpec{ Tables: []tableRule{ diff --git a/internal/output/projection_test.go b/internal/output/projection_test.go index 06c92f8..336bd8d 100644 --- a/internal/output/projection_test.go +++ b/internal/output/projection_test.go @@ -785,6 +785,8 @@ func TestProjectCapabilityAutoFallsBackToDataWhenUnsupported(t *testing.T) { func TestProjectionRulesCoverAllAgentTestCapabilities(t *testing.T) { required := []string{ "amazon.get_product", + "amazon.get_product_metrics", + "amazon.get_product_search_terms", "amazon.get_keyword_overview", "amazon.get_asins_sales_history", "douyin.search_videos", diff --git a/skills/apimux-amazon/SKILL.md b/skills/apimux-amazon/SKILL.md index f212039..72a51e9 100644 --- a/skills/apimux-amazon/SKILL.md +++ b/skills/apimux-amazon/SKILL.md @@ -56,6 +56,8 @@ Query Amazon e-commerce data across products, keywords, reviews, and categories. | `get_asins_sales_history` | Monthly sales history | Batch comparison | | `get_variant_sales_30d` | Variant sales (30 days) | Variant structure analysis | | `get_product` | Product details | Competitive baseline | +| `get_product_metrics` | Product metrics | Perpetua-backed product trend and sales metrics | +| `get_product_search_terms` | Product search terms | Perpetua-backed ASIN search-term intelligence | | `search_products` | Search by keyword | Product research | | `get_product_reviews` | Product reviews | Review analysis | | `get_category_best_sellers` | Category best sellers | Category analysis | @@ -490,6 +492,72 @@ apimux amazon get_product --asin "B0CM5JV26D" --market "US" --- +## amazon.get_product_metrics + +Get Perpetua-backed Amazon product metrics for one ASIN. + +### Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `asin` | string | Yes | Amazon product ASIN, 10 uppercase alphanumeric characters | +| `market` | string | No | Target market; defaults to `US` | + +### CLI usage + +```bash +apimux amazon get_product_metrics --asin "B0CM5JV26D" --market "US" +``` + +### Response fields + +| Field | Type | Description | +|-------|------|-------------| +| `asin` | string | Product ASIN | +| `market` | string | Amazon market code | +| `totalMetric` | object | Perpetua total metric payload normalized into APIMux envelope | +| `brandMetric` | object/null | Perpetua brand metric payload when available | + +### Notes + +- Use `--output data-pretty` when you need the complete nested metrics payload. +- This capability is live upstream data and may intermittently fail when Perpetua is unavailable. + +--- + +## amazon.get_product_search_terms + +Get Perpetua-backed search terms associated with one Amazon ASIN. + +### Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `asin` | string | Yes | Amazon product ASIN, 10 uppercase alphanumeric characters | +| `market` | string | No | Target market; defaults to `US` | + +### CLI usage + +```bash +apimux amazon get_product_search_terms --asin "B0CM5JV26D" --market "US" +``` + +### Response fields + +| Field | Type | Description | +|-------|------|-------------| +| `searchTerm` | string | Search term text | +| `searchFrequencyRank` | integer | Search frequency rank | +| `organicRank` | object/null | Organic rank payload for the queried ASIN | +| `paidRank` | integer/null | Paid rank when available | +| `proprietaryAsinOrganicRank` | integer/null | Proprietary ASIN organic rank | +| `organicRankDelta` | integer/null | Organic rank delta | +| `sales` | number/null | Sales attributed to the term when available | +| `adSpend` | number/null | Ad spend attributed to the term when available | +| `perpetuaId` | string/null | Perpetua identifier when available | + +--- + ## amazon.search_products Search Amazon products by keyword.