@cimplify/sdk 0.40.1 → 0.42.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/dist/cli/commands/deploy.mjs +561 -0
- package/dist/cli/commands/dev.mjs +434 -0
- package/dist/cli/commands/domains.mjs +479 -0
- package/dist/cli/commands/env.mjs +610 -0
- package/dist/cli/commands/link.mjs +325 -0
- package/dist/cli/commands/login.mjs +515 -0
- package/dist/cli/commands/logout.mjs +54 -0
- package/dist/cli/commands/logs.mjs +358 -0
- package/dist/cli/commands/projects.mjs +327 -0
- package/dist/cli/commands/rollback.mjs +477 -0
- package/dist/cli/commands/status.mjs +380 -0
- package/dist/cli/commands/unlink.mjs +58 -0
- package/dist/cli/commands/whoami.mjs +234 -0
- package/dist/dispatcher.mjs +94 -2
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/templates/storefront-bakery/.env.example +5 -0
- package/templates/storefront-bakery/AGENTS.md +57 -0
- package/templates/storefront-bakery/app/about/page.tsx +21 -44
- package/templates/storefront-bakery/app/accessibility/page.tsx +11 -0
- package/templates/storefront-bakery/app/account/addresses/page.tsx +21 -0
- package/templates/storefront-bakery/app/account/orders/page.tsx +21 -0
- package/templates/storefront-bakery/app/account/page.tsx +22 -0
- package/templates/storefront-bakery/app/account/settings/page.tsx +21 -0
- package/templates/storefront-bakery/app/categories/[slug]/page.tsx +2 -1
- package/templates/storefront-bakery/app/collections/[slug]/page.tsx +2 -1
- package/templates/storefront-bakery/app/contact/contact-form.tsx +109 -0
- package/templates/storefront-bakery/app/contact/page.tsx +54 -0
- package/templates/storefront-bakery/app/error.tsx +60 -0
- package/templates/storefront-bakery/app/faq/page.tsx +13 -89
- package/templates/storefront-bakery/app/layout.tsx +37 -2
- package/templates/storefront-bakery/app/llms.txt/route.ts +94 -0
- package/templates/storefront-bakery/app/login/page.tsx +17 -0
- package/templates/storefront-bakery/app/not-found.tsx +39 -0
- package/templates/storefront-bakery/app/opensearch.xml/route.ts +37 -0
- package/templates/storefront-bakery/app/page.tsx +6 -6
- package/templates/storefront-bakery/app/privacy/page.tsx +24 -68
- package/templates/storefront-bakery/app/returns/page.tsx +11 -0
- package/templates/storefront-bakery/app/robots.ts +18 -0
- package/templates/storefront-bakery/app/search/page.tsx +38 -0
- package/templates/storefront-bakery/app/search/search-client.tsx +7 -0
- package/templates/storefront-bakery/app/shipping/page.tsx +16 -0
- package/templates/storefront-bakery/app/shop/page.tsx +3 -2
- package/templates/storefront-bakery/app/signup/page.tsx +17 -0
- package/templates/storefront-bakery/app/sitemap-page/page.tsx +167 -0
- package/templates/storefront-bakery/app/sitemap.ts +62 -0
- package/templates/storefront-bakery/app/terms/page.tsx +24 -82
- package/templates/storefront-bakery/app/track-order/page.tsx +24 -0
- package/templates/storefront-bakery/app/track-order/track-order-form.tsx +69 -0
- package/templates/storefront-bakery/components/account-iframe.tsx +13 -0
- package/templates/storefront-bakery/components/footer.tsx +88 -130
- package/templates/storefront-bakery/components/header.tsx +8 -16
- package/templates/storefront-bakery/components/policy-page.tsx +49 -0
- package/templates/storefront-bakery/lib/brand.ts +568 -0
- package/templates/storefront-fashion/.env.example +16 -0
- package/templates/storefront-fashion/AGENTS.md +52 -0
- package/templates/storefront-fashion/README.md +77 -0
- package/templates/storefront-fashion/app/about/page.tsx +41 -0
- package/templates/storefront-fashion/app/accessibility/page.tsx +11 -0
- package/templates/storefront-fashion/app/account/addresses/page.tsx +21 -0
- package/templates/storefront-fashion/app/account/orders/page.tsx +21 -0
- package/templates/storefront-fashion/app/account/page.tsx +22 -0
- package/templates/storefront-fashion/app/account/settings/page.tsx +21 -0
- package/templates/storefront-fashion/app/cart/page.tsx +9 -0
- package/templates/storefront-fashion/app/categories/[slug]/listing-client.tsx +19 -0
- package/templates/storefront-fashion/app/categories/[slug]/page.tsx +130 -0
- package/templates/storefront-fashion/app/checkout/page.tsx +17 -0
- package/templates/storefront-fashion/app/collections/[slug]/listing-client.tsx +20 -0
- package/templates/storefront-fashion/app/collections/[slug]/page.tsx +130 -0
- package/templates/storefront-fashion/app/contact/contact-form.tsx +109 -0
- package/templates/storefront-fashion/app/contact/page.tsx +54 -0
- package/templates/storefront-fashion/app/error.tsx +61 -0
- package/templates/storefront-fashion/app/faq/page.tsx +46 -0
- package/templates/storefront-fashion/app/globals.css +46 -0
- package/templates/storefront-fashion/app/layout.tsx +76 -0
- package/templates/storefront-fashion/app/llms.txt/route.ts +94 -0
- package/templates/storefront-fashion/app/login/page.tsx +17 -0
- package/templates/storefront-fashion/app/lookbook/page.tsx +132 -0
- package/templates/storefront-fashion/app/not-found.tsx +39 -0
- package/templates/storefront-fashion/app/opensearch.xml/route.ts +37 -0
- package/templates/storefront-fashion/app/orders/[id]/page.tsx +24 -0
- package/templates/storefront-fashion/app/page.tsx +183 -0
- package/templates/storefront-fashion/app/privacy/page.tsx +44 -0
- package/templates/storefront-fashion/app/products/[slug]/page.tsx +165 -0
- package/templates/storefront-fashion/app/products/[slug]/product-detail.tsx +71 -0
- package/templates/storefront-fashion/app/returns/page.tsx +11 -0
- package/templates/storefront-fashion/app/robots.ts +18 -0
- package/templates/storefront-fashion/app/search/page.tsx +38 -0
- package/templates/storefront-fashion/app/search/search-client.tsx +7 -0
- package/templates/storefront-fashion/app/shipping/page.tsx +16 -0
- package/templates/storefront-fashion/app/shop/page.tsx +63 -0
- package/templates/storefront-fashion/app/shop/shop-client.tsx +32 -0
- package/templates/storefront-fashion/app/signup/page.tsx +17 -0
- package/templates/storefront-fashion/app/sitemap-page/page.tsx +167 -0
- package/templates/storefront-fashion/app/sitemap.ts +59 -0
- package/templates/storefront-fashion/app/size-guide/page.tsx +155 -0
- package/templates/storefront-fashion/app/terms/page.tsx +44 -0
- package/templates/storefront-fashion/app/track-order/page.tsx +24 -0
- package/templates/storefront-fashion/app/track-order/track-order-form.tsx +69 -0
- package/templates/storefront-fashion/components/account-iframe.tsx +13 -0
- package/templates/storefront-fashion/components/brand-marquee.tsx +27 -0
- package/templates/storefront-fashion/components/cart-pill.tsx +32 -0
- package/templates/storefront-fashion/components/category-grid.tsx +28 -0
- package/templates/storefront-fashion/components/category-tiles.tsx +104 -0
- package/templates/storefront-fashion/components/collection-strip.tsx +45 -0
- package/templates/storefront-fashion/components/feature-hero.tsx +82 -0
- package/templates/storefront-fashion/components/footer.tsx +153 -0
- package/templates/storefront-fashion/components/header.tsx +43 -0
- package/templates/storefront-fashion/components/hero.tsx +28 -0
- package/templates/storefront-fashion/components/nav-link.tsx +20 -0
- package/templates/storefront-fashion/components/newsletter.tsx +50 -0
- package/templates/storefront-fashion/components/policy-page.tsx +49 -0
- package/templates/storefront-fashion/components/promo-banner.tsx +41 -0
- package/templates/storefront-fashion/components/providers.tsx +31 -0
- package/templates/storefront-fashion/components/section-heading.tsx +37 -0
- package/templates/storefront-fashion/components/store-product-card.tsx +83 -0
- package/templates/storefront-fashion/components/trade-in-cta.tsx +54 -0
- package/templates/storefront-fashion/components/trust-bar.tsx +66 -0
- package/templates/storefront-fashion/lib/brand.ts +516 -0
- package/templates/storefront-fashion/lib/cart.ts +27 -0
- package/templates/storefront-fashion/next.config.ts +40 -0
- package/templates/storefront-fashion/package.json +28 -0
- package/templates/storefront-fashion/postcss.config.mjs +7 -0
- package/templates/storefront-fashion/tsconfig.json +23 -0
- package/templates/storefront-grocery/.env.example +16 -0
- package/templates/storefront-grocery/AGENTS.md +39 -0
- package/templates/storefront-grocery/README.md +73 -0
- package/templates/storefront-grocery/app/about/page.tsx +38 -0
- package/templates/storefront-grocery/app/accessibility/page.tsx +11 -0
- package/templates/storefront-grocery/app/account/addresses/page.tsx +21 -0
- package/templates/storefront-grocery/app/account/orders/page.tsx +21 -0
- package/templates/storefront-grocery/app/account/page.tsx +22 -0
- package/templates/storefront-grocery/app/account/settings/page.tsx +21 -0
- package/templates/storefront-grocery/app/cart/page.tsx +9 -0
- package/templates/storefront-grocery/app/categories/[slug]/listing-client.tsx +19 -0
- package/templates/storefront-grocery/app/categories/[slug]/page.tsx +118 -0
- package/templates/storefront-grocery/app/checkout/page.tsx +17 -0
- package/templates/storefront-grocery/app/collections/[slug]/listing-client.tsx +20 -0
- package/templates/storefront-grocery/app/collections/[slug]/page.tsx +118 -0
- package/templates/storefront-grocery/app/contact/contact-form.tsx +109 -0
- package/templates/storefront-grocery/app/contact/page.tsx +54 -0
- package/templates/storefront-grocery/app/error.tsx +60 -0
- package/templates/storefront-grocery/app/faq/page.tsx +46 -0
- package/templates/storefront-grocery/app/globals.css +45 -0
- package/templates/storefront-grocery/app/layout.tsx +75 -0
- package/templates/storefront-grocery/app/llms.txt/route.ts +94 -0
- package/templates/storefront-grocery/app/login/page.tsx +17 -0
- package/templates/storefront-grocery/app/not-found.tsx +39 -0
- package/templates/storefront-grocery/app/opensearch.xml/route.ts +37 -0
- package/templates/storefront-grocery/app/orders/[id]/page.tsx +21 -0
- package/templates/storefront-grocery/app/page.tsx +97 -0
- package/templates/storefront-grocery/app/privacy/page.tsx +44 -0
- package/templates/storefront-grocery/app/returns/page.tsx +11 -0
- package/templates/storefront-grocery/app/robots.ts +18 -0
- package/templates/storefront-grocery/app/search/page.tsx +38 -0
- package/templates/storefront-grocery/app/search/search-client.tsx +7 -0
- package/templates/storefront-grocery/app/shipping/page.tsx +16 -0
- package/templates/storefront-grocery/app/shop/page.tsx +31 -0
- package/templates/storefront-grocery/app/shop/shop-client.tsx +27 -0
- package/templates/storefront-grocery/app/signup/page.tsx +17 -0
- package/templates/storefront-grocery/app/sitemap-page/page.tsx +167 -0
- package/templates/storefront-grocery/app/sitemap.ts +62 -0
- package/templates/storefront-grocery/app/terms/page.tsx +44 -0
- package/templates/storefront-grocery/app/track-order/page.tsx +24 -0
- package/templates/storefront-grocery/app/track-order/track-order-form.tsx +69 -0
- package/templates/storefront-grocery/components/account-iframe.tsx +13 -0
- package/templates/storefront-grocery/components/cart-pill.tsx +32 -0
- package/templates/storefront-grocery/components/category-grid.tsx +28 -0
- package/templates/storefront-grocery/components/collection-strip.tsx +45 -0
- package/templates/storefront-grocery/components/footer.tsx +148 -0
- package/templates/storefront-grocery/components/header.tsx +43 -0
- package/templates/storefront-grocery/components/hero.tsx +25 -0
- package/templates/storefront-grocery/components/nav-link.tsx +20 -0
- package/templates/storefront-grocery/components/policy-page.tsx +49 -0
- package/templates/storefront-grocery/components/providers.tsx +31 -0
- package/templates/storefront-grocery/components/store-product-card.tsx +83 -0
- package/templates/storefront-grocery/lib/brand.ts +373 -0
- package/templates/storefront-grocery/lib/cart.ts +27 -0
- package/templates/storefront-grocery/next.config.ts +40 -0
- package/templates/storefront-grocery/package.json +28 -0
- package/templates/storefront-grocery/postcss.config.mjs +7 -0
- package/templates/storefront-grocery/tsconfig.json +23 -0
- package/templates/storefront-restaurant/.env.example +16 -0
- package/templates/storefront-restaurant/AGENTS.md +39 -0
- package/templates/storefront-restaurant/README.md +73 -0
- package/templates/storefront-restaurant/app/about/page.tsx +38 -0
- package/templates/storefront-restaurant/app/accessibility/page.tsx +11 -0
- package/templates/storefront-restaurant/app/account/addresses/page.tsx +21 -0
- package/templates/storefront-restaurant/app/account/orders/page.tsx +21 -0
- package/templates/storefront-restaurant/app/account/page.tsx +22 -0
- package/templates/storefront-restaurant/app/account/settings/page.tsx +21 -0
- package/templates/storefront-restaurant/app/cart/page.tsx +9 -0
- package/templates/storefront-restaurant/app/categories/[slug]/listing-client.tsx +19 -0
- package/templates/storefront-restaurant/app/categories/[slug]/page.tsx +118 -0
- package/templates/storefront-restaurant/app/checkout/page.tsx +17 -0
- package/templates/storefront-restaurant/app/collections/[slug]/listing-client.tsx +20 -0
- package/templates/storefront-restaurant/app/collections/[slug]/page.tsx +118 -0
- package/templates/storefront-restaurant/app/contact/contact-form.tsx +109 -0
- package/templates/storefront-restaurant/app/contact/page.tsx +54 -0
- package/templates/storefront-restaurant/app/error.tsx +60 -0
- package/templates/storefront-restaurant/app/faq/page.tsx +46 -0
- package/templates/storefront-restaurant/app/globals.css +44 -0
- package/templates/storefront-restaurant/app/layout.tsx +80 -0
- package/templates/storefront-restaurant/app/llms.txt/route.ts +94 -0
- package/templates/storefront-restaurant/app/login/page.tsx +17 -0
- package/templates/storefront-restaurant/app/not-found.tsx +39 -0
- package/templates/storefront-restaurant/app/opensearch.xml/route.ts +37 -0
- package/templates/storefront-restaurant/app/orders/[id]/page.tsx +21 -0
- package/templates/storefront-restaurant/app/page.tsx +97 -0
- package/templates/storefront-restaurant/app/privacy/page.tsx +44 -0
- package/templates/storefront-restaurant/app/reservations/page.tsx +66 -0
- package/templates/storefront-restaurant/app/reservations/reservations-client.tsx +234 -0
- package/templates/storefront-restaurant/app/returns/page.tsx +11 -0
- package/templates/storefront-restaurant/app/robots.ts +18 -0
- package/templates/storefront-restaurant/app/search/page.tsx +38 -0
- package/templates/storefront-restaurant/app/search/search-client.tsx +7 -0
- package/templates/storefront-restaurant/app/shipping/page.tsx +16 -0
- package/templates/storefront-restaurant/app/shop/page.tsx +31 -0
- package/templates/storefront-restaurant/app/shop/shop-client.tsx +27 -0
- package/templates/storefront-restaurant/app/signup/page.tsx +17 -0
- package/templates/storefront-restaurant/app/sitemap-page/page.tsx +167 -0
- package/templates/storefront-restaurant/app/sitemap.ts +62 -0
- package/templates/storefront-restaurant/app/terms/page.tsx +44 -0
- package/templates/storefront-restaurant/app/track-order/page.tsx +24 -0
- package/templates/storefront-restaurant/app/track-order/track-order-form.tsx +69 -0
- package/templates/storefront-restaurant/components/account-iframe.tsx +13 -0
- package/templates/storefront-restaurant/components/cart-pill.tsx +32 -0
- package/templates/storefront-restaurant/components/category-grid.tsx +28 -0
- package/templates/storefront-restaurant/components/collection-strip.tsx +45 -0
- package/templates/storefront-restaurant/components/footer.tsx +148 -0
- package/templates/storefront-restaurant/components/header.tsx +43 -0
- package/templates/storefront-restaurant/components/hero.tsx +25 -0
- package/templates/storefront-restaurant/components/nav-link.tsx +20 -0
- package/templates/storefront-restaurant/components/policy-page.tsx +49 -0
- package/templates/storefront-restaurant/components/product-modal.tsx +105 -0
- package/templates/storefront-restaurant/components/providers.tsx +31 -0
- package/templates/storefront-restaurant/components/store-product-card.tsx +83 -0
- package/templates/storefront-restaurant/lib/brand.ts +375 -0
- package/templates/storefront-restaurant/lib/cart.ts +27 -0
- package/templates/storefront-restaurant/next.config.ts +40 -0
- package/templates/storefront-restaurant/package.json +28 -0
- package/templates/storefront-restaurant/postcss.config.mjs +7 -0
- package/templates/storefront-restaurant/tsconfig.json +23 -0
- package/templates/storefront-retail/.env.example +5 -0
- package/templates/storefront-retail/AGENTS.md +64 -0
- package/templates/storefront-retail/app/about/page.tsx +24 -54
- package/templates/storefront-retail/app/accessibility/page.tsx +11 -0
- package/templates/storefront-retail/app/account/addresses/page.tsx +21 -0
- package/templates/storefront-retail/app/account/orders/page.tsx +21 -0
- package/templates/storefront-retail/app/account/page.tsx +22 -0
- package/templates/storefront-retail/app/account/settings/page.tsx +21 -0
- package/templates/storefront-retail/app/categories/[slug]/page.tsx +2 -1
- package/templates/storefront-retail/app/collections/[slug]/page.tsx +2 -1
- package/templates/storefront-retail/app/contact/contact-form.tsx +109 -0
- package/templates/storefront-retail/app/contact/page.tsx +54 -0
- package/templates/storefront-retail/app/error.tsx +61 -0
- package/templates/storefront-retail/app/faq/page.tsx +11 -92
- package/templates/storefront-retail/app/layout.tsx +37 -8
- package/templates/storefront-retail/app/llms.txt/route.ts +94 -0
- package/templates/storefront-retail/app/login/page.tsx +17 -0
- package/templates/storefront-retail/app/not-found.tsx +39 -0
- package/templates/storefront-retail/app/opensearch.xml/route.ts +37 -0
- package/templates/storefront-retail/app/page.tsx +16 -73
- package/templates/storefront-retail/app/privacy/page.tsx +24 -81
- package/templates/storefront-retail/app/products/[slug]/page.tsx +165 -0
- package/templates/storefront-retail/app/products/[slug]/product-detail.tsx +71 -0
- package/templates/storefront-retail/app/returns/page.tsx +11 -0
- package/templates/storefront-retail/app/robots.ts +18 -0
- package/templates/storefront-retail/app/search/page.tsx +38 -0
- package/templates/storefront-retail/app/search/search-client.tsx +7 -0
- package/templates/storefront-retail/app/shipping/page.tsx +16 -0
- package/templates/storefront-retail/app/shop/page.tsx +3 -3
- package/templates/storefront-retail/app/signup/page.tsx +17 -0
- package/templates/storefront-retail/app/sitemap-page/page.tsx +167 -0
- package/templates/storefront-retail/app/sitemap.ts +59 -0
- package/templates/storefront-retail/app/terms/page.tsx +24 -97
- package/templates/storefront-retail/app/track-order/page.tsx +24 -0
- package/templates/storefront-retail/app/track-order/track-order-form.tsx +69 -0
- package/templates/storefront-retail/components/account-iframe.tsx +13 -0
- package/templates/storefront-retail/components/brand-marquee.tsx +6 -15
- package/templates/storefront-retail/components/feature-hero.tsx +2 -1
- package/templates/storefront-retail/components/footer.tsx +85 -120
- package/templates/storefront-retail/components/header.tsx +10 -12
- package/templates/storefront-retail/components/newsletter.tsx +7 -9
- package/templates/storefront-retail/components/policy-page.tsx +49 -0
- package/templates/storefront-retail/components/promo-banner.tsx +12 -15
- package/templates/storefront-retail/components/store-product-card.tsx +7 -7
- package/templates/storefront-retail/components/trade-in-cta.tsx +11 -17
- package/templates/storefront-retail/components/trust-bar.tsx +45 -5
- package/templates/storefront-retail/lib/brand.ts +663 -0
- package/templates/storefront-services/.env.example +16 -0
- package/templates/storefront-services/AGENTS.md +41 -0
- package/templates/storefront-services/README.md +73 -0
- package/templates/storefront-services/app/about/page.tsx +38 -0
- package/templates/storefront-services/app/accessibility/page.tsx +11 -0
- package/templates/storefront-services/app/account/addresses/page.tsx +21 -0
- package/templates/storefront-services/app/account/orders/page.tsx +21 -0
- package/templates/storefront-services/app/account/page.tsx +22 -0
- package/templates/storefront-services/app/account/settings/page.tsx +21 -0
- package/templates/storefront-services/app/book/book-client.tsx +195 -0
- package/templates/storefront-services/app/book/page.tsx +65 -0
- package/templates/storefront-services/app/cart/page.tsx +9 -0
- package/templates/storefront-services/app/categories/[slug]/listing-client.tsx +19 -0
- package/templates/storefront-services/app/categories/[slug]/page.tsx +118 -0
- package/templates/storefront-services/app/checkout/page.tsx +17 -0
- package/templates/storefront-services/app/collections/[slug]/listing-client.tsx +20 -0
- package/templates/storefront-services/app/collections/[slug]/page.tsx +118 -0
- package/templates/storefront-services/app/contact/contact-form.tsx +109 -0
- package/templates/storefront-services/app/contact/page.tsx +54 -0
- package/templates/storefront-services/app/error.tsx +60 -0
- package/templates/storefront-services/app/faq/page.tsx +46 -0
- package/templates/storefront-services/app/globals.css +45 -0
- package/templates/storefront-services/app/layout.tsx +80 -0
- package/templates/storefront-services/app/llms.txt/route.ts +94 -0
- package/templates/storefront-services/app/login/page.tsx +17 -0
- package/templates/storefront-services/app/not-found.tsx +39 -0
- package/templates/storefront-services/app/opensearch.xml/route.ts +37 -0
- package/templates/storefront-services/app/orders/[id]/page.tsx +21 -0
- package/templates/storefront-services/app/page.tsx +97 -0
- package/templates/storefront-services/app/privacy/page.tsx +44 -0
- package/templates/storefront-services/app/returns/page.tsx +11 -0
- package/templates/storefront-services/app/robots.ts +18 -0
- package/templates/storefront-services/app/search/page.tsx +38 -0
- package/templates/storefront-services/app/search/search-client.tsx +7 -0
- package/templates/storefront-services/app/shipping/page.tsx +16 -0
- package/templates/storefront-services/app/shop/page.tsx +31 -0
- package/templates/storefront-services/app/shop/shop-client.tsx +27 -0
- package/templates/storefront-services/app/signup/page.tsx +17 -0
- package/templates/storefront-services/app/sitemap-page/page.tsx +167 -0
- package/templates/storefront-services/app/sitemap.ts +62 -0
- package/templates/storefront-services/app/terms/page.tsx +44 -0
- package/templates/storefront-services/app/track-order/page.tsx +24 -0
- package/templates/storefront-services/app/track-order/track-order-form.tsx +69 -0
- package/templates/storefront-services/components/account-iframe.tsx +13 -0
- package/templates/storefront-services/components/cart-pill.tsx +32 -0
- package/templates/storefront-services/components/category-grid.tsx +28 -0
- package/templates/storefront-services/components/collection-strip.tsx +45 -0
- package/templates/storefront-services/components/footer.tsx +148 -0
- package/templates/storefront-services/components/header.tsx +43 -0
- package/templates/storefront-services/components/hero.tsx +25 -0
- package/templates/storefront-services/components/nav-link.tsx +20 -0
- package/templates/storefront-services/components/policy-page.tsx +49 -0
- package/templates/storefront-services/components/product-modal.tsx +105 -0
- package/templates/storefront-services/components/providers.tsx +31 -0
- package/templates/storefront-services/components/store-product-card.tsx +83 -0
- package/templates/storefront-services/lib/brand.ts +394 -0
- package/templates/storefront-services/lib/cart.ts +27 -0
- package/templates/storefront-services/next.config.ts +40 -0
- package/templates/storefront-services/package.json +28 -0
- package/templates/storefront-services/postcss.config.mjs +7 -0
- package/templates/storefront-services/tsconfig.json +23 -0
- /package/templates/{storefront-retail → storefront-grocery}/components/product-modal.tsx +0 -0
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import { promises } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { randomBytes, createHash } from 'crypto';
|
|
5
|
+
import { createServer } from 'http';
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
|
|
8
|
+
// src/cli/commands/login.ts
|
|
9
|
+
|
|
10
|
+
// src/cli/errors.ts
|
|
11
|
+
var CLI_ERROR_CODE = {
|
|
12
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
13
|
+
AUTH_FAILED: "AUTH_FAILED",
|
|
14
|
+
PROJECT_NOT_FOUND: "PROJECT_NOT_FOUND",
|
|
15
|
+
INVALID_INPUT: "INVALID_INPUT",
|
|
16
|
+
ALREADY_LINKED: "ALREADY_LINKED",
|
|
17
|
+
SERVER_ERROR: "SERVER_ERROR",
|
|
18
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
19
|
+
TIMEOUT: "TIMEOUT"
|
|
20
|
+
};
|
|
21
|
+
var EXIT_CODE = {
|
|
22
|
+
ERROR: 1};
|
|
23
|
+
var CliError = class extends Error {
|
|
24
|
+
constructor(code, message, exitCode = EXIT_CODE.ERROR) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "CliError";
|
|
27
|
+
this.code = code;
|
|
28
|
+
this.exitCode = exitCode;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/cli/api-client.ts
|
|
33
|
+
var DEFAULT_API_BASE_URL = "https://api.cimplify.io";
|
|
34
|
+
var ENV_API_BASE_URL = "CIMPLIFY_API_URL";
|
|
35
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
36
|
+
var HEADER_AUTHORIZATION = "Authorization";
|
|
37
|
+
var HEADER_CONTENT_TYPE = "Content-Type";
|
|
38
|
+
var HEADER_ACCEPT = "Accept";
|
|
39
|
+
var HEADER_USER_AGENT = "User-Agent";
|
|
40
|
+
var BEARER_PREFIX = "Bearer ";
|
|
41
|
+
var CONTENT_TYPE_JSON = "application/json";
|
|
42
|
+
var USER_AGENT = "cimplify-cli";
|
|
43
|
+
var METHOD_GET = "GET";
|
|
44
|
+
var METHOD_POST = "POST";
|
|
45
|
+
var METHOD_PUT = "PUT";
|
|
46
|
+
var METHOD_DELETE = "DELETE";
|
|
47
|
+
var STATUS_UNAUTHORIZED = 401;
|
|
48
|
+
var STATUS_FORBIDDEN = 403;
|
|
49
|
+
var STATUS_NOT_FOUND = 404;
|
|
50
|
+
var STATUS_CONFLICT = 409;
|
|
51
|
+
var STATUS_BAD_REQUEST_MIN = 400;
|
|
52
|
+
var STATUS_SERVER_ERROR_MIN = 500;
|
|
53
|
+
var ApiClient = class _ApiClient {
|
|
54
|
+
constructor(baseUrl, apiKey) {
|
|
55
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
56
|
+
this.apiKey = apiKey;
|
|
57
|
+
}
|
|
58
|
+
static fromAuth(auth) {
|
|
59
|
+
return new _ApiClient(auth.apiBaseUrl, auth.apiKey);
|
|
60
|
+
}
|
|
61
|
+
static unauthenticated(baseUrl = resolveBaseUrl()) {
|
|
62
|
+
return new _ApiClient(baseUrl, "");
|
|
63
|
+
}
|
|
64
|
+
static withKey(apiKey, baseUrl = resolveBaseUrl()) {
|
|
65
|
+
return new _ApiClient(baseUrl, apiKey);
|
|
66
|
+
}
|
|
67
|
+
get(pathname, options) {
|
|
68
|
+
return this.request(METHOD_GET, pathname, void 0, options);
|
|
69
|
+
}
|
|
70
|
+
post(pathname, body, options) {
|
|
71
|
+
return this.request(METHOD_POST, pathname, body, options);
|
|
72
|
+
}
|
|
73
|
+
put(pathname, body, options) {
|
|
74
|
+
return this.request(METHOD_PUT, pathname, body, options);
|
|
75
|
+
}
|
|
76
|
+
delete(pathname, options) {
|
|
77
|
+
return this.request(METHOD_DELETE, pathname, void 0, options);
|
|
78
|
+
}
|
|
79
|
+
async request(method, pathname, body, options) {
|
|
80
|
+
const url = this.buildUrl(pathname, options?.query);
|
|
81
|
+
const headers = {
|
|
82
|
+
[HEADER_ACCEPT]: CONTENT_TYPE_JSON,
|
|
83
|
+
[HEADER_USER_AGENT]: USER_AGENT
|
|
84
|
+
};
|
|
85
|
+
if (this.apiKey) {
|
|
86
|
+
headers[HEADER_AUTHORIZATION] = `${BEARER_PREFIX}${this.apiKey}`;
|
|
87
|
+
}
|
|
88
|
+
let payload;
|
|
89
|
+
if (body !== void 0) {
|
|
90
|
+
headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_JSON;
|
|
91
|
+
payload = JSON.stringify(body);
|
|
92
|
+
}
|
|
93
|
+
const controller = new AbortController();
|
|
94
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
95
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
96
|
+
let response;
|
|
97
|
+
try {
|
|
98
|
+
response = await fetch(url, {
|
|
99
|
+
method,
|
|
100
|
+
headers,
|
|
101
|
+
body: payload,
|
|
102
|
+
signal: controller.signal
|
|
103
|
+
});
|
|
104
|
+
} catch (err) {
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
throw toNetworkError(err);
|
|
107
|
+
}
|
|
108
|
+
clearTimeout(timer);
|
|
109
|
+
return parseResponse(response);
|
|
110
|
+
}
|
|
111
|
+
buildUrl(pathname, query) {
|
|
112
|
+
const base = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
113
|
+
const url = new URL(`${this.baseUrl}${base}`);
|
|
114
|
+
if (query) {
|
|
115
|
+
for (const [k, v] of Object.entries(query)) {
|
|
116
|
+
if (v === void 0 || v === null) continue;
|
|
117
|
+
url.searchParams.set(k, String(v));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return url.toString();
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
function resolveBaseUrl(override) {
|
|
124
|
+
if (override && override.length > 0) return override;
|
|
125
|
+
const fromEnv = process.env[ENV_API_BASE_URL];
|
|
126
|
+
return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_API_BASE_URL;
|
|
127
|
+
}
|
|
128
|
+
async function parseResponse(response) {
|
|
129
|
+
const text = await response.text();
|
|
130
|
+
let body = void 0;
|
|
131
|
+
if (text.length > 0) {
|
|
132
|
+
try {
|
|
133
|
+
body = JSON.parse(text);
|
|
134
|
+
} catch {
|
|
135
|
+
throw new CliError(
|
|
136
|
+
CLI_ERROR_CODE.SERVER_ERROR,
|
|
137
|
+
`Server returned non-JSON response (status ${response.status}): ${truncate(text)}`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (response.ok) {
|
|
142
|
+
if (body && typeof body === "object" && "data" in body) {
|
|
143
|
+
return body.data;
|
|
144
|
+
}
|
|
145
|
+
return body;
|
|
146
|
+
}
|
|
147
|
+
const code = mapStatusToCode(response.status);
|
|
148
|
+
const message = extractErrorMessage(body, response.status);
|
|
149
|
+
throw new CliError(code, message);
|
|
150
|
+
}
|
|
151
|
+
function mapStatusToCode(status) {
|
|
152
|
+
if (status === STATUS_UNAUTHORIZED) return CLI_ERROR_CODE.AUTH_FAILED;
|
|
153
|
+
if (status === STATUS_FORBIDDEN) return CLI_ERROR_CODE.AUTH_FAILED;
|
|
154
|
+
if (status === STATUS_NOT_FOUND) return CLI_ERROR_CODE.PROJECT_NOT_FOUND;
|
|
155
|
+
if (status === STATUS_CONFLICT) return CLI_ERROR_CODE.ALREADY_LINKED;
|
|
156
|
+
if (status >= STATUS_SERVER_ERROR_MIN) return CLI_ERROR_CODE.SERVER_ERROR;
|
|
157
|
+
if (status >= STATUS_BAD_REQUEST_MIN) return CLI_ERROR_CODE.INVALID_INPUT;
|
|
158
|
+
return CLI_ERROR_CODE.SERVER_ERROR;
|
|
159
|
+
}
|
|
160
|
+
function extractErrorMessage(body, status) {
|
|
161
|
+
if (body && typeof body === "object") {
|
|
162
|
+
const envelope = body;
|
|
163
|
+
if (envelope.error && typeof envelope.error.error_message === "string") {
|
|
164
|
+
return envelope.error.error_code ? `${envelope.error.error_code}: ${envelope.error.error_message}` : envelope.error.error_message;
|
|
165
|
+
}
|
|
166
|
+
if (typeof envelope.message === "string") return envelope.message;
|
|
167
|
+
}
|
|
168
|
+
return `HTTP ${status}`;
|
|
169
|
+
}
|
|
170
|
+
function toNetworkError(err) {
|
|
171
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
172
|
+
return new CliError(CLI_ERROR_CODE.NETWORK_ERROR, "Request timed out.");
|
|
173
|
+
}
|
|
174
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
175
|
+
return new CliError(CLI_ERROR_CODE.NETWORK_ERROR, `Network error: ${message}`);
|
|
176
|
+
}
|
|
177
|
+
function truncate(value, max = 200) {
|
|
178
|
+
return value.length > max ? `${value.slice(0, max)}\u2026` : value;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/cli/args.ts
|
|
182
|
+
function parseArgs(argv) {
|
|
183
|
+
const positional = [];
|
|
184
|
+
const flags = /* @__PURE__ */ new Map();
|
|
185
|
+
for (let i = 0; i < argv.length; i++) {
|
|
186
|
+
const arg = argv[i];
|
|
187
|
+
if (!arg.startsWith("--")) {
|
|
188
|
+
positional.push(arg);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const eq = arg.indexOf("=");
|
|
192
|
+
if (eq !== -1) {
|
|
193
|
+
flags.set(arg.slice(2, eq), arg.slice(eq + 1));
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const name = arg.slice(2);
|
|
197
|
+
const next = argv[i + 1];
|
|
198
|
+
if (next !== void 0 && !next.startsWith("--")) {
|
|
199
|
+
flags.set(name, next);
|
|
200
|
+
i++;
|
|
201
|
+
} else {
|
|
202
|
+
flags.set(name, true);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return { positional, flags };
|
|
206
|
+
}
|
|
207
|
+
function flagString(args, name) {
|
|
208
|
+
const v = args.flags.get(name);
|
|
209
|
+
return typeof v === "string" ? v : void 0;
|
|
210
|
+
}
|
|
211
|
+
function flagBool(args, name) {
|
|
212
|
+
return args.flags.has(name);
|
|
213
|
+
}
|
|
214
|
+
var AUTH_FILE_NAME = "auth.json";
|
|
215
|
+
var ENV_XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
|
|
216
|
+
var APP_CONFIG_DIR_NAME = "cimplify";
|
|
217
|
+
var HOME_CONFIG_DIR_NAME = ".config";
|
|
218
|
+
var FILE_MODE_PRIVATE = 384;
|
|
219
|
+
var FILE_MODE_DIR = 448;
|
|
220
|
+
var ENCODING_UTF8 = "utf8";
|
|
221
|
+
function authConfigDir() {
|
|
222
|
+
const xdg = process.env[ENV_XDG_CONFIG_HOME];
|
|
223
|
+
return xdg ? path.join(xdg, APP_CONFIG_DIR_NAME) : path.join(os.homedir(), HOME_CONFIG_DIR_NAME, APP_CONFIG_DIR_NAME);
|
|
224
|
+
}
|
|
225
|
+
function authConfigPath() {
|
|
226
|
+
return path.join(authConfigDir(), AUTH_FILE_NAME);
|
|
227
|
+
}
|
|
228
|
+
async function writeJsonFile(filePath, value, mode) {
|
|
229
|
+
await promises.mkdir(path.dirname(filePath), { recursive: true, mode: FILE_MODE_DIR });
|
|
230
|
+
const payload = `${JSON.stringify(value, null, 2)}
|
|
231
|
+
`;
|
|
232
|
+
await promises.writeFile(filePath, payload, { encoding: ENCODING_UTF8, mode });
|
|
233
|
+
{
|
|
234
|
+
await promises.chmod(filePath, mode);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function writeAuth(creds) {
|
|
238
|
+
await writeJsonFile(authConfigPath(), creds, FILE_MODE_PRIVATE);
|
|
239
|
+
}
|
|
240
|
+
var CODE_VERIFIER_BYTES = 32;
|
|
241
|
+
var STATE_BYTES = 16;
|
|
242
|
+
var LOOPBACK_HOST = "127.0.0.1";
|
|
243
|
+
var CALLBACK_PATH = "/callback";
|
|
244
|
+
var CALLBACK_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
245
|
+
function generatePkcePair() {
|
|
246
|
+
const codeVerifier = base64UrlEncode(randomBytes(CODE_VERIFIER_BYTES));
|
|
247
|
+
const codeChallenge = base64UrlEncode(
|
|
248
|
+
createHash("sha256").update(codeVerifier).digest()
|
|
249
|
+
);
|
|
250
|
+
return { codeVerifier, codeChallenge, codeChallengeMethod: "S256" };
|
|
251
|
+
}
|
|
252
|
+
function generateState() {
|
|
253
|
+
return base64UrlEncode(randomBytes(STATE_BYTES));
|
|
254
|
+
}
|
|
255
|
+
function base64UrlEncode(buf) {
|
|
256
|
+
return buf.toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
257
|
+
}
|
|
258
|
+
var HTML_SUCCESS = (apiBaseUrl) => `<!doctype html>
|
|
259
|
+
<html lang="en"><head><meta charset="utf-8"><title>Cimplify CLI</title>
|
|
260
|
+
<style>
|
|
261
|
+
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
|
262
|
+
background:#0a0a0a;color:#e5e5e5;display:flex;align-items:center;
|
|
263
|
+
justify-content:center;height:100vh;margin:0}
|
|
264
|
+
.card{background:#141414;border:1px solid #262626;border-radius:12px;
|
|
265
|
+
padding:32px 40px;max-width:420px;text-align:center}
|
|
266
|
+
.tick{color:#22c55e;font-size:32px}
|
|
267
|
+
h1{margin:8px 0 4px;font-size:20px;font-weight:600}
|
|
268
|
+
p{margin:4px 0;color:#a3a3a3;font-size:14px}
|
|
269
|
+
.host{color:#737373;font-family:ui-monospace,monospace;font-size:12px;margin-top:16px}
|
|
270
|
+
</style></head><body><div class="card">
|
|
271
|
+
<div class="tick">\u2713</div>
|
|
272
|
+
<h1>You're logged in</h1>
|
|
273
|
+
<p>Return to your terminal \u2014 it'll continue from here.</p>
|
|
274
|
+
<p>You can close this tab.</p>
|
|
275
|
+
<div class="host">${apiBaseUrl}</div>
|
|
276
|
+
</div></body></html>`;
|
|
277
|
+
var HTML_ERROR = (msg) => `<!doctype html>
|
|
278
|
+
<html lang="en"><head><meta charset="utf-8"><title>Cimplify CLI</title>
|
|
279
|
+
<style>body{font-family:sans-serif;padding:40px;max-width:480px;margin:auto}
|
|
280
|
+
h1{color:#dc2626}</style></head><body>
|
|
281
|
+
<h1>Sign-in failed</h1><p>${escapeHtml(msg)}</p>
|
|
282
|
+
<p>Return to your terminal and try <code>cimplify login</code> again.</p>
|
|
283
|
+
</body></html>`;
|
|
284
|
+
function escapeHtml(s) {
|
|
285
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
286
|
+
}
|
|
287
|
+
async function startLoopbackServer(apiBaseUrl) {
|
|
288
|
+
let resolveCb = null;
|
|
289
|
+
let rejectCb = null;
|
|
290
|
+
const callbackPromise = new Promise((resolve, reject) => {
|
|
291
|
+
resolveCb = resolve;
|
|
292
|
+
rejectCb = reject;
|
|
293
|
+
});
|
|
294
|
+
const server = createServer((req, res) => {
|
|
295
|
+
if (!req.url) {
|
|
296
|
+
res.writeHead(400).end();
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const parsed = new URL(req.url, `http://${LOOPBACK_HOST}`);
|
|
300
|
+
if (parsed.pathname !== CALLBACK_PATH) {
|
|
301
|
+
res.writeHead(404).end("Not Found");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const code = parsed.searchParams.get("code");
|
|
305
|
+
const state = parsed.searchParams.get("state");
|
|
306
|
+
const error = parsed.searchParams.get("error");
|
|
307
|
+
if (error) {
|
|
308
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" }).end(HTML_ERROR(error));
|
|
309
|
+
rejectCb?.(new CliError(CLI_ERROR_CODE.UNAUTHORIZED, `OAuth error: ${error}`));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (!code || !state) {
|
|
313
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" }).end(HTML_ERROR("Missing code or state in callback"));
|
|
314
|
+
rejectCb?.(new CliError(CLI_ERROR_CODE.UNAUTHORIZED, "Malformed callback"));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(HTML_SUCCESS(apiBaseUrl));
|
|
318
|
+
resolveCb?.({ code, state });
|
|
319
|
+
});
|
|
320
|
+
await new Promise((resolve, reject) => {
|
|
321
|
+
server.once("error", reject);
|
|
322
|
+
server.listen(0, LOOPBACK_HOST, resolve);
|
|
323
|
+
});
|
|
324
|
+
const address = server.address();
|
|
325
|
+
if (!address || typeof address === "string") {
|
|
326
|
+
server.close();
|
|
327
|
+
throw new CliError(
|
|
328
|
+
CLI_ERROR_CODE.UNAUTHORIZED,
|
|
329
|
+
"Could not bind loopback server"
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
const redirectUri = `http://${LOOPBACK_HOST}:${address.port}${CALLBACK_PATH}`;
|
|
333
|
+
return {
|
|
334
|
+
redirectUri,
|
|
335
|
+
async awaitCallback(timeoutMs = CALLBACK_TIMEOUT_MS) {
|
|
336
|
+
let timer = null;
|
|
337
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
338
|
+
timer = setTimeout(() => {
|
|
339
|
+
reject(
|
|
340
|
+
new CliError(
|
|
341
|
+
CLI_ERROR_CODE.TIMEOUT,
|
|
342
|
+
`Login timed out after ${Math.round(timeoutMs / 1e3)}s`
|
|
343
|
+
)
|
|
344
|
+
);
|
|
345
|
+
}, timeoutMs);
|
|
346
|
+
});
|
|
347
|
+
try {
|
|
348
|
+
return await Promise.race([callbackPromise, timeoutPromise]);
|
|
349
|
+
} finally {
|
|
350
|
+
if (timer) clearTimeout(timer);
|
|
351
|
+
server.close();
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
close() {
|
|
355
|
+
server.close();
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function openBrowser(url) {
|
|
360
|
+
const platformCommand = (() => {
|
|
361
|
+
switch (process.platform) {
|
|
362
|
+
case "darwin":
|
|
363
|
+
return { cmd: "open", args: [url] };
|
|
364
|
+
case "win32":
|
|
365
|
+
return { cmd: "cmd", args: ["/c", "start", '""', url] };
|
|
366
|
+
default:
|
|
367
|
+
return { cmd: "xdg-open", args: [url] };
|
|
368
|
+
}
|
|
369
|
+
})();
|
|
370
|
+
try {
|
|
371
|
+
const child = spawn(platformCommand.cmd, platformCommand.args, {
|
|
372
|
+
detached: true,
|
|
373
|
+
stdio: "ignore"
|
|
374
|
+
});
|
|
375
|
+
child.on("error", () => {
|
|
376
|
+
});
|
|
377
|
+
child.unref();
|
|
378
|
+
} catch {
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// src/cli/output.ts
|
|
383
|
+
var RESET = "\x1B[0m";
|
|
384
|
+
var DIM_OPEN = "\x1B[2m";
|
|
385
|
+
var GREEN_OPEN = "\x1B[32m";
|
|
386
|
+
var CYAN_OPEN = "\x1B[36m";
|
|
387
|
+
var PREFIX_STEP = "\u25B8";
|
|
388
|
+
var PREFIX_SUCCESS = "\u2713";
|
|
389
|
+
function wrap(open, value) {
|
|
390
|
+
return `${open}${value}${RESET}`;
|
|
391
|
+
}
|
|
392
|
+
function dim(value) {
|
|
393
|
+
return wrap(DIM_OPEN, value);
|
|
394
|
+
}
|
|
395
|
+
function green(value) {
|
|
396
|
+
return wrap(GREEN_OPEN, value);
|
|
397
|
+
}
|
|
398
|
+
function cyan(value) {
|
|
399
|
+
return wrap(CYAN_OPEN, value);
|
|
400
|
+
}
|
|
401
|
+
function step(message) {
|
|
402
|
+
console.log(`${cyan(PREFIX_STEP)} ${message}`);
|
|
403
|
+
}
|
|
404
|
+
function success(message) {
|
|
405
|
+
console.log(`${green(PREFIX_SUCCESS)} ${message}`);
|
|
406
|
+
}
|
|
407
|
+
function info(message) {
|
|
408
|
+
console.log(message);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/cli/commands/login.ts
|
|
412
|
+
var FLAG_API_KEY = "api-key";
|
|
413
|
+
var FLAG_BASE_URL = "base-url";
|
|
414
|
+
var FLAG_NO_BROWSER = "no-browser";
|
|
415
|
+
var ENDPOINT_AUTH_ME = "/v1/auth/me";
|
|
416
|
+
var ENDPOINT_CLI_START = "/v1/auth/cli/start";
|
|
417
|
+
var ENDPOINT_CLI_TOKEN = "/v1/auth/cli/token";
|
|
418
|
+
var KEY_PREFIX_DK = "dk_";
|
|
419
|
+
var KEY_PREFIX_SK = "sk_";
|
|
420
|
+
var PRODUCT_NAME = "cimplify-cli";
|
|
421
|
+
async function run(argv) {
|
|
422
|
+
const args = parseArgs(argv);
|
|
423
|
+
const baseUrl = resolveBaseUrl(flagString(args, FLAG_BASE_URL));
|
|
424
|
+
const explicitKey = flagString(args, FLAG_API_KEY);
|
|
425
|
+
if (explicitKey) {
|
|
426
|
+
await loginWithKey(baseUrl, explicitKey);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
await loginWithBrowser(baseUrl, flagBool(args, FLAG_NO_BROWSER));
|
|
430
|
+
}
|
|
431
|
+
async function loginWithKey(baseUrl, apiKey) {
|
|
432
|
+
if (!apiKey.startsWith(KEY_PREFIX_DK) && !apiKey.startsWith(KEY_PREFIX_SK)) {
|
|
433
|
+
throw new CliError(
|
|
434
|
+
CLI_ERROR_CODE.INVALID_INPUT,
|
|
435
|
+
`API key must start with "${KEY_PREFIX_DK}" or "${KEY_PREFIX_SK}".`
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
const client = ApiClient.withKey(apiKey, baseUrl);
|
|
439
|
+
const me = await client.get(ENDPOINT_AUTH_ME);
|
|
440
|
+
await writeAuth({
|
|
441
|
+
apiKey,
|
|
442
|
+
apiBaseUrl: baseUrl,
|
|
443
|
+
accountId: me.id,
|
|
444
|
+
businessId: me.business_id,
|
|
445
|
+
email: me.email,
|
|
446
|
+
name: me.name,
|
|
447
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
448
|
+
});
|
|
449
|
+
success(`Logged in as ${me.email ?? me.name ?? me.id} (business ${me.business_id})`);
|
|
450
|
+
}
|
|
451
|
+
async function loginWithBrowser(baseUrl, noBrowser) {
|
|
452
|
+
const pkce = generatePkcePair();
|
|
453
|
+
const state = generateState();
|
|
454
|
+
const loopback = await startLoopbackServer(baseUrl);
|
|
455
|
+
const startBody = {
|
|
456
|
+
code_challenge: pkce.codeChallenge,
|
|
457
|
+
code_challenge_method: pkce.codeChallengeMethod,
|
|
458
|
+
redirect_uri: loopback.redirectUri,
|
|
459
|
+
state,
|
|
460
|
+
client_meta: {
|
|
461
|
+
hostname: os.hostname(),
|
|
462
|
+
platform: process.platform,
|
|
463
|
+
arch: process.arch,
|
|
464
|
+
node_version: process.version,
|
|
465
|
+
product: PRODUCT_NAME
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
const unauthClient = ApiClient.unauthenticated(baseUrl);
|
|
469
|
+
const startResponse = await unauthClient.post(
|
|
470
|
+
ENDPOINT_CLI_START,
|
|
471
|
+
startBody
|
|
472
|
+
);
|
|
473
|
+
step("Opening browser to authorize this CLI...");
|
|
474
|
+
info(dim(startResponse.approval_url));
|
|
475
|
+
if (!noBrowser) {
|
|
476
|
+
openBrowser(startResponse.approval_url);
|
|
477
|
+
} else {
|
|
478
|
+
info("");
|
|
479
|
+
info(dim("Open the URL above in any browser to continue."));
|
|
480
|
+
}
|
|
481
|
+
let callback;
|
|
482
|
+
try {
|
|
483
|
+
callback = await loopback.awaitCallback(startResponse.expires_in_secs * 1e3);
|
|
484
|
+
} catch (err) {
|
|
485
|
+
loopback.close();
|
|
486
|
+
throw err;
|
|
487
|
+
}
|
|
488
|
+
if (callback.state !== state) {
|
|
489
|
+
throw new CliError(
|
|
490
|
+
CLI_ERROR_CODE.UNAUTHORIZED,
|
|
491
|
+
"OAuth state mismatch. Possible CSRF attempt \u2014 try again."
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
const token = await unauthClient.post(ENDPOINT_CLI_TOKEN, {
|
|
495
|
+
auth_code: callback.code,
|
|
496
|
+
code_verifier: pkce.codeVerifier,
|
|
497
|
+
redirect_uri: loopback.redirectUri
|
|
498
|
+
});
|
|
499
|
+
const meClient = ApiClient.withKey(token.access_token, baseUrl);
|
|
500
|
+
const me = await meClient.get(ENDPOINT_AUTH_ME);
|
|
501
|
+
await writeAuth({
|
|
502
|
+
apiKey: token.access_token,
|
|
503
|
+
apiBaseUrl: baseUrl,
|
|
504
|
+
accountId: me.id,
|
|
505
|
+
businessId: me.business_id,
|
|
506
|
+
email: me.email,
|
|
507
|
+
name: me.name,
|
|
508
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
509
|
+
});
|
|
510
|
+
success(
|
|
511
|
+
`Logged in as ${me.email ?? me.name ?? me.id} (business ${me.business_id})`
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export { run as default };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { promises } from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
// src/cli/config.ts
|
|
6
|
+
var AUTH_FILE_NAME = "auth.json";
|
|
7
|
+
var ENV_XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
|
|
8
|
+
var APP_CONFIG_DIR_NAME = "cimplify";
|
|
9
|
+
var HOME_CONFIG_DIR_NAME = ".config";
|
|
10
|
+
function authConfigDir() {
|
|
11
|
+
const xdg = process.env[ENV_XDG_CONFIG_HOME];
|
|
12
|
+
return xdg ? path.join(xdg, APP_CONFIG_DIR_NAME) : path.join(os.homedir(), HOME_CONFIG_DIR_NAME, APP_CONFIG_DIR_NAME);
|
|
13
|
+
}
|
|
14
|
+
function authConfigPath() {
|
|
15
|
+
return path.join(authConfigDir(), AUTH_FILE_NAME);
|
|
16
|
+
}
|
|
17
|
+
async function clearAuth() {
|
|
18
|
+
try {
|
|
19
|
+
await promises.unlink(authConfigPath());
|
|
20
|
+
return true;
|
|
21
|
+
} catch (err) {
|
|
22
|
+
if (err.code === "ENOENT") return false;
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/cli/output.ts
|
|
28
|
+
var RESET = "\x1B[0m";
|
|
29
|
+
var GREEN_OPEN = "\x1B[32m";
|
|
30
|
+
var PREFIX_SUCCESS = "\u2713";
|
|
31
|
+
function wrap(open, value) {
|
|
32
|
+
return `${open}${value}${RESET}`;
|
|
33
|
+
}
|
|
34
|
+
function green(value) {
|
|
35
|
+
return wrap(GREEN_OPEN, value);
|
|
36
|
+
}
|
|
37
|
+
function success(message) {
|
|
38
|
+
console.log(`${green(PREFIX_SUCCESS)} ${message}`);
|
|
39
|
+
}
|
|
40
|
+
function info(message) {
|
|
41
|
+
console.log(message);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/cli/commands/logout.ts
|
|
45
|
+
async function run(_argv) {
|
|
46
|
+
const removed = await clearAuth();
|
|
47
|
+
if (removed) {
|
|
48
|
+
success("Logged out.");
|
|
49
|
+
} else {
|
|
50
|
+
info("Not logged in.");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { run as default };
|