Svelte Commerce
A standalone open-source storefront built with SvelteKit, headless across 26 commerce backends. MIT licensed.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Restaurant Vertical AI
翻译准备中
正在加载完整 README。
查看完整 README ↗An AI operations team for restaurants that sell on Zomato, Swiggy and dine-in. A web app plus a conversational copilot that can act on the owner's behalf, with every change held for the owner's approval. Product plan: PLAN.md.
npm install
cp .env.example .env.local # optional: add ANTHROPIC_API_KEY for the full copilot
npm run dev # http://localhost:3000
The first request creates .data/zest.db and seeds a sample restaurant (Rasoi 27). "Look around with sample data" opens it; "Get started" creates your own restaurant in the same database.
For a production build: npm run build && npm start. Production needs ZEST_SECRET_KEY, and keeps the sample restaurant read-only unless ZEST_DEMO_WRITABLE=1.
To try a real MCP connection locally:
node scripts/mock-partner-mcp.mjs # a stand-in partner MCP server on :8787
Then in Connections pick a platform → "MCP server" → http://localhost:8787/mcp. Approving a menu change calls its tools; http://localhost:8787/log shows what it received.
The pages and the API are one Next.js app, so one Vercel project serves both. The database lives on Turso (hosted SQLite).
Database. In Turso, create a database (pick the Mumbai region). Copy its URL (libsql://…) and create a token.
Project. In Vercel, import this GitHub repository. Framework is detected as Next.js; leave the build settings as they are.
Environment variables (Project → Settings → Environment Variables, then redeploy):
| Name | Value |
|---|---|
DATABASE_URL | the Turso URL |
DATABASE_AUTH_TOKEN | the Turso token |
ZEST_SECRET_KEY | output of node -e "console.log(require('crypto').randomBytes(32).toString('base64'))". Keep it safe: changing it makes stored platform credentials unreadable. |
ANTHROPIC_API_KEY | optional, for the full copilot and document reading |
ZEST_DEMO_WRITABLE | optional, 1 to let visitors change the sample restaurant |
Open the deployment URL. The first request creates the tables and the sample restaurant.
Without DATABASE_URL the app still starts on Vercel, but on a temporary database that resets whenever the server restarts.
Other hosts. Dockerfile builds a self-contained server (data on /data), and render.yaml is a Render blueprint with a persistent disk.
Browser (Next.js pages) ──► /api/* route handlers ──► lib/server/*
├─ db.ts, schema.ts libSQL/SQLite via Drizzle
├─ copilot.ts Claude tool runner + keyword fallback
├─ approvals.ts draft → approve → execute → audit
├─ connectors.ts sandbox | MCP | partner API | partner-app sign-in
├─ ingest.ts payout emails (rules, Claude fallback)
├─ vision.ts documents and menu photos (Claude vision)
├─ whatsapp.ts Meta Cloud API, outbox
└─ crypto.ts, session.ts encrypted credentials, signed cookie
Nothing reaches a platform without the owner. Agents and the menu editor create approvals. Approving one runs it through each live platform's connector and records every step (and any failure, per platform) in the audit log. Menu changes can be undone; posted replies and disputes can't.
Connectors. Every platform implements one interface (setAvailability, setPrice, updateItem, replyToReview, raiseDispute, createPromo, listOrders):
| Method | Status |
|---|---|
| MCP server | Works. Verified on connect by listing tools; actions call the server's tools. |
| Partner API | Adapter in place; paths are placeholders until partner API access and specs are available. Saved keys show as "pending" until *_PARTNER_API_URL is set. |
| Partner-app sign-in | Credentials are stored encrypted. The browser worker that signs in and acts on the partner portal is the next build step; until then actions report that clearly. |
| Sandbox | Used by the sample restaurant. Mirrors actions locally. |
Copilot. With ANTHROPIC_API_KEY, the copilot runs Claude (claude-opus-5-5, server-side refusal fallback on) with nine tools: five that read the database (sales, orders, payouts, reviews, menu) and four that draft approvals (menu change, review reply, dispute, promo). Without a key it answers from the same data with keyword rules. Conversations are stored per restaurant.
Zomato and Swiggy listings. When an owner pastes their Zomato link (full or zoma.to share link) or Swiggy link, Zest reads it (Zomato: the public page and order page; Swiggy: the menu API its mobile site uses), fills in their profile, saves the rating and details, and brings in the menu with photos. The first read of a platform imports the menu if there is none, or sets that platform's prices and stock on dishes with the same name, adds the rest for that platform only, and marks dishes the listing doesn't have as off on that platform. Reading again (Connections) refreshes rating and details but never the menu. Results are cached for 10 minutes per link. Zomato blocks data-centre IPs, Vercel's included, so production needs LISTING_FETCH_VIA, a scraping relay URL template (see .env.example); without it the owner fills the form in by hand.
Dining. Tables grouped by area, each free, seated, bill printed or reserved, with party, time at the table and the running bill. Staff seat walk-ins, bookings or waitlisted guests, add dishes from the master menu at the dine-in price (the lowest platform price), print, and settle by cash, UPI or card; settling records a dine-in order so it counts in Today's sales. Bookings and the waitlist live alongside, with a WhatsApp "table ready" message to the guest. Each restaurant gets a public QR menu page. New restaurants get a starter floor plan to edit; the demo floor is rebuilt each day.
Growth. Promos and ads per platform with a hard spend cap (MAX_PROMO_SPEND). The builder estimates extra orders, spend, sales and return from the outlet's last 14 days of orders (or stated typical numbers when there is no history) and sends the plan for approval; approving it starts it through the platform connector and tracks it as a campaign. Suggestions come from the slowest open hours, a well-rated dish that sells little, new customers and upcoming festivals. The daily cron (and "Check now") ends campaigns at their cap or end date and pauses any returning less than its floor (3×) after ₹500, with a WhatsApp note. Campaign results need the platform's attribution data, so real outlets show spend and results once connectors report them; pausing on the platform side also waits on connector support.
Gmail. Owners connect Gmail with read-only access. Zest searches only Zomato and Swiggy senders (GMAIL_SENDERS), reads the last 30 days on first connect, then new mail on each sync. Each mail is sorted into payout, report, complaint, onboarding or other; payouts are extracted from the body (rules) or, with Claude, from PDF and CSV attachments. Tokens are stored encrypted. Disconnecting revokes access and deletes everything read, including payouts. While the Google app is in Testing mode, owners must be listed as test users and reconnect every 7 days.
Data in. Payout emails also go through POST /api/ingest/email (paste on the Money page, or a mail-forwarding rule). Orders arrive by POST /api/sync (pull from MCP/API connectors; run it on a schedule) or signed webhooks at /api/webhooks/{platform}.
| Endpoint | Does |
|---|---|
GET/POST/PATCH /api/outlet | Current restaurant; POST creates your restaurant and owner profile (name, area, city, optional FSSAI/GSTIN and listing links), PATCH updates details |
POST/DELETE /api/outlet/demo | Switch to the sample restaurant / reset it |
GET/POST/DELETE /api/connections | Platform connections |
GET /api/today | Dashboard summary |
GET /api/menu, POST /api/menu/changes, POST /api/menu/import | Menu, edits as an approval, add dishes |
GET /api/orders, POST /api/sync, POST /api/webhooks/[platform] | Orders in |
GET /api/payouts, POST /api/ingest/email | Payouts and email ingestion |
GET/DELETE /api/mail, POST /api/mail/sync | Connected inbox status, disconnect, sync now |
POST /api/listing/preview, POST /api/listing/{zomato|swiggy} | Read a listing link (preview only / save to the restaurant: rating, address, hours, FSSAI, cost for two, and the menu with photos and per-platform prices) |
GET /api/dining; POST /api/dining/tables, DELETE /api/dining/tables/{id} | Floor, open bills, today's bookings and waitlist, today's dine-in numbers; edit the floor plan |
POST /api/dining/tabs, PATCH /api/dining/tabs/{id} | Seat a party; change dishes, print, reopen, settle (cash/UPI/card, records a dine-in order) or clear |
POST /api/dining/reservations, PATCH /api/dining/reservations/{id} | Add a booking or waitlist entry; tell a guest their table is ready on WhatsApp, cancel, no-show |
GET /api/dining/qr, page /m/{outletId} | Table QR code and the public dine-in menu it opens |
GET /api/growth; POST /api/growth/estimate, /propose, /check; PATCH /api/growth/campaigns/{id} |
lib/server/jobs.ts): queued rows, claimed with an expiring lock, retried after 1, 5, 25 and 60 minutes, and marked failed after 5 attempts. /api/jobs/run queues an order sync per connected outlet every 5 minutes and runs due jobs; call it every minute from a free scheduler such as cron-job.org with Authorization: Bearer $CRON_SECRET. .github/workflows/sync.yml calls it every 15 minutes as a backup, and Vercel Cron calls /api/cron daily to queue inbox syncs and campaign checks. Failed jobs: SELECT kind, attempts, last_error FROM jobs WHERE status = 'failed'.components/dishes.tsx) until the owner uploads photos in the menu editor.motion and respects reduced-motion settings.| Campaigns and suggestions; estimate a plan from the outlet's orders; send it for approval; run the automatic pause; pause, resume or end |
GET /api/mail/insights | Payouts per platform, commission/ads/deduction shares, complaints and listing mails from the last 30 days |
GET /api/mail/google/start, /callback | Google sign-in for Gmail |
GET /api/cron | Scheduled sync of every inbox and platform (needs CRON_SECRET) |
GET /api/reviews, POST /api/reviews/[id]/reply | Reviews and reply drafts |
GET/POST /api/approvals, POST /api/approvals/[id] | Drafts; {action: approve | skip | undo} |
POST /api/onboarding/document, /menu, /submit | Read documents and menu photos, listing application |
GET/POST/DELETE /api/copilot | Conversation |
GET /api/notifications, POST /api/notifications/daily-summary | WhatsApp outbox, morning summary |
GET /api/audit | Activity log |