Hermes Shop is a standalone shop app that Hermes can operate conversationally. It should help a user create products, edit products, upload product images, configure site copy, and manage per-shop Stripe checkout plus Shippo shipping/labels.
Important reference:
references/stripe-onboarding-and-product-model-ui.md — session learning on per-shop Stripe onboarding, product model visibility, and avoiding Jungle framing.Current test deployment:
/home/avalon/apps/hermes-shophttps://shop.apps.poofc.comhermes-shop4039/home/avalon/apps/hermes-shop/data/shop.jsonThis skill is for operating Hermes Shop directly and for shaping the future Hermes Spawn optional shop add-on.
Be short and practical. If the user did not provide enough information, ask only for the missing fields needed for the next action. For setup/onboarding flows, use one clear next step at a time and avoid long explanatory checklists unless Alex asks for a plan.
Good:
I can add it. I need the product name, price, and whether it ships.
Good for Stripe:
I can connect Stripe. I need the Stripe account or permission to start a new Connect onboarding link.
Bad:
To create a comprehensive commerce object, please provide...
A product can include:
title — requireddescriptiontype — physical, digital, or servicestatus — draft, active, or archivedpriceCents, currency, taxable, compareAtCents, costCentssku, barcode, inventorytagsimages[] — { id, url, alt, role }variants[] — { id, title, options, priceCents, sku, inventory, imageUrl, active, stripePriceId }shipping — package/weight/origin fieldsstripe — product/price/checkout fieldsshippo — provider defaults for rates/labelsfulfillment — tracking/label/status fieldsmetadata — flexible extra dataUI rule: product cards must expose readable model details in the app, not only JSON/API output. At minimum show type, status, SKU, inventory, Stripe product/price state, Shippo state, fulfillment state, and optional raw JSON.
Need:
If missing, ask:
What should I call it, and what price should it be?
Need:
If missing, ask one compact question listing only missing pieces.
Need:
Use:
curl -fsS -X POST http://127.0.0.1:4039/api/products/<id>/images \
-H 'Content-Type: application/json' \
--data-binary '{"dataUrl":"data:image/jpeg;base64,...","alt":"Front of product"}'
Need:
Use PUT /api/products/:id with only changed fields.
GET /api/health
GET /api/shop
GET /api/products
GET /api/products/:id
PUT /api/site
GET /api/stripe/config
PUT /api/stripe/config
POST /api/stripe/onboarding-link
POST /api/products
PUT /api/products/:id
DELETE /api/products/:id
POST /api/products/:id/images
Base URL:
http://127.0.0.1:4039
curl -fsS http://127.0.0.1:4039/api/shop | jq .
curl -fsS -X POST http://127.0.0.1:4039/api/products \
-H 'Content-Type: application/json' \
--data-binary '{
"title":"Example Product",
"type":"physical",
"status":"draft",
"priceCents":2500,
"currency":"USD",
"inventory":5,
"shipping":{"requiresShipping":true,"weightValue":8,"weightUnit":"oz"}
}' | jq .product
curl -fsS -X PUT http://127.0.0.1:4039/api/products/<id> \
-H 'Content-Type: application/json' \
--data-binary '{"status":"archived"}' | jq .product
curl -fsS -X PUT http://127.0.0.1:4039/api/site \
-H 'Content-Type: application/json' \
--data-binary '{"headline":"New headline","statusText":"Live"}' | jq .site
The storefront hero is intentionally minimal and image-led. Site config supports:
nameeyebrowheadlinesubheadlinestatusTextctaLabelheroImage — optional URL/path for the hero backgroundCurrent frontend fallback order for the hero background:
site.heroImage/shop-hero.svgFor “clean up the hero” requests, prefer one big background image, a short eyebrow/title, and a single product-scroll CTA. Avoid restoring multiple parallel CTAs or verbose descriptive subcopy in the hero unless asked. If Alex asks for “featured product” language, set the hero title to a concise product feature label such as Feature product one instead of repeating Hermes Shop.
Hermes Shop should separate customer-facing browsing from operator/configuration screens:
Edit config or similar.Reusable modal direction:
OverlayModal) inside Hermes Shop for product details and future shop overlays.inset:0, padding:0 on the backdrop, full viewport-height panel, and safe-area padding inside the panel only.Performance and motion rules for reusable overlays:
rendered/is-closing state so the close animation plays, then unmount after the transition.transform, opacity) with a short duration (~180–220ms) and will-change; avoid heavy blur/backdrop-filter transitions on modal surfaces.prefers-reduced-motion: reduce override.×).Product grid sizing rules:
aspect-ratio: 1/1) and slightly larger card padding/title sizing on wider viewports, with tighter spacing only on narrow mobile.Stripe is a payment processor, not the catalog source of truth. Each Hermes Shop gets its own Stripe configuration under integrations.stripe; never treat Stripe as one global account for all tenant shops.
Current config operations:
curl -fsS http://127.0.0.1:4039/api/stripe/config | jq .
curl -fsS -X PUT http://127.0.0.1:4039/api/stripe/config \
-H 'Content-Type: application/json' \
--data-binary '{"mode":"test","businessName":"Example Shop","accountId":"acct_...","publishableKey":"pk_test_...","onboardingComplete":true}' | jq .
The UI has a Stripe onboarding panel. Stripe Connect Standard OAuth is wired through POST /api/stripe/onboarding-link and GET /api/stripe/oauth/callback. It requires platform env vars STRIPE_SECRET_KEY and STRIPE_CONNECT_CLIENT_ID (or STRIPE_CLIENT_ID).
Current live operations when env is configured:
acct_... and publishable key under integrations.stripePOST /api/checkout/:idproduct.stripe / variant.stripePriceIdDo not claim live checkout is enabled unless the Stripe account is connected and a checkout/session endpoint is verified.
Shippo handles shipping rates, labels, and tracking.
Planned operations:
fulfillmentFor physical products, ask for package weight/dimensions before enabling shipping automation.
When this becomes a Spawn add-on:
When packaging a standalone Hermes Shop build for the scoped Spawn deployment broker:
dist/, tenant data/shop.json, product uploads, and a dependency-free server.js at the artifact root.node server.js; if server.js uses ESM imports, include a root package.json with {"private":true,"type":"module"}.deployment-manifest.json with all required identity/runtime fields:{
"tenantId": "tenant-id",
"appName": "shop-app-name",
"runtime": "node",
"start": "node server.js",
"healthPath": "/healthz",
"hostnames": ["shop.example.com"]
}
appName, artifact path, SHA-256, verification.healthPath, and requested hostnames. Manifest hostnames may be a superset, but every requested hostname must be declared./data/hermes/bin/spawn-deploy <request.json>.dns_pending; that means PM2/local nginx are live but authoritative DNS/TLS is not ready. Verify localHealth, every localNginx result, PM2 status, and authoritative DNS separately.cd /home/avalon/apps/hermes-shop
npm run build
pm2 restart /home/avalon/apps/ecosystem.config.js --only hermes-shop --update-env
pm2 save
npm run smoke
curl -fsS https://shop.apps.poofc.com/api/health
Pitfall: restarting by bare PM2 process name (pm2 restart hermes-shop --update-env) can pick up the caller's current environment instead of the ecosystem file and may drop the configured PORT=4039. Use the ecosystem restart form above for deploys.
npm run build passes.npm run smoke passes.hermes-shop is online on port 4039.ok: true.data/shop.json./uploads/....