@reactionary/feeds 0.10.41 → 0.10.43

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
@@ -13,7 +13,8 @@ The package keeps feed generation separate from protocol adapters. A host can pr
13
13
 
14
14
  The default registry includes:
15
15
 
16
- - `acp-product-feed` — ACP product feed JSONL by default, with JSON support in protocol adapters.
16
+ - `acp-product-feed` — ACP Feed API (2026-04-17) `Product` records: `products.jsonl` for file ingestion by default (one product with its variants per line), or with `format: 'json'` the `{ "products": [...] }` body of `PATCH /feeds/{id}/products`. Prices are integer minor units with upper-case currency codes. `toACPFeedMetadata(feedId, feed)` builds the matching `metadata.json`.
17
+ - `acp-feed-metadata` — the ACP feed's `metadata.json` (`id`, `target_country`, `updated_at`) for file ingestion next to `products.jsonl`.
17
18
  - `google-merchant-feed` — Google Merchant RSS XML.
18
19
  - `sitemap-feed` — sitemap XML of product URLs.
19
20
  - `pricerunner-feed` — PriceRunner-style product XML.
@@ -243,6 +244,7 @@ reactionary-feeds generate \
243
244
  --feed finnish \
244
245
  --testMode \
245
246
  --output acp-product-feed=./products.jsonl \
247
+ --output acp-feed-metadata=./metadata.json \
246
248
  --output google-merchant-feed=./google-merchant.xml \
247
249
  --output sitemap-feed=./sitemap.xml
248
250
  ```
@@ -255,3 +257,75 @@ node dist/packages/feeds/cli.js list-feeds --config ./packages/feeds/documentati
255
257
  ```
256
258
 
257
259
  When `@reactionary/feeds` is installed as a package, npm/pnpm/yarn exposes the `reactionary-feeds` command from the package `bin` entry.
260
+
261
+ ## Publishing ACP feeds to an agent
262
+
263
+ ACP product feeds (2026-04-17) are **pushed by the merchant to the agent**. The agent (e.g. OpenAI) hosts the Feed API — `POST /feeds`, `GET /feeds/{id}`, `GET`/`PATCH /feeds/{id}/products` — and never calls the merchant for feeds. Your ACP checkout server has no feed endpoints; publishing is a separate job you schedule:
264
+
265
+ ```
266
+ your scheduler (cron, Kubernetes CronJob, CI)
267
+ └─ reactionary-feeds publish-acp ── reads the catalogue (Reactionary client)
268
+ └─ PATCH {agent Feed API}/feeds/{id}/products ──▶ agent
269
+ ```
270
+
271
+ From the agent's onboarding you receive the Feed API base URL, an API key and (usually) a feed id per catalogue. Put them in the feeds config — the key stays in the environment:
272
+
273
+ ```js
274
+ // feeds.config.mjs
275
+ export default {
276
+ acpFeedApi: {
277
+ baseUrl: 'https://feeds.agent.example/api', // from onboarding
278
+ apiKeyEnv: 'ACP_FEED_API_KEY', // default; the key itself is never in config
279
+ },
280
+ feeds: {
281
+ finnish: {
282
+ languageContext: { locale: 'fi-FI', currencyCode: 'EUR' },
283
+ search: { term: '', facets: [], filters: ['market:fi'], paginationOptions: { pageNumber: 1, pageSize: 50 } },
284
+ productUrlBase: 'https://shop.example/{lang}/products/{slug}',
285
+ acp: { feedId: 'feed_8f3K2x' }, // from onboarding
286
+ },
287
+ },
288
+ };
289
+ ```
290
+
291
+ Publish on a schedule. The command prints `{ "feedId", "products", "batches" }` and exits non-zero on any failure, so the scheduler's alerting sees it:
292
+
293
+ ```bash
294
+ ACP_FEED_API_KEY=... reactionary-feeds publish-acp -c ./feeds.config.mjs -f finnish
295
+ ```
296
+
297
+ ```cron
298
+ # Every 15 minutes
299
+ */15 * * * * ACP_FEED_API_KEY=... reactionary-feeds publish-acp -c /etc/shop/feeds.config.mjs -f finnish --no-progress
300
+ ```
301
+
302
+ ```yaml
303
+ apiVersion: batch/v1
304
+ kind: CronJob
305
+ metadata:
306
+ name: acp-feed-finnish
307
+ spec:
308
+ schedule: "*/15 * * * *"
309
+ concurrencyPolicy: Forbid
310
+ jobTemplate:
311
+ spec:
312
+ template:
313
+ spec:
314
+ restartPolicy: Never
315
+ containers:
316
+ - name: publish
317
+ image: registry.example/shop-feeds:latest
318
+ args: ["publish-acp", "-c", "/config/feeds.config.mjs", "-f", "finnish", "--no-progress"]
319
+ env:
320
+ - name: ACP_FEED_API_KEY
321
+ valueFrom: { secretKeyRef: { name: acp-feed-api, key: api-key } }
322
+ ```
323
+
324
+ Options: `--batch-size <n>` (products per upsert, default 100), `--testMode` (two search pages only) and `--no-progress`.
325
+
326
+ Notes:
327
+
328
+ - If the agent does not hand out feed ids, run `reactionary-feeds create-acp-feed -c ./feeds.config.mjs -f finnish` once; it calls `POST /feeds` with the feed's target country and prints the id to put into `acp.feedId`. Nothing is stored at runtime.
329
+ - `PATCH` upserts by product id and never removes products. Products that should no longer be sold are reported as unavailable when they are still found; to drop products entirely, publish a full replacement.
330
+ - **Full replacement** instead of the API: `reactionary-feeds generate -c ./feeds.config.mjs -f finnish --output acp-product-feed=./products.jsonl --output acp-feed-metadata=./metadata.json`, then upload both files however the agent's onboarding specifies (the ACP spec leaves file transfer to the platform).
331
+ - The same publisher is available in code as `ReactionaryACPFeedPublisher` / `createACPFeedPublisherFromConfig` (also re-exported from `@reactionary/acp`).