create-sailor 1.4.0 → 1.4.1
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 +224 -207
- package/package.json +2 -1
- package/templates/deploy/Dockerfile.web +57 -0
- package/templates/deploy/docker-compose.yml +40 -0
- package/templates/deploy/railway.toml +13 -0
- package/templates/deploy/vercel.json +37 -0
- package/templates/deploy/wrangler.toml +8 -0
- package/templates/docs/docusaurus/README.md +24 -0
- package/templates/docs/docusaurus/docs/getting-started/develop.md +20 -0
- package/templates/docs/docusaurus/docs/getting-started/install.md +11 -0
- package/templates/docs/docusaurus/docs/intro.md +15 -0
- package/templates/docs/docusaurus/docusaurus.config.ts +66 -0
- package/templates/docs/docusaurus/package.json +43 -0
- package/templates/docs/docusaurus/sidebars.ts +14 -0
- package/templates/docs/docusaurus/src/css/custom.css +22 -0
- package/templates/docs/docusaurus/tsconfig.json +7 -0
- package/templates/docs/fumadocs/README.md +46 -0
- package/templates/docs/fumadocs/app/(home)/page.tsx +22 -0
- package/templates/docs/fumadocs/app/api/search/route.ts +4 -0
- package/templates/docs/fumadocs/app/docs/[[...slug]]/page.tsx +39 -0
- package/templates/docs/fumadocs/app/docs/layout.tsx +11 -0
- package/templates/docs/fumadocs/app/global.css +10 -0
- package/templates/docs/fumadocs/app/layout.tsx +18 -0
- package/templates/docs/fumadocs/content/docs/api.mdx +31 -0
- package/templates/docs/fumadocs/content/docs/concepts.mdx +19 -0
- package/templates/docs/fumadocs/content/docs/getting-started.mdx +56 -0
- package/templates/docs/fumadocs/content/docs/index.mdx +17 -0
- package/templates/docs/fumadocs/content/docs/meta.json +4 -0
- package/templates/docs/fumadocs/lib/source.ts +7 -0
- package/templates/docs/fumadocs/next.config.mjs +17 -0
- package/templates/docs/fumadocs/package.json +30 -0
- package/templates/docs/fumadocs/source.config.ts +17 -0
- package/templates/docs/fumadocs/tsconfig.json +25 -0
- package/templates/docs/mintlify/README.md +25 -0
- package/templates/docs/mintlify/api-reference/introduction.mdx +25 -0
- package/templates/docs/mintlify/introduction.mdx +21 -0
- package/templates/docs/mintlify/mint.json +37 -0
- package/templates/docs/mintlify/package.json +13 -0
- package/templates/docs/mintlify/quickstart.mdx +26 -0
- package/templates/docs/nextra/README.md +35 -0
- package/templates/docs/nextra/app/[[...mdxPath]]/page.tsx +23 -0
- package/templates/docs/nextra/app/layout.tsx +36 -0
- package/templates/docs/nextra/content/_meta.ts +4 -0
- package/templates/docs/nextra/content/getting-started.mdx +35 -0
- package/templates/docs/nextra/content/index.mdx +18 -0
- package/templates/docs/nextra/mdx-components.tsx +10 -0
- package/templates/docs/nextra/next.config.mjs +9 -0
- package/templates/docs/nextra/package.json +24 -0
- package/templates/docs/nextra/tsconfig.json +21 -0
- package/templates/docs/vitepress/.vitepress/config.ts +35 -0
- package/templates/docs/vitepress/README.md +23 -0
- package/templates/docs/vitepress/guide/getting-started.md +26 -0
- package/templates/docs/vitepress/guide/introduction.md +12 -0
- package/templates/docs/vitepress/index.md +23 -0
- package/templates/docs/vitepress/package.json +17 -0
- package/templates/docs/vitepress/tsconfig.json +15 -0
- package/dist/index.d.ts +0 -1
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 yt}from"child_process";import Se from"fs";import St from"path";import*as k from"@clack/prompts";import{Command as Zs}from"commander";import C from"picocolors";import ea from"update-notifier";import rr from"cfonts";import ir from"picocolors";import ln from"fs";import jt from"path";import{fileURLToPath as nr}from"url";function or(){try{let t=jt.dirname(nr(import.meta.url));for(let o=0;o<4;o++){let n=jt.join(t,"package.json");if(ln.existsSync(n)){let r=ln.readFileSync(n,"utf-8"),i=JSON.parse(r);if(i.name==="create-sailor"&&typeof i.version=="string")return i.version}t=jt.dirname(t)}return"0.0.0-dev"}catch{return"0.0.0-dev"}}var ie=or();function dn(){if(process.env.NO_COLOR||!process.stdout.isTTY){process.stdout.write([`Sailor v${ie}`,"AI-Native SaaS Unicorn Template","by Nebutra \xB7 https://nebutra.com","",""].join(`
|
|
3
|
+
`));return}rr.say("Sailor",{font:"block",align:"left",colors:["#0033FE","#0BF1C3"],gradient:["#0033FE","#0BF1C3"],transitionGradient:!0,space:!1}),process.stdout.write(ir.dim(` AI-Native SaaS Template \xB7 v${ie} \xB7 nebutra.com
|
|
4
4
|
|
|
5
|
-
`))}import P from"picocolors";var sr={qstash:"foundation",bullmq:"foundation",upstash:"foundation",sqs:"foundation",meilisearch:"foundation",typesense:"foundation",algolia:"foundation",pgvector:"foundation",novu:"foundation",knock:"foundation",svix:"foundation","vercel-flags":"wip",growthbook:"wip",configcat:"wip",turnstile:"wip",hcaptcha:"wip","aliyun-slide":"wip"};function
|
|
5
|
+
`))}import P from"picocolors";var sr={qstash:"foundation",bullmq:"foundation",upstash:"foundation",sqs:"foundation",meilisearch:"foundation",typesense:"foundation",algolia:"foundation",pgvector:"foundation",novu:"foundation",knock:"foundation",svix:"foundation","vercel-flags":"wip",growthbook:"wip",configcat:"wip",turnstile:"wip",hcaptcha:"wip","aliyun-slide":"wip"};function ar(e){return e?sr[e]??"stable":"stable"}function Je(e){switch(e){case"stable":return"";case"foundation":return"[Foundation]";case"wip":return"[WIP]";case"deprecated":return"[Deprecated]"}}function se(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 un(e){let t=[];for(let{flag:o,provider:n}of e){if(!n||n==="none")continue;let r=ar(n);r!=="stable"&&t.push({flag:o,provider:n,status:r})}return t}function cr(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function mn(e){let t=cr(),o=t?"\u2693":"-",n=t?"\u25B8":"->",r=t?"\u2605":"*",i=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 x=t?P.bold(P.yellow("\u26A0 Preview features selected:")):"!! Preview features selected:";c.push("",` ${x}`);for(let N of a){let L=Je(N.status),re=` ${n} ${N.flag} (${N.provider}) ${L}`;c.push(t?P.yellow(re):re)}c.push("",` ${t?P.dim(se("foundation")):se("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=x=>x!==!1,y=[];m(l.apiKeys)&&y.push("Manage API keys at /settings/api-keys"),m(l.auditLog)&&y.push("View audit log at /settings/audit-log"),y.push("Configure webhooks at /settings/webhooks"),m(l.commandPalette)&&y.push("Press Cmd/Ctrl+K to open the command palette"),y.push("Edit notification preferences at /settings/notifications"),y.push("Export your data at /settings/account/export"),l.chinaCompliance&&y.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 x of y)c.push(` ${n} ${x}`);c.push("",` ${t?P.bold("Ship faster:"):"Ship faster:"}`,` ${r} Star us https://github.com/nebutra/nebutra-sailor`,` ${i} Free license https://nebutra.com/get-license`,""),process.stdout.write(c.join(`
|
|
6
6
|
`)+`
|
|
7
|
-
`)}var
|
|
7
|
+
`)}var pr=`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 Dt=[{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:[]}],Ea=Dt.reduce((e,t)=>(e[t.category]||(e[t.category]=[]),e[t.category].push(t),e),{});var lr=["openai","anthropic","google"],dr=["openai"];function Kt(e){let t=e.providerIds??[];switch(e.mode){case"gateway":{let o=t.length>0?t:[...lr];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:[...dr];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 Je from"fs";import j from"path";var fn=[{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:""}],ba=fn.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function yn(e){return fn.find(t=>t.id===e)}function ur(e){Je.mkdirSync(e,{recursive:!0})}function G(e,t){ur(j.dirname(e)),Je.writeFileSync(e,t)}function Ee(e,t){let o=j.join(e,".env.example");Je.existsSync(o)?Je.appendFileSync(o,`
|
|
78
|
-
`+t):
|
|
76
|
+
`;function gn(){process.stdout.write(pr)}import Pt from"picocolors";function fn(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function lr(e){let t=fn()?"\u25B8":"->",o=`[${e.index}/${e.total}]`,n=e.label.padEnd(18," ");return fn()?`${Pt.dim(o)} ${Pt.bold(n)} ${Pt.dim(t)} ${Pt.cyan(e.value)}`:`${o} ${n} ${t} ${e.value}`}function yn(e){process.stdout.write(lr(e)+`
|
|
77
|
+
`)}var Kt=[{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:[]}],va=Kt.reduce((e,t)=>(e[t.category]||(e[t.category]=[]),e[t.category].push(t),e),{});var dr=["openai","anthropic","google"],ur=["openai"];function Vt(e){let t=e.providerIds??[];switch(e.mode){case"gateway":{let o=t.length>0?t:[...dr];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:[...ur];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 F from"path";var Sn=[{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:""}],Pa=Sn.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function hn(e){return Sn.find(t=>t.id===e)}function mr(e){Qe.mkdirSync(e,{recursive:!0})}function W(e,t){mr(F.dirname(e)),Qe.writeFileSync(e,t)}function ve(e,t){let o=F.join(e,".env.example");Qe.existsSync(o)?Qe.appendFileSync(o,`
|
|
78
|
+
`+t):Qe.writeFileSync(o,t)}function gr(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
|
-
`;
|
|
161
|
+
`;W(F.join(e,"src","lib","analytics","posthog.client.ts"),t),W(F.join(e,"src","lib","analytics","posthog.server.ts"),o),W(F.join(e,"src","components","analytics","AnalyticsProvider.tsx"),n)}function fr(){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 yr(e){W(F.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 Sr(){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 hr(e){W(F.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 Er(){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 vr(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
|
-
`;
|
|
250
|
+
`;W(F.join(e,"src","lib","analytics","mixpanel.client.ts"),t),W(F.join(e,"src","lib","analytics","mixpanel.server.ts"),o)}function _r(){return`# Mixpanel \u2014 product analytics
|
|
251
251
|
MIXPANEL_TOKEN=
|
|
252
252
|
NEXT_PUBLIC_MIXPANEL_TOKEN=
|
|
253
|
-
`}function
|
|
253
|
+
`}function br(e){W(F.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 Pr(){return`# \u767E\u5EA6\u7EDF\u8BA1 \u2014 \u4E2D\u56FD\u533A\u7F51\u7AD9\u5206\u6790
|
|
280
280
|
NEXT_PUBLIC_BAIDU_STATS_ID=
|
|
281
|
-
`}function
|
|
281
|
+
`}function Cr(e){W(F.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 Ir(){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 Ar(e){W(F.join(e,"src","components","analytics","GrowingIoScript.tsx"),`"use client";
|
|
313
313
|
/**
|
|
314
314
|
* GrowingIO CDP \u2014 inline snippet installer.
|
|
315
315
|
*/
|
|
@@ -334,21 +334,29 @@ gdp("send");
|
|
|
334
334
|
/>
|
|
335
335
|
);
|
|
336
336
|
}
|
|
337
|
-
`)}function
|
|
337
|
+
`)}function wr(){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 _n(e,t){let o
|
|
339
|
+
`}async function En(e,t,o="global"){let n=hn(t);if(!n||n.id==="none")return;let r=F.join(e,"apps","web");if(!Qe.existsSync(r))return;let i=n.id;try{switch(i){case"posthog":gr(r),ve(e,fr());return;case"plausible":yr(r),ve(e,Sr());return;case"umami":hr(r),ve(e,Er());return;case"mixpanel":vr(r),ve(e,_r());return;case"baidu":br(r),ve(e,Pr());return;case"sensors":Cr(r),ve(e,Ir());return;case"growingio":Ar(r),ve(e,wr());return;default:return}}catch(s){throw console.error(`Failed to apply analytics selection "${t}":`,s),new Error(`Analytics scaffold for "${t}" failed \u2014 see stderr for details.`)}}var Rr="https://analytics.nebutra.com";function kr(e={}){if(e.noTelemetry===!0)return!0;let t=process.env.NEBUTRA_TELEMETRY;return t==="0"||t==="false"}function vn(e,t={}){kr(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??Rr},onError:()=>{}});if(typeof n?.track!="function")return;let r=n.track("scaffold.completed",e);r&&typeof r.then=="function"&&await r.catch(()=>{})}catch{}})()}import j from"fs";import $ from"path";function _e(e){j.existsSync(e)&&j.rmSync(e,{recursive:!0,force:!0})}function Bt(e,t){let o=$.join(e,".env.example");j.existsSync(o)?j.appendFileSync(o,`
|
|
340
|
+
`+t):j.writeFileSync(o,t)}function $t(e,t){if(!j.existsSync(e))return;let o=j.readFileSync(e,"utf8"),n=o.replace(/export type AuthProviderId\s*=\s*(?:"[^"]+"\s*\|\s*)+"[^"]+";/,`export type AuthProviderId = "${t}";`);n!==o&&j.writeFileSync(e,n)}function xr(e,t){let o=$.join(e,"README.md");if(!j.existsSync(o)){j.writeFileSync(o,t);return}j.appendFileSync(o,`
|
|
341
|
+
`+t)}async function _n(e,t){let o=$.join(e,"packages","auth");if(!j.existsSync(o))return;if(t==="none"){_e(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=$.join(o,"src","providers"),r=$.join(o,"src","types.ts");if(t==="clerk"){_e($.join(n,"better-auth.ts")),_e($.join(n,"nextauth.ts")),$t(r,"clerk"),Bt(e,`# Clerk auth
|
|
346
346
|
CLERK_PUBLISHABLE_KEY=
|
|
347
347
|
CLERK_SECRET_KEY=
|
|
348
|
-
`);return}if(t==="betterauth"){
|
|
348
|
+
`);return}if(t==="betterauth"){_e($.join(n,"clerk.ts")),_e($.join(n,"nextauth.ts")),$t(r,"better-auth"),Bt(e,`# Better Auth
|
|
349
349
|
BETTER_AUTH_SECRET=
|
|
350
350
|
BETTER_AUTH_URL=http://localhost:3000
|
|
351
|
-
`);return}
|
|
351
|
+
`);return}if(t==="nextauth"){_e($.join(n,"clerk.ts")),_e($.join(n,"better-auth.ts")),$t(r,"nextauth"),Bt(e,`# NextAuth (Auth.js v5)
|
|
352
|
+
AUTH_SECRET=
|
|
353
|
+
NEXTAUTH_URL=http://localhost:3000
|
|
354
|
+
# OAuth (optional \u2014 leave blank to disable that provider)
|
|
355
|
+
GOOGLE_CLIENT_ID=
|
|
356
|
+
GOOGLE_CLIENT_SECRET=
|
|
357
|
+
GITHUB_CLIENT_ID=
|
|
358
|
+
GITHUB_CLIENT_SECRET=
|
|
359
|
+
`);return}}var at=[{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"}],Nr=new Set(at.map(e=>e.id));function bn(e){return at.find(t=>t.id===e)}function Pn(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)Nr.has(r)&&!n.has(r)&&(o.push(r),n.add(r));return o}import Pe from"fs";import be from"path";function Tr(e,t){let o=`// apps/web/src/app/api/auth/callback/${e.id}/route.ts
|
|
352
360
|
// Generated by create-sailor \u2014 OAuth callback for ${e.name}
|
|
353
361
|
// Primary auth provider: ${t}
|
|
354
362
|
// Docs: ${e.docs}`,n={wechat:` const tokenRes = await fetch(
|
|
@@ -473,7 +481,7 @@ ${n[e.id]}
|
|
|
473
481
|
void state;
|
|
474
482
|
return NextResponse.redirect(new URL("/dashboard", req.url));
|
|
475
483
|
}
|
|
476
|
-
`}function
|
|
484
|
+
`}function Or(e){return`"use client";
|
|
477
485
|
|
|
478
486
|
/**
|
|
479
487
|
* SocialLoginButtons \u2014 generated by create-sailor.
|
|
@@ -516,23 +524,23 @@ export function SocialLoginButtons() {
|
|
|
516
524
|
</div>
|
|
517
525
|
);
|
|
518
526
|
}
|
|
519
|
-
`}function Cn(e){
|
|
520
|
-
`+t):
|
|
527
|
+
`}function Cn(e){Pe.mkdirSync(e,{recursive:!0})}function Ur(e,t){let o=be.join(e,".env.example");Pe.existsSync(o)?Pe.appendFileSync(o,`
|
|
528
|
+
`+t):Pe.writeFileSync(o,t)}function Lr(e){let t=`# ${e.name} (${e.id}) \u2014 ${e.docs}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
521
529
|
`);return`${t}
|
|
522
530
|
${o}
|
|
523
|
-
`}async function In(e,t,o){if(!t||t.length===0)return;let n=
|
|
524
|
-
`);
|
|
525
|
-
`)}async function Rn(e,t,o){let n=
|
|
526
|
-
`);
|
|
531
|
+
`}async function In(e,t,o){if(!t||t.length===0)return;let n=be.join(e,"apps","web");if(!Pe.existsSync(n))return;let r=new Set,i=[];for(let l of t){if(r.has(l))continue;r.add(l);let m=bn(l);m&&i.push(m)}if(i.length===0)return;for(let l of i)Ur(e,Lr(l));let s=be.join(n,"src","app","api","auth","callback");for(let l of i){let m=be.join(s,l.id);Cn(m);let y=be.join(m,"route.ts");Pe.writeFileSync(y,Tr(l,o))}let c=be.join(n,"src","components","auth");Cn(c);let a=be.join(c,"SocialLoginButtons.tsx");Pe.writeFileSync(a,Or(i))}import ae from"fs";import Yt from"path";var Ct=[{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 An(e){return Ct.find(t=>t.id===e)}var Da=Ct.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function wn(e){return ae.existsSync(e)?ae.readFileSync(e,"utf-8"):null}function Mr(e,t){let o=Yt.join(e,".env.example"),n=wn(o);if(n===null)return;let r=`# Cache: ${t.name}`;if(n.includes(r))return;let i=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(s=>`${s}=""`),""].join(`
|
|
532
|
+
`);ae.appendFileSync(o,i)}function Fr(e,t,o){let n=Yt.join(e,".env.example"),r=wn(n);if(r===null)return;let i=new RegExp(`^${t}=.*$`,"m");i.test(r)?ae.writeFileSync(n,r.replace(i,`${t}="${o}"`)):ae.appendFileSync(n,`${t}="${o}"
|
|
533
|
+
`)}async function Rn(e,t,o){let n=Yt.join(e,"packages","cache");if(t==="none"){ae.existsSync(n)&&ae.rmSync(n,{recursive:!0,force:!0});return}let r=An(t);if(!r)throw new Error(`Unknown cache provider "${t}". Valid ids: ${Ct.map(i=>i.id).join(", ")}`);ae.existsSync(n)&&(Mr(e,r),Fr(e,"CACHE_PROVIDER",t))}var It=[{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 kn(e){return It.find(t=>t.id===e)}var Ya=It.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});import te from"fs";import qt from"path";function H(e,t){let o=qt.join(e,".env.example");if(!te.existsSync(o)||t.envVars.length===0)return;let r=`# ${t.category??t.name}: ${t.name}`;if(te.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(`
|
|
534
|
+
`);te.appendFileSync(o,c)}function X(e,t,o){let n=qt.join(e,".env.example");if(!te.existsSync(n))return;let r=te.readFileSync(n,"utf-8"),i=`${t}="${o}"`,s=new RegExp(`^${t}=.*$`,"m");if(s.test(r)){te.writeFileSync(n,r.replace(s,i));return}let c=r.endsWith(`
|
|
527
535
|
`)?"":`
|
|
528
|
-
`;
|
|
529
|
-
`)}function Y(e,t){let o=
|
|
530
|
-
`)[0]??""))return;
|
|
536
|
+
`;te.appendFileSync(n,`${c}${i}
|
|
537
|
+
`)}function Y(e,t){let o=qt.join(e,"packages",t);te.existsSync(o)&&te.rmSync(o,{recursive:!0,force:!0})}async function xn(e,t,o="global"){let n=kn(t);if(!n)throw new Error(`Unknown captcha provider "${t}". Valid ids: ${It.map(r=>r.id).join(", ")}`);if(n.id==="none"){Y(e,"captcha");return}n.envVars.length>0&&H(e,{category:"Captcha",name:n.name,envVars:n.envVars,docs:n.docs}),X(e,"CAPTCHA_PROVIDER",n.id)}import wt from"fs";import Dr from"path";var At=[{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 Nn(e){return At.find(t=>t.id===e)}var Qa=At.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Tn(e,t,o="global"){let n=Nn(t);if(!n)throw new Error(`Unknown CMS provider "${t}". Valid ids: ${At.map(i=>i.id).join(", ")}`);let r=Dr.join(e,"apps","studio");if(n.id==="none"){Y(e,"sanity"),wt.existsSync(r)&&wt.rmSync(r,{recursive:!0,force:!0});return}n.id!=="sanity"&&(Y(e,"sanity"),wt.existsSync(r)&&wt.rmSync(r,{recursive:!0,force:!0})),n.envVars.length>0&&H(e,{category:"CMS",name:n.name,envVars:n.envVars,docs:n.docs}),X(e,"CMS_PROVIDER",n.id)}import R from"fs";import w from"path";var jr=w.join("apps","web"),Gt=w.join("src","components","compliance"),ct=w.join("src","app","[locale]");function z(e){return w.join(e,jr)}function Kr(e){R.mkdirSync(e,{recursive:!0})}function q(e,t){Kr(w.dirname(e)),R.writeFileSync(e,t,"utf8")}function Wt(e,t){let o=w.join(e,".env.example");if(R.existsSync(o)){if(R.readFileSync(o,"utf8").includes(t.split(`
|
|
538
|
+
`)[0]??""))return;R.appendFileSync(o,`
|
|
531
539
|
`+t+`
|
|
532
|
-
`)}else
|
|
533
|
-
`)}var
|
|
540
|
+
`)}else R.writeFileSync(o,t+`
|
|
541
|
+
`)}var pt=`> **TEMPLATE \u2014 NOT LEGAL ADVICE.** This document is a boilerplate scaffold only.
|
|
534
542
|
> Consult a qualified lawyer in every jurisdiction you operate in before publishing.
|
|
535
|
-
> \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`,Vr=`import Link from "next/link";
|
|
536
544
|
import { IcpFooter } from "@/components/compliance/IcpFooter";
|
|
537
545
|
import { PublicSecurityFooter } from "@/components/compliance/PublicSecurityFooter";
|
|
538
546
|
|
|
@@ -583,7 +591,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
583
591
|
</footer>
|
|
584
592
|
);
|
|
585
593
|
}
|
|
586
|
-
`,
|
|
594
|
+
`,Br=`export function IcpFooter() {
|
|
587
595
|
const icp = process.env.NEXT_PUBLIC_ICP_BEIAN;
|
|
588
596
|
if (!icp) return null;
|
|
589
597
|
return (
|
|
@@ -597,7 +605,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
597
605
|
</a>
|
|
598
606
|
);
|
|
599
607
|
}
|
|
600
|
-
|
|
608
|
+
`,$r=`export function PublicSecurityFooter() {
|
|
601
609
|
const psb = process.env.NEXT_PUBLIC_PSB_BEIAN;
|
|
602
610
|
const psbCode = process.env.NEXT_PUBLIC_PSB_CODE;
|
|
603
611
|
if (!psb) return null;
|
|
@@ -841,7 +849,7 @@ function CategoryRow({ id, title, description, checked, disabled, onChange }: Ca
|
|
|
841
849
|
</label>
|
|
842
850
|
);
|
|
843
851
|
}
|
|
844
|
-
|
|
852
|
+
`,qr=`interface AigcDisclaimerProps {
|
|
845
853
|
className?: string;
|
|
846
854
|
}
|
|
847
855
|
|
|
@@ -861,13 +869,13 @@ export function AigcDisclaimer({ className }: AigcDisclaimerProps) {
|
|
|
861
869
|
</div>
|
|
862
870
|
);
|
|
863
871
|
}
|
|
864
|
-
`,
|
|
872
|
+
`,Gr=`---
|
|
865
873
|
title: Privacy Policy / \u9690\u79C1\u653F\u7B56
|
|
866
874
|
description: How {{COMPANY_NAME}} collects, uses, shares, and protects personal information.
|
|
867
875
|
lastUpdated: 2026-01-01
|
|
868
876
|
---
|
|
869
877
|
|
|
870
|
-
${
|
|
878
|
+
${pt}
|
|
871
879
|
|
|
872
880
|
# Privacy Policy / \u9690\u79C1\u653F\u7B56
|
|
873
881
|
|
|
@@ -1006,13 +1014,13 @@ or in-app notification before the change takes effect.
|
|
|
1006
1014
|
**Data Controller / \u6570\u636E\u63A7\u5236\u8005:** {{COMPANY_NAME}}
|
|
1007
1015
|
**Address / \u5730\u5740:** {{COMPANY_ADDRESS}}
|
|
1008
1016
|
**Email / \u90AE\u7BB1:** {{COMPANY_CONTACT_EMAIL}}
|
|
1009
|
-
`,
|
|
1017
|
+
`,Wr=`---
|
|
1010
1018
|
title: Terms of Service / \u670D\u52A1\u6761\u6B3E
|
|
1011
1019
|
description: Terms governing your use of {{COMPANY_NAME}}.
|
|
1012
1020
|
lastUpdated: 2026-01-01
|
|
1013
1021
|
---
|
|
1014
1022
|
|
|
1015
|
-
${
|
|
1023
|
+
${pt}
|
|
1016
1024
|
|
|
1017
1025
|
# Terms of Service / \u670D\u52A1\u6761\u6B3E
|
|
1018
1026
|
|
|
@@ -1125,13 +1133,13 @@ conflict-of-laws principles. Disputes shall be resolved in the competent courts
|
|
|
1125
1133
|
## 11. Contact / \u8054\u7CFB\u65B9\u5F0F
|
|
1126
1134
|
|
|
1127
1135
|
{{COMPANY_NAME}} \xB7 {{COMPANY_ADDRESS}} \xB7 {{COMPANY_CONTACT_EMAIL}}
|
|
1128
|
-
`,
|
|
1136
|
+
`,Hr=`---
|
|
1129
1137
|
title: AIGC Disclosure / \u751F\u6210\u5F0F AI \u670D\u52A1\u8BF4\u660E
|
|
1130
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
|
|
1131
1139
|
lastUpdated: 2026-01-01
|
|
1132
1140
|
---
|
|
1133
1141
|
|
|
1134
|
-
${
|
|
1142
|
+
${pt}
|
|
1135
1143
|
|
|
1136
1144
|
# AIGC Disclosure / \u751F\u6210\u5F0F AI \u670D\u52A1\u8BF4\u660E
|
|
1137
1145
|
|
|
@@ -1207,13 +1215,13 @@ human professional.
|
|
|
1207
1215
|
|
|
1208
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
|
|
1209
1217
|
\u7528\u4E8E\u533B\u7597\u3001\u6CD5\u5F8B\u3001\u91D1\u878D\u6216\u5B89\u5168\u6538\u5173\u7684\u51B3\u7B56\u3002
|
|
1210
|
-
`,
|
|
1218
|
+
`,Xr=`---
|
|
1211
1219
|
title: Cross-Border Data Notice / \u6570\u636E\u51FA\u5883\u544A\u77E5
|
|
1212
1220
|
description: Notice of cross-border transfer of personal information, per PIPL.
|
|
1213
1221
|
lastUpdated: 2026-01-01
|
|
1214
1222
|
---
|
|
1215
1223
|
|
|
1216
|
-
${
|
|
1224
|
+
${pt}
|
|
1217
1225
|
|
|
1218
1226
|
# Cross-Border Data Notice / \u6570\u636E\u51FA\u5883\u544A\u77E5
|
|
1219
1227
|
|
|
@@ -1272,13 +1280,13 @@ Refusal may limit your ability to use features that rely on overseas services.
|
|
|
1272
1280
|
- \u4F20\u8F93\u4E2D\u52A0\u5BC6 (TLS 1.2+) \u53CA\u9759\u6001\u52A0\u5BC6 (AES-256)\u3002
|
|
1273
1281
|
- \u5408\u540C\u6761\u6B3E\u8981\u6C42\u5883\u5916\u63A5\u6536\u65B9\u63D0\u4F9B\u7B49\u540C\u4E8E PIPL \u7684\u4FDD\u62A4\u3002
|
|
1274
1282
|
- \u8BBF\u95EE\u63A7\u5236\u3001\u5BA1\u8BA1\u65E5\u5FD7\u548C\u8FDD\u89C4\u901A\u77E5\u7A0B\u5E8F\u3002
|
|
1275
|
-
`,
|
|
1283
|
+
`,zr=`---
|
|
1276
1284
|
title: Personal Information Collection List / \u4E2A\u4EBA\u4FE1\u606F\u6536\u96C6\u6E05\u5355
|
|
1277
1285
|
description: Itemized list of personal information collected, per PIPL Article 17.
|
|
1278
1286
|
lastUpdated: 2026-01-01
|
|
1279
1287
|
---
|
|
1280
1288
|
|
|
1281
|
-
${
|
|
1289
|
+
${pt}
|
|
1282
1290
|
|
|
1283
1291
|
# Personal Information Collection List / \u4E2A\u4EBA\u4FE1\u606F\u6536\u96C6\u6E05\u5355
|
|
1284
1292
|
|
|
@@ -1314,19 +1322,19 @@ To exercise your rights (access, rectify, delete, export, withdraw consent), con
|
|
|
1314
1322
|
**{{COMPANY_CONTACT_EMAIL}}**.
|
|
1315
1323
|
|
|
1316
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
|
|
1317
|
-
`;async function
|
|
1318
|
-
`))}async function
|
|
1319
|
-
`))}async function
|
|
1320
|
-
`))}async function
|
|
1321
|
-
`))}async function
|
|
1322
|
-
`)}import
|
|
1323
|
-
`+t):
|
|
1324
|
-
DATABASE_URL="${
|
|
1325
|
-
`),!
|
|
1326
|
-
`+t):
|
|
1325
|
+
`;async function Jr(e,t){let o=z(e);if(!R.existsSync(o))return;let n=w.join(o,"src","components","compliance","Footer.tsx");q(n,Vr);let r=w.join(o,"src","components","compliance","README.md");q(r,["# Compliance Components","",`Auto-generated by \`create-sailor\` for region: \`${t}\`.`,"","## Mounting the Footer","","```tsx",'import { Footer } from "@/components/compliance/Footer";','import { CookieBanner } from "@/components/compliance/CookieBanner";',"","export default function RootLayout({ children }) {"," return ("," <html>"," <body>"," {children}",` <Footer region="${t}" />`," <CookieBanner />"," </body>"," </html>"," );","}","```","","## Placeholders","","Policy MDX files contain `{{COMPANY_NAME}}`, `{{COMPANY_ADDRESS}}`,","`{{COMPANY_CONTACT_EMAIL}}`, `{{GOVERNING_JURISDICTION}}`, and","`{{AIGC_ALGORITHM_FILING_NUMBER}}`. These are filled by the `brand-apply` step.",""].join(`
|
|
1326
|
+
`))}async function Qr(e,t){let o=z(e);if(!R.existsSync(o))return;let n=w.join(o,"src","components","compliance","CookieBanner.tsx");q(n,Yr)}async function Zr(e,t){let o=z(e);if(!R.existsSync(o))return;let n=w.join(o,ct,"privacy","page.mdx");q(n,Gr)}async function ei(e,t){let o=z(e);if(!R.existsSync(o))return;let n=w.join(o,ct,"terms","page.mdx");q(n,Wr)}async function ti(e){let t=z(e);if(!R.existsSync(t))return;let o=w.join(t,Gt,"IcpFooter.tsx");q(o,Br),Wt(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 ni(e){let t=z(e);if(!R.existsSync(t))return;let o=w.join(t,Gt,"PublicSecurityFooter.tsx");q(o,$r),Wt(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 oi(e){let t=z(e);if(!R.existsSync(t))return;let o=w.join(t,Gt,"AigcDisclaimer.tsx");q(o,qr),Wt(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 ri(e){let t=z(e);if(!R.existsSync(t))return;let o=w.join(t,ct,"aigc-disclosure","page.mdx");q(o,Hr)}async function ii(e){let t=z(e);if(!R.existsSync(t))return;let o=w.join(t,ct,"cross-border-data","page.mdx");q(o,Xr)}async function si(e){let t=z(e);if(!R.existsSync(t))return;let o=w.join(t,ct,"pii-collection","page.mdx");q(o,zr)}async function On(e,t){try{await Jr(e,t),await Qr(e,t),await Zr(e,t),await ei(e,t),(t==="cn"||t==="hybrid")&&(await ti(e),await ni(e),await oi(e),await ri(e),await ii(e),await si(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 ai from"fs";import ci from"path";async function Un(e,t){let o=ci.join(e,"nebutra.config.json");ai.writeFileSync(o,JSON.stringify(t,null,2)+`
|
|
1330
|
+
`)}import ce from"fs";import Ht from"path";function pi(e){ce.existsSync(e)&&ce.rmSync(e,{recursive:!0,force:!0})}function Ln(e,t){return e.replace(/provider\s*=\s*"postgresql"/,`provider = "${t}"`)}function li(e){return e.replace(/^\s*directUrl\s*=\s*env\([^)]*\)\s*\n/gm,"")}function Mn(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 di(e,t){let o=Ht.join(e,".env.example");ce.existsSync(o)?ce.appendFileSync(o,`
|
|
1331
|
+
`+t):ce.writeFileSync(o,t)}async function Fn(e,t,o){let n=Ht.join(e,"packages","db");if(t==="none"){pi(n);return}let r=Ht.join(n,"prisma","schema.prisma"),i;if(t==="postgresql"?i=`postgresql://postgres:postgres@localhost:5432/${o}`:t==="mysql"?i=`mysql://root:root@localhost:3306/${o}`:i="file:./dev.db",di(e,`# Database (${t})
|
|
1332
|
+
DATABASE_URL="${i}"
|
|
1333
|
+
`),!ce.existsSync(r))return;let s=ce.readFileSync(r,"utf8"),c=s;t==="mysql"?(c=Ln(c,"mysql"),c=Mn(c)):t==="sqlite"&&(c=Ln(c,"sqlite"),c=li(c),c=Mn(c)),c!==s&&ce.writeFileSync(r,c)}import Ze from"fs";import D from"path";import{fileURLToPath as ui}from"url";var mi=ui(import.meta.url),Dn=D.dirname(mi);function gi(){let e=[D.join(Dn,"..","templates","deploy"),D.join(Dn,"..","..","templates","deploy")];for(let t of e)if(Ze.existsSync(t))return t;return e[0]}async function jn(e,t){if(t==="none")return;let o=gi();t==="vercel"?await Ze.promises.copyFile(D.join(o,"vercel.json"),D.join(e,"vercel.json")):t==="railway"?await Ze.promises.copyFile(D.join(o,"railway.toml"),D.join(e,"railway.toml")):t==="cloudflare"?await Ze.promises.copyFile(D.join(o,"wrangler.toml"),D.join(e,"wrangler.toml")):t==="selfhost"&&(await Ze.promises.copyFile(D.join(o,"docker-compose.yml"),D.join(e,"docker-compose.yml")),await Ze.promises.copyFile(D.join(o,"Dockerfile.web"),D.join(e,"Dockerfile.web")))}import pe from"fs";import Ce from"path";import{fileURLToPath as fi}from"url";var yi=fi(import.meta.url),Kn=Ce.dirname(yi),Si=/\.(md|mdx|json|ts|tsx|js|mjs|cjs|yaml|yml|css|html|vue)$/,Vn=["fumadocs","nextra","mintlify","docusaurus","vitepress"];function hi(e){let t=[Ce.join(Kn,"..","templates","docs",e),Ce.join(Kn,"..","..","templates","docs",e)];for(let o of t)if(pe.existsSync(o))return o;return t[0]}async function Bn(e,t){if(t.framework==="none")return;if(!Vn.includes(t.framework))throw new Error(`Unsupported docs framework: "${t.framework}". Supported: ${Vn.join(", ")}, none.`);let o=hi(t.framework),n=Ce.join(e,"apps","docs");if(!pe.existsSync(o))throw new Error(`Docs template not found: ${o}`);$n(o,n),Yn(n,{"{PRODUCT_NAME}":t.projectName})}function $n(e,t){pe.mkdirSync(t,{recursive:!0});for(let o of pe.readdirSync(e,{withFileTypes:!0})){let n=Ce.join(e,o.name),r=Ce.join(t,o.name);o.isDirectory()?$n(n,r):pe.copyFileSync(n,r)}}function Yn(e,t){for(let o of pe.readdirSync(e,{withFileTypes:!0})){let n=Ce.join(e,o.name);if(o.isDirectory())Yn(n,t);else if(Si.test(o.name)){let r=pe.readFileSync(n,"utf-8");for(let[i,s]of Object.entries(t))r=r.split(i).join(s);pe.writeFileSync(n,r)}}}import Ae from"fs";import Ie from"path";var Rt=[{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 qn(e){return Rt.find(t=>t.id===e)}var bc=Rt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});var we="@nebutra/email";function Xt(e,t){Ae.mkdirSync(Ie.dirname(e),{recursive:!0}),Ae.writeFileSync(e,t)}function Ei(e,t){let o=Ie.join(e,".env.example");Ae.existsSync(o)?Ae.appendFileSync(o,`
|
|
1334
|
+
`+t):Ae.writeFileSync(o,t)}function vi(e){if(e.envVars.length===0)return"";let t=`# Email provider: ${e.name}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
1327
1335
|
`);return`${t}
|
|
1328
1336
|
${o}
|
|
1329
|
-
`}var
|
|
1337
|
+
`}var _i=`/**
|
|
1330
1338
|
* Public inputs accepted by the \`sendEmail\` wrapper.
|
|
1331
1339
|
* Keep the surface minimal \u2014 individual providers may accept more, but
|
|
1332
1340
|
* consumers should code against this shape for portability.
|
|
@@ -1346,14 +1354,14 @@ export interface SendEmailResult {
|
|
|
1346
1354
|
/** Raw provider response \u2014 useful for debugging, not a stable contract. */
|
|
1347
1355
|
raw?: unknown;
|
|
1348
1356
|
}
|
|
1349
|
-
`;function
|
|
1350
|
-
* @${
|
|
1357
|
+
`;function bi(){return`/**
|
|
1358
|
+
* @${we.slice(1)} \u2014 Resend transactional email wrapper.
|
|
1351
1359
|
*
|
|
1352
1360
|
* Docs: https://resend.com/docs
|
|
1353
1361
|
*/
|
|
1354
1362
|
import { Resend } from "resend";
|
|
1355
1363
|
|
|
1356
|
-
import type { SendEmailInput, SendEmailResult } from "./types
|
|
1364
|
+
import type { SendEmailInput, SendEmailResult } from "./types";
|
|
1357
1365
|
|
|
1358
1366
|
const apiKey = process.env.RESEND_API_KEY;
|
|
1359
1367
|
if (!apiKey) {
|
|
@@ -1384,15 +1392,15 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1384
1392
|
return { id: data?.id, raw: data };
|
|
1385
1393
|
}
|
|
1386
1394
|
|
|
1387
|
-
export type { SendEmailInput, SendEmailResult } from "./types
|
|
1388
|
-
`}function
|
|
1389
|
-
* @${
|
|
1395
|
+
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1396
|
+
`}function Pi(){return`/**
|
|
1397
|
+
* @${we.slice(1)} \u2014 Postmark transactional email wrapper.
|
|
1390
1398
|
*
|
|
1391
1399
|
* Docs: https://postmarkapp.com/developer
|
|
1392
1400
|
*/
|
|
1393
1401
|
import { ServerClient } from "postmark";
|
|
1394
1402
|
|
|
1395
|
-
import type { SendEmailInput, SendEmailResult } from "./types
|
|
1403
|
+
import type { SendEmailInput, SendEmailResult } from "./types";
|
|
1396
1404
|
|
|
1397
1405
|
const apiKey = process.env.POSTMARK_API_KEY;
|
|
1398
1406
|
if (!apiKey) {
|
|
@@ -1422,9 +1430,9 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1422
1430
|
return { id: response.MessageID, raw: response };
|
|
1423
1431
|
}
|
|
1424
1432
|
|
|
1425
|
-
export type { SendEmailInput, SendEmailResult } from "./types
|
|
1426
|
-
`}function
|
|
1427
|
-
* @${
|
|
1433
|
+
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1434
|
+
`}function Ci(){return`/**
|
|
1435
|
+
* @${we.slice(1)} \u2014 AWS SES v2 transactional email wrapper.
|
|
1428
1436
|
*
|
|
1429
1437
|
* Docs: https://docs.aws.amazon.com/ses
|
|
1430
1438
|
*/
|
|
@@ -1433,7 +1441,7 @@ import {
|
|
|
1433
1441
|
SESv2Client,
|
|
1434
1442
|
} from "@aws-sdk/client-sesv2";
|
|
1435
1443
|
|
|
1436
|
-
import type { SendEmailInput, SendEmailResult } from "./types
|
|
1444
|
+
import type { SendEmailInput, SendEmailResult } from "./types";
|
|
1437
1445
|
|
|
1438
1446
|
const region = process.env.AWS_SES_REGION;
|
|
1439
1447
|
if (!region) {
|
|
@@ -1478,16 +1486,16 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1478
1486
|
return { id: response.MessageId, raw: response };
|
|
1479
1487
|
}
|
|
1480
1488
|
|
|
1481
|
-
export type { SendEmailInput, SendEmailResult } from "./types
|
|
1482
|
-
`}function
|
|
1483
|
-
* @${
|
|
1489
|
+
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1490
|
+
`}function Ii(){return`/**
|
|
1491
|
+
* @${we.slice(1)} \u2014 \u963F\u91CC\u4E91\u90AE\u4EF6\u63A8\u9001 (Aliyun DirectMail) wrapper.
|
|
1484
1492
|
*
|
|
1485
1493
|
* Docs: https://help.aliyun.com/product/29412.html
|
|
1486
1494
|
*/
|
|
1487
1495
|
import Dm20151123, * as $Dm20151123 from "@alicloud/dm20151123";
|
|
1488
1496
|
import * as $OpenApi from "@alicloud/openapi-client";
|
|
1489
1497
|
|
|
1490
|
-
import type { SendEmailInput, SendEmailResult } from "./types
|
|
1498
|
+
import type { SendEmailInput, SendEmailResult } from "./types";
|
|
1491
1499
|
|
|
1492
1500
|
const accessKeyId = process.env.ALIYUN_DM_ACCESS_KEY;
|
|
1493
1501
|
const accessKeySecret = process.env.ALIYUN_DM_ACCESS_SECRET;
|
|
@@ -1528,15 +1536,15 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1528
1536
|
return { id: response.body?.requestId, raw: response };
|
|
1529
1537
|
}
|
|
1530
1538
|
|
|
1531
|
-
export type { SendEmailInput, SendEmailResult } from "./types
|
|
1532
|
-
`}function
|
|
1533
|
-
* @${
|
|
1539
|
+
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1540
|
+
`}function Ai(){return`/**
|
|
1541
|
+
* @${we.slice(1)} \u2014 \u817E\u8BAF\u4E91 SES wrapper.
|
|
1534
1542
|
*
|
|
1535
1543
|
* Docs: https://cloud.tencent.com/document/product/1288
|
|
1536
1544
|
*/
|
|
1537
1545
|
import tencentcloud from "tencentcloud-sdk-nodejs-ses";
|
|
1538
1546
|
|
|
1539
|
-
import type { SendEmailInput, SendEmailResult } from "./types
|
|
1547
|
+
import type { SendEmailInput, SendEmailResult } from "./types";
|
|
1540
1548
|
|
|
1541
1549
|
const secretId = process.env.TENCENT_SECRET_ID;
|
|
1542
1550
|
const secretKey = process.env.TENCENT_SECRET_KEY;
|
|
@@ -1576,15 +1584,15 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1576
1584
|
return { id: response.MessageId, raw: response };
|
|
1577
1585
|
}
|
|
1578
1586
|
|
|
1579
|
-
export type { SendEmailInput, SendEmailResult } from "./types
|
|
1580
|
-
`}function
|
|
1581
|
-
* @${
|
|
1587
|
+
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1588
|
+
`}function wi(){return`/**
|
|
1589
|
+
* @${we.slice(1)} \u2014 \u7F51\u6613\u4F01\u4E1A\u90AE\u7BB1 via SMTP (nodemailer).
|
|
1582
1590
|
*
|
|
1583
1591
|
* Docs: https://qiye.163.com/help
|
|
1584
1592
|
*/
|
|
1585
1593
|
import nodemailer from "nodemailer";
|
|
1586
1594
|
|
|
1587
|
-
import type { SendEmailInput, SendEmailResult } from "./types
|
|
1595
|
+
import type { SendEmailInput, SendEmailResult } from "./types";
|
|
1588
1596
|
|
|
1589
1597
|
const user = process.env.NETEASE_USER;
|
|
1590
1598
|
const pass = process.env.NETEASE_PASS;
|
|
@@ -1614,25 +1622,25 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1614
1622
|
return { id: response.messageId, raw: response };
|
|
1615
1623
|
}
|
|
1616
1624
|
|
|
1617
|
-
export type { SendEmailInput, SendEmailResult } from "./types
|
|
1618
|
-
`}function
|
|
1619
|
-
`}}function
|
|
1620
|
-
`}async function
|
|
1625
|
+
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1626
|
+
`}function Ri(e){switch(e){case"resend":return bi();case"postmark":return Pi();case"ses":return Ci();case"aliyun-dm":return Ii();case"tencent-ses":return Ai();case"netease":return wi();case"none":return`export {};
|
|
1627
|
+
`}}function ki(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:we,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)+`
|
|
1628
|
+
`}async function Gn(e,t,o){if(t==="none")return;let n=qn(t);if(!n)throw new Error(`Unknown email provider "${t}". Valid ids: ${Rt.map(s=>s.id).join(", ")}`);let r=Ie.join(e,"packages","email"),i=Ie.join(r,"src");Ae.existsSync(i)&&Ae.rmSync(i,{recursive:!0,force:!0}),Xt(Ie.join(r,"package.json"),ki(n)),Xt(Ie.join(i,"types.ts"),_i),Xt(Ie.join(i,"index.ts"),Ri(n.id)),Ei(e,vi(n))}import kt from"fs";import Wn from"path";async function Hn(e,t){let o=`# Automatically injected by create-sailor
|
|
1621
1629
|
DATABASE_URL="${String(t.databaseUrl)}"
|
|
1622
1630
|
|
|
1623
1631
|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="${String(t.clerkPublishable)}"
|
|
1624
1632
|
CLERK_SECRET_KEY="${String(t.clerkSecret)}"
|
|
1625
1633
|
|
|
1626
1634
|
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
|
|
1627
|
-
`,n=
|
|
1628
|
-
`+o)}import
|
|
1635
|
+
`,n=Wn.join(e,".env"),r=Wn.join(e,".env.local");!kt.existsSync(n)&&!kt.existsSync(r)?kt.writeFileSync(r,o):kt.appendFileSync(r,`
|
|
1636
|
+
`+o)}import zn from"crypto";import lt from"fs";import Xn from"path";function Re(e){return zn.randomBytes(e).toString("base64").replace(/=+$/,"")}function xi(e){return zn.randomBytes(e).toString("hex")}function Ni(){return{AUTH_SECRET:Re(32),NEXTAUTH_SECRET:Re(32),BETTER_AUTH_SECRET:Re(32),JWT_SECRET:Re(32),WEBHOOK_SECRET:xi(16),ENCRYPTION_KEY:Re(32),API_SECRET_KEY:Re(32),SESSION_SECRET:Re(32)}}function Ti(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function Oi(e,t,o){let n=new RegExp(`^(?!#)\\s*${t}=.*$`,"gm"),r=Ti(o);return e.replace(n,`${t}="${r}"`)}async function Jn(e){let t=Xn.join(e,".env.example"),o=Xn.join(e,".env.local");if(!lt.existsSync(t))return null;try{let n=lt.readFileSync(t,"utf8"),r=Ni(),s=lt.existsSync(o)?lt.readFileSync(o,"utf8"):n;for(let c of Object.keys(r))s=Oi(s,c,r[c]);return lt.writeFileSync(o,s),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 xt=[{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 Qn(e){return xt.find(t=>t.id===e)}var Lc=xt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Zn(e,t,o="global"){let n=Qn(t);if(!n)throw new Error(`Unknown feature-flags provider "${t}". Valid ids: ${xt.map(r=>r.id).join(", ")}`);if(n.id==="none"){Y(e,"feature-flags");return}n.envVars.length>0&&H(e,{category:"Feature Flags",name:n.name,envVars:n.envVars,docs:n.docs}),X(e,"FEATURE_FLAGS_PROVIDER",n.id)}import{execFileSync as no}from"child_process";import O from"fs";import Ui from"os";import le from"path";import Li from"ignore";var Mi={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 Fi(e,t){return`https://github.com/${e}/archive/refs/heads/${t}.tar.gz`}function Di(e,t){return`https://github.com/${e}/archive/${t}.tar.gz`}function oo(e){return/^[0-9a-f]{7,40}$/i.test(e)}function ji(e,t){let o=`https://github.com/${e}.git`,n=[`refs/heads/${t}`,`refs/tags/${t}`];for(let r of n)try{let i=no("git",["ls-remote",o,r],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(!i)continue;let[s]=i.split(/\s+/);if(oo(s))return s}catch{}return null}async function Ki(e,t){if(oo(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=ji(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 ro(e,t,o){let n=O.readdirSync(t,{withFileTypes:!0});for(let r of n){if(r.name==="node_modules"||r.name===".git")continue;let i=le.join(t,r.name),s=le.relative(e,i).split(le.sep).join("/");r.isDirectory()?(o.push(s+"/"),ro(e,i,o)):o.push(s)}}function Vi(e){let t=le.join(e,".templateignore");if(!O.existsSync(t))return;let o=O.readFileSync(t,"utf8"),n=Li().add(o),r=[];ro(e,e,r);let i=r.map(a=>a.endsWith("/")?a.slice(0,-1):a),s=new Set(n.filter(i)),c=i.filter(a=>!s.has(a)).sort((a,l)=>l.split("/").length-a.split("/").length);for(let a of c){let l=le.join(e,a);try{O.existsSync(l)&&O.rmSync(l,{recursive:!0,force:!0})}catch{}}try{O.existsSync(t)&&O.rmSync(t,{force:!0})}catch{}}async function eo(e,t){let o=await fetch(e,{headers:{Accept:"application/octet-stream","User-Agent":"create-sailor"}});if(!o.ok)throw new Error(`Failed to download archive (${o.status})`);let n=Buffer.from(await o.arrayBuffer());if(n.byteLength<1024)throw new Error(`Downloaded archive is unexpectedly small (${n.byteLength} bytes)`);O.writeFileSync(t,n);let r=O.statSync(t);if(r.size!==n.byteLength)throw new Error(`Archive write verification failed (${r.size} != ${n.byteLength})`);return r.size}function to(e,t){no("tar",["-xzf",e,"-C",t,"--strip-components=1"],{stdio:"ignore"})}function io(e){try{let t=O.readdirSync(e);for(let o of t)O.rmSync(le.join(e,o),{recursive:!0,force:!0})}catch{}}async function Bi(e,t){let o=O.mkdtempSync(le.join(Ui.tmpdir(),"create-sailor-")),n=le.join(o,"template.tar.gz");try{let r=await Ki(e.repo,e.ref);await eo(Di(e.repo,r),n),to(n,t)}catch(r){if(!e.allowMutableFallback)throw r;io(t),await eo(Fi(e.repo,e.ref),n),to(n,t)}finally{O.rmSync(o,{recursive:!0,force:!0})}}async function so(e){O.mkdirSync(e,{recursive:!0});let o=process.env.SAILOR_TEMPLATE_SOURCE==="main"?["main"]:["mirror","main"],n;for(let r of o){let i=Mi[r];try{if(await Bi(i,e),i.applyIgnore)try{Vi(e)}catch(s){throw new Error(`Template cloned, but .templateignore processing failed: ${s instanceof Error?s.message:String(s)}`)}return}catch(s){n=s instanceof Error?s:new Error(String(s)),io(e)}}throw new Error(`Failed to download the template from any source. Last error: ${n?.message??"unknown"}
|
|
1629
1637
|
|
|
1630
1638
|
Ensure you have internet access and 'tar' installed.
|
|
1631
|
-
You can override the source with SAILOR_TEMPLATE_REPO / SAILOR_TEMPLATE_REF env vars.`)}import
|
|
1632
|
-
`);
|
|
1633
|
-
`);
|
|
1634
|
-
`)}function
|
|
1635
|
-
`+t):
|
|
1639
|
+
You can override the source with SAILOR_TEMPLATE_REPO / SAILOR_TEMPLATE_REF env vars.`)}import ke from"fs";import zt from"path";async function ao(e,t){let o=zt.join(e,"packages","mcp");if(t==="off"){ke.existsSync(o)&&ke.rmSync(o,{recursive:!0,force:!0});let r=zt.join(e,"packages","cli","src","commands","mcp-server.ts");ke.existsSync(r)&&ke.rmSync(r,{force:!0});return}let n=zt.join(e,".env.example");if(ke.existsSync(n)){let r=ke.readFileSync(n,"utf-8"),i="# MCP Server (Agent-friendly API)";if(!r.includes(i)){let s=["","# =============================================",i,"# 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(`
|
|
1640
|
+
`);ke.appendFileSync(n,s)}}}import dt from"fs";import co from"path";async function po(e,t,o){let n=co.join(e,"packages","metering"),r;if(t==="auto"?r=o!=="none"?"on":"off":t==="on"||t==="off"?r=t:r="off",r==="off"){dt.existsSync(n)&&dt.rmSync(n,{recursive:!0,force:!0});return}let i=co.join(e,".env.example");if(!dt.existsSync(i))return;let s=dt.readFileSync(i,"utf-8"),c="# Metering (usage-based billing)";if(!s.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(`
|
|
1641
|
+
`);dt.appendFileSync(i,a)}}import ne from"fs";import U from"path";var lo=[{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:""}],Qc=lo.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function uo(e){return lo.find(t=>t.id===e)}var Jt={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 $i(e){ne.mkdirSync(e,{recursive:!0})}function Qt(e,t){let o=U.join(e,"package.json");if(!ne.existsSync(o))return;let n;try{n=JSON.parse(ne.readFileSync(o,"utf-8"))}catch(c){console.error(`Failed to parse ${o}:`,c);return}let i={...n.dependencies??{}};for(let[c,a]of Object.entries(t))i[c]||(i[c]=a);let s={...n,dependencies:i};ne.writeFileSync(o,JSON.stringify(s,null,2)+`
|
|
1642
|
+
`)}function J(e,t){$i(U.dirname(e)),ne.writeFileSync(e,t)}function ut(e,t){let o=U.join(e,".env.example");ne.existsSync(o)?ne.appendFileSync(o,`
|
|
1643
|
+
`+t):ne.writeFileSync(o,t)}function Yi(e){let t=`import * as Sentry from "@sentry/nextjs";
|
|
1636
1644
|
|
|
1637
1645
|
Sentry.init({
|
|
1638
1646
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
@@ -1675,14 +1683,14 @@ export async function register() {
|
|
|
1675
1683
|
}
|
|
1676
1684
|
|
|
1677
1685
|
export { captureRequestError as onRequestError } from "@sentry/nextjs";
|
|
1678
|
-
`;
|
|
1686
|
+
`;J(U.join(e,"sentry.client.config.ts"),t),J(U.join(e,"sentry.server.config.ts"),o),J(U.join(e,"sentry.edge.config.ts"),n),J(U.join(e,"instrumentation.ts"),r)}function qi(){return`# Sentry \u2014 error tracking + performance monitoring
|
|
1679
1687
|
# Get your DSN from https://sentry.io/settings/projects/<project>/keys/
|
|
1680
1688
|
SENTRY_DSN=
|
|
1681
1689
|
NEXT_PUBLIC_SENTRY_DSN=
|
|
1682
1690
|
SENTRY_ORG=
|
|
1683
1691
|
SENTRY_PROJECT=
|
|
1684
1692
|
SENTRY_AUTH_TOKEN=
|
|
1685
|
-
`}function
|
|
1693
|
+
`}function Gi(e){let t=`/**
|
|
1686
1694
|
* Next.js instrumentation hook \u2014 wires Datadog APM (dd-trace) into Node runtime.
|
|
1687
1695
|
* Edge runtime is not supported by dd-trace; only nodejs is initialized.
|
|
1688
1696
|
*/
|
|
@@ -1730,7 +1738,7 @@ export function initDatadogRum(): void {
|
|
|
1730
1738
|
});
|
|
1731
1739
|
initialized = true;
|
|
1732
1740
|
}
|
|
1733
|
-
`;
|
|
1741
|
+
`;J(U.join(e,"instrumentation.ts"),t),J(U.join(e,"src","lib","monitoring","datadog-rum.ts"),o)}function Wi(){return`# Datadog \u2014 APM + RUM
|
|
1734
1742
|
# Server APM (dd-trace)
|
|
1735
1743
|
DD_API_KEY=
|
|
1736
1744
|
DD_APP_KEY=
|
|
@@ -1745,7 +1753,7 @@ NEXT_PUBLIC_DD_SITE=datadoghq.com
|
|
|
1745
1753
|
NEXT_PUBLIC_DD_SERVICE=sailor-web
|
|
1746
1754
|
NEXT_PUBLIC_DD_ENV=production
|
|
1747
1755
|
NEXT_PUBLIC_DD_VERSION=
|
|
1748
|
-
`}function
|
|
1756
|
+
`}function Hi(e){let t=`/**
|
|
1749
1757
|
* Bugsnag server-side initialization.
|
|
1750
1758
|
* Import this once at the top of your server entrypoint or a shared init module.
|
|
1751
1759
|
*/
|
|
@@ -1794,11 +1802,11 @@ export function getBugsnagErrorBoundary() {
|
|
|
1794
1802
|
errorBoundary = plugin.createErrorBoundary(React) as typeof errorBoundary;
|
|
1795
1803
|
return errorBoundary;
|
|
1796
1804
|
}
|
|
1797
|
-
`;
|
|
1805
|
+
`;J(U.join(e,"src","lib","monitoring","bugsnag.server.ts"),t),J(U.join(e,"src","lib","monitoring","bugsnag.client.tsx"),o)}function Xi(){return`# Bugsnag \u2014 error tracking
|
|
1798
1806
|
BUGSNAG_API_KEY=
|
|
1799
1807
|
NEXT_PUBLIC_BUGSNAG_API_KEY=
|
|
1800
1808
|
APP_VERSION=
|
|
1801
|
-
`}function
|
|
1809
|
+
`}function zi(e){J(U.join(e,"src","components","monitoring","ArmsScript.tsx"),`"use client";
|
|
1802
1810
|
/**
|
|
1803
1811
|
* \u963F\u91CC\u4E91 ARMS (Application Real-time Monitoring Service) browser agent.
|
|
1804
1812
|
* Injects the official JS agent via next/script.
|
|
@@ -1819,11 +1827,11 @@ export function ArmsScript() {
|
|
|
1819
1827
|
/>
|
|
1820
1828
|
);
|
|
1821
1829
|
}
|
|
1822
|
-
`)}function
|
|
1830
|
+
`)}function Ji(){return`# \u963F\u91CC\u4E91 ARMS \u2014 APM (\u4E2D\u56FD\u533A)
|
|
1823
1831
|
ALIYUN_ARMS_LICENSE_KEY=
|
|
1824
1832
|
ALIYUN_ARMS_APP_NAME=
|
|
1825
1833
|
NEXT_PUBLIC_ARMS_PID=
|
|
1826
|
-
`}function
|
|
1834
|
+
`}function Qi(e){J(U.join(e,"src","components","monitoring","TingYunScript.tsx"),`"use client";
|
|
1827
1835
|
/**
|
|
1828
1836
|
* \u542C\u4E91 Browser Agent (\u524D\u7AEF\u6027\u80FD\u76D1\u63A7).
|
|
1829
1837
|
* Docs: https://www.tingyun.com/docs
|
|
@@ -1841,12 +1849,12 @@ export function TingYunScript() {
|
|
|
1841
1849
|
/>
|
|
1842
1850
|
);
|
|
1843
1851
|
}
|
|
1844
|
-
`)}function
|
|
1852
|
+
`)}function Zi(){return`# \u542C\u4E91 TingYun \u2014 APM (\u4E2D\u56FD\u533A)
|
|
1845
1853
|
TINGYUN_LICENSE_KEY=
|
|
1846
1854
|
NEXT_PUBLIC_TINGYUN_APP_KEY=
|
|
1847
|
-
`}async function
|
|
1848
|
-
`)}}import
|
|
1849
|
-
`+t):
|
|
1855
|
+
`}async function mo(e,t,o="global"){let n=uo(t);if(!n||n.id==="none")return;let r=U.join(e,"apps","web");if(!ne.existsSync(r))return;let i=n.id;try{switch(i){case"sentry":Yi(r),Qt(r,Jt.sentry),ut(e,qi());return;case"datadog":Gi(r),Qt(r,Jt.datadog),ut(e,Wi());return;case"bugsnag":Hi(r),Qt(r,Jt.bugsnag),ut(e,Xi());return;case"aliyun-arms":zi(r),ut(e,Ji());return;case"tingyun":Qi(r),ut(e,Zi());return;default:return}}catch(s){throw console.error(`Failed to apply monitoring selection "${t}":`,s),new Error(`Monitoring scaffold for "${t}" failed \u2014 see stderr for details.`)}}var Nt=[{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 go(e){return Nt.find(t=>t.id===e)}var rp=Nt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function fo(e,t,o="global"){let n=go(t);if(!n)throw new Error(`Unknown notifications provider "${t}". Valid ids: ${Nt.map(r=>r.id).join(", ")}`);if(n.id==="none"){Y(e,"notifications");return}n.envVars.length>0&&H(e,{category:"Notifications",name:n.name,envVars:n.envVars,docs:n.docs}),X(e,"NOTIFICATIONS_PROVIDER",n.id)}import Zt from"fs";import es from"path";async function yo(e,t){let o=es.join(e,"package.json");if(Zt.existsSync(o)){let n=Zt.readFileSync(o,"utf8"),r=JSON.parse(n);r.name=t,r.version="0.1.0",Zt.writeFileSync(o,JSON.stringify(r,null,2)+`
|
|
1856
|
+
`)}}import oe from"fs";import mt from"path";var ts=["stripe","lemonsqueezy","chinapay","polar"];function So(e){oe.existsSync(e)&&oe.rmSync(e,{recursive:!0,force:!0})}function ns(e){switch(e){case"stripe":return["stripe"];case"lemon":return["lemonsqueezy"];case"wechat":case"alipay":return["chinapay"]}}function os(e,t){let o=mt.join(e,".env.example");oe.existsSync(o)?oe.appendFileSync(o,`
|
|
1857
|
+
`+t):oe.writeFileSync(o,t)}function rs(e){switch(e){case"stripe":return`# Stripe
|
|
1850
1858
|
STRIPE_SECRET_KEY=
|
|
1851
1859
|
STRIPE_WEBHOOK_SECRET=
|
|
1852
1860
|
`;case"lemon":return`# Lemon Squeezy
|
|
@@ -1861,13 +1869,13 @@ CHINAPAY_WECHAT_ENABLED=true
|
|
|
1861
1869
|
CHINAPAY_APP_ID=
|
|
1862
1870
|
CHINAPAY_APP_SECRET=
|
|
1863
1871
|
CHINAPAY_ALIPAY_ENABLED=true
|
|
1864
|
-
`}}async function
|
|
1865
|
-
`),n=[],r=e.includes("#"),
|
|
1866
|
-
`),t)}function
|
|
1872
|
+
`}}async function ho(e,t){let o=mt.join(e,"packages","billing");if(!oe.existsSync(o))return;if(t==="none"){So(o);return}let n=mt.join(o,"src"),r=new Set(ns(t));for(let s of ts)r.has(s)||So(mt.join(n,s));let i=mt.join(n,"checkout","types.ts");if(oe.existsSync(i)){let s=t==="stripe"?"stripe":t==="lemon"?"lemonsqueezy":"chinapay",c=oe.readFileSync(i,"utf8"),a=c.replace(/export type CheckoutProviderType\s*=\s*[^;]+;/,`export type CheckoutProviderType = "${s}";`);a!==c&&oe.writeFileSync(i,a)}os(e,rs(t))}import de from"fs";import et from"path";var _o=new Set(["siliconflow","volcengine-ark","bailian","moonshot"]),en=/@sailor:([^\s*]+)/;function is(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(i=>_o.has(i));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 Eo(e){return e.replace(/\s*\/\/\s*@sailor:[^\n]*$/,"")}function vo(e){return e.replace(/\s*#\s*@sailor:[^\n]*$/,"")}function bo(e,t){let o=e.split(`
|
|
1873
|
+
`),n=[],r=e.includes("#"),i=0;for(;i<o.length;){let s=o[i],c=s.match(en);if(!c){n.push(s),i++;continue}let{marker:a,keep:l}=is(c[1],t);if(a==="registry-entries"){n.push(r?vo(s):Eo(s)),i++;continue}if(l){let m=r?vo(s):Eo(s);m.trim().length>0&&n.push(m);let y=i+1;for(;y<o.length&&o[y].trim()!==""&&!en.test(o[y]);)n.push(o[y]),y++;y<o.length&&o[y].trim()===""&&(n.push(o[y]),y++),i=y}else{let m=i+1;for(;m<o.length&&o[m].trim()!==""&&!en.test(o[m]);)m++;m<o.length&&o[m].trim()===""&&m++,i=m}}return ss(n.join(`
|
|
1874
|
+
`),t)}function ss(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 i=/createProviderRegistry\(\{([\s\S]*?)\}\)/,s=e.match(i);if(!s)return e;let a=s[1].split(`
|
|
1867
1875
|
`).filter(l=>{let m=l.trim();if(m===""||m.startsWith("//"))return!0;let y=m.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*,?$/);return y?r.has(y[1]):!0}).join(`
|
|
1868
|
-
`);return e.replace(
|
|
1869
|
-
`+r);let
|
|
1870
|
-
`)}}import f from"fs";import d from"path";function
|
|
1876
|
+
`);return e.replace(i,`createProviderRegistry({${a}})`)}function as(e,t){let o=et.join(t,"registry.ts.template"),n=de.readFileSync(o,"utf-8");return bo(n,e)}function cs(e,t){let o=et.join(t,".env.example.template"),n=de.readFileSync(o,"utf-8");return bo(n,e)}async function Po(e,t,o){let n=as(t,o),r=cs(t,o),i=et.join(e,"packages/ai/agents/src");await de.promises.mkdir(i,{recursive:!0}),await de.promises.writeFile(et.join(i,"registry.ts"),n),await de.promises.appendFile(et.join(e,".env.example"),`
|
|
1877
|
+
`+r);let s={ai:"latest"},{providerIds:c,customEndpoint:a}=t;(c.includes("openai")||a||c.some(m=>_o.has(m)))&&(s["@ai-sdk/openai"]="latest",s["@ai-sdk/openai-compatible"]="latest"),c.includes("anthropic")&&(s["@ai-sdk/anthropic"]="latest"),c.includes("google")&&(s["@ai-sdk/google"]="latest"),c.includes("mistral")&&(s["@ai-sdk/mistral"]="latest"),c.includes("xai")&&(s["@ai-sdk/openai"]="latest");let l=et.join(e,"package.json");if(de.existsSync(l)){let m=JSON.parse(await de.promises.readFile(l,"utf-8"));m.dependencies||(m.dependencies={});for(let[y,x]of Object.entries(s))m.dependencies[y]=x;await de.promises.writeFile(l,JSON.stringify(m,null,2)+`
|
|
1878
|
+
`)}}import f from"fs";import d from"path";function Co(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()?Co(r,t):t(r)}}async function Io(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 i=d.join(r,"prisma");f.existsSync(i)&&f.rmSync(i,{recursive:!0,force:!0});let s=d.join(r,"package.json");if(f.existsSync(s)){let a=JSON.parse(f.readFileSync(s,"utf8"));a.dependencies&&delete a.dependencies["@prisma/client"],a.devDependencies&&delete a.devDependencies.prisma,f.writeFileSync(s,JSON.stringify(a,null,2)+`
|
|
1871
1879
|
`)}let c=d.join(r,"src","index.ts");f.existsSync(c)&&f.writeFileSync(c,`
|
|
1872
1880
|
export const prisma = {} as any;
|
|
1873
1881
|
export const db = {} as any;
|
|
@@ -1914,7 +1922,7 @@ export type UsageAggregate = any;
|
|
|
1914
1922
|
export type UsageLedgerEntry = any;
|
|
1915
1923
|
export type UsageType = any;
|
|
1916
1924
|
export type UsageLedgerSource = any;
|
|
1917
|
-
`)}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
|
|
1925
|
+
`)}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 i=f.readdirSync(r);for(let s of i){let c=d.join(r,s);if(!f.statSync(c).isDirectory())continue;let a=d.join(c,"src","app");if(!f.existsSync(a))continue;["[lang]","[locale]"].forEach(b=>{let u=d.join(a,b);if(f.existsSync(u)){let T=f.readdirSync(u);for(let ht of T)f.renameSync(d.join(u,ht),d.join(a,ht));f.rmdirSync(u)}}),["src/middleware.ts","middleware.ts"].forEach(b=>{let u=d.join(c,b);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,`
|
|
1918
1926
|
import React from 'react';
|
|
1919
1927
|
export function useTranslations(namespace?: string): any { return (key: string) => key; }
|
|
1920
1928
|
export function useLocale() { return 'en'; }
|
|
@@ -1932,7 +1940,7 @@ export const useRouter = () => ({ push: (...args: any[]) => {}, replace: (...arg
|
|
|
1932
1940
|
export default function createMiddleware(...args: any[]) { return (req: any) => req; }
|
|
1933
1941
|
export function createNavigation(...args: any[]) { return { Link, redirect, usePathname, useRouter, getPathname: () => '/' }; }
|
|
1934
1942
|
export function defineRouting<T>(config: T): T { return config; }
|
|
1935
|
-
`);let
|
|
1943
|
+
`);let x=d.join(l,"mock-i18n.d.ts");f.writeFileSync(x,`declare module '@/lib/mock-i18n' {
|
|
1936
1944
|
export function useTranslations(namespace?: string): any;
|
|
1937
1945
|
export function useLocale(): string;
|
|
1938
1946
|
export const NextIntlClientProvider: React.FC<any>;
|
|
@@ -1953,16 +1961,16 @@ export function defineRouting<T>(config: T): T { return config; }
|
|
|
1953
1961
|
`),f.writeFileSync(d.join(l,"mock-i18n-index.ts"),`
|
|
1954
1962
|
export * from './mock-i18n';
|
|
1955
1963
|
export { default } from './mock-i18n';
|
|
1956
|
-
`);let
|
|
1964
|
+
`);let N=d.join(c,"tsconfig.json");if(f.existsSync(N))try{let b=f.readFileSync(N,"utf8");if(b.includes('"paths": {')){let u=b.replace(/"paths":\s*\{/,`"paths": {
|
|
1957
1965
|
"next-intl": ["./src/lib/mock-i18n"],
|
|
1958
|
-
"next-intl/*": ["./src/lib/mock-i18n"],`);f.writeFileSync(
|
|
1966
|
+
"next-intl/*": ["./src/lib/mock-i18n"],`);f.writeFileSync(N,u)}}catch{}let L=d.join(c,"src");f.existsSync(L)&&Co(L,b=>{if(!b.endsWith(".ts")&&!b.endsWith(".tsx"))return;let u=f.readFileSync(b,"utf8"),T=!1;b.endsWith("src/app/layout.tsx")&&u.includes("../providers")&&(u=u.replace(/from\s+['"]\.\.\/providers['"]/g,"from './providers'"),T=!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"),T=!0),u.match(/lang\s*:\s*any/)&&(u=u.replace(/lang\s*:\s*any/g,"lang: string"),T=!0),u.match(/requestLocale\s*:\s*any/)&&(u=u.replace(/requestLocale\s*:\s*any/g,"requestLocale: string"),T=!0),u.match(/\(locale\s*=\s*(.*)\)/)&&(u=u.replace(/\(locale\s*=\s*(.*)\)/g,"() "),T=!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 })"),T=!0),(b.endsWith("src/app/layout.tsx")||b.endsWith("src/app/sitemap.ts")||b.endsWith("src/app/robots.ts")||b.endsWith("src/components/ui/locale-switcher.tsx")||b.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) =>"),T=!0),b.endsWith("src/i18n/request.ts")&&(u=u.replace(/async\s*\(\{\s*requestLocale\s*\}\)/g,"async ({ requestLocale }: any)"),T=!0),(u.match(/locale\s*:/)||u.match(/lang\s*:/))&&(T=!0),T&&f.writeFileSync(b,u)});let re=d.join(c,"package.json");if(f.existsSync(re)){let b=JSON.parse(f.readFileSync(re,"utf8"));b.dependencies&&delete b.dependencies["next-intl"],f.writeFileSync(re,JSON.stringify(b,null,2)+`
|
|
1959
1967
|
`)}}}}f.writeFileSync(o,JSON.stringify(n,null,2)+`
|
|
1960
|
-
`)}function E(e){try{f.rmSync(e,{recursive:!0,force:!0})}catch{}}function
|
|
1961
|
-
`)}function
|
|
1962
|
-
`);
|
|
1963
|
-
`)}async function
|
|
1964
|
-
`);
|
|
1965
|
-
`)}async function
|
|
1968
|
+
`)}function E(e){try{f.rmSync(e,{recursive:!0,force:!0})}catch{}}function ps(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,...i}=o.dependencies;o={...o,dependencies:i},n=!0}if(o.devDependencies&&t in o.devDependencies){let{[t]:r,...i}=o.devDependencies;o={...o,devDependencies:i},n=!0}n&&f.writeFileSync(e,JSON.stringify(o,null,2)+`
|
|
1969
|
+
`)}function Ao(e,t){t.cronJobs||(E(d.join(e,"apps/web/src/app/api/cron")),E(d.join(e,"packages/integrations/queue/src/scheduled"))),t.auditLog||(E(d.join(e,"apps/web/src/app/[locale]/(app)/settings/audit-log")),E(d.join(e,"apps/web/src/app/api/audit-logs")),E(d.join(e,"apps/web/src/components/audit"))),t.apiKeys||(E(d.join(e,"apps/web/src/app/[locale]/(app)/settings/api-keys")),E(d.join(e,"apps/web/src/app/api/api-keys")),E(d.join(e,"apps/web/src/components/api-keys"))),t.webhooks||(E(d.join(e,"apps/web/src/app/[locale]/(app)/settings/webhooks")),E(d.join(e,"apps/web/src/app/api/webhooks")),E(d.join(e,"apps/web/src/components/webhooks")),E(d.join(e,"packages/webhooks"))),t.commandPalette||(E(d.join(e,"apps/web/src/components/command-palette")),E(d.join(e,"apps/web/src/app/[locale]/providers/command-palette-mount.tsx"))),t.cookieConsent||(E(d.join(e,"apps/landing-page/src/components/cookie-consent-banner.tsx")),E(d.join(e,"apps/landing-page/src/components/__tests__/cookie-consent-banner.test.tsx")),E(d.join(e,"apps/landing-page/src/lib/cookie-consent.ts")),E(d.join(e,"apps/landing-page/src/lib/__tests__/cookie-consent.test.ts")),E(d.join(e,"apps/web/src/app/api/cookie-consent"))),t.legalPages||(E(d.join(e,"apps/landing-page/src/app/[lang]/(legal)/legal")),E(d.join(e,"apps/landing-page/src/lib/legal-documents.ts")),E(d.join(e,"apps/landing-page/src/lib/__tests__/legal-documents.test.ts"))),t.chinaCompliance||(E(d.join(e,"packages/china-compliance")),E(d.join(e,"apps/landing-page/src/components/icp-footer.tsx")),E(d.join(e,"apps/landing-page/src/components/__tests__/icp-footer.test.tsx")),E(d.join(e,"apps/web/src/components/auth/wechat-login-button.tsx")),E(d.join(e,"apps/web/src/components/auth/__tests__/wechat-login-button.test.tsx")),ps(d.join(e,"apps/web/package.json"),"@nebutra/china-compliance"))}import{getSchema as ls,printSchema as ds}from"@mrleebo/prisma-ast";var us=/^\/\/\/\s*@conditional\(([A-Za-z][A-Za-z0-9_-]*)=([^)]+)\)\s*$/;function ms(e){return e.type==="comment"&&e.text.trim().startsWith("///")}function gs(e){let t=us.exec(e.trim());if(!t)return null;let[,o,n]=t,r=n.split("|").map(i=>i.trim()).filter(i=>i.length>0);return{flag:o,values:r}}function fs(e,t){for(let o=t-1;o>=0;o--){let n=e[o];if(n.type==="comment"){let r=gs(n.text);if(r)return{annIndex:o,parsed:r};continue}break}return null}function tn(e,t){let o=ls(e),n=new Set;for(let c=0;c<o.list.length;c++){if(o.list[c].type!=="model")continue;let l=fs(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 L=o.list[N];if(L.type==="comment"&&ms(L)){n.add(N);continue}break}let x=o.list[c+1];x&&x.type==="break"&&n.add(c+1)}}let r=o.list.filter((c,a)=>!n.has(a)),i=[];for(let c of r){let a=i[i.length-1];c.type==="break"&&a&&a.type==="break"||i.push(c)}return ds({type:"schema",list:i})}import ue from"fs";import nn from"path";var Tt=[{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:"upstash",name:"Upstash Kafka",region:"both",envVars:["UPSTASH_KAFKA_REST_URL","UPSTASH_KAFKA_REST_USERNAME","UPSTASH_KAFKA_REST_PASSWORD"],docs:"https://upstash.com/docs/kafka",description:"Serverless Kafka"},{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 wo(e){return Tt.find(t=>t.id===e)}var Pp=Tt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Ro(e){return ue.existsSync(e)?ue.readFileSync(e,"utf-8"):null}function ys(e,t){let o=nn.join(e,".env.example"),n=Ro(o);if(n===null)return;let r=`# Queue: ${t.name}`;if(n.includes(r))return;let i=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(s=>`${s}=""`),""].join(`
|
|
1970
|
+
`);ue.appendFileSync(o,i)}function Ss(e,t,o){let n=nn.join(e,".env.example"),r=Ro(n);if(r===null)return;let i=new RegExp(`^${t}=.*$`,"m");i.test(r)?ue.writeFileSync(n,r.replace(i,`${t}="${o}"`)):ue.appendFileSync(n,`${t}="${o}"
|
|
1971
|
+
`)}async function ko(e,t,o){let n=nn.join(e,"packages","queue");if(t==="none"){ue.existsSync(n)&&ue.rmSync(n,{recursive:!0,force:!0});return}let r=wo(t);if(!r)throw new Error(`Unknown queue provider "${t}". Valid ids: ${Tt.map(i=>i.id).join(", ")}`);ue.existsSync(n)&&(ys(e,r),Ss(e,"QUEUE_PROVIDER",t))}import me from"fs";import on from"path";var Ot=[{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 xo(e){return Ot.find(t=>t.id===e)}var kp=Ot.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function No(e){return me.existsSync(e)?me.readFileSync(e,"utf-8"):null}function hs(e,t){let o=on.join(e,".env.example"),n=No(o);if(n===null)return;let r=`# Search: ${t.name}`;if(n.includes(r))return;let i=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(s=>`${s}=""`),""].join(`
|
|
1972
|
+
`);me.appendFileSync(o,i)}function Es(e,t,o){let n=on.join(e,".env.example"),r=No(n);if(r===null)return;let i=new RegExp(`^${t}=.*$`,"m");i.test(r)?me.writeFileSync(n,r.replace(i,`${t}="${o}"`)):me.appendFileSync(n,`${t}="${o}"
|
|
1973
|
+
`)}async function To(e,t,o){let n=on.join(e,"packages","search");if(t==="none"){me.existsSync(n)&&me.rmSync(n,{recursive:!0,force:!0});return}let r=xo(t);if(!r)throw new Error(`Unknown search provider "${t}". Valid ids: ${Ot.map(i=>i.id).join(", ")}`);me.existsSync(n)&&(hs(e,r),Es(e,"SEARCH_PROVIDER",t))}import ge from"fs";import xe from"path";function vs(e){let t=[xe.join(e,"packages","db"),xe.join(e,"apps","web")];for(let o of t){let n=xe.join(o,"prisma","schema.prisma");if(ge.existsSync(n))return{packageDir:o,seedPath:xe.join(o,"prisma","seed.ts"),packageJsonPath:xe.join(o,"package.json")}}return null}function _s(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 bs(e,t){return!t||e==="none"?` // Skipping admin user \u2014 auth=${JSON.stringify(e)} (or no User model).`:e==="clerk"?` try {
|
|
1966
1974
|
const admin = await prisma.user.create({
|
|
1967
1975
|
data: {
|
|
1968
1976
|
clerkId: "user_mock_admin",
|
|
@@ -1993,7 +2001,7 @@ export { default } from './mock-i18n';
|
|
|
1993
2001
|
console.log("\\u2713 Created admin user:", admin.email);
|
|
1994
2002
|
} catch (err) {
|
|
1995
2003
|
console.warn("\\u26A0 Admin user creation skipped:", (err as Error).message);
|
|
1996
|
-
}`}function
|
|
2004
|
+
}`}function Ps(e,t){let o=t.has("Tenant"),n=t.has("User"),r=t.has("Project"),i=bs(e,n),s=o?` let tenants: Array<{ id: string; name: string }> = [];
|
|
1997
2005
|
try {
|
|
1998
2006
|
tenants = await Promise.all([
|
|
1999
2007
|
prisma.tenant.create({
|
|
@@ -2043,10 +2051,10 @@ async function main(): Promise<void> {
|
|
|
2043
2051
|
console.log("\\uD83C\\uDF31 Seeding database...");
|
|
2044
2052
|
|
|
2045
2053
|
// --- Tenants ---------------------------------------------------------
|
|
2046
|
-
${
|
|
2054
|
+
${s}
|
|
2047
2055
|
|
|
2048
2056
|
// --- Admin user ------------------------------------------------------
|
|
2049
|
-
${
|
|
2057
|
+
${i}
|
|
2050
2058
|
|
|
2051
2059
|
// --- Sample projects per tenant --------------------------------------
|
|
2052
2060
|
${c}
|
|
@@ -2062,12 +2070,12 @@ main()
|
|
|
2062
2070
|
.finally(async () => {
|
|
2063
2071
|
await prisma.$disconnect();
|
|
2064
2072
|
});
|
|
2065
|
-
`}function
|
|
2066
|
-
`)}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
|
|
2067
|
-
`+t):
|
|
2073
|
+
`}function Cs(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 i=o.prisma??{};if(i.seed||(i.seed="tsx prisma/seed.ts",r=!0),!r)return;let s={...o,scripts:n,prisma:i};ge.writeFileSync(e,JSON.stringify(s,null,2)+`
|
|
2074
|
+
`)}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 Oo(e,t){let o=vs(e);if(o)try{let n=xe.join(o.packageDir,"prisma","schema.prisma"),r=_s(n),i=Ps(t,r),s=xe.dirname(o.seedPath);ge.existsSync(s)||ge.mkdirSync(s,{recursive:!0}),ge.writeFileSync(o.seedPath,i),Cs(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 Ne from"path";var Ut=[{id:"twilio",name:"Twilio",region:"global",envVars:["TWILIO_ACCOUNT_SID","TWILIO_AUTH_TOKEN","TWILIO_FROM_NUMBER"],docs:"https://www.twilio.com/docs/sms",pkgs:["twilio"]},{id:"messagebird",name:"MessageBird (Bird)",region:"global",envVars:["MESSAGEBIRD_ACCESS_KEY","MESSAGEBIRD_ORIGINATOR"],docs:"https://developers.messagebird.com/api/sms-messaging/",pkgs:["messagebird"]},{id:"plivo",name:"Plivo",region:"global",envVars:["PLIVO_AUTH_ID","PLIVO_AUTH_TOKEN","PLIVO_FROM_NUMBER"],docs:"https://www.plivo.com/docs/sms/",pkgs:["plivo"]},{id:"aliyun-sms",name:"\u963F\u91CC\u4E91\u77ED\u4FE1 Aliyun SMS",region:"cn",envVars:["ALIYUN_SMS_ACCESS_KEY","ALIYUN_SMS_ACCESS_SECRET","ALIYUN_SMS_SIGN_NAME","ALIYUN_SMS_TEMPLATE_CODE","ALIYUN_SMS_ENDPOINT"],docs:"https://help.aliyun.com/product/44282.html",pkgs:["@alicloud/dysmsapi20170525","@alicloud/openapi-client"]},{id:"tencent-sms",name:"\u817E\u8BAF\u4E91\u77ED\u4FE1 Tencent SMS",region:"cn",envVars:["TENCENT_SMS_SECRET_ID","TENCENT_SMS_SECRET_KEY","TENCENT_SMS_SDK_APP_ID","TENCENT_SMS_SIGN_NAME","TENCENT_SMS_TEMPLATE_ID","TENCENT_SMS_REGION"],docs:"https://cloud.tencent.com/document/product/382",pkgs:["tencentcloud-sdk-nodejs-sms"]},{id:"yunpian",name:"\u4E91\u7247 Yunpian",region:"cn",envVars:["YUNPIAN_API_KEY","YUNPIAN_SIGN"],docs:"https://www.yunpian.com/official/document/sms/zh_CN/introduction_product_intro",pkgs:["yunpian-nodejs-sdk"]},{id:"none",name:"None",region:"both",envVars:[],docs:""}],Dp=Ut.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Uo(e){return Ut.find(t=>t.id===e)}var Is="@nebutra/sms",As={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 rn(e,t){fe.mkdirSync(Ne.dirname(e),{recursive:!0}),fe.writeFileSync(e,t)}function ws(e,t){let o=Ne.join(e,".env.example");fe.existsSync(o)?fe.appendFileSync(o,`
|
|
2075
|
+
`+t):fe.writeFileSync(o,t)}function Rs(e){if(e.envVars.length===0)return"";let t=`# SMS provider: ${e.name}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
2068
2076
|
`);return`${t}
|
|
2069
2077
|
${o}
|
|
2070
|
-
`}var
|
|
2078
|
+
`}var ks=`/**
|
|
2071
2079
|
* Inputs for the \`sendSms\` wrapper. Keep this surface portable across
|
|
2072
2080
|
* providers \u2014 provider-specific options belong in the wrapper itself.
|
|
2073
2081
|
*/
|
|
@@ -2087,8 +2095,8 @@ export interface SendSmsResult {
|
|
|
2087
2095
|
provider: string;
|
|
2088
2096
|
raw?: unknown;
|
|
2089
2097
|
}
|
|
2090
|
-
`;function
|
|
2091
|
-
import type { SendSmsInput, SendSmsResult } from "./types
|
|
2098
|
+
`;function xs(){return`import twilio from "twilio";
|
|
2099
|
+
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2092
2100
|
|
|
2093
2101
|
let client: ReturnType<typeof twilio> | null = null;
|
|
2094
2102
|
|
|
@@ -2121,9 +2129,9 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2121
2129
|
return { messageId: message.sid, provider: "twilio", raw: message };
|
|
2122
2130
|
}
|
|
2123
2131
|
|
|
2124
|
-
export type { SendSmsInput, SendSmsResult } from "./types
|
|
2125
|
-
`}function
|
|
2126
|
-
import type { SendSmsInput, SendSmsResult } from "./types
|
|
2132
|
+
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2133
|
+
`}function Ns(){return`import messagebird from "messagebird";
|
|
2134
|
+
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2127
2135
|
|
|
2128
2136
|
let client: ReturnType<typeof messagebird> | null = null;
|
|
2129
2137
|
|
|
@@ -2159,9 +2167,9 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2159
2167
|
});
|
|
2160
2168
|
}
|
|
2161
2169
|
|
|
2162
|
-
export type { SendSmsInput, SendSmsResult } from "./types
|
|
2163
|
-
`}function
|
|
2164
|
-
import type { SendSmsInput, SendSmsResult } from "./types
|
|
2170
|
+
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2171
|
+
`}function Ts(){return`import plivo from "plivo";
|
|
2172
|
+
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2165
2173
|
|
|
2166
2174
|
let client: InstanceType<typeof plivo.Client> | null = null;
|
|
2167
2175
|
|
|
@@ -2198,10 +2206,10 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2198
2206
|
};
|
|
2199
2207
|
}
|
|
2200
2208
|
|
|
2201
|
-
export type { SendSmsInput, SendSmsResult } from "./types
|
|
2202
|
-
`}function
|
|
2209
|
+
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2210
|
+
`}function Os(){return`import Dysmsapi, { SendSmsRequest } from "@alicloud/dysmsapi20170525";
|
|
2203
2211
|
import * as $OpenApi from "@alicloud/openapi-client";
|
|
2204
|
-
import type { SendSmsInput, SendSmsResult } from "./types
|
|
2212
|
+
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2205
2213
|
|
|
2206
2214
|
let client: Dysmsapi | null = null;
|
|
2207
2215
|
|
|
@@ -2247,9 +2255,9 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2247
2255
|
};
|
|
2248
2256
|
}
|
|
2249
2257
|
|
|
2250
|
-
export type { SendSmsInput, SendSmsResult } from "./types
|
|
2251
|
-
`}function
|
|
2252
|
-
import type { SendSmsInput, SendSmsResult } from "./types
|
|
2258
|
+
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2259
|
+
`}function Us(){return`import * as tencentcloud from "tencentcloud-sdk-nodejs-sms";
|
|
2260
|
+
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2253
2261
|
|
|
2254
2262
|
const SmsClient = tencentcloud.sms.v20210111.Client;
|
|
2255
2263
|
type SmsClientInstance = InstanceType<typeof SmsClient>;
|
|
@@ -2312,10 +2320,10 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2312
2320
|
};
|
|
2313
2321
|
}
|
|
2314
2322
|
|
|
2315
|
-
export type { SendSmsInput, SendSmsResult } from "./types
|
|
2316
|
-
`}function
|
|
2323
|
+
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2324
|
+
`}function Ls(){return`// @ts-expect-error \u2014 yunpian-nodejs-sdk ships no type declarations
|
|
2317
2325
|
import Yunpian from "yunpian-nodejs-sdk";
|
|
2318
|
-
import type { SendSmsInput, SendSmsResult } from "./types
|
|
2326
|
+
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2319
2327
|
|
|
2320
2328
|
let client: { sms: { single_send(opts: unknown): Promise<unknown>; tpl_single_send(opts: unknown): Promise<unknown> } } | null =
|
|
2321
2329
|
null;
|
|
@@ -2364,14 +2372,14 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2364
2372
|
return { provider: "yunpian", raw };
|
|
2365
2373
|
}
|
|
2366
2374
|
|
|
2367
|
-
export type { SendSmsInput, SendSmsResult } from "./types
|
|
2368
|
-
`}function
|
|
2369
|
-
`}}function
|
|
2370
|
-
`}async function
|
|
2371
|
-
`+t):
|
|
2375
|
+
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2376
|
+
`}function Ms(e){switch(e){case"twilio":return xs();case"messagebird":return Ns();case"plivo":return Ts();case"aliyun-sms":return Os();case"tencent-sms":return Us();case"yunpian":return Ls();case"none":return`export {};
|
|
2377
|
+
`}}function Fs(e){let t={};for(let n of e.pkgs??[]){let r=As[n];r&&(t[n]=r)}let o={name:Is,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)+`
|
|
2378
|
+
`}async function Lo(e,t,o="global"){if(t==="none")return;let n=Uo(t);if(!n)throw new Error(`Unknown SMS provider "${t}". Valid ids: ${Ut.map(s=>s.id).join(", ")}`);let r=Ne.join(e,"packages","sms");if(!fe.existsSync(r))return;let i=Ne.join(r,"src");fe.existsSync(i)&&fe.rmSync(i,{recursive:!0,force:!0});try{rn(Ne.join(r,"package.json"),Fs(n)),rn(Ne.join(i,"types.ts"),ks),rn(Ne.join(i,"index.ts"),Ms(n.id)),ws(e,Rs(n))}catch(s){throw console.error(`Failed to apply SMS selection "${t}":`,s),new Error(`SMS scaffold for "${t}" failed \u2014 see stderr for details.`)}}import Oe from"fs";import Te from"path";var gt=[{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 sn(e){let t=e==="supabase"?"supabase-storage":e;return gt.some(o=>o.id===t)?t:void 0}function Mo(e){let t=sn(e);return t?gt.find(o=>o.id===t):void 0}var Yp=gt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});var Ue="@nebutra/storage";function an(e,t){Oe.mkdirSync(Te.dirname(e),{recursive:!0}),Oe.writeFileSync(e,t)}function Ds(e,t){let o=Te.join(e,".env.example");Oe.existsSync(o)?Oe.appendFileSync(o,`
|
|
2379
|
+
`+t):Oe.writeFileSync(o,t)}function js(e){if(e.envVars.length===0)return"";let t=`# Storage provider: ${e.name}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
2372
2380
|
`);return`${t}
|
|
2373
2381
|
${o}
|
|
2374
|
-
`}var
|
|
2382
|
+
`}var Ks=`/**
|
|
2375
2383
|
* Inputs for the \`uploadFile\` wrapper. Keep this surface portable across
|
|
2376
2384
|
* providers \u2014 provider-specific options belong in the wrapper itself.
|
|
2377
2385
|
*/
|
|
@@ -2397,8 +2405,8 @@ export interface SignedUrlOptions {
|
|
|
2397
2405
|
operation?: "get" | "put";
|
|
2398
2406
|
contentType?: string;
|
|
2399
2407
|
}
|
|
2400
|
-
`;function
|
|
2401
|
-
* @${
|
|
2408
|
+
`;function Vs(){return`/**
|
|
2409
|
+
* @${Ue.slice(1)} \u2014 Cloudflare R2 via the S3-compatible API.
|
|
2402
2410
|
*
|
|
2403
2411
|
* Docs: https://developers.cloudflare.com/r2/api/s3/api/
|
|
2404
2412
|
*/
|
|
@@ -2413,7 +2421,7 @@ import type {
|
|
|
2413
2421
|
SignedUrlOptions,
|
|
2414
2422
|
UploadFileInput,
|
|
2415
2423
|
UploadFileResult,
|
|
2416
|
-
} from "./types
|
|
2424
|
+
} from "./types";
|
|
2417
2425
|
|
|
2418
2426
|
const accountId = process.env.R2_ACCOUNT_ID;
|
|
2419
2427
|
const accessKeyId = process.env.R2_ACCESS_KEY_ID;
|
|
@@ -2460,9 +2468,9 @@ export async function getSignedUrl(
|
|
|
2460
2468
|
return awsGetSignedUrl(client, command, { expiresIn });
|
|
2461
2469
|
}
|
|
2462
2470
|
|
|
2463
|
-
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types
|
|
2464
|
-
`}function
|
|
2465
|
-
* @${
|
|
2471
|
+
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2472
|
+
`}function Bs(){return`/**
|
|
2473
|
+
* @${Ue.slice(1)} \u2014 AWS S3 wrapper.
|
|
2466
2474
|
*
|
|
2467
2475
|
* Docs: https://docs.aws.amazon.com/s3/
|
|
2468
2476
|
*/
|
|
@@ -2477,7 +2485,7 @@ import type {
|
|
|
2477
2485
|
SignedUrlOptions,
|
|
2478
2486
|
UploadFileInput,
|
|
2479
2487
|
UploadFileResult,
|
|
2480
|
-
} from "./types
|
|
2488
|
+
} from "./types";
|
|
2481
2489
|
|
|
2482
2490
|
const region = process.env.AWS_S3_REGION;
|
|
2483
2491
|
const bucket = process.env.S3_BUCKET;
|
|
@@ -2525,9 +2533,9 @@ export async function getSignedUrl(
|
|
|
2525
2533
|
return awsGetSignedUrl(client, command, { expiresIn });
|
|
2526
2534
|
}
|
|
2527
2535
|
|
|
2528
|
-
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types
|
|
2529
|
-
`}function
|
|
2530
|
-
* @${
|
|
2536
|
+
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2537
|
+
`}function $s(){return`/**
|
|
2538
|
+
* @${Ue.slice(1)} \u2014 Supabase Storage wrapper.
|
|
2531
2539
|
*
|
|
2532
2540
|
* Docs: https://supabase.com/docs/guides/storage
|
|
2533
2541
|
*/
|
|
@@ -2537,7 +2545,7 @@ import type {
|
|
|
2537
2545
|
SignedUrlOptions,
|
|
2538
2546
|
UploadFileInput,
|
|
2539
2547
|
UploadFileResult,
|
|
2540
|
-
} from "./types
|
|
2548
|
+
} from "./types";
|
|
2541
2549
|
|
|
2542
2550
|
const url = process.env.SUPABASE_URL;
|
|
2543
2551
|
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
@@ -2588,9 +2596,9 @@ export async function getSignedUrl(
|
|
|
2588
2596
|
return data.signedUrl;
|
|
2589
2597
|
}
|
|
2590
2598
|
|
|
2591
|
-
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types
|
|
2592
|
-
`}function
|
|
2593
|
-
* @${
|
|
2599
|
+
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2600
|
+
`}function Ys(){return`/**
|
|
2601
|
+
* @${Ue.slice(1)} \u2014 \u963F\u91CC\u4E91 OSS wrapper via ali-oss.
|
|
2594
2602
|
*
|
|
2595
2603
|
* Docs: https://help.aliyun.com/product/31815.html
|
|
2596
2604
|
*/
|
|
@@ -2600,7 +2608,7 @@ import type {
|
|
|
2600
2608
|
SignedUrlOptions,
|
|
2601
2609
|
UploadFileInput,
|
|
2602
2610
|
UploadFileResult,
|
|
2603
|
-
} from "./types
|
|
2611
|
+
} from "./types";
|
|
2604
2612
|
|
|
2605
2613
|
const accessKeyId = process.env.ALIYUN_OSS_ACCESS_KEY;
|
|
2606
2614
|
const accessKeySecret = process.env.ALIYUN_OSS_ACCESS_SECRET;
|
|
@@ -2635,9 +2643,9 @@ export async function getSignedUrl(
|
|
|
2635
2643
|
});
|
|
2636
2644
|
}
|
|
2637
2645
|
|
|
2638
|
-
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types
|
|
2639
|
-
`}function
|
|
2640
|
-
* @${
|
|
2646
|
+
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2647
|
+
`}function qs(){return`/**
|
|
2648
|
+
* @${Ue.slice(1)} \u2014 \u817E\u8BAF\u4E91 COS wrapper via cos-nodejs-sdk-v5.
|
|
2641
2649
|
*
|
|
2642
2650
|
* Docs: https://cloud.tencent.com/document/product/436
|
|
2643
2651
|
*/
|
|
@@ -2647,7 +2655,7 @@ import type {
|
|
|
2647
2655
|
SignedUrlOptions,
|
|
2648
2656
|
UploadFileInput,
|
|
2649
2657
|
UploadFileResult,
|
|
2650
|
-
} from "./types
|
|
2658
|
+
} from "./types";
|
|
2651
2659
|
|
|
2652
2660
|
const secretId = process.env.TENCENT_COS_SECRET_ID;
|
|
2653
2661
|
const secretKey = process.env.TENCENT_COS_SECRET_KEY;
|
|
@@ -2690,9 +2698,9 @@ export async function getSignedUrl(
|
|
|
2690
2698
|
return url as unknown as string;
|
|
2691
2699
|
}
|
|
2692
2700
|
|
|
2693
|
-
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types
|
|
2694
|
-
`}function
|
|
2695
|
-
* @${
|
|
2701
|
+
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2702
|
+
`}function Gs(){return`/**
|
|
2703
|
+
* @${Ue.slice(1)} \u2014 \u4E03\u725B\u4E91 Kodo wrapper.
|
|
2696
2704
|
*
|
|
2697
2705
|
* Docs: https://developer.qiniu.com/kodo
|
|
2698
2706
|
*/
|
|
@@ -2702,7 +2710,7 @@ import type {
|
|
|
2702
2710
|
SignedUrlOptions,
|
|
2703
2711
|
UploadFileInput,
|
|
2704
2712
|
UploadFileResult,
|
|
2705
|
-
} from "./types
|
|
2713
|
+
} from "./types";
|
|
2706
2714
|
|
|
2707
2715
|
const accessKey = process.env.QINIU_ACCESS_KEY;
|
|
2708
2716
|
const secretKey = process.env.QINIU_SECRET_KEY;
|
|
@@ -2758,10 +2766,10 @@ export async function getSignedUrl(
|
|
|
2758
2766
|
return bucketManager.privateDownloadUrl(domain, key, deadline);
|
|
2759
2767
|
}
|
|
2760
2768
|
|
|
2761
|
-
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types
|
|
2762
|
-
`}function
|
|
2763
|
-
`}}function
|
|
2764
|
-
`}async function
|
|
2769
|
+
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2770
|
+
`}function Ws(e){switch(e){case"r2":return Vs();case"s3":return Bs();case"supabase-storage":return $s();case"aliyun-oss":return Ys();case"tencent-cos":return qs();case"qiniu":return Gs();case"none":return`export {};
|
|
2771
|
+
`}}function Hs(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:Ue,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)+`
|
|
2772
|
+
`}async function Fo(e,t,o){if(t==="none")return;let n=Mo(t);if(!n)throw new Error(`Unknown storage provider "${t}". Valid ids: ${gt.map(s=>s.id).join(", ")}`);let r=Te.join(e,"packages","storage"),i=Te.join(r,"src");Oe.existsSync(i)&&Oe.rmSync(i,{recursive:!0,force:!0}),an(Te.join(r,"package.json"),Hs(n)),an(Te.join(i,"types.ts"),Ks),an(Te.join(i,"index.ts"),Ws(n.id)),Ds(e,js(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 Do(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 Lt=[{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 jo(e){return Lt.find(t=>t.id===e)}var Jp=Lt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Ko(e,t,o="global"){let n=jo(t);if(!n)throw new Error(`Unknown webhooks provider "${t}". Valid ids: ${Lt.map(r=>r.id).join(", ")}`);if(n.id==="none"){Y(e,"webhooks");return}n.envVars.length>0&&H(e,{category:"Webhooks",name:n.name,envVars:n.envVars,docs:n.docs}),X(e,"WEBHOOKS_PROVIDER",n.id)}import Le from"fs";import ft from"path";function Xs(e){return`import Link from "next/link";
|
|
2765
2773
|
|
|
2766
2774
|
export default function WelcomePage() {
|
|
2767
2775
|
const projectName = ${JSON.stringify(e)};
|
|
@@ -2870,21 +2878,21 @@ function Step({
|
|
|
2870
2878
|
</li>
|
|
2871
2879
|
);
|
|
2872
2880
|
}
|
|
2873
|
-
`}function
|
|
2881
|
+
`}function zs(e={}){return`
|
|
2874
2882
|
## What you can do next
|
|
2875
2883
|
|
|
2876
|
-
${[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/china-compliance/README.md`"].join(`
|
|
2884
|
+
${[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(`
|
|
2877
2885
|
`)}
|
|
2878
|
-
`}function
|
|
2886
|
+
`}function Js(e=[]){return e.length===0?"":`
|
|
2879
2887
|
## Production readiness holds
|
|
2880
2888
|
|
|
2881
2889
|
Do not enable these in production until you replace stubs, add provider credentials, and verify the integration contract:
|
|
2882
2890
|
|
|
2883
|
-
${e.map(o=>{let n=
|
|
2891
|
+
${e.map(o=>{let n=Je(o.status);return`- ${o.flag}=${o.provider} ${n} \u2014 ${se(o.status)}`}).join(`
|
|
2884
2892
|
`)}
|
|
2885
2893
|
|
|
2886
2894
|
Review the package readiness matrix before handoff: [docs/package-status.md](docs/package-status.md)
|
|
2887
|
-
`}function
|
|
2895
|
+
`}function Qs(e,t,o=[],n={}){return`# Next steps \u2014 ${e}
|
|
2888
2896
|
|
|
2889
2897
|
Region: ${t}
|
|
2890
2898
|
|
|
@@ -2910,33 +2918,42 @@ Your AI-native SaaS scaffold is ready. Complete these four steps to go from temp
|
|
|
2910
2918
|
\`\`\`
|
|
2911
2919
|
|
|
2912
2920
|
After setup, delete the welcome route at \`apps/web/src/app/[locale]/welcome/\`.
|
|
2913
|
-
${
|
|
2921
|
+
${zs(n)}${Js(o)}
|
|
2914
2922
|
|
|
2915
2923
|
## Resources
|
|
2916
2924
|
|
|
2917
2925
|
- [Documentation](/docs)
|
|
2918
2926
|
- [GitHub](https://github.com/nebutra/nebutra-sailor)
|
|
2919
2927
|
- [Get free license (Individual/OPC)](https://nebutra.com/get-license)
|
|
2920
|
-
`}async function
|
|
2921
|
-
`)}function
|
|
2922
|
-
`)+
|
|
2923
|
-
`)),"fumadocs"}return"fumadocs"}function
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
+
`}async function Vo(e,t){let{projectName:o,region:n,previewSelections:r=[],waveFeatures:i={}}=t;try{let s=ft.join(e,".sailor");Le.existsSync(s)||Le.mkdirSync(s,{recursive:!0}),Le.writeFileSync(ft.join(s,"next-steps.md"),Qs(o,n,r,i))}catch(s){let c=s instanceof Error?s.message:String(s);console.warn(`[create-sailor] Failed to write .sailor/next-steps.md: ${c}`)}try{let s=ft.join(e,"apps","web");if(!Le.existsSync(s))return;let c=ft.join(s,"src","app","[locale]","welcome");Le.existsSync(c)||Le.mkdirSync(c,{recursive:!0}),Le.writeFileSync(ft.join(c,"page.tsx"),Xs(o))}catch(s){let c=s instanceof Error?s.message:String(s);console.warn(`[create-sailor] Failed to write welcome page: ${c}`)}}var Bo="create-sailor";function p(e,t){e&&process.stdout.write(JSON.stringify(t)+`
|
|
2929
|
+
`)}function ta(){let e=process.env.npm_config_user_agent??"";return e.startsWith("pnpm")?"pnpm":e.startsWith("yarn")?"yarn":e.startsWith("bun")?"bun":"npm"}function $o(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 Yo(e){return e==="drizzle"?"drizzle":e==="none"?"none":"prisma"}function qo(e){return e==="lemon"||e==="lemonsqueezy"?"lemon":e==="wechat"?"wechat":e==="alipay"?"alipay":e==="none"?"none":"stripe"}function Go(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 Wo(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"}function Ho(e){if(!e)return["openai"];let t=e.split(",").map(o=>o.trim().toLowerCase());return t.includes("none")?[]:t}function na(e,t){let o=e??t;return sn(o)??o}var Xo={mintlify:"Mintlify",docusaurus:"Docusaurus",nextra:"Nextra",vitepress:"VitePress"};function zo(e,t){if(!e)return"fumadocs";let o=e.toLowerCase();if(o==="fumadocs"||o==="none")return o;if(o in Xo){let n=Xo[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.
|
|
2930
|
+
`)+C.dim(` Track progress: https://github.com/Nebutra/Nebutra-Sailor/issues
|
|
2931
|
+
`)),"fumadocs"}return"fumadocs"}function Jo(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 Qo(e){if(!e)return"global";let t=e.toLowerCase();return t==="cn"?"cn":t==="hybrid"?"hybrid":"global"}function oa(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 Zo(e){return e==="cn"?"wechat":"stripe"}async function ra(){let e=new Zs;e.name(Bo).description("Nebutra-Sailor \u2014 AI-Native SaaS template").version(ie,"-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 | drizzle | none").option("--db <id>","postgres | mysql | sqlite | none").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 <ids>","expert: comma-separated provider ids used as an AI provider seed").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 | upstash | 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("--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&&(gn(),process.exit(0));let n=!!t.json,r=!!t.dryRun,i=!!t.yes,s=i||!process.stdin.isTTY;if(n||dn(),p(n,{event:"start",version:ie}),!t.pm)try{yt("pnpm --version",{stdio:"ignore"})}catch{n||process.stderr.write(`
|
|
2932
|
+
${C.red("\u2718")} ${C.bold("pnpm is required")} but was not found on PATH.
|
|
2933
|
+
|
|
2934
|
+
The scaffold uses pnpm workspaces + Turborepo. Install pnpm first:
|
|
2935
|
+
${C.cyan("npm i -g pnpm@10")}
|
|
2936
|
+
|
|
2937
|
+
Then retry: ${C.cyan("pnpm dlx create-sailor@latest")}
|
|
2938
|
+
(or pass ${C.dim("--pm=npm")} if you know workspace:* won't resolve in your setup)
|
|
2939
|
+
|
|
2940
|
+
`),p(n,{event:"error",code:"PNPM_MISSING"}),process.exit(1)}let c=o??(i?"./my-saas-app":void 0),a;if(c)a=c;else if(s)a="./my-saas-app";else{let g=await k.group({name:()=>k.text({message:"Where should we create your project?",placeholder:"./my-saas-app",defaultValue:"./my-saas-app",validate:S=>{if(S.length===0)return"Please enter a path."}})},{onCancel:()=>{process.stdout.write(C.red(`\u2718 Cancelled
|
|
2941
|
+
`)),process.exit(130)}});a=String(g.name)}let l=St.basename(St.resolve(a)),m=t.pm??ta(),y=!!t.region,x=!!t.auth,N=!!t.payment,L=!!t.ai,re=!!t.email,b=!!t.storage,u=!!t.monitoring,T=!!t.analytics,ht=!!t.sms,cn=t.i18n!==void 0,h,tt,Q,nt,K,M,G=Pn(t.socialLogin),ot,Mt,he,Me,Z,V,Et;if(s){h=Qo(t.region),tt=Yo(t.orm),Q=$o(t.db);let g=N?t.payment:Zo(h);nt=qo(g),K=Go(g),M=Wo(t.auth);let S=Vt({mode:L?t.ai?.trim().toLowerCase()==="none"?"none":"direct":"gateway",providerIds:L?Ho(t.ai):void 0});ot=S.mode,Mt=S.routing,he=S.providerIds,Z=Jo(t.deploy),V=zo(t.docs,n),Et=cn?!!t.i18n:!0}else{let g={};y||(g.region=()=>k.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"})),x||(g.auth=()=>k.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"})),L||(g.aiMode=()=>k.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:_})=>{if(_.aiMode!=="direct")return Promise.resolve([]);let ee=Kt.map(Dt=>({value:Dt.id,label:`[${Dt.category}] ${Dt.name}`}));return k.multiselect({message:"Select direct AI provider adapters (expert path)",options:ee,initialValues:["openai"],required:!1})},g.customAiName=({results:_})=>_.aiMode==="custom"?k.text({message:"Custom endpoint name (e.g. proxy, local):",defaultValue:"custom",placeholder:"custom"}):Promise.resolve(void 0),g.customAiBaseUrl=({results:_})=>_.aiMode==="custom"?k.text({message:"Custom endpoint base URL (e.g. https://api.proxy.com/v1):",validate:ee=>{if(ee.length===0)return"Base URL is required."}}):Promise.resolve(void 0),g.customAiApiKeyEnv=({results:_})=>_.aiMode==="custom"?k.text({message:"Environment variable name for the API Key:",defaultValue:"CUSTOM_AI_API_KEY",placeholder:"CUSTOM_AI_API_KEY"}):Promise.resolve(void 0));let S=Object.keys(g).length>0?await k.group(g,{onCancel:()=>{process.stdout.write(C.red(`\u2718 Cancelled
|
|
2942
|
+
`)),process.exit(130)}}):{};h=Qo(y?t.region:S.region),tt=Yo(t.orm),Q=$o(t.db);let B=N?t.payment:Zo(h);nt=qo(B),K=Go(B),M=Wo(x?t.auth:S.auth);let bt=S.aiMode==="custom"?{name:S.customAiName??"custom",baseURL:S.customAiBaseUrl??"",apiKeyEnvName:S.customAiApiKeyEnv??"CUSTOM_AI_API_KEY"}:void 0,v=Vt({mode:L?t.ai?.trim().toLowerCase()==="none"?"none":"direct":S.aiMode??"gateway",providerIds:L?Ho(t.ai):S.aiProviders,customEndpoint:bt});ot=v.mode,Mt=v.routing,he=v.providerIds,v.customEndpoint&&(Me={name:v.customEndpoint.name,baseURL:v.customEndpoint.baseURL,apiKeyEnvName:v.customEndpoint.apiKeyEnvName}),Z=Jo(t.deploy),V=zo(t.docs,n),Et=cn?!!t.i18n:!0}let A=oa(h),Fe=re?t.email:A.email,De=na(b?t.storage:void 0,A.storage),je=u?t.monitoring:A.monitoring,Ke=T?t.analytics:A.analytics,Ve=ht?t.sms:A.sms,Be=t.queue??A.queue,$e=t.search??A.search,rt=t.cache??A.cache,Ye=t.notifications??A.notifications,qe=t.webhooks??A.webhooks,it=t.cms??A.cms,Ge=t.featureFlags??A.featureFlags,We=t.captcha??A.captcha,vt=t.mcp??A.mcp,st=t.metering??A.metering,He=t.billingMode??A.billingMode,Xe=t.idp??A.idp,I=Do({cronJobs:t.cronJobs,auditLog:t.auditLog,apiKeys:t.apiKeys,commandPalette:t.commandPalette,cookieConsent:t.cookieConsent,legalPages:t.legalPages,chinaCompliance:t.chinaCompliance},h),ze=un([{flag:"queue",provider:Be},{flag:"search",provider:$e},{flag:"notifications",provider:Ye},{flag:"webhooks",provider:qe},{flag:"feature-flags",provider:Ge},{flag:"captcha",provider:We}]);function Ft(){for(let g of ze)if(n)p(!0,{event:"warn",step:g.flag,provider:g.provider,packageStatus:g.status,message:se(g.status)});else{let S=Je(g.status);process.stdout.write(C.yellow(`\u26A0 ${g.flag}=${g.provider} ${S} \u2014 ${se(g.status)}
|
|
2943
|
+
`))}}let Ee={region:h,orm:tt,database:Q,payment:nt,aiMode:ot,aiRouting:Mt,aiProviders:he,customAiEndpoint:Me,deployTarget:Z,docs:V,i18n:Et,email:Fe,storage:De,monitoring:je,analytics:Ke,sms:Ve,queue:Be,search:$e,cache:rt,notifications:Ye,webhooks:qe,cms:it,featureFlags:Ge,captcha:We,mcp:vt,metering:st,billingMode:He,idp:Xe,cronJobs:I.cronJobs,auditLog:I.auditLog,apiKeys:I.apiKeys,commandPalette:I.commandPalette,cookieConsent:I.cookieConsent,legalPages:I.legalPages,chinaCompliance:I.chinaCompliance},_t=[["Project name",l],["Region",h],["Auth",M],["Social login",G.length>0?G.map(g=>at.find(S=>S.id===g)?.name??g).join(", "):"none"],["ORM",tt],["Database",Q],["Payment",K],["AI topology",`${ot}${he.length>0?` (${he.join(", ")} seed)`:""}${Me?" + custom endpoint":""}`],["Email",Fe],["Storage",De],["Monitoring",je],["Analytics",Ke],["SMS",Ve],["Deploy Target",Z],["Docs Framework",V]];n?_t.forEach(([g,S],B)=>{p(!0,{event:"step",step:g,value:S,index:B+1,total:_t.length})}):_t.forEach(([g,S],B)=>{yn({index:B+1,total:_t.length,label:g,value:S})});let er={databaseUrl:"postgresql://postgres:postgres@localhost:5432/nebutra",clerkPublishable:"",clerkSecret:""},pn=Date.now();if(r){let g=he.length,S=[`clone template \u2192 ${a}`,"write nebutra.config.json",`prune template (orm=${tt}, i18n=${Et})`,`region \u2192 ${h}`,`auth \u2192 ${M==="none"?"skip (remove packages/auth)":`configure ${M}`}`,...G.length>0?[`social-login \u2192 generate ${G.length} callback route${G.length===1?"":"s"} + SocialLoginButtons.tsx (${G.join(", ")})`]:[],`db \u2192 ${Q==="none"?"skip (remove packages/db)":`configure Prisma for ${Q}`}`,`payment \u2192 ${K==="none"?"skip (remove packages/billing)":`configure ${K}`}`,...V!=="none"?[`docs \u2192 scaffold apps/docs (${V==="fumadocs"?"fumadocs":`${V} \u2192 fumadocs fallback`})`]:[],...g>0||Me?[`ai-providers \u2192 generate ${ot} registry seed + env (${g}${Me?" + custom":""} provider${g===1&&!Me?"":"s"})`]:[],...Fe!=="none"?[`email \u2192 configure ${Fe}`]:[],...De!=="none"?[`storage \u2192 configure ${De}`]:[],...je!=="none"?[`monitoring \u2192 configure ${je}`]:[],...Ke!=="none"?[`analytics \u2192 configure ${Ke}`]:[],...Ve!=="none"?[`sms \u2192 configure ${Ve}`]:[],...Be!=="none"?[`queue \u2192 configure ${Be}`]:[],...$e!=="none"?[`search \u2192 configure ${$e}`]:[],...rt!=="none"?[`cache \u2192 configure ${rt}`]:[],...Ye!=="none"?[`notifications \u2192 configure ${Ye}`]:[],...qe!=="none"?[`webhooks \u2192 configure ${qe}`]:[],...it!=="none"?[`cms \u2192 configure ${it}`]:[],...Ge!=="none"?[`feature-flags \u2192 configure ${Ge}`]:[],...We!=="none"?[`captcha \u2192 configure ${We}`]:[],`mcp \u2192 ${vt==="on"?"enable MCP server":"remove packages/mcp"}`,`metering \u2192 ${st==="off"?"disabled":st==="auto"?nt!=="none"?"enabled (auto: payment set)":"disabled (auto: no payment)":"enabled"}`,...He!=="usage"?[`billing-mode \u2192 ${He}`]:[],...Xe!=="clerk"?[`idp \u2192 ${Xe}`]:[],`cron-jobs \u2192 ${I.cronJobs?"enabled":"disabled"}`,`audit-log \u2192 ${I.auditLog?"enabled":"disabled"}`,`api-keys \u2192 ${I.apiKeys?"enabled":"disabled"}`,`command-palette \u2192 ${I.commandPalette?"enabled":"disabled"}`,`cookie-consent \u2192 ${I.cookieConsent?"enabled":"disabled"}`,`legal-pages \u2192 ${I.legalPages?"enabled":"disabled"}`,`china-compliance \u2192 ${I.chinaCompliance?"enabled (@nebutra/china-compliance + ICP footer)":"disabled"}`,`compliance \u2192 inject ${h} 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)",...Z!=="none"?[`deploy \u2192 inject ${Z} 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 B of S)p(!0,{event:"plan",action:B});Ft(),p(!0,{event:"done",dryRun:!0})}else{process.stdout.write(`
|
|
2944
|
+
`+C.bold(`Dry run \u2014 planned actions:
|
|
2928
2945
|
`));for(let B of S)process.stdout.write(` \u2022 ${B}
|
|
2929
|
-
`);
|
|
2930
|
-
`+
|
|
2931
|
-
`))),
|
|
2946
|
+
`);ze.length>0&&(process.stdout.write(`
|
|
2947
|
+
`+C.bold(C.yellow(`Preview-status providers selected:
|
|
2948
|
+
`))),Ft()),process.stdout.write(C.dim(`
|
|
2932
2949
|
No files were written.
|
|
2933
|
-
`))}process.exit(0)}let
|
|
2934
|
-
`+
|
|
2935
|
-
`)),
|
|
2936
|
-
`))),process.exit(130)};process.on("SIGINT",
|
|
2937
|
-
`));try{
|
|
2938
|
-
`))}}else p(n,{event:"step",step:"install",status:"skip"});if(t.git!==!1){n&&p(!0,{event:"step",step:"git-init",status:"start"});try{
|
|
2939
|
-
`))}}else p(n,{event:"step",step:"git-init",status:"skip"});let
|
|
2950
|
+
`))}process.exit(0)}let tr=async()=>{process.stdout.write(`
|
|
2951
|
+
`+C.red(`\u2718 Cancelled
|
|
2952
|
+
`)),Se.existsSync(a)&&await k.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}
|
|
2953
|
+
`))),process.exit(130)};process.on("SIGINT",tr);try{p(n,{event:"step",step:"clone",status:"start"}),await so(a),p(n,{event:"step",step:"clone",status:"ok"}),p(n,{event:"step",step:"package",status:"start"}),await yo(a,l),p(n,{event:"step",step:"package",status:"ok"}),p(n,{event:"step",step:"config",status:"start"}),await Un(a,Ee),p(n,{event:"step",step:"config",status:"ok"}),p(n,{event:"step",step:"prune",status:"start"}),await Io(a,Ee),p(n,{event:"step",step:"prune",status:"ok"});let g=St.join(a,"packages/platform/db/prisma/schema.prisma");if(Se.existsSync(g)){p(n,{event:"step",step:"schema-prune",status:"start"});try{let v=Se.readFileSync(g,"utf-8"),_=tn(v,{auth:M,payment:K,"billing-mode":He,idp:Xe,template:"saas"});Se.writeFileSync(g,_),p(n,{event:"step",step:"schema-prune",status:"ok"})}catch(v){let _=v instanceof Error?v.message:String(v);p(n,{event:"step",step:"schema-prune",status:"error",error:_})}}else p(n,{event:"step",step:"schema-prune",status:"skip"});if(p(n,{event:"step",step:"auth",choice:M,status:"start"}),await _n(a,M),p(n,{event:"step",step:"auth",choice:M,status:"ok"}),G.length>0?(p(n,{event:"step",step:"social-login",providers:G,status:"start"}),await In(a,G,M),p(n,{event:"step",step:"social-login",providers:G,status:"ok"})):p(n,{event:"step",step:"social-login",status:"skip"}),p(n,{event:"step",step:"db",choice:Q,status:"start"}),await Fn(a,Q,l),p(n,{event:"step",step:"db",choice:Q,status:"ok"}),p(n,{event:"step",step:"payment",choice:K,status:"start"}),await ho(a,K),p(n,{event:"step",step:"payment",choice:K,status:"ok"}),await Gn(a,Fe,h),n&&p(!0,{event:"step",step:"email",choice:Fe,status:"ok"}),await Fo(a,De,h),n&&p(!0,{event:"step",step:"storage",choice:De,status:"ok"}),await mo(a,je,h),n&&p(!0,{event:"step",step:"monitoring",choice:je,status:"ok"}),await En(a,Ke,h),n&&p(!0,{event:"step",step:"analytics",choice:Ke,status:"ok"}),await Lo(a,Ve,h),n&&p(!0,{event:"step",step:"sms",choice:Ve,status:"ok"}),await ko(a,Be,h),n&&p(!0,{event:"step",step:"queue",choice:Be,status:"ok"}),await To(a,$e,h),n&&p(!0,{event:"step",step:"search",choice:$e,status:"ok"}),await Rn(a,rt,h),n&&p(!0,{event:"step",step:"cache",choice:rt,status:"ok"}),await fo(a,Ye,h),n&&p(!0,{event:"step",step:"notifications",choice:Ye,status:"ok"}),await Ko(a,qe,h),n&&p(!0,{event:"step",step:"webhooks",choice:qe,status:"ok"}),await Tn(a,it,h),n&&p(!0,{event:"step",step:"cms",choice:it,status:"ok"}),await Zn(a,Ge,h),n&&p(!0,{event:"step",step:"feature-flags",choice:Ge,status:"ok"}),await xn(a,We,h),n&&p(!0,{event:"step",step:"captcha",choice:We,status:"ok"}),await ao(a,vt),n&&p(!0,{event:"step",step:"mcp",mode:vt,status:"ok"}),await po(a,st,nt),n&&p(!0,{event:"step",step:"metering",mode:st,status:"ok"}),p(n,{event:"step",step:"prune-wave-features",status:"start"}),Ao(a,I),p(n,{event:"step",step:"prune-wave-features",status:"ok",toggles:I}),Ee.orm==="prisma"&&Ee.database!=="none"){let v=St.join(a,"packages/platform/db/prisma/schema.prisma");if(Se.existsSync(v)){p(n,{event:"step",step:"schema-prune",status:"start"});try{let _=Se.readFileSync(v,"utf8"),ee=tn(_,{auth:M,payment:K,"billing-mode":He,idp:Xe,template:"saas"});Se.writeFileSync(v,ee),p(n,{event:"step",step:"schema-prune",status:"ok"})}catch(_){let ee=_ instanceof Error?_.message:String(_);p(n,{event:"step",step:"schema-prune",status:"error",error:ee})}}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:He,status:"ok"}),p(n,{event:"step",step:"idp",choice:Xe,status:"ok"}),await On(a,h),n&&p(!0,{event:"step",step:"compliance",region:h,status:"ok"}),await Jn(a),await Oo(a,M),await Vo(a,{projectName:l,region:h,previewSelections:ze,waveFeatures:I}),Ee.aiMode!=="none"){p(n,{event:"step",step:"ai-providers",status:"start"});let v={providerIds:Ee.aiProviders,customEndpoint:Ee.customAiEndpoint},_=St.join(a,"packages/ai/ai-providers/templates");await Po(a,v,_),p(n,{event:"step",step:"ai-providers",status:"ok"})}else p(n,{event:"step",step:"ai-providers",status:"skip"});if(p(n,{event:"step",step:"docs",status:"start"}),V!=="none"?(await Bn(a,{framework:V,projectName:l}),p(n,{event:"step",step:"docs",framework:"fumadocs",requested:V,status:"ok"})):p(n,{event:"step",step:"docs",status:"skip"}),p(n,{event:"step",step:"deploy-target",status:"start"}),Z!=="none"&&await jn(a,Z),p(n,{event:"step",step:"deploy-target",status:"ok"}),p(n,{event:"step",step:"env",status:"start"}),await Hn(a,er),p(n,{event:"step",step:"env",status:"ok"}),t.install!==!1){let v=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
|
|
2954
|
+
`));try{yt(v,{cwd:a,stdio:n?"ignore":"inherit"}),p(n,{event:"step",step:"install",status:"ok"})}catch(_){let ee=_ instanceof Error?_.message:String(_);n?p(!0,{event:"step",step:"install",status:"error",error:ee}):process.stdout.write(C.yellow(` \u26A0 install failed \u2014 run '${v}' manually.
|
|
2955
|
+
`))}}else p(n,{event:"step",step:"install",status:"skip"});if(t.git!==!1){n&&p(!0,{event:"step",step:"git-init",status:"start"});try{yt("git init -q",{cwd:a,stdio:"ignore"}),yt("git add -A",{cwd:a,stdio:"ignore"}),yt('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(v){let _=v instanceof Error?v.message:String(v);n?p(!0,{event:"step",step:"git-init",status:"error",error:_}):process.stdout.write(C.yellow(` \u26A0 git init skipped \u2014 not fatal.
|
|
2956
|
+
`))}}else p(n,{event:"step",step:"git-init",status:"skip"});let bt=Math.max(1,Math.round((Date.now()-pn)/1e3));vn({template_version:ie,package_manager:m,region:h,auth:M,payment:K,ai_providers:he,deploy_target:Z,duration_ms:Date.now()-pn}),ze.length>0&&Ft(),n?p(!0,{event:"done",status:"ok",elapsedSec:bt,targetDir:a,previewSelections:ze,waveFeatures:I}):mn({elapsedSec:bt,targetDir:a,skippedInstall:t.install===!1,previewSelections:ze,waveFeatures:I});try{ea({pkg:{name:Bo,version:ie},updateCheckInterval:1e3*60*60*24}).notify({defer:!1,isGlobal:!0})}catch{}process.exit(0)}catch(g){let S=g instanceof Error?g.message:String(g);n?p(!0,{event:"error",message:S}):process.stdout.write(C.red(`
|
|
2940
2957
|
\u2718 Failed: ${S}
|
|
2941
|
-
`)),process.exit(1)}}
|
|
2958
|
+
`)),process.exit(1)}}ra().catch(e=>{process.stderr.write(String(e)+`
|
|
2942
2959
|
`),process.exit(1)});
|