@rankwrangler/cli 0.9.0 → 0.10.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 CHANGED
@@ -7,6 +7,7 @@ npm install -g @rankwrangler/cli
7
7
 
8
8
  rw auth set ak_...
9
9
  rw product get B0DV53VS61
10
+ rw product get B0DV53VS61 --shortName
10
11
  rw product search "retro gardening shirt"
11
12
  rw product history B0DV53VS61 --bucket week
12
13
  rw keyword get "retro gardening shirt"
@@ -20,5 +21,7 @@ data and expose no refresh control; Product search retains its separate search c
20
21
  data contains `keyword`, `searchedAt`, and compact results with `organicSearchPlacement`. Output is
21
22
  a JSON `{ ok, data }` envelope; failures use `{ ok: false, error }` on stderr.
22
23
  The CLI does not expose Catalog, Operation, provider status, or polling commands.
24
+ `--shortName` adds a nullable image-informed `listing.shortName` to a single Product get, waiting
25
+ up to 20 seconds for generation and returning `null` if it is still running.
23
26
 
24
27
  See the [CLI reference](../../docs/reference/cli.md) for options and release workflow.
package/dist/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## v0.10.0 - 2026-10-20
6
+
7
+ ### Added
8
+
9
+ - Add optional `include` on `product.get` for `marketData`, `shortName`, and `cutoutThumbnail`.
10
+ The default remains `marketData`.
11
+ - Add image-informed design short names. A Product get that includes `shortName` waits up to 20
12
+ seconds. `rw product get <asin> --shortName` requests that include together with `marketData`.
13
+ The response carries `listing.shortName`, or `null` when no name is ready.
14
+ - Add on-demand transparent 128×128 cutout thumbnails through `cutoutThumbnail` on that same
15
+ `include` list. RankWrangler centers the subject and reuses the cutout until the listing image
16
+ changes.
17
+ - Advertise the website URL and a 128px icon in hosted MCP server info.
18
+ - Name the stale ingestion check on `GET /api/health` when the service is degraded, and include
19
+ the deployed commit as `revision` on both health responses.
20
+
21
+ ### Changed
22
+
23
+ - Breaking: replace `isUnavailable` with `amazonListingStatus` (`active` or `deleted`) on Product
24
+ listings, search results, and basic `getMany` results. A deleted listing keeps its last-known
25
+ title and thumbnail. `active` means the Amazon detail page exists. The dashboard marks deleted
26
+ listings.
27
+ - Return last-known `getMany` listings immediately and refresh stale listings in the background.
28
+ Short names and cutouts stay on single Product get.
29
+
30
+ ### Fixed
31
+
32
+ - Show the server error when the dashboard Products read fails, and reserve the empty catalog
33
+ state for a successful read with no rows.
34
+ - Load the API usage card across the UTC day boundary.
35
+ - Fail an Amazon Catalog read that does not start within 2 minutes with a retryable capacity
36
+ error.
37
+ - Require a second overdue Product before Keepa or SP-API freshness degrades `GET /api/health`,
38
+ and wait out a top search terms report that is still pending.
39
+
5
40
  ## v0.9.0 - 2026-08-09
6
41
 
7
42
  ### Added
@@ -13,6 +13,7 @@ export interface CliOptionValues {
13
13
  bucket?: string;
14
14
  cursor?: string;
15
15
  refresh?: boolean;
16
+ shortName?: boolean;
16
17
  help?: boolean;
17
18
  version?: boolean;
18
19
  }
@@ -3,6 +3,9 @@ export const runProductCommand = async (command, client, config, options, fail)
3
3
  if (command.verb !== 'search' && options.refresh) {
4
4
  fail('INVALID_INPUT', '--refresh is only supported for product search');
5
5
  }
6
+ if (command.verb !== 'get' && options.shortName) {
7
+ fail('INVALID_INPUT', '--shortName is only supported for product get');
8
+ }
6
9
  if (command.verb === 'get') {
7
10
  return await runProductGet(command.args, client, config, options, fail);
8
11
  }
