create-sailor 1.4.4 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +173 -173
- package/package.json +13 -14
- package/templates/backends/gateway/README.md +13 -0
- package/templates/backends/gateway/package.json +22 -0
- package/templates/backends/gateway/src/index.ts +16 -0
- package/templates/backends/gateway/tsconfig.json +13 -0
- package/templates/backends/python/README.md +16 -0
- package/templates/backends/python/_shared/__init__.py +5 -0
- package/templates/backends/python/pyproject.toml +13 -0
- package/templates/e2e/README.md +11 -0
- package/templates/e2e/golden/signup.spec.ts +6 -0
- package/templates/e2e/playwright.config.ts +14 -0
- package/templates/e2e/sleptons/community.spec.ts +6 -0
- package/templates/e2e/smoke/health.spec.ts +6 -0
- package/templates/infra/README.md +10 -0
- package/templates/infra/data/README.md +3 -0
- package/templates/infra/iac/README.md +3 -0
- package/templates/infra/ops/README.md +3 -0
- package/templates/infra/runtime/README.md +3 -0
- package/templates/orm/drizzle/README.md +58 -0
- package/templates/orm/drizzle/drizzle.config.ts +16 -0
- package/templates/orm/drizzle/migrations/.gitkeep +0 -0
- package/templates/orm/drizzle/package.json +29 -0
- package/templates/orm/drizzle/src/index.ts +31 -0
- package/templates/orm/drizzle/src/schema/auth.ts +73 -0
- package/templates/orm/drizzle/src/schema/billing.ts +37 -0
- package/templates/orm/drizzle/src/schema/index.ts +3 -0
- package/templates/orm/drizzle/src/schema/tenant.ts +57 -0
- package/templates/orm/drizzle/tsconfig.json +16 -0
- package/templates/tests/README.md +8 -0
- package/templates/tests/architecture/no-cycles.test.ts +7 -0
- package/templates/tests/load/smoke.k6.js +13 -0
- package/templates/workflows/README.md +9 -0
- package/templates/workflows/inngest/README.md +3 -0
- package/templates/workflows/inngest/welcome-email.ts +14 -0
- package/templates/workflows/n8n/README.md +3 -0
- package/templates/workflows/n8n/example-flow.json +6 -0
- package/templates/workflows/pusher/README.md +3 -0
- package/templates/workflows/pusher/events.ts +6 -0
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{execSync as
|
|
3
|
-
`));return}
|
|
2
|
+
import{execSync as Pt}from"child_process";import Se from"fs";import Ct from"path";import*as x from"@clack/prompts";import{Command as Ta}from"commander";import C from"picocolors";import Na from"update-notifier";import Pr from"cfonts";import Cr from"picocolors";import fn from"fs";import Wt from"path";import{fileURLToPath as _r}from"url";function br(){try{let t=Wt.dirname(_r(import.meta.url));for(let o=0;o<4;o++){let n=Wt.join(t,"package.json");if(fn.existsSync(n)){let r=fn.readFileSync(n,"utf-8"),s=JSON.parse(r);if(s.name==="create-sailor"&&typeof s.version=="string")return s.version}t=Wt.dirname(t)}return"0.0.0-dev"}catch{return"0.0.0-dev"}}var ce=br();function yn(){if(process.env.NO_COLOR||!process.stdout.isTTY){process.stdout.write([`Sailor v${ce}`,"AI-Native SaaS Unicorn Template","by Nebutra \xB7 https://nebutra.com","",""].join(`
|
|
3
|
+
`));return}Pr.say("Sailor",{font:"block",align:"left",colors:["#0033FE","#0BF1C3"],gradient:["#0033FE","#0BF1C3"],transitionGradient:!0,space:!1}),process.stdout.write(Cr.dim(` AI-Native SaaS Template \xB7 v${ce} \xB7 nebutra.com
|
|
4
4
|
|
|
5
|
-
`))}import P from"picocolors";var
|
|
5
|
+
`))}import P from"picocolors";var Ar={qstash:"foundation",bullmq:"foundation",upstash:"foundation",sqs:"foundation",meilisearch:"foundation",typesense:"foundation",algolia:"foundation",pgvector:"foundation",novu:"foundation",knock:"foundation",svix:"foundation","vercel-flags":"wip",growthbook:"wip",configcat:"wip",turnstile:"wip",hcaptcha:"wip","aliyun-slide":"wip"};function wr(e){return e?Ar[e]??"stable":"stable"}function tt(e){switch(e){case"stable":return"";case"foundation":return"[Foundation]";case"wip":return"[WIP]";case"deprecated":return"[Deprecated]"}}function pe(e){switch(e){case"stable":return"Production-ready.";case"foundation":return"Foundation status \u2014 types + factory are complete, but provider adapters are stub-level and require external credentials.";case"wip":return"WIP \u2014 actively being built, should not be used in production.";case"deprecated":return"Deprecated \u2014 scheduled for removal, do not use."}}function Sn(e){let t=[];for(let{flag:o,provider:n}of e){if(!n||n==="none")continue;let r=wr(n);r!=="stable"&&t.push({flag:o,provider:n,status:r})}return t}function Ir(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function hn(e){let t=Ir(),o=t?"\u2693":"-",n=t?"\u25B8":"->",r=t?"\u2605":"*",s=t?"\u{1F4E7}":"*",c=["",` ${t?P.bold(`${o} Done in ${e.elapsedSec}s \xB7 ${e.targetDir}/`):`${o} Done in ${e.elapsedSec}s \xB7 ${e.targetDir}/`}`,"",` ${t?P.bold("Next:"):"Next:"}`,` ${n} cd ${e.targetDir}`];e.skippedInstall&&c.push(` ${n} pnpm install (if --no-install was set)`),c.push(` ${n} fill in .env.local ${t?P.dim("\u2192 add provider keys before booting apps"):"-> add provider keys before booting apps"}`,` ${n} pnpm db:migrate ${t?P.dim("\u2192 sync Prisma schema"):"-> sync Prisma schema"}`,` ${n} pnpm db:seed ${t?P.dim("\u2192 load example tenants and records"):"-> load example tenants and records"}`,` ${n} pnpm dev ${t?P.dim("\u2192 http://localhost:3000"):"-> http://localhost:3000"}`,"",` ${t?P.bold("Customize:"):"Customize:"}`,` ${n} pnpm brand:init ${t?P.dim("\u2192 create brand.config.ts"):"-> create brand.config.ts"}`,` ${n} pnpm brand:apply ${t?P.dim("\u2192 propagate brand changes"):"-> propagate brand changes"}`,` ${n} pnpm preset:env ${t?P.dim("\u2192 review preset env requirements"):"-> review preset env requirements"}`,"",` ${t?P.bold("Useful scripts:"):"Useful scripts:"}`,` ${n} pnpm infra:up ${t?P.dim("\u2192 start local PostgreSQL/Redis/ClickHouse"):"-> start local PostgreSQL/Redis/ClickHouse"}`,` ${n} pnpm generate:api-types ${t?P.dim("\u2192 refresh shared API client types"):"-> refresh shared API client types"}`,` ${n} pnpm lint ${t?P.dim("\u2192 quick repo sanity check"):"-> quick repo sanity check"}`,` ${n} pnpm audit ${t?P.dim("\u2192 scan deps for known CVEs"):"-> scan deps for known CVEs"}`);let a=e.previewSelections??[];if(a.length>0){let T=t?P.bold(P.yellow("\u26A0 Preview features selected:")):"!! Preview features selected:";c.push("",` ${T}`);for(let N of a){let D=tt(N.status),ae=` ${n} ${N.flag} (${N.provider}) ${D}`;c.push(t?P.yellow(ae):ae)}c.push("",` ${t?P.dim(pe("foundation")):pe("foundation")}`,` ${t?P.dim("You may need to contribute the provider integration yourself."):"You may need to contribute the provider integration yourself."}`,` ${t?P.dim("See: https://github.com/Nebutra/Nebutra-Sailor/blob/main/docs/package-status.md"):"See: https://github.com/Nebutra/Nebutra-Sailor/blob/main/docs/package-status.md"}`)}let l=e.waveFeatures??{},m=T=>T!==!1,S=[];m(l.apiKeys)&&S.push("Manage API keys at /settings/api-keys"),m(l.auditLog)&&S.push("View audit log at /settings/audit-log"),S.push("Configure webhooks at /settings/webhooks"),m(l.commandPalette)&&S.push("Press Cmd/Ctrl+K to open the command palette"),S.push("Edit notification preferences at /settings/notifications"),S.push("Export your data at /settings/account/export"),l.chinaCompliance&&S.push("China deployments \u2014 see packages/ops/china-compliance/README.md"),c.push("",` ${t?P.bold("What you can do next:"):"What you can do next:"}`);for(let T of S)c.push(` ${n} ${T}`);c.push("",` ${t?P.bold("Ship faster:"):"Ship faster:"}`,` ${r} Star us https://github.com/nebutra/nebutra-sailor`,` ${s} Free license https://nebutra.com/get-license`,""),process.stdout.write(c.join(`
|
|
6
6
|
`)+`
|
|
7
|
-
`)}var
|
|
7
|
+
`)}var Rr=`Usage: create-sailor [name] [options]
|
|
8
8
|
|
|
9
9
|
Arguments:
|
|
10
10
|
name project directory (default: ./my-saas-app)
|
|
@@ -73,9 +73,9 @@ Examples:
|
|
|
73
73
|
$ npx create-sailor@latest my-app --region=cn --auth=clerk
|
|
74
74
|
$ npx create-sailor@latest my-app --region=hybrid --ai=openai,deepseek
|
|
75
75
|
$ npm create sailor@latest --dry-run --region=cn
|
|
76
|
-
`;function
|
|
77
|
-
`)}var qt=[{id:"anthropic",name:"Anthropic",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.anthropic.com",envVarPrefix:"ANTHROPIC",requiredEnvVars:["ANTHROPIC_API_KEY"]},{id:"openai",name:"OpenAI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.openai.com/docs",envVarPrefix:"OPENAI",requiredEnvVars:["OPENAI_API_KEY"]},{id:"deepseek",name:"DeepSeek",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.deepseek.com/api-docs",envVarPrefix:"DEEPSEEK",requiredEnvVars:["DEEPSEEK_API_KEY"]},{id:"xai",name:"xAI (Grok)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.x.ai/api",envVarPrefix:"XAI",requiredEnvVars:["XAI_API_KEY"]},{id:"moonshot",name:"Moonshot (Kimi)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.moonshot.cn/docs",envVarPrefix:"MOONSHOT",requiredEnvVars:["MOONSHOT_API_KEY","MOONSHOT_BASE_URL"]},{id:"google",name:"Google Gemini",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://ai.google.dev/gemini-api/docs",envVarPrefix:"GOOGLE_GENERATIVE_AI",requiredEnvVars:["GOOGLE_GENERATIVE_AI_API_KEY"]},{id:"mistral",name:"Mistral AI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.mistral.ai",envVarPrefix:"MISTRAL",requiredEnvVars:["MISTRAL_API_KEY"]},{id:"cohere",name:"Cohere",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.cohere.com",envVarPrefix:"COHERE",requiredEnvVars:["COHERE_API_KEY"]},{id:"perplexity",name:"Perplexity",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.perplexity.ai",baseURL:"https://api.perplexity.ai",envVarPrefix:"PERPLEXITY",requiredEnvVars:["PERPLEXITY_API_KEY"]},{id:"ai21",name:"AI21 Labs",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.ai21.com",baseURL:"https://api.ai21.com/studio/v1",envVarPrefix:"AI21",requiredEnvVars:["AI21_API_KEY"]},{id:"upstage",name:"Upstage (Solar)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://developers.upstage.ai/docs",baseURL:"https://api.upstage.ai/v1/solar",envVarPrefix:"UPSTAGE",requiredEnvVars:["UPSTAGE_API_KEY"]},{id:"siliconflow",name:"\u7845\u57FA\u6D41\u52A8 SiliconFlow",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://docs.siliconflow.cn",baseURL:"https://api.siliconflow.cn/v1",envVarPrefix:"SILICONFLOW",requiredEnvVars:["SILICONFLOW_API_KEY"]},{id:"volcengine-ark",name:"\u706B\u5C71\u5F15\u64CE ARK (\u8C46\u5305)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://www.volcengine.com/docs/82379",baseURL:"https://ark.cn-beijing.volces.com/api/v3",envVarPrefix:"ARK",requiredEnvVars:["ARK_API_KEY"]},{id:"bailian",name:"\u767E\u70BC Bailian (\u901A\u4E49)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://help.aliyun.com/zh/model-studio",baseURL:"https://dashscope.aliyuncs.com/compatible-mode/v1",envVarPrefix:"DASHSCOPE",requiredEnvVars:["DASHSCOPE_API_KEY"]},{id:"zhipu",name:"\u667A\u8C31 AI Zhipu",category:"\u56FD\u5185\u5E73\u53F0",status:"ai-sdk",docs:"https://docs.bigmodel.cn",baseURL:"https://open.bigmodel.cn/api/paas/v4",envVarPrefix:"ZHIPU",requiredEnvVars:["ZHIPU_API_KEY"]},{id:"baichuan",name:"\u767E\u5DDD\u667A\u80FD Baichuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.baichuan-ai.com/docs",baseURL:"https://api.baichuan-ai.com/v1",envVarPrefix:"BAICHUAN",requiredEnvVars:["BAICHUAN_API_KEY"]},{id:"minimax",name:"MiniMax (ABAB)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.minimaxi.com/document/guides",baseURL:"https://api.minimax.chat/v1",envVarPrefix:"MINIMAX",requiredEnvVars:["MINIMAX_API_KEY","MINIMAX_GROUP_ID"]},{id:"stepfun",name:"\u9636\u8DC3\u661F\u8FB0 StepFun",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.stepfun.com/docs",baseURL:"https://api.stepfun.com/v1",envVarPrefix:"STEPFUN",requiredEnvVars:["STEPFUN_API_KEY"]},{id:"sensetime",name:"\u5546\u6C64 SenseNova",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.sensetime.com",baseURL:"https://api.sensenova.cn/v1",envVarPrefix:"SENSENOVA",requiredEnvVars:["SENSENOVA_API_KEY"]},{id:"tencent",name:"\u817E\u8BAF\u6DF7\u5143 Hunyuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://cloud.tencent.com/document/product/1729",baseURL:"https://api.hunyuan.cloud.tencent.com/v1",envVarPrefix:"HUNYUAN",requiredEnvVars:["HUNYUAN_API_KEY"]},{id:"lingyi",name:"\u96F6\u4E00\u4E07\u7269 Yi",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.lingyiwanwu.com/docs",baseURL:"https://api.lingyiwanwu.com/v1",envVarPrefix:"YI",requiredEnvVars:["YI_API_KEY"]},{id:"openrouter",name:"OpenRouter",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://openrouter.ai/docs",envVarPrefix:"OPENROUTER",requiredEnvVars:["OPENROUTER_API_KEY"]},{id:"vercel-gateway",name:"Vercel AI Gateway",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://vercel.com/docs/ai-gateway",envVarPrefix:"AI_GATEWAY",requiredEnvVars:["AI_GATEWAY_API_KEY"]},{id:"litellm",name:"LiteLLM Proxy",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://docs.litellm.ai",baseURL:"http://localhost:4000/v1",envVarPrefix:"LITELLM",requiredEnvVars:["LITELLM_API_KEY"]},{id:"portkey",name:"Portkey",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://portkey.ai/docs",baseURL:"https://api.portkey.ai/v1",envVarPrefix:"PORTKEY",requiredEnvVars:["PORTKEY_API_KEY"]},{id:"aws-bedrock",name:"AWS Bedrock",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://aws.amazon.com/bedrock/",envVarPrefix:"AWS_BEDROCK",requiredEnvVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"]},{id:"azure-openai",name:"Azure OpenAI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/",envVarPrefix:"AZURE_OPENAI",requiredEnvVars:["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"]},{id:"gcp-vertex",name:"GCP Vertex AI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://cloud.google.com/vertex-ai",envVarPrefix:"GOOGLE_VERTEX",requiredEnvVars:["GOOGLE_APPLICATION_CREDENTIALS"]},{id:"groq",name:"Groq",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://console.groq.com/docs",envVarPrefix:"GROQ",requiredEnvVars:["GROQ_API_KEY"]},{id:"fireworks",name:"Fireworks AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.fireworks.ai",envVarPrefix:"FIREWORKS",requiredEnvVars:["FIREWORKS_API_KEY"]},{id:"together",name:"Together AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.together.ai",envVarPrefix:"TOGETHER",requiredEnvVars:["TOGETHER_API_KEY"]},{id:"huggingface",name:"Hugging Face",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://huggingface.co/docs/inference-providers",envVarPrefix:"HF",requiredEnvVars:["HF_API_KEY"]},{id:"replicate",name:"Replicate",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://replicate.com/docs",envVarPrefix:"REPLICATE",requiredEnvVars:["REPLICATE_API_TOKEN"]},{id:"lepton",name:"Lepton AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://www.lepton.ai/docs",baseURL:"https://api.lepton.ai/v1",envVarPrefix:"LEPTON",requiredEnvVars:["LEPTON_API_KEY"]},{id:"anyscale",name:"Anyscale Endpoints",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://docs.endpoints.anyscale.com/",baseURL:"https://api.endpoints.anyscale.com/v1",envVarPrefix:"ANYSCALE",requiredEnvVars:["ANYSCALE_API_KEY"]},{id:"octoai",name:"OctoAI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://octoai.cloud/docs",baseURL:"https://text.octoai.run/v1",envVarPrefix:"OCTOAI",requiredEnvVars:["OCTOAI_TOKEN"]},{id:"deepinfra",name:"DeepInfra",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://deepinfra.com/docs",baseURL:"https://api.deepinfra.com/v1/openai",envVarPrefix:"DEEPINFRA",requiredEnvVars:["DEEPINFRA_API_KEY"]},{id:"novita",name:"Novita AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://novita.ai/docs",baseURL:"https://api.novita.ai/v3/openai",envVarPrefix:"NOVITA",requiredEnvVars:["NOVITA_API_KEY"]},{id:"black-forest-labs",name:"Black Forest Labs (FLUX)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://docs.bfl.ml",envVarPrefix:"BFL",requiredEnvVars:["BFL_API_KEY"]},{id:"elevenlabs",name:"ElevenLabs (TTS)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://elevenlabs.io/docs",envVarPrefix:"ELEVENLABS",requiredEnvVars:["ELEVENLABS_API_KEY"]},{id:"runway",name:"Runway (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://runwayml.com",envVarPrefix:"RUNWAY",requiredEnvVars:["RUNWAY_API_KEY"]},{id:"luma",name:"Luma (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://lumalabs.ai",envVarPrefix:"LUMA",requiredEnvVars:["LUMA_API_KEY"]},{id:"midjourney",name:"Midjourney",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://docs.midjourney.com",envVarPrefix:"MIDJOURNEY",requiredEnvVars:[]},{id:"ollama",name:"Ollama (\u672C\u5730)",category:"\u672C\u5730\u90E8\u7F72",status:"opencode",docs:"https://github.com/ollama/ollama",baseURL:"http://localhost:11434/v1",envVarPrefix:"OLLAMA",requiredEnvVars:["OLLAMA_BASE_URL"]},{id:"lmstudio",name:"LM Studio",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://lmstudio.ai/docs",baseURL:"http://localhost:1234/v1",envVarPrefix:"LMSTUDIO",requiredEnvVars:[]},{id:"vllm",name:"vLLM",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://docs.vllm.ai",baseURL:"http://localhost:8000/v1",envVarPrefix:"VLLM",requiredEnvVars:[]},{id:"tgi",name:"HF TGI",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://huggingface.co/docs/text-generation-inference",baseURL:"http://localhost:8080/v1",envVarPrefix:"TGI",requiredEnvVars:[]}],Ua=qt.reduce((e,t)=>(e[t.category]||(e[t.category]=[]),e[t.category].push(t),e),{});var Er=["openai","anthropic","google"],vr=["openai"];function Gt(e){let t=e.providerIds??[];switch(e.mode){case"gateway":{let o=t.length>0?t:[...Er];return{mode:"gateway",providerIds:o,customEndpoint:e.customEndpoint,routing:{profile:"multi-provider-gateway",providerSeed:o,runtimeGovernance:!0}}}case"direct":{let o=t.length>0?t:[...vr];return{mode:"direct",providerIds:o,customEndpoint:e.customEndpoint,routing:{profile:"direct-adapters",providerSeed:o,runtimeGovernance:!1}}}case"custom":return{mode:"custom",providerIds:t,customEndpoint:e.customEndpoint,routing:{profile:"openai-compatible",providerSeed:t,runtimeGovernance:!0}};case"none":return{mode:"none",providerIds:[],routing:{profile:"disabled",providerSeed:[],runtimeGovernance:!1}}}}import Qe from"fs";import j from"path";var hn=[{id:"posthog",name:"PostHog",region:"both",envVars:["POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_HOST"],docs:"https://posthog.com/docs",pkgs:["posthog-js","posthog-node"],providerFile:"apps/web/src/lib/analytics/posthog.client.ts"},{id:"plausible",name:"Plausible",region:"global",envVars:["NEXT_PUBLIC_PLAUSIBLE_DOMAIN","NEXT_PUBLIC_PLAUSIBLE_SRC"],docs:"https://plausible.io/docs",providerFile:"apps/web/src/components/analytics/PlausibleScript.tsx"},{id:"umami",name:"Umami",region:"both",envVars:["NEXT_PUBLIC_UMAMI_WEBSITE_ID","NEXT_PUBLIC_UMAMI_SRC"],docs:"https://umami.is/docs",providerFile:"apps/web/src/components/analytics/UmamiScript.tsx"},{id:"mixpanel",name:"Mixpanel",region:"global",envVars:["MIXPANEL_TOKEN","NEXT_PUBLIC_MIXPANEL_TOKEN"],docs:"https://docs.mixpanel.com",pkgs:["mixpanel-browser","mixpanel"],providerFile:"apps/web/src/lib/analytics/mixpanel.client.ts"},{id:"baidu",name:"\u767E\u5EA6\u7EDF\u8BA1 Baidu Tongji",region:"cn",envVars:["NEXT_PUBLIC_BAIDU_STATS_ID"],docs:"https://tongji.baidu.com",providerFile:"apps/web/src/components/analytics/BaiduStatsScript.tsx"},{id:"sensors",name:"\u795E\u7B56 Sensors Analytics",region:"cn",envVars:["NEXT_PUBLIC_SENSORS_SERVER_URL","NEXT_PUBLIC_SENSORS_APP_JS_URL"],docs:"https://manual.sensorsdata.cn",pkgs:["sa-sdk-javascript"],providerFile:"apps/web/src/lib/analytics/sensors.client.ts"},{id:"growingio",name:"GrowingIO",region:"cn",envVars:["NEXT_PUBLIC_GROWINGIO_ACCOUNT_ID"],docs:"https://growingio.com/docs",providerFile:"apps/web/src/components/analytics/GrowingIoScript.tsx"},{id:"none",name:"None",region:"both",envVars:[],docs:""}],Da=hn.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function En(e){return hn.find(t=>t.id===e)}function _r(e){Qe.mkdirSync(e,{recursive:!0})}function z(e,t){_r(j.dirname(e)),Qe.writeFileSync(e,t)}function he(e,t){let o=j.join(e,".env.example");Qe.existsSync(o)?Qe.appendFileSync(o,`
|
|
78
|
-
`+t):
|
|
76
|
+
`;function En(){process.stdout.write(Rr)}import xt from"picocolors";function vn(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function kr(e){let t=vn()?"\u25B8":"->",o=`[${e.index}/${e.total}]`,n=e.label.padEnd(18," ");return vn()?`${xt.dim(o)} ${xt.bold(n)} ${xt.dim(t)} ${xt.cyan(e.value)}`:`${o} ${n} ${t} ${e.value}`}function _n(e){process.stdout.write(kr(e)+`
|
|
77
|
+
`)}var Xt=[{id:"anthropic",name:"Anthropic",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.anthropic.com",envVarPrefix:"ANTHROPIC",requiredEnvVars:["ANTHROPIC_API_KEY"]},{id:"openai",name:"OpenAI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.openai.com/docs",envVarPrefix:"OPENAI",requiredEnvVars:["OPENAI_API_KEY"]},{id:"deepseek",name:"DeepSeek",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.deepseek.com/api-docs",envVarPrefix:"DEEPSEEK",requiredEnvVars:["DEEPSEEK_API_KEY"]},{id:"xai",name:"xAI (Grok)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.x.ai/api",envVarPrefix:"XAI",requiredEnvVars:["XAI_API_KEY"]},{id:"moonshot",name:"Moonshot (Kimi)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.moonshot.cn/docs",envVarPrefix:"MOONSHOT",requiredEnvVars:["MOONSHOT_API_KEY","MOONSHOT_BASE_URL"]},{id:"google",name:"Google Gemini",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://ai.google.dev/gemini-api/docs",envVarPrefix:"GOOGLE_GENERATIVE_AI",requiredEnvVars:["GOOGLE_GENERATIVE_AI_API_KEY"]},{id:"mistral",name:"Mistral AI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.mistral.ai",envVarPrefix:"MISTRAL",requiredEnvVars:["MISTRAL_API_KEY"]},{id:"cohere",name:"Cohere",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.cohere.com",envVarPrefix:"COHERE",requiredEnvVars:["COHERE_API_KEY"]},{id:"perplexity",name:"Perplexity",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.perplexity.ai",baseURL:"https://api.perplexity.ai",envVarPrefix:"PERPLEXITY",requiredEnvVars:["PERPLEXITY_API_KEY"]},{id:"ai21",name:"AI21 Labs",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.ai21.com",baseURL:"https://api.ai21.com/studio/v1",envVarPrefix:"AI21",requiredEnvVars:["AI21_API_KEY"]},{id:"upstage",name:"Upstage (Solar)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://developers.upstage.ai/docs",baseURL:"https://api.upstage.ai/v1/solar",envVarPrefix:"UPSTAGE",requiredEnvVars:["UPSTAGE_API_KEY"]},{id:"siliconflow",name:"\u7845\u57FA\u6D41\u52A8 SiliconFlow",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://docs.siliconflow.cn",baseURL:"https://api.siliconflow.cn/v1",envVarPrefix:"SILICONFLOW",requiredEnvVars:["SILICONFLOW_API_KEY"]},{id:"volcengine-ark",name:"\u706B\u5C71\u5F15\u64CE ARK (\u8C46\u5305)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://www.volcengine.com/docs/82379",baseURL:"https://ark.cn-beijing.volces.com/api/v3",envVarPrefix:"ARK",requiredEnvVars:["ARK_API_KEY"]},{id:"bailian",name:"\u767E\u70BC Bailian (\u901A\u4E49)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://help.aliyun.com/zh/model-studio",baseURL:"https://dashscope.aliyuncs.com/compatible-mode/v1",envVarPrefix:"DASHSCOPE",requiredEnvVars:["DASHSCOPE_API_KEY"]},{id:"zhipu",name:"\u667A\u8C31 AI Zhipu",category:"\u56FD\u5185\u5E73\u53F0",status:"ai-sdk",docs:"https://docs.bigmodel.cn",baseURL:"https://open.bigmodel.cn/api/paas/v4",envVarPrefix:"ZHIPU",requiredEnvVars:["ZHIPU_API_KEY"]},{id:"baichuan",name:"\u767E\u5DDD\u667A\u80FD Baichuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.baichuan-ai.com/docs",baseURL:"https://api.baichuan-ai.com/v1",envVarPrefix:"BAICHUAN",requiredEnvVars:["BAICHUAN_API_KEY"]},{id:"minimax",name:"MiniMax (ABAB)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.minimaxi.com/document/guides",baseURL:"https://api.minimax.chat/v1",envVarPrefix:"MINIMAX",requiredEnvVars:["MINIMAX_API_KEY","MINIMAX_GROUP_ID"]},{id:"stepfun",name:"\u9636\u8DC3\u661F\u8FB0 StepFun",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.stepfun.com/docs",baseURL:"https://api.stepfun.com/v1",envVarPrefix:"STEPFUN",requiredEnvVars:["STEPFUN_API_KEY"]},{id:"sensetime",name:"\u5546\u6C64 SenseNova",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.sensetime.com",baseURL:"https://api.sensenova.cn/v1",envVarPrefix:"SENSENOVA",requiredEnvVars:["SENSENOVA_API_KEY"]},{id:"tencent",name:"\u817E\u8BAF\u6DF7\u5143 Hunyuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://cloud.tencent.com/document/product/1729",baseURL:"https://api.hunyuan.cloud.tencent.com/v1",envVarPrefix:"HUNYUAN",requiredEnvVars:["HUNYUAN_API_KEY"]},{id:"lingyi",name:"\u96F6\u4E00\u4E07\u7269 Yi",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.lingyiwanwu.com/docs",baseURL:"https://api.lingyiwanwu.com/v1",envVarPrefix:"YI",requiredEnvVars:["YI_API_KEY"]},{id:"openrouter",name:"OpenRouter",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://openrouter.ai/docs",envVarPrefix:"OPENROUTER",requiredEnvVars:["OPENROUTER_API_KEY"]},{id:"vercel-gateway",name:"Vercel AI Gateway",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://vercel.com/docs/ai-gateway",envVarPrefix:"AI_GATEWAY",requiredEnvVars:["AI_GATEWAY_API_KEY"]},{id:"litellm",name:"LiteLLM Proxy",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://docs.litellm.ai",baseURL:"http://localhost:4000/v1",envVarPrefix:"LITELLM",requiredEnvVars:["LITELLM_API_KEY"]},{id:"portkey",name:"Portkey",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://portkey.ai/docs",baseURL:"https://api.portkey.ai/v1",envVarPrefix:"PORTKEY",requiredEnvVars:["PORTKEY_API_KEY"]},{id:"aws-bedrock",name:"AWS Bedrock",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://aws.amazon.com/bedrock/",envVarPrefix:"AWS_BEDROCK",requiredEnvVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"]},{id:"azure-openai",name:"Azure OpenAI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/",envVarPrefix:"AZURE_OPENAI",requiredEnvVars:["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"]},{id:"gcp-vertex",name:"GCP Vertex AI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://cloud.google.com/vertex-ai",envVarPrefix:"GOOGLE_VERTEX",requiredEnvVars:["GOOGLE_APPLICATION_CREDENTIALS"]},{id:"groq",name:"Groq",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://console.groq.com/docs",envVarPrefix:"GROQ",requiredEnvVars:["GROQ_API_KEY"]},{id:"fireworks",name:"Fireworks AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.fireworks.ai",envVarPrefix:"FIREWORKS",requiredEnvVars:["FIREWORKS_API_KEY"]},{id:"together",name:"Together AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.together.ai",envVarPrefix:"TOGETHER",requiredEnvVars:["TOGETHER_API_KEY"]},{id:"huggingface",name:"Hugging Face",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://huggingface.co/docs/inference-providers",envVarPrefix:"HF",requiredEnvVars:["HF_API_KEY"]},{id:"replicate",name:"Replicate",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://replicate.com/docs",envVarPrefix:"REPLICATE",requiredEnvVars:["REPLICATE_API_TOKEN"]},{id:"lepton",name:"Lepton AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://www.lepton.ai/docs",baseURL:"https://api.lepton.ai/v1",envVarPrefix:"LEPTON",requiredEnvVars:["LEPTON_API_KEY"]},{id:"anyscale",name:"Anyscale Endpoints",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://docs.endpoints.anyscale.com/",baseURL:"https://api.endpoints.anyscale.com/v1",envVarPrefix:"ANYSCALE",requiredEnvVars:["ANYSCALE_API_KEY"]},{id:"octoai",name:"OctoAI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://octoai.cloud/docs",baseURL:"https://text.octoai.run/v1",envVarPrefix:"OCTOAI",requiredEnvVars:["OCTOAI_TOKEN"]},{id:"deepinfra",name:"DeepInfra",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://deepinfra.com/docs",baseURL:"https://api.deepinfra.com/v1/openai",envVarPrefix:"DEEPINFRA",requiredEnvVars:["DEEPINFRA_API_KEY"]},{id:"novita",name:"Novita AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://novita.ai/docs",baseURL:"https://api.novita.ai/v3/openai",envVarPrefix:"NOVITA",requiredEnvVars:["NOVITA_API_KEY"]},{id:"black-forest-labs",name:"Black Forest Labs (FLUX)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://docs.bfl.ml",envVarPrefix:"BFL",requiredEnvVars:["BFL_API_KEY"]},{id:"elevenlabs",name:"ElevenLabs (TTS)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://elevenlabs.io/docs",envVarPrefix:"ELEVENLABS",requiredEnvVars:["ELEVENLABS_API_KEY"]},{id:"runway",name:"Runway (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://runwayml.com",envVarPrefix:"RUNWAY",requiredEnvVars:["RUNWAY_API_KEY"]},{id:"luma",name:"Luma (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://lumalabs.ai",envVarPrefix:"LUMA",requiredEnvVars:["LUMA_API_KEY"]},{id:"midjourney",name:"Midjourney",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://docs.midjourney.com",envVarPrefix:"MIDJOURNEY",requiredEnvVars:[]},{id:"ollama",name:"Ollama (\u672C\u5730)",category:"\u672C\u5730\u90E8\u7F72",status:"opencode",docs:"https://github.com/ollama/ollama",baseURL:"http://localhost:11434/v1",envVarPrefix:"OLLAMA",requiredEnvVars:["OLLAMA_BASE_URL"]},{id:"lmstudio",name:"LM Studio",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://lmstudio.ai/docs",baseURL:"http://localhost:1234/v1",envVarPrefix:"LMSTUDIO",requiredEnvVars:[]},{id:"vllm",name:"vLLM",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://docs.vllm.ai",baseURL:"http://localhost:8000/v1",envVarPrefix:"VLLM",requiredEnvVars:[]},{id:"tgi",name:"HF TGI",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://huggingface.co/docs/text-generation-inference",baseURL:"http://localhost:8080/v1",envVarPrefix:"TGI",requiredEnvVars:[]}],Za=Xt.reduce((e,t)=>(e[t.category]||(e[t.category]=[]),e[t.category].push(t),e),{});var xr=["openai","anthropic","google"],Tr=["openai"];function zt(e){let t=e.providerIds??[];switch(e.mode){case"gateway":{let o=t.length>0?t:[...xr];return{mode:"gateway",providerIds:o,customEndpoint:e.customEndpoint,routing:{profile:"multi-provider-gateway",providerSeed:o,runtimeGovernance:!0}}}case"direct":{let o=t.length>0?t:[...Tr];return{mode:"direct",providerIds:o,customEndpoint:e.customEndpoint,routing:{profile:"direct-adapters",providerSeed:o,runtimeGovernance:!1}}}case"custom":return{mode:"custom",providerIds:t,customEndpoint:e.customEndpoint,routing:{profile:"openai-compatible",providerSeed:t,runtimeGovernance:!0}};case"none":return{mode:"none",providerIds:[],routing:{profile:"disabled",providerSeed:[],runtimeGovernance:!1}}}}import nt from"fs";import V from"path";var bn=[{id:"posthog",name:"PostHog",region:"both",envVars:["POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_HOST"],docs:"https://posthog.com/docs",pkgs:["posthog-js","posthog-node"],providerFile:"apps/web/src/lib/analytics/posthog.client.ts"},{id:"plausible",name:"Plausible",region:"global",envVars:["NEXT_PUBLIC_PLAUSIBLE_DOMAIN","NEXT_PUBLIC_PLAUSIBLE_SRC"],docs:"https://plausible.io/docs",providerFile:"apps/web/src/components/analytics/PlausibleScript.tsx"},{id:"umami",name:"Umami",region:"both",envVars:["NEXT_PUBLIC_UMAMI_WEBSITE_ID","NEXT_PUBLIC_UMAMI_SRC"],docs:"https://umami.is/docs",providerFile:"apps/web/src/components/analytics/UmamiScript.tsx"},{id:"mixpanel",name:"Mixpanel",region:"global",envVars:["MIXPANEL_TOKEN","NEXT_PUBLIC_MIXPANEL_TOKEN"],docs:"https://docs.mixpanel.com",pkgs:["mixpanel-browser","mixpanel"],providerFile:"apps/web/src/lib/analytics/mixpanel.client.ts"},{id:"baidu",name:"\u767E\u5EA6\u7EDF\u8BA1 Baidu Tongji",region:"cn",envVars:["NEXT_PUBLIC_BAIDU_STATS_ID"],docs:"https://tongji.baidu.com",providerFile:"apps/web/src/components/analytics/BaiduStatsScript.tsx"},{id:"sensors",name:"\u795E\u7B56 Sensors Analytics",region:"cn",envVars:["NEXT_PUBLIC_SENSORS_SERVER_URL","NEXT_PUBLIC_SENSORS_APP_JS_URL"],docs:"https://manual.sensorsdata.cn",pkgs:["sa-sdk-javascript"],providerFile:"apps/web/src/lib/analytics/sensors.client.ts"},{id:"growingio",name:"GrowingIO",region:"cn",envVars:["NEXT_PUBLIC_GROWINGIO_ACCOUNT_ID"],docs:"https://growingio.com/docs",providerFile:"apps/web/src/components/analytics/GrowingIoScript.tsx"},{id:"none",name:"None",region:"both",envVars:[],docs:""}],nc=bn.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Pn(e){return bn.find(t=>t.id===e)}function Nr(e){nt.mkdirSync(e,{recursive:!0})}function z(e,t){Nr(V.dirname(e)),nt.writeFileSync(e,t)}function _e(e,t){let o=V.join(e,".env.example");nt.existsSync(o)?nt.appendFileSync(o,`
|
|
78
|
+
`+t):nt.writeFileSync(o,t)}function Or(e){let t=`"use client";
|
|
79
79
|
/**
|
|
80
80
|
* PostHog browser SDK \u2014 lazy-initialized on first access.
|
|
81
81
|
*/
|
|
@@ -158,11 +158,11 @@ export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
|
|
|
158
158
|
|
|
159
159
|
return <>{children}</>;
|
|
160
160
|
}
|
|
161
|
-
`;z(
|
|
161
|
+
`;z(V.join(e,"src","lib","analytics","posthog.client.ts"),t),z(V.join(e,"src","lib","analytics","posthog.server.ts"),o),z(V.join(e,"src","components","analytics","AnalyticsProvider.tsx"),n)}function Ur(){return`# PostHog \u2014 product analytics
|
|
162
162
|
POSTHOG_KEY=
|
|
163
163
|
NEXT_PUBLIC_POSTHOG_KEY=
|
|
164
164
|
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
|
|
165
|
-
`}function
|
|
165
|
+
`}function Lr(e){z(V.join(e,"src","components","analytics","PlausibleScript.tsx"),`"use client";
|
|
166
166
|
/**
|
|
167
167
|
* Plausible script tag \u2014 lightweight, privacy-friendly analytics.
|
|
168
168
|
*/
|
|
@@ -182,10 +182,10 @@ export function PlausibleScript() {
|
|
|
182
182
|
/>
|
|
183
183
|
);
|
|
184
184
|
}
|
|
185
|
-
`)}function
|
|
185
|
+
`)}function Mr(){return`# Plausible \u2014 privacy-friendly analytics
|
|
186
186
|
NEXT_PUBLIC_PLAUSIBLE_DOMAIN=
|
|
187
187
|
NEXT_PUBLIC_PLAUSIBLE_SRC=https://plausible.io/js/script.js
|
|
188
|
-
`}function
|
|
188
|
+
`}function Dr(e){z(V.join(e,"src","components","analytics","UmamiScript.tsx"),`"use client";
|
|
189
189
|
/**
|
|
190
190
|
* Umami analytics \u2014 self-hosted OR umami.is cloud.
|
|
191
191
|
*/
|
|
@@ -205,10 +205,10 @@ export function UmamiScript() {
|
|
|
205
205
|
/>
|
|
206
206
|
);
|
|
207
207
|
}
|
|
208
|
-
`)}function
|
|
208
|
+
`)}function Fr(){return`# Umami \u2014 self-hostable analytics
|
|
209
209
|
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
|
|
210
210
|
NEXT_PUBLIC_UMAMI_SRC=https://cloud.umami.is/script.js
|
|
211
|
-
`}function
|
|
211
|
+
`}function jr(e){let t=`"use client";
|
|
212
212
|
import mixpanel from "mixpanel-browser";
|
|
213
213
|
|
|
214
214
|
let initialized = false;
|
|
@@ -247,10 +247,10 @@ export function trackServer(
|
|
|
247
247
|
if (!c) return;
|
|
248
248
|
c.track(event, { distinct_id: distinctId, ...properties });
|
|
249
249
|
}
|
|
250
|
-
`;z(
|
|
250
|
+
`;z(V.join(e,"src","lib","analytics","mixpanel.client.ts"),t),z(V.join(e,"src","lib","analytics","mixpanel.server.ts"),o)}function Vr(){return`# Mixpanel \u2014 product analytics
|
|
251
251
|
MIXPANEL_TOKEN=
|
|
252
252
|
NEXT_PUBLIC_MIXPANEL_TOKEN=
|
|
253
|
-
`}function
|
|
253
|
+
`}function Br(e){z(V.join(e,"src","components","analytics","BaiduStatsScript.tsx"),`"use client";
|
|
254
254
|
/**
|
|
255
255
|
* \u767E\u5EA6\u7EDF\u8BA1 Baidu Tongji \u2014 hm.js \u57CB\u70B9\u811A\u672C.
|
|
256
256
|
*/
|
|
@@ -276,9 +276,9 @@ var _hmt = _hmt || [];
|
|
|
276
276
|
/>
|
|
277
277
|
);
|
|
278
278
|
}
|
|
279
|
-
`)}function
|
|
279
|
+
`)}function Kr(){return`# \u767E\u5EA6\u7EDF\u8BA1 \u2014 \u4E2D\u56FD\u533A\u7F51\u7AD9\u5206\u6790
|
|
280
280
|
NEXT_PUBLIC_BAIDU_STATS_ID=
|
|
281
|
-
`}function
|
|
281
|
+
`}function $r(e){z(V.join(e,"src","lib","analytics","sensors.client.ts"),`"use client";
|
|
282
282
|
/**
|
|
283
283
|
* \u795E\u7B56\u5206\u6790 (Sensors Analytics) \u2014 sa-sdk-javascript \u5C01\u88C5.
|
|
284
284
|
*/
|
|
@@ -306,10 +306,10 @@ export function initSensors(): void {
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
export { sensors };
|
|
309
|
-
`)}function
|
|
309
|
+
`)}function Yr(){return`# \u795E\u7B56\u5206\u6790 Sensors Analytics
|
|
310
310
|
NEXT_PUBLIC_SENSORS_SERVER_URL=
|
|
311
311
|
NEXT_PUBLIC_SENSORS_APP_JS_URL=
|
|
312
|
-
`}function
|
|
312
|
+
`}function qr(e){z(V.join(e,"src","components","analytics","GrowingIoScript.tsx"),`"use client";
|
|
313
313
|
/**
|
|
314
314
|
* GrowingIO CDP \u2014 inline snippet installer.
|
|
315
315
|
*/
|
|
@@ -334,21 +334,21 @@ gdp("send");
|
|
|
334
334
|
/>
|
|
335
335
|
);
|
|
336
336
|
}
|
|
337
|
-
`)}function
|
|
337
|
+
`)}function Gr(){return`# GrowingIO \u2014 \u5168\u57CB\u70B9 CDP (\u4E2D\u56FD\u533A)
|
|
338
338
|
NEXT_PUBLIC_GROWINGIO_ACCOUNT_ID=
|
|
339
|
-
`}async function
|
|
340
|
-
`+t):
|
|
341
|
-
`+t)}async function
|
|
339
|
+
`}async function Cn(e,t,o="global"){let n=Pn(t);if(!n||n.id==="none")return;let r=V.join(e,"apps","web");if(!nt.existsSync(r))return;let s=n.id;try{switch(s){case"posthog":Or(r),_e(e,Ur());return;case"plausible":Lr(r),_e(e,Mr());return;case"umami":Dr(r),_e(e,Fr());return;case"mixpanel":jr(r),_e(e,Vr());return;case"baidu":Br(r),_e(e,Kr());return;case"sensors":$r(r),_e(e,Yr());return;case"growingio":qr(r),_e(e,Gr());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 Hr="https://analytics.nebutra.com";function Wr(e={}){if(e.noTelemetry===!0)return!0;let t=process.env.NEBUTRA_TELEMETRY;return t==="0"||t==="false"}function An(e,t={}){Wr(t)||(async()=>{try{let o=await import("@nebutra/analytics");if(typeof o.createAnalyticsClient!="function")return;let n=o.createAnalyticsClient({posthog:{apiKey:process.env.NEBUTRA_POSTHOG_KEY??"",host:process.env.NEBUTRA_POSTHOG_HOST??Hr},onError:()=>{}});if(typeof n?.track!="function")return;let r=n.track("scaffold.completed",e);r&&typeof r.then=="function"&&await r.catch(()=>{})}catch{}})()}import K from"fs";import G from"path";function be(e){K.existsSync(e)&&K.rmSync(e,{recursive:!0,force:!0})}function Jt(e,t){let o=G.join(e,".env.example");K.existsSync(o)?K.appendFileSync(o,`
|
|
340
|
+
`+t):K.writeFileSync(o,t)}function Qt(e,t){if(!K.existsSync(e))return;let o=K.readFileSync(e,"utf8"),n=o.replace(/export type AuthProviderId\s*=\s*(?:"[^"]+"\s*\|\s*)+"[^"]+";/,`export type AuthProviderId = "${t}";`);n!==o&&K.writeFileSync(e,n)}function Xr(e,t){let o=G.join(e,"README.md");if(!K.existsSync(o)){K.writeFileSync(o,t);return}K.appendFileSync(o,`
|
|
341
|
+
`+t)}async function wn(e,t){let o=G.join(e,"packages","iam","auth");if(!K.existsSync(o))return;if(t==="none"){be(o),Xr(e,`
|
|
342
342
|
## Auth
|
|
343
343
|
|
|
344
344
|
Auth was skipped at scaffold time (\`--auth=none\`). Add your preferred auth provider manually (e.g. Clerk, Better Auth).
|
|
345
|
-
`);return}let n=
|
|
345
|
+
`);return}let n=G.join(o,"src","providers"),r=G.join(o,"src","types.ts");if(t==="clerk"){be(G.join(n,"better-auth.ts")),be(G.join(n,"nextauth.ts")),Qt(r,"clerk"),Jt(e,`# Clerk auth
|
|
346
346
|
CLERK_PUBLISHABLE_KEY=
|
|
347
347
|
CLERK_SECRET_KEY=
|
|
348
|
-
`);return}if(t==="betterauth"){
|
|
348
|
+
`);return}if(t==="betterauth"){be(G.join(n,"clerk.ts")),be(G.join(n,"nextauth.ts")),Qt(r,"better-auth"),Jt(e,`# Better Auth
|
|
349
349
|
BETTER_AUTH_SECRET=
|
|
350
350
|
BETTER_AUTH_URL=http://localhost:3000
|
|
351
|
-
`);return}if(t==="nextauth"){
|
|
351
|
+
`);return}if(t==="nextauth"){be(G.join(n,"clerk.ts")),be(G.join(n,"better-auth.ts")),Qt(r,"nextauth"),Jt(e,`# NextAuth (Auth.js v5)
|
|
352
352
|
AUTH_SECRET=
|
|
353
353
|
NEXTAUTH_URL=http://localhost:3000
|
|
354
354
|
# OAuth (optional \u2014 leave blank to disable that provider)
|
|
@@ -356,7 +356,7 @@ GOOGLE_CLIENT_ID=
|
|
|
356
356
|
GOOGLE_CLIENT_SECRET=
|
|
357
357
|
GITHUB_CLIENT_ID=
|
|
358
358
|
GITHUB_CLIENT_SECRET=
|
|
359
|
-
`);return}}var
|
|
359
|
+
`);return}}var gt=[{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"}],zr=new Set(gt.map(e=>e.id));function In(e){return gt.find(t=>t.id===e)}function Rn(e){if(!e)return[];let t=e.split(",").map(r=>r.trim().toLowerCase()).filter(Boolean);if(t.includes("none"))return[];let o=[],n=new Set;for(let r of t)zr.has(r)&&!n.has(r)&&(o.push(r),n.add(r));return o}import Ce from"fs";import Pe from"path";function Jr(e,t){let o=`// apps/web/src/app/api/auth/callback/${e.id}/route.ts
|
|
360
360
|
// Generated by create-sailor \u2014 OAuth callback for ${e.name}
|
|
361
361
|
// Primary auth provider: ${t}
|
|
362
362
|
// Docs: ${e.docs}`,n={wechat:` const tokenRes = await fetch(
|
|
@@ -481,7 +481,7 @@ ${n[e.id]}
|
|
|
481
481
|
void state;
|
|
482
482
|
return NextResponse.redirect(new URL("/dashboard", req.url));
|
|
483
483
|
}
|
|
484
|
-
`}function
|
|
484
|
+
`}function Qr(e){return`"use client";
|
|
485
485
|
|
|
486
486
|
/**
|
|
487
487
|
* SocialLoginButtons \u2014 generated by create-sailor.
|
|
@@ -524,23 +524,23 @@ export function SocialLoginButtons() {
|
|
|
524
524
|
</div>
|
|
525
525
|
);
|
|
526
526
|
}
|
|
527
|
-
`}function
|
|
528
|
-
`+t):
|
|
527
|
+
`}function kn(e){Ce.mkdirSync(e,{recursive:!0})}function Zr(e,t){let o=Pe.join(e,".env.example");Ce.existsSync(o)?Ce.appendFileSync(o,`
|
|
528
|
+
`+t):Ce.writeFileSync(o,t)}function es(e){let t=`# ${e.name} (${e.id}) \u2014 ${e.docs}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
529
529
|
`);return`${t}
|
|
530
530
|
${o}
|
|
531
|
-
`}async function
|
|
532
|
-
`);J.appendFileSync(o,c)}function Z(e,t,o){let n=
|
|
531
|
+
`}async function xn(e,t,o){if(!t||t.length===0)return;let n=Pe.join(e,"apps","web");if(!Ce.existsSync(n))return;let r=new Set,s=[];for(let l of t){if(r.has(l))continue;r.add(l);let m=In(l);m&&s.push(m)}if(s.length===0)return;for(let l of s)Zr(e,es(l));let i=Pe.join(n,"src","app","api","auth","callback");for(let l of s){let m=Pe.join(i,l.id);kn(m);let S=Pe.join(m,"route.ts");Ce.writeFileSync(S,Jr(l,o))}let c=Pe.join(n,"src","components","auth");kn(c);let a=Pe.join(c,"SocialLoginButtons.tsx");Ce.writeFileSync(a,Qr(s))}import ot from"fs";import Nn from"path";var Tt=[{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 Tn(e){return Tt.find(t=>t.id===e)}var Sc=Tt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});import J from"fs";import Nt from"path";function Q(e,t){let o=Nt.join(e,".env.example");if(!J.existsSync(o)||t.envVars.length===0)return;let r=`# ${t.category??t.name}: ${t.name}`;if(J.readFileSync(o,"utf-8").includes(r))return;let c=["","# =============================================",r,t.docs?`# Docs: ${t.docs}`:null,"# =============================================",...t.envVars.map(a=>`${a}=""`),""].filter(a=>a!==null).join(`
|
|
532
|
+
`);J.appendFileSync(o,c)}function Z(e,t,o){let n=Nt.join(e,".env.example");if(!J.existsSync(n))return;let r=J.readFileSync(n,"utf-8"),s=`${t}="${o}"`,i=new RegExp(`^${t}=.*$`,"m");if(i.test(r)){J.writeFileSync(n,r.replace(i,s));return}let c=r.endsWith(`
|
|
533
533
|
`)?"":`
|
|
534
534
|
`;J.appendFileSync(n,`${c}${s}
|
|
535
|
-
`)}var
|
|
536
|
-
`);
|
|
537
|
-
`)}async function
|
|
535
|
+
`)}var ts=["design","iam","commerce","integrations","platform","ops","ai"];function Ae(e,t){for(let n of ts){let r=Nt.join(e,"packages",n,t);if(J.existsSync(r))return r}let o=Nt.join(e,"packages",t);return J.existsSync(o)?o:null}function H(e,t){let o=Ae(e,t);o&&J.rmSync(o,{recursive:!0,force:!0})}function On(e){return ot.existsSync(e)?ot.readFileSync(e,"utf-8"):null}function ns(e,t){let o=Nn.join(e,".env.example"),n=On(o);if(n===null)return;let r=`# Cache: ${t.name}`;if(n.includes(r))return;let s=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
536
|
+
`);ot.appendFileSync(o,s)}function os(e,t,o){let n=Nn.join(e,".env.example"),r=On(n);if(r===null)return;let s=new RegExp(`^${t}=.*$`,"m");s.test(r)?ot.writeFileSync(n,r.replace(s,`${t}="${o}"`)):ot.appendFileSync(n,`${t}="${o}"
|
|
537
|
+
`)}async function Un(e,t,o){let n=Ae(e,"cache");if(t==="none"){n&&ot.rmSync(n,{recursive:!0,force:!0});return}let r=Tn(t);if(!r)throw new Error(`Unknown cache provider "${t}". Valid ids: ${Tt.map(s=>s.id).join(", ")}`);n&&(ns(e,r),os(e,"CACHE_PROVIDER",t))}var Ot=[{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 Ln(e){return Ot.find(t=>t.id===e)}var Ic=Ot.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Mn(e,t,o="global"){let n=Ln(t);if(!n)throw new Error(`Unknown captcha provider "${t}". Valid ids: ${Ot.map(r=>r.id).join(", ")}`);if(n.id==="none"){H(e,"captcha");return}n.envVars.length>0&&Q(e,{category:"Captcha",name:n.name,envVars:n.envVars,docs:n.docs}),Z(e,"CAPTCHA_PROVIDER",n.id)}import Lt from"fs";import rs from"path";var Ut=[{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 Dn(e){return Ut.find(t=>t.id===e)}var Nc=Ut.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Fn(e,t,o="global"){let n=Dn(t);if(!n)throw new Error(`Unknown CMS provider "${t}". Valid ids: ${Ut.map(s=>s.id).join(", ")}`);let r=rs.join(e,"apps","studio");if(n.id==="none"){H(e,"sanity"),Lt.existsSync(r)&&Lt.rmSync(r,{recursive:!0,force:!0});return}n.id!=="sanity"&&(H(e,"sanity"),Lt.existsSync(r)&&Lt.rmSync(r,{recursive:!0,force:!0})),n.envVars.length>0&&Q(e,{category:"CMS",name:n.name,envVars:n.envVars,docs:n.docs}),Z(e,"CMS_PROVIDER",n.id)}import k from"fs";import R from"path";var ss=R.join("apps","web"),Zt=R.join("src","components","compliance"),ft=R.join("src","app","[locale]");function ee(e){return R.join(e,ss)}function is(e){k.mkdirSync(e,{recursive:!0})}function W(e,t){is(R.dirname(e)),k.writeFileSync(e,t,"utf8")}function en(e,t){let o=R.join(e,".env.example");if(k.existsSync(o)){if(k.readFileSync(o,"utf8").includes(t.split(`
|
|
538
538
|
`)[0]??""))return;k.appendFileSync(o,`
|
|
539
539
|
`+t+`
|
|
540
540
|
`)}else k.writeFileSync(o,t+`
|
|
541
|
-
`)}var
|
|
541
|
+
`)}var yt=`> **TEMPLATE \u2014 NOT LEGAL ADVICE.** This document is a boilerplate scaffold only.
|
|
542
542
|
> Consult a qualified lawyer in every jurisdiction you operate in before publishing.
|
|
543
|
-
> \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`,
|
|
543
|
+
> \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`,as=`import Link from "next/link";
|
|
544
544
|
import { IcpFooter } from "@/components/compliance/IcpFooter";
|
|
545
545
|
import { PublicSecurityFooter } from "@/components/compliance/PublicSecurityFooter";
|
|
546
546
|
|
|
@@ -591,7 +591,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
591
591
|
</footer>
|
|
592
592
|
);
|
|
593
593
|
}
|
|
594
|
-
`,
|
|
594
|
+
`,cs=`export function IcpFooter() {
|
|
595
595
|
const icp = process.env.NEXT_PUBLIC_ICP_BEIAN;
|
|
596
596
|
if (!icp) return null;
|
|
597
597
|
return (
|
|
@@ -605,7 +605,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
605
605
|
</a>
|
|
606
606
|
);
|
|
607
607
|
}
|
|
608
|
-
`,
|
|
608
|
+
`,ps=`export function PublicSecurityFooter() {
|
|
609
609
|
const psb = process.env.NEXT_PUBLIC_PSB_BEIAN;
|
|
610
610
|
const psbCode = process.env.NEXT_PUBLIC_PSB_CODE;
|
|
611
611
|
if (!psb) return null;
|
|
@@ -622,7 +622,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
622
622
|
</a>
|
|
623
623
|
);
|
|
624
624
|
}
|
|
625
|
-
`,
|
|
625
|
+
`,ls=`"use client";
|
|
626
626
|
|
|
627
627
|
import { useCallback, useEffect, useState } from "react";
|
|
628
628
|
|
|
@@ -786,7 +786,7 @@ export function CookieBanner() {
|
|
|
786
786
|
<button
|
|
787
787
|
type="button"
|
|
788
788
|
onClick={saveCustom}
|
|
789
|
-
className="rounded-md bg-[var(--blue-9)] px-4 py-2 text-sm font-medium text-white hover:opacity-90
|
|
789
|
+
className="rounded-md bg-[var(--blue-9)] px-4 py-2 text-sm font-medium text-white hover:opacity-90"
|
|
790
790
|
>
|
|
791
791
|
Save Preferences / \u4FDD\u5B58\u504F\u597D
|
|
792
792
|
</button>
|
|
@@ -794,7 +794,7 @@ export function CookieBanner() {
|
|
|
794
794
|
<button
|
|
795
795
|
type="button"
|
|
796
796
|
onClick={() => setShowCustomize(true)}
|
|
797
|
-
className="rounded-md border border-[var(--neutral-7)] px-4 py-2 text-sm font-medium text-[var(--neutral-12)] hover:bg-[var(--neutral-3)]
|
|
797
|
+
className="rounded-md border border-[var(--neutral-7)] px-4 py-2 text-sm font-medium text-[var(--neutral-12)] hover:bg-[var(--neutral-3)]"
|
|
798
798
|
>
|
|
799
799
|
Customize / \u81EA\u5B9A\u4E49
|
|
800
800
|
</button>
|
|
@@ -802,14 +802,14 @@ export function CookieBanner() {
|
|
|
802
802
|
<button
|
|
803
803
|
type="button"
|
|
804
804
|
onClick={rejectNonEssential}
|
|
805
|
-
className="rounded-md border border-[var(--neutral-7)] px-4 py-2 text-sm font-medium text-[var(--neutral-12)] hover:bg-[var(--neutral-3)]
|
|
805
|
+
className="rounded-md border border-[var(--neutral-7)] px-4 py-2 text-sm font-medium text-[var(--neutral-12)] hover:bg-[var(--neutral-3)]"
|
|
806
806
|
>
|
|
807
807
|
Reject Non-Essential / \u62D2\u7EDD\u975E\u5FC5\u8981
|
|
808
808
|
</button>
|
|
809
809
|
<button
|
|
810
810
|
type="button"
|
|
811
811
|
onClick={acceptAll}
|
|
812
|
-
className="rounded-md bg-[var(--blue-9)] px-4 py-2 text-sm font-medium text-white hover:opacity-90
|
|
812
|
+
className="rounded-md bg-[var(--blue-9)] px-4 py-2 text-sm font-medium text-white hover:opacity-90"
|
|
813
813
|
>
|
|
814
814
|
Accept All / \u5168\u90E8\u63A5\u53D7
|
|
815
815
|
</button>
|
|
@@ -849,7 +849,7 @@ function CategoryRow({ id, title, description, checked, disabled, onChange }: Ca
|
|
|
849
849
|
</label>
|
|
850
850
|
);
|
|
851
851
|
}
|
|
852
|
-
`,
|
|
852
|
+
`,ds=`interface AigcDisclaimerProps {
|
|
853
853
|
className?: string;
|
|
854
854
|
}
|
|
855
855
|
|
|
@@ -869,13 +869,13 @@ export function AigcDisclaimer({ className }: AigcDisclaimerProps) {
|
|
|
869
869
|
</div>
|
|
870
870
|
);
|
|
871
871
|
}
|
|
872
|
-
`,
|
|
872
|
+
`,us=`---
|
|
873
873
|
title: Privacy Policy / \u9690\u79C1\u653F\u7B56
|
|
874
874
|
description: How {{COMPANY_NAME}} collects, uses, shares, and protects personal information.
|
|
875
875
|
lastUpdated: 2026-01-01
|
|
876
876
|
---
|
|
877
877
|
|
|
878
|
-
${
|
|
878
|
+
${yt}
|
|
879
879
|
|
|
880
880
|
# Privacy Policy / \u9690\u79C1\u653F\u7B56
|
|
881
881
|
|
|
@@ -1014,13 +1014,13 @@ or in-app notification before the change takes effect.
|
|
|
1014
1014
|
**Data Controller / \u6570\u636E\u63A7\u5236\u8005:** {{COMPANY_NAME}}
|
|
1015
1015
|
**Address / \u5730\u5740:** {{COMPANY_ADDRESS}}
|
|
1016
1016
|
**Email / \u90AE\u7BB1:** {{COMPANY_CONTACT_EMAIL}}
|
|
1017
|
-
`,
|
|
1017
|
+
`,ms=`---
|
|
1018
1018
|
title: Terms of Service / \u670D\u52A1\u6761\u6B3E
|
|
1019
1019
|
description: Terms governing your use of {{COMPANY_NAME}}.
|
|
1020
1020
|
lastUpdated: 2026-01-01
|
|
1021
1021
|
---
|
|
1022
1022
|
|
|
1023
|
-
${
|
|
1023
|
+
${yt}
|
|
1024
1024
|
|
|
1025
1025
|
# Terms of Service / \u670D\u52A1\u6761\u6B3E
|
|
1026
1026
|
|
|
@@ -1133,13 +1133,13 @@ conflict-of-laws principles. Disputes shall be resolved in the competent courts
|
|
|
1133
1133
|
## 11. Contact / \u8054\u7CFB\u65B9\u5F0F
|
|
1134
1134
|
|
|
1135
1135
|
{{COMPANY_NAME}} \xB7 {{COMPANY_ADDRESS}} \xB7 {{COMPANY_CONTACT_EMAIL}}
|
|
1136
|
-
`,
|
|
1136
|
+
`,gs=`---
|
|
1137
1137
|
title: AIGC Disclosure / \u751F\u6210\u5F0F AI \u670D\u52A1\u8BF4\u660E
|
|
1138
1138
|
description: Disclosure required by \u7F51\u4FE1\u529E\u300A\u751F\u6210\u5F0F\u4EBA\u5DE5\u667A\u80FD\u670D\u52A1\u7BA1\u7406\u6682\u884C\u529E\u6CD5\u300B
|
|
1139
1139
|
lastUpdated: 2026-01-01
|
|
1140
1140
|
---
|
|
1141
1141
|
|
|
1142
|
-
${
|
|
1142
|
+
${yt}
|
|
1143
1143
|
|
|
1144
1144
|
# AIGC Disclosure / \u751F\u6210\u5F0F AI \u670D\u52A1\u8BF4\u660E
|
|
1145
1145
|
|
|
@@ -1215,13 +1215,13 @@ human professional.
|
|
|
1215
1215
|
|
|
1216
1216
|
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
|
|
1217
1217
|
\u7528\u4E8E\u533B\u7597\u3001\u6CD5\u5F8B\u3001\u91D1\u878D\u6216\u5B89\u5168\u6538\u5173\u7684\u51B3\u7B56\u3002
|
|
1218
|
-
`,
|
|
1218
|
+
`,fs=`---
|
|
1219
1219
|
title: Cross-Border Data Notice / \u6570\u636E\u51FA\u5883\u544A\u77E5
|
|
1220
1220
|
description: Notice of cross-border transfer of personal information, per PIPL.
|
|
1221
1221
|
lastUpdated: 2026-01-01
|
|
1222
1222
|
---
|
|
1223
1223
|
|
|
1224
|
-
${
|
|
1224
|
+
${yt}
|
|
1225
1225
|
|
|
1226
1226
|
# Cross-Border Data Notice / \u6570\u636E\u51FA\u5883\u544A\u77E5
|
|
1227
1227
|
|
|
@@ -1280,13 +1280,13 @@ Refusal may limit your ability to use features that rely on overseas services.
|
|
|
1280
1280
|
- \u4F20\u8F93\u4E2D\u52A0\u5BC6 (TLS 1.2+) \u53CA\u9759\u6001\u52A0\u5BC6 (AES-256)\u3002
|
|
1281
1281
|
- \u5408\u540C\u6761\u6B3E\u8981\u6C42\u5883\u5916\u63A5\u6536\u65B9\u63D0\u4F9B\u7B49\u540C\u4E8E PIPL \u7684\u4FDD\u62A4\u3002
|
|
1282
1282
|
- \u8BBF\u95EE\u63A7\u5236\u3001\u5BA1\u8BA1\u65E5\u5FD7\u548C\u8FDD\u89C4\u901A\u77E5\u7A0B\u5E8F\u3002
|
|
1283
|
-
`,
|
|
1283
|
+
`,ys=`---
|
|
1284
1284
|
title: Personal Information Collection List / \u4E2A\u4EBA\u4FE1\u606F\u6536\u96C6\u6E05\u5355
|
|
1285
1285
|
description: Itemized list of personal information collected, per PIPL Article 17.
|
|
1286
1286
|
lastUpdated: 2026-01-01
|
|
1287
1287
|
---
|
|
1288
1288
|
|
|
1289
|
-
${
|
|
1289
|
+
${yt}
|
|
1290
1290
|
|
|
1291
1291
|
# Personal Information Collection List / \u4E2A\u4EBA\u4FE1\u606F\u6536\u96C6\u6E05\u5355
|
|
1292
1292
|
|
|
@@ -1322,26 +1322,26 @@ To exercise your rights (access, rectify, delete, export, withdraw consent), con
|
|
|
1322
1322
|
**{{COMPANY_CONTACT_EMAIL}}**.
|
|
1323
1323
|
|
|
1324
1324
|
\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
|
|
1325
|
-
`;async function
|
|
1326
|
-
`))}async function
|
|
1327
|
-
`))}async function
|
|
1328
|
-
`))}async function
|
|
1329
|
-
`))}async function
|
|
1330
|
-
`)}import
|
|
1331
|
-
`+t):
|
|
1325
|
+
`;async function Ss(e,t){let o=ee(e);if(!k.existsSync(o))return;let n=R.join(o,"src","components","compliance","Footer.tsx");W(n,as);let r=R.join(o,"src","components","compliance","README.md");W(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(`
|
|
1326
|
+
`))}async function hs(e,t){let o=ee(e);if(!k.existsSync(o))return;let n=R.join(o,"src","components","compliance","CookieBanner.tsx");W(n,ls)}async function Es(e,t){let o=ee(e);if(!k.existsSync(o))return;let n=R.join(o,ft,"privacy","page.mdx");W(n,us)}async function vs(e,t){let o=ee(e);if(!k.existsSync(o))return;let n=R.join(o,ft,"terms","page.mdx");W(n,ms)}async function _s(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,Zt,"IcpFooter.tsx");W(o,cs),en(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(`
|
|
1327
|
+
`))}async function bs(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,Zt,"PublicSecurityFooter.tsx");W(o,ps),en(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(`
|
|
1328
|
+
`))}async function Ps(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,Zt,"AigcDisclaimer.tsx");W(o,ds),en(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(`
|
|
1329
|
+
`))}async function Cs(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,ft,"aigc-disclosure","page.mdx");W(o,gs)}async function As(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,ft,"cross-border-data","page.mdx");W(o,fs)}async function ws(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,ft,"pii-collection","page.mdx");W(o,ys)}async function jn(e,t){try{await Ss(e,t),await hs(e,t),await Es(e,t),await vs(e,t),(t==="cn"||t==="hybrid")&&(await _s(e),await bs(e),await Ps(e),await Cs(e),await As(e),await ws(e))}catch(o){throw console.error("applyComplianceTemplates failed:",o),new Error(`Failed to apply compliance templates for region "${t}". Check that ${e} is writable.`)}}import Is from"fs";import Rs from"path";async function Vn(e,t){let o=Rs.join(e,"nebutra.config.json");Is.writeFileSync(o,JSON.stringify(t,null,2)+`
|
|
1330
|
+
`)}import w from"fs";import we from"path";var tn=[{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:6543/postgres?pgbouncer=true",comment:"Supabase pooler \u2014 use for runtime queries"},{name:"DIRECT_URL",placeholder:"postgresql://postgres.<project-ref>:<password>@aws-0-<region>.pooler.supabase.com:5432/postgres",comment:"Supabase direct \u2014 use for Prisma migrations"},{name:"NEXT_PUBLIC_SUPABASE_URL",placeholder:"https://<project>.supabase.co"},{name:"NEXT_PUBLIC_SUPABASE_ANON_KEY",placeholder:""},{name:"SUPABASE_SERVICE_ROLE_KEY",placeholder:""}],docs:"https://supabase.com/docs",description:"Managed Postgres + auth/storage/realtime/edge-functions.",tier:"managed"},{id:"neon",name:"Neon",region:"global",supportedEngines:["postgresql"],forcedEngine:"postgresql",keepDirectUrl:!0,envVars:[{name:"DATABASE_URL",placeholder:"postgresql://<user>:<pwd>@ep-<branch>-pooler.<region>.neon.tech/<db>?sslmode=require",comment:"Neon pooled \u2014 runtime queries"},{name:"DIRECT_URL",placeholder:"postgresql://<user>:<pwd>@ep-<branch>.<region>.neon.tech/<db>?sslmode=require",comment:"Neon direct (no -pooler) \u2014 migrations"}],docs:"https://neon.tech/docs",description:"Serverless Postgres with branching and scale-to-zero.",tier:"managed"},{id:"vercel-postgres",name:"Vercel Postgres",region:"global",supportedEngines:["postgresql"],forcedEngine:"postgresql",keepDirectUrl:!0,envVars:[{name:"DATABASE_URL",placeholder:"$POSTGRES_PRISMA_URL",comment:"Vercel Marketplace injects POSTGRES_PRISMA_URL automatically"},{name:"DIRECT_URL",placeholder:"$POSTGRES_URL_NON_POOLING",comment:"Vercel Marketplace injects POSTGRES_URL_NON_POOLING automatically"}],docs:"https://vercel.com/docs/storage/vercel-postgres",description:"Neon-backed, auto-provisioned via Vercel Marketplace.",tier:"managed"},{id:"planetscale",name:"PlanetScale",region:"global",supportedEngines:["mysql"],forcedEngine:"mysql",prismaDatasourceExtras:['relationMode = "prisma"'],envVars:[{name:"DATABASE_URL",placeholder:"mysql://<user>:<pass>@<host>.psdb.cloud/<db>?sslaccept=strict",comment:'PlanetScale: relationMode = "prisma" (no FK support server-side)'}],docs:"https://planetscale.com/docs",description:"Managed MySQL with branching. No foreign keys.",tier:"managed"},{id:"railway",name:"Railway",region:"global",supportedEngines:["postgresql","mysql"],envVars:[{name:"DATABASE_URL",placeholder:"$DATABASE_URL",comment:"Railway service env var; injected when you link a Postgres service"}],docs:"https://docs.railway.app/databases/postgresql",description:"Generalist managed infra; standard TCP Postgres/MySQL.",tier:"managed"},{id:"aliyun-rds",name:"Aliyun RDS",region:"cn",supportedEngines:["postgresql","mysql"],envVars:[{name:"DATABASE_URL",placeholder:"postgresql://<user>:<pwd>@<rds-instance>.rds.aliyuncs.com:5432/<db>",comment:"Aliyun RDS Postgres \u2014 internal VPC endpoint recommended"}],docs:"https://www.alibabacloud.com/help/en/rds",description:"Aliyun RDS \u2014 China compliance + intranet routing.",tier:"managed"},{id:"tencent-cdb",name:"Tencent CloudDB (CDB)",region:"cn",supportedEngines:["mysql","postgresql"],envVars:[{name:"DATABASE_URL",placeholder:"mysql://<user>:<pwd>@<cdb-instance>.tencentcdb.com:3306/<db>",comment:"Tencent CloudDB \u2014 VPC endpoint recommended"}],docs:"https://intl.cloud.tencent.com/document/product/236",description:"Tencent CloudDB \u2014 China compliance.",tier:"managed"},{id:"none",name:"None",region:"both",supportedEngines:["postgresql","mysql","sqlite"],envVars:[],docs:"",description:"Skip database scaffolding.",tier:"self-hosted"}];function Mt(e){return tn.find(t=>t.id===e)}function Bn(e){return e==="cn"||e==="hybrid"?"local":"supabase"}function ks(e){w.existsSync(e)&&w.rmSync(e,{recursive:!0,force:!0})}function Kn(e,t){return e.replace(/provider\s*=\s*"postgresql"/,`provider = "${t}"`)}function xs(e){return e.replace(/^\s*directUrl\s*=\s*env\([^)]*\)\s*\n/gm,"")}function $n(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 Ts(e,t){let o=we.join(e,".env.example");w.existsSync(o)?w.appendFileSync(o,`
|
|
1331
|
+
`+t):w.writeFileSync(o,t)}async function Yn(e,t,o){let n=we.join(e,"packages","platform","db");if(t==="none"){ks(n);return}let r=we.join(n,"prisma","schema.prisma"),s;if(t==="postgresql"?s=`postgresql://postgres:postgres@localhost:5432/${o}`:t==="mysql"?s=`mysql://root:root@localhost:3306/${o}`:s="file:./dev.db",Ts(e,`# Database (${t})
|
|
1332
1332
|
DATABASE_URL="${s}"
|
|
1333
|
-
`),!
|
|
1333
|
+
`),!w.existsSync(r))return;let i=w.readFileSync(r,"utf8"),c=i;t==="mysql"?(c=Kn(c,"mysql"),c=$n(c)):t==="sqlite"&&(c=Kn(c,"sqlite"),c=xs(c),c=$n(c)),c!==i&&w.writeFileSync(r,c)}function Ns(e){let t=we.join(e,".env.example");return w.existsSync(t)?w.readFileSync(t,"utf8"):""}function Os(e,t,o,n){let r=we.join(e,".env.example"),s=Ns(e),i=n?`# ${n}
|
|
1334
1334
|
${t}="${o}"
|
|
1335
1335
|
`:`${t}="${o}"
|
|
1336
|
-
`,c=new RegExp(`^\\s*${t}\\s*=.*$`,"m");if(c.test(s)){
|
|
1337
|
-
${i}`):
|
|
1336
|
+
`,c=new RegExp(`^\\s*${t}\\s*=.*$`,"m");if(c.test(s)){w.writeFileSync(r,s.replace(c,`${t}="${o}"`));return}s?w.appendFileSync(r,`
|
|
1337
|
+
${i}`):w.writeFileSync(r,i)}function Us(e,t){let o=we.join(e,"packages","platform","db","prisma","schema.prisma");if(!w.existsSync(o))return;let n=w.readFileSync(o,"utf8"),r=/(datasource\s+db\s*\{[^}]*?)(\n})/;if(!n.match(r))return;let i=t.filter(l=>!n.includes(l.trim()));if(i.length===0)return;let c=i.map(l=>` ${l}`).join(`
|
|
1338
1338
|
`),a=n.replace(r,`$1
|
|
1339
|
-
${c}$2`);
|
|
1340
|
-
`))}async function
|
|
1341
|
-
`+t):
|
|
1339
|
+
${c}$2`);w.writeFileSync(o,a)}function Ls(e){let t=we.join(e,"packages","platform","db","prisma","schema.prisma");if(!w.existsSync(t))return;let o=w.readFileSync(t,"utf8");if(/directUrl\s*=\s*env\(/.test(o))return;let n=/(datasource\s+db\s*\{[\s\S]*?url\s*=\s*env\(\s*"DATABASE_URL"\s*\)[^\n]*\n)/;n.test(o)&&w.writeFileSync(t,o.replace(n,`$1 directUrl = env("DIRECT_URL")
|
|
1340
|
+
`))}async function qn(e,t,o){if(!(t.id==="none"||t.id==="local")){for(let n of t.envVars){let r=n.placeholder.replace(/\$PROJECT/g,o);Os(e,n.name,r,n.comment)}t.prismaDatasourceExtras&&t.prismaDatasourceExtras.length>0&&Us(e,t.prismaDatasourceExtras),t.keepDirectUrl&&Ls(e)}}import rt from"fs";import B from"path";import{fileURLToPath as Ms}from"url";var Ds=Ms(import.meta.url),Gn=B.dirname(Ds);function Fs(){let e=[B.join(Gn,"..","templates","deploy"),B.join(Gn,"..","..","templates","deploy")];for(let t of e)if(rt.existsSync(t))return t;return e[0]}async function Hn(e,t){if(t==="none")return;let o=Fs();t==="vercel"?await rt.promises.copyFile(B.join(o,"vercel.json"),B.join(e,"vercel.json")):t==="railway"?await rt.promises.copyFile(B.join(o,"railway.toml"),B.join(e,"railway.toml")):t==="cloudflare"?await rt.promises.copyFile(B.join(o,"wrangler.toml"),B.join(e,"wrangler.toml")):t==="selfhost"&&(await rt.promises.copyFile(B.join(o,"docker-compose.yml"),B.join(e,"docker-compose.yml")),await rt.promises.copyFile(B.join(o,"Dockerfile.web"),B.join(e,"Dockerfile.web")))}import le from"fs";import Ie from"path";import{fileURLToPath as js}from"url";var Vs=js(import.meta.url),Wn=Ie.dirname(Vs),Bs=/\.(md|mdx|json|ts|tsx|js|mjs|cjs|yaml|yml|css|html|vue)$/,Xn=["fumadocs","nextra","mintlify","docusaurus","vitepress"];function Ks(e){let t=[Ie.join(Wn,"..","templates","docs",e),Ie.join(Wn,"..","..","templates","docs",e)];for(let o of t)if(le.existsSync(o))return o;return t[0]}async function zn(e,t){if(t.framework==="none")return;if(!Xn.includes(t.framework))throw new Error(`Unsupported docs framework: "${t.framework}". Supported: ${Xn.join(", ")}, none.`);let o=Ks(t.framework),n=Ie.join(e,"apps","docs");if(!le.existsSync(o))throw new Error(`Docs template not found: ${o}`);Jn(o,n),Qn(n,{"{PRODUCT_NAME}":t.projectName})}function Jn(e,t){le.mkdirSync(t,{recursive:!0});for(let o of le.readdirSync(e,{withFileTypes:!0})){let n=Ie.join(e,o.name),r=Ie.join(t,o.name);o.isDirectory()?Jn(n,r):le.copyFileSync(n,r)}}function Qn(e,t){for(let o of le.readdirSync(e,{withFileTypes:!0})){let n=Ie.join(e,o.name);if(o.isDirectory())Qn(n,t);else if(Bs.test(o.name)){let r=le.readFileSync(n,"utf-8");for(let[s,i]of Object.entries(t))r=r.split(s).join(i);le.writeFileSync(n,r)}}}import ke from"fs";import Re from"path";var Dt=[{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 Zn(e){return Dt.find(t=>t.id===e)}var rp=Dt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});var xe="@nebutra/email";function nn(e,t){ke.mkdirSync(Re.dirname(e),{recursive:!0}),ke.writeFileSync(e,t)}function $s(e,t){let o=Re.join(e,".env.example");ke.existsSync(o)?ke.appendFileSync(o,`
|
|
1341
|
+
`+t):ke.writeFileSync(o,t)}function Ys(e){if(e.envVars.length===0)return"";let t=`# Email provider: ${e.name}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
1342
1342
|
`);return`${t}
|
|
1343
1343
|
${o}
|
|
1344
|
-
`}var
|
|
1344
|
+
`}var qs=`/**
|
|
1345
1345
|
* Public inputs accepted by the \`sendEmail\` wrapper.
|
|
1346
1346
|
* Keep the surface minimal \u2014 individual providers may accept more, but
|
|
1347
1347
|
* consumers should code against this shape for portability.
|
|
@@ -1361,8 +1361,8 @@ export interface SendEmailResult {
|
|
|
1361
1361
|
/** Raw provider response \u2014 useful for debugging, not a stable contract. */
|
|
1362
1362
|
raw?: unknown;
|
|
1363
1363
|
}
|
|
1364
|
-
`;function
|
|
1365
|
-
* @${
|
|
1364
|
+
`;function Gs(){return`/**
|
|
1365
|
+
* @${xe.slice(1)} \u2014 Resend transactional email wrapper.
|
|
1366
1366
|
*
|
|
1367
1367
|
* Docs: https://resend.com/docs
|
|
1368
1368
|
*/
|
|
@@ -1400,8 +1400,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1400
1400
|
}
|
|
1401
1401
|
|
|
1402
1402
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1403
|
-
`}function
|
|
1404
|
-
* @${
|
|
1403
|
+
`}function Hs(){return`/**
|
|
1404
|
+
* @${xe.slice(1)} \u2014 Postmark transactional email wrapper.
|
|
1405
1405
|
*
|
|
1406
1406
|
* Docs: https://postmarkapp.com/developer
|
|
1407
1407
|
*/
|
|
@@ -1438,8 +1438,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1438
1438
|
}
|
|
1439
1439
|
|
|
1440
1440
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1441
|
-
`}function
|
|
1442
|
-
* @${
|
|
1441
|
+
`}function Ws(){return`/**
|
|
1442
|
+
* @${xe.slice(1)} \u2014 AWS SES v2 transactional email wrapper.
|
|
1443
1443
|
*
|
|
1444
1444
|
* Docs: https://docs.aws.amazon.com/ses
|
|
1445
1445
|
*/
|
|
@@ -1494,8 +1494,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1494
1494
|
}
|
|
1495
1495
|
|
|
1496
1496
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1497
|
-
`}function
|
|
1498
|
-
* @${
|
|
1497
|
+
`}function Xs(){return`/**
|
|
1498
|
+
* @${xe.slice(1)} \u2014 \u963F\u91CC\u4E91\u90AE\u4EF6\u63A8\u9001 (Aliyun DirectMail) wrapper.
|
|
1499
1499
|
*
|
|
1500
1500
|
* Docs: https://help.aliyun.com/product/29412.html
|
|
1501
1501
|
*/
|
|
@@ -1544,8 +1544,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1544
1544
|
}
|
|
1545
1545
|
|
|
1546
1546
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1547
|
-
`}function
|
|
1548
|
-
* @${
|
|
1547
|
+
`}function zs(){return`/**
|
|
1548
|
+
* @${xe.slice(1)} \u2014 \u817E\u8BAF\u4E91 SES wrapper.
|
|
1549
1549
|
*
|
|
1550
1550
|
* Docs: https://cloud.tencent.com/document/product/1288
|
|
1551
1551
|
*/
|
|
@@ -1592,8 +1592,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1592
1592
|
}
|
|
1593
1593
|
|
|
1594
1594
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1595
|
-
`}function
|
|
1596
|
-
* @${
|
|
1595
|
+
`}function Js(){return`/**
|
|
1596
|
+
* @${xe.slice(1)} \u2014 \u7F51\u6613\u4F01\u4E1A\u90AE\u7BB1 via SMTP (nodemailer).
|
|
1597
1597
|
*
|
|
1598
1598
|
* Docs: https://qiye.163.com/help
|
|
1599
1599
|
*/
|
|
@@ -1630,24 +1630,24 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1630
1630
|
}
|
|
1631
1631
|
|
|
1632
1632
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1633
|
-
`}function
|
|
1634
|
-
`}}function
|
|
1635
|
-
`}async function
|
|
1633
|
+
`}function Qs(e){switch(e){case"resend":return Gs();case"postmark":return Hs();case"ses":return Ws();case"aliyun-dm":return Xs();case"tencent-ses":return zs();case"netease":return Js();case"none":return`export {};
|
|
1634
|
+
`}}function Zs(e){let t={};e.pkg&&e.pkgVersion&&(t[e.pkg]=e.pkgVersion),e.id==="aliyun-dm"&&(t["@alicloud/openapi-client"]="^0.4.12");let o={name:xe,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(o,null,2)+`
|
|
1635
|
+
`}async function eo(e,t,o){if(t==="none")return;let n=Zn(t);if(!n)throw new Error(`Unknown email provider "${t}". Valid ids: ${Dt.map(i=>i.id).join(", ")}`);let r=Re.join(e,"packages","email"),s=Re.join(r,"src");ke.existsSync(s)&&ke.rmSync(s,{recursive:!0,force:!0}),nn(Re.join(r,"package.json"),Zs(n)),nn(Re.join(s,"types.ts"),qs),nn(Re.join(s,"index.ts"),Qs(n.id)),$s(e,Ys(n))}import Ft from"fs";import to from"path";async function no(e,t){let o=`# Automatically injected by create-sailor
|
|
1636
1636
|
DATABASE_URL="${String(t.databaseUrl)}"
|
|
1637
1637
|
|
|
1638
1638
|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="${String(t.clerkPublishable)}"
|
|
1639
1639
|
CLERK_SECRET_KEY="${String(t.clerkSecret)}"
|
|
1640
1640
|
|
|
1641
1641
|
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
|
|
1642
|
-
`,n=
|
|
1643
|
-
`+o)}import
|
|
1642
|
+
`,n=to.join(e,".env"),r=to.join(e,".env.local");!Ft.existsSync(n)&&!Ft.existsSync(r)?Ft.writeFileSync(r,o):Ft.appendFileSync(r,`
|
|
1643
|
+
`+o)}import ro from"crypto";import St from"fs";import oo from"path";function Te(e){return ro.randomBytes(e).toString("base64").replace(/=+$/,"")}function ei(e){return ro.randomBytes(e).toString("hex")}function ti(){return{AUTH_SECRET:Te(32),NEXTAUTH_SECRET:Te(32),BETTER_AUTH_SECRET:Te(32),JWT_SECRET:Te(32),WEBHOOK_SECRET:ei(16),ENCRYPTION_KEY:Te(32),API_SECRET_KEY:Te(32),SESSION_SECRET:Te(32)}}function ni(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function oi(e,t,o){let n=new RegExp(`^(?!#)\\s*${t}=.*$`,"gm"),r=ni(o);return e.replace(n,`${t}="${r}"`)}async function so(e){let t=oo.join(e,".env.example"),o=oo.join(e,".env.local");if(!St.existsSync(t))return null;try{let n=St.readFileSync(t,"utf8"),r=ti(),i=St.existsSync(o)?St.readFileSync(o,"utf8"):n;for(let c of Object.keys(r))i=oi(i,c,r[c]);return St.writeFileSync(o,i),r}catch(n){let r=n instanceof Error?n.message:String(n);throw console.error(`[create-sailor] Failed to generate env secrets: ${r}`),new Error(`Unable to write .env.local: ${r}`)}}var jt=[{id:"vercel-flags",name:"Vercel Flags SDK",region:"global",envVars:["FLAGS_SECRET"],docs:"https://vercel.com/docs/workflow-collaboration/feature-flags",description:"Native Vercel feature flags"},{id:"growthbook",name:"GrowthBook",region:"both",envVars:["GROWTHBOOK_API_HOST","GROWTHBOOK_CLIENT_KEY","GROWTHBOOK_DECRYPTION_KEY"],docs:"https://docs.growthbook.io",description:"Open-source A/B testing + flags"},{id:"configcat",name:"ConfigCat",region:"global",envVars:["CONFIGCAT_SDK_KEY"],docs:"https://configcat.com/docs/",description:"Feature flag & config management"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function io(e){return jt.find(t=>t.id===e)}var Sp=jt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function ao(e,t,o="global"){let n=io(t);if(!n)throw new Error(`Unknown feature-flags provider "${t}". Valid ids: ${jt.map(r=>r.id).join(", ")}`);if(n.id==="none"){H(e,"feature-flags");return}n.envVars.length>0&&Q(e,{category:"Feature Flags",name:n.name,envVars:n.envVars,docs:n.docs}),Z(e,"FEATURE_FLAGS_PROVIDER",n.id)}import{execFileSync as lo}from"child_process";import L from"fs";import ri from"os";import de from"path";import si from"ignore";var ii={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 ai(e,t){return`https://github.com/${e}/archive/refs/heads/${t}.tar.gz`}function ci(e,t){return`https://github.com/${e}/archive/${t}.tar.gz`}function uo(e){return/^[0-9a-f]{7,40}$/i.test(e)}function pi(e,t){let o=`https://github.com/${e}.git`,n=[`refs/heads/${t}`,`refs/tags/${t}`];for(let r of n)try{let s=lo("git",["ls-remote",o,r],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(!s)continue;let[i]=s.split(/\s+/);if(uo(i))return i}catch{}return null}async function li(e,t){if(uo(t))return t;let o=await fetch(`https://api.github.com/repos/${e}/commits/${encodeURIComponent(t)}`,{headers:{Accept:"application/vnd.github+json","User-Agent":"create-sailor"}});if(!o.ok){let r=pi(e,t);if(r)return r;throw new Error(`Failed to resolve ${e}@${t} (GitHub API ${o.status})`)}let n=await o.json();if(typeof n.sha!="string"||n.sha.length===0)throw new Error(`GitHub API did not return a commit SHA for ${e}@${t}`);return n.sha}function mo(e,t,o){let n=L.readdirSync(t,{withFileTypes:!0});for(let r of n){if(r.name==="node_modules"||r.name===".git")continue;let s=de.join(t,r.name),i=de.relative(e,s).split(de.sep).join("/");r.isDirectory()?(o.push(i+"/"),mo(e,s,o)):o.push(i)}}function di(e){let t=de.join(e,".templateignore");if(!L.existsSync(t))return;let o=L.readFileSync(t,"utf8"),n=si().add(o),r=[];mo(e,e,r);let s=r.map(a=>a.endsWith("/")?a.slice(0,-1):a),i=new Set(n.filter(s)),c=s.filter(a=>!i.has(a)).sort((a,l)=>l.split("/").length-a.split("/").length);for(let a of c){let l=de.join(e,a);try{L.existsSync(l)&&L.rmSync(l,{recursive:!0,force:!0})}catch{}}try{L.existsSync(t)&&L.rmSync(t,{force:!0})}catch{}}async function co(e,t){let o=await fetch(e,{headers:{Accept:"application/octet-stream","User-Agent":"create-sailor"}});if(!o.ok)throw new Error(`Failed to download archive (${o.status})`);let n=Buffer.from(await o.arrayBuffer());if(n.byteLength<1024)throw new Error(`Downloaded archive is unexpectedly small (${n.byteLength} bytes)`);L.writeFileSync(t,n);let r=L.statSync(t);if(r.size!==n.byteLength)throw new Error(`Archive write verification failed (${r.size} != ${n.byteLength})`);return r.size}function po(e,t){lo("tar",["-xzf",e,"-C",t,"--strip-components=1"],{stdio:"ignore"})}function go(e){try{let t=L.readdirSync(e);for(let o of t)L.rmSync(de.join(e,o),{recursive:!0,force:!0})}catch{}}async function ui(e,t){let o=L.mkdtempSync(de.join(ri.tmpdir(),"create-sailor-")),n=de.join(o,"template.tar.gz");try{let r=await li(e.repo,e.ref);await co(ci(e.repo,r),n),po(n,t)}catch(r){if(!e.allowMutableFallback)throw r;go(t),await co(ai(e.repo,e.ref),n),po(n,t)}finally{L.rmSync(o,{recursive:!0,force:!0})}}async function fo(e){L.mkdirSync(e,{recursive:!0});let o=process.env.SAILOR_TEMPLATE_SOURCE==="main"?["main"]:["mirror","main"],n;for(let r of o){let s=ii[r];try{if(await ui(s,e),s.applyIgnore)try{di(e)}catch(i){throw new Error(`Template cloned, but .templateignore processing failed: ${i instanceof Error?i.message:String(i)}`)}return}catch(i){n=i instanceof Error?i:new Error(String(i)),go(e)}}throw new Error(`Failed to download the template from any source. Last error: ${n?.message??"unknown"}
|
|
1644
1644
|
|
|
1645
1645
|
Ensure you have internet access and 'tar' installed.
|
|
1646
|
-
You can override the source with SAILOR_TEMPLATE_REPO / SAILOR_TEMPLATE_REF env vars.`)}import
|
|
1647
|
-
`);
|
|
1648
|
-
`);
|
|
1649
|
-
`)}function te(e,t){
|
|
1650
|
-
`+t):re.writeFileSync(o,t)}function
|
|
1646
|
+
You can override the source with SAILOR_TEMPLATE_REPO / SAILOR_TEMPLATE_REF env vars.`)}import Ne from"fs";import on from"path";async function yo(e,t){let o=on.join(e,"packages","mcp");if(t==="off"){Ne.existsSync(o)&&Ne.rmSync(o,{recursive:!0,force:!0});let r=on.join(e,"packages","cli","src","commands","mcp-server.ts");Ne.existsSync(r)&&Ne.rmSync(r,{force:!0});return}let n=on.join(e,".env.example");if(Ne.existsSync(n)){let r=Ne.readFileSync(n,"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(`
|
|
1647
|
+
`);Ne.appendFileSync(n,i)}}}import ht from"fs";import So from"path";async function ho(e,t,o){let n=So.join(e,"packages","metering"),r;if(t==="auto"?r=o!=="none"?"on":"off":t==="on"||t==="off"?r=t:r="off",r==="off"){ht.existsSync(n)&&ht.rmSync(n,{recursive:!0,force:!0});return}let s=So.join(e,".env.example");if(!ht.existsSync(s))return;let i=ht.readFileSync(s,"utf-8"),c="# Metering (usage-based billing)";if(!i.includes(c)){let a=["","# =============================================",c,"# 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(`
|
|
1648
|
+
`);ht.appendFileSync(s,a)}}import re from"fs";import M from"path";var Eo=[{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:""}],Up=Eo.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function vo(e){return Eo.find(t=>t.id===e)}var rn={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 mi(e){re.mkdirSync(e,{recursive:!0})}function sn(e,t){let o=M.join(e,"package.json");if(!re.existsSync(o))return;let n;try{n=JSON.parse(re.readFileSync(o,"utf-8"))}catch(c){console.error(`Failed to parse ${o}:`,c);return}let s={...n.dependencies??{}};for(let[c,a]of Object.entries(t))s[c]||(s[c]=a);let i={...n,dependencies:s};re.writeFileSync(o,JSON.stringify(i,null,2)+`
|
|
1649
|
+
`)}function te(e,t){mi(M.dirname(e)),re.writeFileSync(e,t)}function Et(e,t){let o=M.join(e,".env.example");re.existsSync(o)?re.appendFileSync(o,`
|
|
1650
|
+
`+t):re.writeFileSync(o,t)}function gi(e){let t=`import * as Sentry from "@sentry/nextjs";
|
|
1651
1651
|
|
|
1652
1652
|
Sentry.init({
|
|
1653
1653
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
@@ -1690,14 +1690,14 @@ export async function register() {
|
|
|
1690
1690
|
}
|
|
1691
1691
|
|
|
1692
1692
|
export { captureRequestError as onRequestError } from "@sentry/nextjs";
|
|
1693
|
-
`;te(M.join(e,"sentry.client.config.ts"),t),te(M.join(e,"sentry.server.config.ts"),o),te(M.join(e,"sentry.edge.config.ts"),n),te(M.join(e,"instrumentation.ts"),r)}function
|
|
1693
|
+
`;te(M.join(e,"sentry.client.config.ts"),t),te(M.join(e,"sentry.server.config.ts"),o),te(M.join(e,"sentry.edge.config.ts"),n),te(M.join(e,"instrumentation.ts"),r)}function fi(){return`# Sentry \u2014 error tracking + performance monitoring
|
|
1694
1694
|
# Get your DSN from https://sentry.io/settings/projects/<project>/keys/
|
|
1695
1695
|
SENTRY_DSN=
|
|
1696
1696
|
NEXT_PUBLIC_SENTRY_DSN=
|
|
1697
1697
|
SENTRY_ORG=
|
|
1698
1698
|
SENTRY_PROJECT=
|
|
1699
1699
|
SENTRY_AUTH_TOKEN=
|
|
1700
|
-
`}function
|
|
1700
|
+
`}function yi(e){let t=`/**
|
|
1701
1701
|
* Next.js instrumentation hook \u2014 wires Datadog APM (dd-trace) into Node runtime.
|
|
1702
1702
|
* Edge runtime is not supported by dd-trace; only nodejs is initialized.
|
|
1703
1703
|
*/
|
|
@@ -1745,7 +1745,7 @@ export function initDatadogRum(): void {
|
|
|
1745
1745
|
});
|
|
1746
1746
|
initialized = true;
|
|
1747
1747
|
}
|
|
1748
|
-
`;te(M.join(e,"instrumentation.ts"),t),te(M.join(e,"src","lib","monitoring","datadog-rum.ts"),o)}function
|
|
1748
|
+
`;te(M.join(e,"instrumentation.ts"),t),te(M.join(e,"src","lib","monitoring","datadog-rum.ts"),o)}function Si(){return`# Datadog \u2014 APM + RUM
|
|
1749
1749
|
# Server APM (dd-trace)
|
|
1750
1750
|
DD_API_KEY=
|
|
1751
1751
|
DD_APP_KEY=
|
|
@@ -1760,7 +1760,7 @@ NEXT_PUBLIC_DD_SITE=datadoghq.com
|
|
|
1760
1760
|
NEXT_PUBLIC_DD_SERVICE=sailor-web
|
|
1761
1761
|
NEXT_PUBLIC_DD_ENV=production
|
|
1762
1762
|
NEXT_PUBLIC_DD_VERSION=
|
|
1763
|
-
`}function
|
|
1763
|
+
`}function hi(e){let t=`/**
|
|
1764
1764
|
* Bugsnag server-side initialization.
|
|
1765
1765
|
* Import this once at the top of your server entrypoint or a shared init module.
|
|
1766
1766
|
*/
|
|
@@ -1809,11 +1809,11 @@ export function getBugsnagErrorBoundary() {
|
|
|
1809
1809
|
errorBoundary = plugin.createErrorBoundary(React) as typeof errorBoundary;
|
|
1810
1810
|
return errorBoundary;
|
|
1811
1811
|
}
|
|
1812
|
-
`;te(M.join(e,"src","lib","monitoring","bugsnag.server.ts"),t),te(M.join(e,"src","lib","monitoring","bugsnag.client.tsx"),o)}function
|
|
1812
|
+
`;te(M.join(e,"src","lib","monitoring","bugsnag.server.ts"),t),te(M.join(e,"src","lib","monitoring","bugsnag.client.tsx"),o)}function Ei(){return`# Bugsnag \u2014 error tracking
|
|
1813
1813
|
BUGSNAG_API_KEY=
|
|
1814
1814
|
NEXT_PUBLIC_BUGSNAG_API_KEY=
|
|
1815
1815
|
APP_VERSION=
|
|
1816
|
-
`}function
|
|
1816
|
+
`}function vi(e){te(M.join(e,"src","components","monitoring","ArmsScript.tsx"),`"use client";
|
|
1817
1817
|
/**
|
|
1818
1818
|
* \u963F\u91CC\u4E91 ARMS (Application Real-time Monitoring Service) browser agent.
|
|
1819
1819
|
* Injects the official JS agent via next/script.
|
|
@@ -1834,11 +1834,11 @@ export function ArmsScript() {
|
|
|
1834
1834
|
/>
|
|
1835
1835
|
);
|
|
1836
1836
|
}
|
|
1837
|
-
`)}function
|
|
1837
|
+
`)}function _i(){return`# \u963F\u91CC\u4E91 ARMS \u2014 APM (\u4E2D\u56FD\u533A)
|
|
1838
1838
|
ALIYUN_ARMS_LICENSE_KEY=
|
|
1839
1839
|
ALIYUN_ARMS_APP_NAME=
|
|
1840
1840
|
NEXT_PUBLIC_ARMS_PID=
|
|
1841
|
-
`}function
|
|
1841
|
+
`}function bi(e){te(M.join(e,"src","components","monitoring","TingYunScript.tsx"),`"use client";
|
|
1842
1842
|
/**
|
|
1843
1843
|
* \u542C\u4E91 Browser Agent (\u524D\u7AEF\u6027\u80FD\u76D1\u63A7).
|
|
1844
1844
|
* Docs: https://www.tingyun.com/docs
|
|
@@ -1856,12 +1856,12 @@ export function TingYunScript() {
|
|
|
1856
1856
|
/>
|
|
1857
1857
|
);
|
|
1858
1858
|
}
|
|
1859
|
-
`)}function
|
|
1859
|
+
`)}function Pi(){return`# \u542C\u4E91 TingYun \u2014 APM (\u4E2D\u56FD\u533A)
|
|
1860
1860
|
TINGYUN_LICENSE_KEY=
|
|
1861
1861
|
NEXT_PUBLIC_TINGYUN_APP_KEY=
|
|
1862
|
-
`}async function
|
|
1863
|
-
`)}}import se from"fs";import
|
|
1864
|
-
`+t):se.writeFileSync(o,t)}function
|
|
1862
|
+
`}async function _o(e,t,o="global"){let n=vo(t);if(!n||n.id==="none")return;let r=M.join(e,"apps","web");if(!re.existsSync(r))return;let s=n.id;try{switch(s){case"sentry":gi(r),sn(r,rn.sentry),Et(e,fi());return;case"datadog":yi(r),sn(r,rn.datadog),Et(e,Si());return;case"bugsnag":hi(r),sn(r,rn.bugsnag),Et(e,Ei());return;case"aliyun-arms":vi(r),Et(e,_i());return;case"tingyun":bi(r),Et(e,Pi());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 Vt=[{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 bo(e){return Vt.find(t=>t.id===e)}var Vp=Vt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Po(e,t,o="global"){let n=bo(t);if(!n)throw new Error(`Unknown notifications provider "${t}". Valid ids: ${Vt.map(r=>r.id).join(", ")}`);if(n.id==="none"){H(e,"notifications");return}n.envVars.length>0&&Q(e,{category:"Notifications",name:n.name,envVars:n.envVars,docs:n.docs}),Z(e,"NOTIFICATIONS_PROVIDER",n.id)}import an from"fs";import Ci from"path";async function Co(e,t){let o=Ci.join(e,"package.json");if(an.existsSync(o)){let n=an.readFileSync(o,"utf8"),r=JSON.parse(n);r.name=t,r.version="0.1.0",an.writeFileSync(o,JSON.stringify(r,null,2)+`
|
|
1863
|
+
`)}}import Bt from"fs";import st from"path";import{fileURLToPath as Ai}from"url";var wi=Ai(import.meta.url),Ao=st.dirname(wi);function Ii(){let e=[st.join(Ao,"..","templates","orm","drizzle"),st.join(Ao,"..","..","templates","orm","drizzle")];for(let t of e)if(Bt.existsSync(t))return t;return null}function wo(e,t){Bt.mkdirSync(t,{recursive:!0});for(let o of Bt.readdirSync(e,{withFileTypes:!0})){let n=st.join(e,o.name),r=st.join(t,o.name);o.isDirectory()?wo(n,r):Bt.copyFileSync(n,r)}}async function Io(e,t,o){if(t!=="drizzle")return{applied:!1};if(o!=="postgresql")return{applied:!1,reason:`Drizzle template currently targets Postgres only; --db=${o} keeps Prisma as sole ORM`};let n=Ii();if(!n)return{applied:!1,reason:"Drizzle template directory not found in package \u2014 bundling error"};let r=st.join(e,"packages","platform","db-drizzle");return wo(n,r),{applied:!0}}import se from"fs";import vt from"path";var Ri=["stripe","lemonsqueezy","chinapay","polar"];function Ro(e){se.existsSync(e)&&se.rmSync(e,{recursive:!0,force:!0})}function ki(e){switch(e){case"stripe":return["stripe"];case"lemon":return["lemonsqueezy"];case"wechat":case"alipay":return["chinapay"]}}function xi(e,t){let o=vt.join(e,".env.example");se.existsSync(o)?se.appendFileSync(o,`
|
|
1864
|
+
`+t):se.writeFileSync(o,t)}function Ti(e){switch(e){case"stripe":return`# Stripe
|
|
1865
1865
|
STRIPE_SECRET_KEY=
|
|
1866
1866
|
STRIPE_WEBHOOK_SECRET=
|
|
1867
1867
|
`;case"lemon":return`# Lemon Squeezy
|
|
@@ -1876,13 +1876,13 @@ CHINAPAY_WECHAT_ENABLED=true
|
|
|
1876
1876
|
CHINAPAY_APP_ID=
|
|
1877
1877
|
CHINAPAY_APP_SECRET=
|
|
1878
1878
|
CHINAPAY_ALIPAY_ENABLED=true
|
|
1879
|
-
`}}async function
|
|
1880
|
-
`),n=[],r=e.includes("#"),s=0;for(;s<o.length;){let i=o[s],c=i.match(
|
|
1881
|
-
`),t)}function
|
|
1879
|
+
`}}async function ko(e,t){let o=vt.join(e,"packages","commerce","billing");if(!se.existsSync(o))return;if(t==="none"){Ro(o);return}let n=vt.join(o,"src"),r=new Set(ki(t));for(let i of Ri)r.has(i)||Ro(vt.join(n,i));let s=vt.join(n,"checkout","types.ts");if(se.existsSync(s)){let i=t==="stripe"?"stripe":t==="lemon"?"lemonsqueezy":"chinapay",c=se.readFileSync(s,"utf8"),a=c.replace(/export type CheckoutProviderType\s*=\s*[^;]+;/,`export type CheckoutProviderType = "${i}";`);a!==c&&se.writeFileSync(s,a)}xi(e,Ti(t))}import ue from"fs";import it from"path";var No=new Set(["siliconflow","volcengine-ark","bailian","moonshot"]),cn=/@sailor:([^\s*]+)/;function Ni(e,t){let{providerIds:o,customEndpoint:n}=t;if(e.startsWith("provider:")){let r=e.slice(9);return{marker:e,keep:o.includes(r)}}if(e==="cn-compatible"){let r=o.some(s=>No.has(s));return{marker:e,keep:r||!!n}}if(e.startsWith("cn-instance:")){let r=e.slice(12);return{marker:e,keep:o.includes(r)}}return e==="custom-endpoint"?{marker:e,keep:!!n}:e==="registry-entries"?{marker:e,keep:!0}:{marker:e,keep:!0}}function xo(e){return e.replace(/\s*\/\/\s*@sailor:[^\n]*$/,"")}function To(e){return e.replace(/\s*#\s*@sailor:[^\n]*$/,"")}function Oo(e,t){let o=e.split(`
|
|
1880
|
+
`),n=[],r=e.includes("#"),s=0;for(;s<o.length;){let i=o[s],c=i.match(cn);if(!c){n.push(i),s++;continue}let{marker:a,keep:l}=Ni(c[1],t);if(a==="registry-entries"){n.push(r?To(i):xo(i)),s++;continue}if(l){let m=r?To(i):xo(i);m.trim().length>0&&n.push(m);let S=s+1;for(;S<o.length&&o[S].trim()!==""&&!cn.test(o[S]);)n.push(o[S]),S++;S<o.length&&o[S].trim()===""&&(n.push(o[S]),S++),s=S}else{let m=s+1;for(;m<o.length&&o[m].trim()!==""&&!cn.test(o[m]);)m++;m<o.length&&o[m].trim()===""&&m++,s=m}}return Oi(n.join(`
|
|
1881
|
+
`),t)}function Oi(e,t){let{providerIds:o,customEndpoint:n}=t,r=new Set;for(let l of o)["openai","anthropic","google","mistral","xai"].includes(l)&&r.add(l),r.add(l.replace(/-/g,"_")),r.add(l);n&&r.add("custom");let s=/createProviderRegistry\(\{([\s\S]*?)\}\)/,i=e.match(s);if(!i)return e;let a=i[1].split(`
|
|
1882
1882
|
`).filter(l=>{let m=l.trim();if(m===""||m.startsWith("//"))return!0;let S=m.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*,?$/);return S?r.has(S[1]):!0}).join(`
|
|
1883
|
-
`);return e.replace(s,`createProviderRegistry({${a}})`)}function
|
|
1884
|
-
`+r);let i={ai:"latest"},{providerIds:c,customEndpoint:a}=t;(c.includes("openai")||a||c.some(m=>
|
|
1885
|
-
`)}}import f from"fs";import d from"path";function
|
|
1883
|
+
`);return e.replace(s,`createProviderRegistry({${a}})`)}function Ui(e,t){let o=it.join(t,"registry.ts.template"),n=ue.readFileSync(o,"utf-8");return Oo(n,e)}function Li(e,t){let o=it.join(t,".env.example.template"),n=ue.readFileSync(o,"utf-8");return Oo(n,e)}async function Uo(e,t,o){let n=Ui(t,o),r=Li(t,o),s=it.join(e,"packages/ai/agents/src");await ue.promises.mkdir(s,{recursive:!0}),await ue.promises.writeFile(it.join(s,"registry.ts"),n),await ue.promises.appendFile(it.join(e,".env.example"),`
|
|
1884
|
+
`+r);let i={ai:"latest"},{providerIds:c,customEndpoint:a}=t;(c.includes("openai")||a||c.some(m=>No.has(m)))&&(i["@ai-sdk/openai"]="latest",i["@ai-sdk/openai-compatible"]="latest"),c.includes("anthropic")&&(i["@ai-sdk/anthropic"]="latest"),c.includes("google")&&(i["@ai-sdk/google"]="latest"),c.includes("mistral")&&(i["@ai-sdk/mistral"]="latest"),c.includes("xai")&&(i["@ai-sdk/openai"]="latest");let l=it.join(e,"package.json");if(ue.existsSync(l)){let m=JSON.parse(await ue.promises.readFile(l,"utf-8"));m.dependencies||(m.dependencies={});for(let[S,T]of Object.entries(i))m.dependencies[S]=T;await ue.promises.writeFile(l,JSON.stringify(m,null,2)+`
|
|
1885
|
+
`)}}import f from"fs";import d from"path";function Lo(e,t){if(!f.existsSync(e))return;let o=f.readdirSync(e);for(let n of o){let r=d.join(e,n);f.statSync(r).isDirectory()?Lo(r,t):t(r)}}async function Mo(e,t){let o=d.join(e,"package.json");if(!f.existsSync(o))return;let n=JSON.parse(f.readFileSync(o,"utf8"));if(t.orm!=="prisma"){let r=d.join(e,"packages","db");if(f.existsSync(r)){let s=d.join(r,"prisma");f.existsSync(s)&&f.rmSync(s,{recursive:!0,force:!0});let i=d.join(r,"package.json");if(f.existsSync(i)){let a=JSON.parse(f.readFileSync(i,"utf8"));a.dependencies&&delete a.dependencies["@prisma/client"],a.devDependencies&&delete a.devDependencies.prisma,f.writeFileSync(i,JSON.stringify(a,null,2)+`
|
|
1886
1886
|
`)}let c=d.join(r,"src","index.ts");f.existsSync(c)&&f.writeFileSync(c,`
|
|
1887
1887
|
export const prisma = {} as any;
|
|
1888
1888
|
export const db = {} as any;
|
|
@@ -1929,7 +1929,7 @@ export type UsageAggregate = any;
|
|
|
1929
1929
|
export type UsageLedgerEntry = any;
|
|
1930
1930
|
export type UsageType = any;
|
|
1931
1931
|
export type UsageLedgerSource = any;
|
|
1932
|
-
`)}n.scripts?.postinstall&&(n.scripts.postinstall=n.scripts.postinstall.replace(/prisma generate/g,'echo "ORM Disabled"'))}if(!t.i18n){let r=d.join(e,"apps");if(f.existsSync(r)){let s=f.readdirSync(r);for(let i of s){let c=d.join(r,i);if(!f.statSync(c).isDirectory())continue;let a=d.join(c,"src","app");if(!f.existsSync(a))continue;["[lang]","[locale]"].forEach(
|
|
1932
|
+
`)}n.scripts?.postinstall&&(n.scripts.postinstall=n.scripts.postinstall.replace(/prisma generate/g,'echo "ORM Disabled"'))}if(!t.i18n){let r=d.join(e,"apps");if(f.existsSync(r)){let s=f.readdirSync(r);for(let i of s){let c=d.join(r,i);if(!f.statSync(c).isDirectory())continue;let a=d.join(c,"src","app");if(!f.existsSync(a))continue;["[lang]","[locale]"].forEach(_=>{let u=d.join(a,_);if(f.existsSync(u)){let U=f.readdirSync(u);for(let At of U)f.renameSync(d.join(u,At),d.join(a,At));f.rmdirSync(u)}}),["src/middleware.ts","middleware.ts"].forEach(_=>{let u=d.join(c,_);f.existsSync(u)&&f.rmSync(u)});let l=d.join(c,"src","lib");f.existsSync(l)||f.mkdirSync(l,{recursive:!0});let m=d.join(l,"mock-i18n.ts");f.writeFileSync(m,`
|
|
1933
1933
|
import React from 'react';
|
|
1934
1934
|
export function useTranslations(namespace?: string): any { return (key: string) => key; }
|
|
1935
1935
|
export function useLocale() { return 'en'; }
|
|
@@ -1947,7 +1947,7 @@ export const useRouter = () => ({ push: (...args: any[]) => {}, replace: (...arg
|
|
|
1947
1947
|
export default function createMiddleware(...args: any[]) { return (req: any) => req; }
|
|
1948
1948
|
export function createNavigation(...args: any[]) { return { Link, redirect, usePathname, useRouter, getPathname: () => '/' }; }
|
|
1949
1949
|
export function defineRouting<T>(config: T): T { return config; }
|
|
1950
|
-
`);let
|
|
1950
|
+
`);let T=d.join(l,"mock-i18n.d.ts");f.writeFileSync(T,`declare module '@/lib/mock-i18n' {
|
|
1951
1951
|
export function useTranslations(namespace?: string): any;
|
|
1952
1952
|
export function useLocale(): string;
|
|
1953
1953
|
export const NextIntlClientProvider: React.FC<any>;
|
|
@@ -1968,16 +1968,16 @@ export function defineRouting<T>(config: T): T { return config; }
|
|
|
1968
1968
|
`),f.writeFileSync(d.join(l,"mock-i18n-index.ts"),`
|
|
1969
1969
|
export * from './mock-i18n';
|
|
1970
1970
|
export { default } from './mock-i18n';
|
|
1971
|
-
`);let
|
|
1971
|
+
`);let N=d.join(c,"tsconfig.json");if(f.existsSync(N))try{let _=f.readFileSync(N,"utf8");if(_.includes('"paths": {')){let u=_.replace(/"paths":\s*\{/,`"paths": {
|
|
1972
1972
|
"next-intl": ["./src/lib/mock-i18n"],
|
|
1973
|
-
"next-intl/*": ["./src/lib/mock-i18n"],`);f.writeFileSync(
|
|
1973
|
+
"next-intl/*": ["./src/lib/mock-i18n"],`);f.writeFileSync(N,u)}}catch{}let D=d.join(c,"src");f.existsSync(D)&&Lo(D,_=>{if(!_.endsWith(".ts")&&!_.endsWith(".tsx"))return;let u=f.readFileSync(_,"utf8"),U=!1;_.endsWith("src/app/layout.tsx")&&u.includes("../providers")&&(u=u.replace(/from\s+['"]\.\.\/providers['"]/g,"from './providers'"),U=!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"),U=!0),u.match(/lang\s*:\s*any/)&&(u=u.replace(/lang\s*:\s*any/g,"lang: string"),U=!0),u.match(/requestLocale\s*:\s*any/)&&(u=u.replace(/requestLocale\s*:\s*any/g,"requestLocale: string"),U=!0),u.match(/\(locale\s*=\s*(.*)\)/)&&(u=u.replace(/\(locale\s*=\s*(.*)\)/g,"() "),U=!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 })"),U=!0),(_.endsWith("src/app/layout.tsx")||_.endsWith("src/app/sitemap.ts")||_.endsWith("src/app/robots.ts")||_.endsWith("src/components/ui/locale-switcher.tsx")||_.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) =>"),U=!0),_.endsWith("src/i18n/request.ts")&&(u=u.replace(/async\s*\(\{\s*requestLocale\s*\}\)/g,"async ({ requestLocale }: any)"),U=!0),(u.match(/locale\s*:/)||u.match(/lang\s*:/))&&(U=!0),U&&f.writeFileSync(_,u)});let ae=d.join(c,"package.json");if(f.existsSync(ae)){let _=JSON.parse(f.readFileSync(ae,"utf8"));_.dependencies&&delete _.dependencies["next-intl"],f.writeFileSync(ae,JSON.stringify(_,null,2)+`
|
|
1974
1974
|
`)}}}}f.writeFileSync(o,JSON.stringify(n,null,2)+`
|
|
1975
|
-
`)}function
|
|
1976
|
-
`)}function
|
|
1977
|
-
`);
|
|
1978
|
-
`)}async function
|
|
1979
|
-
`);
|
|
1980
|
-
`)}async function
|
|
1975
|
+
`)}function v(e){try{f.rmSync(e,{recursive:!0,force:!0})}catch{}}function Mi(e,t){if(!f.existsSync(e))return;let o;try{o=JSON.parse(f.readFileSync(e,"utf8"))}catch{return}let n=!1;if(o.dependencies&&t in o.dependencies){let{[t]:r,...s}=o.dependencies;o={...o,dependencies:s},n=!0}if(o.devDependencies&&t in o.devDependencies){let{[t]:r,...s}=o.devDependencies;o={...o,devDependencies:s},n=!0}n&&f.writeFileSync(e,JSON.stringify(o,null,2)+`
|
|
1976
|
+
`)}function Do(e,t){t.cronJobs||(v(d.join(e,"apps/web/src/app/api/cron")),v(d.join(e,"packages/integrations/queue/src/scheduled"))),t.auditLog||(v(d.join(e,"apps/web/src/app/[locale]/(app)/settings/audit-log")),v(d.join(e,"apps/web/src/app/api/audit-logs")),v(d.join(e,"apps/web/src/components/audit"))),t.apiKeys||(v(d.join(e,"apps/web/src/app/[locale]/(app)/settings/api-keys")),v(d.join(e,"apps/web/src/app/api/api-keys")),v(d.join(e,"apps/web/src/components/api-keys"))),t.webhooks||(v(d.join(e,"apps/web/src/app/[locale]/(app)/settings/webhooks")),v(d.join(e,"apps/web/src/app/api/webhooks")),v(d.join(e,"apps/web/src/components/webhooks")),v(d.join(e,"packages/integrations/webhooks"))),t.commandPalette||(v(d.join(e,"apps/web/src/components/command-palette")),v(d.join(e,"apps/web/src/app/[locale]/providers/command-palette-mount.tsx"))),t.cookieConsent||(v(d.join(e,"apps/landing-page/src/components/cookie-consent-banner.tsx")),v(d.join(e,"apps/landing-page/src/components/__tests__/cookie-consent-banner.test.tsx")),v(d.join(e,"apps/landing-page/src/lib/cookie-consent.ts")),v(d.join(e,"apps/landing-page/src/lib/__tests__/cookie-consent.test.ts")),v(d.join(e,"apps/web/src/app/api/cookie-consent"))),t.legalPages||(v(d.join(e,"apps/landing-page/src/app/[lang]/(legal)/legal")),v(d.join(e,"apps/landing-page/src/lib/legal-documents.ts")),v(d.join(e,"apps/landing-page/src/lib/__tests__/legal-documents.test.ts"))),t.chinaCompliance||(v(d.join(e,"packages/ops/china-compliance")),v(d.join(e,"apps/landing-page/src/components/icp-footer.tsx")),v(d.join(e,"apps/landing-page/src/components/__tests__/icp-footer.test.tsx")),v(d.join(e,"apps/web/src/components/auth/wechat-login-button.tsx")),v(d.join(e,"apps/web/src/components/auth/__tests__/wechat-login-button.test.tsx")),Mi(d.join(e,"apps/web/package.json"),"@nebutra/china-compliance"))}import{getSchema as Di,printSchema as Fi}from"@mrleebo/prisma-ast";var ji=/^\/\/\/\s*@conditional\(([A-Za-z][A-Za-z0-9_-]*)=([^)]+)\)\s*$/;function Vi(e){return e.type==="comment"&&e.text.trim().startsWith("///")}function Bi(e){let t=ji.exec(e.trim());if(!t)return null;let[,o,n]=t,r=n.split("|").map(s=>s.trim()).filter(s=>s.length>0);return{flag:o,values:r}}function Ki(e,t){for(let o=t-1;o>=0;o--){let n=e[o];if(n.type==="comment"){let r=Bi(n.text);if(r)return{annIndex:o,parsed:r};continue}break}return null}function pn(e,t){let o=Di(e),n=new Set;for(let c=0;c<o.list.length;c++){if(o.list[c].type!=="model")continue;let l=Ki(o.list,c);if(!l)continue;let m=t[l.parsed.flag];if(m!==void 0&&l.parsed.values.includes(m))n.add(l.annIndex);else{n.add(c);for(let N=c-1;N>=0;N--){let D=o.list[N];if(D.type==="comment"&&Vi(D)){n.add(N);continue}break}let T=o.list[c+1];T&&T.type==="break"&&n.add(c+1)}}let r=o.list.filter((c,a)=>!n.has(a)),s=[];for(let c of r){let a=s[s.length-1];c.type==="break"&&a&&a.type==="break"||s.push(c)}return Fi({type:"schema",list:s})}import at from"fs";import jo from"path";var Kt=[{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 Fo(e){return Kt.find(t=>t.id===e)}var pl=Kt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Vo(e){return at.existsSync(e)?at.readFileSync(e,"utf-8"):null}function $i(e,t){let o=jo.join(e,".env.example"),n=Vo(o);if(n===null)return;let r=`# Queue: ${t.name}`;if(n.includes(r))return;let s=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
1977
|
+
`);at.appendFileSync(o,s)}function Yi(e,t,o){let n=jo.join(e,".env.example"),r=Vo(n);if(r===null)return;let s=new RegExp(`^${t}=.*$`,"m");s.test(r)?at.writeFileSync(n,r.replace(s,`${t}="${o}"`)):at.appendFileSync(n,`${t}="${o}"
|
|
1978
|
+
`)}async function Bo(e,t,o){let n=Ae(e,"queue");if(t==="none"){n&&at.rmSync(n,{recursive:!0,force:!0});return}let r=Fo(t);if(!r)throw new Error(`Unknown queue provider "${t}". Valid ids: ${Kt.map(s=>s.id).join(", ")}`);n&&($i(e,r),Yi(e,"QUEUE_PROVIDER",t))}import ie from"fs";import me from"path";import{fileURLToPath as qi}from"url";var Gi=qi(import.meta.url),Ko=me.dirname(Gi),Hi=/\.(md|mdx|json|ts|tsx|js|mjs|cjs|yaml|yml|css|html|toml|py)$/,Wi=["backends/gateway","infra","e2e","tests"];function Xi(){let e=[me.join(Ko,"..","templates"),me.join(Ko,"..","..","templates")];for(let t of e)if(ie.existsSync(t))return t;return e[0]}async function $o(e,t){let o=Xi(),n=[],r=[],s=[...Wi,...t.withWorkflows?["workflows"]:[],...t.withPythonBackend?["backends/python"]:[]];for(let i of s){let c=me.join(o,i);if(!ie.existsSync(c)){r.push(i);continue}let a=me.join(e,i);Yo(c,a),qo(a,{"{PRODUCT_NAME}":t.projectName}),n.push(i)}return{applied:n,skipped:r}}function Yo(e,t){ie.mkdirSync(t,{recursive:!0});for(let o of ie.readdirSync(e,{withFileTypes:!0})){let n=me.join(e,o.name),r=me.join(t,o.name);o.isDirectory()?Yo(n,r):ie.existsSync(r)||ie.copyFileSync(n,r)}}function qo(e,t){for(let o of ie.readdirSync(e,{withFileTypes:!0})){let n=me.join(e,o.name);if(o.isDirectory()){qo(n,t);continue}if(!Hi.test(o.name))continue;let r=ie.readFileSync(n,"utf-8"),s=!1;for(let[i,c]of Object.entries(t))r.includes(i)&&(r=r.split(i).join(c),s=!0);s&&ie.writeFileSync(n,r)}}import ct from"fs";import Ho from"path";var $t=[{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 Go(e){return $t.find(t=>t.id===e)}var vl=$t.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Wo(e){return ct.existsSync(e)?ct.readFileSync(e,"utf-8"):null}function zi(e,t){let o=Ho.join(e,".env.example"),n=Wo(o);if(n===null)return;let r=`# Search: ${t.name}`;if(n.includes(r))return;let s=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
1979
|
+
`);ct.appendFileSync(o,s)}function Ji(e,t,o){let n=Ho.join(e,".env.example"),r=Wo(n);if(r===null)return;let s=new RegExp(`^${t}=.*$`,"m");s.test(r)?ct.writeFileSync(n,r.replace(s,`${t}="${o}"`)):ct.appendFileSync(n,`${t}="${o}"
|
|
1980
|
+
`)}async function Xo(e,t,o){let n=Ae(e,"search");if(t==="none"){n&&ct.rmSync(n,{recursive:!0,force:!0});return}let r=Go(t);if(!r)throw new Error(`Unknown search provider "${t}". Valid ids: ${$t.map(s=>s.id).join(", ")}`);n&&(zi(e,r),Ji(e,"SEARCH_PROVIDER",t))}import ge from"fs";import Oe from"path";function Qi(e){let t=[Oe.join(e,"packages","db"),Oe.join(e,"apps","web")];for(let o of t){let n=Oe.join(o,"prisma","schema.prisma");if(ge.existsSync(n))return{packageDir:o,seedPath:Oe.join(o,"prisma","seed.ts"),packageJsonPath:Oe.join(o,"package.json")}}return null}function Zi(e){try{let t=ge.readFileSync(e,"utf8"),o=new Set,n=/^\s*model\s+(\w+)\s*\{/gm,r=n.exec(t);for(;r;)o.add(r[1]),r=n.exec(t);return o}catch{return new Set}}function ea(e,t){return!t||e==="none"?` // Skipping admin user \u2014 auth=${JSON.stringify(e)} (or no User model).`:e==="clerk"?` try {
|
|
1981
1981
|
const admin = await prisma.user.create({
|
|
1982
1982
|
data: {
|
|
1983
1983
|
clerkId: "user_mock_admin",
|
|
@@ -2008,7 +2008,7 @@ export { default } from './mock-i18n';
|
|
|
2008
2008
|
console.log("\\u2713 Created admin user:", admin.email);
|
|
2009
2009
|
} catch (err) {
|
|
2010
2010
|
console.warn("\\u26A0 Admin user creation skipped:", (err as Error).message);
|
|
2011
|
-
}`}function
|
|
2011
|
+
}`}function ta(e,t){let o=t.has("Tenant"),n=t.has("User"),r=t.has("Project"),s=ea(e,n),i=o?` let tenants: Array<{ id: string; name: string }> = [];
|
|
2012
2012
|
try {
|
|
2013
2013
|
tenants = await Promise.all([
|
|
2014
2014
|
prisma.tenant.create({
|
|
@@ -2077,12 +2077,12 @@ main()
|
|
|
2077
2077
|
.finally(async () => {
|
|
2078
2078
|
await prisma.$disconnect();
|
|
2079
2079
|
});
|
|
2080
|
-
`}function
|
|
2081
|
-
`)}catch(t){let o=t instanceof Error?t.message:String(t);console.warn(`[create-sailor] Failed to patch seed scripts in ${e}: ${o}`)}}async function
|
|
2082
|
-
`+t):
|
|
2080
|
+
`}function na(e){if(ge.existsSync(e))try{let t=ge.readFileSync(e,"utf8"),o=JSON.parse(t),n=o.scripts??{},r=!1;n["db:seed"]||(n["db:seed"]="prisma db seed",r=!0);let s=o.prisma??{};if(s.seed||(s.seed="tsx prisma/seed.ts",r=!0),!r)return;let i={...o,scripts:n,prisma:s};ge.writeFileSync(e,JSON.stringify(i,null,2)+`
|
|
2081
|
+
`)}catch(t){let o=t instanceof Error?t.message:String(t);console.warn(`[create-sailor] Failed to patch seed scripts in ${e}: ${o}`)}}async function zo(e,t){let o=Qi(e);if(o)try{let n=Oe.join(o.packageDir,"prisma","schema.prisma"),r=Zi(n),s=ta(t,r),i=Oe.dirname(o.seedPath);ge.existsSync(i)||ge.mkdirSync(i,{recursive:!0}),ge.writeFileSync(o.seedPath,s),na(o.packageJsonPath)}catch(n){let r=n instanceof Error?n.message:String(n);throw console.error(`[create-sailor] Failed to generate seed data: ${r}`),new Error(`Unable to write prisma/seed.ts: ${r}`)}}import fe from"fs";import Ue from"path";var Yt=[{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:""}],xl=Yt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Jo(e){return Yt.find(t=>t.id===e)}var oa="@nebutra/sms",ra={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 ln(e,t){fe.mkdirSync(Ue.dirname(e),{recursive:!0}),fe.writeFileSync(e,t)}function sa(e,t){let o=Ue.join(e,".env.example");fe.existsSync(o)?fe.appendFileSync(o,`
|
|
2082
|
+
`+t):fe.writeFileSync(o,t)}function ia(e){if(e.envVars.length===0)return"";let t=`# SMS provider: ${e.name}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
2083
2083
|
`);return`${t}
|
|
2084
2084
|
${o}
|
|
2085
|
-
`}var
|
|
2085
|
+
`}var aa=`/**
|
|
2086
2086
|
* Inputs for the \`sendSms\` wrapper. Keep this surface portable across
|
|
2087
2087
|
* providers \u2014 provider-specific options belong in the wrapper itself.
|
|
2088
2088
|
*/
|
|
@@ -2102,7 +2102,7 @@ export interface SendSmsResult {
|
|
|
2102
2102
|
provider: string;
|
|
2103
2103
|
raw?: unknown;
|
|
2104
2104
|
}
|
|
2105
|
-
`;function
|
|
2105
|
+
`;function ca(){return`import twilio from "twilio";
|
|
2106
2106
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2107
2107
|
|
|
2108
2108
|
let client: ReturnType<typeof twilio> | null = null;
|
|
@@ -2137,7 +2137,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2137
2137
|
}
|
|
2138
2138
|
|
|
2139
2139
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2140
|
-
`}function
|
|
2140
|
+
`}function pa(){return`import messagebird from "messagebird";
|
|
2141
2141
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2142
2142
|
|
|
2143
2143
|
let client: ReturnType<typeof messagebird> | null = null;
|
|
@@ -2175,7 +2175,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2175
2175
|
}
|
|
2176
2176
|
|
|
2177
2177
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2178
|
-
`}function
|
|
2178
|
+
`}function la(){return`import plivo from "plivo";
|
|
2179
2179
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2180
2180
|
|
|
2181
2181
|
let client: InstanceType<typeof plivo.Client> | null = null;
|
|
@@ -2214,7 +2214,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2214
2214
|
}
|
|
2215
2215
|
|
|
2216
2216
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2217
|
-
`}function
|
|
2217
|
+
`}function da(){return`import Dysmsapi, { SendSmsRequest } from "@alicloud/dysmsapi20170525";
|
|
2218
2218
|
import * as $OpenApi from "@alicloud/openapi-client";
|
|
2219
2219
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2220
2220
|
|
|
@@ -2263,7 +2263,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2263
2263
|
}
|
|
2264
2264
|
|
|
2265
2265
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2266
|
-
`}function
|
|
2266
|
+
`}function ua(){return`import * as tencentcloud from "tencentcloud-sdk-nodejs-sms";
|
|
2267
2267
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2268
2268
|
|
|
2269
2269
|
const SmsClient = tencentcloud.sms.v20210111.Client;
|
|
@@ -2328,7 +2328,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2328
2328
|
}
|
|
2329
2329
|
|
|
2330
2330
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2331
|
-
`}function
|
|
2331
|
+
`}function ma(){return`// @ts-expect-error \u2014 yunpian-nodejs-sdk ships no type declarations
|
|
2332
2332
|
import Yunpian from "yunpian-nodejs-sdk";
|
|
2333
2333
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2334
2334
|
|
|
@@ -2380,13 +2380,13 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2380
2380
|
}
|
|
2381
2381
|
|
|
2382
2382
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2383
|
-
`}function
|
|
2384
|
-
`}}function
|
|
2385
|
-
`}async function
|
|
2386
|
-
`+t):
|
|
2383
|
+
`}function ga(e){switch(e){case"twilio":return ca();case"messagebird":return pa();case"plivo":return la();case"aliyun-sms":return da();case"tencent-sms":return ua();case"yunpian":return ma();case"none":return`export {};
|
|
2384
|
+
`}}function fa(e){let t={};for(let n of e.pkgs??[]){let r=ra[n];r&&(t[n]=r)}let o={name:oa,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(o,null,2)+`
|
|
2385
|
+
`}async function Qo(e,t,o="global"){if(t==="none")return;let n=Jo(t);if(!n)throw new Error(`Unknown SMS provider "${t}". Valid ids: ${Yt.map(i=>i.id).join(", ")}`);let r=Ue.join(e,"packages","sms");if(!fe.existsSync(r))return;let s=Ue.join(r,"src");fe.existsSync(s)&&fe.rmSync(s,{recursive:!0,force:!0});try{ln(Ue.join(r,"package.json"),fa(n)),ln(Ue.join(s,"types.ts"),aa),ln(Ue.join(s,"index.ts"),ga(n.id)),sa(e,ia(n))}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 Me from"fs";import Le from"path";var _t=[{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 dn(e){let t=e==="supabase"?"supabase-storage":e;return _t.some(o=>o.id===t)?t:void 0}function Zo(e){let t=dn(e);return t?_t.find(o=>o.id===t):void 0}var Ml=_t.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});var De="@nebutra/storage";function un(e,t){Me.mkdirSync(Le.dirname(e),{recursive:!0}),Me.writeFileSync(e,t)}function ya(e,t){let o=Le.join(e,".env.example");Me.existsSync(o)?Me.appendFileSync(o,`
|
|
2386
|
+
`+t):Me.writeFileSync(o,t)}function Sa(e){if(e.envVars.length===0)return"";let t=`# Storage provider: ${e.name}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
2387
2387
|
`);return`${t}
|
|
2388
2388
|
${o}
|
|
2389
|
-
`}var
|
|
2389
|
+
`}var ha=`/**
|
|
2390
2390
|
* Inputs for the \`uploadFile\` wrapper. Keep this surface portable across
|
|
2391
2391
|
* providers \u2014 provider-specific options belong in the wrapper itself.
|
|
2392
2392
|
*/
|
|
@@ -2412,8 +2412,8 @@ export interface SignedUrlOptions {
|
|
|
2412
2412
|
operation?: "get" | "put";
|
|
2413
2413
|
contentType?: string;
|
|
2414
2414
|
}
|
|
2415
|
-
`;function
|
|
2416
|
-
* @${
|
|
2415
|
+
`;function Ea(){return`/**
|
|
2416
|
+
* @${De.slice(1)} \u2014 Cloudflare R2 via the S3-compatible API.
|
|
2417
2417
|
*
|
|
2418
2418
|
* Docs: https://developers.cloudflare.com/r2/api/s3/api/
|
|
2419
2419
|
*/
|
|
@@ -2476,8 +2476,8 @@ export async function getSignedUrl(
|
|
|
2476
2476
|
}
|
|
2477
2477
|
|
|
2478
2478
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2479
|
-
`}function
|
|
2480
|
-
* @${
|
|
2479
|
+
`}function va(){return`/**
|
|
2480
|
+
* @${De.slice(1)} \u2014 AWS S3 wrapper.
|
|
2481
2481
|
*
|
|
2482
2482
|
* Docs: https://docs.aws.amazon.com/s3/
|
|
2483
2483
|
*/
|
|
@@ -2541,8 +2541,8 @@ export async function getSignedUrl(
|
|
|
2541
2541
|
}
|
|
2542
2542
|
|
|
2543
2543
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2544
|
-
`}function
|
|
2545
|
-
* @${
|
|
2544
|
+
`}function _a(){return`/**
|
|
2545
|
+
* @${De.slice(1)} \u2014 Supabase Storage wrapper.
|
|
2546
2546
|
*
|
|
2547
2547
|
* Docs: https://supabase.com/docs/guides/storage
|
|
2548
2548
|
*/
|
|
@@ -2604,8 +2604,8 @@ export async function getSignedUrl(
|
|
|
2604
2604
|
}
|
|
2605
2605
|
|
|
2606
2606
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2607
|
-
`}function
|
|
2608
|
-
* @${
|
|
2607
|
+
`}function ba(){return`/**
|
|
2608
|
+
* @${De.slice(1)} \u2014 \u963F\u91CC\u4E91 OSS wrapper via ali-oss.
|
|
2609
2609
|
*
|
|
2610
2610
|
* Docs: https://help.aliyun.com/product/31815.html
|
|
2611
2611
|
*/
|
|
@@ -2651,8 +2651,8 @@ export async function getSignedUrl(
|
|
|
2651
2651
|
}
|
|
2652
2652
|
|
|
2653
2653
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2654
|
-
`}function
|
|
2655
|
-
* @${
|
|
2654
|
+
`}function Pa(){return`/**
|
|
2655
|
+
* @${De.slice(1)} \u2014 \u817E\u8BAF\u4E91 COS wrapper via cos-nodejs-sdk-v5.
|
|
2656
2656
|
*
|
|
2657
2657
|
* Docs: https://cloud.tencent.com/document/product/436
|
|
2658
2658
|
*/
|
|
@@ -2706,8 +2706,8 @@ export async function getSignedUrl(
|
|
|
2706
2706
|
}
|
|
2707
2707
|
|
|
2708
2708
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2709
|
-
`}function
|
|
2710
|
-
* @${
|
|
2709
|
+
`}function Ca(){return`/**
|
|
2710
|
+
* @${De.slice(1)} \u2014 \u4E03\u725B\u4E91 Kodo wrapper.
|
|
2711
2711
|
*
|
|
2712
2712
|
* Docs: https://developer.qiniu.com/kodo
|
|
2713
2713
|
*/
|
|
@@ -2774,9 +2774,9 @@ export async function getSignedUrl(
|
|
|
2774
2774
|
}
|
|
2775
2775
|
|
|
2776
2776
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2777
|
-
`}function
|
|
2778
|
-
`}}function
|
|
2779
|
-
`}async function
|
|
2777
|
+
`}function Aa(e){switch(e){case"r2":return Ea();case"s3":return va();case"supabase-storage":return _a();case"aliyun-oss":return ba();case"tencent-cos":return Pa();case"qiniu":return Ca();case"none":return`export {};
|
|
2778
|
+
`}}function wa(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 o={name:De,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(o,null,2)+`
|
|
2779
|
+
`}async function er(e,t,o){if(t==="none")return;let n=Zo(t);if(!n)throw new Error(`Unknown storage provider "${t}". Valid ids: ${_t.map(i=>i.id).join(", ")}`);let r=Le.join(e,"packages","storage"),s=Le.join(r,"src");Me.existsSync(s)&&Me.rmSync(s,{recursive:!0,force:!0}),un(Le.join(r,"package.json"),wa(n)),un(Le.join(s,"types.ts"),ha),un(Le.join(s,"index.ts"),Aa(n.id)),ya(e,Sa(n))}function ye(e,t){if(e===void 0)return t;let o=String(e).trim().toLowerCase();return o==="true"||o==="1"||o==="yes"||o==="on"?!0:o==="false"||o==="0"||o==="no"||o==="off"?!1:t}function tr(e,t){let o=t==="cn";return{cronJobs:ye(e.cronJobs,!0),auditLog:ye(e.auditLog,!0),apiKeys:ye(e.apiKeys,!0),webhooks:ye(e.webhooks,!0),commandPalette:ye(e.commandPalette,!0),cookieConsent:ye(e.cookieConsent,!0),legalPages:ye(e.legalPages,!0),chinaCompliance:ye(e.chinaCompliance,o)}}var qt=[{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 nr(e){return qt.find(t=>t.id===e)}var $l=qt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function or(e,t,o="global"){let n=nr(t);if(!n)throw new Error(`Unknown webhooks provider "${t}". Valid ids: ${qt.map(r=>r.id).join(", ")}`);if(n.id==="none"){H(e,"webhooks");return}n.envVars.length>0&&Q(e,{category:"Webhooks",name:n.name,envVars:n.envVars,docs:n.docs}),Z(e,"WEBHOOKS_PROVIDER",n.id)}import Fe from"fs";import bt from"path";function Ia(e){return`import Link from "next/link";
|
|
2780
2780
|
|
|
2781
2781
|
export default function WelcomePage() {
|
|
2782
2782
|
const projectName = ${JSON.stringify(e)};
|
|
@@ -2885,21 +2885,21 @@ function Step({
|
|
|
2885
2885
|
</li>
|
|
2886
2886
|
);
|
|
2887
2887
|
}
|
|
2888
|
-
`}function
|
|
2888
|
+
`}function Ra(e={}){return`
|
|
2889
2889
|
## What you can do next
|
|
2890
2890
|
|
|
2891
2891
|
${[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(`
|
|
2892
2892
|
`)}
|
|
2893
|
-
`}function
|
|
2893
|
+
`}function ka(e=[]){return e.length===0?"":`
|
|
2894
2894
|
## Production readiness holds
|
|
2895
2895
|
|
|
2896
2896
|
Do not enable these in production until you replace stubs, add provider credentials, and verify the integration contract:
|
|
2897
2897
|
|
|
2898
|
-
${e.map(o=>{let n=
|
|
2898
|
+
${e.map(o=>{let n=tt(o.status);return`- ${o.flag}=${o.provider} ${n} \u2014 ${pe(o.status)}`}).join(`
|
|
2899
2899
|
`)}
|
|
2900
2900
|
|
|
2901
2901
|
Review the package readiness matrix before handoff: [docs/package-status.md](docs/package-status.md)
|
|
2902
|
-
`}function
|
|
2902
|
+
`}function xa(e,t,o=[],n={}){return`# Next steps \u2014 ${e}
|
|
2903
2903
|
|
|
2904
2904
|
Region: ${t}
|
|
2905
2905
|
|
|
@@ -2925,17 +2925,17 @@ Your AI-native SaaS scaffold is ready. Complete these four steps to go from temp
|
|
|
2925
2925
|
\`\`\`
|
|
2926
2926
|
|
|
2927
2927
|
After setup, delete the welcome route at \`apps/web/src/app/[locale]/welcome/\`.
|
|
2928
|
-
${
|
|
2928
|
+
${Ra(n)}${ka(o)}
|
|
2929
2929
|
|
|
2930
2930
|
## Resources
|
|
2931
2931
|
|
|
2932
2932
|
- [Documentation](/docs)
|
|
2933
2933
|
- [GitHub](https://github.com/nebutra/nebutra-sailor)
|
|
2934
2934
|
- [Get free license (Individual/OPC)](https://nebutra.com/get-license)
|
|
2935
|
-
`}async function
|
|
2936
|
-
`)}function
|
|
2935
|
+
`}async function rr(e,t){let{projectName:o,region:n,previewSelections:r=[],waveFeatures:s={}}=t;try{let i=bt.join(e,".sailor");Fe.existsSync(i)||Fe.mkdirSync(i,{recursive:!0}),Fe.writeFileSync(bt.join(i,"next-steps.md"),xa(o,n,r,s))}catch(i){let c=i instanceof Error?i.message:String(i);console.warn(`[create-sailor] Failed to write .sailor/next-steps.md: ${c}`)}try{let i=bt.join(e,"apps","web");if(!Fe.existsSync(i))return;let c=bt.join(i,"src","app","[locale]","welcome");Fe.existsSync(c)||Fe.mkdirSync(c,{recursive:!0}),Fe.writeFileSync(bt.join(c,"page.tsx"),Ia(o))}catch(i){let c=i instanceof Error?i.message:String(i);console.warn(`[create-sailor] Failed to write welcome page: ${c}`)}}var sr="create-sailor";function p(e,t){e&&process.stdout.write(JSON.stringify(t)+`
|
|
2936
|
+
`)}function Oa(){let e=process.env.npm_config_user_agent??"";return e.startsWith("pnpm")?"pnpm":e.startsWith("yarn")?"yarn":e.startsWith("bun")?"bun":"npm"}function ir(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 ar(e,t,o){let n=e?.trim().toLowerCase()??Bn(t),r=Mt(n);if(!r){let i=tn.map(c=>c.id).join(", ");throw new Error(`Unknown --db-host="${n}". Valid: ${i}`)}let s=o;return r.forcedEngine&&s!=="none"&&s!==r.forcedEngine&&(s=r.forcedEngine),{hostId:r.id,engine:s}}function cr(e){return e==="drizzle"?"drizzle":"prisma"}function pr(e){return e==="lemon"||e==="lemonsqueezy"?"lemon":e==="wechat"?"wechat":e==="alipay"?"alipay":e==="none"?"none":"stripe"}function lr(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 dr(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==="none"?"none":"clerk"}var Ua=new Set(["gateway","direct","custom","none"]);function hr(e){if(!e)return!1;let t=e.trim().toLowerCase();return Ua.has(t)}function ur(e){if(!e)return["openai"];if(hr(e))return[];let t=e.split(",").map(o=>o.trim().toLowerCase());return t.includes("none")?[]:t}function La(e,t){let o=e??t;return dn(o)??o}var mr={mintlify:"Mintlify",docusaurus:"Docusaurus",nextra:"Nextra",vitepress:"VitePress"};function gr(e,t){if(!e)return"fumadocs";let o=e.toLowerCase();if(o==="fumadocs"||o==="none")return o;if(o in mr){let n=mr[o];return t?p(!0,{event:"notice",kind:"docs-fallback",requested:o,effective:"fumadocs"}):process.stdout.write(C.yellow(`\u26A0 ${n} support is coming in v1.2. Falling back to fumadocs.
|
|
2937
2937
|
`)+C.dim(` Track progress: https://github.com/Nebutra/Nebutra-Sailor/issues
|
|
2938
|
-
`)),"fumadocs"}return"fumadocs"}function
|
|
2938
|
+
`)),"fumadocs"}return"fumadocs"}function fr(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 yr(e){if(!e)return"global";let t=e.toLowerCase();return t==="cn"?"cn":t==="hybrid"?"hybrid":"global"}function Ma(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:e==="cn"?"aliyun-slide":"turnstile",mcp:"on",metering:"auto",billingMode:"usage",idp:"clerk"}}function Sr(e){return e==="cn"?"wechat":"stripe"}async function Da(){let e=new Ta;e.name(sr).description("Nebutra-Sailor \u2014 AI-Native SaaS template").version(ce,"-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 | 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("--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: true)").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.parse(process.argv);let t=e.opts(),[o]=e.args;t.help&&(En(),process.exit(0));let n=!!t.json,r=!!t.dryRun,s=!!t.yes,i=s||!process.stdin.isTTY;if(n||yn(),p(n,{event:"start",version:ce}),!t.pm)try{Pt("pnpm --version",{stdio:"ignore"})}catch{n||process.stderr.write(`
|
|
2939
2939
|
${C.red("\u2718")} ${C.bold("pnpm is required")} but was not found on PATH.
|
|
2940
2940
|
|
|
2941
2941
|
The scaffold uses pnpm workspaces + Turborepo. Install pnpm first:
|
|
@@ -2944,23 +2944,23 @@ The scaffold uses pnpm workspaces + Turborepo. Install pnpm first:
|
|
|
2944
2944
|
Then retry: ${C.cyan("pnpm dlx create-sailor@latest")}
|
|
2945
2945
|
(or pass ${C.dim("--pm=npm")} if you know workspace:* won't resolve in your setup)
|
|
2946
2946
|
|
|
2947
|
-
`),p(n,{event:"error",code:"PNPM_MISSING"}),process.exit(1)}let c=o??(s?"./my-saas-app":void 0),a;if(c)a=c;else if(i)a="./my-saas-app";else{let g=await x.group({name:()=>x.text({message:"Where should we create your project?",placeholder:"./my-saas-app",defaultValue:"./my-saas-app",validate:
|
|
2948
|
-
`)),process.exit(130)}});a=String(g.name)}let l=
|
|
2949
|
-
`)),process.exit(130)}}):{};
|
|
2950
|
-
`))}}let
|
|
2947
|
+
`),p(n,{event:"error",code:"PNPM_MISSING"}),process.exit(1)}let c=o??(s?"./my-saas-app":void 0),a;if(c)a=c;else if(i)a="./my-saas-app";else{let g=await x.group({name:()=>x.text({message:"Where should we create your project?",placeholder:"./my-saas-app",defaultValue:"./my-saas-app",validate:h=>{if(h.length===0)return"Please enter a path."}})},{onCancel:()=>{process.stdout.write(C.red(`\u2718 Cancelled
|
|
2948
|
+
`)),process.exit(130)}});a=String(g.name)}let l=Ct.basename(Ct.resolve(a)),m=t.pm??Oa(),S=!!t.region,T=!!t.auth,N=!!t.payment,D=!!t.ai,ae=!!t.email,_=!!t.storage,u=!!t.monitoring,U=!!t.analytics,At=!!t.sms,mn=t.i18n!==void 0,E,je,$,wt="local",pt,Y,F,X=Rn(t.socialLogin),lt,Gt,he,Ve,ne,q,It;if(i){E=yr(t.region),je=cr(t.orm);{let ve=ir(t.db),j=ar(t.dbHost,E,ve);$=j.engine,wt=j.hostId}let g=N?t.payment:Sr(E);pt=pr(g),Y=lr(g),F=dr(t.auth);let h="gateway";if(D){let ve=t.ai?.trim().toLowerCase()??"";hr(ve)?h=ve:h="direct"}let O=zt({mode:h,providerIds:D?ur(t.ai):void 0});lt=O.mode,Gt=O.routing,he=O.providerIds,ne=fr(t.deploy),q=gr(t.docs,n),It=mn?!!t.i18n:!0}else{let g={};S||(g.region=()=>x.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"})),T||(g.auth=()=>x.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:"none",label:"None"}],initialValue:"clerk"})),D||(g.aiMode=()=>x.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"}),g.aiProviders=({results:y})=>{if(y.aiMode!=="direct")return Promise.resolve([]);let b=Xt.map(oe=>({value:oe.id,label:`[${oe.category}] ${oe.name}`}));return x.multiselect({message:"Select direct AI provider adapters (expert path)",options:b,initialValues:["openai"],required:!1})},g.customAiName=({results:y})=>y.aiMode==="custom"?x.text({message:"Custom endpoint name (e.g. proxy, local):",defaultValue:"custom",placeholder:"custom"}):Promise.resolve(void 0),g.customAiBaseUrl=({results:y})=>y.aiMode==="custom"?x.text({message:"Custom endpoint base URL (e.g. https://api.proxy.com/v1):",validate:b=>{if(b.length===0)return"Base URL is required."}}):Promise.resolve(void 0),g.customAiApiKeyEnv=({results:y})=>y.aiMode==="custom"?x.text({message:"Environment variable name for the API Key:",defaultValue:"CUSTOM_AI_API_KEY",placeholder:"CUSTOM_AI_API_KEY"}):Promise.resolve(void 0));let h=Object.keys(g).length>0?await x.group(g,{onCancel:()=>{process.stdout.write(C.red(`\u2718 Cancelled
|
|
2949
|
+
`)),process.exit(130)}}):{};E=yr(S?t.region:h.region),je=cr(t.orm);{let y=ir(t.db),b=ar(t.dbHost,E,y);$=b.engine,wt=b.hostId}let O=N?t.payment:Sr(E);pt=pr(O),Y=lr(O),F=dr(T?t.auth:h.auth);let ve=h.aiMode==="custom"?{name:h.customAiName??"custom",baseURL:h.customAiBaseUrl??"",apiKeyEnvName:h.customAiApiKeyEnv??"CUSTOM_AI_API_KEY"}:void 0,j=zt({mode:D?t.ai?.trim().toLowerCase()==="none"?"none":"direct":h.aiMode??"gateway",providerIds:D?ur(t.ai):h.aiProviders,customEndpoint:ve});lt=j.mode,Gt=j.routing,he=j.providerIds,j.customEndpoint&&(Ve={name:j.customEndpoint.name,baseURL:j.customEndpoint.baseURL,apiKeyEnvName:j.customEndpoint.apiKeyEnvName}),ne=fr(t.deploy),q=gr(t.docs,n),It=mn?!!t.i18n:!0}let I=Ma(E),Be=ae?t.email:I.email,Ke=La(_?t.storage:void 0,I.storage),$e=u?t.monitoring:I.monitoring,Ye=U?t.analytics:I.analytics,qe=At?t.sms:I.sms,Ge=t.queue??I.queue,He=t.search??I.search,dt=t.cache??I.cache,We=t.notifications??I.notifications,Xe=t.webhooks??I.webhooks,ut=t.cms??I.cms,ze=t.featureFlags??I.featureFlags,Je=t.captcha??I.captcha,Rt=t.mcp??I.mcp,mt=t.metering??I.metering,Qe=t.billingMode??I.billingMode,Ze=t.idp??I.idp,A=tr({cronJobs:t.cronJobs,auditLog:t.auditLog,apiKeys:t.apiKeys,commandPalette:t.commandPalette,cookieConsent:t.cookieConsent,legalPages:t.legalPages,chinaCompliance:t.chinaCompliance},E),et=Sn([{flag:"queue",provider:Ge},{flag:"search",provider:He},{flag:"notifications",provider:We},{flag:"webhooks",provider:Xe},{flag:"feature-flags",provider:ze},{flag:"captcha",provider:Je}]);function Ht(){for(let g of et)if(n)p(!0,{event:"warn",step:g.flag,provider:g.provider,packageStatus:g.status,message:pe(g.status)});else{let h=tt(g.status);process.stdout.write(C.yellow(`\u26A0 ${g.flag}=${g.provider} ${h} \u2014 ${pe(g.status)}
|
|
2950
|
+
`))}}let Ee={region:E,orm:je,database:$,payment:pt,aiMode:lt,aiRouting:Gt,aiProviders:he,customAiEndpoint:Ve,deployTarget:ne,docs:q,i18n:It,email:Be,storage:Ke,monitoring:$e,analytics:Ye,sms:qe,queue:Ge,search:He,cache:dt,notifications:We,webhooks:Xe,cms:ut,featureFlags:ze,captcha:Je,mcp:Rt,metering:mt,billingMode:Qe,idp:Ze,cronJobs:A.cronJobs,auditLog:A.auditLog,apiKeys:A.apiKeys,commandPalette:A.commandPalette,cookieConsent:A.cookieConsent,legalPages:A.legalPages,chinaCompliance:A.chinaCompliance},kt=[["Project name",l],["Region",E],["Auth",F],["Social login",X.length>0?X.map(g=>gt.find(h=>h.id===g)?.name??g).join(", "):"none"],["ORM",je],["Database",$],["Payment",Y],["AI topology",`${lt}${he.length>0?` (${he.join(", ")} seed)`:""}${Ve?" + custom endpoint":""}`],["Email",Be],["Storage",Ke],["Monitoring",$e],["Analytics",Ye],["SMS",qe],["Deploy Target",ne],["Docs Framework",q]];n?kt.forEach(([g,h],O)=>{p(!0,{event:"step",step:g,value:h,index:O+1,total:kt.length})}):kt.forEach(([g,h],O)=>{_n({index:O+1,total:kt.length,label:g,value:h})});let Er={databaseUrl:"postgresql://postgres:postgres@localhost:5432/nebutra",clerkPublishable:"",clerkSecret:""},gn=Date.now();if(r){let g=he.length,h=[`clone template \u2192 ${a}`,"write nebutra.config.json",`prune template (orm=${je}, i18n=${It})`,`region \u2192 ${E}`,`auth \u2192 ${F==="none"?"skip (remove packages/auth)":`configure ${F}`}`,...X.length>0?[`social-login \u2192 generate ${X.length} callback route${X.length===1?"":"s"} + SocialLoginButtons.tsx (${X.join(", ")})`]:[],`db \u2192 ${$==="none"?"skip (remove packages/db)":`configure Prisma for ${$}`}`,`payment \u2192 ${Y==="none"?"skip (remove packages/billing)":`configure ${Y}`}`,...q!=="none"?[`docs \u2192 scaffold apps/docs (${q==="fumadocs"?"fumadocs":`${q} \u2192 fumadocs fallback`})`]:[],...g>0||Ve?[`ai-providers \u2192 generate ${lt} registry seed + env (${g}${Ve?" + custom":""} provider${g===1&&!Ve?"":"s"})`]:[],...Be!=="none"?[`email \u2192 configure ${Be}`]:[],...Ke!=="none"?[`storage \u2192 configure ${Ke}`]:[],...$e!=="none"?[`monitoring \u2192 configure ${$e}`]:[],...Ye!=="none"?[`analytics \u2192 configure ${Ye}`]:[],...qe!=="none"?[`sms \u2192 configure ${qe}`]:[],...Ge!=="none"?[`queue \u2192 configure ${Ge}`]:[],...He!=="none"?[`search \u2192 configure ${He}`]:[],...dt!=="none"?[`cache \u2192 configure ${dt}`]:[],...We!=="none"?[`notifications \u2192 configure ${We}`]:[],...Xe!=="none"?[`webhooks \u2192 configure ${Xe}`]:[],...ut!=="none"?[`cms \u2192 configure ${ut}`]:[],...ze!=="none"?[`feature-flags \u2192 configure ${ze}`]:[],...Je!=="none"?[`captcha \u2192 configure ${Je}`]:[],`mcp \u2192 ${Rt==="on"?"enable MCP server":"remove packages/mcp"}`,`metering \u2192 ${mt==="off"?"disabled":mt==="auto"?pt!=="none"?"enabled (auto: payment set)":"disabled (auto: no payment)":"enabled"}`,...Qe!=="usage"?[`billing-mode \u2192 ${Qe}`]:[],...Ze!=="clerk"?[`idp \u2192 ${Ze}`]:[],`cron-jobs \u2192 ${A.cronJobs?"enabled":"disabled"}`,`audit-log \u2192 ${A.auditLog?"enabled":"disabled"}`,`api-keys \u2192 ${A.apiKeys?"enabled":"disabled"}`,`command-palette \u2192 ${A.commandPalette?"enabled":"disabled"}`,`cookie-consent \u2192 ${A.cookieConsent?"enabled":"disabled"}`,`legal-pages \u2192 ${A.legalPages?"enabled":"disabled"}`,`china-compliance \u2192 ${A.chinaCompliance?"enabled (@nebutra/china-compliance + ICP footer)":"disabled"}`,`compliance \u2192 inject ${E} 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)",...ne!=="none"?[`deploy \u2192 inject ${ne} config`]:[],"inject .env.local",t.install===!1?"skip install":`run ${m} install`,t.git===!1?"skip git init":"run git init"];if(n){for(let O of h)p(!0,{event:"plan",action:O});Ht(),p(!0,{event:"done",dryRun:!0})}else{process.stdout.write(`
|
|
2951
2951
|
`+C.bold(`Dry run \u2014 planned actions:
|
|
2952
|
-
`));for(let O of
|
|
2953
|
-
`);
|
|
2952
|
+
`));for(let O of h)process.stdout.write(` \u2022 ${O}
|
|
2953
|
+
`);et.length>0&&(process.stdout.write(`
|
|
2954
2954
|
`+C.bold(C.yellow(`Preview-status providers selected:
|
|
2955
|
-
`))),
|
|
2955
|
+
`))),Ht()),process.stdout.write(C.dim(`
|
|
2956
2956
|
No files were written.
|
|
2957
|
-
`))}process.exit(0)}let
|
|
2957
|
+
`))}process.exit(0)}let vr=async()=>{process.stdout.write(`
|
|
2958
2958
|
`+C.red(`\u2718 Cancelled
|
|
2959
|
-
`)),
|
|
2960
|
-
`))),process.exit(130)};process.on("SIGINT",
|
|
2961
|
-
`));try{
|
|
2962
|
-
`))}}else p(n,{event:"step",step:"install",status:"skip"});if(t.git!==!1){n&&p(!0,{event:"step",step:"git-init",status:"start"});try{
|
|
2963
|
-
`))}}else p(n,{event:"step",step:"git-init",status:"skip"});let
|
|
2964
|
-
\u2718 Failed: ${
|
|
2965
|
-
`)),process.exit(1)}}
|
|
2959
|
+
`)),Se.existsSync(a)&&await x.confirm({message:`Cleanup partial install at ${a}?`,initialValue:!0})===!0&&(Se.rmSync(a,{recursive:!0,force:!0}),process.stdout.write(C.dim(` \u2713 Removed ${a}
|
|
2960
|
+
`))),process.exit(130)};process.on("SIGINT",vr);try{p(n,{event:"step",step:"clone",status:"start"}),await fo(a),p(n,{event:"step",step:"clone",status:"ok"}),p(n,{event:"step",step:"package",status:"start"}),await Co(a,l),p(n,{event:"step",step:"package",status:"ok"}),p(n,{event:"step",step:"config",status:"start"}),await Vn(a,Ee),p(n,{event:"step",step:"config",status:"ok"}),p(n,{event:"step",step:"prune",status:"start"}),await Mo(a,Ee),p(n,{event:"step",step:"prune",status:"ok"});let g=Ct.join(a,"packages/platform/db/prisma/schema.prisma");if(Se.existsSync(g)){p(n,{event:"step",step:"schema-prune",status:"start"});try{let y=Se.readFileSync(g,"utf-8"),b=pn(y,{auth:F,payment:Y,"billing-mode":Qe,idp:Ze,template:"saas"});Se.writeFileSync(g,b),p(n,{event:"step",step:"schema-prune",status:"ok"})}catch(y){let b=y instanceof Error?y.message:String(y);p(n,{event:"step",step:"schema-prune",status:"error",error:b})}}else p(n,{event:"step",step:"schema-prune",status:"skip"});if(p(n,{event:"step",step:"auth",choice:F,status:"start"}),await wn(a,F),p(n,{event:"step",step:"auth",choice:F,status:"ok"}),X.length>0?(p(n,{event:"step",step:"social-login",providers:X,status:"start"}),await xn(a,X,F),p(n,{event:"step",step:"social-login",providers:X,status:"ok"})):p(n,{event:"step",step:"social-login",status:"skip"}),p(n,{event:"step",step:"db",choice:$,status:"start"}),await Yn(a,$,l),$!=="none"){let y=Mt(wt);y&&(await qn(a,y,l),p(n,{event:"step",step:"db-host",choice:wt,status:"ok"}))}if(p(n,{event:"step",step:"db",choice:$,status:"ok"}),je==="drizzle"){p(n,{event:"step",step:"orm",choice:"drizzle",status:"start"});let y=await Io(a,"drizzle",$);p(n,{event:"step",step:"orm",choice:"drizzle",status:y.applied?"ok":"skip",...y.reason?{reason:y.reason}:{}})}if(p(n,{event:"step",step:"payment",choice:Y,status:"start"}),await ko(a,Y),p(n,{event:"step",step:"payment",choice:Y,status:"ok"}),await eo(a,Be,E),n&&p(!0,{event:"step",step:"email",choice:Be,status:"ok"}),await er(a,Ke,E),n&&p(!0,{event:"step",step:"storage",choice:Ke,status:"ok"}),await _o(a,$e,E),n&&p(!0,{event:"step",step:"monitoring",choice:$e,status:"ok"}),await Cn(a,Ye,E),n&&p(!0,{event:"step",step:"analytics",choice:Ye,status:"ok"}),await Qo(a,qe,E),n&&p(!0,{event:"step",step:"sms",choice:qe,status:"ok"}),await Bo(a,Ge,E),n&&p(!0,{event:"step",step:"queue",choice:Ge,status:"ok"}),await Xo(a,He,E),n&&p(!0,{event:"step",step:"search",choice:He,status:"ok"}),await Un(a,dt,E),n&&p(!0,{event:"step",step:"cache",choice:dt,status:"ok"}),await Po(a,We,E),n&&p(!0,{event:"step",step:"notifications",choice:We,status:"ok"}),await or(a,Xe,E),n&&p(!0,{event:"step",step:"webhooks",choice:Xe,status:"ok"}),await Fn(a,ut,E),n&&p(!0,{event:"step",step:"cms",choice:ut,status:"ok"}),await ao(a,ze,E),n&&p(!0,{event:"step",step:"feature-flags",choice:ze,status:"ok"}),await Mn(a,Je,E),n&&p(!0,{event:"step",step:"captcha",choice:Je,status:"ok"}),await yo(a,Rt),n&&p(!0,{event:"step",step:"mcp",mode:Rt,status:"ok"}),await ho(a,mt,pt),n&&p(!0,{event:"step",step:"metering",mode:mt,status:"ok"}),p(n,{event:"step",step:"prune-wave-features",status:"start"}),Do(a,A),p(n,{event:"step",step:"prune-wave-features",status:"ok",toggles:A}),Ee.orm==="prisma"&&Ee.database!=="none"){let y=Ct.join(a,"packages/platform/db/prisma/schema.prisma");if(Se.existsSync(y)){p(n,{event:"step",step:"schema-prune",status:"start"});try{let b=Se.readFileSync(y,"utf8"),oe=pn(b,{auth:F,payment:Y,"billing-mode":Qe,idp:Ze,template:"saas"});Se.writeFileSync(y,oe),p(n,{event:"step",step:"schema-prune",status:"ok"})}catch(b){let oe=b instanceof Error?b.message:String(b);p(n,{event:"step",step:"schema-prune",status:"error",error:oe})}}else p(n,{event:"step",step:"schema-prune",status:"skip"})}else p(n,{event:"step",step:"schema-prune",status:"skip"});if(p(n,{event:"step",step:"billing-mode",choice:Qe,status:"ok"}),p(n,{event:"step",step:"idp",choice:Ze,status:"ok"}),await jn(a,E),n&&p(!0,{event:"step",step:"compliance",region:E,status:"ok"}),await so(a),await zo(a,F),await rr(a,{projectName:l,region:E,previewSelections:et,waveFeatures:A}),Ee.aiMode!=="none"){p(n,{event:"step",step:"ai-providers",status:"start"});let y={providerIds:Ee.aiProviders,customEndpoint:Ee.customAiEndpoint},b=Ct.join(a,"packages/ai/ai-providers/templates");await Uo(a,y,b),p(n,{event:"step",step:"ai-providers",status:"ok"})}else p(n,{event:"step",step:"ai-providers",status:"skip"});p(n,{event:"step",step:"docs",status:"start"}),q!=="none"?(await zn(a,{framework:q,projectName:l}),p(n,{event:"step",step:"docs",framework:"fumadocs",requested:q,status:"ok"})):p(n,{event:"step",step:"docs",status:"skip"}),p(n,{event:"step",step:"deploy-target",status:"start"}),ne!=="none"&&await Hn(a,ne),p(n,{event:"step",step:"deploy-target",status:"ok"}),p(n,{event:"step",step:"env",status:"start"}),await no(a,Er),p(n,{event:"step",step:"env",status:"ok"}),p(n,{event:"step",step:"scaffold-extras",status:"start"});let h=await $o(a,{projectName:l,withWorkflows:!!t.withWorkflows,withPythonBackend:!!t.withPythonBackend});if(p(n,{event:"step",step:"scaffold-extras",status:"ok",applied:h.applied,skipped:h.skipped}),t.install!==!1){let y=m==="bun"?"bun install":`${m} install`;n?p(!0,{event:"step",step:"install",pm:m,status:"start"}):process.stdout.write(C.dim(` Installing dependencies with ${m}\u2026
|
|
2961
|
+
`));try{Pt(y,{cwd:a,stdio:n?"ignore":"inherit"}),p(n,{event:"step",step:"install",status:"ok"})}catch(b){let oe=b instanceof Error?b.message:String(b);n?p(!0,{event:"step",step:"install",status:"error",error:oe}):process.stdout.write(C.yellow(` \u26A0 install failed \u2014 run '${y}' manually.
|
|
2962
|
+
`))}}else p(n,{event:"step",step:"install",status:"skip"});if(t.git!==!1){n&&p(!0,{event:"step",step:"git-init",status:"start"});try{Pt("git init -q",{cwd:a,stdio:"ignore"}),Pt("git add -A",{cwd:a,stdio:"ignore"}),Pt('git -c user.email=you@example.com -c user.name="You" commit -q -m "chore: initial scaffold from create-sailor"',{cwd:a,stdio:"ignore"}),p(n,{event:"step",step:"git-init",status:"ok"})}catch(y){let b=y instanceof Error?y.message:String(y);n?p(!0,{event:"step",step:"git-init",status:"error",error:b}):process.stdout.write(C.yellow(` \u26A0 git init skipped \u2014 not fatal.
|
|
2963
|
+
`))}}else p(n,{event:"step",step:"git-init",status:"skip"});let j=Math.max(1,Math.round((Date.now()-gn)/1e3));An({template_version:ce,package_manager:m,region:E,auth:F,payment:Y,ai_providers:he,deploy_target:ne,duration_ms:Date.now()-gn}),et.length>0&&Ht(),n?p(!0,{event:"done",status:"ok",elapsedSec:j,targetDir:a,previewSelections:et,waveFeatures:A}):hn({elapsedSec:j,targetDir:a,skippedInstall:t.install===!1,previewSelections:et,waveFeatures:A});try{Na({pkg:{name:sr,version:ce},updateCheckInterval:1e3*60*60*24}).notify({defer:!1,isGlobal:!0})}catch{}process.exit(0)}catch(g){let h=g instanceof Error?g.message:String(g);n?p(!0,{event:"error",message:h}):process.stdout.write(C.red(`
|
|
2964
|
+
\u2718 Failed: ${h}
|
|
2965
|
+
`)),process.exit(1)}}Da().catch(e=>{process.stderr.write(String(e)+`
|
|
2966
2966
|
`),process.exit(1)});
|