create-sailor 1.7.3 → 1.8.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 +9 -0
- package/dist/aws-kms-EYG3LGYA.js +1 -0
- package/dist/chunk-224RJRPV.js +5 -0
- package/dist/chunk-2YUF6TO6.js +2 -0
- package/dist/chunk-3T4EOSMQ.js +1 -0
- package/dist/chunk-4AGDXYPL.js +1 -0
- package/dist/chunk-76FD5Z7D.js +23 -0
- package/dist/chunk-7OZT53MS.js +1 -0
- package/dist/chunk-AAIUIEEA.js +1 -0
- package/dist/chunk-C6CKWQY5.js +20 -0
- package/dist/chunk-CHWVD37K.js +1 -0
- package/dist/chunk-EBTFPGDR.js +9 -0
- package/dist/chunk-EIQ2HSZP.js +1 -0
- package/dist/chunk-FSRPMVAS.js +1 -0
- package/dist/chunk-IY2KKEUD.js +1 -0
- package/dist/chunk-M3QJPQWS.js +1 -0
- package/dist/chunk-OWZF7P72.js +1 -0
- package/dist/chunk-QSLKOXQ6.js +1 -0
- package/dist/chunk-S4HCBMFP.js +1 -0
- package/dist/chunk-TX7AAHER.js +1 -0
- package/dist/chunk-UU4YUKYZ.js +40 -0
- package/dist/chunk-WI7DO2VU.js +1 -0
- package/dist/chunk-XB6R2VIU.js +1 -0
- package/dist/chunk-XPRSUMI6.js +1 -0
- package/dist/chunk-ZCWB3HFB.js +1 -0
- package/dist/credits-7FJY7HK2.js +2613 -0
- package/dist/dist-LOLT3W3R.js +1 -0
- package/dist/dist-V6L2DNL2.js +5 -0
- package/dist/dist-es-3AHFNYTD.js +5 -0
- package/dist/dist-es-6KZILXYZ.js +1 -0
- package/dist/dist-es-H627TNCU.js +1 -0
- package/dist/dist-es-I6TTSEP7.js +2 -0
- package/dist/dist-es-JATELSE4.js +1 -0
- package/dist/dist-es-NRANDF25.js +2 -0
- package/dist/dist-es-YSAFQMS4.js +1 -0
- package/dist/event-streams-IPTOYMEP.js +1 -0
- package/dist/index.js +358 -419
- package/dist/loadSso-FR7LG5FX.js +1 -0
- package/dist/local-BUKM6VCO.js +1 -0
- package/dist/query_compiler_fast_bg.postgresql-FOSFFKA5.js +2 -0
- package/dist/query_compiler_fast_bg.postgresql.wasm-base64-JU7X67WB.js +1 -0
- package/dist/signin-N4XQB5ZB.js +1 -0
- package/dist/sso-oidc-FXPYRI5I.js +1 -0
- package/dist/sts-SIEJPPW3.js +6 -0
- package/package.json +21 -20
- package/templates/backends/gateway/package.json +4 -3
- package/templates/backends/gateway/src/index.ts +0 -4
- package/templates/backends/gateway/src/node.ts +5 -0
- package/templates/backends/gateway/src/worker.ts +11 -0
- package/templates/deploy/Dockerfile.web +1 -1
- package/templates/deploy/wrangler.gateway.toml +11 -0
package/dist/index.js
CHANGED
|
@@ -1,82 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{execSync as kt}from"child_process";import _e from"fs";import de from"path";import*as O from"@clack/prompts";import{Command as pc}from"commander";import A from"picocolors";import lc from"update-notifier";import Br from"cfonts";import Kr from"picocolors";import _n from"fs";import tn from"path";import{fileURLToPath as jr}from"url";function Vr(){try{let t=tn.dirname(jr(import.meta.url));for(let o=0;o<4;o++){let n=tn.join(t,"package.json");if(_n.existsSync(n)){let r=_n.readFileSync(n,"utf-8"),s=JSON.parse(r);if(s.name==="create-sailor"&&typeof s.version=="string")return s.version}t=tn.dirname(t)}return"0.0.0-dev"}catch{return"0.0.0-dev"}}var ae=Vr();function bn(){if(process.env.NO_COLOR||!process.stdout.isTTY){process.stdout.write([`Sailor v${ae}`,"AI-Native SaaS Unicorn Template","by Nebutra \xB7 https://nebutra.com","",""].join(`
|
|
3
|
-
`)
|
|
4
|
-
|
|
5
|
-
`))}import P from"picocolors";var $r={qstash:"foundation",bullmq:"foundation",upstash:"foundation",sqs:"foundation",meilisearch:"foundation",typesense:"foundation",algolia:"foundation",pgvector:"foundation",novu:"foundation",knock:"foundation",svix:"foundation","vercel-flags":"wip",growthbook:"wip",configcat:"wip",turnstile:"wip",hcaptcha:"wip","aliyun-slide":"wip",invite:"foundation"};function Yr(e){return e?$r[e]??"stable":"stable"}function rt(e){switch(e){case"stable":return"";case"foundation":return"[Foundation]";case"wip":return"[WIP]";case"deprecated":return"[Deprecated]"}}function me(e){switch(e){case"stable":return"Production-ready.";case"foundation":return"Foundation status \u2014 types + factory are complete, but provider adapters are stub-level and require external credentials.";case"wip":return"WIP \u2014 actively being built, should not be used in production.";case"deprecated":return"Deprecated \u2014 scheduled for removal, do not use."}}function Pn(e){let t=[];for(let{flag:o,provider:n}of e){if(!n||n==="none")continue;let r=Yr(n);r!=="stable"&&t.push({flag:o,provider:n,status:r})}return t}function qr(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function An(e){let t=qr(),o=t?"\u2693":"-",n=t?"\u25B8":"->",r=t?"\u2605":"*",s=t?"\u{1F4E7}":"*",c=["",` ${t?P.bold(`${o} Done in ${e.elapsedSec}s \xB7 ${e.targetDir}/`):`${o} Done in ${e.elapsedSec}s \xB7 ${e.targetDir}/`}`,"",` ${t?P.bold("Next:"):"Next:"}`,` ${n} cd ${e.targetDir}`];e.skippedInstall&&c.push(` ${n} pnpm install (if --no-install was set)`),c.push(` ${n} fill in .env.local ${t?P.dim("\u2192 add provider keys before booting apps"):"-> add provider keys before booting apps"}`,` ${n} pnpm db:migrate ${t?P.dim("\u2192 sync Prisma schema"):"-> sync Prisma schema"}`,` ${n} pnpm db:seed ${t?P.dim("\u2192 load example tenants and records"):"-> load example tenants and records"}`,` ${n} pnpm dev ${t?P.dim("\u2192 http://localhost:3000"):"-> http://localhost:3000"}`,"",` ${t?P.bold("Customize:"):"Customize:"}`,` ${n} pnpm brand:init ${t?P.dim("\u2192 create brand.config.ts"):"-> create brand.config.ts"}`,` ${n} pnpm brand:apply ${t?P.dim("\u2192 propagate brand changes"):"-> propagate brand changes"}`,` ${n} pnpm preset:env ${t?P.dim("\u2192 review preset env requirements"):"-> review preset env requirements"}`,"",` ${t?P.bold("Useful scripts:"):"Useful scripts:"}`,` ${n} pnpm infra:up ${t?P.dim("\u2192 start local PostgreSQL/Redis/ClickHouse"):"-> start local PostgreSQL/Redis/ClickHouse"}`,` ${n} pnpm generate:api-types ${t?P.dim("\u2192 refresh shared API client types"):"-> refresh shared API client types"}`,` ${n} pnpm lint ${t?P.dim("\u2192 quick repo sanity check"):"-> quick repo sanity check"}`,` ${n} pnpm audit ${t?P.dim("\u2192 scan deps for known CVEs"):"-> scan deps for known CVEs"}`);let a=e.previewSelections??[];if(a.length>0){let I=t?P.bold(P.yellow("\u26A0 Preview features selected:")):"!! Preview features selected:";c.push("",` ${I}`);for(let C of a){let U=rt(C.status),ue=` ${n} ${C.flag} (${C.provider}) ${U}`;c.push(t?P.yellow(ue):ue)}c.push("",` ${t?P.dim(me("foundation")):me("foundation")}`,` ${t?P.dim("You may need to contribute the provider integration yourself."):"You may need to contribute the provider integration yourself."}`,` ${t?P.dim("See: https://github.com/Nebutra/Nebutra-Sailor/blob/main/docs/package-status.md"):"See: https://github.com/Nebutra/Nebutra-Sailor/blob/main/docs/package-status.md"}`)}let l=e.waveFeatures??{},u=I=>I!==!1,S=[];u(l.apiKeys)&&S.push("Manage API keys at /settings/api-keys"),u(l.auditLog)&&S.push("View audit log at /settings/audit-log"),S.push("Configure webhooks at /settings/webhooks"),u(l.commandPalette)&&S.push("Press Cmd/Ctrl+K to open the command palette"),S.push("Edit notification preferences at /settings/notifications"),S.push("Export your data at /settings/account/export"),l.chinaCompliance&&S.push("China deployments \u2014 see packages/ops/china-compliance/README.md"),c.push("",` ${t?P.bold("What you can do next:"):"What you can do next:"}`);for(let I of S)c.push(` ${n} ${I}`);c.push("",` ${t?P.bold("Ship faster:"):"Ship faster:"}`,` ${r} Star us https://github.com/nebutra/nebutra-sailor`,` ${s} Free license https://nebutra.com/get-license`,""),process.stdout.write(c.join(`
|
|
2
|
+
import"./chunk-FSRPMVAS.js";import{execSync as jc}from"child_process";import Vs from"fs";import Ks from"path";import*as nt from"@clack/prompts";import F from"picocolors";import{Command as Hs}from"commander";import In from"fs";import Vt from"path";import{fileURLToPath as qs}from"url";function Gs(){try{let t=Vt.dirname(qs(import.meta.url));for(let n=0;n<4;n++){let o=Vt.join(t,"package.json");if(In.existsSync(o)){let s=In.readFileSync(o,"utf-8"),r=JSON.parse(s);if(r.name==="create-sailor"&&typeof r.version=="string")return r.version}t=Vt.dirname(t)}return"0.0.0-dev"}catch{return"0.0.0-dev"}}var X=Gs();var Ws="create-sailor";function Cn(){let e=new Hs;return e.name(Ws).description("Nebutra-Sailor \u2014 AI-Native SaaS template").version(X,"-v, --version").helpOption(!1).argument("[name]","project directory",void 0).option("-p, --pm <id>","npm | pnpm | yarn | bun").option("--region <id>","global | cn | hybrid").option("--orm <id>","prisma (default, primary) | drizzle (dual-ORM: adds db-drizzle alongside Prisma \u2014 Postgres only)").option("--db <id>","postgres | mysql | sqlite | none \u2014 engine (Prisma provider)").option("--db-host <id>","local | supabase | neon | vercel-postgres | planetscale | railway | aliyun-rds | tencent-cdb | none \u2014 managed-provider (region default: supabase global, local cn)").option("--auth <id>","clerk | betterauth | nextauth | supabase | none").option("--social-login <ids>","CN social login providers \u2014 wechat | qq | dingtalk | workweixin | feishu | weibo (comma-separated)").option("--payment <id>","stripe | lemon | wechat | alipay | none").option("--ai <mode-or-ids>","topology shorthand (gateway | direct | custom | none) OR comma-separated provider ids (e.g. openai,anthropic)").option("--deploy <target>","vercel | railway | cloudflare | selfhost").option("--docs <id>","fumadocs | mintlify | docusaurus | nextra | vitepress | none").option("--email <id>","resend | postmark | ses | aliyun-dm | tencent-ses | netease | none").option("--storage <id>","r2 | s3 | supabase-storage | aliyun-oss | tencent-cos | qiniu | none").option("--monitoring <id>","sentry | datadog | aliyun-arms | tingyun | none").option("--analytics <id>","posthog | plausible | umami | baidu | sensors | none").option("--sms <id>","twilio | aliyun-sms | tencent-sms | yunpian | none").option("--queue <id>","qstash | bullmq | sqs | none").option("--search <id>","meilisearch | typesense | algolia | pgvector | none").option("--cache <id>","upstash-redis | vercel-kv | redis | dragonfly | none").option("--notifications <id>","novu | knock | custom | none").option("--webhooks <id>","svix | custom | none").option("--cms <id>","sanity | contentful | strapi | none").option("--feature-flags <id>","vercel-flags | growthbook | configcat | none").option("--captcha <id>","turnstile | hcaptcha | aliyun-slide | none").option("--mcp <mode>","on | off (default: on)").option("--metering <mode>","auto | on | off (default: auto \u2014 auto-on when payment is set)").option("--billing-mode <mode>","usage | seat | credits (default: usage)").option("--idp <id>","clerk | oauth-server (default: clerk)").option("--access-gate <mode>","none | invite (default: none)").option("--cron-jobs <bool>","true | false \u2014 scaffold scheduled cron handlers (default: true)").option("--audit-log <bool>","true | false \u2014 enable /settings/audit-log + arch test (default: false \u2014 @nebutra/audit is WIP)").option("--api-keys <bool>","true | false \u2014 enable /settings/api-keys page (default: true)").option("--command-palette <bool>","true | false \u2014 enable \u2318K command palette (default: true)").option("--cookie-consent <bool>","true | false \u2014 enable GDPR/CCPA cookie banner (default: true)").option("--legal-pages <bool>","true | false \u2014 enable dynamic /legal/[slug] route (default: true)").option("--china-compliance <bool>","true | false \u2014 enable @nebutra/china-compliance + ICP footer (default: true when --region=cn, otherwise false)").option("--i18n","enable i18n").option("--no-i18n","disable i18n").option("--with-workflows","scaffold workflows/ (inngest + n8n + pusher stubs)").option("--with-python-backend","scaffold backends/python/ (FastAPI stub \u2014 only when TS-by-Default ADR exception applies)").option("--no-install","skip package install").option("--no-git","skip git init").option("-y, --yes","accept all defaults (non-interactive)").option("--dry-run","preview actions without writing files").option("--json","machine-readable output").option("--no-color","disable color output").option("-h, --help","show help"),e}import kn from"picocolors";var Kt=[{id:"local",name:"Local / Self-hosted Docker",region:"both",supportedEngines:["postgresql","mysql","sqlite"],envVars:[{name:"DATABASE_URL",placeholder:"postgresql://postgres:postgres@localhost:5432/$PROJECT",comment:"Local Postgres via docker-compose (pnpm infra:up)"}],docs:"https://www.postgresql.org/docs/",description:"Docker on localhost. You operate it.",tier:"self-hosted"},{id:"supabase",name:"Supabase",region:"global",supportedEngines:["postgresql"],forcedEngine:"postgresql",keepDirectUrl:!0,envVars:[{name:"DATABASE_URL",placeholder:"postgresql://postgres.<project-ref>:<password>@aws-0-<region>.pooler.supabase.com:5432/postgres?sslmode=require",comment:"Supabase session pooler \u2014 use for runtime queries"},{name:"DIRECT_URL",placeholder:"postgresql://postgres:<password>@db.<project-ref>.supabase.co:5432/postgres?sslmode=require",comment:"Supabase direct \u2014 use for Prisma migrations"},{name:"NEXT_PUBLIC_SUPABASE_URL",placeholder:"https://<project>.supabase.co"},{name:"NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY",placeholder:""},{name:"NEXT_PUBLIC_SUPABASE_ANON_KEY",placeholder:""},{name:"SUPABASE_PUBLISHABLE_KEY",placeholder:""},{name:"SUPABASE_SERVICE_ROLE_KEY",placeholder:""}],docs:"https://supabase.com/docs",description:"Managed Postgres + auth/storage/realtime/edge-functions.",tier:"managed"},{id:"neon",name:"Neon",region:"global",supportedEngines:["postgresql"],forcedEngine:"postgresql",keepDirectUrl:!0,envVars:[{name:"DATABASE_URL",placeholder:"postgresql://<user>:<pwd>@ep-<branch>-pooler.<region>.neon.tech/<db>?sslmode=require",comment:"Neon pooled \u2014 runtime queries"},{name:"DIRECT_URL",placeholder:"postgresql://<user>:<pwd>@ep-<branch>.<region>.neon.tech/<db>?sslmode=require",comment:"Neon direct (no -pooler) \u2014 migrations"}],docs:"https://neon.tech/docs",description:"Serverless Postgres with branching and scale-to-zero.",tier:"managed"},{id:"vercel-postgres",name:"Vercel Postgres",region:"global",supportedEngines:["postgresql"],forcedEngine:"postgresql",keepDirectUrl:!0,envVars:[{name:"DATABASE_URL",placeholder:"$POSTGRES_PRISMA_URL",comment:"Vercel Marketplace injects POSTGRES_PRISMA_URL automatically"},{name:"DIRECT_URL",placeholder:"$POSTGRES_URL_NON_POOLING",comment:"Vercel Marketplace injects POSTGRES_URL_NON_POOLING automatically"}],docs:"https://vercel.com/docs/storage/vercel-postgres",description:"Neon-backed, auto-provisioned via Vercel Marketplace.",tier:"managed"},{id:"planetscale",name:"PlanetScale",region:"global",supportedEngines:["mysql"],forcedEngine:"mysql",prismaDatasourceExtras:['relationMode = "prisma"'],envVars:[{name:"DATABASE_URL",placeholder:"mysql://<user>:<pass>@<host>.psdb.cloud/<db>?sslaccept=strict",comment:'PlanetScale: relationMode = "prisma" (no FK support server-side)'}],docs:"https://planetscale.com/docs",description:"Managed MySQL with branching. No foreign keys.",tier:"managed"},{id:"railway",name:"Railway",region:"global",supportedEngines:["postgresql","mysql"],envVars:[{name:"DATABASE_URL",placeholder:"$DATABASE_URL",comment:"Railway service env var; injected when you link a Postgres service"}],docs:"https://docs.railway.app/databases/postgresql",description:"Generalist managed infra; standard TCP Postgres/MySQL.",tier:"managed"},{id:"aliyun-rds",name:"Aliyun RDS",region:"cn",supportedEngines:["postgresql","mysql"],envVars:[{name:"DATABASE_URL",placeholder:"postgresql://<user>:<pwd>@<rds-instance>.rds.aliyuncs.com:5432/<db>",comment:"Aliyun RDS Postgres \u2014 internal VPC endpoint recommended"}],docs:"https://www.alibabacloud.com/help/en/rds",description:"Aliyun RDS \u2014 China compliance + intranet routing.",tier:"managed"},{id:"tencent-cdb",name:"Tencent CloudDB (CDB)",region:"cn",supportedEngines:["mysql","postgresql"],envVars:[{name:"DATABASE_URL",placeholder:"mysql://<user>:<pwd>@<cdb-instance>.tencentcdb.com:3306/<db>",comment:"Tencent CloudDB \u2014 VPC endpoint recommended"}],docs:"https://intl.cloud.tencent.com/document/product/236",description:"Tencent CloudDB \u2014 China compliance.",tier:"managed"},{id:"none",name:"None",region:"both",supportedEngines:["postgresql","mysql","sqlite"],envVars:[],docs:"",description:"Skip database scaffolding.",tier:"self-hosted"}];function st(e){return Kt.find(t=>t.id===e)}function xn(e){return e==="cn"||e==="hybrid"?"local":"supabase"}var rt=[{id:"r2",name:"Cloudflare R2",region:"global",envVars:["R2_ACCOUNT_ID","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY","R2_BUCKET"],docs:"https://developers.cloudflare.com/r2/api/s3/api/",pkg:"@aws-sdk/client-s3",pkgVersion:"^3.658.0"},{id:"s3",name:"AWS S3",region:"global",envVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_S3_REGION","S3_BUCKET"],docs:"https://docs.aws.amazon.com/s3/",pkg:"@aws-sdk/client-s3",pkgVersion:"^3.658.0"},{id:"supabase-storage",name:"Supabase Storage",region:"global",envVars:["SUPABASE_URL","SUPABASE_SERVICE_ROLE_KEY","SUPABASE_BUCKET"],docs:"https://supabase.com/docs/guides/storage",pkg:"@supabase/supabase-js",pkgVersion:"^2.45.4"},{id:"aliyun-oss",name:"\u963F\u91CC\u4E91 OSS",region:"cn",envVars:["ALIYUN_OSS_ACCESS_KEY","ALIYUN_OSS_ACCESS_SECRET","ALIYUN_OSS_REGION","ALIYUN_OSS_BUCKET"],docs:"https://help.aliyun.com/product/31815.html",pkg:"ali-oss",pkgVersion:"^6.21.0"},{id:"tencent-cos",name:"\u817E\u8BAF\u4E91 COS",region:"cn",envVars:["TENCENT_COS_SECRET_ID","TENCENT_COS_SECRET_KEY","TENCENT_COS_BUCKET","TENCENT_COS_REGION"],docs:"https://cloud.tencent.com/document/product/436",pkg:"cos-nodejs-sdk-v5",pkgVersion:"^2.13.7"},{id:"qiniu",name:"\u4E03\u725B\u4E91 Kodo",region:"cn",envVars:["QINIU_ACCESS_KEY","QINIU_SECRET_KEY","QINIU_BUCKET","QINIU_DOMAIN"],docs:"https://developer.qiniu.com/kodo",pkg:"qiniu",pkgVersion:"^7.14.0"},{id:"none",name:"None",region:"global",envVars:[],docs:""}];function $t(e){let t=e==="supabase"?"supabase-storage":e;return rt.some(n=>n.id===t)?t:void 0}function Rn(e){let t=$t(e);return t?rt.find(n=>n.id===t):void 0}var zc=rt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Nn(){let e=process.env.npm_config_user_agent??"";return e.startsWith("pnpm")?"pnpm":e.startsWith("yarn")?"yarn":e.startsWith("bun")?"bun":"npm"}function Yt(e){switch(e){case"postgres":case"postgresql":return"postgresql";case"mysql":return"mysql";case"sqlite":return"sqlite";case"none":return"none";default:return"postgresql"}}function qt(e,t,n){let o=e?.trim().toLowerCase()??xn(t),s=st(o);if(!s){let i=Kt.map(a=>a.id).join(", ");throw new Error(`Unknown --db-host="${o}". Valid: ${i}`)}let r=n;return s.forcedEngine&&r!=="none"&&r!==s.forcedEngine&&(r=s.forcedEngine),{hostId:s.id,engine:r}}function Gt(e){return e==="drizzle"?"drizzle":"prisma"}function Ht(e){return e==="lemon"||e==="lemonsqueezy"?"lemon":e==="wechat"?"wechat":e==="alipay"?"alipay":e==="none"?"none":"stripe"}function Wt(e){if(!e)return"stripe";let t=e.toLowerCase();return t==="lemon"||t==="lemonsqueezy"?"lemon":t==="wechat"?"wechat":t==="alipay"?"alipay":t==="none"?"none":"stripe"}function Xt(e){if(!e)return"clerk";let t=e.toLowerCase();return t==="betterauth"||t==="better-auth"?"betterauth":t==="nextauth"||t==="next-auth"||t==="authjs"||t==="auth.js"?"nextauth":t==="supabase"||t==="supabase-auth"?"supabase":t==="none"?"none":"clerk"}var Xs=new Set(["gateway","direct","custom","none"]);function zt(e){if(!e)return!1;let t=e.trim().toLowerCase();return Xs.has(t)}function Jt(e){if(!e)return["openai"];if(zt(e))return[];let t=e.split(",").map(n=>n.trim().toLowerCase());return t.includes("none")?[]:t}function On(e,t){let n=e??t;return $t(n)??n}var Tn={mintlify:"Mintlify",docusaurus:"Docusaurus",nextra:"Nextra",vitepress:"VitePress"};function Qt(e,t){if(!e)return"fumadocs";let n=e.toLowerCase();if(n==="fumadocs"||n==="none")return n;if(n in Tn){let o=Tn[n];return t?process.stdout.write(JSON.stringify({event:"notice",kind:"docs-fallback",requested:n,effective:"fumadocs"})+`
|
|
3
|
+
`):process.stdout.write(kn.yellow(`\u26A0 ${o} support is coming in v1.2. Falling back to fumadocs.
|
|
4
|
+
`)+kn.dim(` Track progress: https://github.com/Nebutra/Nebutra-Sailor/issues
|
|
5
|
+
`)),"fumadocs"}return"fumadocs"}function Zt(e){switch(e){case"vercel":return"vercel";case"railway":return"railway";case"cloudflare":return"cloudflare";case"selfhost":return"selfhost";case"none":return"none";default:return"vercel"}}function en(e){if(!e)return"global";let t=e.toLowerCase();return t==="cn"?"cn":t==="hybrid"?"hybrid":"global"}function Ln(e){return{...e==="cn"?{email:"aliyun-dm",storage:"aliyun-oss",monitoring:"sentry",analytics:"baidu",sms:"aliyun-sms"}:e==="hybrid"?{email:"resend",storage:"aliyun-oss",monitoring:"sentry",analytics:"posthog",sms:"aliyun-sms"}:{email:"resend",storage:"r2",monitoring:"sentry",analytics:"posthog",sms:"twilio"},queue:"none",search:"none",cache:e==="global"?"upstash-redis":e==="cn"?"redis":"upstash-redis",notifications:"none",webhooks:"none",cms:"none",featureFlags:"none",captcha:"none",mcp:"on",metering:"auto",billingMode:"usage",idp:"clerk",accessGate:"none"}}function tn(e){return e==="cn"?"wechat":"stripe"}import*as Y from"@clack/prompts";import ir from"picocolors";var nn=[{id:"anthropic",name:"Anthropic",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.anthropic.com",envVarPrefix:"ANTHROPIC",requiredEnvVars:["ANTHROPIC_API_KEY"]},{id:"openai",name:"OpenAI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.openai.com/docs",envVarPrefix:"OPENAI",requiredEnvVars:["OPENAI_API_KEY"]},{id:"deepseek",name:"DeepSeek",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.deepseek.com/api-docs",envVarPrefix:"DEEPSEEK",requiredEnvVars:["DEEPSEEK_API_KEY"]},{id:"xai",name:"xAI (Grok)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.x.ai/api",envVarPrefix:"XAI",requiredEnvVars:["XAI_API_KEY"]},{id:"moonshot",name:"Moonshot (Kimi)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.moonshot.cn/docs",envVarPrefix:"MOONSHOT",requiredEnvVars:["MOONSHOT_API_KEY","MOONSHOT_BASE_URL"]},{id:"google",name:"Google Gemini",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://ai.google.dev/gemini-api/docs",envVarPrefix:"GOOGLE_GENERATIVE_AI",requiredEnvVars:["GOOGLE_GENERATIVE_AI_API_KEY"]},{id:"mistral",name:"Mistral AI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.mistral.ai",envVarPrefix:"MISTRAL",requiredEnvVars:["MISTRAL_API_KEY"]},{id:"cohere",name:"Cohere",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.cohere.com",envVarPrefix:"COHERE",requiredEnvVars:["COHERE_API_KEY"]},{id:"perplexity",name:"Perplexity",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.perplexity.ai",baseURL:"https://api.perplexity.ai",envVarPrefix:"PERPLEXITY",requiredEnvVars:["PERPLEXITY_API_KEY"]},{id:"ai21",name:"AI21 Labs",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.ai21.com",baseURL:"https://api.ai21.com/studio/v1",envVarPrefix:"AI21",requiredEnvVars:["AI21_API_KEY"]},{id:"upstage",name:"Upstage (Solar)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://developers.upstage.ai/docs",baseURL:"https://api.upstage.ai/v1/solar",envVarPrefix:"UPSTAGE",requiredEnvVars:["UPSTAGE_API_KEY"]},{id:"siliconflow",name:"\u7845\u57FA\u6D41\u52A8 SiliconFlow",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://docs.siliconflow.cn",baseURL:"https://api.siliconflow.cn/v1",envVarPrefix:"SILICONFLOW",requiredEnvVars:["SILICONFLOW_API_KEY"]},{id:"volcengine-ark",name:"\u706B\u5C71\u5F15\u64CE ARK (\u8C46\u5305)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://www.volcengine.com/docs/82379",baseURL:"https://ark.cn-beijing.volces.com/api/v3",envVarPrefix:"ARK",requiredEnvVars:["ARK_API_KEY"]},{id:"bailian",name:"\u767E\u70BC Bailian (\u901A\u4E49)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://help.aliyun.com/zh/model-studio",baseURL:"https://dashscope.aliyuncs.com/compatible-mode/v1",envVarPrefix:"DASHSCOPE",requiredEnvVars:["DASHSCOPE_API_KEY"]},{id:"zhipu",name:"\u667A\u8C31 AI Zhipu",category:"\u56FD\u5185\u5E73\u53F0",status:"ai-sdk",docs:"https://docs.bigmodel.cn",baseURL:"https://open.bigmodel.cn/api/paas/v4",envVarPrefix:"ZHIPU",requiredEnvVars:["ZHIPU_API_KEY"]},{id:"baichuan",name:"\u767E\u5DDD\u667A\u80FD Baichuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.baichuan-ai.com/docs",baseURL:"https://api.baichuan-ai.com/v1",envVarPrefix:"BAICHUAN",requiredEnvVars:["BAICHUAN_API_KEY"]},{id:"minimax",name:"MiniMax (ABAB)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.minimaxi.com/document/guides",baseURL:"https://api.minimax.chat/v1",envVarPrefix:"MINIMAX",requiredEnvVars:["MINIMAX_API_KEY","MINIMAX_GROUP_ID"]},{id:"stepfun",name:"\u9636\u8DC3\u661F\u8FB0 StepFun",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.stepfun.com/docs",baseURL:"https://api.stepfun.com/v1",envVarPrefix:"STEPFUN",requiredEnvVars:["STEPFUN_API_KEY"]},{id:"sensetime",name:"\u5546\u6C64 SenseNova",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.sensetime.com",baseURL:"https://api.sensenova.cn/v1",envVarPrefix:"SENSENOVA",requiredEnvVars:["SENSENOVA_API_KEY"]},{id:"tencent",name:"\u817E\u8BAF\u6DF7\u5143 Hunyuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://cloud.tencent.com/document/product/1729",baseURL:"https://api.hunyuan.cloud.tencent.com/v1",envVarPrefix:"HUNYUAN",requiredEnvVars:["HUNYUAN_API_KEY"]},{id:"lingyi",name:"\u96F6\u4E00\u4E07\u7269 Yi",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.lingyiwanwu.com/docs",baseURL:"https://api.lingyiwanwu.com/v1",envVarPrefix:"YI",requiredEnvVars:["YI_API_KEY"]},{id:"openrouter",name:"OpenRouter",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://openrouter.ai/docs",envVarPrefix:"OPENROUTER",requiredEnvVars:["OPENROUTER_API_KEY"]},{id:"vercel-gateway",name:"Vercel AI Gateway",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://vercel.com/docs/ai-gateway",envVarPrefix:"AI_GATEWAY",requiredEnvVars:["AI_GATEWAY_API_KEY"]},{id:"litellm",name:"LiteLLM Proxy",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://docs.litellm.ai",baseURL:"http://localhost:4000/v1",envVarPrefix:"LITELLM",requiredEnvVars:["LITELLM_API_KEY"]},{id:"portkey",name:"Portkey",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://portkey.ai/docs",baseURL:"https://api.portkey.ai/v1",envVarPrefix:"PORTKEY",requiredEnvVars:["PORTKEY_API_KEY"]},{id:"aws-bedrock",name:"AWS Bedrock",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://aws.amazon.com/bedrock/",envVarPrefix:"AWS_BEDROCK",requiredEnvVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"]},{id:"azure-openai",name:"Azure OpenAI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/",envVarPrefix:"AZURE_OPENAI",requiredEnvVars:["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"]},{id:"gcp-vertex",name:"GCP Vertex AI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://cloud.google.com/vertex-ai",envVarPrefix:"GOOGLE_VERTEX",requiredEnvVars:["GOOGLE_APPLICATION_CREDENTIALS"]},{id:"groq",name:"Groq",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://console.groq.com/docs",envVarPrefix:"GROQ",requiredEnvVars:["GROQ_API_KEY"]},{id:"fireworks",name:"Fireworks AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.fireworks.ai",envVarPrefix:"FIREWORKS",requiredEnvVars:["FIREWORKS_API_KEY"]},{id:"together",name:"Together AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.together.ai",envVarPrefix:"TOGETHER",requiredEnvVars:["TOGETHER_API_KEY"]},{id:"huggingface",name:"Hugging Face",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://huggingface.co/docs/inference-providers",envVarPrefix:"HF",requiredEnvVars:["HF_API_KEY"]},{id:"replicate",name:"Replicate",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://replicate.com/docs",envVarPrefix:"REPLICATE",requiredEnvVars:["REPLICATE_API_TOKEN"]},{id:"lepton",name:"Lepton AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://www.lepton.ai/docs",baseURL:"https://api.lepton.ai/v1",envVarPrefix:"LEPTON",requiredEnvVars:["LEPTON_API_KEY"]},{id:"anyscale",name:"Anyscale Endpoints",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://docs.endpoints.anyscale.com/",baseURL:"https://api.endpoints.anyscale.com/v1",envVarPrefix:"ANYSCALE",requiredEnvVars:["ANYSCALE_API_KEY"]},{id:"octoai",name:"OctoAI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://octoai.cloud/docs",baseURL:"https://text.octoai.run/v1",envVarPrefix:"OCTOAI",requiredEnvVars:["OCTOAI_TOKEN"]},{id:"deepinfra",name:"DeepInfra",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://deepinfra.com/docs",baseURL:"https://api.deepinfra.com/v1/openai",envVarPrefix:"DEEPINFRA",requiredEnvVars:["DEEPINFRA_API_KEY"]},{id:"novita",name:"Novita AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://novita.ai/docs",baseURL:"https://api.novita.ai/v3/openai",envVarPrefix:"NOVITA",requiredEnvVars:["NOVITA_API_KEY"]},{id:"black-forest-labs",name:"Black Forest Labs (FLUX)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://docs.bfl.ml",envVarPrefix:"BFL",requiredEnvVars:["BFL_API_KEY"]},{id:"elevenlabs",name:"ElevenLabs (TTS)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://elevenlabs.io/docs",envVarPrefix:"ELEVENLABS",requiredEnvVars:["ELEVENLABS_API_KEY"]},{id:"runway",name:"Runway (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://runwayml.com",envVarPrefix:"RUNWAY",requiredEnvVars:["RUNWAY_API_KEY"]},{id:"luma",name:"Luma (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://lumalabs.ai",envVarPrefix:"LUMA",requiredEnvVars:["LUMA_API_KEY"]},{id:"midjourney",name:"Midjourney",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://docs.midjourney.com",envVarPrefix:"MIDJOURNEY",requiredEnvVars:[]},{id:"ollama",name:"Ollama (\u672C\u5730)",category:"\u672C\u5730\u90E8\u7F72",status:"opencode",docs:"https://github.com/ollama/ollama",baseURL:"http://localhost:11434/v1",envVarPrefix:"OLLAMA",requiredEnvVars:["OLLAMA_BASE_URL"]},{id:"lmstudio",name:"LM Studio",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://lmstudio.ai/docs",baseURL:"http://localhost:1234/v1",envVarPrefix:"LMSTUDIO",requiredEnvVars:[]},{id:"vllm",name:"vLLM",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://docs.vllm.ai",baseURL:"http://localhost:8000/v1",envVarPrefix:"VLLM",requiredEnvVars:[]},{id:"tgi",name:"HF TGI",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://huggingface.co/docs/text-generation-inference",baseURL:"http://localhost:8080/v1",envVarPrefix:"TGI",requiredEnvVars:[]}],np=nn.reduce((e,t)=>(e[t.category]||(e[t.category]=[]),e[t.category].push(t),e),{});var zs=["openai","anthropic","google"],Js=["openai"];function on(e){let t=e.providerIds??[];switch(e.mode){case"gateway":{let n=t.length>0?t:[...zs];return{mode:"gateway",providerIds:n,customEndpoint:e.customEndpoint,routing:{profile:"multi-provider-gateway",providerSeed:n,runtimeGovernance:!0}}}case"direct":{let n=t.length>0?t:[...Js];return{mode:"direct",providerIds:n,customEndpoint:e.customEndpoint,routing:{profile:"direct-adapters",providerSeed:n,runtimeGovernance:!1}}}case"custom":return{mode:"custom",providerIds:t,customEndpoint:e.customEndpoint,routing:{profile:"openai-compatible",providerSeed:t,runtimeGovernance:!0}};case"none":return{mode:"none",providerIds:[],routing:{profile:"disabled",providerSeed:[],runtimeGovernance:!1}}}}var He=[{id:"wechat",name:"\u5FAE\u4FE1\u5F00\u653E\u5E73\u53F0",region:"cn",envVars:["WECHAT_APP_ID","WECHAT_APP_SECRET"],docs:"https://open.weixin.qq.com/cgi-bin/showdocument",callbackUrl:"/api/auth/callback/wechat",tokenEndpoint:"https://api.weixin.qq.com/sns/oauth2/access_token"},{id:"qq",name:"QQ \u767B\u5F55",region:"cn",envVars:["QQ_APP_ID","QQ_APP_KEY"],docs:"https://wiki.connect.qq.com",callbackUrl:"/api/auth/callback/qq",tokenEndpoint:"https://graph.qq.com/oauth2.0/token"},{id:"dingtalk",name:"\u9489\u9489",region:"cn",envVars:["DINGTALK_APP_ID","DINGTALK_APP_SECRET"],docs:"https://open.dingtalk.com/document/",callbackUrl:"/api/auth/callback/dingtalk",tokenEndpoint:"https://api.dingtalk.com/v1.0/oauth2/userAccessToken"},{id:"workweixin",name:"\u4F01\u4E1A\u5FAE\u4FE1",region:"cn",envVars:["WORKWEIXIN_CORP_ID","WORKWEIXIN_AGENT_ID","WORKWEIXIN_SECRET"],docs:"https://developer.work.weixin.qq.com",callbackUrl:"/api/auth/callback/workweixin",tokenEndpoint:"https://qyapi.weixin.qq.com/cgi-bin/gettoken"},{id:"feishu",name:"\u98DE\u4E66 / Lark",region:"cn",envVars:["FEISHU_APP_ID","FEISHU_APP_SECRET"],docs:"https://open.feishu.cn/document/",callbackUrl:"/api/auth/callback/feishu",tokenEndpoint:"https://open.feishu.cn/open-apis/authen/v1/access_token"},{id:"weibo",name:"\u5FAE\u535A",region:"cn",envVars:["WEIBO_APP_KEY","WEIBO_APP_SECRET"],docs:"https://open.weibo.com/wiki/",callbackUrl:"/api/auth/callback/weibo",tokenEndpoint:"https://api.weibo.com/oauth2/access_token"}],Qs=new Set(He.map(e=>e.id));function Un(e){return He.find(t=>t.id===e)}function Mn(e){if(!e)return[];let t=e.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);if(t.includes("none"))return[];let n=[],o=new Set;for(let s of t)Qs.has(s)&&!o.has(s)&&(n.push(s),o.add(s));return n}import ee from"fs";import T from"path";import{fileURLToPath as Zs}from"url";var er=Zs(import.meta.url),Dn=T.dirname(er),tr={web:"vercel","landing-page":"vercel","design-docs":"vercel","sailor-docs":"vercel",gateway:"cloudflare-workers","python-ai":"ecs-docker"};function nr(){let e=[T.join(Dn,"..","templates","deploy"),T.join(Dn,"..","..","templates","deploy")];for(let t of e)if(ee.existsSync(t))return t;return e[0]}function jn(e){switch(e){case"cloudflare":return{web:"cloudflare-pages","landing-page":"cloudflare-pages","design-docs":"cloudflare-pages","sailor-docs":"cloudflare-pages",gateway:"cloudflare-workers","python-ai":"ecs-docker"};case"selfhost":return{web:"standalone","landing-page":"standalone","design-docs":"standalone","sailor-docs":"standalone",gateway:"ecs-docker","python-ai":"ecs-docker"};case"railway":return{web:"railway","landing-page":"railway","design-docs":"railway","sailor-docs":"railway",gateway:"railway","python-ai":"railway"};case"none":case"vercel":return{...tr}}}function or(e){return`DEPLOY_TARGET_${e.toUpperCase().replace(/-/g,"_")}`}async function Fn(e,t){let n=T.join(e,"backends","gateway");ee.existsSync(n)&&await ee.promises.copyFile(T.join(t,"wrangler.gateway.toml"),T.join(n,"wrangler.toml"))}async function Bn(e,t){let n=T.join(e,".env.example"),o;try{o=await ee.promises.readFile(n,"utf8")}catch(i){if(i.code==="ENOENT")return;throw i}if(o.includes("# Deployment Targets"))return;let r=["","# Deployment Targets",...Object.entries(t).map(([i,a])=>`${or(i)}="${a}"`)].join(`
|
|
6
|
+
`);await ee.promises.appendFile(n,`${r}
|
|
7
|
+
`)}async function Vn(e,t){if(t==="none")return;let n=nr();t==="vercel"?(await ee.promises.copyFile(T.join(n,"vercel.json"),T.join(e,"vercel.json")),await Fn(e,n)):t==="railway"?await ee.promises.copyFile(T.join(n,"railway.toml"),T.join(e,"railway.toml")):t==="cloudflare"?(await ee.promises.copyFile(T.join(n,"wrangler.toml"),T.join(e,"wrangler.toml")),await Fn(e,n)):t==="selfhost"&&(await ee.promises.copyFile(T.join(n,"docker-compose.yml"),T.join(e,"docker-compose.yml")),await ee.promises.copyFile(T.join(n,"Dockerfile.web"),T.join(e,"Dockerfile.web")))}var sr={qstash:"foundation",bullmq:"foundation",upstash:"foundation",sqs:"foundation",meilisearch:"foundation",typesense:"foundation",algolia:"foundation",pgvector:"foundation",novu:"foundation",knock:"foundation",svix:"foundation","vercel-flags":"wip",growthbook:"wip",configcat:"wip",turnstile:"wip",hcaptcha:"wip","aliyun-slide":"wip",invite:"foundation"};function rr(e){return e?sr[e]??"stable":"stable"}function We(e){switch(e){case"stable":return"";case"foundation":return"[Foundation]";case"wip":return"[WIP]";case"deprecated":return"[Deprecated]"}}function he(e){switch(e){case"stable":return"Production-ready.";case"foundation":return"Foundation status \u2014 types + factory are complete, but provider adapters are stub-level and require external credentials.";case"wip":return"WIP \u2014 actively being built, should not be used in production.";case"deprecated":return"Deprecated \u2014 scheduled for removal, do not use."}}function Kn(e){let t=[];for(let{flag:n,provider:o}of e){if(!o||o==="none")continue;let s=rr(o);s!=="stable"&&t.push({flag:n,provider:o,status:s})}return t}function Ee(e,t){if(e===void 0)return t;let n=String(e).trim().toLowerCase();return n==="true"||n==="1"||n==="yes"||n==="on"?!0:n==="false"||n==="0"||n==="no"||n==="off"?!1:t}function $n(e,t){let n=t==="cn";return{cronJobs:Ee(e.cronJobs,!0),auditLog:Ee(e.auditLog,!1),apiKeys:Ee(e.apiKeys,!0),webhooks:Ee(e.webhooks,!0),commandPalette:Ee(e.commandPalette,!0),cookieConsent:Ee(e.cookieConsent,!0),legalPages:Ee(e.legalPages,!0),chinaCompliance:Ee(e.chinaCompliance,n)}}async function ar(e,t,n,o){let s={};return t||(s.region=()=>Y.select({message:"Target region?",options:[{value:"global",label:"global \u2014 \u6D77\u5916\u4F18\u5148"},{value:"cn",label:"cn \u2014 \u56FD\u5185\u4F18\u5148"},{value:"hybrid",label:"hybrid \u2014 \u53CC\u8F68\uFF08\u56FD\u5185+\u51FA\u6D77\uFF09"}],initialValue:"global"})),n||(s.auth=()=>Y.select({message:"Auth provider?",options:[{value:"clerk",label:"Clerk",hint:"managed, fastest setup"},{value:"betterauth",label:"Better Auth",hint:"self-hosted, modern (2FA / passkeys / RBAC)"},{value:"nextauth",label:"NextAuth (Auth.js v5)",hint:"self-hosted, mature, large ecosystem"},{value:"supabase",label:"Supabase Auth",hint:"managed auth with Supabase storage/realtime ecosystem"},{value:"none",label:"None"}],initialValue:"clerk"})),o||(s.aiMode=()=>Y.select({message:"AI topology?",options:[{value:"gateway",label:"Multi-provider AI Gateway / router",hint:"recommended"},{value:"direct",label:"Direct SDK/provider adapters"},{value:"custom",label:"OpenAI-compatible endpoint"},{value:"none",label:"Skip AI"}],initialValue:"gateway"}),s.aiProviders=({results:r})=>{if(r.aiMode!=="direct")return Promise.resolve([]);let i=nn.map(a=>({value:a.id,label:`[${a.category}] ${a.name}`}));return Y.multiselect({message:"Select direct AI provider adapters (expert path)",options:i,initialValues:["openai"],required:!1})},s.customAiName=({results:r})=>r.aiMode==="custom"?Y.text({message:"Custom endpoint name (e.g. proxy, local):",defaultValue:"custom",placeholder:"custom"}):Promise.resolve(void 0),s.customAiBaseUrl=({results:r})=>r.aiMode==="custom"?Y.text({message:"Custom endpoint base URL (e.g. https://api.proxy.com/v1):",validate:i=>{if(!i?.length)return"Base URL is required."}}):Promise.resolve(void 0),s.customAiApiKeyEnv=({results:r})=>r.aiMode==="custom"?Y.text({message:"Environment variable name for the API Key:",defaultValue:"CUSTOM_AI_API_KEY",placeholder:"CUSTOM_AI_API_KEY"}):Promise.resolve(void 0)),Object.keys(s).length===0?{}:await Y.group(s,{onCancel:()=>{process.stdout.write(ir.red(`\u2718 Cancelled
|
|
8
|
+
`)),process.exit(130)}})}async function Yn(e,t){let n=!!e.region,o=!!e.auth,s=!!e.payment,r=!!e.ai,i=!!e.email,a=!!e.storage,c=!!e.monitoring,l=!!e.analytics,u=!!e.sms,d=e.i18n!==void 0,h=!!e.yes||!process.stdin.isTTY,f,E,S,m,P,G,M,ge=Mn(e.socialLogin),pe,Z,j,K,C,H,le;if(h){f=en(e.region),E=Gt(e.orm);{let $=Yt(e.db),ft=qt(e.dbHost,f,$);S=ft.engine,m=ft.hostId}let W=s?e.payment:tn(f);P=Ht(W),G=Wt(W),M=Xt(e.auth);let Ge="gateway";if(r){let $=e.ai?.trim().toLowerCase()??"";zt($)?Ge=$:Ge="direct"}let ot=on({mode:Ge,providerIds:r?Jt(e.ai):void 0});pe=ot.mode,Z=ot.routing,j=ot.providerIds,C=Zt(e.deploy),H=Qt(e.docs,t),le=d?!!e.i18n:!0}else{let W=await ar(e,n,o,r);f=en(n?e.region:W.region),E=Gt(e.orm);{let ft=Yt(e.db),An=qt(e.dbHost,f,ft);S=An.engine,m=An.hostId}let Ge=s?e.payment:tn(f);P=Ht(Ge),G=Wt(Ge),M=Xt(o?e.auth:W.auth);let ot=W.aiMode==="custom"?{name:W.customAiName??"custom",baseURL:W.customAiBaseUrl??"",apiKeyEnvName:W.customAiApiKeyEnv??"CUSTOM_AI_API_KEY"}:void 0,$=on({mode:r?e.ai?.trim().toLowerCase()==="none"?"none":"direct":W.aiMode??"gateway",providerIds:r?Jt(e.ai):W.aiProviders,customEndpoint:ot});pe=$.mode,Z=$.routing,j=$.providerIds,$.customEndpoint&&(K={name:$.customEndpoint.name,baseURL:$.customEndpoint.baseURL,apiKeyEnvName:$.customEndpoint.apiKeyEnvName}),C=Zt(e.deploy),H=Qt(e.docs,t),le=d?!!e.i18n:!0}let w=Ln(f),fe=i?e.email:w.email,Ye=On(a?e.storage:void 0,w.storage),qe=c?e.monitoring:w.monitoring,ye=l?e.analytics:w.analytics,v=u?e.sms:w.sms,k=e.queue??w.queue,I=e.search??w.search,x=e.cache??w.cache,Mt=e.notifications??w.notifications,Dt=e.webhooks??w.webhooks,En=e.cms??w.cms,Ft=e.featureFlags??w.featureFlags,jt=e.captcha??w.captcha,vn=e.mcp??w.mcp,bn=e.metering??w.metering,_n=e.billingMode??w.billingMode,Pn=e.idp??w.idp,Bt=e.accessGate==="invite"?"invite":w.accessGate,wn=jn(C),Se=$n({cronJobs:e.cronJobs,auditLog:e.auditLog,apiKeys:e.apiKeys,commandPalette:e.commandPalette,cookieConsent:e.cookieConsent,legalPages:e.legalPages,chinaCompliance:e.chinaCompliance},f),$s=Kn([{flag:"queue",provider:k},{flag:"search",provider:I},{flag:"notifications",provider:Mt},{flag:"webhooks",provider:Dt},{flag:"feature-flags",provider:Ft},{flag:"captcha",provider:jt},{flag:"access-gate",provider:Bt}]),Ys={region:f,orm:E,database:S,payment:P,aiMode:pe,aiRouting:Z,aiProviders:j,customAiEndpoint:K,deployTarget:C,deployTargets:wn,docs:H,i18n:le,email:fe,storage:Ye,monitoring:qe,analytics:ye,sms:v,queue:k,search:I,cache:x,notifications:Mt,webhooks:Dt,cms:En,featureFlags:Ft,captcha:jt,mcp:vn,metering:bn,billingMode:_n,idp:Pn,accessGate:Bt,cronJobs:Se.cronJobs,auditLog:Se.auditLog,apiKeys:Se.apiKeys,commandPalette:Se.commandPalette,cookieConsent:Se.cookieConsent,legalPages:Se.legalPages,chinaCompliance:Se.chinaCompliance};return{region:f,orm:E,database:S,databaseHost:m,payment:P,paymentChoice:G,auth:M,socialLoginIds:ge,aiMode:pe,aiRouting:Z,aiProviders:j,customAiEndpoint:K,deployTarget:C,deployTargets:wn,docs:H,i18n:le,email:fe,storage:Ye,monitoring:qe,analytics:ye,sms:v,queue:k,search:I,cache:x,notifications:Mt,webhooks:Dt,cms:En,featureFlags:Ft,captcha:jt,mcp:vn,metering:bn,billingMode:_n,idp:Pn,accessGate:Bt,waveToggles:Se,previewSelections:$s,config:Ys}}import{execFileSync as Ot}from"child_process";import Lt from"fs";import Ke from"path";import mt from"picocolors";import xc from"update-notifier";import A from"picocolors";function cr(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function qn(e){let t=cr(),n=t?"\u2693":"-",o=t?"\u25B8":"->",s=t?"\u2605":"*",r=t?"\u{1F4E7}":"*",a=["",` ${t?A.bold(`${n} Done in ${e.elapsedSec}s \xB7 ${e.targetDir}/`):`${n} Done in ${e.elapsedSec}s \xB7 ${e.targetDir}/`}`,"",` ${t?A.bold("Next:"):"Next:"}`,` ${o} cd ${e.targetDir}`];e.skippedInstall&&a.push(` ${o} pnpm install (if --no-install was set)`),a.push(` ${o} fill in .env.local ${t?A.dim("\u2192 add provider keys before booting apps"):"-> add provider keys before booting apps"}`,` ${o} pnpm db:migrate ${t?A.dim("\u2192 sync Prisma schema"):"-> sync Prisma schema"}`,` ${o} pnpm db:seed ${t?A.dim("\u2192 load example tenants and records"):"-> load example tenants and records"}`,` ${o} pnpm dev ${t?A.dim("\u2192 http://localhost:3000"):"-> http://localhost:3000"}`,"",` ${t?A.bold("Customize:"):"Customize:"}`,` ${o} pnpm brand:init ${t?A.dim("\u2192 create brand.config.ts"):"-> create brand.config.ts"}`,` ${o} pnpm brand:apply ${t?A.dim("\u2192 propagate brand changes"):"-> propagate brand changes"}`,` ${o} pnpm preset:env ${t?A.dim("\u2192 review preset env requirements"):"-> review preset env requirements"}`,"",` ${t?A.bold("Useful scripts:"):"Useful scripts:"}`,` ${o} pnpm infra:up ${t?A.dim("\u2192 start local PostgreSQL/Redis/ClickHouse"):"-> start local PostgreSQL/Redis/ClickHouse"}`,` ${o} pnpm generate:api-types ${t?A.dim("\u2192 refresh shared API client types"):"-> refresh shared API client types"}`,` ${o} pnpm lint ${t?A.dim("\u2192 quick repo sanity check"):"-> quick repo sanity check"}`,` ${o} pnpm audit ${t?A.dim("\u2192 scan deps for known CVEs"):"-> scan deps for known CVEs"}`);let c=e.previewSelections??[];if(c.length>0){let _=t?A.bold(A.yellow("\u26A0 Preview features selected:")):"!! Preview features selected:";a.push("",` ${_}`);for(let h of c){let f=We(h.status),E=` ${o} ${h.flag} (${h.provider}) ${f}`;a.push(t?A.yellow(E):E)}a.push("",` ${t?A.dim(he("foundation")):he("foundation")}`,` ${t?A.dim("You may need to contribute the provider integration yourself."):"You may need to contribute the provider integration yourself."}`,` ${t?A.dim("See: https://github.com/Nebutra/Nebutra-Sailor/blob/main/docs/package-status.md"):"See: https://github.com/Nebutra/Nebutra-Sailor/blob/main/docs/package-status.md"}`)}let l=e.waveFeatures??{},u=_=>_!==!1,d=[];u(l.apiKeys)&&d.push("Manage API keys at /settings/api-keys"),u(l.auditLog)&&d.push("View audit log at /settings/audit-log"),d.push("Configure webhooks at /settings/webhooks"),u(l.commandPalette)&&d.push("Press Cmd/Ctrl+K to open the command palette"),d.push("Edit notification preferences at /settings/notifications"),d.push("Export your data at /settings/account/export"),l.chinaCompliance&&d.push("China deployments \u2014 see packages/ops/china-compliance/README.md"),a.push("",` ${t?A.bold("What you can do next:"):"What you can do next:"}`);for(let _ of d)a.push(` ${o} ${_}`);a.push("",` ${t?A.bold("Ship faster:"):"Ship faster:"}`,` ${s} Star us https://github.com/nebutra/nebutra-sailor`,` ${r} Free license https://nebutra.com/get-license`,""),process.stdout.write(a.join(`
|
|
6
9
|
`)+`
|
|
7
|
-
`)}var
|
|
8
|
-
|
|
9
|
-
Arguments:
|
|
10
|
-
name project directory (default: ./my-saas-app)
|
|
11
|
-
|
|
12
|
-
Core options:
|
|
13
|
-
-p, --pm <id> npm | pnpm | yarn | bun (auto-detected)
|
|
14
|
-
--region <id> global | cn | hybrid (default: global)
|
|
15
|
-
--auth <id> clerk | betterauth | nextauth | supabase | none
|
|
16
|
-
--ai <ids> expert/non-interactive provider seed (e.g. openai,anthropic)
|
|
17
|
-
|
|
18
|
-
Interactive AI topology:
|
|
19
|
-
Default first-run asks for topology, not individual provider enablement:
|
|
20
|
-
gateway Multi-provider AI Gateway / router (recommended)
|
|
21
|
-
direct Direct SDK/provider adapters
|
|
22
|
-
custom OpenAI-compatible endpoint
|
|
23
|
-
none Skip AI
|
|
24
|
-
|
|
25
|
-
Stack flags (region-based smart defaults):
|
|
26
|
-
--orm <id> prisma | drizzle | none (default: prisma)
|
|
27
|
-
--db <id> postgres | mysql | sqlite | none (default: postgres)
|
|
28
|
-
--payment <id> stripe | lemon | wechat | alipay | none (default: region-based)
|
|
29
|
-
--deploy <target> vercel | railway | cloudflare | selfhost (default: vercel)
|
|
30
|
-
--docs <id> fumadocs | mintlify | docusaurus | nextra | vitepress | none
|
|
31
|
-
|
|
32
|
-
Feature flags (region-based smart defaults):
|
|
33
|
-
--email <id> resend | postmark | ses | aliyun-dm | tencent-ses | netease | none
|
|
34
|
-
--storage <id> r2 | s3 | supabase-storage | aliyun-oss | tencent-cos | qiniu | none
|
|
35
|
-
--monitoring <id> sentry | datadog | aliyun-arms | tingyun | none
|
|
36
|
-
--analytics <id> posthog | plausible | umami | baidu | sensors | none
|
|
37
|
-
--sms <id> twilio | aliyun-sms | tencent-sms | yunpian | none
|
|
38
|
-
--queue <id> qstash | bullmq | upstash | sqs | none [Foundation]
|
|
39
|
-
--search <id> meilisearch | typesense | algolia | pgvector | none [Foundation]
|
|
40
|
-
--cache <id> upstash-redis | vercel-kv | redis | dragonfly | none
|
|
41
|
-
--notifications <id> novu | knock | custom | none [Foundation]
|
|
42
|
-
--webhooks <id> svix | custom | none [Foundation]
|
|
43
|
-
--cms <id> sanity | contentful | strapi | none
|
|
44
|
-
--feature-flags <id> vercel-flags | growthbook | configcat | none [WIP]
|
|
45
|
-
--captcha <id> turnstile | hcaptcha | aliyun-slide | none [WIP]
|
|
46
|
-
--billing-mode <mode> usage | seat | credits (default: usage)
|
|
47
|
-
--idp <id> clerk | oauth-server (default: clerk)
|
|
48
|
-
--access-gate <mode> none | invite (default: none)
|
|
49
|
-
|
|
50
|
-
Preview statuses:
|
|
51
|
-
[Foundation] \u2014 types + factory complete; adapters need external credentials & may not work out-of-the-box
|
|
52
|
-
[WIP] \u2014 actively being built; do not use in production
|
|
53
|
-
See: https://github.com/Nebutra/Nebutra-Sailor/blob/main/docs/package-status.md
|
|
54
|
-
|
|
55
|
-
Sailor features:
|
|
56
|
-
--mcp <mode> on | off (default: on \u2014 Agent-friendly MCP server)
|
|
57
|
-
--metering <mode> auto | on | off (default: auto)
|
|
58
|
-
|
|
59
|
-
Toggles:
|
|
60
|
-
--i18n enable i18n (default: true)
|
|
61
|
-
--no-i18n disable
|
|
62
|
-
--no-install skip package install
|
|
63
|
-
--no-git skip git init
|
|
64
|
-
-y, --yes accept all defaults (non-interactive)
|
|
65
|
-
--dry-run preview actions without writing files
|
|
66
|
-
--json machine-readable output
|
|
67
|
-
--no-color disable color output
|
|
68
|
-
-h, --help show this help
|
|
69
|
-
-v, --version show version
|
|
70
|
-
|
|
71
|
-
Examples:
|
|
72
|
-
$ npx create-sailor@latest
|
|
73
|
-
$ npx create-sailor@latest my-app -y
|
|
74
|
-
$ npx create-sailor@latest my-app --region=cn --auth=clerk
|
|
75
|
-
$ npx create-sailor@latest my-app --region=hybrid --ai=openai,deepseek
|
|
76
|
-
$ npm create sailor@latest --dry-run --region=cn
|
|
77
|
-
`;function Cn(){process.stdout.write(Gr)}import Ut from"picocolors";function In(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function Hr(e){let t=In()?"\u25B8":"->",o=`[${e.index}/${e.total}]`,n=e.label.padEnd(18," ");return In()?`${Ut.dim(o)} ${Ut.bold(n)} ${Ut.dim(t)} ${Ut.cyan(e.value)}`:`${o} ${n} ${t} ${e.value}`}function wn(e){process.stdout.write(Hr(e)+`
|
|
78
|
-
`)}var nn=[{id:"anthropic",name:"Anthropic",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.anthropic.com",envVarPrefix:"ANTHROPIC",requiredEnvVars:["ANTHROPIC_API_KEY"]},{id:"openai",name:"OpenAI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.openai.com/docs",envVarPrefix:"OPENAI",requiredEnvVars:["OPENAI_API_KEY"]},{id:"deepseek",name:"DeepSeek",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.deepseek.com/api-docs",envVarPrefix:"DEEPSEEK",requiredEnvVars:["DEEPSEEK_API_KEY"]},{id:"xai",name:"xAI (Grok)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.x.ai/api",envVarPrefix:"XAI",requiredEnvVars:["XAI_API_KEY"]},{id:"moonshot",name:"Moonshot (Kimi)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.moonshot.cn/docs",envVarPrefix:"MOONSHOT",requiredEnvVars:["MOONSHOT_API_KEY","MOONSHOT_BASE_URL"]},{id:"google",name:"Google Gemini",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://ai.google.dev/gemini-api/docs",envVarPrefix:"GOOGLE_GENERATIVE_AI",requiredEnvVars:["GOOGLE_GENERATIVE_AI_API_KEY"]},{id:"mistral",name:"Mistral AI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.mistral.ai",envVarPrefix:"MISTRAL",requiredEnvVars:["MISTRAL_API_KEY"]},{id:"cohere",name:"Cohere",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.cohere.com",envVarPrefix:"COHERE",requiredEnvVars:["COHERE_API_KEY"]},{id:"perplexity",name:"Perplexity",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.perplexity.ai",baseURL:"https://api.perplexity.ai",envVarPrefix:"PERPLEXITY",requiredEnvVars:["PERPLEXITY_API_KEY"]},{id:"ai21",name:"AI21 Labs",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.ai21.com",baseURL:"https://api.ai21.com/studio/v1",envVarPrefix:"AI21",requiredEnvVars:["AI21_API_KEY"]},{id:"upstage",name:"Upstage (Solar)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://developers.upstage.ai/docs",baseURL:"https://api.upstage.ai/v1/solar",envVarPrefix:"UPSTAGE",requiredEnvVars:["UPSTAGE_API_KEY"]},{id:"siliconflow",name:"\u7845\u57FA\u6D41\u52A8 SiliconFlow",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://docs.siliconflow.cn",baseURL:"https://api.siliconflow.cn/v1",envVarPrefix:"SILICONFLOW",requiredEnvVars:["SILICONFLOW_API_KEY"]},{id:"volcengine-ark",name:"\u706B\u5C71\u5F15\u64CE ARK (\u8C46\u5305)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://www.volcengine.com/docs/82379",baseURL:"https://ark.cn-beijing.volces.com/api/v3",envVarPrefix:"ARK",requiredEnvVars:["ARK_API_KEY"]},{id:"bailian",name:"\u767E\u70BC Bailian (\u901A\u4E49)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://help.aliyun.com/zh/model-studio",baseURL:"https://dashscope.aliyuncs.com/compatible-mode/v1",envVarPrefix:"DASHSCOPE",requiredEnvVars:["DASHSCOPE_API_KEY"]},{id:"zhipu",name:"\u667A\u8C31 AI Zhipu",category:"\u56FD\u5185\u5E73\u53F0",status:"ai-sdk",docs:"https://docs.bigmodel.cn",baseURL:"https://open.bigmodel.cn/api/paas/v4",envVarPrefix:"ZHIPU",requiredEnvVars:["ZHIPU_API_KEY"]},{id:"baichuan",name:"\u767E\u5DDD\u667A\u80FD Baichuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.baichuan-ai.com/docs",baseURL:"https://api.baichuan-ai.com/v1",envVarPrefix:"BAICHUAN",requiredEnvVars:["BAICHUAN_API_KEY"]},{id:"minimax",name:"MiniMax (ABAB)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.minimaxi.com/document/guides",baseURL:"https://api.minimax.chat/v1",envVarPrefix:"MINIMAX",requiredEnvVars:["MINIMAX_API_KEY","MINIMAX_GROUP_ID"]},{id:"stepfun",name:"\u9636\u8DC3\u661F\u8FB0 StepFun",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.stepfun.com/docs",baseURL:"https://api.stepfun.com/v1",envVarPrefix:"STEPFUN",requiredEnvVars:["STEPFUN_API_KEY"]},{id:"sensetime",name:"\u5546\u6C64 SenseNova",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.sensetime.com",baseURL:"https://api.sensenova.cn/v1",envVarPrefix:"SENSENOVA",requiredEnvVars:["SENSENOVA_API_KEY"]},{id:"tencent",name:"\u817E\u8BAF\u6DF7\u5143 Hunyuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://cloud.tencent.com/document/product/1729",baseURL:"https://api.hunyuan.cloud.tencent.com/v1",envVarPrefix:"HUNYUAN",requiredEnvVars:["HUNYUAN_API_KEY"]},{id:"lingyi",name:"\u96F6\u4E00\u4E07\u7269 Yi",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.lingyiwanwu.com/docs",baseURL:"https://api.lingyiwanwu.com/v1",envVarPrefix:"YI",requiredEnvVars:["YI_API_KEY"]},{id:"openrouter",name:"OpenRouter",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://openrouter.ai/docs",envVarPrefix:"OPENROUTER",requiredEnvVars:["OPENROUTER_API_KEY"]},{id:"vercel-gateway",name:"Vercel AI Gateway",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://vercel.com/docs/ai-gateway",envVarPrefix:"AI_GATEWAY",requiredEnvVars:["AI_GATEWAY_API_KEY"]},{id:"litellm",name:"LiteLLM Proxy",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://docs.litellm.ai",baseURL:"http://localhost:4000/v1",envVarPrefix:"LITELLM",requiredEnvVars:["LITELLM_API_KEY"]},{id:"portkey",name:"Portkey",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://portkey.ai/docs",baseURL:"https://api.portkey.ai/v1",envVarPrefix:"PORTKEY",requiredEnvVars:["PORTKEY_API_KEY"]},{id:"aws-bedrock",name:"AWS Bedrock",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://aws.amazon.com/bedrock/",envVarPrefix:"AWS_BEDROCK",requiredEnvVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"]},{id:"azure-openai",name:"Azure OpenAI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/",envVarPrefix:"AZURE_OPENAI",requiredEnvVars:["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"]},{id:"gcp-vertex",name:"GCP Vertex AI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://cloud.google.com/vertex-ai",envVarPrefix:"GOOGLE_VERTEX",requiredEnvVars:["GOOGLE_APPLICATION_CREDENTIALS"]},{id:"groq",name:"Groq",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://console.groq.com/docs",envVarPrefix:"GROQ",requiredEnvVars:["GROQ_API_KEY"]},{id:"fireworks",name:"Fireworks AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.fireworks.ai",envVarPrefix:"FIREWORKS",requiredEnvVars:["FIREWORKS_API_KEY"]},{id:"together",name:"Together AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.together.ai",envVarPrefix:"TOGETHER",requiredEnvVars:["TOGETHER_API_KEY"]},{id:"huggingface",name:"Hugging Face",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://huggingface.co/docs/inference-providers",envVarPrefix:"HF",requiredEnvVars:["HF_API_KEY"]},{id:"replicate",name:"Replicate",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://replicate.com/docs",envVarPrefix:"REPLICATE",requiredEnvVars:["REPLICATE_API_TOKEN"]},{id:"lepton",name:"Lepton AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://www.lepton.ai/docs",baseURL:"https://api.lepton.ai/v1",envVarPrefix:"LEPTON",requiredEnvVars:["LEPTON_API_KEY"]},{id:"anyscale",name:"Anyscale Endpoints",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://docs.endpoints.anyscale.com/",baseURL:"https://api.endpoints.anyscale.com/v1",envVarPrefix:"ANYSCALE",requiredEnvVars:["ANYSCALE_API_KEY"]},{id:"octoai",name:"OctoAI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://octoai.cloud/docs",baseURL:"https://text.octoai.run/v1",envVarPrefix:"OCTOAI",requiredEnvVars:["OCTOAI_TOKEN"]},{id:"deepinfra",name:"DeepInfra",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://deepinfra.com/docs",baseURL:"https://api.deepinfra.com/v1/openai",envVarPrefix:"DEEPINFRA",requiredEnvVars:["DEEPINFRA_API_KEY"]},{id:"novita",name:"Novita AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://novita.ai/docs",baseURL:"https://api.novita.ai/v3/openai",envVarPrefix:"NOVITA",requiredEnvVars:["NOVITA_API_KEY"]},{id:"black-forest-labs",name:"Black Forest Labs (FLUX)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://docs.bfl.ml",envVarPrefix:"BFL",requiredEnvVars:["BFL_API_KEY"]},{id:"elevenlabs",name:"ElevenLabs (TTS)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://elevenlabs.io/docs",envVarPrefix:"ELEVENLABS",requiredEnvVars:["ELEVENLABS_API_KEY"]},{id:"runway",name:"Runway (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://runwayml.com",envVarPrefix:"RUNWAY",requiredEnvVars:["RUNWAY_API_KEY"]},{id:"luma",name:"Luma (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://lumalabs.ai",envVarPrefix:"LUMA",requiredEnvVars:["LUMA_API_KEY"]},{id:"midjourney",name:"Midjourney",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://docs.midjourney.com",envVarPrefix:"MIDJOURNEY",requiredEnvVars:[]},{id:"ollama",name:"Ollama (\u672C\u5730)",category:"\u672C\u5730\u90E8\u7F72",status:"opencode",docs:"https://github.com/ollama/ollama",baseURL:"http://localhost:11434/v1",envVarPrefix:"OLLAMA",requiredEnvVars:["OLLAMA_BASE_URL"]},{id:"lmstudio",name:"LM Studio",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://lmstudio.ai/docs",baseURL:"http://localhost:1234/v1",envVarPrefix:"LMSTUDIO",requiredEnvVars:[]},{id:"vllm",name:"vLLM",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://docs.vllm.ai",baseURL:"http://localhost:8000/v1",envVarPrefix:"VLLM",requiredEnvVars:[]},{id:"tgi",name:"HF TGI",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://huggingface.co/docs/text-generation-inference",baseURL:"http://localhost:8080/v1",envVarPrefix:"TGI",requiredEnvVars:[]}],Nc=nn.reduce((e,t)=>(e[t.category]||(e[t.category]=[]),e[t.category].push(t),e),{});var Wr=["openai","anthropic","google"],Xr=["openai"];function on(e){let t=e.providerIds??[];switch(e.mode){case"gateway":{let o=t.length>0?t:[...Wr];return{mode:"gateway",providerIds:o,customEndpoint:e.customEndpoint,routing:{profile:"multi-provider-gateway",providerSeed:o,runtimeGovernance:!0}}}case"direct":{let o=t.length>0?t:[...Xr];return{mode:"direct",providerIds:o,customEndpoint:e.customEndpoint,routing:{profile:"direct-adapters",providerSeed:o,runtimeGovernance:!1}}}case"custom":return{mode:"custom",providerIds:t,customEndpoint:e.customEndpoint,routing:{profile:"openai-compatible",providerSeed:t,runtimeGovernance:!0}};case"none":return{mode:"none",providerIds:[],routing:{profile:"disabled",providerSeed:[],runtimeGovernance:!1}}}}import st from"fs";import B from"path";var Rn=[{id:"posthog",name:"PostHog",region:"both",envVars:["POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_HOST"],docs:"https://posthog.com/docs",pkgs:["posthog-js","posthog-node"],providerFile:"apps/web/src/lib/analytics/posthog.client.ts"},{id:"plausible",name:"Plausible",region:"global",envVars:["NEXT_PUBLIC_PLAUSIBLE_DOMAIN","NEXT_PUBLIC_PLAUSIBLE_SRC"],docs:"https://plausible.io/docs",providerFile:"apps/web/src/components/analytics/PlausibleScript.tsx"},{id:"umami",name:"Umami",region:"both",envVars:["NEXT_PUBLIC_UMAMI_WEBSITE_ID","NEXT_PUBLIC_UMAMI_SRC"],docs:"https://umami.is/docs",providerFile:"apps/web/src/components/analytics/UmamiScript.tsx"},{id:"mixpanel",name:"Mixpanel",region:"global",envVars:["MIXPANEL_TOKEN","NEXT_PUBLIC_MIXPANEL_TOKEN"],docs:"https://docs.mixpanel.com",pkgs:["mixpanel-browser","mixpanel"],providerFile:"apps/web/src/lib/analytics/mixpanel.client.ts"},{id:"baidu",name:"\u767E\u5EA6\u7EDF\u8BA1 Baidu Tongji",region:"cn",envVars:["NEXT_PUBLIC_BAIDU_STATS_ID"],docs:"https://tongji.baidu.com",providerFile:"apps/web/src/components/analytics/BaiduStatsScript.tsx"},{id:"sensors",name:"\u795E\u7B56 Sensors Analytics",region:"cn",envVars:["NEXT_PUBLIC_SENSORS_SERVER_URL","NEXT_PUBLIC_SENSORS_APP_JS_URL"],docs:"https://manual.sensorsdata.cn",pkgs:["sa-sdk-javascript"],providerFile:"apps/web/src/lib/analytics/sensors.client.ts"},{id:"growingio",name:"GrowingIO",region:"cn",envVars:["NEXT_PUBLIC_GROWINGIO_ACCOUNT_ID"],docs:"https://growingio.com/docs",providerFile:"apps/web/src/components/analytics/GrowingIoScript.tsx"},{id:"none",name:"None",region:"both",envVars:[],docs:""}],Lc=Rn.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function kn(e){return Rn.find(t=>t.id===e)}function zr(e){st.mkdirSync(e,{recursive:!0})}function Z(e,t){zr(B.dirname(e)),st.writeFileSync(e,t)}function Ce(e,t){let o=B.join(e,".env.example");st.existsSync(o)?st.appendFileSync(o,`
|
|
79
|
-
`+t):st.writeFileSync(o,t)}function Jr(e){let t=`"use client";
|
|
10
|
+
`)}import Xe from"fs";import B from"path";var Gn=[{id:"posthog",name:"PostHog",region:"both",envVars:["POSTHOG_KEY","POSTHOG_HOST","NEXT_PUBLIC_POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_HOST"],docs:"https://posthog.com/docs",pkgs:["posthog-js","posthog-node"],providerFile:"apps/web/src/lib/analytics/posthog.client.ts"},{id:"plausible",name:"Plausible",region:"global",envVars:["NEXT_PUBLIC_PLAUSIBLE_DOMAIN","NEXT_PUBLIC_PLAUSIBLE_SRC"],docs:"https://plausible.io/docs",providerFile:"apps/web/src/components/analytics/PlausibleScript.tsx"},{id:"umami",name:"Umami",region:"both",envVars:["NEXT_PUBLIC_UMAMI_WEBSITE_ID","NEXT_PUBLIC_UMAMI_SRC"],docs:"https://umami.is/docs",providerFile:"apps/web/src/components/analytics/UmamiScript.tsx"},{id:"mixpanel",name:"Mixpanel",region:"global",envVars:["MIXPANEL_TOKEN","NEXT_PUBLIC_MIXPANEL_TOKEN"],docs:"https://docs.mixpanel.com",pkgs:["mixpanel-browser","mixpanel"],providerFile:"apps/web/src/lib/analytics/mixpanel.client.ts"},{id:"baidu",name:"\u767E\u5EA6\u7EDF\u8BA1 Baidu Tongji",region:"cn",envVars:["NEXT_PUBLIC_BAIDU_STATS_ID"],docs:"https://tongji.baidu.com",providerFile:"apps/web/src/components/analytics/BaiduStatsScript.tsx"},{id:"sensors",name:"\u795E\u7B56 Sensors Analytics",region:"cn",envVars:["NEXT_PUBLIC_SENSORS_SERVER_URL","NEXT_PUBLIC_SENSORS_APP_JS_URL"],docs:"https://manual.sensorsdata.cn",pkgs:["sa-sdk-javascript"],providerFile:"apps/web/src/lib/analytics/sensors.client.ts"},{id:"growingio",name:"GrowingIO",region:"cn",envVars:["NEXT_PUBLIC_GROWINGIO_ACCOUNT_ID"],docs:"https://growingio.com/docs",providerFile:"apps/web/src/components/analytics/GrowingIoScript.tsx"},{id:"none",name:"None",region:"both",envVars:[],docs:""}],Ap=Gn.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Hn(e){return Gn.find(t=>t.id===e)}function pr(e){Xe.mkdirSync(e,{recursive:!0})}function te(e,t){pr(B.dirname(e)),Xe.writeFileSync(e,t)}function Ae(e,t){let n=B.join(e,".env.example");Xe.existsSync(n)?Xe.appendFileSync(n,`
|
|
11
|
+
`+t):Xe.writeFileSync(n,t)}function lr(e){let t=`"use client";
|
|
80
12
|
/**
|
|
81
13
|
* PostHog browser SDK \u2014 lazy-initialized on first access.
|
|
82
14
|
*/
|
|
@@ -101,7 +33,7 @@ export function initPostHog(): void {
|
|
|
101
33
|
}
|
|
102
34
|
|
|
103
35
|
export { posthog };
|
|
104
|
-
`,
|
|
36
|
+
`,n=`import { PostHog } from "posthog-node";
|
|
105
37
|
|
|
106
38
|
/**
|
|
107
39
|
* PostHog server-side client. Remember to await client.shutdown() at the end
|
|
@@ -115,7 +47,7 @@ export function getPostHogServerClient(): PostHog | null {
|
|
|
115
47
|
if (!key) return null;
|
|
116
48
|
|
|
117
49
|
client = new PostHog(key, {
|
|
118
|
-
host: process.env.NEXT_PUBLIC_POSTHOG_HOST ?? "https://us.i.posthog.com",
|
|
50
|
+
host: process.env.POSTHOG_HOST ?? process.env.NEXT_PUBLIC_POSTHOG_HOST ?? "https://us.i.posthog.com",
|
|
119
51
|
flushAt: 1,
|
|
120
52
|
flushInterval: 0,
|
|
121
53
|
});
|
|
@@ -132,7 +64,7 @@ export async function captureServerEvent(
|
|
|
132
64
|
ph.capture({ distinctId, event, properties });
|
|
133
65
|
await ph.shutdown();
|
|
134
66
|
}
|
|
135
|
-
`,
|
|
67
|
+
`,o=`"use client";
|
|
136
68
|
/**
|
|
137
69
|
* <AnalyticsProvider /> \u2014 wrap the app root to enable PostHog + auto page views.
|
|
138
70
|
*/
|
|
@@ -159,11 +91,12 @@ export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
|
|
|
159
91
|
|
|
160
92
|
return <>{children}</>;
|
|
161
93
|
}
|
|
162
|
-
`;
|
|
94
|
+
`;te(B.join(e,"src","lib","analytics","posthog.client.ts"),t),te(B.join(e,"src","lib","analytics","posthog.server.ts"),n),te(B.join(e,"src","components","analytics","AnalyticsProvider.tsx"),o)}function dr(){return`# PostHog \u2014 product analytics
|
|
163
95
|
POSTHOG_KEY=
|
|
96
|
+
POSTHOG_HOST=https://us.i.posthog.com
|
|
164
97
|
NEXT_PUBLIC_POSTHOG_KEY=
|
|
165
98
|
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
|
|
166
|
-
`}function
|
|
99
|
+
`}function ur(e){te(B.join(e,"src","components","analytics","PlausibleScript.tsx"),`"use client";
|
|
167
100
|
/**
|
|
168
101
|
* Plausible script tag \u2014 lightweight, privacy-friendly analytics.
|
|
169
102
|
*/
|
|
@@ -183,10 +116,10 @@ export function PlausibleScript() {
|
|
|
183
116
|
/>
|
|
184
117
|
);
|
|
185
118
|
}
|
|
186
|
-
`)}function
|
|
119
|
+
`)}function mr(){return`# Plausible \u2014 privacy-friendly analytics
|
|
187
120
|
NEXT_PUBLIC_PLAUSIBLE_DOMAIN=
|
|
188
121
|
NEXT_PUBLIC_PLAUSIBLE_SRC=https://plausible.io/js/script.js
|
|
189
|
-
`}function
|
|
122
|
+
`}function gr(e){te(B.join(e,"src","components","analytics","UmamiScript.tsx"),`"use client";
|
|
190
123
|
/**
|
|
191
124
|
* Umami analytics \u2014 self-hosted OR umami.is cloud.
|
|
192
125
|
*/
|
|
@@ -206,10 +139,10 @@ export function UmamiScript() {
|
|
|
206
139
|
/>
|
|
207
140
|
);
|
|
208
141
|
}
|
|
209
|
-
`)}function
|
|
142
|
+
`)}function fr(){return`# Umami \u2014 self-hostable analytics
|
|
210
143
|
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
|
|
211
144
|
NEXT_PUBLIC_UMAMI_SRC=https://cloud.umami.is/script.js
|
|
212
|
-
`}function
|
|
145
|
+
`}function yr(e){let t=`"use client";
|
|
213
146
|
import mixpanel from "mixpanel-browser";
|
|
214
147
|
|
|
215
148
|
let initialized = false;
|
|
@@ -227,7 +160,7 @@ export function initMixpanel(): void {
|
|
|
227
160
|
}
|
|
228
161
|
|
|
229
162
|
export { mixpanel };
|
|
230
|
-
`,
|
|
163
|
+
`,n=`import Mixpanel from "mixpanel";
|
|
231
164
|
|
|
232
165
|
let client: Mixpanel.Mixpanel | null = null;
|
|
233
166
|
|
|
@@ -248,10 +181,10 @@ export function trackServer(
|
|
|
248
181
|
if (!c) return;
|
|
249
182
|
c.track(event, { distinct_id: distinctId, ...properties });
|
|
250
183
|
}
|
|
251
|
-
`;
|
|
184
|
+
`;te(B.join(e,"src","lib","analytics","mixpanel.client.ts"),t),te(B.join(e,"src","lib","analytics","mixpanel.server.ts"),n)}function Sr(){return`# Mixpanel \u2014 product analytics
|
|
252
185
|
MIXPANEL_TOKEN=
|
|
253
186
|
NEXT_PUBLIC_MIXPANEL_TOKEN=
|
|
254
|
-
`}function
|
|
187
|
+
`}function hr(e){te(B.join(e,"src","components","analytics","BaiduStatsScript.tsx"),`"use client";
|
|
255
188
|
/**
|
|
256
189
|
* \u767E\u5EA6\u7EDF\u8BA1 Baidu Tongji \u2014 hm.js \u57CB\u70B9\u811A\u672C.
|
|
257
190
|
*/
|
|
@@ -277,9 +210,9 @@ var _hmt = _hmt || [];
|
|
|
277
210
|
/>
|
|
278
211
|
);
|
|
279
212
|
}
|
|
280
|
-
`)}function
|
|
213
|
+
`)}function Er(){return`# \u767E\u5EA6\u7EDF\u8BA1 \u2014 \u4E2D\u56FD\u533A\u7F51\u7AD9\u5206\u6790
|
|
281
214
|
NEXT_PUBLIC_BAIDU_STATS_ID=
|
|
282
|
-
`}function
|
|
215
|
+
`}function vr(e){te(B.join(e,"src","lib","analytics","sensors.client.ts"),`"use client";
|
|
283
216
|
/**
|
|
284
217
|
* \u795E\u7B56\u5206\u6790 (Sensors Analytics) \u2014 sa-sdk-javascript \u5C01\u88C5.
|
|
285
218
|
*/
|
|
@@ -307,10 +240,10 @@ export function initSensors(): void {
|
|
|
307
240
|
}
|
|
308
241
|
|
|
309
242
|
export { sensors };
|
|
310
|
-
`)}function
|
|
243
|
+
`)}function br(){return`# \u795E\u7B56\u5206\u6790 Sensors Analytics
|
|
311
244
|
NEXT_PUBLIC_SENSORS_SERVER_URL=
|
|
312
245
|
NEXT_PUBLIC_SENSORS_APP_JS_URL=
|
|
313
|
-
`}function
|
|
246
|
+
`}function _r(e){te(B.join(e,"src","components","analytics","GrowingIoScript.tsx"),`"use client";
|
|
314
247
|
/**
|
|
315
248
|
* GrowingIO CDP \u2014 inline snippet installer.
|
|
316
249
|
*/
|
|
@@ -335,21 +268,21 @@ gdp("send");
|
|
|
335
268
|
/>
|
|
336
269
|
);
|
|
337
270
|
}
|
|
338
|
-
`)}function
|
|
271
|
+
`)}function Pr(){return`# GrowingIO \u2014 \u5168\u57CB\u70B9 CDP (\u4E2D\u56FD\u533A)
|
|
339
272
|
NEXT_PUBLIC_GROWINGIO_ACCOUNT_ID=
|
|
340
|
-
`}async function
|
|
341
|
-
`+t):q.writeFileSync(
|
|
342
|
-
`+t)}async function
|
|
273
|
+
`}async function Wn(e,t,n="global"){let o=Hn(t);if(!o||o.id==="none")return;let s=B.join(e,"apps","web");if(!Xe.existsSync(s))return;let r=o.id;try{switch(r){case"posthog":lr(s),Ae(e,dr());return;case"plausible":ur(s),Ae(e,mr());return;case"umami":gr(s),Ae(e,fr());return;case"mixpanel":yr(s),Ae(e,Sr());return;case"baidu":hr(s),Ae(e,Er());return;case"sensors":vr(s),Ae(e,br());return;case"growingio":_r(s),Ae(e,Pr());return;default:return}}catch(i){throw console.error(`Failed to apply analytics selection "${t}":`,i),new Error(`Analytics scaffold for "${t}" failed \u2014 see stderr for details.`)}}var wr="https://analytics.nebutra.com";function Ar(e={}){if(e.noTelemetry===!0)return!0;let t=process.env.NEBUTRA_TELEMETRY;return t==="0"||t==="false"}function Xn(e,t={}){Ar(t)||(async()=>{try{let n=await import("./dist-V6L2DNL2.js");if(typeof n.createProductAnalyticsClient!="function")return;let o=n.createProductAnalyticsClient({posthog:{apiKey:process.env.POSTHOG_KEY??process.env.NEXT_PUBLIC_POSTHOG_KEY??process.env.NEBUTRA_POSTHOG_KEY??"",host:process.env.POSTHOG_HOST??process.env.NEXT_PUBLIC_POSTHOG_HOST??process.env.NEBUTRA_POSTHOG_HOST??wr},onError:()=>{}});if(typeof o?.track!="function")return;let s=o.track("scaffold.completed",e);s&&typeof s.then=="function"&&await s.catch(()=>{})}catch{}})()}import q from"fs";import N from"path";function V(e){q.existsSync(e)&&q.rmSync(e,{recursive:!0,force:!0})}function yt(e,t){let n=N.join(e,".env.example");q.existsSync(n)?q.appendFileSync(n,`
|
|
274
|
+
`+t):q.writeFileSync(n,t)}function St(e,t){if(!q.existsSync(e))return;let n=q.readFileSync(e,"utf8"),o=n.replace(/export type AuthProviderId\s*=\s*(?:"[^"]+"\s*\|\s*)+"[^"]+";/,`export type AuthProviderId = "${t}";`);o!==n&&q.writeFileSync(e,o)}function Ir(e,t){let n=N.join(e,"README.md");if(!q.existsSync(n)){q.writeFileSync(n,t);return}q.appendFileSync(n,`
|
|
275
|
+
`+t)}async function zn(e,t){let n=N.join(e,"packages","iam","auth");if(!q.existsSync(n))return;if(t==="none"){V(n),Ir(e,`
|
|
343
276
|
## Auth
|
|
344
277
|
|
|
345
278
|
Auth was skipped at scaffold time (\`--auth=none\`). Add your preferred auth provider manually (e.g. Clerk, Better Auth, NextAuth, Supabase).
|
|
346
|
-
`);return}let
|
|
279
|
+
`);return}let o=N.join(n,"src","providers"),s=N.join(n,"src","types.ts");if(t==="clerk"){V(N.join(o,"better-auth.ts")),V(N.join(o,"nextauth.ts")),V(N.join(o,"supabase.ts")),St(s,"clerk"),yt(e,`# Clerk auth
|
|
347
280
|
CLERK_PUBLISHABLE_KEY=
|
|
348
281
|
CLERK_SECRET_KEY=
|
|
349
|
-
`);return}if(t==="betterauth"){
|
|
282
|
+
`);return}if(t==="betterauth"){V(N.join(o,"clerk.ts")),V(N.join(o,"nextauth.ts")),V(N.join(o,"supabase.ts")),St(s,"better-auth"),yt(e,`# Better Auth
|
|
350
283
|
BETTER_AUTH_SECRET=
|
|
351
284
|
BETTER_AUTH_URL=http://localhost:3000
|
|
352
|
-
`);return}if(t==="nextauth"){
|
|
285
|
+
`);return}if(t==="nextauth"){V(N.join(o,"clerk.ts")),V(N.join(o,"better-auth.ts")),V(N.join(o,"supabase.ts")),St(s,"nextauth"),yt(e,`# NextAuth (Auth.js v5)
|
|
353
286
|
AUTH_SECRET=
|
|
354
287
|
NEXTAUTH_URL=http://localhost:3000
|
|
355
288
|
# OAuth (optional \u2014 leave blank to disable that provider)
|
|
@@ -357,16 +290,18 @@ GOOGLE_CLIENT_ID=
|
|
|
357
290
|
GOOGLE_CLIENT_SECRET=
|
|
358
291
|
GITHUB_CLIENT_ID=
|
|
359
292
|
GITHUB_CLIENT_SECRET=
|
|
360
|
-
`);return}if(t==="supabase"){
|
|
293
|
+
`);return}if(t==="supabase"){V(N.join(o,"clerk.ts")),V(N.join(o,"better-auth.ts")),V(N.join(o,"nextauth.ts")),St(s,"supabase"),yt(e,`# Supabase Auth
|
|
361
294
|
NEXT_PUBLIC_SUPABASE_URL=
|
|
295
|
+
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=
|
|
362
296
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=
|
|
363
297
|
SUPABASE_URL=
|
|
298
|
+
SUPABASE_PUBLISHABLE_KEY=
|
|
364
299
|
SUPABASE_ANON_KEY=
|
|
365
300
|
SUPABASE_SERVICE_ROLE_KEY=
|
|
366
|
-
`);return}}
|
|
301
|
+
`);return}}import Ce from"fs";import Ie from"path";function Cr(e,t){let n=`// apps/web/src/app/api/auth/callback/${e.id}/route.ts
|
|
367
302
|
// Generated by create-sailor \u2014 OAuth callback for ${e.name}
|
|
368
303
|
// Primary auth provider: ${t}
|
|
369
|
-
// Docs: ${e.docs}`,
|
|
304
|
+
// Docs: ${e.docs}`,o={wechat:` const tokenRes = await fetch(
|
|
370
305
|
"${e.tokenEndpoint}?" +
|
|
371
306
|
new URLSearchParams({
|
|
372
307
|
appid: process.env.WECHAT_APP_ID ?? "",
|
|
@@ -467,7 +402,7 @@ SUPABASE_SERVICE_ROLE_KEY=
|
|
|
467
402
|
const token = (await tokenRes.json()) as { access_token?: string; uid?: string };
|
|
468
403
|
if (!token.access_token) {
|
|
469
404
|
return NextResponse.redirect(new URL("/login?error=weibo_token", req.url));
|
|
470
|
-
}`};return`${
|
|
405
|
+
}`};return`${n}
|
|
471
406
|
import { type NextRequest, NextResponse } from "next/server";
|
|
472
407
|
|
|
473
408
|
export async function GET(req: NextRequest) {
|
|
@@ -478,7 +413,7 @@ export async function GET(req: NextRequest) {
|
|
|
478
413
|
return NextResponse.redirect(new URL("/login?error=no_code", req.url));
|
|
479
414
|
}
|
|
480
415
|
|
|
481
|
-
${
|
|
416
|
+
${o[e.id]}
|
|
482
417
|
|
|
483
418
|
// TODO: fetch user profile from ${e.name} using the access token.
|
|
484
419
|
// TODO: upsert the user into your primary auth provider (${t}).
|
|
@@ -488,7 +423,7 @@ ${n[e.id]}
|
|
|
488
423
|
void state;
|
|
489
424
|
return NextResponse.redirect(new URL("/dashboard", req.url));
|
|
490
425
|
}
|
|
491
|
-
`}function
|
|
426
|
+
`}function xr(e){return`"use client";
|
|
492
427
|
|
|
493
428
|
/**
|
|
494
429
|
* SocialLoginButtons \u2014 generated by create-sailor.
|
|
@@ -508,7 +443,7 @@ interface SocialProvider {
|
|
|
508
443
|
}
|
|
509
444
|
|
|
510
445
|
const PROVIDERS: SocialProvider[] = [
|
|
511
|
-
${e.map(
|
|
446
|
+
${e.map(n=>` { id: ${JSON.stringify(n.id)}, name: ${JSON.stringify(n.name)}, href: ${JSON.stringify(`/api/auth/oauth/${n.id}`)} }`).join(`,
|
|
512
447
|
`)},
|
|
513
448
|
];
|
|
514
449
|
|
|
@@ -531,23 +466,23 @@ export function SocialLoginButtons() {
|
|
|
531
466
|
</div>
|
|
532
467
|
);
|
|
533
468
|
}
|
|
534
|
-
`}function
|
|
535
|
-
`+t):
|
|
469
|
+
`}function Jn(e){Ce.mkdirSync(e,{recursive:!0})}function Rr(e,t){let n=Ie.join(e,".env.example");Ce.existsSync(n)?Ce.appendFileSync(n,`
|
|
470
|
+
`+t):Ce.writeFileSync(n,t)}function kr(e){let t=`# ${e.name} (${e.id}) \u2014 ${e.docs}`,n=e.envVars.map(o=>`${o}=`).join(`
|
|
536
471
|
`);return`${t}
|
|
537
|
-
${
|
|
538
|
-
`}async function
|
|
539
|
-
`);
|
|
472
|
+
${n}
|
|
473
|
+
`}async function Qn(e,t,n){if(!t||t.length===0)return;let o=Ie.join(e,"apps","web");if(!Ce.existsSync(o))return;let s=new Set,r=[];for(let l of t){if(s.has(l))continue;s.add(l);let u=Un(l);u&&r.push(u)}if(r.length===0)return;for(let l of r)Rr(e,kr(l));let i=Ie.join(o,"src","app","api","auth","callback");for(let l of r){let u=Ie.join(i,l.id);Jn(u);let d=Ie.join(u,"route.ts");Ce.writeFileSync(d,Cr(l,n))}let a=Ie.join(o,"src","components","auth");Jn(a);let c=Ie.join(a,"SocialLoginButtons.tsx");Ce.writeFileSync(c,xr(r))}import ze from"fs";import eo from"path";var ht=[{id:"upstash-redis",name:"Upstash Redis",region:"both",envVars:["UPSTASH_REDIS_REST_URL","UPSTASH_REDIS_REST_TOKEN"],docs:"https://upstash.com/docs/redis",description:"Serverless Redis"},{id:"vercel-kv",name:"Vercel KV",region:"global",envVars:["KV_URL","KV_REST_API_URL","KV_REST_API_TOKEN","KV_REST_API_READ_ONLY_TOKEN"],docs:"https://vercel.com/docs/storage/vercel-kv",description:"Vercel native KV"},{id:"redis",name:"Redis (self-hosted)",region:"both",envVars:["REDIS_URL"],docs:"https://redis.io/docs/",description:"Self-hosted Redis"},{id:"dragonfly",name:"Dragonfly",region:"global",envVars:["DRAGONFLY_URL"],docs:"https://www.dragonflydb.io/docs",description:"Drop-in Redis replacement, faster"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:"Skip cache package"}];function Zn(e){return ht.find(t=>t.id===e)}var jp=ht.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});import ne from"fs";import Et from"path";function oe(e,t){let n=Et.join(e,".env.example");if(!ne.existsSync(n)||t.envVars.length===0)return;let s=`# ${t.category??t.name}: ${t.name}`;if(ne.readFileSync(n,"utf-8").includes(s))return;let a=["","# =============================================",s,t.docs?`# Docs: ${t.docs}`:null,"# =============================================",...t.envVars.map(c=>`${c}=""`),""].filter(c=>c!==null).join(`
|
|
474
|
+
`);ne.appendFileSync(n,a)}function se(e,t,n){let o=Et.join(e,".env.example");if(!ne.existsSync(o))return;let s=ne.readFileSync(o,"utf-8"),r=`${t}="${n}"`,i=new RegExp(`^${t}=.*$`,"m");if(i.test(s)){ne.writeFileSync(o,s.replace(i,r));return}let a=s.endsWith(`
|
|
540
475
|
`)?"":`
|
|
541
|
-
`;
|
|
542
|
-
`)}var
|
|
543
|
-
`);
|
|
544
|
-
`)}async function
|
|
545
|
-
`)[0]??""))return;
|
|
476
|
+
`;ne.appendFileSync(o,`${a}${r}
|
|
477
|
+
`)}var Tr=["design","iam","commerce","integrations","platform","ops","ai"];function xe(e,t){for(let o of Tr){let s=Et.join(e,"packages",o,t);if(ne.existsSync(s))return s}let n=Et.join(e,"packages",t);return ne.existsSync(n)?n:null}function z(e,t){let n=xe(e,t);n&&ne.rmSync(n,{recursive:!0,force:!0})}function to(e){return ze.existsSync(e)?ze.readFileSync(e,"utf-8"):null}function Nr(e,t){let n=eo.join(e,".env.example"),o=to(n);if(o===null)return;let s=`# Cache: ${t.name}`;if(o.includes(s))return;let r=["","# =============================================",s,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
478
|
+
`);ze.appendFileSync(n,r)}function Or(e,t,n){let o=eo.join(e,".env.example"),s=to(o);if(s===null)return;let r=new RegExp(`^${t}=.*$`,"m");r.test(s)?ze.writeFileSync(o,s.replace(r,`${t}="${n}"`)):ze.appendFileSync(o,`${t}="${n}"
|
|
479
|
+
`)}async function no(e,t,n){let o=xe(e,"cache");if(t==="none"){o&&ze.rmSync(o,{recursive:!0,force:!0});return}let s=Zn(t);if(!s)throw new Error(`Unknown cache provider "${t}". Valid ids: ${ht.map(r=>r.id).join(", ")}`);o&&(Nr(e,s),Or(e,"CACHE_PROVIDER",t))}var vt=[{id:"turnstile",name:"Cloudflare Turnstile",region:"global",envVars:["NEXT_PUBLIC_TURNSTILE_SITE_KEY","TURNSTILE_SECRET_KEY"],docs:"https://developers.cloudflare.com/turnstile/",description:"Free, privacy-focused captcha alternative"},{id:"hcaptcha",name:"hCaptcha",region:"global",envVars:["NEXT_PUBLIC_HCAPTCHA_SITE_KEY","HCAPTCHA_SECRET_KEY"],docs:"https://docs.hcaptcha.com/",description:"Privacy-respecting captcha"},{id:"aliyun-slide",name:"\u963F\u91CC\u4E91\u9A8C\u8BC1\u7801 2.0",region:"cn",envVars:["ALIYUN_CAPTCHA_ACCESS_KEY_ID","ALIYUN_CAPTCHA_ACCESS_KEY_SECRET","ALIYUN_CAPTCHA_APP_KEY","NEXT_PUBLIC_ALIYUN_CAPTCHA_APP_KEY"],docs:"https://help.aliyun.com/product/28308.html",description:"\u963F\u91CC\u4E91\u667A\u80FD\u9A8C\u8BC1 (\u6ED1\u5757/\u65E0\u75D5)"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function oo(e){return vt.find(t=>t.id===e)}var Xp=vt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function so(e,t,n="global"){let o=oo(t);if(!o)throw new Error(`Unknown captcha provider "${t}". Valid ids: ${vt.map(s=>s.id).join(", ")}`);if(o.id==="none"){z(e,"captcha");return}o.envVars.length>0&&oe(e,{category:"Captcha",name:o.name,envVars:o.envVars,docs:o.docs}),se(e,"CAPTCHA_PROVIDER",o.id)}import _t from"fs";import Lr from"path";var bt=[{id:"sanity",name:"Sanity",region:"global",envVars:["SANITY_PROJECT_ID","SANITY_DATASET","SANITY_API_TOKEN","SANITY_API_VERSION"],docs:"https://www.sanity.io/docs",description:"Structured content platform with Studio"},{id:"contentful",name:"Contentful",region:"global",envVars:["CONTENTFUL_SPACE_ID","CONTENTFUL_ACCESS_TOKEN","CONTENTFUL_PREVIEW_ACCESS_TOKEN"],docs:"https://www.contentful.com/developers/docs/",description:"Headless CMS"},{id:"strapi",name:"Strapi",region:"both",envVars:["STRAPI_URL","STRAPI_API_TOKEN"],docs:"https://docs.strapi.io",description:"Open-source self-hosted CMS"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function ro(e){return bt.find(t=>t.id===e)}var el=bt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function io(e,t,n="global"){let o=ro(t);if(!o)throw new Error(`Unknown CMS provider "${t}". Valid ids: ${bt.map(r=>r.id).join(", ")}`);let s=Lr.join(e,"apps","studio");if(o.id==="none"){z(e,"sanity"),_t.existsSync(s)&&_t.rmSync(s,{recursive:!0,force:!0});return}o.id!=="sanity"&&(z(e,"sanity"),_t.existsSync(s)&&_t.rmSync(s,{recursive:!0,force:!0})),o.envVars.length>0&&oe(e,{category:"CMS",name:o.name,envVars:o.envVars,docs:o.docs}),se(e,"CMS_PROVIDER",o.id)}import U from"fs";import O from"path";var Ur=O.join("apps","web"),sn=O.join("src","components","compliance"),it=O.join("src","app","[locale]");function re(e){return O.join(e,Ur)}function Mr(e){U.mkdirSync(e,{recursive:!0})}function J(e,t){Mr(O.dirname(e)),U.writeFileSync(e,t,"utf8")}function rn(e,t){let n=O.join(e,".env.example");if(U.existsSync(n)){if(U.readFileSync(n,"utf8").includes(t.split(`
|
|
480
|
+
`)[0]??""))return;U.appendFileSync(n,`
|
|
546
481
|
`+t+`
|
|
547
|
-
`)}else
|
|
548
|
-
`)}var
|
|
482
|
+
`)}else U.writeFileSync(n,t+`
|
|
483
|
+
`)}var at=`> **TEMPLATE \u2014 NOT LEGAL ADVICE.** This document is a boilerplate scaffold only.
|
|
549
484
|
> Consult a qualified lawyer in every jurisdiction you operate in before publishing.
|
|
550
|
-
> \u672C\u6587\u6863\u4E3A\u6A21\u677F\u811A\u624B\u67B6\uFF0C\u4E0D\u6784\u6210\u6CD5\u5F8B\u610F\u89C1\u3002\u53D1\u5E03\u524D\u8BF7\u54A8\u8BE2\u76F8\u5173\u53F8\u6CD5\u8F96\u533A\u7684\u5408\u683C\u5F8B\u5E08\u3002`,
|
|
485
|
+
> \u672C\u6587\u6863\u4E3A\u6A21\u677F\u811A\u624B\u67B6\uFF0C\u4E0D\u6784\u6210\u6CD5\u5F8B\u610F\u89C1\u3002\u53D1\u5E03\u524D\u8BF7\u54A8\u8BE2\u76F8\u5173\u53F8\u6CD5\u8F96\u533A\u7684\u5408\u683C\u5F8B\u5E08\u3002`,Dr=`import Link from "next/link";
|
|
551
486
|
import { IcpFooter } from "@/components/compliance/IcpFooter";
|
|
552
487
|
import { PublicSecurityFooter } from "@/components/compliance/PublicSecurityFooter";
|
|
553
488
|
|
|
@@ -598,7 +533,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
598
533
|
</footer>
|
|
599
534
|
);
|
|
600
535
|
}
|
|
601
|
-
`,
|
|
536
|
+
`,Fr=`export function IcpFooter() {
|
|
602
537
|
const icp = process.env.NEXT_PUBLIC_ICP_BEIAN;
|
|
603
538
|
if (!icp) return null;
|
|
604
539
|
return (
|
|
@@ -612,7 +547,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
612
547
|
</a>
|
|
613
548
|
);
|
|
614
549
|
}
|
|
615
|
-
`,
|
|
550
|
+
`,jr=`export function PublicSecurityFooter() {
|
|
616
551
|
const psb = process.env.NEXT_PUBLIC_PSB_BEIAN;
|
|
617
552
|
const psbCode = process.env.NEXT_PUBLIC_PSB_CODE;
|
|
618
553
|
if (!psb) return null;
|
|
@@ -629,9 +564,9 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
629
564
|
</a>
|
|
630
565
|
);
|
|
631
566
|
}
|
|
632
|
-
`,
|
|
567
|
+
`,Br=`"use client";
|
|
633
568
|
|
|
634
|
-
import {
|
|
569
|
+
import { useEffect, useState } from "react";
|
|
635
570
|
|
|
636
571
|
type ConsentMode = "accepted" | "rejected" | "custom";
|
|
637
572
|
|
|
@@ -660,41 +595,57 @@ const DEFAULT_CATEGORIES: CustomConsent = {
|
|
|
660
595
|
preferences: false,
|
|
661
596
|
};
|
|
662
597
|
|
|
598
|
+
function readStoredConsent(): StoredConsent | null {
|
|
599
|
+
if (typeof window === "undefined") return null;
|
|
600
|
+
|
|
601
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
602
|
+
if (!stored) return null;
|
|
603
|
+
|
|
604
|
+
try {
|
|
605
|
+
return JSON.parse(stored) as StoredConsent;
|
|
606
|
+
} catch {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function shouldOpenInitially(): boolean {
|
|
612
|
+
if (typeof window === "undefined") return false;
|
|
613
|
+
|
|
614
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
615
|
+
if (!stored) return true;
|
|
616
|
+
|
|
617
|
+
try {
|
|
618
|
+
JSON.parse(stored);
|
|
619
|
+
return false;
|
|
620
|
+
} catch {
|
|
621
|
+
return true;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
663
625
|
function emitConsent(consent: StoredConsent): void {
|
|
664
626
|
if (typeof window === "undefined") return;
|
|
665
627
|
window.dispatchEvent(new CustomEvent(CONSENT_EVENT, { detail: consent }));
|
|
666
628
|
}
|
|
667
629
|
|
|
668
630
|
export function CookieBanner() {
|
|
669
|
-
const [isOpen, setIsOpen] = useState(
|
|
670
|
-
const [showCustomize, setShowCustomize] = useState(false);
|
|
671
|
-
const [categories, setCategories] = useState<CustomConsent>(DEFAULT_CATEGORIES);
|
|
631
|
+
const [isOpen, setIsOpen] = useState(shouldOpenInitially);
|
|
672
632
|
|
|
673
633
|
useEffect(() => {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
setIsOpen(true);
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
const parsed = JSON.parse(stored) as StoredConsent;
|
|
681
|
-
// Re-emit so analytics/marketing scripts mounted after consent can read state
|
|
682
|
-
emitConsent(parsed);
|
|
683
|
-
} catch {
|
|
684
|
-
setIsOpen(true);
|
|
634
|
+
const stored = readStoredConsent();
|
|
635
|
+
if (stored) {
|
|
636
|
+
emitConsent(stored);
|
|
685
637
|
}
|
|
686
638
|
}, []);
|
|
687
639
|
|
|
688
640
|
useEffect(() => {
|
|
689
641
|
const handler = () => {
|
|
690
|
-
setShowCustomize(true);
|
|
691
642
|
setIsOpen(true);
|
|
692
643
|
};
|
|
693
644
|
window.addEventListener(OPEN_EVENT, handler);
|
|
694
645
|
return () => window.removeEventListener(OPEN_EVENT, handler);
|
|
695
646
|
}, []);
|
|
696
647
|
|
|
697
|
-
|
|
648
|
+
function persist(mode: ConsentMode, cats: CustomConsent) {
|
|
698
649
|
const consent: StoredConsent = {
|
|
699
650
|
mode,
|
|
700
651
|
categories: cats,
|
|
@@ -704,25 +655,20 @@ export function CookieBanner() {
|
|
|
704
655
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(consent));
|
|
705
656
|
emitConsent(consent);
|
|
706
657
|
setIsOpen(false);
|
|
707
|
-
|
|
708
|
-
}, []);
|
|
658
|
+
}
|
|
709
659
|
|
|
710
|
-
|
|
660
|
+
function acceptAll() {
|
|
711
661
|
persist("accepted", {
|
|
712
662
|
necessary: true,
|
|
713
663
|
analytics: true,
|
|
714
664
|
marketing: true,
|
|
715
665
|
preferences: true,
|
|
716
666
|
});
|
|
717
|
-
}
|
|
667
|
+
}
|
|
718
668
|
|
|
719
|
-
|
|
669
|
+
function saveNecessaryOnly() {
|
|
720
670
|
persist("rejected", DEFAULT_CATEGORIES);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
const saveCustom = useCallback(() => {
|
|
724
|
-
persist("custom", categories);
|
|
725
|
-
}, [persist, categories]);
|
|
671
|
+
}
|
|
726
672
|
|
|
727
673
|
if (!isOpen) return null;
|
|
728
674
|
|
|
@@ -731,132 +677,48 @@ export function CookieBanner() {
|
|
|
731
677
|
role="dialog"
|
|
732
678
|
aria-modal="false"
|
|
733
679
|
aria-labelledby="cookie-banner-title"
|
|
734
|
-
className="fixed inset-x-0 bottom-0 z-50 border-t border-[var(--neutral-
|
|
680
|
+
className="fixed inset-x-0 bottom-0 z-50 border-t border-[var(--neutral-6)] bg-[var(--neutral-1)]/95 text-[var(--neutral-12)] shadow-lg backdrop-blur-md"
|
|
735
681
|
>
|
|
736
|
-
<div className="mx-auto flex max-w-
|
|
737
|
-
<div>
|
|
738
|
-
<
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
\u3002
|
|
754
|
-
</p>
|
|
755
|
-
</div>
|
|
756
|
-
|
|
757
|
-
{showCustomize ? (
|
|
758
|
-
<fieldset className="grid gap-3 md:grid-cols-2">
|
|
759
|
-
<CategoryRow
|
|
760
|
-
id="necessary"
|
|
761
|
-
title="Necessary / \u5FC5\u8981"
|
|
762
|
-
description="Required for the site to function. Cannot be disabled. / \u7AD9\u70B9\u8FD0\u884C\u6240\u5FC5\u9700\uFF0C\u65E0\u6CD5\u5173\u95ED\u3002"
|
|
763
|
-
checked={true}
|
|
764
|
-
disabled
|
|
765
|
-
onChange={() => undefined}
|
|
766
|
-
/>
|
|
767
|
-
<CategoryRow
|
|
768
|
-
id="analytics"
|
|
769
|
-
title="Analytics / \u5206\u6790"
|
|
770
|
-
description="Helps us understand usage patterns. / \u5E2E\u52A9\u6211\u4EEC\u4E86\u89E3\u4F7F\u7528\u6A21\u5F0F\u3002"
|
|
771
|
-
checked={categories.analytics}
|
|
772
|
-
onChange={(v) => setCategories((c) => ({ ...c, analytics: v }))}
|
|
773
|
-
/>
|
|
774
|
-
<CategoryRow
|
|
775
|
-
id="marketing"
|
|
776
|
-
title="Marketing / \u8425\u9500"
|
|
777
|
-
description="Used for targeted advertising. / \u7528\u4E8E\u5B9A\u5411\u5E7F\u544A\u3002"
|
|
778
|
-
checked={categories.marketing}
|
|
779
|
-
onChange={(v) => setCategories((c) => ({ ...c, marketing: v }))}
|
|
780
|
-
/>
|
|
781
|
-
<CategoryRow
|
|
782
|
-
id="preferences"
|
|
783
|
-
title="Preferences / \u504F\u597D"
|
|
784
|
-
description="Remember your preferences (language, theme). / \u8BB0\u4F4F\u60A8\u7684\u504F\u597D\uFF08\u8BED\u8A00\u3001\u4E3B\u9898\uFF09\u3002"
|
|
785
|
-
checked={categories.preferences}
|
|
786
|
-
onChange={(v) => setCategories((c) => ({ ...c, preferences: v }))}
|
|
787
|
-
/>
|
|
788
|
-
</fieldset>
|
|
789
|
-
) : null}
|
|
682
|
+
<div className="mx-auto flex max-w-6xl flex-col gap-3 px-4 pb-[calc(0.75rem+env(safe-area-inset-bottom))] pt-3 sm:px-6 lg:px-8">
|
|
683
|
+
<div className="flex flex-col items-start gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
684
|
+
<div className="min-w-0 flex-1">
|
|
685
|
+
<h2 id="cookie-banner-title" className="text-base font-semibold text-[var(--neutral-12)]">
|
|
686
|
+
Your Privacy Choices / \u9690\u79C1\u504F\u597D
|
|
687
|
+
</h2>
|
|
688
|
+
<p className="mt-1 max-w-3xl text-sm leading-6 text-[var(--neutral-11)]">
|
|
689
|
+
Necessary cookies keep the site working. Optional cookies help us improve the product
|
|
690
|
+
and personalize content.{" "}
|
|
691
|
+
<a
|
|
692
|
+
href="/privacy"
|
|
693
|
+
className="font-medium text-[var(--brand-primary)] underline underline-offset-4 transition-colors hover:text-[var(--blue-10)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--blue-7)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--neutral-1)]"
|
|
694
|
+
>
|
|
695
|
+
Privacy Policy
|
|
696
|
+
</a>
|
|
697
|
+
</p>
|
|
698
|
+
</div>
|
|
790
699
|
|
|
791
|
-
|
|
792
|
-
{showCustomize ? (
|
|
700
|
+
<div className="flex w-full flex-col-reverse gap-2 sm:w-auto sm:flex-row sm:items-center">
|
|
793
701
|
<button
|
|
794
702
|
type="button"
|
|
795
|
-
onClick={
|
|
796
|
-
className="rounded-
|
|
703
|
+
onClick={saveNecessaryOnly}
|
|
704
|
+
className="inline-flex min-h-10 items-center justify-center rounded-[var(--radius-lg)] border border-[var(--neutral-7)] bg-[var(--neutral-1)] px-4 text-sm font-medium text-[var(--neutral-12)] transition-colors hover:border-[var(--neutral-8)] hover:bg-[var(--neutral-2)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--blue-7)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--neutral-1)]"
|
|
797
705
|
>
|
|
798
|
-
|
|
706
|
+
Only Necessary / \u4EC5\u5FC5\u8981
|
|
799
707
|
</button>
|
|
800
|
-
) : (
|
|
801
708
|
<button
|
|
802
709
|
type="button"
|
|
803
|
-
onClick={
|
|
804
|
-
className="rounded-
|
|
710
|
+
onClick={acceptAll}
|
|
711
|
+
className="inline-flex min-h-10 items-center justify-center rounded-[var(--radius-lg)] border border-[var(--neutral-12)] bg-[var(--neutral-12)] px-4 text-sm font-medium text-[var(--neutral-1)] transition-colors hover:bg-[var(--neutral-11)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--blue-7)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--neutral-1)]"
|
|
805
712
|
>
|
|
806
|
-
|
|
713
|
+
Accept All / \u5168\u90E8\u63A5\u53D7
|
|
807
714
|
</button>
|
|
808
|
-
|
|
809
|
-
<button
|
|
810
|
-
type="button"
|
|
811
|
-
onClick={rejectNonEssential}
|
|
812
|
-
className="rounded-md border border-[var(--neutral-7)] px-4 py-2 text-sm font-medium text-[var(--neutral-12)] hover:bg-[var(--neutral-3)]"
|
|
813
|
-
>
|
|
814
|
-
Reject Non-Essential / \u62D2\u7EDD\u975E\u5FC5\u8981
|
|
815
|
-
</button>
|
|
816
|
-
<button
|
|
817
|
-
type="button"
|
|
818
|
-
onClick={acceptAll}
|
|
819
|
-
className="rounded-md border border-[var(--neutral-12)] bg-[var(--neutral-12)] px-4 py-2 text-sm font-medium text-[var(--neutral-1)] shadow-sm hover:bg-[var(--neutral-11)]"
|
|
820
|
-
>
|
|
821
|
-
Accept All / \u5168\u90E8\u63A5\u53D7
|
|
822
|
-
</button>
|
|
715
|
+
</div>
|
|
823
716
|
</div>
|
|
824
717
|
</div>
|
|
825
718
|
</div>
|
|
826
719
|
);
|
|
827
720
|
}
|
|
828
|
-
|
|
829
|
-
interface CategoryRowProps {
|
|
830
|
-
id: string;
|
|
831
|
-
title: string;
|
|
832
|
-
description: string;
|
|
833
|
-
checked: boolean;
|
|
834
|
-
disabled?: boolean;
|
|
835
|
-
onChange: (value: boolean) => void;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
function CategoryRow({ id, title, description, checked, disabled, onChange }: CategoryRowProps) {
|
|
839
|
-
return (
|
|
840
|
-
<label
|
|
841
|
-
htmlFor={id}
|
|
842
|
-
className="flex items-start gap-3 rounded-md border border-[var(--neutral-7)] p-3"
|
|
843
|
-
>
|
|
844
|
-
<input
|
|
845
|
-
id={id}
|
|
846
|
-
type="checkbox"
|
|
847
|
-
checked={checked}
|
|
848
|
-
disabled={disabled}
|
|
849
|
-
onChange={(e) => onChange(e.target.checked)}
|
|
850
|
-
className="mt-0.5 h-4 w-4"
|
|
851
|
-
/>
|
|
852
|
-
<span className="flex flex-col">
|
|
853
|
-
<span className="text-sm font-medium text-[var(--neutral-12)]">{title}</span>
|
|
854
|
-
<span className="text-xs text-[var(--neutral-11)]">{description}</span>
|
|
855
|
-
</span>
|
|
856
|
-
</label>
|
|
857
|
-
);
|
|
858
|
-
}
|
|
859
|
-
`,ks=`interface AigcDisclaimerProps {
|
|
721
|
+
`,Vr=`interface AigcDisclaimerProps {
|
|
860
722
|
className?: string;
|
|
861
723
|
}
|
|
862
724
|
|
|
@@ -876,13 +738,13 @@ export function AigcDisclaimer({ className }: AigcDisclaimerProps) {
|
|
|
876
738
|
</div>
|
|
877
739
|
);
|
|
878
740
|
}
|
|
879
|
-
`,
|
|
741
|
+
`,Kr=`---
|
|
880
742
|
title: Privacy Policy / \u9690\u79C1\u653F\u7B56
|
|
881
743
|
description: How {{COMPANY_NAME}} collects, uses, shares, and protects personal information.
|
|
882
744
|
lastUpdated: 2026-01-01
|
|
883
745
|
---
|
|
884
746
|
|
|
885
|
-
${
|
|
747
|
+
${at}
|
|
886
748
|
|
|
887
749
|
# Privacy Policy / \u9690\u79C1\u653F\u7B56
|
|
888
750
|
|
|
@@ -1021,13 +883,13 @@ or in-app notification before the change takes effect.
|
|
|
1021
883
|
**Data Controller / \u6570\u636E\u63A7\u5236\u8005:** {{COMPANY_NAME}}
|
|
1022
884
|
**Address / \u5730\u5740:** {{COMPANY_ADDRESS}}
|
|
1023
885
|
**Email / \u90AE\u7BB1:** {{COMPANY_CONTACT_EMAIL}}
|
|
1024
|
-
|
|
886
|
+
`,$r=`---
|
|
1025
887
|
title: Terms of Service / \u670D\u52A1\u6761\u6B3E
|
|
1026
888
|
description: Terms governing your use of {{COMPANY_NAME}}.
|
|
1027
889
|
lastUpdated: 2026-01-01
|
|
1028
890
|
---
|
|
1029
891
|
|
|
1030
|
-
${
|
|
892
|
+
${at}
|
|
1031
893
|
|
|
1032
894
|
# Terms of Service / \u670D\u52A1\u6761\u6B3E
|
|
1033
895
|
|
|
@@ -1140,13 +1002,13 @@ conflict-of-laws principles. Disputes shall be resolved in the competent courts
|
|
|
1140
1002
|
## 11. Contact / \u8054\u7CFB\u65B9\u5F0F
|
|
1141
1003
|
|
|
1142
1004
|
{{COMPANY_NAME}} \xB7 {{COMPANY_ADDRESS}} \xB7 {{COMPANY_CONTACT_EMAIL}}
|
|
1143
|
-
`,
|
|
1005
|
+
`,Yr=`---
|
|
1144
1006
|
title: AIGC Disclosure / \u751F\u6210\u5F0F AI \u670D\u52A1\u8BF4\u660E
|
|
1145
1007
|
description: Disclosure required by \u7F51\u4FE1\u529E\u300A\u751F\u6210\u5F0F\u4EBA\u5DE5\u667A\u80FD\u670D\u52A1\u7BA1\u7406\u6682\u884C\u529E\u6CD5\u300B
|
|
1146
1008
|
lastUpdated: 2026-01-01
|
|
1147
1009
|
---
|
|
1148
1010
|
|
|
1149
|
-
${
|
|
1011
|
+
${at}
|
|
1150
1012
|
|
|
1151
1013
|
# AIGC Disclosure / \u751F\u6210\u5F0F AI \u670D\u52A1\u8BF4\u660E
|
|
1152
1014
|
|
|
@@ -1222,13 +1084,13 @@ human professional.
|
|
|
1222
1084
|
|
|
1223
1085
|
AI \u8F93\u51FA\u53EF\u80FD\u4E0D\u51C6\u786E\u3001\u5B58\u5728\u504F\u89C1\u6216\u8FC7\u65F6\u3002**\u8BF7\u52FF**\u5728\u672A\u7ECF\u5408\u683C\u4E13\u4E1A\u4EBA\u58EB\u72EC\u7ACB\u6838\u5B9E\u7684\u60C5\u51B5\u4E0B\uFF0C\u5C06 AI \u8F93\u51FA
|
|
1224
1086
|
\u7528\u4E8E\u533B\u7597\u3001\u6CD5\u5F8B\u3001\u91D1\u878D\u6216\u5B89\u5168\u6538\u5173\u7684\u51B3\u7B56\u3002
|
|
1225
|
-
`,
|
|
1087
|
+
`,qr=`---
|
|
1226
1088
|
title: Cross-Border Data Notice / \u6570\u636E\u51FA\u5883\u544A\u77E5
|
|
1227
1089
|
description: Notice of cross-border transfer of personal information, per PIPL.
|
|
1228
1090
|
lastUpdated: 2026-01-01
|
|
1229
1091
|
---
|
|
1230
1092
|
|
|
1231
|
-
${
|
|
1093
|
+
${at}
|
|
1232
1094
|
|
|
1233
1095
|
# Cross-Border Data Notice / \u6570\u636E\u51FA\u5883\u544A\u77E5
|
|
1234
1096
|
|
|
@@ -1287,13 +1149,13 @@ Refusal may limit your ability to use features that rely on overseas services.
|
|
|
1287
1149
|
- \u4F20\u8F93\u4E2D\u52A0\u5BC6 (TLS 1.2+) \u53CA\u9759\u6001\u52A0\u5BC6 (AES-256)\u3002
|
|
1288
1150
|
- \u5408\u540C\u6761\u6B3E\u8981\u6C42\u5883\u5916\u63A5\u6536\u65B9\u63D0\u4F9B\u7B49\u540C\u4E8E PIPL \u7684\u4FDD\u62A4\u3002
|
|
1289
1151
|
- \u8BBF\u95EE\u63A7\u5236\u3001\u5BA1\u8BA1\u65E5\u5FD7\u548C\u8FDD\u89C4\u901A\u77E5\u7A0B\u5E8F\u3002
|
|
1290
|
-
`,
|
|
1152
|
+
`,Gr=`---
|
|
1291
1153
|
title: Personal Information Collection List / \u4E2A\u4EBA\u4FE1\u606F\u6536\u96C6\u6E05\u5355
|
|
1292
1154
|
description: Itemized list of personal information collected, per PIPL Article 17.
|
|
1293
1155
|
lastUpdated: 2026-01-01
|
|
1294
1156
|
---
|
|
1295
1157
|
|
|
1296
|
-
${
|
|
1158
|
+
${at}
|
|
1297
1159
|
|
|
1298
1160
|
# Personal Information Collection List / \u4E2A\u4EBA\u4FE1\u606F\u6536\u96C6\u6E05\u5355
|
|
1299
1161
|
|
|
@@ -1329,26 +1191,26 @@ To exercise your rights (access, rectify, delete, export, withdraw consent), con
|
|
|
1329
1191
|
**{{COMPANY_CONTACT_EMAIL}}**.
|
|
1330
1192
|
|
|
1331
1193
|
\u5982\u9700\u884C\u4F7F\u60A8\u7684\u6743\u5229\uFF08\u8BBF\u95EE\u3001\u66F4\u6B63\u3001\u5220\u9664\u3001\u5BFC\u51FA\u3001\u64A4\u56DE\u540C\u610F\uFF09\uFF0C\u8BF7\u8054\u7CFB **{{COMPANY_CONTACT_EMAIL}}**\u3002
|
|
1332
|
-
`;async function
|
|
1333
|
-
`))}async function
|
|
1334
|
-
`))}async function
|
|
1335
|
-
`))}async function
|
|
1336
|
-
`))}async function
|
|
1337
|
-
`)}import R from"fs";import
|
|
1338
|
-
`+t):R.writeFileSync(
|
|
1339
|
-
DATABASE_URL="${
|
|
1340
|
-
`),!R.existsSync(
|
|
1341
|
-
${t}="${
|
|
1342
|
-
`:`${t}="${
|
|
1343
|
-
`,
|
|
1344
|
-
${i}`):R.writeFileSync(
|
|
1345
|
-
`),
|
|
1346
|
-
${
|
|
1347
|
-
`))}async function
|
|
1348
|
-
`+t):
|
|
1194
|
+
`;async function Hr(e,t){let n=re(e);if(!U.existsSync(n))return;let o=O.join(n,"src","components","compliance","Footer.tsx");J(o,Dr);let s=O.join(n,"src","components","compliance","README.md");J(s,["# Compliance Components","",`Auto-generated by \`create-sailor\` for region: \`${t}\`.`,"","## Mounting the Footer","","```tsx",'import { Footer } from "@/components/compliance/Footer";','import { CookieBanner } from "@/components/compliance/CookieBanner";',"","export default function RootLayout({ children }) {"," return ("," <html>"," <body>"," {children}",` <Footer region="${t}" />`," <CookieBanner />"," </body>"," </html>"," );","}","```","","## Placeholders","","Policy MDX files contain `{{COMPANY_NAME}}`, `{{COMPANY_ADDRESS}}`,","`{{COMPANY_CONTACT_EMAIL}}`, `{{GOVERNING_JURISDICTION}}`, and","`{{AIGC_ALGORITHM_FILING_NUMBER}}`. These are filled by the `brand-apply` step.",""].join(`
|
|
1195
|
+
`))}async function Wr(e,t){let n=re(e);if(!U.existsSync(n))return;let o=O.join(n,"src","components","compliance","CookieBanner.tsx");J(o,Br)}async function Xr(e,t){let n=re(e);if(!U.existsSync(n))return;let o=O.join(n,it,"privacy","page.mdx");J(o,Kr)}async function zr(e,t){let n=re(e);if(!U.existsSync(n))return;let o=O.join(n,it,"terms","page.mdx");J(o,$r)}async function Jr(e){let t=re(e);if(!U.existsSync(t))return;let n=O.join(t,sn,"IcpFooter.tsx");J(n,Fr),rn(e,["# Chinese ICP filing (\u5DE5\u4FE1\u90E8 ICP \u5907\u6848) \u2014 required to legally host a","# public-facing site from mainland China. Apply at https://beian.miit.gov.cn/",'NEXT_PUBLIC_ICP_BEIAN="" # \u5982 \u82CFICP\u59072024XXXXXX\u53F7'].join(`
|
|
1196
|
+
`))}async function Qr(e){let t=re(e);if(!U.existsSync(t))return;let n=O.join(t,sn,"PublicSecurityFooter.tsx");J(n,jr),rn(e,["# Chinese Public Security Bureau filing (\u516C\u5B89\u8054\u7F51\u5907\u6848)","# Required in addition to ICP. Apply at http://www.beian.gov.cn/",'NEXT_PUBLIC_PSB_BEIAN="" # \u5982 \u82CF\u516C\u7F51\u5B89\u5907 32011402XXXXXX\u53F7','NEXT_PUBLIC_PSB_CODE="" # 14-digit record code'].join(`
|
|
1197
|
+
`))}async function Zr(e){let t=re(e);if(!U.existsSync(t))return;let n=O.join(t,sn,"AigcDisclaimer.tsx");J(n,Vr),rn(e,["# Generative AI algorithm filing (\u7B97\u6CD5\u5907\u6848\u53F7) \u2014 required by","# \u7F51\u4FE1\u529E\u300A\u751F\u6210\u5F0F\u4EBA\u5DE5\u667A\u80FD\u670D\u52A1\u7BA1\u7406\u6682\u884C\u529E\u6CD5\u300Bfor services offered in China.",'NEXT_PUBLIC_AIGC_FILING_NUMBER="" # e.g. \u7F51\u4FE1\u7B97\u5907XXXXXXXX\u53F7'].join(`
|
|
1198
|
+
`))}async function ei(e){let t=re(e);if(!U.existsSync(t))return;let n=O.join(t,it,"aigc-disclosure","page.mdx");J(n,Yr)}async function ti(e){let t=re(e);if(!U.existsSync(t))return;let n=O.join(t,it,"cross-border-data","page.mdx");J(n,qr)}async function ni(e){let t=re(e);if(!U.existsSync(t))return;let n=O.join(t,it,"pii-collection","page.mdx");J(n,Gr)}async function ao(e,t){try{await Hr(e,t),await Wr(e,t),await Xr(e,t),await zr(e,t),(t==="cn"||t==="hybrid")&&(await Jr(e),await Qr(e),await Zr(e),await ei(e),await ti(e),await ni(e))}catch(n){throw console.error("applyComplianceTemplates failed:",n),new Error(`Failed to apply compliance templates for region "${t}". Check that ${e} is writable.`)}}import oi from"fs";import si from"path";async function co(e,t){let n=si.join(e,"nebutra.config.json");oi.writeFileSync(n,JSON.stringify(t,null,2)+`
|
|
1199
|
+
`)}import R from"fs";import Re from"path";function ri(e){R.existsSync(e)&&R.rmSync(e,{recursive:!0,force:!0})}function po(e,t){return e.replace(/provider\s*=\s*"postgresql"/,`provider = "${t}"`)}function ii(e){return e.replace(/^\s*directUrl\s*=\s*env\([^)]*\)\s*\n/gm,"")}function lo(e){let t=e.replace(/^\s*extensions\s*=\s*\[[^\]]*\]\s*\n/gm,"");return t=t.replace(/previewFeatures\s*=\s*\[\s*"postgresqlExtensions"\s*\]\s*\n/g,""),t}function ai(e,t){let n=Re.join(e,".env.example");R.existsSync(n)?R.appendFileSync(n,`
|
|
1200
|
+
`+t):R.writeFileSync(n,t)}async function uo(e,t,n){let o=Re.join(e,"packages","platform","db");if(t==="none"){ri(o);return}let s=Re.join(o,"prisma","schema.prisma"),r;if(t==="postgresql"?r=`postgresql://postgres:postgres@localhost:5432/${n}`:t==="mysql"?r=`mysql://root:root@localhost:3306/${n}`:r="file:./dev.db",ai(e,`# Database (${t})
|
|
1201
|
+
DATABASE_URL="${r}"
|
|
1202
|
+
`),!R.existsSync(s))return;let i=R.readFileSync(s,"utf8"),a=i;t==="mysql"?(a=po(a,"mysql"),a=lo(a)):t==="sqlite"&&(a=po(a,"sqlite"),a=ii(a),a=lo(a)),a!==i&&R.writeFileSync(s,a)}function ci(e){let t=Re.join(e,".env.example");return R.existsSync(t)?R.readFileSync(t,"utf8"):""}function pi(e,t,n,o){let s=Re.join(e,".env.example"),r=ci(e),i=o?`# ${o}
|
|
1203
|
+
${t}="${n}"
|
|
1204
|
+
`:`${t}="${n}"
|
|
1205
|
+
`,a=new RegExp(`^\\s*${t}\\s*=.*$`,"m");if(a.test(r)){R.writeFileSync(s,r.replace(a,`${t}="${n}"`));return}r?R.appendFileSync(s,`
|
|
1206
|
+
${i}`):R.writeFileSync(s,i)}function li(e,t){let n=Re.join(e,"packages","platform","db","prisma","schema.prisma");if(!R.existsSync(n))return;let o=R.readFileSync(n,"utf8"),s=/(datasource\s+db\s*\{[^}]*?)(\n})/;if(!o.match(s))return;let i=t.filter(l=>!o.includes(l.trim()));if(i.length===0)return;let a=i.map(l=>` ${l}`).join(`
|
|
1207
|
+
`),c=o.replace(s,`$1
|
|
1208
|
+
${a}$2`);R.writeFileSync(n,c)}function di(e){let t=Re.join(e,"packages","platform","db","prisma","schema.prisma");if(!R.existsSync(t))return;let n=R.readFileSync(t,"utf8");if(/directUrl\s*=\s*env\(/.test(n))return;let o=/(datasource\s+db\s*\{[\s\S]*?url\s*=\s*env\(\s*"DATABASE_URL"\s*\)[^\n]*\n)/;o.test(n)&&R.writeFileSync(t,n.replace(o,`$1 directUrl = env("DIRECT_URL")
|
|
1209
|
+
`))}async function mo(e,t,n){if(!(t.id==="none"||t.id==="local")){for(let o of t.envVars){let s=o.placeholder.replace(/\$PROJECT/g,n);pi(e,o.name,s,o.comment)}t.prismaDatasourceExtras&&t.prismaDatasourceExtras.length>0&&li(e,t.prismaDatasourceExtras),t.keepDirectUrl&&di(e)}}import ve from"fs";import ke from"path";import{fileURLToPath as ui}from"url";var mi=ui(import.meta.url),go=ke.dirname(mi),gi=/\.(md|mdx|json|ts|tsx|js|mjs|cjs|yaml|yml|css|html|vue)$/,fo=["fumadocs","nextra","mintlify","docusaurus","vitepress"];function fi(e){let t=[ke.join(go,"..","templates","docs",e),ke.join(go,"..","..","templates","docs",e)];for(let n of t)if(ve.existsSync(n))return n;return t[0]}async function yo(e,t){if(t.framework==="none")return;if(!fo.includes(t.framework))throw new Error(`Unsupported docs framework: "${t.framework}". Supported: ${fo.join(", ")}, none.`);let n=fi(t.framework),o=ke.join(e,"apps","docs");if(!ve.existsSync(n))throw new Error(`Docs template not found: ${n}`);So(n,o),ho(o,{"{PRODUCT_NAME}":t.projectName})}function So(e,t){ve.mkdirSync(t,{recursive:!0});for(let n of ve.readdirSync(e,{withFileTypes:!0})){let o=ke.join(e,n.name),s=ke.join(t,n.name);n.isDirectory()?So(o,s):ve.copyFileSync(o,s)}}function ho(e,t){for(let n of ve.readdirSync(e,{withFileTypes:!0})){let o=ke.join(e,n.name);if(n.isDirectory())ho(o,t);else if(gi.test(n.name)){let s=ve.readFileSync(o,"utf-8");for(let[r,i]of Object.entries(t))s=s.split(r).join(i);ve.writeFileSync(o,s)}}}import Ne from"fs";import Te from"path";var Pt=[{id:"resend",name:"Resend",region:"global",envVars:["RESEND_API_KEY","RESEND_FROM"],docs:"https://resend.com/docs",pkg:"resend",pkgVersion:"^4.0.1"},{id:"postmark",name:"Postmark",region:"global",envVars:["POSTMARK_API_KEY","POSTMARK_FROM"],docs:"https://postmarkapp.com/developer",pkg:"postmark",pkgVersion:"^4.0.5"},{id:"ses",name:"AWS SES",region:"global",envVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_SES_REGION","SES_FROM"],docs:"https://docs.aws.amazon.com/ses",pkg:"@aws-sdk/client-sesv2",pkgVersion:"^3.658.0"},{id:"aliyun-dm",name:"\u963F\u91CC\u4E91\u90AE\u4EF6\u63A8\u9001",region:"cn",envVars:["ALIYUN_DM_ACCESS_KEY","ALIYUN_DM_ACCESS_SECRET","ALIYUN_DM_ACCOUNT_NAME","ALIYUN_DM_FROM_ALIAS"],docs:"https://help.aliyun.com/product/29412.html",pkg:"@alicloud/dm20151123",pkgVersion:"^3.0.0"},{id:"tencent-ses",name:"\u817E\u8BAF\u4E91 SES",region:"cn",envVars:["TENCENT_SECRET_ID","TENCENT_SECRET_KEY","TENCENT_SES_FROM_EMAIL"],docs:"https://cloud.tencent.com/document/product/1288",pkg:"tencentcloud-sdk-nodejs-ses",pkgVersion:"^4.0.900"},{id:"netease",name:"\u7F51\u6613\u4F01\u4E1A\u90AE\u7BB1",region:"cn",envVars:["NETEASE_USER","NETEASE_PASS","NETEASE_FROM"],docs:"https://qiye.163.com/help",pkg:"nodemailer",pkgVersion:"^6.9.15"},{id:"none",name:"None",region:"global",envVars:[],docs:""}];function Eo(e){return Pt.find(t=>t.id===e)}var bl=Pt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});var Oe="@nebutra/email";function an(e,t){Ne.mkdirSync(Te.dirname(e),{recursive:!0}),Ne.writeFileSync(e,t)}function yi(e,t){let n=Te.join(e,".env.example");Ne.existsSync(n)?Ne.appendFileSync(n,`
|
|
1210
|
+
`+t):Ne.writeFileSync(n,t)}function Si(e){if(e.envVars.length===0)return"";let t=`# Email provider: ${e.name}`,n=e.envVars.map(o=>`${o}=`).join(`
|
|
1349
1211
|
`);return`${t}
|
|
1350
|
-
${
|
|
1351
|
-
`}var
|
|
1212
|
+
${n}
|
|
1213
|
+
`}var hi=`/**
|
|
1352
1214
|
* Public inputs accepted by the \`sendEmail\` wrapper.
|
|
1353
1215
|
* Keep the surface minimal \u2014 individual providers may accept more, but
|
|
1354
1216
|
* consumers should code against this shape for portability.
|
|
@@ -1368,7 +1230,7 @@ export interface SendEmailResult {
|
|
|
1368
1230
|
/** Raw provider response \u2014 useful for debugging, not a stable contract. */
|
|
1369
1231
|
raw?: unknown;
|
|
1370
1232
|
}
|
|
1371
|
-
`;function
|
|
1233
|
+
`;function Ei(){return`/**
|
|
1372
1234
|
* @${Oe.slice(1)} \u2014 Resend transactional email wrapper.
|
|
1373
1235
|
*
|
|
1374
1236
|
* Docs: https://resend.com/docs
|
|
@@ -1407,7 +1269,7 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1407
1269
|
}
|
|
1408
1270
|
|
|
1409
1271
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1410
|
-
`}function
|
|
1272
|
+
`}function vi(){return`/**
|
|
1411
1273
|
* @${Oe.slice(1)} \u2014 Postmark transactional email wrapper.
|
|
1412
1274
|
*
|
|
1413
1275
|
* Docs: https://postmarkapp.com/developer
|
|
@@ -1445,7 +1307,7 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1445
1307
|
}
|
|
1446
1308
|
|
|
1447
1309
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1448
|
-
`}function
|
|
1310
|
+
`}function bi(){return`/**
|
|
1449
1311
|
* @${Oe.slice(1)} \u2014 AWS SES v2 transactional email wrapper.
|
|
1450
1312
|
*
|
|
1451
1313
|
* Docs: https://docs.aws.amazon.com/ses
|
|
@@ -1501,7 +1363,7 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1501
1363
|
}
|
|
1502
1364
|
|
|
1503
1365
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1504
|
-
`}function
|
|
1366
|
+
`}function _i(){return`/**
|
|
1505
1367
|
* @${Oe.slice(1)} \u2014 \u963F\u91CC\u4E91\u90AE\u4EF6\u63A8\u9001 (Aliyun DirectMail) wrapper.
|
|
1506
1368
|
*
|
|
1507
1369
|
* Docs: https://help.aliyun.com/product/29412.html
|
|
@@ -1551,7 +1413,7 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1551
1413
|
}
|
|
1552
1414
|
|
|
1553
1415
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1554
|
-
`}function
|
|
1416
|
+
`}function Pi(){return`/**
|
|
1555
1417
|
* @${Oe.slice(1)} \u2014 \u817E\u8BAF\u4E91 SES wrapper.
|
|
1556
1418
|
*
|
|
1557
1419
|
* Docs: https://cloud.tencent.com/document/product/1288
|
|
@@ -1599,7 +1461,7 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1599
1461
|
}
|
|
1600
1462
|
|
|
1601
1463
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1602
|
-
`}function
|
|
1464
|
+
`}function wi(){return`/**
|
|
1603
1465
|
* @${Oe.slice(1)} \u2014 \u7F51\u6613\u4F01\u4E1A\u90AE\u7BB1 via SMTP (nodemailer).
|
|
1604
1466
|
*
|
|
1605
1467
|
* Docs: https://qiye.163.com/help
|
|
@@ -1637,32 +1499,33 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1637
1499
|
}
|
|
1638
1500
|
|
|
1639
1501
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1640
|
-
`}function
|
|
1641
|
-
`}}function
|
|
1642
|
-
`}async function
|
|
1502
|
+
`}function Ai(e){switch(e){case"resend":return Ei();case"postmark":return vi();case"ses":return bi();case"aliyun-dm":return _i();case"tencent-ses":return Pi();case"netease":return wi();case"none":return`export {};
|
|
1503
|
+
`}}function Ii(e){let t={};e.pkg&&e.pkgVersion&&(t[e.pkg]=e.pkgVersion),e.id==="aliyun-dm"&&(t["@alicloud/openapi-client"]="^0.4.12");let n={name:Oe,version:"0.1.0",private:!0,description:`Transactional email via ${e.name}`,type:"module",main:"./dist/index.js",exports:{".":{import:"./dist/index.js",types:"./dist/index.d.ts"}},scripts:{build:"tsup",typecheck:"tsc --noEmit",clean:"rm -rf dist .turbo"},dependencies:t,devDependencies:{tsup:"^8.5.0",typescript:"catalog:"}};return JSON.stringify(n,null,2)+`
|
|
1504
|
+
`}async function vo(e,t,n){if(t==="none")return;let o=Eo(t);if(!o)throw new Error(`Unknown email provider "${t}". Valid ids: ${Pt.map(i=>i.id).join(", ")}`);let s=Te.join(e,"packages","email"),r=Te.join(s,"src");Ne.existsSync(r)&&Ne.rmSync(r,{recursive:!0,force:!0}),an(Te.join(s,"package.json"),Ii(o)),an(Te.join(r,"types.ts"),hi),an(Te.join(r,"index.ts"),Ai(o.id)),yi(e,Si(o))}import wt from"fs";import bo from"path";async function _o(e,t){let n=`# Automatically injected by create-sailor
|
|
1643
1505
|
DATABASE_URL="${String(t.databaseUrl)}"
|
|
1644
1506
|
|
|
1645
1507
|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="${String(t.clerkPublishable)}"
|
|
1646
1508
|
CLERK_SECRET_KEY="${String(t.clerkSecret)}"
|
|
1647
1509
|
|
|
1648
1510
|
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
|
|
1649
|
-
`,
|
|
1650
|
-
`+
|
|
1651
|
-
`))}catch{}try{fo(e,{recursive:!0}),yo(t,"")}catch{}}import{execFileSync as _o}from"child_process";import D from"fs";import Ii from"os";import fe from"path";import wi from"ignore";var Ri={mirror:{repo:process.env.SAILOR_TEMPLATE_REPO||"Nebutra/Sailor-Template",ref:process.env.SAILOR_TEMPLATE_REF||"main",applyIgnore:!1,allowMutableFallback:process.env.SAILOR_TEMPLATE_ALLOW_MUTABLE_FALLBACK==="1"},main:{repo:"Nebutra/Nebutra-Sailor",ref:"main",applyIgnore:!0,allowMutableFallback:process.env.SAILOR_TEMPLATE_ALLOW_MUTABLE_FALLBACK==="1"}};function ki(e,t){return`https://github.com/${e}/archive/refs/heads/${t}.tar.gz`}function xi(e,t){return`https://github.com/${e}/archive/${t}.tar.gz`}function bo(e){return/^[0-9a-f]{7,40}$/i.test(e)}function Ni(e,t){let o=`https://github.com/${e}.git`,n=[`refs/heads/${t}`,`refs/tags/${t}`];for(let r of n)try{let s=_o("git",["ls-remote",o,r],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(!s)continue;let[i]=s.split(/\s+/);if(bo(i))return i}catch{}return null}async function Ti(e,t){if(bo(t))return t;let o=await fetch(`https://api.github.com/repos/${e}/commits/${encodeURIComponent(t)}`,{headers:{Accept:"application/vnd.github+json","User-Agent":"create-sailor"}});if(!o.ok){let r=Ni(e,t);if(r)return r;throw new Error(`Failed to resolve ${e}@${t} (GitHub API ${o.status})`)}let n=await o.json();if(typeof n.sha!="string"||n.sha.length===0)throw new Error(`GitHub API did not return a commit SHA for ${e}@${t}`);return n.sha}function Po(e,t,o){let n=D.readdirSync(t,{withFileTypes:!0});for(let r of n){if(r.name==="node_modules"||r.name===".git")continue;let s=fe.join(t,r.name),i=fe.relative(e,s).split(fe.sep).join("/");r.isDirectory()?(o.push(i+"/"),Po(e,s,o)):o.push(i)}}function Oi(e){let t=fe.join(e,".templateignore");if(!D.existsSync(t))return;let o=D.readFileSync(t,"utf8"),n=wi().add(o),r=[];Po(e,e,r);let s=r.map(a=>a.endsWith("/")?a.slice(0,-1):a),i=new Set(n.filter(s)),c=s.filter(a=>!i.has(a)).sort((a,l)=>l.split("/").length-a.split("/").length);for(let a of c){let l=fe.join(e,a);try{D.existsSync(l)&&D.rmSync(l,{recursive:!0,force:!0})}catch{}}try{D.existsSync(t)&&D.rmSync(t,{force:!0})}catch{}}async function Eo(e,t){let o=await fetch(e,{headers:{Accept:"application/octet-stream","User-Agent":"create-sailor"}});if(!o.ok)throw new Error(`Failed to download archive (${o.status})`);let n=Buffer.from(await o.arrayBuffer());if(n.byteLength<1024)throw new Error(`Downloaded archive is unexpectedly small (${n.byteLength} bytes)`);D.writeFileSync(t,n);let r=D.statSync(t);if(r.size!==n.byteLength)throw new Error(`Archive write verification failed (${r.size} != ${n.byteLength})`);return r.size}function vo(e,t){_o("tar",["-xzf",e,"-C",t,"--strip-components=1"],{stdio:"ignore"})}function Ao(e){try{let t=D.readdirSync(e);for(let o of t)D.rmSync(fe.join(e,o),{recursive:!0,force:!0})}catch{}}async function Li(e,t){let o=D.mkdtempSync(fe.join(Ii.tmpdir(),"create-sailor-")),n=fe.join(o,"template.tar.gz");try{let r=await Ti(e.repo,e.ref);await Eo(xi(e.repo,r),n),vo(n,t)}catch(r){if(!e.allowMutableFallback)throw r;Ao(t),await Eo(ki(e.repo,e.ref),n),vo(n,t)}finally{D.rmSync(o,{recursive:!0,force:!0})}}async function Co(e){D.mkdirSync(e,{recursive:!0});let o=process.env.SAILOR_TEMPLATE_SOURCE==="main"?["main"]:["mirror","main"],n;for(let r of o){let s=Ri[r];try{if(await Li(s,e),s.applyIgnore)try{Oi(e)}catch(i){throw new Error(`Template cloned, but .templateignore processing failed: ${i instanceof Error?i.message:String(i)}`)}return}catch(i){n=i instanceof Error?i:new Error(String(i)),Ao(e)}}throw new Error(`Failed to download the template from any source. Last error: ${n?.message??"unknown"}
|
|
1511
|
+
`,o=bo.join(e,".env"),s=bo.join(e,".env.local");!wt.existsSync(o)&&!wt.existsSync(s)?wt.writeFileSync(s,n):wt.appendFileSync(s,`
|
|
1512
|
+
`+n)}import wo from"crypto";import ct from"fs";import Po from"path";function Le(e){return wo.randomBytes(e).toString("base64").replace(/=+$/,"")}function Ci(e){return wo.randomBytes(e).toString("hex")}function xi(){return{AUTH_SECRET:Le(32),NEXTAUTH_SECRET:Le(32),BETTER_AUTH_SECRET:Le(32),JWT_SECRET:Le(32),WEBHOOK_SECRET:Ci(16),ENCRYPTION_KEY:Le(32),API_SECRET_KEY:Le(32),SESSION_SECRET:Le(32)}}function Ri(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function ki(e,t,n){let o=new RegExp(`^(?!#)\\s*${t}=.*$`,"gm"),s=Ri(n);return e.replace(o,`${t}="${s}"`)}async function Ao(e){let t=Po.join(e,".env.example"),n=Po.join(e,".env.local");if(!ct.existsSync(t))return null;try{let o=ct.readFileSync(t,"utf8"),s=xi(),i=ct.existsSync(n)?ct.readFileSync(n,"utf8"):o;for(let a of Object.keys(s))i=ki(i,a,s[a]);return ct.writeFileSync(n,i),s}catch(o){let s=o instanceof Error?o.message:String(o);throw console.error(`[create-sailor] Failed to generate env secrets: ${s}`),new Error(`Unable to write .env.local: ${s}`)}}var At=[{id:"vercel-flags",name:"Vercel Flags SDK",region:"global",envVars:["FLAGS_SECRET"],docs:"https://vercel.com/docs/workflow-collaboration/feature-flags",description:"Native Vercel feature flags"},{id:"growthbook",name:"GrowthBook",region:"both",envVars:["GROWTHBOOK_API_HOST","GROWTHBOOK_CLIENT_KEY","GROWTHBOOK_DECRYPTION_KEY"],docs:"https://docs.growthbook.io",description:"Open-source A/B testing + flags"},{id:"configcat",name:"ConfigCat",region:"global",envVars:["CONFIGCAT_SDK_KEY"],docs:"https://configcat.com/docs/",description:"Feature flag & config management"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function Io(e){return At.find(t=>t.id===e)}var Ll=At.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Co(e,t,n="global"){let o=Io(t);if(!o)throw new Error(`Unknown feature-flags provider "${t}". Valid ids: ${At.map(s=>s.id).join(", ")}`);if(o.id==="none"){z(e,"feature-flags");return}o.envVars.length>0&&oe(e,{category:"Feature Flags",name:o.name,envVars:o.envVars,docs:o.docs}),se(e,"FEATURE_FLAGS_PROVIDER",o.id)}import{execFileSync as To}from"child_process";import L from"fs";import Ti from"os";import ie from"path";import Ni from"ignore";var Oi={mirror:{repo:process.env.SAILOR_TEMPLATE_REPO||"Nebutra/Sailor-Template",ref:process.env.SAILOR_TEMPLATE_REF||"main",applyIgnore:!1,allowMutableFallback:process.env.SAILOR_TEMPLATE_ALLOW_MUTABLE_FALLBACK==="1"},main:{repo:"Nebutra/Nebutra-Sailor",ref:"main",applyIgnore:!0,allowMutableFallback:process.env.SAILOR_TEMPLATE_ALLOW_MUTABLE_FALLBACK==="1"}};function Li(){let e=process.env.SAILOR_TEMPLATE_LOCAL_DIR;if(!(!e||e.trim().length===0))return ie.resolve(e.trim())}var Ui=new Set(["node_modules",".git","dist",".next",".turbo"]);function Mi(e,t){if(!L.statSync(e).isDirectory())throw new Error(`SAILOR_TEMPLATE_LOCAL_DIR is not a directory: ${e}`);L.cpSync(e,t,{recursive:!0,dereference:!0,filter:o=>{let s=ie.basename(o);return!Ui.has(s)}})}function Di(e,t){return`https://github.com/${e}/archive/refs/heads/${t}.tar.gz`}function Fi(e,t){return`https://github.com/${e}/archive/${t}.tar.gz`}function No(e){return/^[0-9a-f]{7,40}$/i.test(e)}function ji(e,t){let n=`https://github.com/${e}.git`,o=[`refs/heads/${t}`,`refs/tags/${t}`];for(let s of o)try{let r=To("git",["ls-remote",n,s],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(!r)continue;let[i]=r.split(/\s+/);if(No(i))return i}catch{}return null}async function Bi(e,t){if(No(t))return t;let n=await fetch(`https://api.github.com/repos/${e}/commits/${encodeURIComponent(t)}`,{headers:{Accept:"application/vnd.github+json","User-Agent":"create-sailor"}});if(!n.ok){let s=ji(e,t);if(s)return s;throw new Error(`Failed to resolve ${e}@${t} (GitHub API ${n.status})`)}let o=await n.json();if(typeof o.sha!="string"||o.sha.length===0)throw new Error(`GitHub API did not return a commit SHA for ${e}@${t}`);return o.sha}function Oo(e,t,n){let o=L.readdirSync(t,{withFileTypes:!0});for(let s of o){if(s.name==="node_modules"||s.name===".git")continue;let r=ie.join(t,s.name),i=ie.relative(e,r).split(ie.sep).join("/");s.isDirectory()?(n.push(i+"/"),Oo(e,r,n)):n.push(i)}}function xo(e){let t=ie.join(e,".templateignore");if(!L.existsSync(t))return;let n=L.readFileSync(t,"utf8"),o=Ni().add(n),s=[];Oo(e,e,s);let r=s.map(c=>c.endsWith("/")?c.slice(0,-1):c),i=new Set(o.filter(r)),a=r.filter(c=>!i.has(c)).sort((c,l)=>l.split("/").length-c.split("/").length);for(let c of a){let l=ie.join(e,c);try{L.existsSync(l)&&L.rmSync(l,{recursive:!0,force:!0})}catch{}}try{L.existsSync(t)&&L.rmSync(t,{force:!0})}catch{}}async function Ro(e,t){let n=await fetch(e,{headers:{Accept:"application/octet-stream","User-Agent":"create-sailor"}});if(!n.ok)throw new Error(`Failed to download archive (${n.status})`);let o=Buffer.from(await n.arrayBuffer());if(o.byteLength<1024)throw new Error(`Downloaded archive is unexpectedly small (${o.byteLength} bytes)`);L.writeFileSync(t,o);let s=L.statSync(t);if(s.size!==o.byteLength)throw new Error(`Archive write verification failed (${s.size} != ${o.byteLength})`);return s.size}function ko(e,t){To("tar",["-xzf",e,"-C",t,"--strip-components=1"],{stdio:"ignore"})}function cn(e){try{let t=L.readdirSync(e);for(let n of t)L.rmSync(ie.join(e,n),{recursive:!0,force:!0})}catch{}}async function Vi(e,t){let n=L.mkdtempSync(ie.join(Ti.tmpdir(),"create-sailor-")),o=ie.join(n,"template.tar.gz");try{let s=await Bi(e.repo,e.ref);await Ro(Fi(e.repo,s),o),ko(o,t)}catch(s){if(!e.allowMutableFallback)throw s;cn(t),await Ro(Di(e.repo,e.ref),o),ko(o,t)}finally{L.rmSync(n,{recursive:!0,force:!0})}}async function Lo(e){L.mkdirSync(e,{recursive:!0});let t=Li();if(t){if(!L.existsSync(t))throw new Error(`SAILOR_TEMPLATE_LOCAL_DIR does not exist: ${t}`);cn(e),Mi(t,e);try{xo(e)}catch(r){throw new Error(`Local template copied, but .templateignore processing failed: ${r instanceof Error?r.message:String(r)}`)}return}let o=process.env.SAILOR_TEMPLATE_SOURCE==="main"?["main"]:["mirror","main"],s;for(let r of o){let i=Oi[r];try{if(await Vi(i,e),i.applyIgnore)try{xo(e)}catch(a){throw new Error(`Template cloned, but .templateignore processing failed: ${a instanceof Error?a.message:String(a)}`)}return}catch(a){s=a instanceof Error?a:new Error(String(a)),cn(e)}}throw new Error(`Failed to download the template from any source. Last error: ${s?.message??"unknown"}
|
|
1652
1513
|
|
|
1653
1514
|
Ensure you have internet access and 'tar' installed.
|
|
1654
|
-
You can override the source with SAILOR_TEMPLATE_REPO / SAILOR_TEMPLATE_REF env vars.`)}import
|
|
1655
|
-
`)
|
|
1515
|
+
You can override the source with SAILOR_TEMPLATE_REPO / SAILOR_TEMPLATE_REF env vars.`)}import It from"fs";import pn from"path";var Ki="node scripts/governance/lint-no-raw-inputs.mjs",$i="node scripts/governance/lint-repository-seam.mjs",Yi="node scripts/governance/lint-brand-literals.mjs",qi="node scripts/governance/lint-microcopy.mjs",Gi=/\bnode\s+scripts\/(governance\/)?lint-[\w-]+\.mjs/g,Hi=/\bnode\s+scripts\/lint-[\w-]+\.mjs/,Wi={scanRoots:["apps"],primitivesImport:"@nebutra/ui/primitives",whitelist:["/storybook/src/stories/","/design-docs/src/components/previews/","/sailor-docs/src/components/previews/","\\.test\\.tsx?$","/__tests__/"]},Xi={coreDomains:["^packages/.*/(billing|license|metering|auth|audit|permissions|identity|tenant)/","^backends/gateway/src/routes/(billing|ai|admin|legal|integrations|webhooks)/","^apps/web/src/app/api/"],seamPaths:["^packages/platform/repositories/","^packages/platform/db/"],dbAccessors:["getTenantDb","getSystemDb"],allowlist:["apps/web/src/app/api/invitations/[invitationId]/accept/route.ts","apps/web/src/app/api/onboarding/invite-members/route.ts","apps/web/src/app/api/startup-os/projects/[projectId]/runs/[runId]/execute/route.ts","apps/web/src/app/api/cofounder/room/[profileId]/form-team/route.ts","apps/web/src/app/api/provider-keys/route.ts","backends/gateway/src/routes/admin/index.ts","backends/gateway/src/routes/ai/api-keys.ts","backends/gateway/src/routes/ai/usage.ts","backends/gateway/src/routes/billing/index.ts","backends/gateway/src/routes/integrations/index.ts","backends/gateway/src/routes/legal/consent.ts","backends/gateway/src/routes/webhooks/clerk.ts","backends/gateway/src/routes/webhooks/stripe.ts","packages/commerce/license/src/issue-license.ts","packages/commerce/license/src/validate-license.ts","packages/commerce/billing/src/credits/service.ts","packages/commerce/billing/src/usage/ledger.ts","packages/iam/audit/src/index.ts","packages/iam/auth/src/plugins/passkey-plugin.ts"]},zi={scanRoots:["apps/web/src"],excludePaths:["/api/","\\.test\\.tsx?$","/__tests__/","/storybook/src/stories/","/design-docs/","/sailor-docs/"],bannedPatterns:[],allowlist:[]},Uo={governedPaths:["apps","packages/commerce","packages/integrations/email"],allowExpressions:["Nebutra","\u4E91\u6BD3\u667A\u80FD","\u4E91\u6BD3","nebutra\\.com","nebutra\\.ai","#0033FE","#0BF1C3"],knownExemptPatterns:["\\.stories\\.tsx?$","/__tests__/","\\.test\\.tsx?$","/previews/","^packages/design/brand/","^packages/design/tokens/","^packages/design/design-tokens/"],allowlist:[]},Ji=["apps/design-docs/mdx-components.tsx","apps/design-docs/next.config.ts","apps/design-docs/src/__registry__/index.tsx","apps/design-docs/src/app/[lang]/docs/[[...slug]]/page.tsx","apps/design-docs/src/app/[lang]/docs/layout.tsx","apps/design-docs/src/app/[lang]/layout.tsx","apps/design-docs/src/app/[lang]/registry/[name]/page.tsx","apps/design-docs/src/app/[lang]/registry/page.tsx","apps/design-docs/src/app/og/docs/[...slug]/route.tsx","apps/design-docs/src/components/brand-overview-visuals.tsx","apps/design-docs/src/components/color-usage.tsx","apps/design-docs/src/components/component-preview.tsx","apps/design-docs/src/components/gradient-demos.tsx","apps/design-docs/src/components/introduction-hero.tsx","apps/design-docs/src/components/motion-demos.tsx","apps/design-docs/src/components/pattern-demos.tsx","apps/design-docs/src/components/registry/registry-card.tsx","apps/design-docs/src/lib/github.ts","apps/design-docs/src/lib/registry-strings.ts","apps/design-docs/src/lib/registry.ts","apps/idp/src/app/layout.tsx","apps/idp/src/app/oauth/authorize/page.tsx","apps/idp/src/app/page.tsx","apps/idp/src/lib/oidc.ts","apps/landing-page/src/app/[lang]/layout.tsx","apps/landing-page/src/app/api/blog/comments/route.ts","apps/landing-page/src/app/api/blog/reactions/route.ts","apps/landing-page/src/app/api/og/route.ts","apps/landing-page/src/app/apple-icon.tsx","apps/landing-page/src/components/auth/google-one-tap.tsx","apps/landing-page/src/components/cookie-consent-banner.tsx","apps/landing-page/src/components/marketing/ProductHuntSection.tsx","apps/landing-page/src/components/marketing/TestimonialsSection.tsx","apps/landing-page/src/i18n/request.ts","apps/landing-page/src/lib/analytics/emit.ts","apps/landing-page/src/lib/blog-fallback.ts","apps/landing-page/src/lib/constants/playbook-data.ts","apps/landing-page/src/lib/constants/resources-data.ts","apps/landing-page/src/lib/constants/solutions-data.ts","apps/landing-page/src/lib/docs-links.ts","apps/landing-page/src/lib/env.ts","apps/landing-page/src/lib/landing-content.ts","apps/landing-page/src/lib/seo/metadata.ts","apps/landing-page/src/lib/seo/site-routes.ts","apps/landing-page/src/lib/status-checks.ts","apps/landing-page/src/proxy.ts","apps/mail-preview/scripts/export.ts","apps/mail-preview/scripts/render-react-templates.ts","apps/mail-preview/src/app/api/send-test/route.ts","apps/mail-preview/src/app/layout.tsx","apps/mail-preview/src/lib/fixtures.ts","apps/web/next.config.ts","apps/web/src/app/(app)/checkout-return/page.tsx","apps/web/src/app/(app)/choose-plan/page.tsx","apps/web/src/app/(app)/settings/api-keys/page.tsx","apps/web/src/app/(app)/settings/notifications/page.tsx","apps/web/src/app/(app)/settings/provider-keys/page.tsx","apps/web/src/app/(auth)/desktop-auth/complete/page.tsx","apps/web/src/app/(public)/page.tsx","apps/web/src/app/[locale]/welcome/page.tsx","apps/web/src/app/api/account/email-change/route.ts","apps/web/src/app/api/admin/access-invites/route.ts","apps/web/src/app/api/blog/comments/route.ts","apps/web/src/app/api/blog/reactions/route.ts","apps/web/src/app/api/me/public/route.ts","apps/web/src/app/api/organizations/[orgId]/members/route.ts","apps/web/src/app/global-error.tsx","apps/web/src/components/ErrorBoundary.tsx","apps/web/src/components/api-keys/create-api-key-dialog.tsx","apps/web/src/components/appearance/store.ts","apps/web/src/components/auth/auth-banner.tsx","apps/web/src/components/auth/desktop-auth-complete-handoff.tsx","apps/web/src/components/auth/sign-up-form.tsx","apps/web/src/components/billing/billing-self-service.tsx","apps/web/src/components/navigation/public-page-chrome.tsx","apps/web/src/components/notifications/inbox-bell.tsx","apps/web/src/components/notifications/notification-inbox-preview.tsx","apps/web/src/components/notifications/notification-preference-matrix.tsx","apps/web/src/components/notifications/notification-runtime-banner.tsx","apps/web/src/components/settings/security/security-capabilities.ts","apps/web/src/components/settings/security/security-settings-client.tsx","apps/web/src/components/theme-playground/design-md-import.tsx","apps/web/src/components/theme-playground/theme-playground-workbench.tsx","apps/web/src/components/theme-playground/theme-token-data.ts","apps/web/src/lib/api.ts","apps/web/src/lib/api/client.ts","apps/web/src/lib/env.ts","apps/web/src/lib/integrations/catalog.tsx","apps/web/src/lib/permissions.ts","apps/web/src/lib/public-url-defaults.ts","apps/web/src/lib/session-hint.ts","apps/web/src/lib/startup-os/company-context/generate.ts","apps/web/src/lib/startup-os/company-context/projection.ts","apps/web/src/lib/startup-os/conversation.ts","apps/web/src/lib/startup-os/execution.ts","apps/web/src/lib/startup-os/files.ts","apps/web/src/proxy.ts","packages/commerce/billing/src/index.ts","packages/commerce/contracts/src/identity.ts","packages/commerce/legal/src/components/CookieBanner.tsx","packages/commerce/legal/src/documents/config.ts","packages/commerce/legal/src/index.ts","packages/commerce/marketing/src/components/ProductHuntBadge.tsx","packages/commerce/marketing/src/config/index.ts","packages/commerce/marketing/src/index.ts","packages/integrations/email/src/index.ts","packages/integrations/email/src/templates/_layout.ts","packages/integrations/email/src/templates/invitation.tsx"];function Qi(e,t){let n=(e??"").replace(Gi,"").split("&&").map(s=>s.trim()).filter(Boolean).join(" && ");return[n.length>0?n:"biome check .",...t].join(" && ")}function Zi(e){let t=new RegExp(Uo.allowExpressions.join("|"));return Ji.filter(n=>{try{return t.test(It.readFileSync(pn.join(e,n),"utf8"))}catch{return!1}})}async function Mo(e,t){let n=t.database!=="none",o=[Ki];n&&o.push($i),o.push(Yi),o.push(qi);let s={...Uo,allowlist:Zi(e)},r={rawInputs:Wi,brandLiterals:s,microcopyRules:zi};n&&(r.repositorySeam=Xi);let i=pn.join(e,"governance.config.json");It.writeFileSync(i,JSON.stringify(r,null,2)+`
|
|
1516
|
+
`);let a=pn.join(e,"package.json"),c;try{c=It.readFileSync(a,"utf8")}catch(l){if(l.code!=="ENOENT")throw l}if(c!==void 0){let l=JSON.parse(c);l.scripts=l.scripts??{};for(let[u,d]of Object.entries(l.scripts))u!=="lint"&&typeof d=="string"&&Hi.test(d)&&delete l.scripts[u];l.scripts.lint=Qi(l.scripts.lint,o),It.writeFileSync(a,JSON.stringify(l,null,2)+`
|
|
1517
|
+
`)}return{lints:o,configWritten:!0}}import{createHmac as ta,randomBytes as na}from"crypto";import Q from"fs";import ae from"path";import{fileURLToPath as oa}from"url";var ea=[{id:"v1",key:"nebutra-sailor:scaffold-marker:v1"}];function Do(){let e=ea[0];if(!e)throw new Error("license-signing-keys: registry is empty \u2014 at least one key is required.");return e}var sa=oa(import.meta.url),Fo=ae.dirname(sa),jo="scaffold-meta.json",Bo=".nebutra";function ra(e){if(e)return e;let t=[ae.join(Fo,"..","templates"),ae.join(Fo,"..","..","templates")];for(let n of t)if(Q.existsSync(n))return n;return t[0]}function ia(e,t){let n=`${e.cliVersion}|${e.scaffoldedAt}|${e.projectName}|${e.nonce}`;return ta("sha256",t).update(n).digest("hex")}function Vo(e,t){let n=[],o=ra(t.templatesRoot),s=ae.join(e,"LICENSE");if(Q.existsSync(s)){let h=Q.readFileSync(s,"utf-8").slice(0,256);/GNU AFFERO GENERAL PUBLIC LICENSE/i.test(h)&&(Q.renameSync(s,ae.join(e,"LICENSE-AGPL-REFERENCE.md")),n.push("LICENSE-AGPL-REFERENCE.md (preserved upstream AGPL)"))}let r=ae.join(o,"LICENSE-INDEPENDENT.md");if(!Q.existsSync(r))throw new Error(`LICENSE-INDEPENDENT.md template missing at ${r}; cannot emit license.`);Q.copyFileSync(r,ae.join(e,"LICENSE")),n.push("LICENSE");let i=new Date().toISOString(),a=na(12).toString("hex"),c=Do(),l=ia({cliVersion:t.cliVersion,scaffoldedAt:i,projectName:t.projectName,nonce:a},c.key),u={schemaVersion:1,cliVersion:t.cliVersion,scaffoldedAt:i,projectName:t.projectName,nonce:a,signature:l,signingKeyId:c.id,purpose:"Marks this project as scaffolded by the official `create-sailor` CLI. The Independent Developer License in LICENSE applies as long as this file is present and the signature verifies.",license:{tier:"independent",file:"LICENSE",upgradeUrl:"https://nebutra.com/get-license"}},d=ae.join(e,Bo);Q.mkdirSync(d,{recursive:!0}),Q.writeFileSync(ae.join(d,jo),`${JSON.stringify(u,null,2)}
|
|
1518
|
+
`),n.push(`${Bo}/${jo}`);let _=ae.join(e,"README.md");if(Q.existsSync(_)){let h=Q.readFileSync(_,"utf-8");h.includes("Nebutra-Sailor Independent Developer License")||(Q.writeFileSync(_,`> **License notice:** scaffolded by \`create-sailor\` under the
|
|
1656
1519
|
> [Nebutra-Sailor Independent Developer License](./LICENSE). Free for
|
|
1657
1520
|
> individuals/OPC with \u2264 1 FTE and < $1M ARR; upgrade for larger teams at
|
|
1658
1521
|
> https://nebutra.com/get-license. The upstream repository remains
|
|
1659
1522
|
> AGPL-3.0 \u2014 see \`LICENSE-AGPL-REFERENCE.md\`.
|
|
1660
1523
|
|
|
1661
|
-
`+
|
|
1662
|
-
`);Ue.appendFileSync(
|
|
1663
|
-
`);
|
|
1664
|
-
`)}function
|
|
1665
|
-
`+t):
|
|
1524
|
+
`+h),n.push("README.md (prepended license notice)"))}return{wrote:n}}import Ue from"fs";import ln from"path";async function Ko(e,t){let n=ln.join(e,"packages","mcp");if(t==="off"){Ue.existsSync(n)&&Ue.rmSync(n,{recursive:!0,force:!0});let s=ln.join(e,"packages","cli","src","commands","mcp-server.ts");Ue.existsSync(s)&&Ue.rmSync(s,{force:!0});return}let o=ln.join(e,".env.example");if(Ue.existsSync(o)){let s=Ue.readFileSync(o,"utf-8"),r="# MCP Server (Agent-friendly API)";if(!s.includes(r)){let i=["","# =============================================",r,"# Docs: https://modelcontextprotocol.io","# =============================================","# MCP server exposes your SaaS capabilities to AI agents via MCP protocol.","# Run: pnpm mcp-server (starts stdio server)","# No config required for stdio transport. HTTP transport needs:",'MCP_HTTP_PORT="3100"','MCP_API_KEY=""',""].join(`
|
|
1525
|
+
`);Ue.appendFileSync(o,i)}}}import pt from"fs";import $o from"path";async function Yo(e,t,n){let o=$o.join(e,"packages","metering"),s;if(t==="auto"?s=n!=="none"?"on":"off":t==="on"||t==="off"?s=t:s="off",s==="off"){pt.existsSync(o)&&pt.rmSync(o,{recursive:!0,force:!0});return}let r=$o.join(e,".env.example");if(!pt.existsSync(r))return;let i=pt.readFileSync(r,"utf-8"),a="# Metering (usage-based billing)";if(!i.includes(a)){let c=["","# =============================================",a,"# Docs: see packages/commerce/metering/README.md","# =============================================","# ClickHouse-based metering pipeline for consumption billing.",'CLICKHOUSE_URL=""','CLICKHOUSE_USER="default"','CLICKHOUSE_PASSWORD=""','CLICKHOUSE_DATABASE="metering"',""].join(`
|
|
1526
|
+
`);pt.appendFileSync(r,c)}}import de from"fs";import D from"path";var qo=[{id:"sentry",name:"Sentry",region:"both",envVars:["SENTRY_DSN","NEXT_PUBLIC_SENTRY_DSN"],docs:"https://docs.sentry.io",pkg:"@sentry/nextjs",instrumentationFile:"apps/web/instrumentation.ts"},{id:"datadog",name:"Datadog",region:"global",envVars:["DD_API_KEY","DD_APP_KEY","DD_SITE"],docs:"https://docs.datadoghq.com",pkg:"dd-trace",instrumentationFile:"apps/web/instrumentation.ts"},{id:"bugsnag",name:"Bugsnag",region:"global",envVars:["BUGSNAG_API_KEY","NEXT_PUBLIC_BUGSNAG_API_KEY"],docs:"https://docs.bugsnag.com",pkg:"@bugsnag/js",instrumentationFile:"apps/web/src/lib/monitoring/bugsnag.ts"},{id:"aliyun-arms",name:"\u963F\u91CC\u4E91 ARMS",region:"cn",envVars:["ALIYUN_ARMS_LICENSE_KEY","ALIYUN_ARMS_APP_NAME","NEXT_PUBLIC_ARMS_PID"],docs:"https://help.aliyun.com/product/34364.html",instrumentationFile:"apps/web/src/components/monitoring/ArmsScript.tsx"},{id:"tingyun",name:"\u542C\u4E91 TingYun",region:"cn",envVars:["TINGYUN_LICENSE_KEY","NEXT_PUBLIC_TINGYUN_APP_KEY"],docs:"https://www.tingyun.com/docs",instrumentationFile:"apps/web/src/components/monitoring/TingYunScript.tsx"},{id:"none",name:"None",region:"both",envVars:[],docs:""}],cd=qo.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Go(e){return qo.find(t=>t.id===e)}var dn={sentry:{"@sentry/nextjs":"^8.45.0"},datadog:{"dd-trace":"^5.32.0","@datadog/browser-rum":"^5.30.0"},bugsnag:{"@bugsnag/js":"^8.2.0","@bugsnag/plugin-react":"^8.2.0"}};function aa(e){de.mkdirSync(e,{recursive:!0})}function un(e,t){let n=D.join(e,"package.json");if(!de.existsSync(n))return;let o;try{o=JSON.parse(de.readFileSync(n,"utf-8"))}catch(a){console.error(`Failed to parse ${n}:`,a);return}let r={...o.dependencies??{}};for(let[a,c]of Object.entries(t))r[a]||(r[a]=c);let i={...o,dependencies:r};de.writeFileSync(n,JSON.stringify(i,null,2)+`
|
|
1527
|
+
`)}function ce(e,t){aa(D.dirname(e)),de.writeFileSync(e,t)}function lt(e,t){let n=D.join(e,".env.example");de.existsSync(n)?de.appendFileSync(n,`
|
|
1528
|
+
`+t):de.writeFileSync(n,t)}function ca(e){let t=`import * as Sentry from "@sentry/nextjs";
|
|
1666
1529
|
|
|
1667
1530
|
Sentry.init({
|
|
1668
1531
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
@@ -1677,21 +1540,21 @@ Sentry.init({
|
|
|
1677
1540
|
],
|
|
1678
1541
|
environment: process.env.NODE_ENV,
|
|
1679
1542
|
});
|
|
1680
|
-
`,
|
|
1543
|
+
`,n=`import * as Sentry from "@sentry/nextjs";
|
|
1681
1544
|
|
|
1682
1545
|
Sentry.init({
|
|
1683
1546
|
dsn: process.env.SENTRY_DSN ?? process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
1684
1547
|
tracesSampleRate: 1.0,
|
|
1685
1548
|
environment: process.env.NODE_ENV,
|
|
1686
1549
|
});
|
|
1687
|
-
`,
|
|
1550
|
+
`,o=`import * as Sentry from "@sentry/nextjs";
|
|
1688
1551
|
|
|
1689
1552
|
Sentry.init({
|
|
1690
1553
|
dsn: process.env.SENTRY_DSN ?? process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
1691
1554
|
tracesSampleRate: 1.0,
|
|
1692
1555
|
environment: process.env.NODE_ENV,
|
|
1693
1556
|
});
|
|
1694
|
-
`,
|
|
1557
|
+
`,s=`/**
|
|
1695
1558
|
* Next.js instrumentation hook \u2014 wires Sentry into Node and Edge runtimes.
|
|
1696
1559
|
* See https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation
|
|
1697
1560
|
*/
|
|
@@ -1705,14 +1568,14 @@ export async function register() {
|
|
|
1705
1568
|
}
|
|
1706
1569
|
|
|
1707
1570
|
export { captureRequestError as onRequestError } from "@sentry/nextjs";
|
|
1708
|
-
`;
|
|
1571
|
+
`;ce(D.join(e,"sentry.client.config.ts"),t),ce(D.join(e,"sentry.server.config.ts"),n),ce(D.join(e,"sentry.edge.config.ts"),o),ce(D.join(e,"instrumentation.ts"),s)}function pa(){return`# Sentry \u2014 error tracking + performance monitoring
|
|
1709
1572
|
# Get your DSN from https://sentry.io/settings/projects/<project>/keys/
|
|
1710
1573
|
SENTRY_DSN=
|
|
1711
1574
|
NEXT_PUBLIC_SENTRY_DSN=
|
|
1712
1575
|
SENTRY_ORG=
|
|
1713
1576
|
SENTRY_PROJECT=
|
|
1714
1577
|
SENTRY_AUTH_TOKEN=
|
|
1715
|
-
`}function
|
|
1578
|
+
`}function la(e){let t=`/**
|
|
1716
1579
|
* Next.js instrumentation hook \u2014 wires Datadog APM (dd-trace) into Node runtime.
|
|
1717
1580
|
* Edge runtime is not supported by dd-trace; only nodejs is initialized.
|
|
1718
1581
|
*/
|
|
@@ -1729,7 +1592,7 @@ export async function register() {
|
|
|
1729
1592
|
});
|
|
1730
1593
|
}
|
|
1731
1594
|
}
|
|
1732
|
-
`,
|
|
1595
|
+
`,n=`"use client";
|
|
1733
1596
|
/**
|
|
1734
1597
|
* Datadog RUM (Real User Monitoring) \u2014 browser-side tracking.
|
|
1735
1598
|
* Initialize once in your root layout via <DatadogRumProvider />.
|
|
@@ -1760,7 +1623,7 @@ export function initDatadogRum(): void {
|
|
|
1760
1623
|
});
|
|
1761
1624
|
initialized = true;
|
|
1762
1625
|
}
|
|
1763
|
-
`;
|
|
1626
|
+
`;ce(D.join(e,"instrumentation.ts"),t),ce(D.join(e,"src","lib","monitoring","datadog-rum.ts"),n)}function da(){return`# Datadog \u2014 APM + RUM
|
|
1764
1627
|
# Server APM (dd-trace)
|
|
1765
1628
|
DD_API_KEY=
|
|
1766
1629
|
DD_APP_KEY=
|
|
@@ -1775,7 +1638,7 @@ NEXT_PUBLIC_DD_SITE=datadoghq.com
|
|
|
1775
1638
|
NEXT_PUBLIC_DD_SERVICE=sailor-web
|
|
1776
1639
|
NEXT_PUBLIC_DD_ENV=production
|
|
1777
1640
|
NEXT_PUBLIC_DD_VERSION=
|
|
1778
|
-
`}function
|
|
1641
|
+
`}function ua(e){let t=`/**
|
|
1779
1642
|
* Bugsnag server-side initialization.
|
|
1780
1643
|
* Import this once at the top of your server entrypoint or a shared init module.
|
|
1781
1644
|
*/
|
|
@@ -1795,7 +1658,7 @@ export function startBugsnagServer(): void {
|
|
|
1795
1658
|
});
|
|
1796
1659
|
started = true;
|
|
1797
1660
|
}
|
|
1798
|
-
`,
|
|
1661
|
+
`,n=`"use client";
|
|
1799
1662
|
/**
|
|
1800
1663
|
* Bugsnag browser initialization \u2014 wrap your app root with the returned provider.
|
|
1801
1664
|
*/
|
|
@@ -1824,11 +1687,11 @@ export function getBugsnagErrorBoundary() {
|
|
|
1824
1687
|
errorBoundary = plugin.createErrorBoundary(React) as typeof errorBoundary;
|
|
1825
1688
|
return errorBoundary;
|
|
1826
1689
|
}
|
|
1827
|
-
`;
|
|
1690
|
+
`;ce(D.join(e,"src","lib","monitoring","bugsnag.server.ts"),t),ce(D.join(e,"src","lib","monitoring","bugsnag.client.tsx"),n)}function ma(){return`# Bugsnag \u2014 error tracking
|
|
1828
1691
|
BUGSNAG_API_KEY=
|
|
1829
1692
|
NEXT_PUBLIC_BUGSNAG_API_KEY=
|
|
1830
1693
|
APP_VERSION=
|
|
1831
|
-
`}function
|
|
1694
|
+
`}function ga(e){ce(D.join(e,"src","components","monitoring","ArmsScript.tsx"),`"use client";
|
|
1832
1695
|
/**
|
|
1833
1696
|
* \u963F\u91CC\u4E91 ARMS (Application Real-time Monitoring Service) browser agent.
|
|
1834
1697
|
* Injects the official JS agent via next/script.
|
|
@@ -1849,11 +1712,11 @@ export function ArmsScript() {
|
|
|
1849
1712
|
/>
|
|
1850
1713
|
);
|
|
1851
1714
|
}
|
|
1852
|
-
`)}function
|
|
1715
|
+
`)}function fa(){return`# \u963F\u91CC\u4E91 ARMS \u2014 APM (\u4E2D\u56FD\u533A)
|
|
1853
1716
|
ALIYUN_ARMS_LICENSE_KEY=
|
|
1854
1717
|
ALIYUN_ARMS_APP_NAME=
|
|
1855
1718
|
NEXT_PUBLIC_ARMS_PID=
|
|
1856
|
-
`}function
|
|
1719
|
+
`}function ya(e){ce(D.join(e,"src","components","monitoring","TingYunScript.tsx"),`"use client";
|
|
1857
1720
|
/**
|
|
1858
1721
|
* \u542C\u4E91 Browser Agent (\u524D\u7AEF\u6027\u80FD\u76D1\u63A7).
|
|
1859
1722
|
* Docs: https://www.tingyun.com/docs
|
|
@@ -1871,12 +1734,12 @@ export function TingYunScript() {
|
|
|
1871
1734
|
/>
|
|
1872
1735
|
);
|
|
1873
1736
|
}
|
|
1874
|
-
`)}function
|
|
1737
|
+
`)}function Sa(){return`# \u542C\u4E91 TingYun \u2014 APM (\u4E2D\u56FD\u533A)
|
|
1875
1738
|
TINGYUN_LICENSE_KEY=
|
|
1876
1739
|
NEXT_PUBLIC_TINGYUN_APP_KEY=
|
|
1877
|
-
`}async function
|
|
1878
|
-
`)}}import
|
|
1879
|
-
`+t):
|
|
1740
|
+
`}async function Ho(e,t,n="global"){let o=Go(t);if(!o||o.id==="none")return;let s=D.join(e,"apps","web");if(!de.existsSync(s))return;let r=o.id;try{switch(r){case"sentry":ca(s),un(s,dn.sentry),lt(e,pa());return;case"datadog":la(s),un(s,dn.datadog),lt(e,da());return;case"bugsnag":ua(s),un(s,dn.bugsnag),lt(e,ma());return;case"aliyun-arms":ga(s),lt(e,fa());return;case"tingyun":ya(s),lt(e,Sa());return;default:return}}catch(i){throw console.error(`Failed to apply monitoring selection "${t}":`,i),new Error(`Monitoring scaffold for "${t}" failed \u2014 see stderr for details.`)}}var Ct=[{id:"novu",name:"Novu",region:"both",envVars:["NOVU_API_KEY","NOVU_APPLICATION_IDENTIFIER"],docs:"https://docs.novu.co",description:"Multi-channel notification infrastructure"},{id:"knock",name:"Knock",region:"global",envVars:["KNOCK_API_KEY","KNOCK_SIGNING_KEY","KNOCK_PUBLIC_API_KEY"],docs:"https://docs.knock.app",description:"Product notifications platform"},{id:"custom",name:"Custom (direct dispatchers)",region:"both",envVars:[],docs:"",description:"Use email + sms + push directly"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function Wo(e){return Ct.find(t=>t.id===e)}var gd=Ct.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Xo(e,t,n="global"){let o=Wo(t);if(!o)throw new Error(`Unknown notifications provider "${t}". Valid ids: ${Ct.map(s=>s.id).join(", ")}`);if(o.id==="none"){z(e,"notifications");return}o.envVars.length>0&&oe(e,{category:"Notifications",name:o.name,envVars:o.envVars,docs:o.docs}),se(e,"NOTIFICATIONS_PROVIDER",o.id)}import mn from"fs";import ha from"path";async function zo(e,t){let n=ha.join(e,"package.json");if(mn.existsSync(n)){let o=mn.readFileSync(n,"utf8"),s=JSON.parse(o);s.name=t,s.version="0.1.0",mn.writeFileSync(n,JSON.stringify(s,null,2)+`
|
|
1741
|
+
`)}}import xt from"fs";import Je from"path";import{fileURLToPath as Ea}from"url";var va=Ea(import.meta.url),Jo=Je.dirname(va);function ba(){let e=[Je.join(Jo,"..","templates","orm","drizzle"),Je.join(Jo,"..","..","templates","orm","drizzle")];for(let t of e)if(xt.existsSync(t))return t;return null}function Qo(e,t){xt.mkdirSync(t,{recursive:!0});for(let n of xt.readdirSync(e,{withFileTypes:!0})){let o=Je.join(e,n.name),s=Je.join(t,n.name);n.isDirectory()?Qo(o,s):xt.copyFileSync(o,s)}}async function Zo(e,t,n){if(t!=="drizzle")return{applied:!1};if(n!=="postgresql")return{applied:!1,reason:`Drizzle template currently targets Postgres only; --db=${n} keeps Prisma as sole ORM`};let o=ba();if(!o)return{applied:!1,reason:"Drizzle template directory not found in package \u2014 bundling error"};let s=Je.join(e,"packages","platform","db-drizzle");return Qo(o,s),{applied:!0}}import ue from"fs";import dt from"path";var _a=["stripe","lemonsqueezy","chinapay","polar"];function es(e){ue.existsSync(e)&&ue.rmSync(e,{recursive:!0,force:!0})}function Pa(e){switch(e){case"stripe":return["stripe"];case"lemon":return["lemonsqueezy"];case"wechat":case"alipay":return["chinapay"]}}function wa(e,t){let n=dt.join(e,".env.example");ue.existsSync(n)?ue.appendFileSync(n,`
|
|
1742
|
+
`+t):ue.writeFileSync(n,t)}function Aa(e){switch(e){case"stripe":return`# Stripe
|
|
1880
1743
|
STRIPE_SECRET_KEY=
|
|
1881
1744
|
STRIPE_WEBHOOK_SECRET=
|
|
1882
1745
|
`;case"lemon":return`# Lemon Squeezy
|
|
@@ -1891,14 +1754,14 @@ CHINAPAY_WECHAT_ENABLED=true
|
|
|
1891
1754
|
CHINAPAY_APP_ID=
|
|
1892
1755
|
CHINAPAY_APP_SECRET=
|
|
1893
1756
|
CHINAPAY_ALIPAY_ENABLED=true
|
|
1894
|
-
`}}async function
|
|
1895
|
-
`),
|
|
1896
|
-
`),t)}function
|
|
1897
|
-
`).filter(l=>{let u=l.trim();if(u===""||u.startsWith("//"))return!0;let
|
|
1898
|
-
`);return e.replace(
|
|
1899
|
-
`+
|
|
1900
|
-
`)}}import y from"fs";import
|
|
1901
|
-
`)}let
|
|
1757
|
+
`}}async function ts(e,t){let n=dt.join(e,"packages","commerce","billing");if(!ue.existsSync(n))return;if(t==="none"){es(n);return}let o=dt.join(n,"src"),s=new Set(Pa(t));for(let i of _a)s.has(i)||es(dt.join(o,i));let r=dt.join(o,"checkout","types.ts");if(ue.existsSync(r)){let i=t==="stripe"?"stripe":t==="lemon"?"lemonsqueezy":"chinapay",a=ue.readFileSync(r,"utf8"),c=a.replace(/export type CheckoutProviderType\s*=\s*[^;]+;/,`export type CheckoutProviderType = "${i}";`);c!==a&&ue.writeFileSync(r,c)}wa(e,Aa(t))}import be from"fs";import Qe from"path";var ss=new Set(["siliconflow","volcengine-ark","bailian","moonshot"]),gn=/@sailor:([^\s*]+)/;function Ia(e,t){let{providerIds:n,customEndpoint:o}=t;if(e.startsWith("provider:")){let s=e.slice(9);return{marker:e,keep:n.includes(s)}}if(e==="cn-compatible"){let s=n.some(r=>ss.has(r));return{marker:e,keep:s||!!o}}if(e.startsWith("cn-instance:")){let s=e.slice(12);return{marker:e,keep:n.includes(s)}}return e==="custom-endpoint"?{marker:e,keep:!!o}:e==="registry-entries"?{marker:e,keep:!0}:{marker:e,keep:!0}}function ns(e){return e.replace(/\s*\/\/\s*@sailor:[^\n]*$/,"")}function os(e){return e.replace(/\s*#\s*@sailor:[^\n]*$/,"")}function rs(e,t){let n=e.split(`
|
|
1758
|
+
`),o=[],s=e.includes("#"),r=0;for(;r<n.length;){let i=n[r],a=i.match(gn);if(!a){o.push(i),r++;continue}let{marker:c,keep:l}=Ia(a[1],t);if(c==="registry-entries"){o.push(s?os(i):ns(i)),r++;continue}if(l){let u=s?os(i):ns(i);u.trim().length>0&&o.push(u);let d=r+1;for(;d<n.length&&n[d].trim()!==""&&!gn.test(n[d]);)o.push(n[d]),d++;d<n.length&&n[d].trim()===""&&(o.push(n[d]),d++),r=d}else{let u=r+1;for(;u<n.length&&n[u].trim()!==""&&!gn.test(n[u]);)u++;u<n.length&&n[u].trim()===""&&u++,r=u}}return Ca(o.join(`
|
|
1759
|
+
`),t)}function Ca(e,t){let{providerIds:n,customEndpoint:o}=t,s=new Set;for(let l of n)["openai","anthropic","google","mistral","xai"].includes(l)&&s.add(l),s.add(l.replace(/-/g,"_")),s.add(l);o&&s.add("custom");let r=/createProviderRegistry\(\{([\s\S]*?)\}\)/,i=e.match(r);if(!i)return e;let c=i[1].split(`
|
|
1760
|
+
`).filter(l=>{let u=l.trim();if(u===""||u.startsWith("//"))return!0;let d=u.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*,?$/);return d?s.has(d[1]):!0}).join(`
|
|
1761
|
+
`);return e.replace(r,`createProviderRegistry({${c}})`)}function xa(e,t){let n=Qe.join(t,"registry.ts.template"),o=be.readFileSync(n,"utf-8");return rs(o,e)}function Ra(e,t){let n=Qe.join(t,".env.example.template"),o=be.readFileSync(n,"utf-8");return rs(o,e)}async function is(e,t,n){let o=xa(t,n),s=Ra(t,n),r=Qe.join(e,"packages/ai/agents/src");await be.promises.mkdir(r,{recursive:!0}),await be.promises.writeFile(Qe.join(r,"registry.ts"),o),await be.promises.appendFile(Qe.join(e,".env.example"),`
|
|
1762
|
+
`+s);let i={ai:"latest"},{providerIds:a,customEndpoint:c}=t;(a.includes("openai")||c||a.some(u=>ss.has(u)))&&(i["@ai-sdk/openai"]="latest",i["@ai-sdk/openai-compatible"]="latest"),a.includes("anthropic")&&(i["@ai-sdk/anthropic"]="latest"),a.includes("google")&&(i["@ai-sdk/google"]="latest"),a.includes("mistral")&&(i["@ai-sdk/mistral"]="latest"),a.includes("xai")&&(i["@ai-sdk/openai"]="latest");let l=Qe.join(e,"package.json");if(be.existsSync(l)){let u=JSON.parse(await be.promises.readFile(l,"utf-8"));u.dependencies||(u.dependencies={});for(let[d,_]of Object.entries(i))u.dependencies[d]=_;await be.promises.writeFile(l,JSON.stringify(u,null,2)+`
|
|
1763
|
+
`)}}import y from"fs";import g from"path";function as(e,t){if(!y.existsSync(e))return;let n=y.readdirSync(e);for(let o of n){let s=g.join(e,o);y.statSync(s).isDirectory()?as(s,t):t(s)}}async function cs(e,t){let n=g.join(e,"package.json");if(!y.existsSync(n))return;let o=JSON.parse(y.readFileSync(n,"utf8"));if(t.orm!=="prisma"){let s=g.join(e,"packages","db");if(y.existsSync(s)){let r=g.join(s,"prisma");y.existsSync(r)&&y.rmSync(r,{recursive:!0,force:!0});let i=g.join(s,"package.json");if(y.existsSync(i)){let c=JSON.parse(y.readFileSync(i,"utf8"));c.dependencies&&delete c.dependencies["@prisma/client"],c.devDependencies&&delete c.devDependencies.prisma,y.writeFileSync(i,JSON.stringify(c,null,2)+`
|
|
1764
|
+
`)}let a=g.join(s,"src","index.ts");y.existsSync(a)&&y.writeFileSync(a,`
|
|
1902
1765
|
export const prisma = {} as any;
|
|
1903
1766
|
export const db = {} as any;
|
|
1904
1767
|
export type PrismaClient = any;
|
|
@@ -1944,7 +1807,7 @@ export type UsageAggregate = any;
|
|
|
1944
1807
|
export type UsageLedgerEntry = any;
|
|
1945
1808
|
export type UsageType = any;
|
|
1946
1809
|
export type UsageLedgerSource = any;
|
|
1947
|
-
`)}
|
|
1810
|
+
`)}o.scripts?.postinstall&&(o.scripts.postinstall=o.scripts.postinstall.replace(/prisma generate/g,'echo "ORM Disabled"'))}if(!t.i18n){let s=g.join(e,"apps");if(y.existsSync(s)){let r=y.readdirSync(s);for(let i of r){let a=g.join(s,i);if(!y.statSync(a).isDirectory())continue;let c=g.join(a,"src","app");if(!y.existsSync(c))continue;["[lang]","[locale]"].forEach(S=>{let m=g.join(c,S);if(y.existsSync(m)){let P=y.readdirSync(m);for(let G of P)y.renameSync(g.join(m,G),g.join(c,G));y.rmdirSync(m)}}),["src/middleware.ts","middleware.ts"].forEach(S=>{let m=g.join(a,S);y.existsSync(m)&&y.rmSync(m)});let l=g.join(a,"src","lib");y.existsSync(l)||y.mkdirSync(l,{recursive:!0});let u=g.join(l,"mock-i18n.ts");y.writeFileSync(u,`
|
|
1948
1811
|
import React from 'react';
|
|
1949
1812
|
export function useTranslations(namespace?: string): any { return (key: string) => key; }
|
|
1950
1813
|
export function useLocale() { return 'en'; }
|
|
@@ -1962,7 +1825,7 @@ export const useRouter = () => ({ push: (...args: any[]) => {}, replace: (...arg
|
|
|
1962
1825
|
export default function createMiddleware(...args: any[]) { return (req: any) => req; }
|
|
1963
1826
|
export function createNavigation(...args: any[]) { return { Link, redirect, usePathname, useRouter, getPathname: () => '/' }; }
|
|
1964
1827
|
export function defineRouting<T>(config: T): T { return config; }
|
|
1965
|
-
`);let
|
|
1828
|
+
`);let _=g.join(l,"mock-i18n.d.ts");y.writeFileSync(_,`declare module '@/lib/mock-i18n' {
|
|
1966
1829
|
export function useTranslations(namespace?: string): any;
|
|
1967
1830
|
export function useLocale(): string;
|
|
1968
1831
|
export const NextIntlClientProvider: React.FC<any>;
|
|
@@ -1980,19 +1843,19 @@ export function defineRouting<T>(config: T): T { return config; }
|
|
|
1980
1843
|
export function createNavigation(...args: any[]): any;
|
|
1981
1844
|
export function defineRouting<T>(config: T): T;
|
|
1982
1845
|
}
|
|
1983
|
-
`),y.writeFileSync(
|
|
1846
|
+
`),y.writeFileSync(g.join(l,"mock-i18n-index.ts"),`
|
|
1984
1847
|
export * from './mock-i18n';
|
|
1985
1848
|
export { default } from './mock-i18n';
|
|
1986
|
-
`);let
|
|
1849
|
+
`);let h=g.join(a,"tsconfig.json");if(y.existsSync(h))try{let S=y.readFileSync(h,"utf8");if(S.includes('"paths": {')){let m=S.replace(/"paths":\s*\{/,`"paths": {
|
|
1987
1850
|
"next-intl": ["./src/lib/mock-i18n"],
|
|
1988
|
-
"next-intl/*": ["./src/lib/mock-i18n"],`);y.writeFileSync(
|
|
1989
|
-
`)}}}}y.writeFileSync(
|
|
1990
|
-
`)}function
|
|
1991
|
-
`)}function
|
|
1992
|
-
`);
|
|
1993
|
-
`)}async function
|
|
1994
|
-
`);
|
|
1995
|
-
`)}async function
|
|
1851
|
+
"next-intl/*": ["./src/lib/mock-i18n"],`);y.writeFileSync(h,m)}}catch{}let f=g.join(a,"src");y.existsSync(f)&&as(f,S=>{if(!S.endsWith(".ts")&&!S.endsWith(".tsx"))return;let m=y.readFileSync(S,"utf8"),P=!1;S.endsWith("src/app/layout.tsx")&&m.includes("../providers")&&(m=m.replace(/from\s+['"]\.\.\/providers['"]/g,"from './providers'"),P=!0),m=m.replace(/params:\s*Promise<\{[^}]*(lang|locale)[^}]*\}>/g,"params: any"),m=m.replace(/\(\{\s*params\s*\}\s*:\s*\{\s*params:\s*any\s*\}\)/g,"({ params }: any)"),m.match(/locale\s*:\s*any/)&&(m=m.replace(/locale\s*:\s*any/g,"locale: string"),P=!0),m.match(/lang\s*:\s*any/)&&(m=m.replace(/lang\s*:\s*any/g,"lang: string"),P=!0),m.match(/requestLocale\s*:\s*any/)&&(m=m.replace(/requestLocale\s*:\s*any/g,"requestLocale: string"),P=!0),m.match(/\(locale\s*=\s*(.*)\)/)&&(m=m.replace(/\(locale\s*=\s*(.*)\)/g,"() "),P=!0),m.match(/export\s+default\s+async\s+function\s+LangLayout/)&&(m=m.replace(/export\s+default\s+async\s+function\s+LangLayout\s*\(\s*\{\s*children\s*,\s*params\s*\}\s*:\s*LangLayoutProps\s*\)/g,"export default async function LangLayout({ children }: { children: React.ReactNode })"),P=!0),(S.endsWith("src/app/layout.tsx")||S.endsWith("src/app/sitemap.ts")||S.endsWith("src/app/robots.ts")||S.endsWith("src/components/ui/locale-switcher.tsx")||S.endsWith("src/app/(marketing)/blog/[slug]/page.tsx"))&&(m=m.replace(/const\s+\{\s*lang\s*\}\s*=\s*(await\s+)?params;?/g,'const lang = "en";'),m=m.replace(/const\s+\{\s*locale\s*\}\s*=\s*(await\s+)?params;?/g,'const locale = "en";'),m=m.replace(/const\s+locale\s*=\s*lang\s+as\s+Locale;?/g,'const locale = "en";'),m=m.replace(/\(l\)\s*=>/g,"(l: any) =>"),P=!0),S.endsWith("src/i18n/request.ts")&&(m=m.replace(/async\s*\(\{\s*requestLocale\s*\}\)/g,"async ({ requestLocale }: any)"),P=!0),(m.match(/locale\s*:/)||m.match(/lang\s*:/))&&(P=!0),P&&y.writeFileSync(S,m)});let E=g.join(a,"package.json");if(y.existsSync(E)){let S=JSON.parse(y.readFileSync(E,"utf8"));S.dependencies&&delete S.dependencies["next-intl"],y.writeFileSync(E,JSON.stringify(S,null,2)+`
|
|
1852
|
+
`)}}}}y.writeFileSync(n,JSON.stringify(o,null,2)+`
|
|
1853
|
+
`)}function b(e){try{y.rmSync(e,{recursive:!0,force:!0})}catch{}}function ka(e,t){if(!y.existsSync(e))return;let n;try{n=JSON.parse(y.readFileSync(e,"utf8"))}catch{return}let o=!1;if(n.dependencies&&t in n.dependencies){let{[t]:s,...r}=n.dependencies;n={...n,dependencies:r},o=!0}if(n.devDependencies&&t in n.devDependencies){let{[t]:s,...r}=n.devDependencies;n={...n,devDependencies:r},o=!0}o&&y.writeFileSync(e,JSON.stringify(n,null,2)+`
|
|
1854
|
+
`)}function ps(e,t){t.cronJobs||(b(g.join(e,"apps/web/src/app/api/cron")),b(g.join(e,"packages/integrations/queue/src/scheduled"))),t.auditLog||(b(g.join(e,"apps/web/src/app/[locale]/(app)/settings/audit-log")),b(g.join(e,"apps/web/src/app/api/audit-logs")),b(g.join(e,"apps/web/src/components/audit"))),t.apiKeys||(b(g.join(e,"apps/web/src/app/[locale]/(app)/settings/api-keys")),b(g.join(e,"apps/web/src/app/api/api-keys")),b(g.join(e,"apps/web/src/components/api-keys"))),t.webhooks||(b(g.join(e,"apps/web/src/app/[locale]/(app)/settings/webhooks")),b(g.join(e,"apps/web/src/app/api/webhooks")),b(g.join(e,"apps/web/src/components/webhooks")),b(g.join(e,"packages/integrations/webhooks"))),t.commandPalette||(b(g.join(e,"apps/web/src/components/command-palette")),b(g.join(e,"apps/web/src/app/[locale]/providers/command-palette-mount.tsx"))),t.cookieConsent||(b(g.join(e,"apps/landing-page/src/components/cookie-consent-banner.tsx")),b(g.join(e,"apps/landing-page/src/components/__tests__/cookie-consent-banner.test.tsx")),b(g.join(e,"apps/landing-page/src/lib/cookie-consent.ts")),b(g.join(e,"apps/landing-page/src/lib/__tests__/cookie-consent.test.ts")),b(g.join(e,"apps/web/src/app/api/cookie-consent"))),t.legalPages||(b(g.join(e,"apps/landing-page/src/app/[lang]/(legal)/legal")),b(g.join(e,"apps/landing-page/src/lib/legal-documents.ts")),b(g.join(e,"apps/landing-page/src/lib/__tests__/legal-documents.test.ts"))),t.chinaCompliance||(b(g.join(e,"packages/ops/china-compliance")),b(g.join(e,"apps/landing-page/src/components/icp-footer.tsx")),b(g.join(e,"apps/landing-page/src/components/__tests__/icp-footer.test.tsx")),b(g.join(e,"apps/web/src/components/auth/wechat-login-button.tsx")),b(g.join(e,"apps/web/src/components/auth/__tests__/wechat-login-button.test.tsx")),ka(g.join(e,"apps/web/package.json"),"@nebutra/china-compliance"))}import Ze from"fs";import ls from"path";var ds=/^--\s*@conditional\(([A-Za-z][A-Za-z0-9_-]*)=([^)]+)\)\s*$/m;function Ta(e){let t=ds.exec(e);if(!t)return null;let[,n,o]=t,s=o.split("|").map(r=>r.trim()).filter(Boolean);return{flag:n,values:s}}function Na(e){return e.replace(ds,"").replace(/^\n/,"")}function fn(e,t){let n={kept:[],removed:[],skipped:[]};if(!Ze.existsSync(e))return n;let o=Ze.readdirSync(e,{withFileTypes:!0}).filter(s=>s.isDirectory()).sort((s,r)=>s.name.localeCompare(r.name));for(let s of o){let r=ls.join(e,s.name),i=ls.join(r,"migration.sql");if(!Ze.existsSync(i)){n.skipped.push(s.name);continue}let a=Ze.readFileSync(i,"utf8"),c=Ta(a);if(!c){n.skipped.push(s.name);continue}let l=t[c.flag];if(l!==void 0&&c.values.includes(l)){Ze.writeFileSync(i,Na(a)),n.kept.push(s.name);continue}Ze.rmSync(r,{recursive:!0,force:!0}),n.removed.push(s.name)}return n}import{getSchema as Oa,printSchema as La}from"@mrleebo/prisma-ast";var Ua=/^\/\/\/\s*@conditional\(([A-Za-z][A-Za-z0-9_-]*)=([^)]+)\)\s*$/;function Ma(e){return e.type==="comment"&&e.text.trim().startsWith("//")}function Da(e){let t=Ua.exec(e.trim());if(!t)return null;let[,n,o]=t,s=o.split("|").map(r=>r.trim()).filter(r=>r.length>0);return{flag:n,values:s}}function Fa(e,t){for(let n=t-1;n>=0;n--){let o=e[n];if(o.type==="comment"){let s=Da(o.text);if(s)return{annIndex:n,parsed:s};continue}break}return null}function ja(e){return e.type==="model"||e.type==="enum"}function yn(e,t){let n=Oa(e),o=new Set;for(let a=0;a<n.list.length;a++){let c=n.list[a];if(!ja(c))continue;let l=Fa(n.list,a);if(!l)continue;let u=t[l.parsed.flag];if(u!==void 0&&l.parsed.values.includes(u))o.add(l.annIndex);else{o.add(a);for(let h=a-1;h>=0;h--){let f=n.list[h];if(Ma(f)){o.add(h);continue}break}let _=n.list[a+1];_&&_.type==="break"&&o.add(a+1)}}let s=n.list.filter((a,c)=>!o.has(c)),r=[];for(let a of s){let c=r[r.length-1];a.type==="break"&&c&&c.type==="break"||r.push(a)}return La({type:"schema",list:r})}import et from"fs";import ms from"path";var Rt=[{id:"qstash",name:"Upstash QStash",region:"both",envVars:["QSTASH_TOKEN","QSTASH_CURRENT_SIGNING_KEY","QSTASH_NEXT_SIGNING_KEY"],docs:"https://upstash.com/docs/qstash",description:"Serverless HTTP-based queue"},{id:"bullmq",name:"BullMQ",region:"both",envVars:["REDIS_URL"],docs:"https://docs.bullmq.io",description:"Redis-backed job queue"},{id:"sqs",name:"AWS SQS",region:"global",envVars:["AWS_SQS_QUEUE_URL","AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"],docs:"https://docs.aws.amazon.com/AWSSimpleQueueService/",description:"AWS managed queue"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:"Skip queue package"}];function us(e){return Rt.find(t=>t.id===e)}var Bd=Rt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function gs(e){return et.existsSync(e)?et.readFileSync(e,"utf-8"):null}function Ba(e,t){let n=ms.join(e,".env.example"),o=gs(n);if(o===null)return;let s=`# Queue: ${t.name}`;if(o.includes(s))return;let r=["","# =============================================",s,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
1855
|
+
`);et.appendFileSync(n,r)}function Va(e,t,n){let o=ms.join(e,".env.example"),s=gs(o);if(s===null)return;let r=new RegExp(`^${t}=.*$`,"m");r.test(s)?et.writeFileSync(o,s.replace(r,`${t}="${n}"`)):et.appendFileSync(o,`${t}="${n}"
|
|
1856
|
+
`)}async function fs(e,t,n){let o=xe(e,"queue");if(t==="none"){o&&et.rmSync(o,{recursive:!0,force:!0});return}let s=us(t);if(!s)throw new Error(`Unknown queue provider "${t}". Valid ids: ${Rt.map(r=>r.id).join(", ")}`);o&&(Ba(e,s),Va(e,"QUEUE_PROVIDER",t))}import me from"fs";import _e from"path";import{fileURLToPath as Ka}from"url";var $a=Ka(import.meta.url),ys=_e.dirname($a),Ya=/\.(md|mdx|json|ts|tsx|js|mjs|cjs|yaml|yml|css|html|toml|py)$/,qa=["backends/gateway","infra","e2e","tests"];function Ga(){let e=[_e.join(ys,"..","templates"),_e.join(ys,"..","..","templates")];for(let t of e)if(me.existsSync(t))return t;return e[0]}async function Ss(e,t){let n=Ga(),o=[],s=[],r=[...qa,...t.withWorkflows?["workflows"]:[],...t.withPythonBackend?["backends/python"]:[]];for(let i of r){let a=_e.join(n,i);if(!me.existsSync(a)){s.push(i);continue}let c=_e.join(e,i);hs(a,c),Es(c,{"{PRODUCT_NAME}":t.projectName}),o.push(i)}return{applied:o,skipped:s}}function hs(e,t){me.mkdirSync(t,{recursive:!0});for(let n of me.readdirSync(e,{withFileTypes:!0})){let o=_e.join(e,n.name),s=_e.join(t,n.name);n.isDirectory()?hs(o,s):me.existsSync(s)||me.copyFileSync(o,s)}}function Es(e,t){for(let n of me.readdirSync(e,{withFileTypes:!0})){let o=_e.join(e,n.name);if(n.isDirectory()){Es(o,t);continue}if(!Ya.test(n.name))continue;let s=me.readFileSync(o,"utf-8"),r=!1;for(let[i,a]of Object.entries(t))s.includes(i)&&(s=s.split(i).join(a),r=!0);r&&me.writeFileSync(o,s)}}import tt from"fs";import bs from"path";var kt=[{id:"meilisearch",name:"Meilisearch",region:"both",envVars:["MEILISEARCH_HOST","MEILISEARCH_MASTER_KEY"],docs:"https://docs.meilisearch.com",description:"Self-hostable search"},{id:"typesense",name:"Typesense",region:"both",envVars:["TYPESENSE_HOST","TYPESENSE_PORT","TYPESENSE_PROTOCOL","TYPESENSE_API_KEY"],docs:"https://typesense.org/docs",description:"Open-source search"},{id:"algolia",name:"Algolia",region:"global",envVars:["ALGOLIA_APP_ID","ALGOLIA_ADMIN_API_KEY","ALGOLIA_SEARCH_API_KEY"],docs:"https://www.algolia.com/doc/",description:"Managed search as a service"},{id:"pgvector",name:"pgvector (Postgres)",region:"both",envVars:["DATABASE_URL"],docs:"https://github.com/pgvector/pgvector",description:"Postgres vector + full-text"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:"Skip search package"}];function vs(e){return kt.find(t=>t.id===e)}var Jd=kt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function _s(e){return tt.existsSync(e)?tt.readFileSync(e,"utf-8"):null}function Ha(e,t){let n=bs.join(e,".env.example"),o=_s(n);if(o===null)return;let s=`# Search: ${t.name}`;if(o.includes(s))return;let r=["","# =============================================",s,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
1857
|
+
`);tt.appendFileSync(n,r)}function Wa(e,t,n){let o=bs.join(e,".env.example"),s=_s(o);if(s===null)return;let r=new RegExp(`^${t}=.*$`,"m");r.test(s)?tt.writeFileSync(o,s.replace(r,`${t}="${n}"`)):tt.appendFileSync(o,`${t}="${n}"
|
|
1858
|
+
`)}async function Ps(e,t,n){let o=xe(e,"search");if(t==="none"){o&&tt.rmSync(o,{recursive:!0,force:!0});return}let s=vs(t);if(!s)throw new Error(`Unknown search provider "${t}". Valid ids: ${kt.map(r=>r.id).join(", ")}`);o&&(Ha(e,s),Wa(e,"SEARCH_PROVIDER",t))}import Pe from"fs";import Me from"path";function Xa(e){let t=[Me.join(e,"packages","db"),Me.join(e,"apps","web")];for(let n of t){let o=Me.join(n,"prisma","schema.prisma");if(Pe.existsSync(o))return{packageDir:n,seedPath:Me.join(n,"prisma","seed.ts"),packageJsonPath:Me.join(n,"package.json")}}return null}function za(e){try{let t=Pe.readFileSync(e,"utf8"),n=new Set,o=/^\s*model\s+(\w+)\s*\{/gm,s=o.exec(t);for(;s;)n.add(s[1]),s=o.exec(t);return n}catch{return new Set}}function Ja(e,t){return!t||e==="none"?` // Skipping admin user \u2014 auth=${JSON.stringify(e)} (or no User model).`:e==="clerk"?` try {
|
|
1996
1859
|
const admin = await prisma.user.create({
|
|
1997
1860
|
data: {
|
|
1998
1861
|
clerkId: "user_mock_admin",
|
|
@@ -2023,7 +1886,7 @@ export { default } from './mock-i18n';
|
|
|
2023
1886
|
console.log("\\u2713 Created admin user:", admin.email);
|
|
2024
1887
|
} catch (err) {
|
|
2025
1888
|
console.warn("\\u26A0 Admin user creation skipped:", (err as Error).message);
|
|
2026
|
-
}`}function
|
|
1889
|
+
}`}function Qa(e,t){let n=t.has("Tenant"),o=t.has("User"),s=t.has("Project"),r=Ja(e,o),i=n?` let tenants: Array<{ id: string; name: string }> = [];
|
|
2027
1890
|
try {
|
|
2028
1891
|
tenants = await Promise.all([
|
|
2029
1892
|
prisma.tenant.create({
|
|
@@ -2040,7 +1903,7 @@ export { default } from './mock-i18n';
|
|
|
2040
1903
|
} catch (err) {
|
|
2041
1904
|
console.warn("\\u26A0 Tenant creation skipped:", (err as Error).message);
|
|
2042
1905
|
}`:` const tenants: Array<{ id: string; name: string }> = [];
|
|
2043
|
-
console.log("\\u2139 No Tenant model in schema \u2014 skipping tenant seed.");`,
|
|
1906
|
+
console.log("\\u2139 No Tenant model in schema \u2014 skipping tenant seed.");`,a=s&&n?` for (const tenant of tenants) {
|
|
2044
1907
|
try {
|
|
2045
1908
|
await prisma.project.createMany({
|
|
2046
1909
|
data: [
|
|
@@ -2076,10 +1939,10 @@ async function main(): Promise<void> {
|
|
|
2076
1939
|
${i}
|
|
2077
1940
|
|
|
2078
1941
|
// --- Admin user ------------------------------------------------------
|
|
2079
|
-
${
|
|
1942
|
+
${r}
|
|
2080
1943
|
|
|
2081
1944
|
// --- Sample projects per tenant --------------------------------------
|
|
2082
|
-
${
|
|
1945
|
+
${a}
|
|
2083
1946
|
|
|
2084
1947
|
console.log("\\u2713 Seed complete");
|
|
2085
1948
|
}
|
|
@@ -2092,12 +1955,12 @@ main()
|
|
|
2092
1955
|
.finally(async () => {
|
|
2093
1956
|
await prisma.$disconnect();
|
|
2094
1957
|
});
|
|
2095
|
-
`}function
|
|
2096
|
-
`)}catch(t){let
|
|
2097
|
-
`+t):
|
|
1958
|
+
`}function Za(e){if(Pe.existsSync(e))try{let t=Pe.readFileSync(e,"utf8"),n=JSON.parse(t),o=n.scripts??{},s=!1;o["db:seed"]||(o["db:seed"]="prisma db seed",s=!0);let r=n.prisma??{};if(r.seed||(r.seed="tsx prisma/seed.ts",s=!0),!s)return;let i={...n,scripts:o,prisma:r};Pe.writeFileSync(e,JSON.stringify(i,null,2)+`
|
|
1959
|
+
`)}catch(t){let n=t instanceof Error?t.message:String(t);console.warn(`[create-sailor] Failed to patch seed scripts in ${e}: ${n}`)}}async function ws(e,t){let n=Xa(e);if(n)try{let o=Me.join(n.packageDir,"prisma","schema.prisma"),s=za(o),r=Qa(t,s),i=Me.dirname(n.seedPath);Pe.existsSync(i)||Pe.mkdirSync(i,{recursive:!0}),Pe.writeFileSync(n.seedPath,r),Za(n.packageJsonPath)}catch(o){let s=o instanceof Error?o.message:String(o);throw console.error(`[create-sailor] Failed to generate seed data: ${s}`),new Error(`Unable to write prisma/seed.ts: ${s}`)}}import we from"fs";import De from"path";var Tt=[{id:"twilio",name:"Twilio",region:"global",envVars:["TWILIO_ACCOUNT_SID","TWILIO_AUTH_TOKEN","TWILIO_FROM_NUMBER"],docs:"https://www.twilio.com/docs/sms",pkgs:["twilio"]},{id:"messagebird",name:"MessageBird (Bird)",region:"global",envVars:["MESSAGEBIRD_ACCESS_KEY","MESSAGEBIRD_ORIGINATOR"],docs:"https://developers.messagebird.com/api/sms-messaging/",pkgs:["messagebird"]},{id:"plivo",name:"Plivo",region:"global",envVars:["PLIVO_AUTH_ID","PLIVO_AUTH_TOKEN","PLIVO_FROM_NUMBER"],docs:"https://www.plivo.com/docs/sms/",pkgs:["plivo"]},{id:"aliyun-sms",name:"\u963F\u91CC\u4E91\u77ED\u4FE1 Aliyun SMS",region:"cn",envVars:["ALIYUN_SMS_ACCESS_KEY","ALIYUN_SMS_ACCESS_SECRET","ALIYUN_SMS_SIGN_NAME","ALIYUN_SMS_TEMPLATE_CODE","ALIYUN_SMS_ENDPOINT"],docs:"https://help.aliyun.com/product/44282.html",pkgs:["@alicloud/dysmsapi20170525","@alicloud/openapi-client"]},{id:"tencent-sms",name:"\u817E\u8BAF\u4E91\u77ED\u4FE1 Tencent SMS",region:"cn",envVars:["TENCENT_SMS_SECRET_ID","TENCENT_SMS_SECRET_KEY","TENCENT_SMS_SDK_APP_ID","TENCENT_SMS_SIGN_NAME","TENCENT_SMS_TEMPLATE_ID","TENCENT_SMS_REGION"],docs:"https://cloud.tencent.com/document/product/382",pkgs:["tencentcloud-sdk-nodejs-sms"]},{id:"yunpian",name:"\u4E91\u7247 Yunpian",region:"cn",envVars:["YUNPIAN_API_KEY","YUNPIAN_SIGN"],docs:"https://www.yunpian.com/official/document/sms/zh_CN/introduction_product_intro",pkgs:["yunpian-nodejs-sdk"]},{id:"none",name:"None",region:"both",envVars:[],docs:""}],au=Tt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function As(e){return Tt.find(t=>t.id===e)}var ec="@nebutra/sms",tc={twilio:"^5.3.6",messagebird:"^4.0.1",plivo:"^4.73.0","@alicloud/dysmsapi20170525":"^2.0.24","@alicloud/openapi-client":"^0.4.12","tencentcloud-sdk-nodejs-sms":"^4.0.970","yunpian-nodejs-sdk":"^0.0.2"};function Sn(e,t){we.mkdirSync(De.dirname(e),{recursive:!0}),we.writeFileSync(e,t)}function nc(e,t){let n=De.join(e,".env.example");we.existsSync(n)?we.appendFileSync(n,`
|
|
1960
|
+
`+t):we.writeFileSync(n,t)}function oc(e){if(e.envVars.length===0)return"";let t=`# SMS provider: ${e.name}`,n=e.envVars.map(o=>`${o}=`).join(`
|
|
2098
1961
|
`);return`${t}
|
|
2099
|
-
${
|
|
2100
|
-
`}var
|
|
1962
|
+
${n}
|
|
1963
|
+
`}var sc=`/**
|
|
2101
1964
|
* Inputs for the \`sendSms\` wrapper. Keep this surface portable across
|
|
2102
1965
|
* providers \u2014 provider-specific options belong in the wrapper itself.
|
|
2103
1966
|
*/
|
|
@@ -2117,7 +1980,7 @@ export interface SendSmsResult {
|
|
|
2117
1980
|
provider: string;
|
|
2118
1981
|
raw?: unknown;
|
|
2119
1982
|
}
|
|
2120
|
-
`;function
|
|
1983
|
+
`;function rc(){return`import twilio from "twilio";
|
|
2121
1984
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2122
1985
|
|
|
2123
1986
|
let client: ReturnType<typeof twilio> | null = null;
|
|
@@ -2152,7 +2015,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2152
2015
|
}
|
|
2153
2016
|
|
|
2154
2017
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2155
|
-
`}function
|
|
2018
|
+
`}function ic(){return`import messagebird from "messagebird";
|
|
2156
2019
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2157
2020
|
|
|
2158
2021
|
let client: ReturnType<typeof messagebird> | null = null;
|
|
@@ -2190,7 +2053,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2190
2053
|
}
|
|
2191
2054
|
|
|
2192
2055
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2193
|
-
`}function
|
|
2056
|
+
`}function ac(){return`import plivo from "plivo";
|
|
2194
2057
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2195
2058
|
|
|
2196
2059
|
let client: InstanceType<typeof plivo.Client> | null = null;
|
|
@@ -2229,7 +2092,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2229
2092
|
}
|
|
2230
2093
|
|
|
2231
2094
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2232
|
-
`}function
|
|
2095
|
+
`}function cc(){return`import Dysmsapi, { SendSmsRequest } from "@alicloud/dysmsapi20170525";
|
|
2233
2096
|
import * as $OpenApi from "@alicloud/openapi-client";
|
|
2234
2097
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2235
2098
|
|
|
@@ -2278,7 +2141,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2278
2141
|
}
|
|
2279
2142
|
|
|
2280
2143
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2281
|
-
`}function
|
|
2144
|
+
`}function pc(){return`import * as tencentcloud from "tencentcloud-sdk-nodejs-sms";
|
|
2282
2145
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2283
2146
|
|
|
2284
2147
|
const SmsClient = tencentcloud.sms.v20210111.Client;
|
|
@@ -2343,7 +2206,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2343
2206
|
}
|
|
2344
2207
|
|
|
2345
2208
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2346
|
-
`}function
|
|
2209
|
+
`}function lc(){return`// @ts-expect-error \u2014 yunpian-nodejs-sdk ships no type declarations
|
|
2347
2210
|
import Yunpian from "yunpian-nodejs-sdk";
|
|
2348
2211
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2349
2212
|
|
|
@@ -2395,13 +2258,13 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2395
2258
|
}
|
|
2396
2259
|
|
|
2397
2260
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2398
|
-
`}function
|
|
2399
|
-
`}}function
|
|
2400
|
-
`}async function
|
|
2401
|
-
`+t):je.writeFileSync(
|
|
2261
|
+
`}function dc(e){switch(e){case"twilio":return rc();case"messagebird":return ic();case"plivo":return ac();case"aliyun-sms":return cc();case"tencent-sms":return pc();case"yunpian":return lc();case"none":return`export {};
|
|
2262
|
+
`}}function uc(e){let t={};for(let o of e.pkgs??[]){let s=tc[o];s&&(t[o]=s)}let n={name:ec,version:"0.1.0",private:!0,description:`Transactional SMS via ${e.name}`,type:"module",main:"./dist/index.js",exports:{".":{import:"./dist/index.js",types:"./dist/index.d.ts"}},scripts:{build:"tsup",typecheck:"tsc --noEmit",clean:"rm -rf dist .turbo"},dependencies:t,devDependencies:{tsup:"^8.5.0",typescript:"catalog:"}};return JSON.stringify(n,null,2)+`
|
|
2263
|
+
`}async function Is(e,t,n="global"){if(t==="none")return;let o=As(t);if(!o)throw new Error(`Unknown SMS provider "${t}". Valid ids: ${Tt.map(i=>i.id).join(", ")}`);let s=De.join(e,"packages","sms");if(!we.existsSync(s))return;let r=De.join(s,"src");we.existsSync(r)&&we.rmSync(r,{recursive:!0,force:!0});try{Sn(De.join(s,"package.json"),uc(o)),Sn(De.join(r,"types.ts"),sc),Sn(De.join(r,"index.ts"),dc(o.id)),nc(e,oc(o))}catch(i){throw console.error(`Failed to apply SMS selection "${t}":`,i),new Error(`SMS scaffold for "${t}" failed \u2014 see stderr for details.`)}}import je from"fs";import Fe from"path";var Be="@nebutra/storage";function hn(e,t){je.mkdirSync(Fe.dirname(e),{recursive:!0}),je.writeFileSync(e,t)}function mc(e,t){let n=Fe.join(e,".env.example");je.existsSync(n)?je.appendFileSync(n,`
|
|
2264
|
+
`+t):je.writeFileSync(n,t)}function gc(e){if(e.envVars.length===0)return"";let t=`# Storage provider: ${e.name}`,n=e.envVars.map(o=>`${o}=`).join(`
|
|
2402
2265
|
`);return`${t}
|
|
2403
|
-
${
|
|
2404
|
-
`}var
|
|
2266
|
+
${n}
|
|
2267
|
+
`}var fc=`/**
|
|
2405
2268
|
* Inputs for the \`uploadFile\` wrapper. Keep this surface portable across
|
|
2406
2269
|
* providers \u2014 provider-specific options belong in the wrapper itself.
|
|
2407
2270
|
*/
|
|
@@ -2427,8 +2290,8 @@ export interface SignedUrlOptions {
|
|
|
2427
2290
|
operation?: "get" | "put";
|
|
2428
2291
|
contentType?: string;
|
|
2429
2292
|
}
|
|
2430
|
-
`;function
|
|
2431
|
-
* @${
|
|
2293
|
+
`;function yc(){return`/**
|
|
2294
|
+
* @${Be.slice(1)} \u2014 Cloudflare R2 via the S3-compatible API.
|
|
2432
2295
|
*
|
|
2433
2296
|
* Docs: https://developers.cloudflare.com/r2/api/s3/api/
|
|
2434
2297
|
*/
|
|
@@ -2491,8 +2354,8 @@ export async function getSignedUrl(
|
|
|
2491
2354
|
}
|
|
2492
2355
|
|
|
2493
2356
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2494
|
-
`}function
|
|
2495
|
-
* @${
|
|
2357
|
+
`}function Sc(){return`/**
|
|
2358
|
+
* @${Be.slice(1)} \u2014 AWS S3 wrapper.
|
|
2496
2359
|
*
|
|
2497
2360
|
* Docs: https://docs.aws.amazon.com/s3/
|
|
2498
2361
|
*/
|
|
@@ -2556,8 +2419,8 @@ export async function getSignedUrl(
|
|
|
2556
2419
|
}
|
|
2557
2420
|
|
|
2558
2421
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2559
|
-
`}function
|
|
2560
|
-
* @${
|
|
2422
|
+
`}function hc(){return`/**
|
|
2423
|
+
* @${Be.slice(1)} \u2014 Supabase Storage wrapper.
|
|
2561
2424
|
*
|
|
2562
2425
|
* Docs: https://supabase.com/docs/guides/storage
|
|
2563
2426
|
*/
|
|
@@ -2619,8 +2482,8 @@ export async function getSignedUrl(
|
|
|
2619
2482
|
}
|
|
2620
2483
|
|
|
2621
2484
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2622
|
-
`}function
|
|
2623
|
-
* @${
|
|
2485
|
+
`}function Ec(){return`/**
|
|
2486
|
+
* @${Be.slice(1)} \u2014 \u963F\u91CC\u4E91 OSS wrapper via ali-oss.
|
|
2624
2487
|
*
|
|
2625
2488
|
* Docs: https://help.aliyun.com/product/31815.html
|
|
2626
2489
|
*/
|
|
@@ -2666,8 +2529,8 @@ export async function getSignedUrl(
|
|
|
2666
2529
|
}
|
|
2667
2530
|
|
|
2668
2531
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2669
|
-
`}function
|
|
2670
|
-
* @${
|
|
2532
|
+
`}function vc(){return`/**
|
|
2533
|
+
* @${Be.slice(1)} \u2014 \u817E\u8BAF\u4E91 COS wrapper via cos-nodejs-sdk-v5.
|
|
2671
2534
|
*
|
|
2672
2535
|
* Docs: https://cloud.tencent.com/document/product/436
|
|
2673
2536
|
*/
|
|
@@ -2721,8 +2584,8 @@ export async function getSignedUrl(
|
|
|
2721
2584
|
}
|
|
2722
2585
|
|
|
2723
2586
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2724
|
-
`}function
|
|
2725
|
-
* @${
|
|
2587
|
+
`}function bc(){return`/**
|
|
2588
|
+
* @${Be.slice(1)} \u2014 \u4E03\u725B\u4E91 Kodo wrapper.
|
|
2726
2589
|
*
|
|
2727
2590
|
* Docs: https://developer.qiniu.com/kodo
|
|
2728
2591
|
*/
|
|
@@ -2789,9 +2652,10 @@ export async function getSignedUrl(
|
|
|
2789
2652
|
}
|
|
2790
2653
|
|
|
2791
2654
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2792
|
-
`}function
|
|
2793
|
-
`}}function
|
|
2794
|
-
`}async function
|
|
2655
|
+
`}function _c(e){switch(e){case"r2":return yc();case"s3":return Sc();case"supabase-storage":return hc();case"aliyun-oss":return Ec();case"tencent-cos":return vc();case"qiniu":return bc();case"none":return`export {};
|
|
2656
|
+
`}}function Pc(e){let t={};e.pkg&&e.pkgVersion&&(t[e.pkg]=e.pkgVersion),(e.id==="r2"||e.id==="s3")&&(t["@aws-sdk/s3-request-presigner"]="^3.658.0");let n={name:Be,version:"0.1.0",private:!0,description:`Object storage via ${e.name}`,type:"module",main:"./dist/index.js",exports:{".":{import:"./dist/index.js",types:"./dist/index.d.ts"}},scripts:{build:"tsup",typecheck:"tsc --noEmit",clean:"rm -rf dist .turbo"},dependencies:t,devDependencies:{tsup:"^8.5.0",typescript:"catalog:"}};return JSON.stringify(n,null,2)+`
|
|
2657
|
+
`}async function Cs(e,t,n){if(t==="none")return;let o=Rn(t);if(!o)throw new Error(`Unknown storage provider "${t}". Valid ids: ${rt.map(i=>i.id).join(", ")}`);let s=Fe.join(e,"packages","storage"),r=Fe.join(s,"src");je.existsSync(r)&&je.rmSync(r,{recursive:!0,force:!0}),hn(Fe.join(s,"package.json"),Pc(o)),hn(Fe.join(r,"types.ts"),fc),hn(Fe.join(r,"index.ts"),_c(o.id)),mc(e,gc(o))}var Nt=[{id:"svix",name:"Svix",region:"global",envVars:["SVIX_API_TOKEN"],docs:"https://docs.svix.com",description:"Managed webhooks platform"},{id:"custom",name:"Custom (self-hosted)",region:"both",envVars:["WEBHOOK_SIGNING_SECRET"],docs:"",description:"Custom implementation with built-in package"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function xs(e){return Nt.find(t=>t.id===e)}var Su=Nt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Rs(e,t,n="global"){let o=xs(t);if(!o)throw new Error(`Unknown webhooks provider "${t}". Valid ids: ${Nt.map(s=>s.id).join(", ")}`);if(o.id==="none"){z(e,"webhooks");return}o.envVars.length>0&&oe(e,{category:"Webhooks",name:o.name,envVars:o.envVars,docs:o.docs}),se(e,"WEBHOOKS_PROVIDER",o.id)}import Ve from"fs";import ut from"path";function wc(e){return`import { brand } from "@nebutra/brand/metadata";
|
|
2658
|
+
import Link from "next/link";
|
|
2795
2659
|
|
|
2796
2660
|
export default function WelcomePage() {
|
|
2797
2661
|
const projectName = ${JSON.stringify(e)};
|
|
@@ -2856,7 +2720,7 @@ export default function WelcomePage() {
|
|
|
2856
2720
|
</li>
|
|
2857
2721
|
<li>
|
|
2858
2722
|
<a
|
|
2859
|
-
href=
|
|
2723
|
+
href={\`https://\${brand.domains.landing}/get-license\`}
|
|
2860
2724
|
className="underline"
|
|
2861
2725
|
>
|
|
2862
2726
|
Get free license (Individual/OPC)
|
|
@@ -2900,21 +2764,21 @@ function Step({
|
|
|
2900
2764
|
</li>
|
|
2901
2765
|
);
|
|
2902
2766
|
}
|
|
2903
|
-
`}function
|
|
2767
|
+
`}function Ac(e={}){return`
|
|
2904
2768
|
## What you can do next
|
|
2905
2769
|
|
|
2906
2770
|
${[e.apiKeys===!1?"- Manage API keys at `/settings/api-keys` (disabled \u2014 re-enable with `--api-keys=true`)":"- Manage API keys at `/settings/api-keys`",e.auditLog===!1?"- View audit log at `/settings/audit-log` (disabled \u2014 re-enable with `--audit-log=true`)":"- View audit log at `/settings/audit-log`","- Configure webhooks at `/settings/webhooks`",e.commandPalette===!1?"- Press \u2318K to open the command palette (disabled \u2014 re-enable with `--command-palette=true`)":"- Press \u2318K to open the command palette","- Edit notification preferences at `/settings/notifications`","- Export your data at `/settings/account/export`","- For China deployments, see `packages/ops/china-compliance/README.md`"].join(`
|
|
2907
2771
|
`)}
|
|
2908
|
-
`}function
|
|
2772
|
+
`}function Ic(e=[]){return e.length===0?"":`
|
|
2909
2773
|
## Production readiness holds
|
|
2910
2774
|
|
|
2911
2775
|
Do not enable these in production until you replace stubs, add provider credentials, and verify the integration contract:
|
|
2912
2776
|
|
|
2913
|
-
${e.map(
|
|
2777
|
+
${e.map(n=>{let o=We(n.status);return`- ${n.flag}=${n.provider} ${o} \u2014 ${he(n.status)}`}).join(`
|
|
2914
2778
|
`)}
|
|
2915
2779
|
|
|
2916
2780
|
Review the package readiness matrix before handoff: [docs/package-status.md](docs/package-status.md)
|
|
2917
|
-
`}function
|
|
2781
|
+
`}function Cc(e,t,n=[],o={}){return`# Next steps \u2014 ${e}
|
|
2918
2782
|
|
|
2919
2783
|
Region: ${t}
|
|
2920
2784
|
|
|
@@ -2940,44 +2804,119 @@ Your AI-native SaaS scaffold is ready. Complete these four steps to go from temp
|
|
|
2940
2804
|
\`\`\`
|
|
2941
2805
|
|
|
2942
2806
|
After setup, delete the welcome route at \`apps/web/src/app/[locale]/welcome/\`.
|
|
2943
|
-
${
|
|
2807
|
+
${Ac(o)}${Ic(n)}
|
|
2944
2808
|
|
|
2945
2809
|
## Resources
|
|
2946
2810
|
|
|
2947
2811
|
- [Documentation](/docs)
|
|
2948
2812
|
- [GitHub](https://github.com/nebutra/nebutra-sailor)
|
|
2949
2813
|
- [Get free license (Individual/OPC)](https://nebutra.com/get-license)
|
|
2950
|
-
`}async function
|
|
2951
|
-
`)}function
|
|
2952
|
-
`)
|
|
2953
|
-
|
|
2954
|
-
|
|
2814
|
+
`}async function ks(e,t){let{projectName:n,region:o,previewSelections:s=[],waveFeatures:r={}}=t;try{let i=ut.join(e,".sailor");Ve.existsSync(i)||Ve.mkdirSync(i,{recursive:!0}),Ve.writeFileSync(ut.join(i,"next-steps.md"),Cc(n,o,s,r))}catch(i){let a=i instanceof Error?i.message:String(i);console.warn(`[create-sailor] Failed to write .sailor/next-steps.md: ${a}`)}try{let i=ut.join(e,"apps","web");if(!Ve.existsSync(i))return;let a=ut.join(i,"src","app","[locale]","welcome");Ve.existsSync(a)||Ve.mkdirSync(a,{recursive:!0}),Ve.writeFileSync(ut.join(a,"page.tsx"),wc(n))}catch(i){let a=i instanceof Error?i.message:String(i);console.warn(`[create-sailor] Failed to write welcome page: ${a}`)}}function p(e,t){e&&process.stdout.write(JSON.stringify(t)+`
|
|
2815
|
+
`)}function Ts(e){return e!==null&&typeof e=="object"&&"code"in e&&e.code==="ENOENT"}function Rc(e,t){for(let n of t)if(e)p(!0,{event:"warn",step:n.flag,provider:n.provider,packageStatus:n.status,message:he(n.status)});else{let o=We(n.status);process.stdout.write(mt.yellow(`\u26A0 ${n.flag}=${n.provider} ${o} \u2014 ${he(n.status)}
|
|
2816
|
+
`))}}function kc(e,t,n){let o=Ke.join(t,"packages/platform/db/prisma/schema.prisma"),s;try{s=Lt.readFileSync(o,"utf8")}catch(r){if(Ts(r)){p(e,{event:"step",step:"schema-prune",status:"skip"});return}let i=r instanceof Error?r.message:String(r);p(e,{event:"step",step:"schema-prune",status:"error",error:i});return}p(e,{event:"step",step:"schema-prune",status:"start"});try{let r=yn(s,n);Lt.writeFileSync(o,r),fn(Ke.join(Ke.dirname(o),"migrations"),n),p(e,{event:"step",step:"schema-prune",status:"ok"})}catch(r){let i=r instanceof Error?r.message:String(r);p(e,{event:"step",step:"schema-prune",status:"error",error:i})}}async function Ns(e){let{resolvedTarget:t,projectName:n,resolvedPm:o,opts:s,useJson:r,resolved:i,startedAt:a}=e,{region:c,orm:l,database:u,databaseHost:d,payment:_,paymentChoice:h,auth:f,socialLoginIds:E,aiProviders:S,deployTarget:m,docs:P,metering:G,billingMode:M,idp:ge,accessGate:pe,mcp:Z,waveToggles:j,previewSelections:K,config:C}=i,H={auth:f,payment:h,"billing-mode":M,idp:ge,template:"saas","access-gate":pe};p(r,{event:"step",step:"clone",status:"start"}),await Lo(t),p(r,{event:"step",step:"clone",status:"ok"}),p(r,{event:"step",step:"package",status:"start"}),await zo(t,n),p(r,{event:"step",step:"package",status:"ok"}),p(r,{event:"step",step:"config",status:"start"}),await co(t,C),p(r,{event:"step",step:"config",status:"ok"}),p(r,{event:"step",step:"governance-lints",status:"start"});let le=await Mo(t,C);p(r,{event:"step",step:"governance-lints",status:"ok",lints:le.lints}),p(r,{event:"step",step:"prune",status:"start"}),await cs(t,C),p(r,{event:"step",step:"prune",status:"ok"});{let v=Ke.join(t,"packages/platform/db/prisma/schema.prisma"),k;try{k=Lt.readFileSync(v,"utf-8")}catch(I){if(Ts(I))p(r,{event:"step",step:"schema-prune",status:"skip"});else{let x=I instanceof Error?I.message:String(I);p(r,{event:"step",step:"schema-prune",status:"error",error:x})}}if(k!==void 0){p(r,{event:"step",step:"schema-prune",status:"start"});try{let I=yn(k,H);Lt.writeFileSync(v,I),fn(Ke.join(Ke.dirname(v),"migrations"),H),p(r,{event:"step",step:"schema-prune",status:"ok"})}catch(I){let x=I instanceof Error?I.message:String(I);p(r,{event:"step",step:"schema-prune",status:"error",error:x})}}}if(p(r,{event:"step",step:"auth",choice:f,status:"start"}),await zn(t,f),p(r,{event:"step",step:"auth",choice:f,status:"ok"}),E.length>0?(p(r,{event:"step",step:"social-login",providers:E,status:"start"}),await Qn(t,E,f),p(r,{event:"step",step:"social-login",providers:E,status:"ok"})):p(r,{event:"step",step:"social-login",status:"skip"}),p(r,{event:"step",step:"db",choice:u,status:"start"}),await uo(t,u,n),u!=="none"){let v=st(d);v&&(await mo(t,v,n),p(r,{event:"step",step:"db-host",choice:d,status:"ok"}))}if(p(r,{event:"step",step:"db",choice:u,status:"ok"}),l==="drizzle"){p(r,{event:"step",step:"orm",choice:"drizzle",status:"start"});let v=await Zo(t,"drizzle",u);p(r,{event:"step",step:"orm",choice:"drizzle",status:v.applied?"ok":"skip",...v.reason?{reason:v.reason}:{}})}if(p(r,{event:"step",step:"payment",choice:h,status:"start"}),await ts(t,h),p(r,{event:"step",step:"payment",choice:h,status:"ok"}),await vo(t,i.email,c),r&&p(!0,{event:"step",step:"email",choice:i.email,status:"ok"}),await Cs(t,i.storage,c),r&&p(!0,{event:"step",step:"storage",choice:i.storage,status:"ok"}),await Ho(t,i.monitoring,c),r&&p(!0,{event:"step",step:"monitoring",choice:i.monitoring,status:"ok"}),await Wn(t,i.analytics,c),r&&p(!0,{event:"step",step:"analytics",choice:i.analytics,status:"ok"}),await Is(t,i.sms,c),r&&p(!0,{event:"step",step:"sms",choice:i.sms,status:"ok"}),await fs(t,i.queue,c),r&&p(!0,{event:"step",step:"queue",choice:i.queue,status:"ok"}),await Ps(t,i.search,c),r&&p(!0,{event:"step",step:"search",choice:i.search,status:"ok"}),await no(t,i.cache,c),r&&p(!0,{event:"step",step:"cache",choice:i.cache,status:"ok"}),await Xo(t,i.notifications,c),r&&p(!0,{event:"step",step:"notifications",choice:i.notifications,status:"ok"}),await Rs(t,i.webhooks,c),r&&p(!0,{event:"step",step:"webhooks",choice:i.webhooks,status:"ok"}),await io(t,i.cms,c),r&&p(!0,{event:"step",step:"cms",choice:i.cms,status:"ok"}),await Co(t,i.featureFlags,c),r&&p(!0,{event:"step",step:"feature-flags",choice:i.featureFlags,status:"ok"}),await so(t,i.captcha,c),r&&p(!0,{event:"step",step:"captcha",choice:i.captcha,status:"ok"}),await Ko(t,Z),r&&p(!0,{event:"step",step:"mcp",mode:Z,status:"ok"}),await Yo(t,G,_),r&&p(!0,{event:"step",step:"metering",mode:G,status:"ok"}),p(r,{event:"step",step:"prune-wave-features",status:"start"}),ps(t,j),p(r,{event:"step",step:"prune-wave-features",status:"ok",toggles:j}),C.orm==="prisma"&&C.database!=="none"?kc(r,t,H):p(r,{event:"step",step:"schema-prune",status:"skip"}),p(r,{event:"step",step:"billing-mode",choice:M,status:"ok"}),p(r,{event:"step",step:"idp",choice:ge,status:"ok"}),await ao(t,c),r&&p(!0,{event:"step",step:"compliance",region:c,status:"ok"}),await Ao(t),await ws(t,f),await ks(t,{projectName:n,region:c,previewSelections:K,waveFeatures:j}),C.aiMode!=="none"){p(r,{event:"step",step:"ai-providers",status:"start"});let v={providerIds:C.aiProviders,customEndpoint:C.customAiEndpoint},k=Ke.join(t,"packages/ai/ai-providers/templates");await is(t,v,k),p(r,{event:"step",step:"ai-providers",status:"ok"})}else p(r,{event:"step",step:"ai-providers",status:"skip"});p(r,{event:"step",step:"docs",status:"start"}),P!=="none"?(await yo(t,{framework:P,projectName:n}),p(r,{event:"step",step:"docs",framework:"fumadocs",requested:P,status:"ok"})):p(r,{event:"step",step:"docs",status:"skip"}),p(r,{event:"step",step:"deploy-target",status:"start"}),m!=="none"&&await Vn(t,m),await Bn(t,C.deployTargets),p(r,{event:"step",step:"deploy-target",status:"ok",targets:C.deployTargets});let w={databaseUrl:"postgresql://postgres:postgres@localhost:5432/nebutra",clerkPublishable:"",clerkSecret:""};p(r,{event:"step",step:"env",status:"start"}),await _o(t,w),p(r,{event:"step",step:"env",status:"ok"}),p(r,{event:"step",step:"scaffold-extras",status:"start"});let fe=await Ss(t,{projectName:n,withWorkflows:!!s.withWorkflows,withPythonBackend:!!s.withPythonBackend});p(r,{event:"step",step:"scaffold-extras",status:"ok",applied:fe.applied,skipped:fe.skipped}),p(r,{event:"step",step:"license",status:"start"});try{let v=Vo(t,{projectName:n,cliVersion:X});p(r,{event:"step",step:"license",status:"ok",tier:"independent",wrote:v.wrote}),r||process.stdout.write(mt.dim(` License: Nebutra-Sailor Independent Developer License (free for \u2264 1 FTE, < $1M ARR).
|
|
2817
|
+
Upstream AGPL preserved as LICENSE-AGPL-REFERENCE.md.
|
|
2818
|
+
`))}catch(v){p(r,{event:"step",step:"license",status:"warn",message:v instanceof Error?v.message:String(v)})}if(s.install!==!1){let v=o==="bun"?"bun":o,k=["install"];r?p(!0,{event:"step",step:"install",pm:o,status:"start"}):process.stdout.write(mt.dim(` Installing dependencies with ${o}\u2026
|
|
2819
|
+
`));try{Ot(v,k,{cwd:t,stdio:r?"ignore":"inherit"}),p(r,{event:"step",step:"install",status:"ok"})}catch(I){let x=I instanceof Error?I.message:String(I);r?p(!0,{event:"step",step:"install",status:"error",error:x}):process.stdout.write(mt.yellow(` \u26A0 install failed \u2014 run '${o} install' manually.
|
|
2820
|
+
`))}}else p(r,{event:"step",step:"install",status:"skip"});if(s.git!==!1){r&&p(!0,{event:"step",step:"git-init",status:"start"});try{Ot("git",["init","-q"],{cwd:t,stdio:"ignore"}),Ot("git",["add","-A"],{cwd:t,stdio:"ignore"}),Ot("git",["-c","user.email=you@example.com","-c","user.name=You","commit","-q","-m","chore: initial scaffold from create-sailor"],{cwd:t,stdio:"ignore"}),p(r,{event:"step",step:"git-init",status:"ok"})}catch(v){let k=v instanceof Error?v.message:String(v);r?p(!0,{event:"step",step:"git-init",status:"error",error:k}):process.stdout.write(mt.yellow(` \u26A0 git init skipped \u2014 not fatal.
|
|
2821
|
+
`))}}else p(r,{event:"step",step:"git-init",status:"skip"});let ye=Math.max(1,Math.round((Date.now()-a)/1e3));Xn({template_version:X,package_manager:o,region:c,auth:f,payment:h,ai_providers:S,deploy_target:m,duration_ms:Date.now()-a}),K.length>0&&Rc(r,K),r?p(!0,{event:"done",status:"ok",elapsedSec:ye,targetDir:t,previewSelections:K,waveFeatures:j}):qn({elapsedSec:ye,targetDir:t,skippedInstall:s.install===!1,previewSelections:K,waveFeatures:j});try{xc({pkg:{name:"create-sailor",version:X},updateCheckInterval:1e3*60*60*24}).notify({defer:!1,isGlobal:!0})}catch{}}import Tc from"cfonts";import Nc from"picocolors";function Os(){if(process.env.NO_COLOR||!process.stdout.isTTY){process.stdout.write([`Sailor v${X}`,"AI-Native SaaS Unicorn Template","by Nebutra \xB7 https://nebutra.com","",""].join(`
|
|
2822
|
+
`));return}Tc.say("Sailor",{font:"block",align:"left",colors:["#0033FE","#0BF1C3"],gradient:["#0033FE","#0BF1C3"],transitionGradient:!0,space:!1}),process.stdout.write(Nc.dim(` AI-Native SaaS Template \xB7 v${X} \xB7 nebutra.com
|
|
2955
2823
|
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2824
|
+
`))}var Oc=`Usage: create-sailor [name] [options]
|
|
2825
|
+
|
|
2826
|
+
Arguments:
|
|
2827
|
+
name project directory (default: ./my-saas-app)
|
|
2828
|
+
|
|
2829
|
+
Core options:
|
|
2830
|
+
-p, --pm <id> npm | pnpm | yarn | bun (auto-detected)
|
|
2831
|
+
--region <id> global | cn | hybrid (default: global)
|
|
2832
|
+
--auth <id> clerk | betterauth | nextauth | supabase | none
|
|
2833
|
+
--ai <ids> expert/non-interactive provider seed (e.g. openai,anthropic)
|
|
2834
|
+
|
|
2835
|
+
Interactive AI topology:
|
|
2836
|
+
Default first-run asks for topology, not individual provider enablement:
|
|
2837
|
+
gateway Multi-provider AI Gateway / router (recommended)
|
|
2838
|
+
direct Direct SDK/provider adapters
|
|
2839
|
+
custom OpenAI-compatible endpoint
|
|
2840
|
+
none Skip AI
|
|
2841
|
+
|
|
2842
|
+
Stack flags (region-based smart defaults):
|
|
2843
|
+
--orm <id> prisma | drizzle | none (default: prisma)
|
|
2844
|
+
--db <id> postgres | mysql | sqlite | none (default: postgres)
|
|
2845
|
+
--payment <id> stripe | lemon | wechat | alipay | none (default: region-based)
|
|
2846
|
+
--deploy <target> vercel | railway | cloudflare | selfhost (default: vercel)
|
|
2847
|
+
also writes per-service DEPLOY_TARGET_* selectors
|
|
2848
|
+
--docs <id> fumadocs | mintlify | docusaurus | nextra | vitepress | none
|
|
2849
|
+
|
|
2850
|
+
Feature flags (region-based smart defaults):
|
|
2851
|
+
--email <id> resend | postmark | ses | aliyun-dm | tencent-ses | netease | none
|
|
2852
|
+
--storage <id> r2 | s3 | supabase-storage | aliyun-oss | tencent-cos | qiniu | none
|
|
2853
|
+
--monitoring <id> sentry | datadog | aliyun-arms | tingyun | none
|
|
2854
|
+
--analytics <id> posthog | plausible | umami | baidu | sensors | none
|
|
2855
|
+
--sms <id> twilio | aliyun-sms | tencent-sms | yunpian | none
|
|
2856
|
+
--queue <id> qstash | bullmq | upstash | sqs | none [Foundation]
|
|
2857
|
+
--search <id> meilisearch | typesense | algolia | pgvector | none [Foundation]
|
|
2858
|
+
--cache <id> upstash-redis | vercel-kv | redis | dragonfly | none
|
|
2859
|
+
--notifications <id> novu | knock | custom | none [Foundation]
|
|
2860
|
+
--webhooks <id> svix | custom | none [Foundation]
|
|
2861
|
+
--cms <id> sanity | contentful | strapi | none
|
|
2862
|
+
--feature-flags <id> vercel-flags | growthbook | configcat | none [WIP]
|
|
2863
|
+
--captcha <id> turnstile | hcaptcha | aliyun-slide | none [WIP]
|
|
2864
|
+
--billing-mode <mode> usage | seat | credits (default: usage)
|
|
2865
|
+
--idp <id> clerk | oauth-server (default: clerk)
|
|
2866
|
+
--access-gate <mode> none | invite (default: none)
|
|
2867
|
+
|
|
2868
|
+
Preview statuses:
|
|
2869
|
+
[Foundation] \u2014 types + factory complete; adapters need external credentials & may not work out-of-the-box
|
|
2870
|
+
[WIP] \u2014 actively being built; do not use in production
|
|
2871
|
+
See: https://github.com/Nebutra/Nebutra-Sailor/blob/main/docs/package-status.md
|
|
2872
|
+
|
|
2873
|
+
Sailor features:
|
|
2874
|
+
--mcp <mode> on | off (default: on \u2014 Agent-friendly MCP server)
|
|
2875
|
+
--metering <mode> auto | on | off (default: auto)
|
|
2876
|
+
|
|
2877
|
+
Toggles:
|
|
2878
|
+
--i18n enable i18n (default: true)
|
|
2879
|
+
--no-i18n disable
|
|
2880
|
+
--no-install skip package install
|
|
2881
|
+
--no-git skip git init
|
|
2882
|
+
-y, --yes accept all defaults (non-interactive)
|
|
2883
|
+
--dry-run preview actions without writing files
|
|
2884
|
+
--json machine-readable output
|
|
2885
|
+
--no-color disable color output
|
|
2886
|
+
-h, --help show this help
|
|
2887
|
+
-v, --version show version
|
|
2888
|
+
|
|
2889
|
+
Examples:
|
|
2890
|
+
$ npx create-sailor@latest
|
|
2891
|
+
$ npx create-sailor@latest my-app -y
|
|
2892
|
+
$ npx create-sailor@latest my-app --region=cn --auth=clerk
|
|
2893
|
+
$ npx create-sailor@latest my-app --region=hybrid --ai=openai,deepseek
|
|
2894
|
+
$ npm create sailor@latest --dry-run --region=cn
|
|
2895
|
+
`;function Ls(){process.stdout.write(Oc)}import Ut from"picocolors";function Us(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function Lc(e){let t=Us()?"\u25B8":"->",n=`[${e.index}/${e.total}]`,o=e.label.padEnd(18," ");return Us()?`${Ut.dim(n)} ${Ut.bold(o)} ${Ut.dim(t)} ${Ut.cyan(e.value)}`:`${n} ${o} ${t} ${e.value}`}function Ms(e){process.stdout.write(Lc(e)+`
|
|
2896
|
+
`)}import{existsSync as Uc,mkdirSync as Ds,writeFileSync as Fs}from"fs";import{homedir as js,platform as Mc}from"os";import{join as gt}from"path";function Dc(){if(Mc()==="win32"){let t=process.env.LOCALAPPDATA||gt(js(),"AppData","Local");return gt(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?gt(e,"nebutra"):gt(js(),".config","nebutra")}function Fc(){let e=process.env.NEBUTRA_TELEMETRY;return e==="0"||e==="false"||e==="no"}function Bs(){if(!process.stderr.isTTY||!process.stdin.isTTY||process.env.CI)return;let e=Dc(),t=gt(e,"first-run-acked");try{if(Uc(t))return}catch{return}if(Fc()){try{Ds(e,{recursive:!0}),Fs(t,"")}catch{}return}let n=["[36m\u2139[0m Nebutra collects anonymous usage analytics to improve the scaffolder."," Opt out at any time: export NEBUTRA_TELEMETRY=0"," Privacy policy: https://nebutra.com/legal/cli-analytics",""];try{process.stderr.write(n.join(`
|
|
2897
|
+
`))}catch{}try{Ds(e,{recursive:!0}),Fs(t,"")}catch{}}function $e(e,t){e&&process.stdout.write(JSON.stringify(t)+`
|
|
2898
|
+
`)}function Bc(e,t,n,o,s){let{region:r,orm:i,database:a,paymentChoice:c,docs:l,socialLoginIds:u,aiMode:d,aiProviders:_,customAiEndpoint:h,deployTarget:f,metering:E,payment:S,billingMode:m,idp:P,mcp:G,waveToggles:M,previewSelections:ge}=t,{email:pe,storage:Z,monitoring:j,analytics:K,sms:C,queue:H,search:le,cache:w,notifications:fe,webhooks:Ye,cms:qe,featureFlags:ye,captcha:v}=t,k=_.length,I=[`clone template \u2192 ${n}`,"write nebutra.config.json",`governance-lints \u2192 write governance.config.json + wire pnpm lint (no-raw-inputs${a!=="none"?" + repository-seam":""})`,`prune template (orm=${i}, i18n=${t.i18n})`,`region \u2192 ${r}`,`auth \u2192 ${t.auth==="none"?"skip (remove packages/auth)":`configure ${t.auth}`}`,...u.length>0?[`social-login \u2192 generate ${u.length} callback route${u.length===1?"":"s"} + SocialLoginButtons.tsx (${u.join(", ")})`]:[],`db \u2192 ${a==="none"?"skip (remove packages/db)":`configure Prisma for ${a}`}`,`payment \u2192 ${c==="none"?"skip (remove packages/billing)":`configure ${c}`}`,...l!=="none"?[`docs \u2192 scaffold apps/docs (${l==="fumadocs"?"fumadocs":`${l} \u2192 fumadocs fallback`})`]:[],...k>0||h?[`ai-providers \u2192 generate ${d} registry seed + env (${k}${h?" + custom":""} provider${k===1&&!h?"":"s"})`]:[],...pe!=="none"?[`email \u2192 configure ${pe}`]:[],...Z!=="none"?[`storage \u2192 configure ${Z}`]:[],...j!=="none"?[`monitoring \u2192 configure ${j}`]:[],...K!=="none"?[`analytics \u2192 configure ${K}`]:[],...C!=="none"?[`sms \u2192 configure ${C}`]:[],...H!=="none"?[`queue \u2192 configure ${H}`]:[],...le!=="none"?[`search \u2192 configure ${le}`]:[],...w!=="none"?[`cache \u2192 configure ${w}`]:[],...fe!=="none"?[`notifications \u2192 configure ${fe}`]:[],...Ye!=="none"?[`webhooks \u2192 configure ${Ye}`]:[],...qe!=="none"?[`cms \u2192 configure ${qe}`]:[],...ye!=="none"?[`feature-flags \u2192 configure ${ye}`]:[],...v!=="none"?[`captcha \u2192 configure ${v}`]:[],`mcp \u2192 ${G==="on"?"enable MCP server":"remove packages/mcp"}`,`metering \u2192 ${E==="off"?"disabled":E==="auto"?S!=="none"?"enabled (auto: payment set)":"disabled (auto: no payment)":"enabled"}`,...m!=="usage"?[`billing-mode \u2192 ${m}`]:[],...P!=="clerk"?[`idp \u2192 ${P}`]:[],`cron-jobs \u2192 ${M.cronJobs?"enabled":"disabled"}`,`audit-log \u2192 ${M.auditLog?"enabled":"disabled"}`,`api-keys \u2192 ${M.apiKeys?"enabled":"disabled"}`,`command-palette \u2192 ${M.commandPalette?"enabled":"disabled"}`,`cookie-consent \u2192 ${M.cookieConsent?"enabled":"disabled"}`,`legal-pages \u2192 ${M.legalPages?"enabled":"disabled"}`,`china-compliance \u2192 ${M.chinaCompliance?"enabled (@nebutra/china-compliance + ICP footer)":"disabled"}`,`compliance \u2192 inject ${r} boilerplate (ICP/Cookie/AIGC/Privacy)`,"welcome \u2192 generate dev welcome page","env \u2192 generate random secrets (AUTH_SECRET, JWT_SECRET)","seed \u2192 generate prisma/seed.ts (1 admin + 3 tenants)",...f!=="none"?[`deploy \u2192 inject ${f} config`]:[],"inject .env.local",s.install===!1?"skip install":`run ${o} install`,s.git===!1?"skip git init":"run git init"];if(e){for(let x of I)$e(!0,{event:"plan",action:x});for(let x of ge)$e(!0,{event:"warn",step:x.flag,provider:x.provider,packageStatus:x.status});$e(!0,{event:"done",dryRun:!0})}else{process.stdout.write(`
|
|
2899
|
+
`+F.bold(`Dry run \u2014 planned actions:
|
|
2900
|
+
`));for(let x of I)process.stdout.write(` \u2022 ${x}
|
|
2901
|
+
`);if(ge.length>0){process.stdout.write(`
|
|
2902
|
+
`+F.bold(F.yellow(`Preview-status providers selected:
|
|
2903
|
+
`)));for(let x of ge)process.stdout.write(F.yellow(`\u26A0 ${x.flag}=${x.provider}
|
|
2904
|
+
`))}process.stdout.write(F.dim(`
|
|
2971
2905
|
No files were written.
|
|
2972
|
-
`))}process.exit(0)
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
`)),process.exit(
|
|
2906
|
+
`))}}async function Vc(){let e=Cn();e.parse(process.argv);let t=e.opts(),[n]=e.args;t.help&&(Ls(),process.exit(0));let o=!!t.json,s=!!t.dryRun,r=!!t.yes,i=r||!process.stdin.isTTY;if(o||(Bs(),Os()),$e(o,{event:"start",version:X}),!t.pm)try{jc("pnpm --version",{stdio:"ignore"})}catch{o||process.stderr.write(`
|
|
2907
|
+
${F.red("\u2718")} ${F.bold("pnpm is required")} but was not found on PATH.
|
|
2908
|
+
|
|
2909
|
+
The scaffold uses pnpm workspaces + Turborepo. Install pnpm first:
|
|
2910
|
+
${F.cyan("npm i -g pnpm@10")}
|
|
2911
|
+
|
|
2912
|
+
Then retry: ${F.cyan("pnpm dlx create-sailor@latest")}
|
|
2913
|
+
(or pass ${F.dim("--pm=npm")} if you know workspace:* won't resolve in your setup)
|
|
2914
|
+
|
|
2915
|
+
`),$e(o,{event:"error",code:"PNPM_MISSING"}),process.exit(1)}let a=n??(r?"./my-saas-app":void 0),c;if(a)c=a;else if(i)c="./my-saas-app";else{let f=await nt.group({name:()=>nt.text({message:"Where should we create your project?",placeholder:"./my-saas-app",defaultValue:"./my-saas-app",validate:E=>{if(!E?.length)return"Please enter a path."}})},{onCancel:()=>{process.stdout.write(F.red(`\u2718 Cancelled
|
|
2916
|
+
`)),process.exit(130)}});c=String(f.name)}let l=Ks.basename(Ks.resolve(c)),u=t.pm??Nn(),d=await Yn(t,o),_=[["Project name",l],["Region",d.region],["Auth",d.auth],["Social login",d.socialLoginIds.length>0?d.socialLoginIds.map(f=>He.find(E=>E.id===f)?.name??f).join(", "):"none"],["ORM",d.orm],["Database",d.database],["Payment",d.paymentChoice],["AI topology",`${d.aiMode}${d.aiProviders.length>0?` (${d.aiProviders.join(", ")} seed)`:""}${d.customAiEndpoint?" + custom endpoint":""}`],["Email",d.email],["Storage",d.storage],["Monitoring",d.monitoring],["Analytics",d.analytics],["SMS",d.sms],["Deploy Target",d.deployTarget],["Docs Framework",d.docs],["Access gate",d.accessGate]];o?_.forEach(([f,E],S)=>{$e(!0,{event:"step",step:f,value:E,index:S+1,total:_.length})}):_.forEach(([f,E],S)=>{Ms({index:S+1,total:_.length,label:f,value:E})}),s&&(Bc(o,d,c,u,t),process.exit(0));let h=async()=>{process.stdout.write(`
|
|
2917
|
+
`+F.red(`\u2718 Cancelled
|
|
2918
|
+
`)),Vs.existsSync(c)&&await nt.confirm({message:`Cleanup partial install at ${c}?`,initialValue:!0})===!0&&(Vs.rmSync(c,{recursive:!0,force:!0}),process.stdout.write(F.dim(` \u2713 Removed ${c}
|
|
2919
|
+
`))),process.exit(130)};process.on("SIGINT",h);try{await Ns({resolvedTarget:c,projectName:l,resolvedPm:u,opts:t,useJson:o,resolved:d,startedAt:Date.now()}),process.exit(0)}catch(f){let E=f instanceof Error?f.message:String(f);o?$e(!0,{event:"error",message:E}):process.stdout.write(F.red(`
|
|
2920
|
+
\u2718 Failed: ${E}
|
|
2921
|
+
`)),process.exit(1)}}Vc().catch(e=>{process.stderr.write(String(e)+`
|
|
2983
2922
|
`),process.exit(1)});
|