create-sailor 1.9.2 → 1.9.3
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 +6 -1
- package/dist/index.js +200 -195
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as Cn}from"./chunk-PA4CKDRJ.js";import"./chunk-IYWUY3VA.js";import{execSync as Wc}from"child_process";import qr from"fs";import Gr from"path";import*as ot from"@clack/prompts";import F from"picocolors";import{Command as Jr}from"commander";import xn from"fs";import Vt from"path";import{fileURLToPath as Xr}from"url";function zr(){try{let t=Vt.dirname(Xr(import.meta.url));for(let n=0;n<4;n++){let o=Vt.join(t,"package.json");if(xn.existsSync(o)){let r=xn.readFileSync(o,"utf-8"),s=JSON.parse(r);if(s.name==="create-sailor"&&typeof s.version=="string")return s.version}t=Vt.dirname(t)}return"0.0.0-dev"}catch{return"0.0.0-dev"}}var X=zr();var Qr="create-sailor";function Rn(){let e=new Jr;return e.name(Qr).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 Nn from"picocolors";var $t=[{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 Postgres",region:"global",supportedEngines:["postgresql"],forcedEngine:"postgresql",keepDirectUrl:!0,envVars:[{name:"DATABASE_URL",placeholder:"postgresql://postgres.<branch-id>:<password>@<host>.horizon.psdb.cloud:6432/postgres?sslmode=require",comment:"PlanetScale Postgres pooled/PgBouncer URL \u2014 runtime queries"},{name:"DIRECT_URL",placeholder:"postgresql://postgres.<branch-id>:<password>@<host>.horizon.psdb.cloud:5432/postgres?sslmode=require",comment:"PlanetScale Postgres direct URL \u2014 Prisma migrations"}],docs:"https://planetscale.com/docs/postgres",description:"Managed Postgres with branching; compatible with Sailor migrations.",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 $t.find(t=>t.id===e)}function Tn(e){return e==="cn"||e==="hybrid"?"local":"supabase"}var it=[{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 Kt(e){let t=e==="supabase"?"supabase-storage":e;return it.some(n=>n.id===t)?t:void 0}function kn(e){let t=Kt(e);return t?it.find(n=>n.id===t):void 0}var sp=it.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Ln(){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()??Tn(t),r=st(o);if(!r){let i=$t.map(a=>a.id).join(", ");throw new Error(`Unknown --db-host="${o}". Valid: ${i}`)}let s=n;return r.forcedEngine&&s!=="none"&&s!==r.forcedEngine&&(s=r.forcedEngine),{hostId:r.id,engine:s}}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 Zr=new Set(["gateway","direct","custom","none"]);function zt(e){if(!e)return!1;let t=e.trim().toLowerCase();return Zr.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 Un(e,t){let n=e??t;return Kt(n)??n}var On={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 On){let o=On[n];return t?process.stdout.write(JSON.stringify({event:"notice",kind:"docs-fallback",requested:n,effective:"fumadocs"})+`
|
|
3
|
-
`):process.stdout.write(
|
|
4
|
-
`)+
|
|
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 Dn(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 hs 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.sensenova.cn/docs",baseURL:"https://token.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:[]}],dp=nn.reduce((e,t)=>(e[t.category]||(e[t.category]=[]),e[t.category].push(t),e),{});var es=["openai","anthropic","google"],ts=["openai"];function on(e){let t=e.providerIds??[];switch(e.mode){case"gateway":{let n=t.length>0?t:[...es];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:[...ts];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 We=[{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"}],ns=new Set(We.map(e=>e.id));function Mn(e){return We.find(t=>t.id===e)}function Fn(e){if(!e)return[];let t=e.split(",").map(r=>r.trim().toLowerCase()).filter(Boolean);if(t.includes("none"))return[];let n=[],o=new Set;for(let r of t)ns.has(r)&&!o.has(r)&&(n.push(r),o.add(r));return n}import ee from"fs";import k from"path";import{fileURLToPath as ps}from"url";var jn=["web","landing","auth","admin","design-docs","sailor-docs","router","forge","typelens","gateway","python-ai"],os={web:"frontend",landing:"frontend",auth:"frontend",admin:"frontend","design-docs":"frontend","sailor-docs":"frontend",router:"frontend",forge:"frontend",typelens:"frontend",gateway:"edgeGateway","python-ai":"originBackend"},rs={frontend:"vercel",edgeGateway:"cloudflare-workers",originBackend:"ecs-docker"};function ss(e){return jn.includes(e)}function is(e){if(!ss(e))throw new Error(`Unknown deploy service '${e}'. Deploy targets apply only to apps/backends, not packages.`)}function as(e){return is(e),os[e]}function cs(e){let t=as(e);return rs[t]}function Bn(){return Object.fromEntries(jn.map(e=>[e,cs(e)]))}var ls=ps(import.meta.url),Vn=k.dirname(ls),ds="cloudflare-pages",us="standalone",ms="railway";function rn(e,t,n,o){return{...e,web:t,landing:t,auth:t,admin:t,"design-docs":t,"sailor-docs":t,router:t,forge:t,typelens:t,gateway:n,"python-ai":o}}function gs(){let e=[k.join(Vn,"..","templates","deploy"),k.join(Vn,"..","..","templates","deploy")];for(let t of e)if(ee.existsSync(t))return t;return e[0]}function Kn(e){let t=Bn();switch(e){case"cloudflare":return rn(t,ds,"cloudflare-workers","ecs-docker");case"selfhost":return rn(t,us,"ecs-docker","ecs-docker");case"railway":return rn(t,ms,"railway","railway");case"none":case"vercel":return t}}function fs(e){return`DEPLOY_TARGET_${e.toUpperCase().replace(/-/g,"_")}`}async function $n(e,t){let n=k.join(e,"backends","gateway");ee.existsSync(n)&&await ee.promises.copyFile(k.join(t,"wrangler.gateway.toml"),k.join(n,"wrangler.toml"))}async function Yn(e,t){let n=k.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 s=["","# Deployment Targets",...Object.entries(t).map(([i,a])=>`${fs(i)}="${a}"`)].join(`
|
|
6
|
-
`);await
|
|
7
|
-
`)}async function
|
|
8
|
-
`)),process.exit(130)}})}async function
|
|
2
|
+
import{a as Ln}from"./chunk-PA4CKDRJ.js";import"./chunk-IYWUY3VA.js";import{execSync as ip}from"child_process";import ts from"fs";import*as ns from"@clack/prompts";import D from"picocolors";import{Command as as}from"commander";import Un from"fs";import qt from"path";import{fileURLToPath as ss}from"url";function is(){try{let t=qt.dirname(ss(import.meta.url));for(let n=0;n<4;n++){let o=qt.join(t,"package.json");if(Un.existsSync(o)){let r=Un.readFileSync(o,"utf-8"),s=JSON.parse(r);if(s.name==="create-sailor"&&typeof s.version=="string")return s.version}t=qt.dirname(t)}return"0.0.0-dev"}catch{return"0.0.0-dev"}}var z=is();var cs="create-sailor";function Dn(){let e=new as;return e.name(cs).description("Nebutra-Sailor \u2014 AI-Native SaaS template").version(z,"-v, --version").helpOption(!1).argument("[name]","project name or path (default: my-app; use . for current 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 jn from"picocolors";var Gt=[{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 Postgres",region:"global",supportedEngines:["postgresql"],forcedEngine:"postgresql",keepDirectUrl:!0,envVars:[{name:"DATABASE_URL",placeholder:"postgresql://postgres.<branch-id>:<password>@<host>.horizon.psdb.cloud:6432/postgres?sslmode=require",comment:"PlanetScale Postgres pooled/PgBouncer URL \u2014 runtime queries"},{name:"DIRECT_URL",placeholder:"postgresql://postgres.<branch-id>:<password>@<host>.horizon.psdb.cloud:5432/postgres?sslmode=require",comment:"PlanetScale Postgres direct URL \u2014 Prisma migrations"}],docs:"https://planetscale.com/docs/postgres",description:"Managed Postgres with branching; compatible with Sailor migrations.",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 pt(e){return Gt.find(t=>t.id===e)}function Mn(e){return e==="cn"||e==="hybrid"?"local":"supabase"}var lt=[{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 Ht(e){let t=e==="supabase"?"supabase-storage":e;return lt.some(n=>n.id===t)?t:void 0}function Fn(e){let t=Ht(e);return t?lt.find(n=>n.id===t):void 0}var Sp=lt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function $n(){let e=process.env.npm_config_user_agent??"";return e.startsWith("pnpm")?"pnpm":e.startsWith("yarn")?"yarn":e.startsWith("bun")?"bun":"npm"}function Wt(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 Xt(e,t,n){let o=e?.trim().toLowerCase()??Mn(t),r=pt(o);if(!r){let i=Gt.map(a=>a.id).join(", ");throw new Error(`Unknown --db-host="${o}". Valid: ${i}`)}let s=n;return r.forcedEngine&&s!=="none"&&s!==r.forcedEngine&&(s=r.forcedEngine),{hostId:r.id,engine:s}}function zt(e){return e==="drizzle"?"drizzle":"prisma"}function Jt(e){return e==="lemon"||e==="lemonsqueezy"?"lemon":e==="wechat"?"wechat":e==="alipay"?"alipay":e==="none"?"none":"stripe"}function Qt(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 Zt(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 ps=new Set(["gateway","direct","custom","none"]);function en(e){if(!e)return!1;let t=e.trim().toLowerCase();return ps.has(t)}function tn(e){if(!e)return["openai"];if(en(e))return[];let t=e.split(",").map(n=>n.trim().toLowerCase());return t.includes("none")?[]:t}function Vn(e,t){let n=e??t;return Ht(n)??n}var Bn={mintlify:"Mintlify",docusaurus:"Docusaurus",nextra:"Nextra",vitepress:"VitePress"};function nn(e,t){if(!e)return"fumadocs";let n=e.toLowerCase();if(n==="fumadocs"||n==="none")return n;if(n in Bn){let o=Bn[n];return t?process.stdout.write(JSON.stringify({event:"notice",kind:"docs-fallback",requested:n,effective:"fumadocs"})+`
|
|
3
|
+
`):process.stdout.write(jn.yellow(`\u26A0 ${o} support is coming in v1.2. Falling back to fumadocs.
|
|
4
|
+
`)+jn.dim(` Track progress: https://github.com/Nebutra/Nebutra-Sailor/issues
|
|
5
|
+
`)),"fumadocs"}return"fumadocs"}function on(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 rn(e){if(!e)return"global";let t=e.toLowerCase();return t==="cn"?"cn":t==="hybrid"?"hybrid":"global"}function Kn(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 sn(e){return e==="cn"?"wechat":"stripe"}import*as q from"@clack/prompts";import xs from"picocolors";var an=[{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.sensenova.cn/docs",baseURL:"https://token.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:[]}],Pp=an.reduce((e,t)=>(e[t.category]||(e[t.category]=[]),e[t.category].push(t),e),{});var ls=["openai","anthropic","google"],ds=["openai"];function cn(e){let t=e.providerIds??[];switch(e.mode){case"gateway":{let n=t.length>0?t:[...ls];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:[...ds];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 Je=[{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"}],us=new Set(Je.map(e=>e.id));function Yn(e){return Je.find(t=>t.id===e)}function qn(e){if(!e)return[];let t=e.split(",").map(r=>r.trim().toLowerCase()).filter(Boolean);if(t.includes("none"))return[];let n=[],o=new Set;for(let r of t)us.has(r)&&!o.has(r)&&(n.push(r),o.add(r));return n}import te from"fs";import k from"path";import{fileURLToPath as Es}from"url";var Gn=["web","landing","auth","admin","design-docs","sailor-docs","router","forge","typelens","gateway","python-ai"],ms={web:"frontend",landing:"frontend",auth:"frontend",admin:"frontend","design-docs":"frontend","sailor-docs":"frontend",router:"frontend",forge:"frontend",typelens:"frontend",gateway:"edgeGateway","python-ai":"originBackend"},gs={frontend:"vercel",edgeGateway:"cloudflare-workers",originBackend:"ecs-docker"};function fs(e){return Gn.includes(e)}function ys(e){if(!fs(e))throw new Error(`Unknown deploy service '${e}'. Deploy targets apply only to apps/backends, not packages.`)}function Ss(e){return ys(e),ms[e]}function hs(e){let t=Ss(e);return gs[t]}function Hn(){return Object.fromEntries(Gn.map(e=>[e,hs(e)]))}var vs=Es(import.meta.url),Wn=k.dirname(vs),bs="cloudflare-pages",_s="standalone",Ps="railway";function pn(e,t,n,o){return{...e,web:t,landing:t,auth:t,admin:t,"design-docs":t,"sailor-docs":t,router:t,forge:t,typelens:t,gateway:n,"python-ai":o}}function ws(){let e=[k.join(Wn,"..","templates","deploy"),k.join(Wn,"..","..","templates","deploy")];for(let t of e)if(te.existsSync(t))return t;return e[0]}function zn(e){let t=Hn();switch(e){case"cloudflare":return pn(t,bs,"cloudflare-workers","ecs-docker");case"selfhost":return pn(t,_s,"ecs-docker","ecs-docker");case"railway":return pn(t,Ps,"railway","railway");case"none":case"vercel":return t}}function As(e){return`DEPLOY_TARGET_${e.toUpperCase().replace(/-/g,"_")}`}async function Xn(e,t){let n=k.join(e,"backends","gateway");te.existsSync(n)&&await te.promises.copyFile(k.join(t,"wrangler.gateway.toml"),k.join(n,"wrangler.toml"))}async function Jn(e,t){let n=k.join(e,".env.example"),o;try{o=await te.promises.readFile(n,"utf8")}catch(i){if(i.code==="ENOENT")return;throw i}if(o.includes("# Deployment Targets"))return;let s=["","# Deployment Targets",...Object.entries(t).map(([i,a])=>`${As(i)}="${a}"`)].join(`
|
|
6
|
+
`);await te.promises.appendFile(n,`${s}
|
|
7
|
+
`)}async function Qn(e,t){if(t==="none")return;let n=ws();t==="vercel"?(await te.promises.copyFile(k.join(n,"vercel.json"),k.join(e,"vercel.json")),await Xn(e,n)):t==="railway"?await te.promises.copyFile(k.join(n,"railway.toml"),k.join(e,"railway.toml")):t==="cloudflare"?(await te.promises.copyFile(k.join(n,"wrangler.toml"),k.join(e,"wrangler.toml")),await Xn(e,n)):t==="selfhost"&&(await te.promises.copyFile(k.join(n,"docker-compose.yml"),k.join(e,"docker-compose.yml")),await te.promises.copyFile(k.join(n,"Dockerfile.web"),k.join(e,"Dockerfile.web")))}var Cs={qstash:"foundation",bullmq:"foundation",upstash:"foundation",sqs:"foundation",meilisearch:"foundation",typesense:"foundation",algolia:"foundation",pgvector:"foundation","vercel-flags":"foundation",growthbook:"foundation",configcat:"foundation",turnstile:"wip",hcaptcha:"wip","aliyun-slide":"wip",invite:"foundation"};function Is(e){return e?Cs[e]??"stable":"stable"}function Qe(e){switch(e){case"stable":return"";case"foundation":return"[Foundation]";case"wip":return"[WIP]";case"deprecated":return"[Deprecated]"}}function _e(e){switch(e){case"stable":return"Production-ready.";case"foundation":return"Foundation status \u2014 core contracts work, but production use still requires provider credentials, adapter wiring, or a verified integration contract.";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 Zn(e){let t=[];for(let{flag:n,provider:o}of e){if(!o||o==="none")continue;let r=Is(o);r!=="stable"&&t.push({flag:n,provider:o,status:r})}return t}function Pe(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 eo(e,t){let n=t==="cn";return{cronJobs:Pe(e.cronJobs,!0),auditLog:Pe(e.auditLog,!1),apiKeys:Pe(e.apiKeys,!0),webhooks:Pe(e.webhooks,!0),commandPalette:Pe(e.commandPalette,!0),cookieConsent:Pe(e.cookieConsent,!0),legalPages:Pe(e.legalPages,!0),chinaCompliance:Pe(e.chinaCompliance,n)}}async function Rs(e,t,n,o){let r={};return t||(r.region=()=>q.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||(r.auth=()=>q.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||(r.aiMode=()=>q.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"}),r.aiProviders=({results:s})=>{if(s.aiMode!=="direct")return Promise.resolve([]);let i=an.map(a=>({value:a.id,label:`[${a.category}] ${a.name}`}));return q.multiselect({message:"Select direct AI provider adapters (expert path)",options:i,initialValues:["openai"],required:!1})},r.customAiName=({results:s})=>s.aiMode==="custom"?q.text({message:"Custom endpoint name (e.g. proxy, local):",defaultValue:"custom",placeholder:"custom"}):Promise.resolve(void 0),r.customAiBaseUrl=({results:s})=>s.aiMode==="custom"?q.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),r.customAiApiKeyEnv=({results:s})=>s.aiMode==="custom"?q.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(r).length===0?{}:await q.group(r,{onCancel:()=>{process.stdout.write(xs.red(`\u2718 Cancelled
|
|
8
|
+
`)),process.exit(130)}})}async function to(e,t){let n=!!e.region,o=!!e.auth,r=!!e.payment,s=!!e.ai,i=!!e.email,a=!!e.storage,c=!!e.monitoring,p=!!e.analytics,d=!!e.sms,m=e.i18n!==void 0,E=!!e.yes||!process.stdin.isTTY,g,h,S,u,_,H,F,he=qn(e.socialLogin),le,ee,B,K,I,W,de;if(E){g=rn(e.region),h=zt(e.orm);{let Y=Wt(e.db),vt=Xt(e.dbHost,g,Y);S=vt.engine,u=vt.hostId}let X=r?e.payment:sn(g);_=Jt(X),H=Qt(X),F=Zt(e.auth);let ze="gateway";if(s){let Y=e.ai?.trim().toLowerCase()??"";en(Y)?ze=Y:ze="direct"}let ct=cn({mode:ze,providerIds:s?tn(e.ai):void 0});le=ct.mode,ee=ct.routing,B=ct.providerIds,I=on(e.deploy),W=nn(e.docs,t),de=m?!!e.i18n:!0}else{let X=await Rs(e,n,o,s);g=rn(n?e.region:X.region),h=zt(e.orm);{let vt=Wt(e.db),On=Xt(e.dbHost,g,vt);S=On.engine,u=On.hostId}let ze=r?e.payment:sn(g);_=Jt(ze),H=Qt(ze),F=Zt(o?e.auth:X.auth);let ct=X.aiMode==="custom"?{name:X.customAiName??"custom",baseURL:X.customAiBaseUrl??"",apiKeyEnvName:X.customAiApiKeyEnv??"CUSTOM_AI_API_KEY"}:void 0,Y=cn({mode:s?e.ai?.trim().toLowerCase()==="none"?"none":"direct":X.aiMode??"gateway",providerIds:s?tn(e.ai):X.aiProviders,customEndpoint:ct});le=Y.mode,ee=Y.routing,B=Y.providerIds,Y.customEndpoint&&(K={name:Y.customEndpoint.name,baseURL:Y.customEndpoint.baseURL,apiKeyEnvName:Y.customEndpoint.apiKeyEnvName}),I=on(e.deploy),W=nn(e.docs,t),de=m?!!e.i18n:!0}let P=Kn(g),Ee=i?e.email:P.email,We=Vn(a?e.storage:void 0,P.storage),Xe=c?e.monitoring:P.monitoring,ve=p?e.analytics:P.analytics,v=d?e.sms:P.sms,T=e.queue??P.queue,A=e.search??P.search,x=e.cache??P.cache,Bt=e.notifications??P.notifications,$t=e.webhooks??P.webhooks,In=e.cms??P.cms,Vt=e.featureFlags??P.featureFlags,Kt=e.captcha??P.captcha,xn=e.mcp??P.mcp,Rn=e.metering??P.metering,Tn=e.billingMode??P.billingMode,kn=e.idp??P.idp,Yt=e.accessGate==="invite"?"invite":P.accessGate,Nn=zn(I),be=eo({cronJobs:e.cronJobs,auditLog:e.auditLog,apiKeys:e.apiKeys,commandPalette:e.commandPalette,cookieConsent:e.cookieConsent,legalPages:e.legalPages,chinaCompliance:e.chinaCompliance},g),os=Zn([{flag:"queue",provider:T},{flag:"search",provider:A},{flag:"notifications",provider:Bt},{flag:"webhooks",provider:$t},{flag:"feature-flags",provider:Vt},{flag:"captcha",provider:Kt},{flag:"access-gate",provider:Yt}]),rs={region:g,orm:h,database:S,databaseHost:u,payment:_,aiMode:le,aiRouting:ee,aiProviders:B,customAiEndpoint:K,deployTarget:I,deployTargets:Nn,docs:W,i18n:de,email:Ee,storage:We,monitoring:Xe,analytics:ve,sms:v,queue:T,search:A,cache:x,notifications:Bt,webhooks:$t,cms:In,featureFlags:Vt,captcha:Kt,mcp:xn,metering:Rn,billingMode:Tn,idp:kn,accessGate:Yt,cronJobs:be.cronJobs,auditLog:be.auditLog,apiKeys:be.apiKeys,commandPalette:be.commandPalette,cookieConsent:be.cookieConsent,legalPages:be.legalPages,chinaCompliance:be.chinaCompliance};return{region:g,orm:h,database:S,databaseHost:u,payment:_,paymentChoice:H,auth:F,socialLoginIds:he,aiMode:le,aiRouting:ee,aiProviders:B,customAiEndpoint:K,deployTarget:I,deployTargets:Nn,docs:W,i18n:de,email:Ee,storage:We,monitoring:Xe,analytics:ve,sms:v,queue:T,search:A,cache:x,notifications:Bt,webhooks:$t,cms:In,featureFlags:Vt,captcha:Kt,mcp:xn,metering:Rn,billingMode:Tn,idp:kn,accessGate:Yt,waveToggles:be,previewSelections:os,config:rs}}import{execFileSync as Mt}from"child_process";import Ft from"fs";import He from"path";import ht from"picocolors";import Hc from"update-notifier";import w from"picocolors";function Ts(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function no(e){let t=Ts(),n=t?"\u2693":"-",o=t?"\u25B8":"->",r=t?"\u2605":"*",s=t?"\u{1F4E7}":"*",i=e.targetDir==="."||e.targetDir==="./"||e.targetDir===".\\",a=i?".":e.targetDir.endsWith("/")?e.targetDir:`${e.targetDir}/`,p=["",` ${t?w.bold(`${n} Done in ${e.elapsedSec}s \xB7 ${a}`):`${n} Done in ${e.elapsedSec}s \xB7 ${a}`}`,"",` ${t?w.bold("Next:"):"Next:"}`];i||p.push(` ${o} cd ${e.targetDir}`),e.skippedInstall&&p.push(` ${o} pnpm install (if --no-install was set)`),p.push(` ${o} fill in .env.local ${t?w.dim("\u2192 add provider keys before booting apps"):"-> add provider keys before booting apps"}`,` ${o} pnpm db:migrate ${t?w.dim("\u2192 sync Prisma schema"):"-> sync Prisma schema"}`,` ${o} pnpm db:seed ${t?w.dim("\u2192 load example tenants and records"):"-> load example tenants and records"}`,` ${o} pnpm dev ${t?w.dim("\u2192 http://localhost:3000"):"-> http://localhost:3000"}`,"",` ${t?w.bold("Customize:"):"Customize:"}`,` ${o} pnpm brand:init ${t?w.dim("\u2192 create brand.config.ts"):"-> create brand.config.ts"}`,` ${o} pnpm brand:apply ${t?w.dim("\u2192 propagate brand changes"):"-> propagate brand changes"}`,` ${o} pnpm preset:env ${t?w.dim("\u2192 review preset env requirements"):"-> review preset env requirements"}`,"",` ${t?w.bold("Useful scripts:"):"Useful scripts:"}`,` ${o} pnpm infra:up ${t?w.dim("\u2192 start local PostgreSQL/Redis/ClickHouse"):"-> start local PostgreSQL/Redis/ClickHouse"}`,` ${o} pnpm generate:api-types ${t?w.dim("\u2192 refresh shared API client types"):"-> refresh shared API client types"}`,` ${o} pnpm lint ${t?w.dim("\u2192 quick repo sanity check"):"-> quick repo sanity check"}`,` ${o} pnpm audit ${t?w.dim("\u2192 scan deps for known CVEs"):"-> scan deps for known CVEs"}`);let d=e.previewSelections??[];if(d.length>0){let g=t?w.bold(w.yellow("\u26A0 Preview features selected:")):"!! Preview features selected:";p.push("",` ${g}`);for(let h of d){let S=Qe(h.status),u=` ${o} ${h.flag} (${h.provider}) ${S}`;p.push(t?w.yellow(u):u)}p.push("",` ${t?w.dim(_e("foundation")):_e("foundation")}`,` ${t?w.dim("You may need to contribute the provider integration yourself."):"You may need to contribute the provider integration yourself."}`,` ${t?w.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 m=e.waveFeatures??{},C=g=>g!==!1,E=[];C(m.apiKeys)&&E.push("Manage API keys at /settings/api-keys"),C(m.auditLog)&&E.push("View audit log at /settings/audit-log"),E.push("Configure webhooks at /settings/webhooks"),C(m.commandPalette)&&E.push("Press Cmd/Ctrl+K to open the command palette"),E.push("Edit notification preferences at /settings/notifications"),E.push("Export your data at /settings/account/export"),m.chinaCompliance&&E.push("China deployments \u2014 see packages/ops/china-compliance/README.md"),p.push("",` ${t?w.bold("What you can do next:"):"What you can do next:"}`);for(let g of E)p.push(` ${o} ${g}`);p.push("",` ${t?w.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(p.join(`
|
|
9
9
|
`)+`
|
|
10
|
-
`)}import
|
|
11
|
-
`+t):
|
|
10
|
+
`)}import Ze from"fs";import $ from"path";var oo=[{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:""}],Wp=oo.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function ro(e){return oo.find(t=>t.id===e)}function ks(e){Ze.mkdirSync(e,{recursive:!0})}function ne(e,t){ks($.dirname(e)),Ze.writeFileSync(e,t)}function Te(e,t){let n=$.join(e,".env.example");Ze.existsSync(n)?Ze.appendFileSync(n,`
|
|
11
|
+
`+t):Ze.writeFileSync(n,t)}function Ns(e){let t=`"use client";
|
|
12
12
|
/**
|
|
13
13
|
* PostHog browser SDK \u2014 lazy-initialized on first access.
|
|
14
14
|
*/
|
|
@@ -91,12 +91,12 @@ export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
|
|
|
91
91
|
|
|
92
92
|
return <>{children}</>;
|
|
93
93
|
}
|
|
94
|
-
`;
|
|
94
|
+
`;ne($.join(e,"src","lib","analytics","posthog.client.ts"),t),ne($.join(e,"src","lib","analytics","posthog.server.ts"),n),ne($.join(e,"src","components","analytics","AnalyticsProvider.tsx"),o)}function Os(){return`# PostHog \u2014 product analytics
|
|
95
95
|
POSTHOG_KEY=
|
|
96
96
|
POSTHOG_HOST=https://us.i.posthog.com
|
|
97
97
|
NEXT_PUBLIC_POSTHOG_KEY=
|
|
98
98
|
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
|
|
99
|
-
`}function
|
|
99
|
+
`}function Ls(e){ne($.join(e,"src","components","analytics","PlausibleScript.tsx"),`"use client";
|
|
100
100
|
/**
|
|
101
101
|
* Plausible script tag \u2014 lightweight, privacy-friendly analytics.
|
|
102
102
|
*/
|
|
@@ -116,10 +116,10 @@ export function PlausibleScript() {
|
|
|
116
116
|
/>
|
|
117
117
|
);
|
|
118
118
|
}
|
|
119
|
-
`)}function
|
|
119
|
+
`)}function Us(){return`# Plausible \u2014 privacy-friendly analytics
|
|
120
120
|
NEXT_PUBLIC_PLAUSIBLE_DOMAIN=
|
|
121
121
|
NEXT_PUBLIC_PLAUSIBLE_SRC=https://plausible.io/js/script.js
|
|
122
|
-
`}function
|
|
122
|
+
`}function Ds(e){ne($.join(e,"src","components","analytics","UmamiScript.tsx"),`"use client";
|
|
123
123
|
/**
|
|
124
124
|
* Umami analytics \u2014 self-hosted OR umami.is cloud.
|
|
125
125
|
*/
|
|
@@ -139,10 +139,10 @@ export function UmamiScript() {
|
|
|
139
139
|
/>
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
|
-
`)}function
|
|
142
|
+
`)}function Ms(){return`# Umami \u2014 self-hostable analytics
|
|
143
143
|
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
|
|
144
144
|
NEXT_PUBLIC_UMAMI_SRC=https://cloud.umami.is/script.js
|
|
145
|
-
`}function
|
|
145
|
+
`}function Fs(e){let t=`"use client";
|
|
146
146
|
import mixpanel from "mixpanel-browser";
|
|
147
147
|
|
|
148
148
|
let initialized = false;
|
|
@@ -181,10 +181,10 @@ export function trackServer(
|
|
|
181
181
|
if (!c) return;
|
|
182
182
|
c.track(event, { distinct_id: distinctId, ...properties });
|
|
183
183
|
}
|
|
184
|
-
`;
|
|
184
|
+
`;ne($.join(e,"src","lib","analytics","mixpanel.client.ts"),t),ne($.join(e,"src","lib","analytics","mixpanel.server.ts"),n)}function js(){return`# Mixpanel \u2014 product analytics
|
|
185
185
|
MIXPANEL_TOKEN=
|
|
186
186
|
NEXT_PUBLIC_MIXPANEL_TOKEN=
|
|
187
|
-
`}function
|
|
187
|
+
`}function Bs(e){ne($.join(e,"src","components","analytics","BaiduStatsScript.tsx"),`"use client";
|
|
188
188
|
/**
|
|
189
189
|
* \u767E\u5EA6\u7EDF\u8BA1 Baidu Tongji \u2014 hm.js \u57CB\u70B9\u811A\u672C.
|
|
190
190
|
*/
|
|
@@ -210,9 +210,9 @@ var _hmt = _hmt || [];
|
|
|
210
210
|
/>
|
|
211
211
|
);
|
|
212
212
|
}
|
|
213
|
-
`)}function
|
|
213
|
+
`)}function $s(){return`# \u767E\u5EA6\u7EDF\u8BA1 \u2014 \u4E2D\u56FD\u533A\u7F51\u7AD9\u5206\u6790
|
|
214
214
|
NEXT_PUBLIC_BAIDU_STATS_ID=
|
|
215
|
-
`}function
|
|
215
|
+
`}function Vs(e){ne($.join(e,"src","lib","analytics","sensors.client.ts"),`"use client";
|
|
216
216
|
/**
|
|
217
217
|
* \u795E\u7B56\u5206\u6790 (Sensors Analytics) \u2014 sa-sdk-javascript \u5C01\u88C5.
|
|
218
218
|
*/
|
|
@@ -240,10 +240,10 @@ export function initSensors(): void {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
export { sensors };
|
|
243
|
-
`)}function
|
|
243
|
+
`)}function Ks(){return`# \u795E\u7B56\u5206\u6790 Sensors Analytics
|
|
244
244
|
NEXT_PUBLIC_SENSORS_SERVER_URL=
|
|
245
245
|
NEXT_PUBLIC_SENSORS_APP_JS_URL=
|
|
246
|
-
`}function
|
|
246
|
+
`}function Ys(e){ne($.join(e,"src","components","analytics","GrowingIoScript.tsx"),`"use client";
|
|
247
247
|
/**
|
|
248
248
|
* GrowingIO CDP \u2014 inline snippet installer.
|
|
249
249
|
*/
|
|
@@ -268,21 +268,21 @@ gdp("send");
|
|
|
268
268
|
/>
|
|
269
269
|
);
|
|
270
270
|
}
|
|
271
|
-
`)}function
|
|
271
|
+
`)}function qs(){return`# GrowingIO \u2014 \u5168\u57CB\u70B9 CDP (\u4E2D\u56FD\u533A)
|
|
272
272
|
NEXT_PUBLIC_GROWINGIO_ACCOUNT_ID=
|
|
273
|
-
`}async function
|
|
274
|
-
`+t):
|
|
275
|
-
`+t)}async function
|
|
273
|
+
`}async function so(e,t,n="global"){let o=ro(t);if(!o||o.id==="none")return;let r=$.join(e,"apps","web");if(!Ze.existsSync(r))return;let s=o.id;try{switch(s){case"posthog":Ns(r),Te(e,Os());return;case"plausible":Ls(r),Te(e,Us());return;case"umami":Ds(r),Te(e,Ms());return;case"mixpanel":Fs(r),Te(e,js());return;case"baidu":Bs(r),Te(e,$s());return;case"sensors":Vs(r),Te(e,Ks());return;case"growingio":Ys(r),Te(e,qs());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 Gs=Ln("analytics");function Hs(e={}){if(e.noTelemetry===!0)return!0;let t=process.env.NEBUTRA_TELEMETRY;return t==="0"||t==="false"}function io(e,t={}){Hs(t)||(async()=>{try{let n=await import("./dist-ZZCU5PKC.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??Gs},onError:()=>{}});if(typeof o?.track!="function")return;let r=o.track("scaffold.completed",e);r&&typeof r.then=="function"&&await r.catch(()=>{})}catch{}})()}import G from"fs";import N from"path";function V(e){G.existsSync(e)&&G.rmSync(e,{recursive:!0,force:!0})}function bt(e,t){let n=N.join(e,".env.example");G.existsSync(n)?G.appendFileSync(n,`
|
|
274
|
+
`+t):G.writeFileSync(n,t)}function _t(e,t){if(!G.existsSync(e))return;let n=G.readFileSync(e,"utf8"),o=n.replace(/export type AuthProviderId\s*=\s*(?:"[^"]+"\s*\|\s*)+"[^"]+";/,`export type AuthProviderId = "${t}";`);o!==n&&G.writeFileSync(e,o)}function Ws(e,t){let n=N.join(e,"README.md");if(!G.existsSync(n)){G.writeFileSync(n,t);return}G.appendFileSync(n,`
|
|
275
|
+
`+t)}async function ao(e,t){let n=N.join(e,"packages","iam","auth");if(!G.existsSync(n))return;if(t==="none"){V(n),Ws(e,`
|
|
276
276
|
## Auth
|
|
277
277
|
|
|
278
278
|
Auth was skipped at scaffold time (\`--auth=none\`). Add your preferred auth provider manually (e.g. Clerk, Better Auth, NextAuth, Supabase).
|
|
279
|
-
`);return}let o=N.join(n,"src","providers"),r=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")),
|
|
279
|
+
`);return}let o=N.join(n,"src","providers"),r=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")),_t(r,"clerk"),bt(e,`# Clerk auth
|
|
280
280
|
CLERK_PUBLISHABLE_KEY=
|
|
281
281
|
CLERK_SECRET_KEY=
|
|
282
|
-
`);return}if(t==="betterauth"){V(N.join(o,"clerk.ts")),V(N.join(o,"nextauth.ts")),V(N.join(o,"supabase.ts")),
|
|
282
|
+
`);return}if(t==="betterauth"){V(N.join(o,"clerk.ts")),V(N.join(o,"nextauth.ts")),V(N.join(o,"supabase.ts")),_t(r,"better-auth"),bt(e,`# Better Auth
|
|
283
283
|
BETTER_AUTH_SECRET=
|
|
284
284
|
BETTER_AUTH_URL=http://localhost:3000
|
|
285
|
-
`);return}if(t==="nextauth"){V(N.join(o,"clerk.ts")),V(N.join(o,"better-auth.ts")),V(N.join(o,"supabase.ts")),
|
|
285
|
+
`);return}if(t==="nextauth"){V(N.join(o,"clerk.ts")),V(N.join(o,"better-auth.ts")),V(N.join(o,"supabase.ts")),_t(r,"nextauth"),bt(e,`# NextAuth (Auth.js v5)
|
|
286
286
|
AUTH_SECRET=
|
|
287
287
|
NEXTAUTH_URL=http://localhost:3000
|
|
288
288
|
# OAuth (optional \u2014 leave blank to disable that provider)
|
|
@@ -290,7 +290,7 @@ GOOGLE_CLIENT_ID=
|
|
|
290
290
|
GOOGLE_CLIENT_SECRET=
|
|
291
291
|
GITHUB_CLIENT_ID=
|
|
292
292
|
GITHUB_CLIENT_SECRET=
|
|
293
|
-
`);return}if(t==="supabase"){V(N.join(o,"clerk.ts")),V(N.join(o,"better-auth.ts")),V(N.join(o,"nextauth.ts")),
|
|
293
|
+
`);return}if(t==="supabase"){V(N.join(o,"clerk.ts")),V(N.join(o,"better-auth.ts")),V(N.join(o,"nextauth.ts")),_t(r,"supabase"),bt(e,`# Supabase Auth
|
|
294
294
|
NEXT_PUBLIC_SUPABASE_URL=
|
|
295
295
|
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=
|
|
296
296
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=
|
|
@@ -298,7 +298,7 @@ SUPABASE_URL=
|
|
|
298
298
|
SUPABASE_PUBLISHABLE_KEY=
|
|
299
299
|
SUPABASE_ANON_KEY=
|
|
300
300
|
SUPABASE_SERVICE_ROLE_KEY=
|
|
301
|
-
`);return}}import
|
|
301
|
+
`);return}}import Ne from"fs";import ke from"path";function Xs(e,t){let n=`// apps/web/src/app/api/auth/callback/${e.id}/route.ts
|
|
302
302
|
// Generated by create-sailor \u2014 OAuth callback for ${e.name}
|
|
303
303
|
// Primary auth provider: ${t}
|
|
304
304
|
// Docs: ${e.docs}`,o={wechat:` const tokenRes = await fetch(
|
|
@@ -423,7 +423,7 @@ ${o[e.id]}
|
|
|
423
423
|
void state;
|
|
424
424
|
return NextResponse.redirect(new URL("/dashboard", req.url));
|
|
425
425
|
}
|
|
426
|
-
`}function
|
|
426
|
+
`}function zs(e){return`"use client";
|
|
427
427
|
|
|
428
428
|
/**
|
|
429
429
|
* SocialLoginButtons \u2014 generated by create-sailor.
|
|
@@ -466,23 +466,23 @@ export function SocialLoginButtons() {
|
|
|
466
466
|
</div>
|
|
467
467
|
);
|
|
468
468
|
}
|
|
469
|
-
`}function
|
|
470
|
-
`+t):
|
|
469
|
+
`}function co(e){Ne.mkdirSync(e,{recursive:!0})}function Js(e,t){let n=ke.join(e,".env.example");Ne.existsSync(n)?Ne.appendFileSync(n,`
|
|
470
|
+
`+t):Ne.writeFileSync(n,t)}function Qs(e){let t=`# ${e.name} (${e.id}) \u2014 ${e.docs}`,n=e.envVars.map(o=>`${o}=`).join(`
|
|
471
471
|
`);return`${t}
|
|
472
472
|
${n}
|
|
473
|
-
`}async function
|
|
474
|
-
`);
|
|
473
|
+
`}async function po(e,t,n){if(!t||t.length===0)return;let o=ke.join(e,"apps","web");if(!Ne.existsSync(o))return;let r=new Set,s=[];for(let p of t){if(r.has(p))continue;r.add(p);let d=Yn(p);d&&s.push(d)}if(s.length===0)return;for(let p of s)Js(e,Qs(p));let i=ke.join(o,"src","app","api","auth","callback");for(let p of s){let d=ke.join(i,p.id);co(d);let m=ke.join(d,"route.ts");Ne.writeFileSync(m,Xs(p,n))}let a=ke.join(o,"src","components","auth");co(a);let c=ke.join(a,"SocialLoginButtons.tsx");Ne.writeFileSync(c,zs(s))}import et from"fs";import uo from"path";var Pt=[{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 lo(e){return Pt.find(t=>t.id===e)}var pl=Pt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});import oe from"fs";import wt from"path";function re(e,t){let n=wt.join(e,".env.example");if(!oe.existsSync(n)||t.envVars.length===0)return;let r=`# ${t.category??t.name}: ${t.name}`;if(oe.readFileSync(n,"utf-8").includes(r))return;let a=["","# =============================================",r,t.docs?`# Docs: ${t.docs}`:null,"# =============================================",...t.envVars.map(c=>`${c}=""`),""].filter(c=>c!==null).join(`
|
|
474
|
+
`);oe.appendFileSync(n,a)}function se(e,t,n){let o=wt.join(e,".env.example");if(!oe.existsSync(o))return;let r=oe.readFileSync(o,"utf-8"),s=`${t}="${n}"`,i=new RegExp(`^${t}=.*$`,"m");if(i.test(r)){oe.writeFileSync(o,r.replace(i,s));return}let a=r.endsWith(`
|
|
475
475
|
`)?"":`
|
|
476
|
-
`;
|
|
477
|
-
`)}var
|
|
478
|
-
`);
|
|
479
|
-
`)}async function
|
|
476
|
+
`;oe.appendFileSync(o,`${a}${s}
|
|
477
|
+
`)}var Zs=["design","iam","commerce","integrations","platform","ops","ai"];function Oe(e,t){for(let o of Zs){let r=wt.join(e,"packages",o,t);if(oe.existsSync(r))return r}let n=wt.join(e,"packages",t);return oe.existsSync(n)?n:null}function J(e,t){let n=Oe(e,t);n&&oe.rmSync(n,{recursive:!0,force:!0})}function mo(e){return et.existsSync(e)?et.readFileSync(e,"utf-8"):null}function ei(e,t){let n=uo.join(e,".env.example"),o=mo(n);if(o===null)return;let r=`# Cache: ${t.name}`;if(o.includes(r))return;let s=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
478
|
+
`);et.appendFileSync(n,s)}function ti(e,t,n){let o=uo.join(e,".env.example"),r=mo(o);if(r===null)return;let s=new RegExp(`^${t}=.*$`,"m");s.test(r)?et.writeFileSync(o,r.replace(s,`${t}="${n}"`)):et.appendFileSync(o,`${t}="${n}"
|
|
479
|
+
`)}async function go(e,t,n){let o=Oe(e,"cache");if(t==="none"){o&&et.rmSync(o,{recursive:!0,force:!0});return}let r=lo(t);if(!r)throw new Error(`Unknown cache provider "${t}". Valid ids: ${Pt.map(s=>s.id).join(", ")}`);o&&(ei(e,r),ti(e,"CACHE_PROVIDER",t))}var At=[{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 fo(e){return At.find(t=>t.id===e)}var El=At.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function yo(e,t,n="global"){let o=fo(t);if(!o)throw new Error(`Unknown captcha provider "${t}". Valid ids: ${At.map(r=>r.id).join(", ")}`);if(o.id==="none"){J(e,"captcha");return}o.envVars.length>0&&re(e,{category:"Captcha",name:o.name,envVars:o.envVars,docs:o.docs}),se(e,"CAPTCHA_PROVIDER",o.id)}import It from"fs";import ni from"path";var Ct=[{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 So(e){return Ct.find(t=>t.id===e)}var wl=Ct.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function ho(e,t,n="global"){let o=So(t);if(!o)throw new Error(`Unknown CMS provider "${t}". Valid ids: ${Ct.map(s=>s.id).join(", ")}`);let r=ni.join(e,"apps","studio");if(o.id==="none"){J(e,"sanity"),It.existsSync(r)&&It.rmSync(r,{recursive:!0,force:!0});return}o.id!=="sanity"&&(J(e,"sanity"),It.existsSync(r)&&It.rmSync(r,{recursive:!0,force:!0})),o.envVars.length>0&&re(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 oi=O.join("apps","web"),ln=O.join("src","components","compliance"),dt=O.join("src","app","[locale]");function ie(e){return O.join(e,oi)}function ri(e){U.mkdirSync(e,{recursive:!0})}function Q(e,t){ri(O.dirname(e)),U.writeFileSync(e,t,"utf8")}function dn(e,t){let n=O.join(e,".env.example");if(U.existsSync(n)){if(U.readFileSync(n,"utf8").includes(t.split(`
|
|
480
480
|
`)[0]??""))return;U.appendFileSync(n,`
|
|
481
481
|
`+t+`
|
|
482
482
|
`)}else U.writeFileSync(n,t+`
|
|
483
|
-
`)}var
|
|
483
|
+
`)}var ut=`> **TEMPLATE \u2014 NOT LEGAL ADVICE.** This document is a boilerplate scaffold only.
|
|
484
484
|
> Consult a qualified lawyer in every jurisdiction you operate in before publishing.
|
|
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`,
|
|
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`,si=`import Link from "next/link";
|
|
486
486
|
import { IcpFooter } from "@/components/compliance/IcpFooter";
|
|
487
487
|
import { PublicSecurityFooter } from "@/components/compliance/PublicSecurityFooter";
|
|
488
488
|
|
|
@@ -533,7 +533,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
533
533
|
</footer>
|
|
534
534
|
);
|
|
535
535
|
}
|
|
536
|
-
`,
|
|
536
|
+
`,ii=`export function IcpFooter() {
|
|
537
537
|
const icp = process.env.NEXT_PUBLIC_ICP_BEIAN;
|
|
538
538
|
if (!icp) return null;
|
|
539
539
|
return (
|
|
@@ -547,7 +547,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
547
547
|
</a>
|
|
548
548
|
);
|
|
549
549
|
}
|
|
550
|
-
`,
|
|
550
|
+
`,ai=`export function PublicSecurityFooter() {
|
|
551
551
|
const psb = process.env.NEXT_PUBLIC_PSB_BEIAN;
|
|
552
552
|
const psbCode = process.env.NEXT_PUBLIC_PSB_CODE;
|
|
553
553
|
if (!psb) return null;
|
|
@@ -564,7 +564,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
564
564
|
</a>
|
|
565
565
|
);
|
|
566
566
|
}
|
|
567
|
-
`,
|
|
567
|
+
`,ci=`"use client";
|
|
568
568
|
|
|
569
569
|
import { useEffect, useState } from "react";
|
|
570
570
|
|
|
@@ -718,7 +718,7 @@ export function CookieBanner() {
|
|
|
718
718
|
</div>
|
|
719
719
|
);
|
|
720
720
|
}
|
|
721
|
-
`,
|
|
721
|
+
`,pi=`interface AigcDisclaimerProps {
|
|
722
722
|
className?: string;
|
|
723
723
|
}
|
|
724
724
|
|
|
@@ -738,13 +738,13 @@ export function AigcDisclaimer({ className }: AigcDisclaimerProps) {
|
|
|
738
738
|
</div>
|
|
739
739
|
);
|
|
740
740
|
}
|
|
741
|
-
`,
|
|
741
|
+
`,li=`---
|
|
742
742
|
title: Privacy Policy / \u9690\u79C1\u653F\u7B56
|
|
743
743
|
description: How {{COMPANY_NAME}} collects, uses, shares, and protects personal information.
|
|
744
744
|
lastUpdated: 2026-01-01
|
|
745
745
|
---
|
|
746
746
|
|
|
747
|
-
${
|
|
747
|
+
${ut}
|
|
748
748
|
|
|
749
749
|
# Privacy Policy / \u9690\u79C1\u653F\u7B56
|
|
750
750
|
|
|
@@ -883,13 +883,13 @@ or in-app notification before the change takes effect.
|
|
|
883
883
|
**Data Controller / \u6570\u636E\u63A7\u5236\u8005:** {{COMPANY_NAME}}
|
|
884
884
|
**Address / \u5730\u5740:** {{COMPANY_ADDRESS}}
|
|
885
885
|
**Email / \u90AE\u7BB1:** {{COMPANY_CONTACT_EMAIL}}
|
|
886
|
-
`,
|
|
886
|
+
`,di=`---
|
|
887
887
|
title: Terms of Service / \u670D\u52A1\u6761\u6B3E
|
|
888
888
|
description: Terms governing your use of {{COMPANY_NAME}}.
|
|
889
889
|
lastUpdated: 2026-01-01
|
|
890
890
|
---
|
|
891
891
|
|
|
892
|
-
${
|
|
892
|
+
${ut}
|
|
893
893
|
|
|
894
894
|
# Terms of Service / \u670D\u52A1\u6761\u6B3E
|
|
895
895
|
|
|
@@ -1002,13 +1002,13 @@ conflict-of-laws principles. Disputes shall be resolved in the competent courts
|
|
|
1002
1002
|
## 11. Contact / \u8054\u7CFB\u65B9\u5F0F
|
|
1003
1003
|
|
|
1004
1004
|
{{COMPANY_NAME}} \xB7 {{COMPANY_ADDRESS}} \xB7 {{COMPANY_CONTACT_EMAIL}}
|
|
1005
|
-
`,
|
|
1005
|
+
`,ui=`---
|
|
1006
1006
|
title: AIGC Disclosure / \u751F\u6210\u5F0F AI \u670D\u52A1\u8BF4\u660E
|
|
1007
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
|
|
1008
1008
|
lastUpdated: 2026-01-01
|
|
1009
1009
|
---
|
|
1010
1010
|
|
|
1011
|
-
${
|
|
1011
|
+
${ut}
|
|
1012
1012
|
|
|
1013
1013
|
# AIGC Disclosure / \u751F\u6210\u5F0F AI \u670D\u52A1\u8BF4\u660E
|
|
1014
1014
|
|
|
@@ -1084,13 +1084,13 @@ human professional.
|
|
|
1084
1084
|
|
|
1085
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
|
|
1086
1086
|
\u7528\u4E8E\u533B\u7597\u3001\u6CD5\u5F8B\u3001\u91D1\u878D\u6216\u5B89\u5168\u6538\u5173\u7684\u51B3\u7B56\u3002
|
|
1087
|
-
`,
|
|
1087
|
+
`,mi=`---
|
|
1088
1088
|
title: Cross-Border Data Notice / \u6570\u636E\u51FA\u5883\u544A\u77E5
|
|
1089
1089
|
description: Notice of cross-border transfer of personal information, per PIPL.
|
|
1090
1090
|
lastUpdated: 2026-01-01
|
|
1091
1091
|
---
|
|
1092
1092
|
|
|
1093
|
-
${
|
|
1093
|
+
${ut}
|
|
1094
1094
|
|
|
1095
1095
|
# Cross-Border Data Notice / \u6570\u636E\u51FA\u5883\u544A\u77E5
|
|
1096
1096
|
|
|
@@ -1149,13 +1149,13 @@ Refusal may limit your ability to use features that rely on overseas services.
|
|
|
1149
1149
|
- \u4F20\u8F93\u4E2D\u52A0\u5BC6 (TLS 1.2+) \u53CA\u9759\u6001\u52A0\u5BC6 (AES-256)\u3002
|
|
1150
1150
|
- \u5408\u540C\u6761\u6B3E\u8981\u6C42\u5883\u5916\u63A5\u6536\u65B9\u63D0\u4F9B\u7B49\u540C\u4E8E PIPL \u7684\u4FDD\u62A4\u3002
|
|
1151
1151
|
- \u8BBF\u95EE\u63A7\u5236\u3001\u5BA1\u8BA1\u65E5\u5FD7\u548C\u8FDD\u89C4\u901A\u77E5\u7A0B\u5E8F\u3002
|
|
1152
|
-
`,
|
|
1152
|
+
`,gi=`---
|
|
1153
1153
|
title: Personal Information Collection List / \u4E2A\u4EBA\u4FE1\u606F\u6536\u96C6\u6E05\u5355
|
|
1154
1154
|
description: Itemized list of personal information collected, per PIPL Article 17.
|
|
1155
1155
|
lastUpdated: 2026-01-01
|
|
1156
1156
|
---
|
|
1157
1157
|
|
|
1158
|
-
${
|
|
1158
|
+
${ut}
|
|
1159
1159
|
|
|
1160
1160
|
# Personal Information Collection List / \u4E2A\u4EBA\u4FE1\u606F\u6536\u96C6\u6E05\u5355
|
|
1161
1161
|
|
|
@@ -1191,26 +1191,26 @@ To exercise your rights (access, rectify, delete, export, withdraw consent), con
|
|
|
1191
1191
|
**{{COMPANY_CONTACT_EMAIL}}**.
|
|
1192
1192
|
|
|
1193
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
|
|
1194
|
-
`;async function
|
|
1195
|
-
`))}async function
|
|
1196
|
-
`))}async function
|
|
1197
|
-
`))}async function
|
|
1198
|
-
`))}async function
|
|
1199
|
-
`)}import R from"fs";import
|
|
1200
|
-
`+t):R.writeFileSync(n,t)}async function
|
|
1194
|
+
`;async function fi(e,t){let n=ie(e);if(!U.existsSync(n))return;let o=O.join(n,"src","components","compliance","Footer.tsx");Q(o,si);let r=O.join(n,"src","components","compliance","README.md");Q(r,["# 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 yi(e,t){let n=ie(e);if(!U.existsSync(n))return;let o=O.join(n,"src","components","compliance","CookieBanner.tsx");Q(o,ci)}async function Si(e,t){let n=ie(e);if(!U.existsSync(n))return;let o=O.join(n,dt,"privacy","page.mdx");Q(o,li)}async function hi(e,t){let n=ie(e);if(!U.existsSync(n))return;let o=O.join(n,dt,"terms","page.mdx");Q(o,di)}async function Ei(e){let t=ie(e);if(!U.existsSync(t))return;let n=O.join(t,ln,"IcpFooter.tsx");Q(n,ii),dn(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 vi(e){let t=ie(e);if(!U.existsSync(t))return;let n=O.join(t,ln,"PublicSecurityFooter.tsx");Q(n,ai),dn(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 bi(e){let t=ie(e);if(!U.existsSync(t))return;let n=O.join(t,ln,"AigcDisclaimer.tsx");Q(n,pi),dn(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 _i(e){let t=ie(e);if(!U.existsSync(t))return;let n=O.join(t,dt,"aigc-disclosure","page.mdx");Q(n,ui)}async function Pi(e){let t=ie(e);if(!U.existsSync(t))return;let n=O.join(t,dt,"cross-border-data","page.mdx");Q(n,mi)}async function wi(e){let t=ie(e);if(!U.existsSync(t))return;let n=O.join(t,dt,"pii-collection","page.mdx");Q(n,gi)}async function Eo(e,t){try{await fi(e,t),await yi(e,t),await Si(e,t),await hi(e,t),(t==="cn"||t==="hybrid")&&(await Ei(e),await vi(e),await bi(e),await _i(e),await Pi(e),await wi(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 Ai from"fs";import Ci from"path";async function vo(e,t){let n=Ci.join(e,"nebutra.config.json");Ai.writeFileSync(n,JSON.stringify(t,null,2)+`
|
|
1199
|
+
`)}import R from"fs";import Le from"path";function Ii(e){R.existsSync(e)&&R.rmSync(e,{recursive:!0,force:!0})}function bo(e,t){return e.replace(/provider\s*=\s*"postgresql"/,`provider = "${t}"`)}function xi(e){return e.replace(/^\s*directUrl\s*=\s*env\([^)]*\)\s*\n/gm,"")}function _o(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 Ri(e,t){let n=Le.join(e,".env.example");R.existsSync(n)?R.appendFileSync(n,`
|
|
1200
|
+
`+t):R.writeFileSync(n,t)}async function Po(e,t,n){let o=Le.join(e,"packages","platform","db");if(t==="none"){Ii(o);return}let r=Le.join(o,"prisma","schema.prisma"),s;if(t==="postgresql"?s=`postgresql://postgres:postgres@localhost:5432/${n}`:t==="mysql"?s=`mysql://root:root@localhost:3306/${n}`:s="file:./dev.db",Ri(e,`# Database (${t})
|
|
1201
1201
|
DATABASE_URL="${s}"
|
|
1202
|
-
`),!R.existsSync(r))return;let i=R.readFileSync(r,"utf8"),a=i;t==="mysql"?(a=
|
|
1202
|
+
`),!R.existsSync(r))return;let i=R.readFileSync(r,"utf8"),a=i;t==="mysql"?(a=bo(a,"mysql"),a=_o(a)):t==="sqlite"&&(a=bo(a,"sqlite"),a=xi(a),a=_o(a)),a!==i&&R.writeFileSync(r,a)}function Ti(e){let t=Le.join(e,".env.example");return R.existsSync(t)?R.readFileSync(t,"utf8"):""}function ki(e,t,n,o){let r=Le.join(e,".env.example"),s=Ti(e),i=o?`# ${o}
|
|
1203
1203
|
${t}="${n}"
|
|
1204
1204
|
`:`${t}="${n}"
|
|
1205
1205
|
`,a=new RegExp(`^\\s*${t}\\s*=.*$`,"m");if(a.test(s)){R.writeFileSync(r,s.replace(a,`${t}="${n}"`));return}s?R.appendFileSync(r,`
|
|
1206
|
-
${i}`):R.writeFileSync(r,i)}function
|
|
1206
|
+
${i}`):R.writeFileSync(r,i)}function Ni(e,t){let n=Le.join(e,"packages","platform","db","prisma","schema.prisma");if(!R.existsSync(n))return;let o=R.readFileSync(n,"utf8"),r=/(datasource\s+db\s*\{[^}]*?)(\n})/;if(!o.match(r))return;let i=t.filter(p=>!o.includes(p.trim()));if(i.length===0)return;let a=i.map(p=>` ${p}`).join(`
|
|
1207
1207
|
`),c=o.replace(r,`$1
|
|
1208
|
-
${a}$2`);R.writeFileSync(n,c)}function
|
|
1209
|
-
`))}async function
|
|
1210
|
-
`+t):
|
|
1208
|
+
${a}$2`);R.writeFileSync(n,c)}function Oi(e){let t=Le.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 wo(e,t,n){if(!(t.id==="none"||t.id==="local")){for(let o of t.envVars){let r=o.placeholder.replace(/\$PROJECT/g,n);ki(e,o.name,r,o.comment)}t.prismaDatasourceExtras&&t.prismaDatasourceExtras.length>0&&Ni(e,t.prismaDatasourceExtras),t.keepDirectUrl&&Oi(e)}}import we from"fs";import Ue from"path";import{fileURLToPath as Li}from"url";var Ui=Li(import.meta.url),Ao=Ue.dirname(Ui),Di=/\.(md|mdx|json|ts|tsx|js|mjs|cjs|yaml|yml|css|html|vue)$/,Co=["fumadocs","nextra","mintlify","docusaurus","vitepress"];function Mi(e){let t=[Ue.join(Ao,"..","templates","docs",e),Ue.join(Ao,"..","..","templates","docs",e)];for(let n of t)if(we.existsSync(n))return n;return t[0]}async function Io(e,t){if(t.framework==="none")return;if(!Co.includes(t.framework))throw new Error(`Unsupported docs framework: "${t.framework}". Supported: ${Co.join(", ")}, none.`);let n=Mi(t.framework),o=Ue.join(e,"apps","docs");if(!we.existsSync(n))throw new Error(`Docs template not found: ${n}`);xo(n,o),Ro(o,{"{PRODUCT_NAME}":t.projectName})}function xo(e,t){we.mkdirSync(t,{recursive:!0});for(let n of we.readdirSync(e,{withFileTypes:!0})){let o=Ue.join(e,n.name),r=Ue.join(t,n.name);n.isDirectory()?xo(o,r):we.copyFileSync(o,r)}}function Ro(e,t){for(let n of we.readdirSync(e,{withFileTypes:!0})){let o=Ue.join(e,n.name);if(n.isDirectory())Ro(o,t);else if(Di.test(n.name)){let r=we.readFileSync(o,"utf-8");for(let[s,i]of Object.entries(t))r=r.split(s).join(i);we.writeFileSync(o,r)}}}import Me from"fs";import De from"path";var xt=[{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 To(e){return xt.find(t=>t.id===e)}var ql=xt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});var Fe="@nebutra/email";function un(e,t){Me.mkdirSync(De.dirname(e),{recursive:!0}),Me.writeFileSync(e,t)}function Fi(e,t){let n=De.join(e,".env.example");Me.existsSync(n)?Me.appendFileSync(n,`
|
|
1210
|
+
`+t):Me.writeFileSync(n,t)}function ji(e){if(e.envVars.length===0)return"";let t=`# Email provider: ${e.name}`,n=e.envVars.map(o=>`${o}=`).join(`
|
|
1211
1211
|
`);return`${t}
|
|
1212
1212
|
${n}
|
|
1213
|
-
`}var
|
|
1213
|
+
`}var Bi=`/**
|
|
1214
1214
|
* Public inputs accepted by the \`sendEmail\` wrapper.
|
|
1215
1215
|
* Keep the surface minimal \u2014 individual providers may accept more, but
|
|
1216
1216
|
* consumers should code against this shape for portability.
|
|
@@ -1230,8 +1230,8 @@ export interface SendEmailResult {
|
|
|
1230
1230
|
/** Raw provider response \u2014 useful for debugging, not a stable contract. */
|
|
1231
1231
|
raw?: unknown;
|
|
1232
1232
|
}
|
|
1233
|
-
`;function
|
|
1234
|
-
* @${
|
|
1233
|
+
`;function $i(){return`/**
|
|
1234
|
+
* @${Fe.slice(1)} \u2014 Resend transactional email wrapper.
|
|
1235
1235
|
*
|
|
1236
1236
|
* Docs: https://resend.com/docs
|
|
1237
1237
|
*/
|
|
@@ -1269,8 +1269,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1269
1269
|
}
|
|
1270
1270
|
|
|
1271
1271
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1272
|
-
`}function
|
|
1273
|
-
* @${
|
|
1272
|
+
`}function Vi(){return`/**
|
|
1273
|
+
* @${Fe.slice(1)} \u2014 Postmark transactional email wrapper.
|
|
1274
1274
|
*
|
|
1275
1275
|
* Docs: https://postmarkapp.com/developer
|
|
1276
1276
|
*/
|
|
@@ -1307,8 +1307,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1307
1307
|
}
|
|
1308
1308
|
|
|
1309
1309
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1310
|
-
`}function
|
|
1311
|
-
* @${
|
|
1310
|
+
`}function Ki(){return`/**
|
|
1311
|
+
* @${Fe.slice(1)} \u2014 AWS SES v2 transactional email wrapper.
|
|
1312
1312
|
*
|
|
1313
1313
|
* Docs: https://docs.aws.amazon.com/ses
|
|
1314
1314
|
*/
|
|
@@ -1363,8 +1363,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1363
1363
|
}
|
|
1364
1364
|
|
|
1365
1365
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1366
|
-
`}function
|
|
1367
|
-
* @${
|
|
1366
|
+
`}function Yi(){return`/**
|
|
1367
|
+
* @${Fe.slice(1)} \u2014 \u963F\u91CC\u4E91\u90AE\u4EF6\u63A8\u9001 (Aliyun DirectMail) wrapper.
|
|
1368
1368
|
*
|
|
1369
1369
|
* Docs: https://help.aliyun.com/product/29412.html
|
|
1370
1370
|
*/
|
|
@@ -1413,8 +1413,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1413
1413
|
}
|
|
1414
1414
|
|
|
1415
1415
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1416
|
-
`}function
|
|
1417
|
-
* @${
|
|
1416
|
+
`}function qi(){return`/**
|
|
1417
|
+
* @${Fe.slice(1)} \u2014 \u817E\u8BAF\u4E91 SES wrapper.
|
|
1418
1418
|
*
|
|
1419
1419
|
* Docs: https://cloud.tencent.com/document/product/1288
|
|
1420
1420
|
*/
|
|
@@ -1461,8 +1461,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1461
1461
|
}
|
|
1462
1462
|
|
|
1463
1463
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1464
|
-
`}function
|
|
1465
|
-
* @${
|
|
1464
|
+
`}function Gi(){return`/**
|
|
1465
|
+
* @${Fe.slice(1)} \u2014 \u7F51\u6613\u4F01\u4E1A\u90AE\u7BB1 via SMTP (nodemailer).
|
|
1466
1466
|
*
|
|
1467
1467
|
* Docs: https://qiye.163.com/help
|
|
1468
1468
|
*/
|
|
@@ -1499,32 +1499,32 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1499
1499
|
}
|
|
1500
1500
|
|
|
1501
1501
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1502
|
-
`}function
|
|
1503
|
-
`}}function
|
|
1504
|
-
`}async function
|
|
1502
|
+
`}function Hi(e){switch(e){case"resend":return $i();case"postmark":return Vi();case"ses":return Ki();case"aliyun-dm":return Yi();case"tencent-ses":return qi();case"netease":return Gi();case"none":return`export {};
|
|
1503
|
+
`}}function Wi(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:Fe,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 ko(e,t,n){if(t==="none")return;let o=To(t);if(!o)throw new Error(`Unknown email provider "${t}". Valid ids: ${xt.map(i=>i.id).join(", ")}`);let r=De.join(e,"packages","email"),s=De.join(r,"src");Me.existsSync(s)&&Me.rmSync(s,{recursive:!0,force:!0}),un(De.join(r,"package.json"),Wi(o)),un(De.join(s,"types.ts"),Bi),un(De.join(s,"index.ts"),Hi(o.id)),Fi(e,ji(o))}import ue from"fs";import No from"path";function Xi(e,t){return new RegExp(`^\\s*${t}\\s*=`,"m").test(e)}function Oo(e){return e.endsWith(`
|
|
1505
1505
|
`)?e:`${e}
|
|
1506
|
-
`}async function
|
|
1507
|
-
${s}`,a=[{name:"DATABASE_URL",value:String(t.databaseUrl)},{name:"NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY",value:String(t.clerkPublishable)},{name:"CLERK_SECRET_KEY",value:String(t.clerkSecret)},{name:"NEXT_PUBLIC_SITE_URL",value:"http://localhost:3000"}].filter(p=>!
|
|
1506
|
+
`}async function Lo(e,t){let n=No.join(e,".env"),o=No.join(e,".env.local"),r=ue.existsSync(n)?ue.readFileSync(n,"utf8"):"",s=ue.existsSync(o)?ue.readFileSync(o,"utf8"):"",i=`${r}
|
|
1507
|
+
${s}`,a=[{name:"DATABASE_URL",value:String(t.databaseUrl)},{name:"NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY",value:String(t.clerkPublishable)},{name:"CLERK_SECRET_KEY",value:String(t.clerkSecret)},{name:"NEXT_PUBLIC_SITE_URL",value:"http://localhost:3000"}].filter(p=>!Xi(i,p.name)).map(p=>`${p.name}="${p.value}"`);if(a.length===0)return;let c=`# Automatically injected by create-sailor
|
|
1508
1508
|
${a.join(`
|
|
1509
1509
|
`)}
|
|
1510
|
-
`;try{let p=
|
|
1511
|
-
${c}`)}catch{try{
|
|
1512
|
-
${c}`)}}}import
|
|
1510
|
+
`;try{let p=ue.readFileSync(o,"utf8"),d=Oo(p);ue.writeFileSync(o,`${d}
|
|
1511
|
+
${c}`)}catch{try{ue.writeFileSync(o,c,{flag:"wx"})}catch{let p=ue.readFileSync(o,"utf8");ue.writeFileSync(o,`${Oo(p)}
|
|
1512
|
+
${c}`)}}}import Do from"crypto";import mt from"fs";import Uo from"path";function je(e){return Do.randomBytes(e).toString("base64").replace(/=+$/,"")}function zi(e){return Do.randomBytes(e).toString("hex")}function Ji(){return{AUTH_SECRET:je(32),NEXTAUTH_SECRET:je(32),BETTER_AUTH_SECRET:je(32),JWT_SECRET:je(32),WEBHOOK_SECRET:zi(16),ENCRYPTION_KEY:je(32),API_SECRET_KEY:je(32),SESSION_SECRET:je(32)}}function Qi(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function Zi(e,t,n){let o=new RegExp(`^(?!#)\\s*${t}=.*$`,"gm"),r=Qi(n);return e.replace(o,`${t}="${r}"`)}async function Mo(e){let t=Uo.join(e,".env.example"),n=Uo.join(e,".env.local");if(!mt.existsSync(t))return null;try{let o=mt.readFileSync(t,"utf8"),r=Ji(),i=mt.existsSync(n)?mt.readFileSync(n,"utf8"):o;for(let a of Object.keys(r))i=Zi(i,a,r[a]);return mt.writeFileSync(n,i),r}catch(o){let r=o instanceof Error?o.message:String(o);throw console.error(`[create-sailor] Failed to generate env secrets: ${r}`),new Error(`Unable to write .env.local: ${r}`)}}var Rt=[{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 Fo(e){return Rt.find(t=>t.id===e)}var rd=Rt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function jo(e,t,n="global"){let o=Fo(t);if(!o)throw new Error(`Unknown feature-flags provider "${t}". Valid ids: ${Rt.map(r=>r.id).join(", ")}`);if(o.id==="none"){J(e,"feature-flags");return}o.envVars.length>0&&re(e,{category:"Feature Flags",name:o.name,envVars:o.envVars,docs:o.docs}),se(e,"FEATURE_FLAGS_PROVIDER",o.id)}import{execFileSync as Ko}from"child_process";import L from"fs";import ea from"os";import ae from"path";import ta from"ignore";var na={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 oa(){let e=process.env.SAILOR_TEMPLATE_LOCAL_DIR;if(!(!e||e.trim().length===0))return ae.resolve(e.trim())}var ra=new Set(["node_modules",".git","dist",".next",".turbo"]);function sa(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 r=ae.basename(o);return!ra.has(r)}})}function ia(e,t){return`https://github.com/${e}/archive/refs/heads/${t}.tar.gz`}function aa(e,t){return`https://github.com/${e}/archive/${t}.tar.gz`}function Yo(e){return/^[0-9a-f]{7,40}$/i.test(e)}function ca(e,t){let n=`https://github.com/${e}.git`,o=[`refs/heads/${t}`,`refs/tags/${t}`];for(let r of o)try{let s=Ko("git",["ls-remote",n,r],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(!s)continue;let[i]=s.split(/\s+/);if(Yo(i))return i}catch{}return null}async function pa(e,t){if(Yo(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 r=ca(e,t);if(r)return r;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 qo(e,t,n){let o=L.readdirSync(t,{withFileTypes:!0});for(let r of o){if(r.name==="node_modules"||r.name===".git")continue;let s=ae.join(t,r.name),i=ae.relative(e,s).split(ae.sep).join("/");r.isDirectory()?(n.push(i+"/"),qo(e,s,n)):n.push(i)}}function Bo(e){let t=ae.join(e,".templateignore");if(!L.existsSync(t))return;let n=L.readFileSync(t,"utf8"),o=ta().add(n),r=[];qo(e,e,r);let s=r.map(c=>c.endsWith("/")?c.slice(0,-1):c),i=new Set(o.filter(s)),a=s.filter(c=>!i.has(c)).sort((c,p)=>p.split("/").length-c.split("/").length);for(let c of a){let p=ae.join(e,c);try{L.existsSync(p)&&L.rmSync(p,{recursive:!0,force:!0})}catch{}}try{L.existsSync(t)&&L.rmSync(t,{force:!0})}catch{}}async function $o(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 r=L.statSync(t);if(r.size!==o.byteLength)throw new Error(`Archive write verification failed (${r.size} != ${o.byteLength})`);return r.size}function Vo(e,t){Ko("tar",["-xzf",e,"-C",t,"--strip-components=1"],{stdio:"ignore"})}function mn(e){try{let t=L.readdirSync(e);for(let n of t)L.rmSync(ae.join(e,n),{recursive:!0,force:!0})}catch{}}async function la(e,t){let n=L.mkdtempSync(ae.join(ea.tmpdir(),"create-sailor-")),o=ae.join(n,"template.tar.gz");try{let r=await pa(e.repo,e.ref);await $o(aa(e.repo,r),o),Vo(o,t)}catch(r){if(!e.allowMutableFallback)throw r;mn(t),await $o(ia(e.repo,e.ref),o),Vo(o,t)}finally{L.rmSync(n,{recursive:!0,force:!0})}}async function Go(e){L.mkdirSync(e,{recursive:!0});let t=oa();if(t){if(!L.existsSync(t))throw new Error(`SAILOR_TEMPLATE_LOCAL_DIR does not exist: ${t}`);mn(e),sa(t,e);try{Bo(e)}catch(s){throw new Error(`Local template copied, but .templateignore processing failed: ${s instanceof Error?s.message:String(s)}`)}return}let o=process.env.SAILOR_TEMPLATE_SOURCE==="main"?["main"]:["mirror","main"],r;for(let s of o){let i=na[s];try{if(await la(i,e),i.applyIgnore)try{Bo(e)}catch(a){throw new Error(`Template cloned, but .templateignore processing failed: ${a instanceof Error?a.message:String(a)}`)}return}catch(a){r=a instanceof Error?a:new Error(String(a)),mn(e)}}throw new Error(`Failed to download the template from any source. Last error: ${r?.message??"unknown"}
|
|
1513
1513
|
|
|
1514
1514
|
Ensure you have internet access and 'tar' installed.
|
|
1515
|
-
You can override the source with SAILOR_TEMPLATE_REPO / SAILOR_TEMPLATE_REF env vars.`)}import
|
|
1516
|
-
`);let a=
|
|
1517
|
-
`)}return{lints:o,configWritten:!0}}import
|
|
1518
|
-
`),n.push(`${
|
|
1515
|
+
You can override the source with SAILOR_TEMPLATE_REPO / SAILOR_TEMPLATE_REF env vars.`)}import Tt from"fs";import gn from"path";var da="node scripts/governance/lint-no-raw-inputs.mjs",ua="node scripts/governance/lint-repository-seam.mjs",ma="node scripts/governance/lint-brand-literals.mjs",ga="node scripts/governance/lint-microcopy.mjs",fa=/\bnode\s+scripts\/(governance\/)?lint-[\w-]+\.mjs/g,ya=/\bnode\s+scripts\/lint-[\w-]+\.mjs/,Sa={scanRoots:["apps"],primitivesImport:"@nebutra/ui/primitives",whitelist:["/storybook/src/stories/","/design-docs/src/components/previews/","/sailor-docs/src/components/previews/","\\.test\\.tsx?$","/__tests__/"]},ha={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"]},Ea={scanRoots:["apps/web/src"],excludePaths:["/api/","\\.test\\.tsx?$","/__tests__/","/storybook/src/stories/","/design-docs/","/sailor-docs/"],bannedPatterns:[],allowlist:[]},Ho={governedPaths:["apps","packages/commerce","packages/integrations/email"],allowExpressions:["Nebutra","\u4E91\u6BD3\u667A\u80FD","\u4E91\u6BD3","^https?://([a-z0-9-]+\\.)?nebutra\\.com(?:/|$)","^https?://([a-z0-9-]+\\.)?nebutra\\.ai(?:/|$)","#0033FE","#0BF1C3"],knownExemptPatterns:["\\.stories\\.tsx?$","/__tests__/","\\.test\\.tsx?$","/previews/","^packages/design/brand/","^packages/design/tokens/","^packages/design/design-tokens/"],allowlist:[]},va=["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/src/app/[lang]/layout.tsx","apps/landing/src/app/api/blog/comments/route.ts","apps/landing/src/app/api/blog/reactions/route.ts","apps/landing/src/app/api/og/route.ts","apps/landing/src/app/apple-icon.tsx","apps/landing/src/components/auth/google-one-tap.tsx","apps/landing/src/components/cookie-consent-banner.tsx","apps/landing/src/components/marketing/ProductHuntSection.tsx","apps/landing/src/components/marketing/TestimonialsSection.tsx","apps/landing/src/i18n/request.ts","apps/landing/src/lib/analytics/emit.ts","apps/landing/src/lib/blog-fallback.ts","apps/landing/src/lib/constants/playbook-data.ts","apps/landing/src/lib/constants/resources-data.ts","apps/landing/src/lib/constants/solutions-data.ts","apps/landing/src/lib/docs-links.ts","apps/landing/src/lib/env.ts","apps/landing/src/lib/landing-content.ts","apps/landing/src/lib/seo/metadata.ts","apps/landing/src/lib/seo/site-routes.ts","apps/landing/src/lib/status-checks.ts","apps/landing/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 ba(e,t){let n=(e??"").replace(fa,"").split("&&").map(r=>r.trim()).filter(Boolean).join(" && ");return[n.length>0?n:"biome check .",...t].join(" && ")}function _a(e){let t=new RegExp(Ho.allowExpressions.join("|"));return va.filter(n=>{try{return t.test(Tt.readFileSync(gn.join(e,n),"utf8"))}catch{return!1}})}async function Wo(e,t){let n=t.database!=="none",o=[da];n&&o.push(ua),o.push(ma),o.push(ga);let r={...Ho,allowlist:_a(e)},s={rawInputs:Sa,brandLiterals:r,microcopyRules:Ea};n&&(s.repositorySeam=ha);let i=gn.join(e,"governance.config.json");Tt.writeFileSync(i,JSON.stringify(s,null,2)+`
|
|
1516
|
+
`);let a=gn.join(e,"package.json"),c;try{c=Tt.readFileSync(a,"utf8")}catch(p){if(p.code!=="ENOENT")throw p}if(c!==void 0){let p=JSON.parse(c);p.scripts=p.scripts??{};for(let[d,m]of Object.entries(p.scripts))d!=="lint"&&typeof m=="string"&&ya.test(m)&&delete p.scripts[d];p.scripts.lint=ba(p.scripts.lint,o),Tt.writeFileSync(a,JSON.stringify(p,null,2)+`
|
|
1517
|
+
`)}return{lints:o,configWritten:!0}}import Z from"fs";import ce from"path";import{fileURLToPath as Pa}from"url";var wa=Pa(import.meta.url),Xo=ce.dirname(wa),zo="scaffold-meta.json",Jo=".nebutra";function Aa(e){if(e)return e;let t=[ce.join(Xo,"..","templates"),ce.join(Xo,"..","..","templates")];for(let n of t)if(Z.existsSync(n))return n;return t[0]}function Qo(e,t){let n=[],o=Aa(t.templatesRoot),r=ce.join(e,"LICENSE");if(Z.existsSync(r)){let p=Z.readFileSync(r,"utf-8").slice(0,256);(/GNU AFFERO GENERAL PUBLIC LICENSE/i.test(p)||/Functional Source License/i.test(p))&&(Z.renameSync(r,ce.join(e,"LICENSE-UPSTREAM-REFERENCE.md")),n.push("LICENSE-UPSTREAM-REFERENCE.md (preserved upstream licence)"))}let s=ce.join(o,"LICENSE-SCAFFOLD.md");if(!Z.existsSync(s))throw new Error(`LICENSE-SCAFFOLD.md template missing at ${s}; cannot emit license.`);Z.copyFileSync(s,ce.join(e,"LICENSE")),n.push("LICENSE");let i={schemaVersion:1,cliVersion:t.cliVersion,scaffoldedAt:new Date().toISOString(),projectName:t.projectName,purpose:"Records which `create-sailor` version produced this project, for support triage. Provenance only: the MIT licence in LICENSE applies unconditionally and does not depend on this file. Deleting it costs you nothing.",license:{tier:"mit-scaffold",file:"LICENSE"}},a=ce.join(e,Jo);Z.mkdirSync(a,{recursive:!0}),Z.writeFileSync(ce.join(a,zo),`${JSON.stringify(i,null,2)}
|
|
1518
|
+
`),n.push(`${Jo}/${zo}`);let c=ce.join(e,"README.md");if(Z.existsSync(c)){let p=Z.readFileSync(c,"utf-8");p.includes("Scaffolded by `create-sailor`")||(Z.writeFileSync(c,`> **License notice:** Scaffolded by \`create-sailor\`. The Nebutra-Sailor
|
|
1519
1519
|
> code in this project is [MIT licensed](./LICENSE) \u2014 commercial use, closed
|
|
1520
1520
|
> source, no fee, no attribution required. The upstream monorepo is
|
|
1521
1521
|
> FSL-1.1-ALv2 \u2014 see \`LICENSE-UPSTREAM-REFERENCE.md\`.
|
|
1522
1522
|
|
|
1523
|
-
`+p),n.push("README.md (prepended license notice)"))}return{wrote:n}}import
|
|
1524
|
-
`);
|
|
1525
|
-
`);
|
|
1526
|
-
`)}function
|
|
1527
|
-
`+t):
|
|
1523
|
+
`+p),n.push("README.md (prepended license notice)"))}return{wrote:n}}import Be from"fs";import fn from"path";async function Zo(e,t){let n=fn.join(e,"packages","mcp");if(t==="off"){Be.existsSync(n)&&Be.rmSync(n,{recursive:!0,force:!0});let r=fn.join(e,"packages","cli","src","commands","mcp-server.ts");Be.existsSync(r)&&Be.rmSync(r,{force:!0});return}let o=fn.join(e,".env.example");if(Be.existsSync(o)){let r=Be.readFileSync(o,"utf-8"),s="# MCP Server (Agent-friendly API)";if(!r.includes(s)){let i=["","# =============================================",s,"# 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(`
|
|
1524
|
+
`);Be.appendFileSync(o,i)}}}import gt from"fs";import er from"path";async function tr(e,t,n){let o=er.join(e,"packages","metering"),r;if(t==="auto"?r=n!=="none"?"on":"off":t==="on"||t==="off"?r=t:r="off",r==="off"){gt.existsSync(o)&>.rmSync(o,{recursive:!0,force:!0});return}let s=er.join(e,".env.example");if(!gt.existsSync(s))return;let i=gt.readFileSync(s,"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(`
|
|
1525
|
+
`);gt.appendFileSync(s,c)}}import me from"fs";import j from"path";var nr=[{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:""}],xd=nr.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function or(e){return nr.find(t=>t.id===e)}var yn={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 Ca(e){me.mkdirSync(e,{recursive:!0})}function Sn(e,t){let n=j.join(e,"package.json");if(!me.existsSync(n))return;let o;try{o=JSON.parse(me.readFileSync(n,"utf-8"))}catch(a){console.error(`Failed to parse ${n}:`,a);return}let s={...o.dependencies??{}};for(let[a,c]of Object.entries(t))s[a]||(s[a]=c);let i={...o,dependencies:s};me.writeFileSync(n,JSON.stringify(i,null,2)+`
|
|
1526
|
+
`)}function pe(e,t){Ca(j.dirname(e)),me.writeFileSync(e,t)}function ft(e,t){let n=j.join(e,".env.example");me.existsSync(n)?me.appendFileSync(n,`
|
|
1527
|
+
`+t):me.writeFileSync(n,t)}function Ia(e){let t=`import * as Sentry from "@sentry/nextjs";
|
|
1528
1528
|
|
|
1529
1529
|
Sentry.init({
|
|
1530
1530
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
@@ -1567,14 +1567,14 @@ export async function register() {
|
|
|
1567
1567
|
}
|
|
1568
1568
|
|
|
1569
1569
|
export { captureRequestError as onRequestError } from "@sentry/nextjs";
|
|
1570
|
-
`;
|
|
1570
|
+
`;pe(j.join(e,"sentry.client.config.ts"),t),pe(j.join(e,"sentry.server.config.ts"),n),pe(j.join(e,"sentry.edge.config.ts"),o),pe(j.join(e,"instrumentation.ts"),r)}function xa(){return`# Sentry \u2014 error tracking + performance monitoring
|
|
1571
1571
|
# Get your DSN from https://sentry.io/settings/projects/<project>/keys/
|
|
1572
1572
|
SENTRY_DSN=
|
|
1573
1573
|
NEXT_PUBLIC_SENTRY_DSN=
|
|
1574
1574
|
SENTRY_ORG=
|
|
1575
1575
|
SENTRY_PROJECT=
|
|
1576
1576
|
SENTRY_AUTH_TOKEN=
|
|
1577
|
-
`}function
|
|
1577
|
+
`}function Ra(e){let t=`/**
|
|
1578
1578
|
* Next.js instrumentation hook \u2014 wires Datadog APM (dd-trace) into Node runtime.
|
|
1579
1579
|
* Edge runtime is not supported by dd-trace; only nodejs is initialized.
|
|
1580
1580
|
*/
|
|
@@ -1622,7 +1622,7 @@ export function initDatadogRum(): void {
|
|
|
1622
1622
|
});
|
|
1623
1623
|
initialized = true;
|
|
1624
1624
|
}
|
|
1625
|
-
`;
|
|
1625
|
+
`;pe(j.join(e,"instrumentation.ts"),t),pe(j.join(e,"src","lib","monitoring","datadog-rum.ts"),n)}function Ta(){return`# Datadog \u2014 APM + RUM
|
|
1626
1626
|
# Server APM (dd-trace)
|
|
1627
1627
|
DD_API_KEY=
|
|
1628
1628
|
DD_APP_KEY=
|
|
@@ -1637,7 +1637,7 @@ NEXT_PUBLIC_DD_SITE=datadoghq.com
|
|
|
1637
1637
|
NEXT_PUBLIC_DD_SERVICE=sailor-web
|
|
1638
1638
|
NEXT_PUBLIC_DD_ENV=production
|
|
1639
1639
|
NEXT_PUBLIC_DD_VERSION=
|
|
1640
|
-
`}function
|
|
1640
|
+
`}function ka(e){let t=`/**
|
|
1641
1641
|
* Bugsnag server-side initialization.
|
|
1642
1642
|
* Import this once at the top of your server entrypoint or a shared init module.
|
|
1643
1643
|
*/
|
|
@@ -1686,11 +1686,11 @@ export function getBugsnagErrorBoundary() {
|
|
|
1686
1686
|
errorBoundary = plugin.createErrorBoundary(React) as typeof errorBoundary;
|
|
1687
1687
|
return errorBoundary;
|
|
1688
1688
|
}
|
|
1689
|
-
`;
|
|
1689
|
+
`;pe(j.join(e,"src","lib","monitoring","bugsnag.server.ts"),t),pe(j.join(e,"src","lib","monitoring","bugsnag.client.tsx"),n)}function Na(){return`# Bugsnag \u2014 error tracking
|
|
1690
1690
|
BUGSNAG_API_KEY=
|
|
1691
1691
|
NEXT_PUBLIC_BUGSNAG_API_KEY=
|
|
1692
1692
|
APP_VERSION=
|
|
1693
|
-
`}function
|
|
1693
|
+
`}function Oa(e){pe(j.join(e,"src","components","monitoring","ArmsScript.tsx"),`"use client";
|
|
1694
1694
|
/**
|
|
1695
1695
|
* \u963F\u91CC\u4E91 ARMS (Application Real-time Monitoring Service) browser agent.
|
|
1696
1696
|
* Injects the official JS agent via next/script.
|
|
@@ -1711,11 +1711,11 @@ export function ArmsScript() {
|
|
|
1711
1711
|
/>
|
|
1712
1712
|
);
|
|
1713
1713
|
}
|
|
1714
|
-
`)}function
|
|
1714
|
+
`)}function La(){return`# \u963F\u91CC\u4E91 ARMS \u2014 APM (\u4E2D\u56FD\u533A)
|
|
1715
1715
|
ALIYUN_ARMS_LICENSE_KEY=
|
|
1716
1716
|
ALIYUN_ARMS_APP_NAME=
|
|
1717
1717
|
NEXT_PUBLIC_ARMS_PID=
|
|
1718
|
-
`}function
|
|
1718
|
+
`}function Ua(e){pe(j.join(e,"src","components","monitoring","TingYunScript.tsx"),`"use client";
|
|
1719
1719
|
/**
|
|
1720
1720
|
* \u542C\u4E91 Browser Agent (\u524D\u7AEF\u6027\u80FD\u76D1\u63A7).
|
|
1721
1721
|
* Docs: https://www.tingyun.com/docs
|
|
@@ -1733,12 +1733,12 @@ export function TingYunScript() {
|
|
|
1733
1733
|
/>
|
|
1734
1734
|
);
|
|
1735
1735
|
}
|
|
1736
|
-
`)}function
|
|
1736
|
+
`)}function Da(){return`# \u542C\u4E91 TingYun \u2014 APM (\u4E2D\u56FD\u533A)
|
|
1737
1737
|
TINGYUN_LICENSE_KEY=
|
|
1738
1738
|
NEXT_PUBLIC_TINGYUN_APP_KEY=
|
|
1739
|
-
`}async function
|
|
1740
|
-
`)}}import
|
|
1741
|
-
`+t):
|
|
1739
|
+
`}async function rr(e,t,n="global"){let o=or(t);if(!o||o.id==="none")return;let r=j.join(e,"apps","web");if(!me.existsSync(r))return;let s=o.id;try{switch(s){case"sentry":Ia(r),Sn(r,yn.sentry),ft(e,xa());return;case"datadog":Ra(r),Sn(r,yn.datadog),ft(e,Ta());return;case"bugsnag":ka(r),Sn(r,yn.bugsnag),ft(e,Na());return;case"aliyun-arms":Oa(r),ft(e,La());return;case"tingyun":Ua(r),ft(e,Da());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 kt=[{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 sr(e){return kt.find(t=>t.id===e)}var Ld=kt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function ir(e,t,n="global"){let o=sr(t);if(!o)throw new Error(`Unknown notifications provider "${t}". Valid ids: ${kt.map(r=>r.id).join(", ")}`);if(o.id==="none"){J(e,"notifications");return}o.envVars.length>0&&re(e,{category:"Notifications",name:o.name,envVars:o.envVars,docs:o.docs}),se(e,"NOTIFICATIONS_PROVIDER",o.id)}import hn from"fs";import Ma from"path";async function ar(e,t){let n=Ma.join(e,"package.json");if(hn.existsSync(n)){let o=hn.readFileSync(n,"utf8"),r=JSON.parse(o);r.name=t,r.version="0.1.0",hn.writeFileSync(n,JSON.stringify(r,null,2)+`
|
|
1740
|
+
`)}}import Nt from"fs";import tt from"path";import{fileURLToPath as Fa}from"url";var ja=Fa(import.meta.url),cr=tt.dirname(ja);function Ba(){let e=[tt.join(cr,"..","templates","orm","drizzle"),tt.join(cr,"..","..","templates","orm","drizzle")];for(let t of e)if(Nt.existsSync(t))return t;return null}function pr(e,t){Nt.mkdirSync(t,{recursive:!0});for(let n of Nt.readdirSync(e,{withFileTypes:!0})){let o=tt.join(e,n.name),r=tt.join(t,n.name);n.isDirectory()?pr(o,r):Nt.copyFileSync(o,r)}}async function lr(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 r=tt.join(e,"packages","platform","db-drizzle");return pr(o,r),{applied:!0}}import ge from"fs";import yt from"path";var $a=["stripe","lemonsqueezy","chinapay","polar"];function dr(e){ge.existsSync(e)&&ge.rmSync(e,{recursive:!0,force:!0})}function Va(e){switch(e){case"stripe":return["stripe"];case"lemon":return["lemonsqueezy"];case"wechat":case"alipay":return["chinapay"]}}function Ka(e,t){let n=yt.join(e,".env.example");ge.existsSync(n)?ge.appendFileSync(n,`
|
|
1741
|
+
`+t):ge.writeFileSync(n,t)}function Ya(e){switch(e){case"stripe":return`# Stripe
|
|
1742
1742
|
STRIPE_SECRET_KEY=
|
|
1743
1743
|
STRIPE_WEBHOOK_SECRET=
|
|
1744
1744
|
`;case"lemon":return`# Lemon Squeezy
|
|
@@ -1753,14 +1753,14 @@ CHINAPAY_WECHAT_ENABLED=true
|
|
|
1753
1753
|
CHINAPAY_APP_ID=
|
|
1754
1754
|
CHINAPAY_APP_SECRET=
|
|
1755
1755
|
CHINAPAY_ALIPAY_ENABLED=true
|
|
1756
|
-
`}}async function
|
|
1757
|
-
`),o=[],r=e.includes("#"),s=0;for(;s<n.length;){let i=n[s],a=i.match(
|
|
1758
|
-
`),t)}function
|
|
1759
|
-
`).filter(p=>{let
|
|
1760
|
-
`);return e.replace(s,`createProviderRegistry({${c}})`)}function
|
|
1761
|
-
`+r);let i={ai:"latest"},{providerIds:a,customEndpoint:c}=t;(a.includes("openai")||c||a.some(
|
|
1762
|
-
`)}}import y from"fs";import
|
|
1763
|
-
`)}let a=
|
|
1756
|
+
`}}async function ur(e,t){let n=yt.join(e,"packages","commerce","billing");if(!ge.existsSync(n))return;if(t==="none"){dr(n);return}let o=yt.join(n,"src"),r=new Set(Va(t));for(let i of $a)r.has(i)||dr(yt.join(o,i));let s=yt.join(o,"checkout","types.ts");if(ge.existsSync(s)){let i=t==="stripe"?"stripe":t==="lemon"?"lemonsqueezy":"chinapay",a=ge.readFileSync(s,"utf8"),c=a.replace(/export type CheckoutProviderType\s*=\s*[^;]+;/,`export type CheckoutProviderType = "${i}";`);c!==a&&ge.writeFileSync(s,c)}Ka(e,Ya(t))}import Ae from"fs";import nt from"path";var fr=new Set(["siliconflow","volcengine-ark","bailian","moonshot","sensetime"]),En=/@sailor:([^\s*]+)/;function qa(e,t){let{providerIds:n,customEndpoint:o}=t;if(e.startsWith("provider:")){let r=e.slice(9);return{marker:e,keep:n.includes(r)}}if(e==="cn-compatible"){let r=n.some(s=>fr.has(s));return{marker:e,keep:r||!!o}}if(e.startsWith("cn-instance:")){let r=e.slice(12);return{marker:e,keep:n.includes(r)}}return e==="custom-endpoint"?{marker:e,keep:!!o}:e==="registry-entries"?{marker:e,keep:!0}:{marker:e,keep:!0}}function mr(e){return e.replace(/\s*\/\/\s*@sailor:[^\n]*$/,"")}function gr(e){return e.replace(/\s*#\s*@sailor:[^\n]*$/,"")}function yr(e,t){let n=e.split(`
|
|
1757
|
+
`),o=[],r=e.includes("#"),s=0;for(;s<n.length;){let i=n[s],a=i.match(En);if(!a){o.push(i),s++;continue}let{marker:c,keep:p}=qa(a[1],t);if(c==="registry-entries"){o.push(r?gr(i):mr(i)),s++;continue}if(p){let d=r?gr(i):mr(i);d.trim().length>0&&o.push(d);let m=s+1;for(;m<n.length&&n[m].trim()!==""&&!En.test(n[m]);)o.push(n[m]),m++;m<n.length&&n[m].trim()===""&&(o.push(n[m]),m++),s=m}else{let d=s+1;for(;d<n.length&&n[d].trim()!==""&&!En.test(n[d]);)d++;d<n.length&&n[d].trim()===""&&d++,s=d}}return Ga(o.join(`
|
|
1758
|
+
`),t)}function Ga(e,t){let{providerIds:n,customEndpoint:o}=t,r=new Set;for(let p of n)["openai","anthropic","google","mistral","xai"].includes(p)&&r.add(p),r.add(p.replace(/-/g,"_")),r.add(p);o&&r.add("custom");let s=/createProviderRegistry\(\{([\s\S]*?)\}\)/,i=e.match(s);if(!i)return e;let c=i[1].split(`
|
|
1759
|
+
`).filter(p=>{let d=p.trim();if(d===""||d.startsWith("//"))return!0;let m=d.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*,?$/);return m?r.has(m[1]):!0}).join(`
|
|
1760
|
+
`);return e.replace(s,`createProviderRegistry({${c}})`)}function Ha(e,t){let n=nt.join(t,"registry.ts.template"),o=Ae.readFileSync(n,"utf-8");return yr(o,e)}function Wa(e,t){let n=nt.join(t,".env.example.template"),o=Ae.readFileSync(n,"utf-8");return yr(o,e)}async function Sr(e,t,n){let o=Ha(t,n),r=Wa(t,n),s=nt.join(e,"packages/ai/agents/src");await Ae.promises.mkdir(s,{recursive:!0}),await Ae.promises.writeFile(nt.join(s,"registry.ts"),o),await Ae.promises.appendFile(nt.join(e,".env.example"),`
|
|
1761
|
+
`+r);let i={ai:"latest"},{providerIds:a,customEndpoint:c}=t;(a.includes("openai")||c||a.some(d=>fr.has(d)))&&(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 p=nt.join(e,"package.json");if(Ae.existsSync(p)){let d=JSON.parse(await Ae.promises.readFile(p,"utf-8"));d.dependencies||(d.dependencies={});for(let[m,C]of Object.entries(i))d.dependencies[m]=C;await Ae.promises.writeFile(p,JSON.stringify(d,null,2)+`
|
|
1762
|
+
`)}}import y from"fs";import f from"path";function hr(e,t){if(!y.existsSync(e))return;let n=y.readdirSync(e);for(let o of n){let r=f.join(e,o);y.statSync(r).isDirectory()?hr(r,t):t(r)}}async function Er(e,t){let n=f.join(e,"package.json");if(!y.existsSync(n))return;let o=JSON.parse(y.readFileSync(n,"utf8"));if(t.orm!=="prisma"){let r=f.join(e,"packages","db");if(y.existsSync(r)){let s=f.join(r,"prisma");y.existsSync(s)&&y.rmSync(s,{recursive:!0,force:!0});let i=f.join(r,"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)+`
|
|
1763
|
+
`)}let a=f.join(r,"src","index.ts");y.existsSync(a)&&y.writeFileSync(a,`
|
|
1764
1764
|
export const prisma = {} as any;
|
|
1765
1765
|
export const db = {} as any;
|
|
1766
1766
|
export type PrismaClient = any;
|
|
@@ -1806,7 +1806,7 @@ export type UsageAggregate = any;
|
|
|
1806
1806
|
export type UsageLedgerEntry = any;
|
|
1807
1807
|
export type UsageType = any;
|
|
1808
1808
|
export type UsageLedgerSource = any;
|
|
1809
|
-
`)}o.scripts?.postinstall&&(o.scripts.postinstall=o.scripts.postinstall.replace(/prisma generate/g,'echo "ORM Disabled"'))}if(!t.i18n){let r=
|
|
1809
|
+
`)}o.scripts?.postinstall&&(o.scripts.postinstall=o.scripts.postinstall.replace(/prisma generate/g,'echo "ORM Disabled"'))}if(!t.i18n){let r=f.join(e,"apps");if(y.existsSync(r)){let s=y.readdirSync(r);for(let i of s){let a=f.join(r,i);if(!y.statSync(a).isDirectory())continue;let c=f.join(a,"src","app");if(!y.existsSync(c))continue;["[lang]","[locale]"].forEach(S=>{let u=f.join(c,S);if(y.existsSync(u)){let _=y.readdirSync(u);for(let H of _)y.renameSync(f.join(u,H),f.join(c,H));y.rmdirSync(u)}}),["src/middleware.ts","middleware.ts"].forEach(S=>{let u=f.join(a,S);y.existsSync(u)&&y.rmSync(u)});let p=f.join(a,"src","lib");y.existsSync(p)||y.mkdirSync(p,{recursive:!0});let d=f.join(p,"mock-i18n.ts");y.writeFileSync(d,`
|
|
1810
1810
|
import React from 'react';
|
|
1811
1811
|
export function useTranslations(namespace?: string): any { return (key: string) => key; }
|
|
1812
1812
|
export function useLocale() { return 'en'; }
|
|
@@ -1824,7 +1824,7 @@ export const useRouter = () => ({ push: (...args: any[]) => {}, replace: (...arg
|
|
|
1824
1824
|
export default function createMiddleware(...args: any[]) { return (req: any) => req; }
|
|
1825
1825
|
export function createNavigation(...args: any[]) { return { Link, redirect, usePathname, useRouter, getPathname: () => '/' }; }
|
|
1826
1826
|
export function defineRouting<T>(config: T): T { return config; }
|
|
1827
|
-
`);let
|
|
1827
|
+
`);let C=f.join(p,"mock-i18n.d.ts");y.writeFileSync(C,`declare module '@/lib/mock-i18n' {
|
|
1828
1828
|
export function useTranslations(namespace?: string): any;
|
|
1829
1829
|
export function useLocale(): string;
|
|
1830
1830
|
export const NextIntlClientProvider: React.FC<any>;
|
|
@@ -1842,19 +1842,19 @@ export function defineRouting<T>(config: T): T { return config; }
|
|
|
1842
1842
|
export function createNavigation(...args: any[]): any;
|
|
1843
1843
|
export function defineRouting<T>(config: T): T;
|
|
1844
1844
|
}
|
|
1845
|
-
`),y.writeFileSync(
|
|
1845
|
+
`),y.writeFileSync(f.join(p,"mock-i18n-index.ts"),`
|
|
1846
1846
|
export * from './mock-i18n';
|
|
1847
1847
|
export { default } from './mock-i18n';
|
|
1848
|
-
`);let
|
|
1848
|
+
`);let E=f.join(a,"tsconfig.json");if(y.existsSync(E))try{let S=y.readFileSync(E,"utf8");if(S.includes('"paths": {')){let u=S.replace(/"paths":\s*\{/,`"paths": {
|
|
1849
1849
|
"next-intl": ["./src/lib/mock-i18n"],
|
|
1850
|
-
"next-intl/*": ["./src/lib/mock-i18n"],`);y.writeFileSync(
|
|
1850
|
+
"next-intl/*": ["./src/lib/mock-i18n"],`);y.writeFileSync(E,u)}}catch{}let g=f.join(a,"src");y.existsSync(g)&&hr(g,S=>{if(!S.endsWith(".ts")&&!S.endsWith(".tsx"))return;let u=y.readFileSync(S,"utf8"),_=!1;S.endsWith("src/app/layout.tsx")&&u.includes("../providers")&&(u=u.replace(/from\s+['"]\.\.\/providers['"]/g,"from './providers'"),_=!0),u=u.replace(/params:\s*Promise<\{[^}]*(lang|locale)[^}]*\}>/g,"params: any"),u=u.replace(/\(\{\s*params\s*\}\s*:\s*\{\s*params:\s*any\s*\}\)/g,"({ params }: any)"),u.match(/locale\s*:\s*any/)&&(u=u.replace(/locale\s*:\s*any/g,"locale: string"),_=!0),u.match(/lang\s*:\s*any/)&&(u=u.replace(/lang\s*:\s*any/g,"lang: string"),_=!0),u.match(/requestLocale\s*:\s*any/)&&(u=u.replace(/requestLocale\s*:\s*any/g,"requestLocale: string"),_=!0),u.match(/\(locale\s*=\s*(.*)\)/)&&(u=u.replace(/\(locale\s*=\s*(.*)\)/g,"() "),_=!0),u.match(/export\s+default\s+async\s+function\s+LangLayout/)&&(u=u.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 })"),_=!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"))&&(u=u.replace(/const\s+\{\s*lang\s*\}\s*=\s*(await\s+)?params;?/g,'const lang = "en";'),u=u.replace(/const\s+\{\s*locale\s*\}\s*=\s*(await\s+)?params;?/g,'const locale = "en";'),u=u.replace(/const\s+locale\s*=\s*lang\s+as\s+Locale;?/g,'const locale = "en";'),u=u.replace(/\(l\)\s*=>/g,"(l: any) =>"),_=!0),S.endsWith("src/i18n/request.ts")&&(u=u.replace(/async\s*\(\{\s*requestLocale\s*\}\)/g,"async ({ requestLocale }: any)"),_=!0),(u.match(/locale\s*:/)||u.match(/lang\s*:/))&&(_=!0),_&&y.writeFileSync(S,u)});let h=f.join(a,"package.json");if(y.existsSync(h)){let S=JSON.parse(y.readFileSync(h,"utf8"));S.dependencies&&delete S.dependencies["next-intl"],y.writeFileSync(h,JSON.stringify(S,null,2)+`
|
|
1851
1851
|
`)}}}}y.writeFileSync(n,JSON.stringify(o,null,2)+`
|
|
1852
|
-
`)}function
|
|
1853
|
-
`)}function
|
|
1854
|
-
`);
|
|
1855
|
-
`)}async function
|
|
1856
|
-
`);
|
|
1857
|
-
`)}async function
|
|
1852
|
+
`)}function b(e){try{y.rmSync(e,{recursive:!0,force:!0})}catch{}}function Xa(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]:r,...s}=n.dependencies;n={...n,dependencies:s},o=!0}if(n.devDependencies&&t in n.devDependencies){let{[t]:r,...s}=n.devDependencies;n={...n,devDependencies:s},o=!0}o&&y.writeFileSync(e,JSON.stringify(n,null,2)+`
|
|
1853
|
+
`)}function vr(e,t){t.cronJobs||(b(f.join(e,"apps/web/src/app/api/cron")),b(f.join(e,"packages/integrations/queue/src/scheduled"))),t.auditLog||(b(f.join(e,"apps/web/src/app/[locale]/(app)/settings/audit-log")),b(f.join(e,"apps/web/src/app/api/audit-logs")),b(f.join(e,"apps/web/src/components/audit"))),t.apiKeys||(b(f.join(e,"apps/web/src/app/[locale]/(app)/settings/api-keys")),b(f.join(e,"apps/web/src/app/api/api-keys")),b(f.join(e,"apps/web/src/components/api-keys"))),t.webhooks||(b(f.join(e,"apps/web/src/app/[locale]/(app)/settings/webhooks")),b(f.join(e,"apps/web/src/app/api/webhooks")),b(f.join(e,"apps/web/src/components/webhooks")),b(f.join(e,"packages/integrations/webhooks"))),t.commandPalette||(b(f.join(e,"apps/web/src/components/command-palette")),b(f.join(e,"apps/web/src/app/[locale]/providers/command-palette-mount.tsx"))),t.cookieConsent||(b(f.join(e,"apps/landing/src/components/cookie-consent-banner.tsx")),b(f.join(e,"apps/landing/src/components/__tests__/cookie-consent-banner.test.tsx")),b(f.join(e,"apps/landing/src/lib/cookie-consent.ts")),b(f.join(e,"apps/landing/src/lib/__tests__/cookie-consent.test.ts")),b(f.join(e,"apps/web/src/app/api/cookie-consent"))),t.legalPages||(b(f.join(e,"apps/landing/src/app/[lang]/(legal)/legal")),b(f.join(e,"apps/landing/src/lib/legal-documents.ts")),b(f.join(e,"apps/landing/src/lib/__tests__/legal-documents.test.ts"))),t.chinaCompliance||(b(f.join(e,"packages/ops/china-compliance")),b(f.join(e,"apps/landing/src/components/icp-footer.tsx")),b(f.join(e,"apps/landing/src/components/__tests__/icp-footer.test.tsx")),b(f.join(e,"apps/web/src/components/auth/wechat-login-button.tsx")),b(f.join(e,"apps/web/src/components/auth/__tests__/wechat-login-button.test.tsx")),Xa(f.join(e,"apps/web/package.json"),"@nebutra/china-compliance"))}import ot from"fs";import br from"path";var _r=/^--\s*@conditional\(([A-Za-z][A-Za-z0-9_-]*)=([^)]+)\)\s*$/m;function za(e){let t=_r.exec(e);if(!t)return null;let[,n,o]=t,r=o.split("|").map(s=>s.trim()).filter(Boolean);return{flag:n,values:r}}function Ja(e){return e.replace(_r,"").replace(/^\n/,"")}function vn(e,t){let n={kept:[],removed:[],skipped:[]};if(!ot.existsSync(e))return n;let o=ot.readdirSync(e,{withFileTypes:!0}).filter(r=>r.isDirectory()).sort((r,s)=>r.name.localeCompare(s.name));for(let r of o){let s=br.join(e,r.name),i=br.join(s,"migration.sql");if(!ot.existsSync(i)){n.skipped.push(r.name);continue}let a=ot.readFileSync(i,"utf8"),c=za(a);if(!c){n.skipped.push(r.name);continue}let p=t[c.flag];if(p!==void 0&&c.values.includes(p)){ot.writeFileSync(i,Ja(a)),n.kept.push(r.name);continue}ot.rmSync(s,{recursive:!0,force:!0}),n.removed.push(r.name)}return n}import{getSchema as Qa,printSchema as Za}from"@mrleebo/prisma-ast";var ec=/^\/\/\/\s*@conditional\(([A-Za-z][A-Za-z0-9_-]*)=([^)]+)\)\s*$/;function tc(e){return e.type==="comment"&&e.text.trim().startsWith("//")}function nc(e){let t=ec.exec(e.trim());if(!t)return null;let[,n,o]=t,r=o.split("|").map(s=>s.trim()).filter(s=>s.length>0);return{flag:n,values:r}}function oc(e,t){for(let n=t-1;n>=0;n--){let o=e[n];if(o.type==="comment"){let r=nc(o.text);if(r)return{annIndex:n,parsed:r};continue}break}return null}function rc(e){return e.type==="model"||e.type==="enum"}function bn(e,t){let n=Qa(e),o=new Set;for(let a=0;a<n.list.length;a++){let c=n.list[a];if(!rc(c))continue;let p=oc(n.list,a);if(!p)continue;let d=t[p.parsed.flag];if(d!==void 0&&p.parsed.values.includes(d))o.add(p.annIndex);else{o.add(a);for(let E=a-1;E>=0;E--){let g=n.list[E];if(tc(g)){o.add(E);continue}break}let C=n.list[a+1];C&&C.type==="break"&&o.add(a+1)}}let r=n.list.filter((a,c)=>!o.has(c)),s=[];for(let a of r){let c=s[s.length-1];a.type==="break"&&c&&c.type==="break"||s.push(a)}return Za({type:"schema",list:s})}import rt from"fs";import wr from"path";var Ot=[{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 Pr(e){return Ot.find(t=>t.id===e)}var iu=Ot.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Ar(e){return rt.existsSync(e)?rt.readFileSync(e,"utf-8"):null}function sc(e,t){let n=wr.join(e,".env.example"),o=Ar(n);if(o===null)return;let r=`# Queue: ${t.name}`;if(o.includes(r))return;let s=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
1854
|
+
`);rt.appendFileSync(n,s)}function ic(e,t,n){let o=wr.join(e,".env.example"),r=Ar(o);if(r===null)return;let s=new RegExp(`^${t}=.*$`,"m");s.test(r)?rt.writeFileSync(o,r.replace(s,`${t}="${n}"`)):rt.appendFileSync(o,`${t}="${n}"
|
|
1855
|
+
`)}async function Cr(e,t,n){let o=Oe(e,"queue");if(t==="none"){o&&rt.rmSync(o,{recursive:!0,force:!0});return}let r=Pr(t);if(!r)throw new Error(`Unknown queue provider "${t}". Valid ids: ${Ot.map(s=>s.id).join(", ")}`);o&&(sc(e,r),ic(e,"QUEUE_PROVIDER",t))}import fe from"fs";import Ce from"path";import{fileURLToPath as ac}from"url";var cc=ac(import.meta.url),Ir=Ce.dirname(cc),pc=/\.(md|mdx|json|ts|tsx|js|mjs|cjs|yaml|yml|css|html|toml|py)$/,lc=["backends/gateway","infra","e2e","tests"];function dc(){let e=[Ce.join(Ir,"..","templates"),Ce.join(Ir,"..","..","templates")];for(let t of e)if(fe.existsSync(t))return t;return e[0]}async function xr(e,t){let n=dc(),o=[],r=[],s=[...lc,...t.withWorkflows?["workflows"]:[],...t.withPythonBackend?["backends/python"]:[]];for(let i of s){let a=Ce.join(n,i);if(!fe.existsSync(a)){r.push(i);continue}let c=Ce.join(e,i);Rr(a,c),Tr(c,{"{PRODUCT_NAME}":t.projectName}),o.push(i)}return{applied:o,skipped:r}}function Rr(e,t){fe.mkdirSync(t,{recursive:!0});for(let n of fe.readdirSync(e,{withFileTypes:!0})){let o=Ce.join(e,n.name),r=Ce.join(t,n.name);n.isDirectory()?Rr(o,r):fe.existsSync(r)||fe.copyFileSync(o,r)}}function Tr(e,t){for(let n of fe.readdirSync(e,{withFileTypes:!0})){let o=Ce.join(e,n.name);if(n.isDirectory()){Tr(o,t);continue}if(!pc.test(n.name))continue;let r=fe.readFileSync(o,"utf-8"),s=!1;for(let[i,a]of Object.entries(t))r.includes(i)&&(r=r.split(i).join(a),s=!0);s&&fe.writeFileSync(o,r)}}import st from"fs";import Nr from"path";var Lt=[{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 kr(e){return Lt.find(t=>t.id===e)}var Su=Lt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Or(e){return st.existsSync(e)?st.readFileSync(e,"utf-8"):null}function uc(e,t){let n=Nr.join(e,".env.example"),o=Or(n);if(o===null)return;let r=`# Search: ${t.name}`;if(o.includes(r))return;let s=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
1856
|
+
`);st.appendFileSync(n,s)}function mc(e,t,n){let o=Nr.join(e,".env.example"),r=Or(o);if(r===null)return;let s=new RegExp(`^${t}=.*$`,"m");s.test(r)?st.writeFileSync(o,r.replace(s,`${t}="${n}"`)):st.appendFileSync(o,`${t}="${n}"
|
|
1857
|
+
`)}async function Lr(e,t,n){let o=Oe(e,"search");if(t==="none"){o&&st.rmSync(o,{recursive:!0,force:!0});return}let r=kr(t);if(!r)throw new Error(`Unknown search provider "${t}". Valid ids: ${Lt.map(s=>s.id).join(", ")}`);o&&(uc(e,r),mc(e,"SEARCH_PROVIDER",t))}import Ie from"fs";import $e from"path";function gc(e){let t=[$e.join(e,"packages","db"),$e.join(e,"apps","web")];for(let n of t){let o=$e.join(n,"prisma","schema.prisma");if(Ie.existsSync(o))return{packageDir:n,seedPath:$e.join(n,"prisma","seed.ts"),packageJsonPath:$e.join(n,"package.json")}}return null}function fc(e){try{let t=Ie.readFileSync(e,"utf8"),n=new Set,o=/^\s*model\s+(\w+)\s*\{/gm,r=o.exec(t);for(;r;)n.add(r[1]),r=o.exec(t);return n}catch{return new Set}}function yc(e,t){return!t||e==="none"?` // Skipping admin user \u2014 auth=${JSON.stringify(e)} (or no User model).`:e==="clerk"?` try {
|
|
1858
1858
|
const admin = await prisma.user.create({
|
|
1859
1859
|
data: {
|
|
1860
1860
|
clerkId: "user_mock_admin",
|
|
@@ -1885,7 +1885,7 @@ export { default } from './mock-i18n';
|
|
|
1885
1885
|
console.log("\\u2713 Created admin user:", admin.email);
|
|
1886
1886
|
} catch (err) {
|
|
1887
1887
|
console.warn("\\u26A0 Admin user creation skipped:", (err as Error).message);
|
|
1888
|
-
}`}function
|
|
1888
|
+
}`}function Sc(e,t){let n=t.has("Tenant"),o=t.has("User"),r=t.has("Project"),s=yc(e,o),i=n?` let tenants: Array<{ id: string; name: string }> = [];
|
|
1889
1889
|
try {
|
|
1890
1890
|
tenants = await Promise.all([
|
|
1891
1891
|
prisma.tenant.create({
|
|
@@ -1954,12 +1954,12 @@ main()
|
|
|
1954
1954
|
.finally(async () => {
|
|
1955
1955
|
await prisma.$disconnect();
|
|
1956
1956
|
});
|
|
1957
|
-
`}function
|
|
1958
|
-
`)}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
|
|
1959
|
-
`+t):
|
|
1957
|
+
`}function hc(e){if(Ie.existsSync(e))try{let t=Ie.readFileSync(e,"utf8"),n=JSON.parse(t),o=n.scripts??{},r=!1;o["db:seed"]||(o["db:seed"]="prisma db seed",r=!0);let s=n.prisma??{};if(s.seed||(s.seed="tsx prisma/seed.ts",r=!0),!r)return;let i={...n,scripts:o,prisma:s};Ie.writeFileSync(e,JSON.stringify(i,null,2)+`
|
|
1958
|
+
`)}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 Ur(e,t){let n=gc(e);if(n)try{let o=$e.join(n.packageDir,"prisma","schema.prisma"),r=fc(o),s=Sc(t,r),i=$e.dirname(n.seedPath);Ie.existsSync(i)||Ie.mkdirSync(i,{recursive:!0}),Ie.writeFileSync(n.seedPath,s),hc(n.packageJsonPath)}catch(o){let r=o instanceof Error?o.message:String(o);throw console.error(`[create-sailor] Failed to generate seed data: ${r}`),new Error(`Unable to write prisma/seed.ts: ${r}`)}}import xe from"fs";import Ve from"path";var Ut=[{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:""}],Iu=Ut.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Dr(e){return Ut.find(t=>t.id===e)}var Ec="@nebutra/sms",vc={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 _n(e,t){xe.mkdirSync(Ve.dirname(e),{recursive:!0}),xe.writeFileSync(e,t)}function bc(e,t){let n=Ve.join(e,".env.example");xe.existsSync(n)?xe.appendFileSync(n,`
|
|
1959
|
+
`+t):xe.writeFileSync(n,t)}function _c(e){if(e.envVars.length===0)return"";let t=`# SMS provider: ${e.name}`,n=e.envVars.map(o=>`${o}=`).join(`
|
|
1960
1960
|
`);return`${t}
|
|
1961
1961
|
${n}
|
|
1962
|
-
`}var
|
|
1962
|
+
`}var Pc=`/**
|
|
1963
1963
|
* Inputs for the \`sendSms\` wrapper. Keep this surface portable across
|
|
1964
1964
|
* providers \u2014 provider-specific options belong in the wrapper itself.
|
|
1965
1965
|
*/
|
|
@@ -1979,7 +1979,7 @@ export interface SendSmsResult {
|
|
|
1979
1979
|
provider: string;
|
|
1980
1980
|
raw?: unknown;
|
|
1981
1981
|
}
|
|
1982
|
-
`;function
|
|
1982
|
+
`;function wc(){return`import twilio from "twilio";
|
|
1983
1983
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
1984
1984
|
|
|
1985
1985
|
let client: ReturnType<typeof twilio> | null = null;
|
|
@@ -2014,7 +2014,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2014
2014
|
}
|
|
2015
2015
|
|
|
2016
2016
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2017
|
-
`}function
|
|
2017
|
+
`}function Ac(){return`import messagebird from "messagebird";
|
|
2018
2018
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2019
2019
|
|
|
2020
2020
|
let client: ReturnType<typeof messagebird> | null = null;
|
|
@@ -2052,7 +2052,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2052
2052
|
}
|
|
2053
2053
|
|
|
2054
2054
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2055
|
-
`}function
|
|
2055
|
+
`}function Cc(){return`import plivo from "plivo";
|
|
2056
2056
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2057
2057
|
|
|
2058
2058
|
let client: InstanceType<typeof plivo.Client> | null = null;
|
|
@@ -2091,7 +2091,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2091
2091
|
}
|
|
2092
2092
|
|
|
2093
2093
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2094
|
-
`}function
|
|
2094
|
+
`}function Ic(){return`import Dysmsapi, { SendSmsRequest } from "@alicloud/dysmsapi20170525";
|
|
2095
2095
|
import * as $OpenApi from "@alicloud/openapi-client";
|
|
2096
2096
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2097
2097
|
|
|
@@ -2140,7 +2140,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2140
2140
|
}
|
|
2141
2141
|
|
|
2142
2142
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2143
|
-
`}function
|
|
2143
|
+
`}function xc(){return`import * as tencentcloud from "tencentcloud-sdk-nodejs-sms";
|
|
2144
2144
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2145
2145
|
|
|
2146
2146
|
const SmsClient = tencentcloud.sms.v20210111.Client;
|
|
@@ -2205,7 +2205,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2205
2205
|
}
|
|
2206
2206
|
|
|
2207
2207
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2208
|
-
`}function
|
|
2208
|
+
`}function Rc(){return`// @ts-expect-error \u2014 yunpian-nodejs-sdk ships no type declarations
|
|
2209
2209
|
import Yunpian from "yunpian-nodejs-sdk";
|
|
2210
2210
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2211
2211
|
|
|
@@ -2257,13 +2257,13 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2257
2257
|
}
|
|
2258
2258
|
|
|
2259
2259
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2260
|
-
`}function
|
|
2261
|
-
`}}function
|
|
2262
|
-
`}async function
|
|
2263
|
-
`+t):
|
|
2260
|
+
`}function Tc(e){switch(e){case"twilio":return wc();case"messagebird":return Ac();case"plivo":return Cc();case"aliyun-sms":return Ic();case"tencent-sms":return xc();case"yunpian":return Rc();case"none":return`export {};
|
|
2261
|
+
`}}function kc(e){let t={};for(let o of e.pkgs??[]){let r=vc[o];r&&(t[o]=r)}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)+`
|
|
2262
|
+
`}async function Mr(e,t,n="global"){if(t==="none")return;let o=Dr(t);if(!o)throw new Error(`Unknown SMS provider "${t}". Valid ids: ${Ut.map(i=>i.id).join(", ")}`);let r=Ve.join(e,"packages","sms");if(!xe.existsSync(r))return;let s=Ve.join(r,"src");xe.existsSync(s)&&xe.rmSync(s,{recursive:!0,force:!0});try{_n(Ve.join(r,"package.json"),kc(o)),_n(Ve.join(s,"types.ts"),Pc),_n(Ve.join(s,"index.ts"),Tc(o.id)),bc(e,_c(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 Ye from"fs";import Ke from"path";var qe="@nebutra/storage";function Pn(e,t){Ye.mkdirSync(Ke.dirname(e),{recursive:!0}),Ye.writeFileSync(e,t)}function Nc(e,t){let n=Ke.join(e,".env.example");Ye.existsSync(n)?Ye.appendFileSync(n,`
|
|
2263
|
+
`+t):Ye.writeFileSync(n,t)}function Oc(e){if(e.envVars.length===0)return"";let t=`# Storage provider: ${e.name}`,n=e.envVars.map(o=>`${o}=`).join(`
|
|
2264
2264
|
`);return`${t}
|
|
2265
2265
|
${n}
|
|
2266
|
-
`}var
|
|
2266
|
+
`}var Lc=`/**
|
|
2267
2267
|
* Inputs for the \`uploadFile\` wrapper. Keep this surface portable across
|
|
2268
2268
|
* providers \u2014 provider-specific options belong in the wrapper itself.
|
|
2269
2269
|
*/
|
|
@@ -2289,8 +2289,8 @@ export interface SignedUrlOptions {
|
|
|
2289
2289
|
operation?: "get" | "put";
|
|
2290
2290
|
contentType?: string;
|
|
2291
2291
|
}
|
|
2292
|
-
`;function
|
|
2293
|
-
* @${
|
|
2292
|
+
`;function Uc(){return`/**
|
|
2293
|
+
* @${qe.slice(1)} \u2014 Cloudflare R2 via the S3-compatible API.
|
|
2294
2294
|
*
|
|
2295
2295
|
* Docs: https://developers.cloudflare.com/r2/api/s3/api/
|
|
2296
2296
|
*/
|
|
@@ -2353,8 +2353,8 @@ export async function getSignedUrl(
|
|
|
2353
2353
|
}
|
|
2354
2354
|
|
|
2355
2355
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2356
|
-
`}function
|
|
2357
|
-
* @${
|
|
2356
|
+
`}function Dc(){return`/**
|
|
2357
|
+
* @${qe.slice(1)} \u2014 AWS S3 wrapper.
|
|
2358
2358
|
*
|
|
2359
2359
|
* Docs: https://docs.aws.amazon.com/s3/
|
|
2360
2360
|
*/
|
|
@@ -2418,8 +2418,8 @@ export async function getSignedUrl(
|
|
|
2418
2418
|
}
|
|
2419
2419
|
|
|
2420
2420
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2421
|
-
`}function
|
|
2422
|
-
* @${
|
|
2421
|
+
`}function Mc(){return`/**
|
|
2422
|
+
* @${qe.slice(1)} \u2014 Supabase Storage wrapper.
|
|
2423
2423
|
*
|
|
2424
2424
|
* Docs: https://supabase.com/docs/guides/storage
|
|
2425
2425
|
*/
|
|
@@ -2481,8 +2481,8 @@ export async function getSignedUrl(
|
|
|
2481
2481
|
}
|
|
2482
2482
|
|
|
2483
2483
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2484
|
-
`}function
|
|
2485
|
-
* @${
|
|
2484
|
+
`}function Fc(){return`/**
|
|
2485
|
+
* @${qe.slice(1)} \u2014 \u963F\u91CC\u4E91 OSS wrapper via ali-oss.
|
|
2486
2486
|
*
|
|
2487
2487
|
* Docs: https://help.aliyun.com/product/31815.html
|
|
2488
2488
|
*/
|
|
@@ -2528,8 +2528,8 @@ export async function getSignedUrl(
|
|
|
2528
2528
|
}
|
|
2529
2529
|
|
|
2530
2530
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2531
|
-
`}function
|
|
2532
|
-
* @${
|
|
2531
|
+
`}function jc(){return`/**
|
|
2532
|
+
* @${qe.slice(1)} \u2014 \u817E\u8BAF\u4E91 COS wrapper via cos-nodejs-sdk-v5.
|
|
2533
2533
|
*
|
|
2534
2534
|
* Docs: https://cloud.tencent.com/document/product/436
|
|
2535
2535
|
*/
|
|
@@ -2583,8 +2583,8 @@ export async function getSignedUrl(
|
|
|
2583
2583
|
}
|
|
2584
2584
|
|
|
2585
2585
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2586
|
-
`}function
|
|
2587
|
-
* @${
|
|
2586
|
+
`}function Bc(){return`/**
|
|
2587
|
+
* @${qe.slice(1)} \u2014 \u4E03\u725B\u4E91 Kodo wrapper.
|
|
2588
2588
|
*
|
|
2589
2589
|
* Docs: https://developer.qiniu.com/kodo
|
|
2590
2590
|
*/
|
|
@@ -2651,9 +2651,9 @@ export async function getSignedUrl(
|
|
|
2651
2651
|
}
|
|
2652
2652
|
|
|
2653
2653
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2654
|
-
`}function
|
|
2655
|
-
`}}function
|
|
2656
|
-
`}async function
|
|
2654
|
+
`}function $c(e){switch(e){case"r2":return Uc();case"s3":return Dc();case"supabase-storage":return Mc();case"aliyun-oss":return Fc();case"tencent-cos":return jc();case"qiniu":return Bc();case"none":return`export {};
|
|
2655
|
+
`}}function Vc(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:qe,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)+`
|
|
2656
|
+
`}async function Fr(e,t,n){if(t==="none")return;let o=Fn(t);if(!o)throw new Error(`Unknown storage provider "${t}". Valid ids: ${lt.map(i=>i.id).join(", ")}`);let r=Ke.join(e,"packages","storage"),s=Ke.join(r,"src");Ye.existsSync(s)&&Ye.rmSync(s,{recursive:!0,force:!0}),Pn(Ke.join(r,"package.json"),Vc(o)),Pn(Ke.join(s,"types.ts"),Lc),Pn(Ke.join(s,"index.ts"),$c(o.id)),Nc(e,Oc(o))}var Dt=[{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 jr(e){return Dt.find(t=>t.id===e)}var Mu=Dt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Br(e,t,n="global"){let o=jr(t);if(!o)throw new Error(`Unknown webhooks provider "${t}". Valid ids: ${Dt.map(r=>r.id).join(", ")}`);if(o.id==="none"){J(e,"webhooks");return}o.envVars.length>0&&re(e,{category:"Webhooks",name:o.name,envVars:o.envVars,docs:o.docs}),se(e,"WEBHOOKS_PROVIDER",o.id)}import Ge from"fs";import St from"path";function Kc(e){return`import { brand } from "@nebutra/brand/metadata";
|
|
2657
2657
|
import Link from "next/link";
|
|
2658
2658
|
|
|
2659
2659
|
export default function WelcomePage() {
|
|
@@ -2763,21 +2763,21 @@ function Step({
|
|
|
2763
2763
|
</li>
|
|
2764
2764
|
);
|
|
2765
2765
|
}
|
|
2766
|
-
`}function
|
|
2766
|
+
`}function Yc(e={}){return`
|
|
2767
2767
|
## What you can do next
|
|
2768
2768
|
|
|
2769
2769
|
${[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(`
|
|
2770
2770
|
`)}
|
|
2771
|
-
`}function
|
|
2771
|
+
`}function qc(e=[]){return e.length===0?"":`
|
|
2772
2772
|
## Production readiness holds
|
|
2773
2773
|
|
|
2774
2774
|
Do not enable these in production until provider credentials, adapter wiring, and the integration contract are verified:
|
|
2775
2775
|
|
|
2776
|
-
${e.map(n=>{let o=
|
|
2776
|
+
${e.map(n=>{let o=Qe(n.status);return`- ${n.flag}=${n.provider} ${o} \u2014 ${_e(n.status)}`}).join(`
|
|
2777
2777
|
`)}
|
|
2778
2778
|
|
|
2779
2779
|
Review the package readiness matrix before handoff: [docs/package-status.md](docs/package-status.md)
|
|
2780
|
-
`}function
|
|
2780
|
+
`}function Gc(e,t,n=[],o={}){return`# Next steps \u2014 ${e}
|
|
2781
2781
|
|
|
2782
2782
|
Region: ${t}
|
|
2783
2783
|
|
|
@@ -2803,27 +2803,27 @@ Your AI-native SaaS scaffold is ready. Complete these four steps to go from temp
|
|
|
2803
2803
|
\`\`\`
|
|
2804
2804
|
|
|
2805
2805
|
After setup, delete the welcome route at \`apps/web/src/app/[locale]/welcome/\`.
|
|
2806
|
-
${
|
|
2806
|
+
${Yc(o)}${qc(n)}
|
|
2807
2807
|
|
|
2808
2808
|
## Resources
|
|
2809
2809
|
|
|
2810
2810
|
- [Documentation](/docs)
|
|
2811
2811
|
- [GitHub](https://github.com/nebutra/nebutra-sailor)
|
|
2812
2812
|
- [Get free license (Individual/OPC)](https://nebutra.com/get-license)
|
|
2813
|
-
`}async function
|
|
2814
|
-
`)}function
|
|
2815
|
-
`))}}function
|
|
2813
|
+
`}async function $r(e,t){let{projectName:n,region:o,previewSelections:r=[],waveFeatures:s={}}=t;try{let i=St.join(e,".sailor");Ge.existsSync(i)||Ge.mkdirSync(i,{recursive:!0}),Ge.writeFileSync(St.join(i,"next-steps.md"),Gc(n,o,r,s))}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=St.join(e,"apps","web");if(!Ge.existsSync(i))return;let a=St.join(i,"src","app","[locale]","welcome");Ge.existsSync(a)||Ge.mkdirSync(a,{recursive:!0}),Ge.writeFileSync(St.join(a,"page.tsx"),Kc(n))}catch(i){let a=i instanceof Error?i.message:String(i);console.warn(`[create-sailor] Failed to write welcome page: ${a}`)}}function l(e,t){e&&process.stdout.write(JSON.stringify(t)+`
|
|
2814
|
+
`)}function Vr(e){return e!==null&&typeof e=="object"&&"code"in e&&e.code==="ENOENT"}function Wc(e,t){for(let n of t)if(e)l(!0,{event:"warn",step:n.flag,provider:n.provider,packageStatus:n.status,message:_e(n.status)});else{let o=Qe(n.status);process.stdout.write(ht.yellow(`\u26A0 ${n.flag}=${n.provider} ${o} \u2014 ${_e(n.status)}
|
|
2815
|
+
`))}}function Xc(e,t,n){let o=He.join(t,"packages/platform/db/prisma/schema.prisma"),r;try{r=Ft.readFileSync(o,"utf8")}catch(s){if(Vr(s)){l(e,{event:"step",step:"schema-prune",status:"skip"});return}let i=s instanceof Error?s.message:String(s);l(e,{event:"step",step:"schema-prune",status:"error",error:i});return}l(e,{event:"step",step:"schema-prune",status:"start"});try{let s=bn(r,n);Ft.writeFileSync(o,s),vn(He.join(He.dirname(o),"migrations"),n),l(e,{event:"step",step:"schema-prune",status:"ok"})}catch(s){let i=s instanceof Error?s.message:String(s);l(e,{event:"step",step:"schema-prune",status:"error",error:i})}}async function Kr(e){let{resolvedTarget:t,projectName:n,resolvedPm:o,opts:r,useJson:s,resolved:i,startedAt:a}=e,{region:c,orm:p,database:d,databaseHost:m,payment:C,paymentChoice:E,auth:g,socialLoginIds:h,aiProviders:S,deployTarget:u,docs:_,metering:H,billingMode:F,idp:he,accessGate:le,mcp:ee,waveToggles:B,previewSelections:K,config:I}=i,W={auth:g,payment:E,"billing-mode":F,idp:he,template:"saas","access-gate":le};l(s,{event:"step",step:"clone",status:"start"}),await Go(t),l(s,{event:"step",step:"clone",status:"ok"}),l(s,{event:"step",step:"package",status:"start"}),await ar(t,n),l(s,{event:"step",step:"package",status:"ok"}),l(s,{event:"step",step:"config",status:"start"}),await vo(t,I),l(s,{event:"step",step:"config",status:"ok"}),l(s,{event:"step",step:"governance-lints",status:"start"});let de=await Wo(t,I);l(s,{event:"step",step:"governance-lints",status:"ok",lints:de.lints}),l(s,{event:"step",step:"prune",status:"start"}),await Er(t,I),l(s,{event:"step",step:"prune",status:"ok"});{let v=He.join(t,"packages/platform/db/prisma/schema.prisma"),T;try{T=Ft.readFileSync(v,"utf-8")}catch(A){if(Vr(A))l(s,{event:"step",step:"schema-prune",status:"skip"});else{let x=A instanceof Error?A.message:String(A);l(s,{event:"step",step:"schema-prune",status:"error",error:x})}}if(T!==void 0){l(s,{event:"step",step:"schema-prune",status:"start"});try{let A=bn(T,W);Ft.writeFileSync(v,A),vn(He.join(He.dirname(v),"migrations"),W),l(s,{event:"step",step:"schema-prune",status:"ok"})}catch(A){let x=A instanceof Error?A.message:String(A);l(s,{event:"step",step:"schema-prune",status:"error",error:x})}}}if(l(s,{event:"step",step:"auth",choice:g,status:"start"}),await ao(t,g),l(s,{event:"step",step:"auth",choice:g,status:"ok"}),h.length>0?(l(s,{event:"step",step:"social-login",providers:h,status:"start"}),await po(t,h,g),l(s,{event:"step",step:"social-login",providers:h,status:"ok"})):l(s,{event:"step",step:"social-login",status:"skip"}),l(s,{event:"step",step:"db",choice:d,status:"start"}),await Po(t,d,n),d!=="none"){let v=pt(m);v&&(await wo(t,v,n),l(s,{event:"step",step:"db-host",choice:m,status:"ok"}))}if(l(s,{event:"step",step:"db",choice:d,status:"ok"}),p==="drizzle"){l(s,{event:"step",step:"orm",choice:"drizzle",status:"start"});let v=await lr(t,"drizzle",d);l(s,{event:"step",step:"orm",choice:"drizzle",status:v.applied?"ok":"skip",...v.reason?{reason:v.reason}:{}})}if(l(s,{event:"step",step:"payment",choice:E,status:"start"}),await ur(t,E),l(s,{event:"step",step:"payment",choice:E,status:"ok"}),await ko(t,i.email,c),s&&l(!0,{event:"step",step:"email",choice:i.email,status:"ok"}),await Fr(t,i.storage,c),s&&l(!0,{event:"step",step:"storage",choice:i.storage,status:"ok"}),await rr(t,i.monitoring,c),s&&l(!0,{event:"step",step:"monitoring",choice:i.monitoring,status:"ok"}),await so(t,i.analytics,c),s&&l(!0,{event:"step",step:"analytics",choice:i.analytics,status:"ok"}),await Mr(t,i.sms,c),s&&l(!0,{event:"step",step:"sms",choice:i.sms,status:"ok"}),await Cr(t,i.queue,c),s&&l(!0,{event:"step",step:"queue",choice:i.queue,status:"ok"}),await Lr(t,i.search,c),s&&l(!0,{event:"step",step:"search",choice:i.search,status:"ok"}),await go(t,i.cache,c),s&&l(!0,{event:"step",step:"cache",choice:i.cache,status:"ok"}),await ir(t,i.notifications,c),s&&l(!0,{event:"step",step:"notifications",choice:i.notifications,status:"ok"}),await Br(t,i.webhooks,c),s&&l(!0,{event:"step",step:"webhooks",choice:i.webhooks,status:"ok"}),await ho(t,i.cms,c),s&&l(!0,{event:"step",step:"cms",choice:i.cms,status:"ok"}),await jo(t,i.featureFlags,c),s&&l(!0,{event:"step",step:"feature-flags",choice:i.featureFlags,status:"ok"}),await yo(t,i.captcha,c),s&&l(!0,{event:"step",step:"captcha",choice:i.captcha,status:"ok"}),await Zo(t,ee),s&&l(!0,{event:"step",step:"mcp",mode:ee,status:"ok"}),await tr(t,H,C),s&&l(!0,{event:"step",step:"metering",mode:H,status:"ok"}),l(s,{event:"step",step:"prune-wave-features",status:"start"}),vr(t,B),l(s,{event:"step",step:"prune-wave-features",status:"ok",toggles:B}),I.orm==="prisma"&&I.database!=="none"?Xc(s,t,W):l(s,{event:"step",step:"schema-prune",status:"skip"}),l(s,{event:"step",step:"billing-mode",choice:F,status:"ok"}),l(s,{event:"step",step:"idp",choice:he,status:"ok"}),await Eo(t,c),s&&l(!0,{event:"step",step:"compliance",region:c,status:"ok"}),await Mo(t),await Ur(t,g),await $r(t,{projectName:n,region:c,previewSelections:K,waveFeatures:B}),I.aiMode!=="none"){l(s,{event:"step",step:"ai-providers",status:"start"});let v={providerIds:I.aiProviders,customEndpoint:I.customAiEndpoint},T=He.join(t,"packages/ai/ai-providers/templates");await Sr(t,v,T),l(s,{event:"step",step:"ai-providers",status:"ok"})}else l(s,{event:"step",step:"ai-providers",status:"skip"});l(s,{event:"step",step:"docs",status:"start"}),_!=="none"?(await Io(t,{framework:_,projectName:n}),l(s,{event:"step",step:"docs",framework:"fumadocs",requested:_,status:"ok"})):l(s,{event:"step",step:"docs",status:"skip"}),l(s,{event:"step",step:"deploy-target",status:"start"}),u!=="none"&&await Qn(t,u),await Jn(t,I.deployTargets),l(s,{event:"step",step:"deploy-target",status:"ok",targets:I.deployTargets});let P={databaseUrl:"postgresql://postgres:postgres@localhost:5432/nebutra",clerkPublishable:"",clerkSecret:""};l(s,{event:"step",step:"env",status:"start"}),await Lo(t,P),l(s,{event:"step",step:"env",status:"ok"}),l(s,{event:"step",step:"scaffold-extras",status:"start"});let Ee=await xr(t,{projectName:n,withWorkflows:!!r.withWorkflows,withPythonBackend:!!r.withPythonBackend});l(s,{event:"step",step:"scaffold-extras",status:"ok",applied:Ee.applied,skipped:Ee.skipped}),l(s,{event:"step",step:"license",status:"start"});try{let v=Qo(t,{projectName:n,cliVersion:z});l(s,{event:"step",step:"license",status:"ok",tier:"mit-scaffold",wrote:v.wrote}),s||process.stdout.write(ht.dim(` License: MIT \u2014 commercial use, closed source, no fee, no attribution.
|
|
2816
2816
|
Upstream terms preserved as LICENSE-UPSTREAM-REFERENCE.md.
|
|
2817
|
-
`))}catch(
|
|
2818
|
-
`));try{
|
|
2819
|
-
`))}}else l(s,{event:"step",step:"install",status:"skip"});if(r.git!==!1){s&&l(!0,{event:"step",step:"git-init",status:"start"});try{
|
|
2820
|
-
`))}}else l(s,{event:"step",step:"git-init",status:"skip"});let
|
|
2821
|
-
`));return}
|
|
2817
|
+
`))}catch(v){l(s,{event:"step",step:"license",status:"warn",message:v instanceof Error?v.message:String(v)})}if(r.install!==!1){let v=o==="bun"?"bun":o,T=["install"];s?l(!0,{event:"step",step:"install",pm:o,status:"start"}):process.stdout.write(ht.dim(` Installing dependencies with ${o}\u2026
|
|
2818
|
+
`));try{Mt(v,T,{cwd:t,stdio:s?"ignore":"inherit"}),l(s,{event:"step",step:"install",status:"ok"})}catch(A){let x=A instanceof Error?A.message:String(A);s?l(!0,{event:"step",step:"install",status:"error",error:x}):process.stdout.write(ht.yellow(` \u26A0 install failed \u2014 run '${o} install' manually.
|
|
2819
|
+
`))}}else l(s,{event:"step",step:"install",status:"skip"});if(r.git!==!1){s&&l(!0,{event:"step",step:"git-init",status:"start"});try{Mt("git",["init","-q"],{cwd:t,stdio:"ignore"}),Mt("git",["add","-A"],{cwd:t,stdio:"ignore"}),Mt("git",["-c","user.email=you@example.com","-c","user.name=You","commit","-q","-m","chore: initial scaffold from create-sailor"],{cwd:t,stdio:"ignore"}),l(s,{event:"step",step:"git-init",status:"ok"})}catch(v){let T=v instanceof Error?v.message:String(v);s?l(!0,{event:"step",step:"git-init",status:"error",error:T}):process.stdout.write(ht.yellow(` \u26A0 git init skipped \u2014 not fatal.
|
|
2820
|
+
`))}}else l(s,{event:"step",step:"git-init",status:"skip"});let ve=Math.max(1,Math.round((Date.now()-a)/1e3));io({template_version:z,package_manager:o,region:c,auth:g,payment:E,ai_providers:S,deploy_target:u,duration_ms:Date.now()-a}),K.length>0&&Wc(s,K),s?l(!0,{event:"done",status:"ok",elapsedSec:ve,targetDir:t,previewSelections:K,waveFeatures:B}):no({elapsedSec:ve,targetDir:t,skippedInstall:r.install===!1,previewSelections:K,waveFeatures:B});try{Hc({pkg:{name:"create-sailor",version:z},updateCheckInterval:1e3*60*60*24}).notify({defer:!1,isGlobal:!0})}catch{}}import zc from"cfonts";import Jc from"picocolors";function Yr(){if(process.env.NO_COLOR||!process.stdout.isTTY){process.stdout.write([`Sailor v${z}`,"AI-Native SaaS Unicorn Template","by Nebutra \xB7 https://nebutra.com","",""].join(`
|
|
2821
|
+
`));return}zc.say("Sailor",{font:"block",align:"left",colors:["#0033FE","#0BF1C3"],gradient:["#0033FE","#0BF1C3"],transitionGradient:!0,space:!1}),process.stdout.write(Jc.dim(` AI-Native SaaS Template \xB7 v${z} \xB7 nebutra.com
|
|
2822
2822
|
|
|
2823
|
-
`))}var
|
|
2823
|
+
`))}var Qc=`Usage: create-sailor [name] [options]
|
|
2824
2824
|
|
|
2825
2825
|
Arguments:
|
|
2826
|
-
name project
|
|
2826
|
+
name project name or path (default: my-app; use . for cwd)
|
|
2827
2827
|
|
|
2828
2828
|
Core options:
|
|
2829
2829
|
-p, --pm <id> npm | pnpm | yarn | bun (auto-detected)
|
|
@@ -2891,31 +2891,36 @@ Examples:
|
|
|
2891
2891
|
$ npx create-sailor@latest my-app --region=cn --auth=clerk
|
|
2892
2892
|
$ npx create-sailor@latest my-app --region=hybrid --ai=openai,deepseek
|
|
2893
2893
|
$ npm create sailor@latest --dry-run --region=cn
|
|
2894
|
-
`;function
|
|
2895
|
-
`)}import{existsSync as
|
|
2896
|
-
`))}catch{}try{
|
|
2897
|
-
`)}
|
|
2898
|
-
|
|
2899
|
-
`
|
|
2900
|
-
`);
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
`))
|
|
2894
|
+
`;function qr(){process.stdout.write(Qc)}import jt from"picocolors";function Gr(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function Zc(e){let t=Gr()?"\u25B8":"->",n=`[${e.index}/${e.total}]`,o=e.label.padEnd(18," ");return Gr()?`${jt.dim(n)} ${jt.bold(o)} ${jt.dim(t)} ${jt.cyan(e.value)}`:`${n} ${o} ${t} ${e.value}`}function Hr(e){process.stdout.write(Zc(e)+`
|
|
2895
|
+
`)}import{existsSync as ep,mkdirSync as Wr,writeFileSync as Xr}from"fs";import{homedir as zr,platform as tp}from"os";import{join as Et}from"path";function np(){if(tp()==="win32"){let t=process.env.LOCALAPPDATA||Et(zr(),"AppData","Local");return Et(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?Et(e,"nebutra"):Et(zr(),".config","nebutra")}function op(){let e=process.env.NEBUTRA_TELEMETRY;return e==="0"||e==="false"||e==="no"}function Jr(){if(!process.stderr.isTTY||!process.stdin.isTTY||process.env.CI)return;let e=np(),t=Et(e,"first-run-acked");try{if(ep(t))return}catch{return}if(op()){try{Wr(e,{recursive:!0}),Xr(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(`
|
|
2896
|
+
`))}catch{}try{Wr(e,{recursive:!0}),Xr(t,"")}catch{}}import at from"fs";import ye from"path";import*as M from"@clack/prompts";var Se="my-app",rp=new Set([".DS_Store",".Spotlight-V100",".Trashes","Thumbs.db","desktop.ini"]),Qr=new Set(["package.json","node_modules","pnpm-workspace.yaml","pnpm-lock.yaml","yarn.lock","package-lock.json","bun.lockb","turbo.json"]);function Cn(e){let t=e.trim();return t==="."||t==="./"||t===".\\"}function wn(e){if(!at.existsSync(e))return!0;let t;try{t=at.readdirSync(e)}catch{return!1}return t.every(n=>rp.has(n)||n===".git")}function An(e){if(!at.existsSync(e))return!1;let t;try{t=at.readdirSync(e)}catch{return!0}return t.some(n=>Qr.has(n))}function sp(e=process.cwd()){return wn(e)&&!An(e)}function Zr(e){let t=e.trim();if(!t)return"Please enter a project name.";if(t.length>128)return"Name is too long (max 128 characters).";if(!Cn(t)&&!(/[\\/]/.test(t)||ye.isAbsolute(t))&&!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(t))return"Use letters, numbers, dots, underscores, or hyphens (start with a letter or number)."}function it(e,t=process.cwd()){let n=e.trim()||Se;if(Cn(n)){let a=ye.resolve(t);return{targetDir:".",projectName:ye.basename(a)||Se,absoluteDir:a}}if(ye.isAbsolute(n)||n.startsWith("./")||n.startsWith(".\\")||n.startsWith("../")||n.startsWith("..\\")||n.includes("/")||n.includes("\\")){let a=ye.resolve(t,n);return{targetDir:n,projectName:ye.basename(a)||Se,absoluteDir:a}}let r=Zr(n);if(r)throw new Error(r);let s=`./${n}`,i=ye.resolve(t,n);return{targetDir:s,projectName:n,absoluteDir:i}}async function es(e={}){let t=e.cwd??process.cwd(),n=e.onCancel??(()=>{process.stdout.write(`Cancelled
|
|
2897
|
+
`),process.exit(130)}),o=sp(t),r=ye.basename(t)||t,s=await M.select({message:"Create the project\u2026",options:[{value:"new",label:"In a new folder",hint:o?void 0:"recommended from home or a parent directory"},{value:"current",label:"In the current directory",hint:o?`${r} looks empty \u2014 good to go`:t}],initialValue:o?"current":"new"});if(M.isCancel(s)&&n(),s==="current"){if(An(t)){let c=at.readdirSync(t).filter(p=>Qr.has(p)).slice(0,4).join(", ");M.log.error(`Current directory already looks like a project (${c}). Choose a new folder, or pass a different path: create-sailor my-app`),n()}if(!wn(t)){let c=await M.confirm({message:"Current directory is not empty. Scaffold files will be mixed in \u2014 continue?",initialValue:!1});(M.isCancel(c)||!c)&&n()}return it(".",t)}let i=await M.text({message:"What should we call your project?",placeholder:Se,defaultValue:Se,validate:c=>{let p=(c??"").trim()||Se;if(Cn(p)||/[\\/]/.test(p)||ye.isAbsolute(p))try{it(p,t);return}catch(d){return d instanceof Error?d.message:"Invalid path."}return Zr(p)}});M.isCancel(i)&&n();let a=it(String(i).trim()||Se,t);if(at.existsSync(a.absoluteDir)&&!wn(a.absoluteDir)){An(a.absoluteDir)&&(M.log.error(`Folder "${a.projectName}" already exists and looks like a project. Pick another name.`),n());let c=await M.confirm({message:`Folder "${a.projectName}" is not empty. Continue?`,initialValue:!1});(M.isCancel(c)||!c)&&n()}return a}function Re(e,t){e&&process.stdout.write(JSON.stringify(t)+`
|
|
2898
|
+
`)}function ap(e,t,n,o,r){let{region:s,orm:i,database:a,paymentChoice:c,docs:p,socialLoginIds:d,aiMode:m,aiProviders:C,customAiEndpoint:E,deployTarget:g,metering:h,payment:S,billingMode:u,idp:_,mcp:H,waveToggles:F,previewSelections:he}=t,{email:le,storage:ee,monitoring:B,analytics:K,sms:I,queue:W,search:de,cache:P,notifications:Ee,webhooks:We,cms:Xe,featureFlags:ve,captcha:v}=t,T=C.length,A=[`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 ${s}`,`auth \u2192 ${t.auth==="none"?"skip (remove packages/auth)":`configure ${t.auth}`}`,...d.length>0?[`social-login \u2192 generate ${d.length} callback route${d.length===1?"":"s"} + SocialLoginButtons.tsx (${d.join(", ")})`]:[],`db \u2192 ${a==="none"?"skip (remove packages/db)":`configure Prisma for ${a}`}`,...a!=="none"?[`db-host \u2192 configure ${t.databaseHost}`]:[],`payment \u2192 ${c==="none"?"skip (remove packages/billing)":`configure ${c}`}`,...p!=="none"?[`docs \u2192 scaffold apps/docs (${p==="fumadocs"?"fumadocs":`${p} \u2192 fumadocs fallback`})`]:[],...T>0||E?[`ai-providers \u2192 generate ${m} registry seed + env (${T}${E?" + custom":""} provider${T===1&&!E?"":"s"})`]:[],...le!=="none"?[`email \u2192 configure ${le}`]:[],...ee!=="none"?[`storage \u2192 configure ${ee}`]:[],...B!=="none"?[`monitoring \u2192 configure ${B}`]:[],...K!=="none"?[`analytics \u2192 configure ${K}`]:[],...I!=="none"?[`sms \u2192 configure ${I}`]:[],...W!=="none"?[`queue \u2192 configure ${W}`]:[],...de!=="none"?[`search \u2192 configure ${de}`]:[],...P!=="none"?[`cache \u2192 configure ${P}`]:[],...Ee!=="none"?[`notifications \u2192 configure ${Ee}`]:[],...We!=="none"?[`webhooks \u2192 configure ${We}`]:[],...Xe!=="none"?[`cms \u2192 configure ${Xe}`]:[],...ve!=="none"?[`feature-flags \u2192 configure ${ve}`]:[],...v!=="none"?[`captcha \u2192 configure ${v}`]:[],`mcp \u2192 ${H==="on"?"enable MCP server":"remove packages/mcp"}`,`metering \u2192 ${h==="off"?"disabled":h==="auto"?S!=="none"?"enabled (auto: payment set)":"disabled (auto: no payment)":"enabled"}`,...u!=="usage"?[`billing-mode \u2192 ${u}`]:[],..._!=="clerk"?[`idp \u2192 ${_}`]:[],`cron-jobs \u2192 ${F.cronJobs?"enabled":"disabled"}`,`audit-log \u2192 ${F.auditLog?"enabled":"disabled"}`,`api-keys \u2192 ${F.apiKeys?"enabled":"disabled"}`,`command-palette \u2192 ${F.commandPalette?"enabled":"disabled"}`,`cookie-consent \u2192 ${F.cookieConsent?"enabled":"disabled"}`,`legal-pages \u2192 ${F.legalPages?"enabled":"disabled"}`,`china-compliance \u2192 ${F.chinaCompliance?"enabled (@nebutra/china-compliance + ICP footer)":"disabled"}`,`compliance \u2192 inject ${s} 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)",...g!=="none"?[`deploy \u2192 inject ${g} config`]:[],"inject .env.local",r.install===!1?"skip install":`run ${o} install`,r.git===!1?"skip git init":"run git init"];if(e){for(let x of A)Re(!0,{event:"plan",action:x});for(let x of he)Re(!0,{event:"warn",step:x.flag,provider:x.provider,packageStatus:x.status});Re(!0,{event:"done",dryRun:!0})}else{process.stdout.write(`
|
|
2899
|
+
`+D.bold(`Dry run \u2014 planned actions:
|
|
2900
|
+
`));for(let x of A)process.stdout.write(` \u2022 ${x}
|
|
2901
|
+
`);if(he.length>0){process.stdout.write(`
|
|
2902
|
+
`+D.bold(D.yellow(`Preview-status providers selected:
|
|
2903
|
+
`)));for(let x of he)process.stdout.write(D.yellow(`\u26A0 ${x.flag}=${x.provider}
|
|
2904
|
+
`))}process.stdout.write(D.dim(`
|
|
2904
2905
|
No files were written.
|
|
2905
|
-
`))}}async function
|
|
2906
|
-
${
|
|
2906
|
+
`))}}async function cp(){let e=Dn();e.parse(process.argv);let t=e.opts(),[n]=e.args;t.help&&(qr(),process.exit(0));let o=!!t.json,r=!!t.dryRun,i=!!t.yes||!process.stdin.isTTY;if(o||(Jr(),Yr()),Re(o,{event:"start",version:z}),!t.pm)try{ip("pnpm --version",{stdio:"ignore"})}catch{o||process.stderr.write(`
|
|
2907
|
+
${D.red("\u2718")} ${D.bold("pnpm is required")} but was not found on PATH.
|
|
2907
2908
|
|
|
2908
2909
|
The scaffold uses pnpm workspaces + Turborepo. Install pnpm first:
|
|
2909
|
-
${
|
|
2910
|
+
${D.cyan("npm i -g pnpm@10")}
|
|
2910
2911
|
|
|
2911
|
-
Then retry: ${
|
|
2912
|
-
(or pass ${
|
|
2912
|
+
Then retry: ${D.cyan("pnpm dlx create-sailor@latest")}
|
|
2913
|
+
(or pass ${D.dim("--pm=npm")} if you know workspace:* won't resolve in your setup)
|
|
2913
2914
|
|
|
2914
|
-
`),
|
|
2915
|
-
`)),process.exit(130)}
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
`)
|
|
2915
|
+
`),Re(o,{event:"error",code:"PNPM_MISSING"}),process.exit(1)}let a=()=>{process.stdout.write(D.red(`\u2718 Cancelled
|
|
2916
|
+
`)),process.exit(130)},c,p;if(n)try{let g=it(n);c=g.targetDir,p=g.projectName}catch(g){let h=g instanceof Error?g.message:String(g);process.stderr.write(`
|
|
2917
|
+
${D.red("\u2718")} ${h}
|
|
2918
|
+
|
|
2919
|
+
`),Re(o,{event:"error",code:"INVALID_PROJECT_NAME",message:h}),process.exit(1)}else if(i){let g=it(Se);c=g.targetDir,p=g.projectName}else{let g=await es({onCancel:a});c=g.targetDir,p=g.projectName,o||process.stdout.write(D.dim(` \u2192 ${g.absoluteDir}
|
|
2920
|
+
`))}let d=t.pm??$n(),m=await to(t,o),C=[["Project name",p],["Region",m.region],["Auth",m.auth],["Social login",m.socialLoginIds.length>0?m.socialLoginIds.map(g=>Je.find(h=>h.id===g)?.name??g).join(", "):"none"],["ORM",m.orm],["Database",m.database],["Database Host",m.databaseHost],["Payment",m.paymentChoice],["AI topology",`${m.aiMode}${m.aiProviders.length>0?` (${m.aiProviders.join(", ")} seed)`:""}${m.customAiEndpoint?" + custom endpoint":""}`],["Email",m.email],["Storage",m.storage],["Monitoring",m.monitoring],["Analytics",m.analytics],["SMS",m.sms],["Deploy Target",m.deployTarget],["Docs Framework",m.docs],["Access gate",m.accessGate]];o?C.forEach(([g,h],S)=>{Re(!0,{event:"step",step:g,value:h,index:S+1,total:C.length})}):C.forEach(([g,h],S)=>{Hr({index:S+1,total:C.length,label:g,value:h})}),r&&(ap(o,m,c,d,t),process.exit(0));let E=async()=>{process.stdout.write(`
|
|
2921
|
+
`+D.red(`\u2718 Cancelled
|
|
2922
|
+
`)),ts.existsSync(c)&&await ns.confirm({message:`Cleanup partial install at ${c}?`,initialValue:!0})===!0&&(ts.rmSync(c,{recursive:!0,force:!0}),process.stdout.write(D.dim(` \u2713 Removed ${c}
|
|
2923
|
+
`))),process.exit(130)};process.on("SIGINT",E);try{await Kr({resolvedTarget:c,projectName:p,resolvedPm:d,opts:t,useJson:o,resolved:m,startedAt:Date.now()}),process.exit(0)}catch(g){let h=g instanceof Error?g.message:String(g);o?Re(!0,{event:"error",message:h}):process.stdout.write(D.red(`
|
|
2919
2924
|
\u2718 Failed: ${h}
|
|
2920
|
-
`)),process.exit(1)}}
|
|
2925
|
+
`)),process.exit(1)}}cp().catch(e=>{process.stderr.write(String(e)+`
|
|
2921
2926
|
`),process.exit(1)});
|