@@ -19,10 +22,14 @@ export const runProductCommand = async (command, client, config, options, fail)
19
22
  const runProductGet = async (args, client, config, options, fail) => {
20
23
  const marketplaceId = requireMarketplaceId(options, config);
21
24
  const asins = requireAsins(args, fail);
25
+ if (asins.length > 1 && options.shortName) {
26
+ fail('INVALID_INPUT', '--shortName requires exactly one ASIN');
27
+ }
22
28
  if (asins.length === 1) {
23
29
  return await client.product.get.mutate({
24
30
  marketplaceId,
25
31
  asin: asins[0],
32
+ ...(options.shortName ? { include: ['marketData', 'shortName'] } : {}),
26
33
  });
27
34
  }
28
35
  return await client.product.getMany.mutate({
package/dist/index.js CHANGED
@@ -42,6 +42,7 @@ const { positionals, values } = parseArgs({
42
42
  bucket: { type: 'string' },
43
43
  cursor: { type: 'string' },
44
44
  refresh: { type: 'boolean' },
45
+ shortName: { type: 'boolean' },
45
46
  stdin: { type: 'boolean' },
46
47
  },
47
48
  allowPositionals: true,
package/dist/usage.js CHANGED
@@ -39,6 +39,7 @@ export const printUsage = (paths) => {
39
39
  ' --baseUrl <origin> Override API origin',
40
40
  ` -m, --marketplace <id> Override marketplace (default: ${DEFAULT_MARKETPLACE_ID})`,
41
41
  ' --refresh Product search refresh (separate Search contract)',
42
+ ' --shortName Generate a design name for one Merch Product',
42
43
  ' --metrics <list> Product history metrics: salesRank,price (default: salesRank,price)',
43
44
  ' --bucket <unit> Product history buckets: auto,day,week,month (default: auto)',
44
45
  ' --days <N> Product history lookback (30-3650, default: 365)',
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@rankwrangler/cli",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Official RankWrangler command line interface",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/merchbaseco/rankwrangler.git",
8
+ "directory": "packages/cli"
9
+ },
5
10
  "type": "module",
6
11
  "bin": {
7
12
  "rw": "dist/index.js",
@@ -24,8 +29,8 @@
24
29
  "provenance": true
25
30
  },
26
31
  "dependencies": {
27
- "@merchbaseco/access": "0.3.0",
28
- "@rankwrangler/http-client": "^0.9.0",
32
+ "@merchbaseco/access": "0.4.0",
33
+ "@rankwrangler/http-client": "^0.10.0",
29
34
  "keytar": "7.9.0"
30
35
  },
31
36
  "devDependencies": {
@@ -1,41 +0,0 @@
1
- export declare const HISTORY_METRIC_ALIASES: readonly ["bsr", "price"];
2
- export type HistoryMetricAlias = (typeof HISTORY_METRIC_ALIASES)[number];
3
- type HistoryPointTuple = [string, number | null] | [string, null, 1];
4
- export type AgentHistorySeries = {
5
- bsr?: {
6
- unit: 'rank';
7
- category: {
8
- id: number;
9
- name: string | null;
10
- } | null;
11
- points: HistoryPointTuple[];
12
- };
13
- price?: {
14
- unit: 'minorCurrency';
15
- currencyCode: 'USD';
16
- valueScale: 100;
17
- points: HistoryPointTuple[];
18
- };
19
- };
20
- export type AgentHistoryResponse = {
21
- schemaVersion?: number;
22
- status?: string;
23
- syncTriggered: boolean;
24
- latestImportAt: string | null;
25
- series?: AgentHistorySeries;
26
- };
27
- export declare const buildCliHistoryResponse: ({ asin, marketplaceId, metrics, response, }: {
28
- asin: string;
29
- marketplaceId: string;
30
- metrics: HistoryMetricAlias[];
31
- response: AgentHistoryResponse;
32
- }) => {
33
- schemaVersion: number;
34
- asin: string;
35
- marketplaceId: string;
36
- status: string;
37
- latestImportAt: string | null;
38
- syncTriggered: boolean;
39
- series: AgentHistorySeries;
40
- };
41
- export {};
@@ -1,26 +0,0 @@
1
- export const HISTORY_METRIC_ALIASES = ['bsr', 'price'];
2
- export const buildCliHistoryResponse = ({ asin, marketplaceId, metrics, response, }) => {
3
- const series = {
4
- ...(metrics.includes('bsr') && response.series?.bsr ? { bsr: response.series.bsr } : {}),
5
- ...(metrics.includes('price') && response.series?.price
6
- ? { price: response.series.price }
7
- : {}),
8
- };
9
- const hasAnyPoints = Object.values(series).some(metricSeries => metricSeries.points.length > 0);
10
- const status = normalizeHistoryStatus(response.status);
11
- return {
12
- schemaVersion: response.schemaVersion ?? 1,
13
- asin,
14
- marketplaceId,
15
- status: status ?? (response.syncTriggered ? 'collecting' : hasAnyPoints ? 'ready' : 'empty'),
16
- latestImportAt: response.latestImportAt,
17
- syncTriggered: response.syncTriggered,
18
- series,
19
- };
20
- };
21
- const normalizeHistoryStatus = (status) => {
22
- if (status === 'ready' || status === 'collecting' || status === 'empty') {
23
- return status;
24
- }
25
- return null;
26
- };