nebutra 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +676 -0
- package/dist/chunk-I7AUKTXE.js +1 -0
- package/dist/delegate-YL6SAZNF.js +1 -0
- package/dist/index.js +303 -142
- package/dist/logger-KLED6BEE.js +1 -0
- package/package.json +13 -14
- package/dist/aws-kms-CYDJXAN2.js +0 -1
- package/dist/chunk-27HYVYB7.js +0 -1
- package/dist/chunk-7DCGNQSQ.js +0 -5
- package/dist/chunk-7HOF72FC.js +0 -1
- package/dist/chunk-7VJYOZUP.js +0 -2
- package/dist/chunk-A2KGYHAM.js +0 -1
- package/dist/chunk-B5RLHF2U.js +0 -40
- package/dist/chunk-DIXI4M5O.js +0 -1
- package/dist/chunk-ELIYZZ3E.js +0 -1
- package/dist/chunk-EXLRPVEX.js +0 -1
- package/dist/chunk-FGJ5K4WM.js +0 -1
- package/dist/chunk-FSRPMVAS.js +0 -1
- package/dist/chunk-HVYUAVRI.js +0 -9
- package/dist/chunk-LOQM6XQV.js +0 -1
- package/dist/chunk-M5MEP5J7.js +0 -20
- package/dist/chunk-OJM5Y4UT.js +0 -1
- package/dist/chunk-PXU2OOBT.js +0 -1
- package/dist/chunk-QEIMNOY2.js +0 -1
- package/dist/chunk-QMKVUKS6.js +0 -1
- package/dist/chunk-RR5R4AKJ.js +0 -1
- package/dist/chunk-SDX64WW4.js +0 -23
- package/dist/chunk-VOA5NGB4.js +0 -1
- package/dist/chunk-XSSPJU77.js +0 -1
- package/dist/credits-7QOXUUSW.js +0 -1787
- package/dist/delegate-AT4FVWBK.js +0 -1
- package/dist/dist-EDNC5USD.js +0 -5
- package/dist/dist-es-6FH6H7HO.js +0 -1
- package/dist/dist-es-7IMKW7US.js +0 -1
- package/dist/dist-es-GP7DK3S3.js +0 -5
- package/dist/dist-es-MB5QKMT4.js +0 -1
- package/dist/dist-es-MQVJCP43.js +0 -1
- package/dist/dist-es-ZA2NRM7G.js +0 -2
- package/dist/dist-es-ZN3KNHF5.js +0 -2
- package/dist/event-streams-T5RU6C4N.js +0 -1
- package/dist/loadSso-BNAQCJV4.js +0 -1
- package/dist/local-WW37QOXE.js +0 -1
- package/dist/logger-LKYUI2MJ.js +0 -1
- package/dist/query_compiler_fast_bg.postgresql-PN2AWLCP.js +0 -2
- package/dist/query_compiler_fast_bg.postgresql.wasm-base64-CASS54LO.js +0 -1
- package/dist/signin-S2JHPGGK.js +0 -1
- package/dist/src-N47JLVAD.js +0 -1
- package/dist/sso-oidc-CGZCRGZD.js +0 -1
- package/dist/sts-S4HBOJFJ.js +0 -6
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{b as s}from"./chunk-JDX5NXDX.js";import{a as
|
|
2
|
+
import{b as s}from"./chunk-JDX5NXDX.js";import{a as h,b as x,c as _,d as fe,e as $e,f as de}from"./chunk-LGUMYJ4Q.js";import{a as kr}from"./chunk-I7AUKTXE.js";import{Command as bc}from"commander";import{execSync as St}from"child_process";import{promises as Se}from"fs";import V from"path";import*as S from"@clack/prompts";import je from"picocolors";var c={SUCCESS:0,ERROR:1,INVALID_ARGS:2,NOT_FOUND:3,PERMISSION_DENIED:4,CONFLICT:5,NETWORK_ERROR:6,TIMEOUT:7,CANCELLED:8,CONFIG_ERROR:9,DRY_RUN_OK:10,INCOMPATIBLE:11,RESOURCE_EXHAUSTED:12},zt={[c.SUCCESS]:"Command completed successfully",[c.ERROR]:"General or unknown error occurred",[c.INVALID_ARGS]:"Invalid arguments or options provided",[c.NOT_FOUND]:"Requested resource not found",[c.PERMISSION_DENIED]:"Permission denied or authentication required",[c.CONFLICT]:"Resource already exists",[c.NETWORK_ERROR]:"Network or external service error (may be retryable)",[c.TIMEOUT]:"Operation timed out (may be retryable)",[c.CANCELLED]:"Operation cancelled by user",[c.CONFIG_ERROR]:"Configuration error or missing configuration",[c.DRY_RUN_OK]:"Dry-run completed successfully",[c.INCOMPATIBLE]:"Incompatible version or dependency conflict",[c.RESOURCE_EXHAUSTED]:"System resource exhausted"};import{existsSync as Or,readdirSync as Ht,readFileSync as xr,statSync as $r}from"fs";import{dirname as Yt,join as wt,parse as Ar}from"path";import{fileURLToPath as _r}from"url";var Pr=`const featureEnvKeys = {{envKeysArray}} as const;
|
|
3
3
|
|
|
4
4
|
export const platformFeature = {
|
|
5
5
|
name: "{{feature}}",
|
|
@@ -19,7 +19,7 @@ export function getFeatureProvider() {
|
|
|
19
19
|
envKeys: featureEnvKeys,
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
-
`,
|
|
22
|
+
`,Ir=`# {{feature}} Feature
|
|
23
23
|
|
|
24
24
|
Installed by \`nebutra add {{feature}}\`.
|
|
25
25
|
|
|
@@ -31,7 +31,7 @@ Next steps:
|
|
|
31
31
|
1. Set the required environment variables in \`{{envFile}}\`.
|
|
32
32
|
2. Wire this starter into the generated package or app surface.
|
|
33
33
|
3. Replace the placeholder helper with your production adapter.
|
|
34
|
-
|
|
34
|
+
`,Nr="integrations";function Ae(e,t=Nr){let n=`packages/${t}/${e}`;return[{path:`${n}/src/index.ts`,content:Pr},{path:`${n}/README.md`,content:Ir}]}var Tr={queue:{name:"queue",description:"Background job queue starter for Nebutra-style monorepos.",envFile:".env.local",providers:[{id:"upstash",description:"HTTP queue transport powered by Upstash QStash.",dependencies:[{name:"@upstash/qstash",version:"latest"}],env:[{key:"QSTASH_TOKEN",description:"Upstash QStash REST token"},{key:"QSTASH_CURRENT_SIGNING_KEY",description:"Upstash current signing key for webhook verification"},{key:"QSTASH_NEXT_SIGNING_KEY",description:"Upstash next signing key for webhook rotation"}]},{id:"bullmq",description:"Redis-backed worker queue powered by BullMQ.",dependencies:[{name:"bullmq",version:"latest"},{name:"ioredis",version:"latest"}],env:[{key:"REDIS_URL",description:"Redis connection string for BullMQ workers"}]}],files:[{path:"packages/integrations/queue/src/index.ts",content:`const queueEnvKeys = {{envKeysArray}} as const;
|
|
35
35
|
|
|
36
36
|
export const queueFeature = {
|
|
37
37
|
name: "queue",
|
|
@@ -51,7 +51,7 @@ export function getQueueProvider() {
|
|
|
51
51
|
envKeys: queueEnvKeys,
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
`},{path:"packages/queue/README.md",content:`# Queue Feature
|
|
54
|
+
`},{path:"packages/integrations/queue/README.md",content:`# Queue Feature
|
|
55
55
|
|
|
56
56
|
Installed by \`nebutra add queue\`.
|
|
57
57
|
|
|
@@ -63,7 +63,7 @@ Next steps:
|
|
|
63
63
|
1. Set the required environment variables in \`{{envFile}}\`.
|
|
64
64
|
2. Wire this starter into your app or worker runtime.
|
|
65
65
|
3. Replace the placeholder provider helpers with production job handlers.
|
|
66
|
-
`}]},search:{name:"search",description:"Search service starter for Nebutra-style monorepos.",envFile:".env.local",providers:[{id:"meilisearch",description:"Meilisearch client starter for full-text indexing.",dependencies:[{name:"meilisearch",version:"latest"}],env:[{key:"MEILISEARCH_HOST",description:"Meilisearch base URL"},{key:"MEILISEARCH_ADMIN_KEY",description:"Meilisearch admin API key"}]},{id:"typesense",description:"Typesense client starter for typed search queries.",dependencies:[{name:"typesense",version:"latest"}],env:[{key:"TYPESENSE_HOST",description:"Typesense host URL"},{key:"TYPESENSE_API_KEY",description:"Typesense admin API key"},{key:"TYPESENSE_COLLECTION",description:"Default Typesense collection name"}]}],files:[{path:"packages/search/src/index.ts",content:`const searchEnvKeys = {{envKeysArray}} as const;
|
|
66
|
+
`}]},search:{name:"search",description:"Search service starter for Nebutra-style monorepos.",envFile:".env.local",providers:[{id:"meilisearch",description:"Meilisearch client starter for full-text indexing.",dependencies:[{name:"meilisearch",version:"latest"}],env:[{key:"MEILISEARCH_HOST",description:"Meilisearch base URL"},{key:"MEILISEARCH_ADMIN_KEY",description:"Meilisearch admin API key"}]},{id:"typesense",description:"Typesense client starter for typed search queries.",dependencies:[{name:"typesense",version:"latest"}],env:[{key:"TYPESENSE_HOST",description:"Typesense host URL"},{key:"TYPESENSE_API_KEY",description:"Typesense admin API key"},{key:"TYPESENSE_COLLECTION",description:"Default Typesense collection name"}]}],files:[{path:"packages/integrations/search/src/index.ts",content:`const searchEnvKeys = {{envKeysArray}} as const;
|
|
67
67
|
|
|
68
68
|
export const searchFeature = {
|
|
69
69
|
name: "search",
|
|
@@ -83,7 +83,7 @@ export function getSearchProvider() {
|
|
|
83
83
|
envKeys: searchEnvKeys,
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
|
-
`},{path:"packages/search/README.md",content:`# Search Feature
|
|
86
|
+
`},{path:"packages/integrations/search/README.md",content:`# Search Feature
|
|
87
87
|
|
|
88
88
|
Installed by \`nebutra add search\`.
|
|
89
89
|
|
|
@@ -95,47 +95,133 @@ Next steps:
|
|
|
95
95
|
1. Add credentials to \`{{envFile}}\`.
|
|
96
96
|
2. Point your indexing jobs at the configured provider.
|
|
97
97
|
3. Replace the placeholder helpers with production query/index code.
|
|
98
|
-
`}]},cache:{name:"cache",description:"Cache adapter starter aligned with create-sailor cache providers.",envFile:".env.local",providers:[{id:"upstash-redis",description:"Serverless Redis cache powered by Upstash.",dependencies:[{name:"@upstash/redis",version:"latest"}],env:[{key:"UPSTASH_REDIS_REST_URL",description:"Upstash Redis REST URL"}]},{id:"vercel-kv",description:"Vercel KV compatible cache adapter.",dependencies:[{name:"@vercel/kv",version:"latest"}],env:[{key:"KV_REST_API_URL",description:"Vercel KV REST API URL"}]},{id:"redis",description:"Self-hosted Redis cache adapter.",dependencies:[{name:"ioredis",version:"latest"}],env:[{key:"REDIS_URL",description:"Redis connection URL"}]},{id:"dragonfly",description:"Dragonfly Redis-compatible cache adapter.",dependencies:[{name:"ioredis",version:"latest"}],env:[{key:"DRAGONFLY_URL",description:"Dragonfly connection URL"}]}],files:
|
|
99
|
-
`)}function
|
|
98
|
+
`}]},cache:{name:"cache",description:"Cache adapter starter aligned with create-sailor cache providers.",envFile:".env.local",providers:[{id:"upstash-redis",description:"Serverless Redis cache powered by Upstash.",dependencies:[{name:"@upstash/redis",version:"latest"}],env:[{key:"UPSTASH_REDIS_REST_URL",description:"Upstash Redis REST URL"}]},{id:"vercel-kv",description:"Vercel KV compatible cache adapter.",dependencies:[{name:"@vercel/kv",version:"latest"}],env:[{key:"KV_REST_API_URL",description:"Vercel KV REST API URL"}]},{id:"redis",description:"Self-hosted Redis cache adapter.",dependencies:[{name:"ioredis",version:"latest"}],env:[{key:"REDIS_URL",description:"Redis connection URL"}]},{id:"dragonfly",description:"Dragonfly Redis-compatible cache adapter.",dependencies:[{name:"ioredis",version:"latest"}],env:[{key:"DRAGONFLY_URL",description:"Dragonfly connection URL"}]}],files:Ae("cache")},notifications:{name:"notifications",description:"Notification workflow starter for product and lifecycle messages.",envFile:".env.local",providers:[{id:"novu",description:"Novu notification workflow provider.",dependencies:[{name:"@novu/node",version:"latest"}],env:[{key:"NOVU_API_KEY",description:"Novu API key"}]},{id:"knock",description:"Knock notification workflow provider.",dependencies:[{name:"@knocklabs/node",version:"latest"}],env:[{key:"KNOCK_API_KEY",description:"Knock API key"}]},{id:"custom",description:"Custom notification adapter surface.",env:[{key:"NOTIFICATIONS_WEBHOOK_URL",description:"Custom notification webhook URL"}]}],files:Ae("notifications")},webhooks:{name:"webhooks",description:"Outbound webhook starter with provider-specific signing hooks.",envFile:".env.local",providers:[{id:"svix",description:"Svix-backed outbound webhook delivery.",dependencies:[{name:"svix",version:"latest"}],env:[{key:"SVIX_AUTH_TOKEN",description:"Svix authentication token"}]},{id:"custom",description:"Custom outbound webhook adapter.",env:[{key:"WEBHOOK_SIGNING_SECRET",description:"Webhook signing secret"}]}],files:Ae("webhooks")},cms:{name:"cms",description:"Headless CMS integration starter for marketing and docs content.",envFile:".env.local",providers:[{id:"sanity",description:"Sanity content lake client starter.",dependencies:[{name:"next-sanity",version:"latest"}],env:[{key:"NEXT_PUBLIC_SANITY_PROJECT_ID",description:"Sanity project ID"},{key:"NEXT_PUBLIC_SANITY_DATASET",description:"Sanity dataset"}]},{id:"contentful",description:"Contentful content API starter.",dependencies:[{name:"contentful",version:"latest"}],env:[{key:"CONTENTFUL_SPACE_ID",description:"Contentful space ID"},{key:"CONTENTFUL_ACCESS_TOKEN",description:"Contentful access token"}]},{id:"strapi",description:"Strapi REST client starter.",env:[{key:"STRAPI_URL",description:"Strapi base URL"},{key:"STRAPI_API_TOKEN",description:"Strapi API token"}]}],files:Ae("cms")},"feature-flags":{name:"feature-flags",description:"Feature flag starter for gradual rollout and runtime controls.",envFile:".env.local",providers:[{id:"vercel-flags",description:"Vercel Flags SDK starter.",dependencies:[{name:"@vercel/flags",version:"latest"}],env:[{key:"FLAGS_SECRET",description:"Feature flag signing secret"}]},{id:"growthbook",description:"GrowthBook feature flag client starter.",dependencies:[{name:"@growthbook/growthbook",version:"latest"}],env:[{key:"GROWTHBOOK_CLIENT_KEY",description:"GrowthBook client key"}]},{id:"configcat",description:"ConfigCat feature flag client starter.",dependencies:[{name:"configcat-node",version:"latest"}],env:[{key:"CONFIGCAT_SDK_KEY",description:"ConfigCat SDK key"}]}],files:Ae("feature-flags","platform")},captcha:{name:"captcha",description:"Captcha verification starter for abuse prevention.",envFile:".env.local",providers:[{id:"turnstile",description:"Cloudflare Turnstile verification surface.",env:[{key:"NEXT_PUBLIC_TURNSTILE_SITE_KEY",description:"Turnstile site key"},{key:"TURNSTILE_SECRET_KEY",description:"Turnstile secret key"}]},{id:"hcaptcha",description:"hCaptcha verification surface.",env:[{key:"NEXT_PUBLIC_HCAPTCHA_SITE_KEY",description:"hCaptcha site key"},{key:"HCAPTCHA_SECRET_KEY",description:"hCaptcha secret key"}]},{id:"aliyun-slide",description:"Aliyun slide captcha verification surface.",env:[{key:"ALIYUN_CAPTCHA_SCENE_ID",description:"Aliyun captcha scene ID"},{key:"ALIYUN_CAPTCHA_SECRET",description:"Aliyun captcha secret"}]}],files:Ae("captcha","iam")}},jr=["iam","commerce","integrations","ai","design","platform","ops"];function Ur(){let e;try{e=Yt(_r(import.meta.url))}catch{e=process.cwd()}for(;e&&e!==Ar(e).root;){try{let t=Ht(e);if(t.includes("pnpm-workspace.yaml")||t.includes("packages")){let n=wt(e,"packages");if(Or(n)&&$r(n).isDirectory())return n}}catch{}e=Yt(e)}return null}function Dr(e,t,n,r,o){let a=(t.env??[]).map(i=>({key:i,description:`${i} (declared by ${o})`}));return{name:e,description:t.summary??n.description??`${e} feature`,group:t.category??r,dependencies:[{name:o,version:"workspace:*"}],env:a,envFile:t.envFile??".env.local",files:[]}}var He=null;function Lr(){if(He)return He;let e={},t=Ur();if(!t)return He=e,e;for(let n of jr){let r=wt(t,n),o;try{o=Ht(r)}catch{continue}for(let a of o){let i=wt(r,a,"package.json"),l;try{l=xr(i,"utf-8")}catch{continue}let u;try{u=JSON.parse(l)}catch{continue}let m=u.nebutra,y=m?.featureId;!m||!y||!u.name||(e[y]=Dr(y,m,u,n,u.name))}}return He=e,e}function Jt(){return{...Lr(),...Tr}}function Qt(){return Object.values(Jt())}async function Xt(e){return Jt()[e]??null}function Rt(e){return e instanceof Error&&e.message.startsWith("EXIT:")}function Zt(e){try{return St(`npm list ${e}`,{stdio:"pipe"}),!0}catch{return!1}}function en(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
99
|
+
`)}function Fr(e,t){let n=[];if(t["21st"]){let r=t["21st"],o=r.startsWith("http")?r:`https://21st.dev/r/${r}`;n.push({type:"run-command",source:"21st",component:r,resolvedUrl:o,command:`pnpm dlx shadcn@latest add "${o}"`})}return t.v0&&n.push({type:"run-command",source:"v0",component:"v0-component",resolvedUrl:t.v0,command:`pnpm dlx shadcn@latest add "${t.v0}"`}),e.length>0&&!t["21st"]&&!t.v0&&s.warn("Local feature installs are handled separately from external registry flows."),{mode:"dry-run",planType:"external-add",cwd:process.cwd(),timestamp:new Date().toISOString(),operations:n,summary:{execute:n.length}}}function tn(e,t){if(!t||t.length===0)return[];let n=new Map;for(let r of t)n.set(`${e}:${r.name}`,r);return Array.from(n.values())}function nn(e){let t=new Map;for(let n of e)t.has(n.key)||t.set(n.key,n);return Array.from(t.values())}function Mr(e,t){let n=t.envEntries.map(r=>r.key);return e.replaceAll("{{feature}}",t.feature).replaceAll("{{provider}}",t.provider??"default").replaceAll("{{providerDescription}}",t.providerDescription??"No provider selected").replaceAll("{{envFile}}",t.envFile).replaceAll("{{envKeysCsv}}",n.join(", ")).replaceAll("{{envKeysArray}}",JSON.stringify(n))}async function Je(e){try{return await Se.readFile(e,"utf8")}catch(t){if(t.code==="ENOENT")return null;throw t}}async function Gr(e,t){if(t)return V.join(e,t);let n=[".env.local",".env"];for(let r of n)try{return await Se.access(V.join(e,r)),V.join(e,r)}catch{}return V.join(e,".env.local")}function Kr(e){let t=new Set;for(let n of e.matchAll(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=/gm))n[1]&&t.add(n[1]);return t}function qr(e){return e?Object.fromEntries(Object.entries(e).sort(([t],[n])=>t.localeCompare(n))):{}}function rn(e,t){if(!(!e.providers||e.providers.length===0))return t?e.providers.find(n=>n.id===t):e.providers[0]}async function Vr(e,t,n){let r=V.join(t,"package.json"),o=await Je(r);if(!o)throw new Error(`No package.json found in ${t}. Run this command from a project root.`);let a=JSON.parse(o),i=nn(e.flatMap(g=>[...g.descriptor.env??[],...g.provider?.env??[]])),l=await Gr(t,e[0]?.descriptor.envFile),u=await Je(l)??"",m=Kr(u),y=i.filter(g=>!m.has(g.key)),p=i.filter(g=>m.has(g.key)),b=[];for(let g of e){let ee=nn([...g.descriptor.env??[],...g.provider?.env??[]]),Be={feature:g.name,provider:g.provider?.id,providerDescription:g.provider?.description,envFile:V.relative(t,l)||V.basename(l),envEntries:ee};for(let bt of g.descriptor.files??[]){let vt=V.join(t,bt.path),Er=V.relative(t,vt)||bt.path,Wt=Mr(bt.content,Be),Bt=await Je(vt),ze,Ye;Bt===null?(ze="create",Ye="file missing"):Bt===Wt?(ze="skip",Ye="already matches registry template"):(ze="conflict",Ye="file exists with different content"),b.push({type:"write-file",feature:g.name,path:vt,relativePath:Er,status:ze,reason:Ye,content:Wt})}}let M=tn("dependencies",e.flatMap(g=>[...g.descriptor.dependencies??[],...g.provider?.dependencies??[]])),Y=tn("devDependencies",e.flatMap(g=>[...g.descriptor.devDependencies??[],...g.provider?.devDependencies??[]])),H=[];for(let g of M){let ee=a.dependencies?.[g.name];H.push({section:"dependencies",name:g.name,action:ee?ee===g.version?"keep":"update":"add",from:ee,to:g.version})}for(let g of Y){let ee=a.devDependencies?.[g.name];H.push({section:"devDependencies",name:g.name,action:ee?ee===g.version?"keep":"update":"add",from:ee,to:g.version})}let Re={type:"merge-env",path:l,relativePath:V.relative(t,l)||V.basename(l),created:u.length===0,add:y,keep:p},pe={type:"update-package-json",path:r,relativePath:V.relative(t,r)||"package.json",changes:H},Te={create:b.filter(g=>g.status==="create").length,update:H.filter(g=>g.action==="add"||g.action==="update").length,append:y.length,skip:b.filter(g=>g.status==="skip").length+H.filter(g=>g.action==="keep").length+p.length,conflict:b.filter(g=>g.status==="conflict").length};return{mode:n,planType:"local-feature-install",cwd:t,timestamp:new Date().toISOString(),features:e.map(g=>({name:g.name,description:g.descriptor.description,provider:g.provider?.id,providerDescription:g.provider?.description})),operations:[...b,Re,pe],summary:Te}}function on(e){return e.summary.create>0||e.summary.update>0||e.summary.append>0}function Wr(e){return e.summary.conflict>0}async function Br(e){let t=e.operations.find(o=>o.type==="update-package-json"),n=e.operations.find(o=>o.type==="merge-env"),r=e.operations.filter(o=>o.type==="write-file");for(let o of r)o.status==="create"&&(await Se.mkdir(V.dirname(o.path),{recursive:!0}),await Se.writeFile(o.path,o.content,"utf8"));if(n&&n.add.length>0){let o=await Je(n.path)??"",a=n.add.map(u=>`# ${u.description}
|
|
100
100
|
${u.key}=`),i=o.trim().length>0?`
|
|
101
101
|
|
|
102
102
|
`:"",l=`${o.replace(/\s+$/,"")}${i}${a.join(`
|
|
103
103
|
|
|
104
104
|
`)}
|
|
105
|
-
`;await
|
|
106
|
-
`,"utf8")}}async function
|
|
107
|
-
Tenants:`),o.tenants.forEach(a=>{console.log(` ${N.blue(a.id)} \u2014 ${a.name||"(unnamed)"}`),console.log(` Status: ${a.status}`),a.plan&&console.log(` Plan: ${a.plan}`)})))}async function
|
|
108
|
-
Top tenants:`),o.topTenants.forEach((a,i)=>{console.log(` ${i+1}. ${a.tenantName||a.tenantId} \u2014 ${a.usage}`)})))}async function
|
|
109
|
-
Messages:`),n.messages.forEach(r=>{console.log(` ${N.blue(r.id)}`),console.log(` Type: ${r.type}`),console.log(` Error: ${r.error}`),console.log(` Created: ${r.createdAt}`)})))}async function
|
|
110
|
-
Flags:`),n.flags.forEach(r=>{let o=r.override!==void 0?N.yellow(String(r.override)):N.gray(String(r.default));console.log(` ${N.blue(r.name)} = ${o}`)})))}async function
|
|
111
|
-
Audit entries:`),o.entries.forEach(a=>{console.log(` ${a.timestamp} \u2014 ${N.blue(a.action)} by ${a.actor}`),a.details&&console.log(` ${a.details}`)})))}async function
|
|
112
|
-
Services:`),Object.entries(n.services).forEach(([o,a])=>{let i=a.status==="healthy"?N.green("\u2713"):N.red("\u2717");console.log(` ${i} ${o}: ${a.status}`),a.latency&&console.log(` Latency: ${a.latency}ms`),a.error&&console.log(` Error: ${a.error}`)}))}}function Yt(e){let t=e.command("admin").description("Platform admin operations");t.command("tenants").description("List all tenants/organizations").option("--limit <n>","Maximum number of tenants to return",o=>parseInt(o,10)).option("--offset <n>","Offset for pagination",o=>parseInt(o,10)).option("--status <status>","Filter by status (active|suspended)").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await br({limit:o.limit,offset:o.offset,status:o.status,format:o.format||a.format,dryRun:o.dryRun||a.dryRun})}),t.command("tenant <id>").description("Get tenant details").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a)=>{let i=a.optsWithGlobals?.()||a;await vr(o,{format:a.format||i.format,dryRun:a.dryRun||i.dryRun})}),t.command("suspend <id>").description("Suspend a tenant (requires --yes)").option("--yes","Confirm suspension").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a)=>{let i=a.optsWithGlobals?.()||a;await wr(o,{yes:a.yes||i.yes,format:a.format||i.format,dryRun:a.dryRun||i.dryRun})}),t.command("unsuspend <id>").description("Unsuspend a tenant").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a)=>{let i=a.optsWithGlobals?.()||a;await Rr(o,{format:a.format||i.format,dryRun:a.dryRun||i.dryRun})}),t.command("usage").description("Cross-tenant usage report").option("--period <period>","Report period (daily|weekly|monthly)").option("--top <n>","Show top N tenants",o=>parseInt(o,10)).option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await Sr({period:o.period,top:o.top,format:o.format||a.format,dryRun:o.dryRun||a.dryRun})});let n=t.command("dlq").description("Dead letter queue management");n.command("list").description("List failed messages in DLQ").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await Cr({format:o.format||a.format,dryRun:o.dryRun||a.dryRun})}),n.command("replay <id>").description("Replay a failed message").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a)=>{let i=a.optsWithGlobals?.()||a;await Er(o,{format:a.format||i.format,dryRun:a.dryRun||i.dryRun})}),n.command("purge").description("Purge the dead letter queue (requires --yes)").option("--yes","Confirm purge").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await kr({yes:o.yes||a.yes,format:o.format||a.format,dryRun:o.dryRun||a.dryRun})});let r=t.command("flags").description("Feature flag management");r.command("list").description("List all feature flags").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await Or({format:o.format||a.format,dryRun:o.dryRun||a.dryRun})}),r.command("set <flag> <value>").description("Set a feature flag override").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a,i)=>{let l=i.optsWithGlobals?.()||i;await xr(o,a,{format:i.format||l.format,dryRun:i.dryRun||l.dryRun})}),r.command("unset <flag>").description("Remove a feature flag override").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a)=>{let i=a.optsWithGlobals?.()||a;await $r(o,{format:a.format||i.format,dryRun:a.dryRun||i.dryRun})}),t.command("audit").description("Query audit log with optional filters").option("--actor <userId>","Filter by actor/user ID").option("--action <type>","Filter by action type").option("--since <date>","Filter entries since date (ISO format)").option("--limit <n>","Maximum number of entries to return",o=>parseInt(o,10)).option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await Ar({actor:o.actor,action:o.action,since:o.since,limit:o.limit,format:o.format||a.format,dryRun:o.dryRun||a.dryRun})}),t.command("health").description("Platform-wide health check").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await _r({format:o.format||a.format,dryRun:o.dryRun||a.dryRun})})}import{existsSync as Ir,readFileSync as Nr,writeFileSync as Tr}from"fs";import{resolve as bt}from"path";var b={pm:["npm","pnpm","yarn","bun"],region:["global","cn","hybrid"],orm:["prisma","drizzle","none"],db:["postgres","mysql","sqlite","none"],auth:["clerk","betterauth","none"],socialLogin:["wechat","qq","dingtalk","workweixin","feishu","weibo"],payment:["stripe","lemon","wechat","alipay","none"],ai:["openai","anthropic","deepseek","xai","moonshot","google","mistral","cohere","perplexity","ai21","upstage","siliconflow","volcengine-ark","bailian","zhipu","baichuan","minimax","stepfun","sensetime","tencent","lingyi","openrouter","vercel-gateway","litellm","portkey","aws-bedrock","azure-openai","gcp-vertex","groq","fireworks","together","huggingface","replicate","lepton","anyscale","octoai","deepinfra","novita","custom","none"],deploy:["vercel","railway","cloudflare","selfhost","none"],docs:["fumadocs","mintlify","docusaurus","nextra","vitepress","none"],email:["resend","postmark","ses","aliyun-dm","tencent-ses","netease","none"],storage:["r2","s3","supabase-storage","aliyun-oss","tencent-cos","qiniu","none"],monitoring:["sentry","datadog","bugsnag","aliyun-arms","tingyun","none"],analytics:["posthog","plausible","umami","mixpanel","baidu","sensors","growingio","none"],sms:["twilio","messagebird","plivo","aliyun-sms","tencent-sms","yunpian","none"],queue:["qstash","bullmq","upstash","sqs","none"],search:["meilisearch","typesense","algolia","pgvector","none"],cache:["upstash-redis","vercel-kv","redis","dragonfly","none"],notifications:["novu","knock","custom","none"],webhooks:["svix","custom","none"],cms:["sanity","contentful","strapi","none"],featureFlags:["vercel-flags","growthbook","configcat","none"],captcha:["turnstile","hcaptcha","aliyun-slide","none"],mcp:["on","off"],metering:["auto","on","off"],billingMode:["usage","seat","credits"],idp:["clerk","oauth-server"]},Pr=[{flags:"-p, --pm <id>",description:"Package manager: npm, pnpm, yarn, or bun"},{flags:"--region <id>",description:"Target region: global, cn, or hybrid"},{flags:"--orm <id>",description:"ORM: prisma, drizzle, or none"},{flags:"--db <id>",description:"Database: postgres, mysql, sqlite, or none"},{flags:"--auth <id>",description:"Auth provider: clerk, betterauth, or none"},{flags:"--social-login <ids>",description:"Comma-separated CN social login providers"},{flags:"--payment <id>",description:"Payment provider: stripe, lemon, wechat, alipay, or none"},{flags:"--ai <ids>",description:"Expert/non-interactive AI provider seed ids"},{flags:"--deploy <target>",description:"Deploy target: vercel, railway, cloudflare, selfhost, or none"},{flags:"--docs <id>",description:"Docs framework: fumadocs, mintlify, docusaurus, nextra, vitepress, or none"},{flags:"--email <id>",description:"Transactional email provider"},{flags:"--storage <id>",description:"Object storage provider"},{flags:"--monitoring <id>",description:"Monitoring provider"},{flags:"--analytics <id>",description:"Analytics provider"},{flags:"--sms <id>",description:"SMS provider"},{flags:"--queue <id>",description:"Queue provider"},{flags:"--search <id>",description:"Search provider"},{flags:"--cache <id>",description:"Cache provider"},{flags:"--notifications <id>",description:"Notifications provider"},{flags:"--webhooks <id>",description:"Outbound webhooks provider"},{flags:"--cms <id>",description:"Headless CMS provider"},{flags:"--feature-flags <id>",description:"Feature flag provider"},{flags:"--captcha <id>",description:"Captcha provider"},{flags:"--mcp <mode>",description:"MCP server mode: on or off"},{flags:"--metering <mode>",description:"Metering mode: auto, on, or off"},{flags:"--billing-mode <mode>",description:"Billing mode: usage, seat, or credits"},{flags:"--idp <id>",description:"Identity provider: clerk or oauth-server"},{flags:"--i18n",description:"Enable i18n",default:!0},{flags:"--no-i18n",description:"Disable i18n"},{flags:"--no-install",description:"Skip package install"},{flags:"--no-git",description:"Skip git init"},{flags:"--dry-run",description:"Preview project scaffolding without creating files (exit code 10)"},{flags:"--json",description:"Emit machine-readable JSON events"},{flags:"--yes",description:"Skip all interactive prompts (Agent mode)"}],fe={name:"nebutra",description:"Governance-first CLI for Nebutra Sailor scaffolding, registry-backed features, and platform operations",usage:"nebutra [command] [options]",options:[{flags:"--format <type>",description:"Output format: json, table, plain"},{flags:"--yes, --no-interactive",description:"Skip all interactive prompts (Agent mode)"},{flags:"--no-color",description:"Disable colored output"},{flags:"--verbose",description:"Enable verbose output"},{flags:"--quiet",description:"Suppress non-essential output"}],subcommands:[{name:"init",description:"Initialize a Nebutra project and create nebutra.config.json",usage:"nebutra init [options]",options:[{flags:"--dry-run",description:"Preview changes without writing files (exits with code 10)"},{flags:"--yes",description:"Skip all interactive prompts (Agent mode)"},{flags:"--if-not-exists",description:"Skip initialization if nebutra.config.json already exists"}],arguments:[],examples:[{command:"nebutra init",description:"Initialize Nebutra configuration in the current directory"},{command:"nebutra init --dry-run",description:"Preview initialization changes without writing files"},{command:"nebutra init --yes --if-not-exists",description:"Initialize without prompts, skip if already configured"}]},{name:"add",description:"Add a registry-backed platform feature or external UI component",usage:"nebutra add [components...] [options]",arguments:[{name:"components",description:"Local feature names to install from the Nebutra registry",required:!1,variadic:!0}],options:[{flags:"--21st <id>",description:"Fetch and install a component from 21st.dev registry"},{flags:"--v0 <url>",description:"Fetch and install a component from v0.dev by URL"},{flags:"--dry-run",description:"Preview what would be installed without making changes (exit code 10)"},{flags:"--yes",description:"Skip all interactive prompts and use defaults (Agent mode)"},{flags:"--if-not-exists",description:"Skip installation if component already exists"}],examples:[{command:"nebutra add cache --provider upstash-redis --yes",description:"Install the local cache feature with the Upstash Redis provider"},{command:"nebutra add --21st button-01",description:"Add a component from 21st.dev (shadcn-style registry)"},{command:'nebutra add --v0 "https://v0.dev/r/..." --dry-run',description:"Preview adding a component from v0.dev without making changes"},{command:"nebutra add webhooks --provider svix --yes --if-not-exists",description:"Install webhooks without prompts, skip if already configured"}]},{name:"create",description:"Scaffold a topology-first Nebutra Sailor project with governed defaults",usage:"nebutra create [dir] [options]",arguments:[{name:"dir",description:"Target directory for the new project (optional, will prompt if not provided)",required:!1}],options:Pr,examples:[{command:"nebutra create my-saas-app --region=hybrid --ai=openai,deepseek",description:"Create a topology-first project with hybrid defaults and selected AI providers"},{command:"nebutra create my-cn-app --region=cn --payment=wechat --storage=aliyun-oss --deploy=selfhost -y",description:"Create a non-interactive China-ready scaffold"},{command:"nebutra create my-app --storage=supabase-storage --dry-run --json",description:"Preview scaffolding with structured JSON output"}]},{name:"mcp",description:"Start the Nebutra MCP server for AI agents and editors",usage:"nebutra mcp [options]",arguments:[],options:[{flags:"--stdio",description:"Use stdio transport for communication (default: enabled)",default:!0},{flags:"--verbose",description:"Enable verbose logging for MCP server"}],examples:[{command:"nebutra mcp",description:"Start the MCP server for AI-powered project context integration"},{command:"nebutra mcp --verbose",description:"Start the MCP server with detailed logging output"}]},{name:"schema",description:"Show command schema and argument documentation (Agent-friendly JSON output)",usage:"nebutra schema [command] [options]",arguments:[{name:"command",description:"Command name to show schema for (e.g., init, add, create)",required:!1}],options:[{flags:"--all",description:"Show full schema for all commands as JSON",default:!1},{flags:"--list",description:"List all available command names",default:!1},{flags:"--exit-codes",description:"Show exit codes reference",default:!1}],examples:[{command:"nebutra schema --all",description:"Show complete JSON of all commands, args, options, value domains"},{command:"nebutra schema init",description:"Show schema for init command (arguments, options, defaults, examples)"},{command:"nebutra schema add",description:"Show schema for add command with enum values"},{command:"nebutra schema --list",description:"List just the command names (quick discovery)"},{command:"nebutra schema --exit-codes",description:"Show exit codes reference for all possible exit codes"}]},{name:"brand",description:"Manage governed brand tokens, palettes, and visual system outputs",usage:"nebutra brand [subcommand]",examples:[{command:"nebutra brand palette --primary=#0047FF",description:"Generate a token-aligned blue palette"}]},{name:"i18n",description:"Manage localization files and multilingual product copy",usage:"nebutra i18n [subcommand]"},{name:"infra",description:"Manage local infrastructure services for Nebutra development",usage:"nebutra infra [subcommand]",examples:[{command:"nebutra infra up --lite",description:"Start lightweight local infrastructure"}]},{name:"env",description:"Validate and manage environment variables across apps and packages",usage:"nebutra env [subcommand]"},{name:"license",description:"Manage Nebutra Sailor commercial license activation and status",usage:"nebutra license [subcommand]",arguments:[],options:[],subcommands:[{name:"activate",description:"Activate a commercial license key for local development",usage:"nebutra license activate <key>",arguments:[{name:"key",description:"Your Nebutra Sailor commercial license key",required:!0}],options:[{flags:"--quiet",description:"Suppress output"}],examples:[{command:"nebutra license activate liz_1234567890",description:"Activate your license key globally"}]},{name:"status",description:"Check the locally configured license status",usage:"nebutra license status",arguments:[],options:[{flags:"--quiet",description:"Suppress output"}],examples:[{command:"nebutra license status",description:"Check if you have an active license"}]}]},{name:"ai",description:"Configure AI providers, gateway routing, and agent-ready defaults",usage:"nebutra ai [subcommand]"},{name:"auth",description:"Configure authentication providers and tenant access defaults",usage:"nebutra auth [subcommand]"},{name:"billing",description:"Manage billing providers, subscriptions, usage, and return flows",usage:"nebutra billing [subcommand]"},{name:"stats",description:"Show monorepo statistics, health signals, and platform inventory",usage:"nebutra stats [options]"},{name:"db",description:"Manage database schema, migrations, seeds, and generated clients",usage:"nebutra db [subcommand]"},{name:"generate",description:"Generate apps, modules, API surfaces, and typed project artifacts",usage:"nebutra generate [type] [name]"},{name:"preset",description:"List and apply topology presets for governed SaaS projects",usage:"nebutra preset [subcommand]"},{name:"dev",description:"Start development workflows for selected Nebutra apps or presets",usage:"nebutra dev [options]"},{name:"test",description:"Run unit, architecture, and E2E verification workflows",usage:"nebutra test [scope]"},{name:"admin",description:"Operate tenant, platform health, and administrative workflows",usage:"nebutra admin [subcommand]"},{name:"community",description:"Inspect community health, showcases, and external adoption signals",usage:"nebutra community [subcommand]"},{name:"growth",description:"Analyze product growth, funnels, retention, and lifecycle signals",usage:"nebutra growth [subcommand]"},{name:"ecosystem",description:"Manage template marketplace, ideas, project showcase, and ecosystem sync",usage:"nebutra ecosystem [subcommand]"},{name:"services",description:"Inspect and manage microservice health, logs, scaling, and rollouts",usage:"nebutra services [subcommand]"},{name:"search",description:"Manage search indexes, reindexing, and search diagnostics",usage:"nebutra search [subcommand]"},{name:"secrets",description:"Manage encrypted tenant and platform secrets",usage:"nebutra secrets [subcommand]"},{name:"completions",description:"Generate shell completions for the current Nebutra command surface",usage:"nebutra completions [shell]",arguments:[{name:"shell",description:"Shell to generate completions for: bash, zsh, fish, or install",required:!1}]},{name:"doctor",description:"Check local project setup and common Nebutra configuration issues",usage:"nebutra doctor"}]};var Jt="nebutra.config.json",jr=b.ai.filter(e=>e!=="none"),Ht={"aws-bedrock":["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"],"azure-openai":["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"],"gcp-vertex":["GOOGLE_APPLICATION_CREDENTIALS","GOOGLE_CLOUD_PROJECT"],"vercel-gateway":["AI_GATEWAY_API_KEY"],"volcengine-ark":["VOLCENGINE_ARK_API_KEY"],bailian:["DASHSCOPE_API_KEY"],litellm:["LITELLM_API_KEY","LITELLM_BASE_URL"],portkey:["PORTKEY_API_KEY"],custom:["AI_CUSTOM_API_KEY","AI_CUSTOM_BASE_URL"]};function ne(e,t,n){if(t==="json"){let r={command:n||"ai",...e&&typeof e=="object"&&"exitCode"in e?{exitCode:e.exitCode}:{},...e};console.log(JSON.stringify(r,null,2))}else e&&typeof e=="object"&&"stdout"in e&&e.stdout&&console.log(e.stdout),e&&typeof e=="object"&&"stderr"in e&&e.stderr&&(e.exitCode??0)!==0&&console.error(e.stderr)}function Ur(e){return e.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function Dr(e){return Ht[e]?Ht[e]:[`${e.replace(/-/g,"_").toUpperCase()}_API_KEY`]}function Ye(){return Object.fromEntries(jr.map(e=>[e,{id:e,name:Ur(e),envVars:Dr(e)}]))}function oe(){return bt(process.cwd(),Jt)}function Re(){let e=oe();if(!Ir(e))return{$schema:"https://nebutra.com/schema.json"};try{return JSON.parse(Nr(e,"utf-8"))}catch(t){s.error(`Failed to parse ${Jt}: ${t instanceof Error?t.message:String(t)}`),process.exit(c.ERROR)}}function He(e){Tr(oe(),`${JSON.stringify(e,null,2)}
|
|
113
|
-
`)}function ge(e){return e.ai={providers:{},routes:{},...e.ai},e.ai.providers={...e.ai.providers},e.ai.routes={...e.ai.routes},e.ai}function Qt(e){let t=Ye(),n=[...new Set(e.map(o=>o.toLowerCase()))],r=n.filter(o=>!t[o]);return r.length>0&&(s.error(`Unknown AI provider(s): ${r.join(", ")}. Valid providers: ${Object.keys(t).join(", ")}`),process.exit(c.INVALID_ARGS)),n}function Lr(e){let t=e?.split(",").map(r=>r.trim()).filter(Boolean)??[];t.length===0&&(s.error("Route fallback cannot be empty. Use --fallback provider:model,provider:model"),process.exit(c.INVALID_ARGS));let n=t.filter(r=>!/^[a-z0-9-]+:[^,\s]+$/i.test(r));return n.length>0&&(s.error(`Invalid fallback target(s): ${n.join(", ")}. Expected provider:model.`),process.exit(c.INVALID_ARGS)),t}function Xt(e){let t=ge(e),n=t.providers??{},r=t.routes??{},o=Object.entries(n).filter(([,a])=>a.enabled).map(([a])=>a);return{path:oe(),gateway:t.gateway??null,providers:n,enabledProviders:o,routes:r}}function te(e){let t=typeof e.opts=="function"?e.opts():e,n=typeof e.optsWithGlobals=="function"?e.optsWithGlobals():t,r=t.format??n.format??Fr();return{...n,...t,...r?{format:r}:{}}}function Fr(){let e=process.argv.find(n=>n.startsWith("--format="));if(e)return e.slice(9);let t=process.argv.indexOf("--format");if(t>=0)return process.argv[t+1]}function Te(e){return e?e.length<=8?"***":`${e.substring(0,8)}***`:"not set"}async function Mr(e){e.format!=="json"&&s.info("Checking configured AI models and providers...");let t={openai:{name:"OpenAI",keyVars:["OPENAI_API_KEY"],configured:!1},anthropic:{name:"Anthropic Claude",keyVars:["ANTHROPIC_API_KEY"],configured:!1},openrouter:{name:"OpenRouter",keyVars:["OPENROUTER_API_KEY"],configured:!1},vercel:{name:"Vercel AI SDK",keyVars:["VERCEL_AI_API_KEY"],configured:!1}};for(let r of Object.values(t))r.configured=r.keyVars.some(o=>process.env[o]);let n={providers:Object.entries(t).map(([r,o])=>({id:r,name:o.name,configured:o.configured,keys:o.keyVars.map(a=>({name:a,value:Te(process.env[a])}))})),configuredCount:Object.values(t).filter(r=>r.configured).length,totalProviders:Object.keys(t).length};e.dryRun||(n.configuredCount===0?s.warn("No AI providers configured. Set API keys to enable AI features."):s.success(`${n.configuredCount} AI provider(s) configured`)),ne(n,e.format,"ai:models")}async function Gr(e){e.format!=="json"&&s.info("Scanning for available agents...");try{let t=bt(process.cwd(),"apps/api-gateway/src/routes/agents"),n=[];try{let o=await import("fs");o.existsSync(t)&&(n=o.readdirSync(t).filter(i=>i.endsWith(".ts")&&!i.endsWith(".d.ts")).map(i=>({name:i.replace(".ts",""),path:`apps/api-gateway/src/routes/agents/${i}`})))}catch{n=[]}let r={agents:n.length>0?n:[{name:"No agents found",path:"create agents in apps/api-gateway/src/routes/agents/"}],count:n.length};e.dryRun||(r.count===0?s.info("No agents configured. Create agents in apps/api-gateway/src/routes/agents/"):s.success(`Found ${r.count} agent(s)`)),ne(r,e.format,"ai:agents")}catch(t){s.error(`Failed to scan agents: ${t instanceof Error?t.message:String(t)}`),process.exit(c.ERROR)}}async function Kr(e,t){(!e||e.trim().length===0)&&(s.error("Prompt cannot be empty"),process.exit(c.INVALID_ARGS)),t.format!=="json"&&(s.info("Testing AI prompt against default model..."),s.info("Note: Ensure API gateway is running on http://localhost:3001"));let n={prompt:e,note:"To execute this test, the API gateway must be running locally. Run: pnpm --filter @nebutra/api-gateway dev",endpoint:"http://localhost:3001/api/ai/test",method:"POST",payload:{prompt:e.substring(0,100)}};t.dryRun||s.warn("AI test execution requires a running API gateway. Start it with: pnpm --filter @nebutra/api-gateway dev"),ne(n,t.format,"ai:test")}async function qr(e){e.format!=="json"&&s.info("Checking AI configuration...");let t={providers:{openai:{key:Te(process.env.OPENAI_API_KEY),configured:!!process.env.OPENAI_API_KEY},anthropic:{key:Te(process.env.ANTHROPIC_API_KEY),configured:!!process.env.ANTHROPIC_API_KEY},openrouter:{key:Te(process.env.OPENROUTER_API_KEY),configured:!!process.env.OPENROUTER_API_KEY}},environment:{NODE_ENV:process.env.NODE_ENV||"development",NEBUTRA_LOG_LEVEL:process.env.NEBUTRA_LOG_LEVEL||"info"}};try{let r=bt(process.cwd(),"nebutra.config.ts"),o=await import("fs");if(o.existsSync(r)){let i=o.readFileSync(r,"utf-8").match(/preset:\s*["']([^"']+)["']/);i&&(t.preset=i[1])}}catch{}let n=Object.entries(t.providers).filter(([,r])=>r.configured).map(([r])=>r);e.dryRun||(n.length===0?s.warn("No AI providers configured. Set environment variables to enable AI features."):s.success(`Configuration loaded: ${n.join(", ")}`)),ne(t,e.format,"ai:config")}async function Vr(e){let t=Re(),n=ge(t),r=Ye(),o=Object.values(r).map(i=>{let l=n.providers?.[i.id];return{id:i.id,name:i.name,enabled:l?.enabled===!0,configured:i.envVars.some(u=>!!process.env[u]),envVars:i.envVars.map(u=>({name:u,value:Te(process.env[u])}))}}),a={configPath:oe(),providers:o,enabledCount:o.filter(i=>i.enabled).length,totalProviders:o.length};if(e.format!=="json"){let i=o.filter(l=>l.enabled);i.length===0?s.warn("No runtime AI providers enabled in nebutra.config.json."):s.success(`Enabled AI providers: ${i.map(l=>l.id).join(", ")}`)}ne(a,e.format,"ai:provider:list")}async function Wr(e,t){let n=Qt(e),r=Re(),o=ge(r),a=Ye(),i=new Date().toISOString();for(let u of n)o.providers[u]={enabled:!0,envVars:a[u].envVars,enabledAt:o.providers?.[u]?.enabledAt??i};He(r);let l={configPath:oe(),enabled:n,env:t.env===!0?n.flatMap(u=>a[u].envVars.map(p=>({provider:u,name:p,example:`${p}=`}))):void 0,ai:Xt(r)};if(t.format!=="json"&&(s.success(`Enabled AI provider(s): ${n.join(", ")}`),t.env)){s.info("Required environment variables:");for(let u of n)for(let p of a[u].envVars)console.log(`${p}=`)}ne(l,t.format,"ai:provider:enable")}async function zr(e,t){let n=Qt(e),r=Re(),o=ge(r),a=Ye(),i=new Date().toISOString();for(let u of n)o.providers[u]={enabled:!1,envVars:o.providers?.[u]?.envVars??a[u].envVars,disabledAt:i};He(r);let l={configPath:oe(),disabled:n,ai:Xt(r)};t.format!=="json"&&s.success(`Disabled AI provider(s): ${n.join(", ")}`),ne(l,t.format,"ai:provider:disable")}async function Br(e){let t=Re(),n=ge(t),r=new Date().toISOString();n.gateway={enabled:!0,mode:"local",initializedAt:n.gateway?.initializedAt??r,updatedAt:r},He(t);let o={configPath:oe(),gateway:n.gateway};e.format!=="json"&&s.success("AI gateway governance initialized in nebutra.config.json"),ne(o,e.format,"ai:gateway:init")}async function Yr(e){let t=Re(),n=ge(t),r={configPath:oe(),gateway:n.gateway??null,initialized:n.gateway?.enabled===!0};e.format!=="json"&&(r.initialized?s.success("AI gateway governance is initialized"):s.warn("AI gateway governance is not initialized. Run: nebutra ai gateway init")),ne(r,e.format,"ai:gateway:status")}async function Hr(e,t){(!e||e.trim().length===0)&&(s.error("Route name cannot be empty"),process.exit(c.INVALID_ARGS)),t.policy||(s.error("Route policy is required. Use --policy <policy>."),process.exit(c.INVALID_ARGS));let n=Lr(t.fallback),r=Re(),o=ge(r);o.routes[e]={policy:t.policy,fallback:n,updatedAt:new Date().toISOString()},He(r);let a={configPath:oe(),route:{name:e,...o.routes[e]},routes:o.routes};t.format!=="json"&&s.success(`AI route '${e}' set with policy '${t.policy}'`),ne(a,t.format,"ai:route:set")}async function Jr(e){let t=Re(),n=ge(t),r=Object.entries(n.routes??{}).map(([a,i])=>({name:a,...i})),o={configPath:oe(),routes:r,count:r.length};e.format!=="json"&&(r.length===0?s.warn("No AI routes configured. Run: nebutra ai route set chat --policy cost-latency-balanced --fallback openai:gpt-5.4"):s.success(`Configured AI routes: ${r.map(a=>a.name).join(", ")}`)),ne(o,e.format,"ai:route:list")}function Zt(e){let t=e.command("ai").description("Manage AI models, agents, and configuration"),n=t.command("provider").description("Manage runtime AI provider governance");n.command("list").description("List supported and enabled AI providers").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await Vr({format:i.format,interactive:i.yes!==!0})}),n.command("enable <providers...>").description("Enable one or more runtime AI providers in nebutra.config.json").option("--env","Print required environment variable placeholders").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let l=te(i);await Wr(a,{env:l.env===!0,format:l.format,interactive:l.yes!==!0})}),n.command("disable <providers...>").description("Disable one or more runtime AI providers in nebutra.config.json").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let l=te(i);await zr(a,{format:l.format,interactive:l.yes!==!0})});let r=t.command("gateway").description("Manage local AI gateway governance");r.command("init").description("Initialize the local AI gateway governance block").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await Br({format:i.format,interactive:i.yes!==!0})}),r.command("status").description("Show local AI gateway governance status").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await Yr({format:i.format,interactive:i.yes!==!0})});let o=t.command("route").description("Manage AI routing policies");o.command("set <name>").description("Create or update an AI route policy").requiredOption("--policy <policy>","Routing policy id").requiredOption("--fallback <list>","Comma-separated provider:model fallback chain").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let l=te(i);await Hr(a,{policy:l.policy,fallback:l.fallback,format:l.format,interactive:l.yes!==!0})}),o.command("list").description("List configured AI routes").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await Jr({format:i.format,interactive:i.yes!==!0})}),t.command("models").description("List configured AI models and providers").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await Mr({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})}),t.command("agents").description("List available agents in the project").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await Gr({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})}),t.command("test <prompt>").description("Send a test prompt to the default AI model (requires running API gateway)").option("--dry-run","Preview without executing").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let l=te(i);await Kr(a,{dryRun:l.dryRun===!0,format:l.format,interactive:l.yes!==!0})}),t.command("config").description("Show current AI configuration and provider status").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await qr({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})})}import{resolve as Qr}from"path";function vt(e,t,n){if(t==="json"){let r={command:n||"auth",...e&&typeof e=="object"&&"exitCode"in e?{exitCode:e.exitCode}:{},...e};console.log(JSON.stringify(r,null,2))}else e&&typeof e=="object"&&"stdout"in e&&e.stdout&&console.log(e.stdout),e&&typeof e=="object"&&"stderr"in e&&e.stderr&&(e.exitCode??0)!==0&&console.error(e.stderr)}function se(e,t){return e?e.length<=8?"***":`${e.substring(0,8)}*** (${t||e.length} chars)`:"not set"}function Xr(){try{let e=Qr(process.cwd(),"nebutra.config.ts"),t=sr("fs");if(t.existsSync(e)){let n=t.readFileSync(e,"utf-8");if(n.includes("clerk"))return"clerk";if(n.includes("better-auth"))return"better-auth"}}catch{}return"clerk"}async function Zr(e){s.info("Checking authentication configuration...");let t=Xr(),n={provider:t,configured:!1,keys:{}};t==="clerk"?(n.keys={CLERK_SECRET_KEY:se(process.env.CLERK_SECRET_KEY,"secret"),NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:se(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,"publishable"),CLERK_WEBHOOK_SECRET:se(process.env.CLERK_WEBHOOK_SECRET,"webhook")},n.configured=!!process.env.CLERK_SECRET_KEY&&!!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY):(n.keys={BETTER_AUTH_SECRET:se(process.env.BETTER_AUTH_SECRET,"secret"),BETTER_AUTH_URL:process.env.BETTER_AUTH_URL||"not set"},n.configured=!!process.env.BETTER_AUTH_SECRET),e.dryRun||(n.configured?s.success(`Auth provider configured: ${t}`):s.warn(`Auth provider ${t} is not fully configured`)),vt(n,e.format,"auth:status")}async function eo(e,t){let n=["clerk","better-auth"];n.includes(e.toLowerCase())||(s.error(`Invalid provider: ${e}. Choose from: ${n.join(", ")}`),process.exit(c.INVALID_ARGS));let r=e.toLowerCase();s.info(`Setting up ${r} authentication...`),r==="clerk"?(s.info("Clerk setup requires:"),s.info("1. A Clerk account (https://clerk.com)"),s.info("2. A Clerk application created"),s.info("3. API keys from the Clerk dashboard"),s.info(""),s.info("To complete setup:"),s.info("1. Go to https://dashboard.clerk.com"),s.info("2. Copy your Secret Key and Publishable Key"),s.info("3. Add them to .env.local:"),s.info(' CLERK_SECRET_KEY="sk_..."'),s.info(' NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_..."'),s.info(""),s.info("Webhook setup (optional):"),s.info("1. In Clerk dashboard, go to Webhooks"),s.info("2. Create endpoint: https://yourdomain.com/api/webhooks/clerk"),s.info("3. Copy the signing secret to .env.local:"),s.info(' CLERK_WEBHOOK_SECRET="whsec_..."')):(s.info("Better Auth setup requires:"),s.info("1. Database URL configured (DATABASE_URL env var)"),s.info("2. A generated auth secret"),s.info(""),s.info("To complete setup:"),s.info("1. Ensure DATABASE_URL is set in .env.local"),s.info("2. Generate a new secret:"),s.info(" openssl rand -base64 32"),s.info("3. Add to .env.local:"),s.info(' BETTER_AUTH_SECRET="<generated_secret>"'),s.info(' BETTER_AUTH_URL="http://localhost:3000"'),s.info(""),s.info("4. Run database migration:"),s.info(" pnpm --filter @nebutra/web db:push")),t.dryRun||s.success(`Setup instructions for ${r} displayed above`),vt({provider:r,setupGuide:"Complete setup by following the instructions above",documentationUrl:r==="clerk"?"https://clerk.com/docs":"https://www.better-auth.com"},t.format,"auth:setup")}async function to(e){s.info("Checking authentication keys and secrets...");let n={keys:{clerk:{CLERK_SECRET_KEY:se(process.env.CLERK_SECRET_KEY,"secret key"),NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:se(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,"publishable key"),CLERK_WEBHOOK_SECRET:se(process.env.CLERK_WEBHOOK_SECRET,"webhook secret")},betterAuth:{BETTER_AUTH_SECRET:se(process.env.BETTER_AUTH_SECRET,"secret"),BETTER_AUTH_URL:process.env.BETTER_AUTH_URL||"not set"},common:{DATABASE_URL:se(process.env.DATABASE_URL,"database url"),JWT_SECRET:se(process.env.JWT_SECRET,"jwt secret")}},configured:{clerk:{secret:!!process.env.CLERK_SECRET_KEY,publishable:!!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY},betterAuth:{secret:!!process.env.BETTER_AUTH_SECRET,url:!!process.env.BETTER_AUTH_URL}}};if(!e.dryRun){let r=n.configured.clerk.secret&&n.configured.clerk.publishable,o=n.configured.betterAuth.secret&&n.configured.betterAuth.url;!r&&!o?s.warn("No authentication keys configured"):(s.info("Auth keys status:"),r&&s.success("Clerk configured"),o&&s.success("Better Auth configured"))}vt(n,e.format,"auth:keys")}function en(e){let t=e.command("auth").description("Manage authentication configuration (Clerk or Better Auth)");t.command("status").description("Check current authentication provider configuration").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Zr({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("setup <provider>").description("Configure Clerk or Better Auth (interactive wizard)").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async(n,r)=>{let o=r.optsWithGlobals?.()||r;await eo(n,{dryRun:r.dryRun||!1,format:r.format||o.format,interactive:o.yes!==!0})}),t.command("keys").description("Show authentication-related environment variables (masked)").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await to({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}function wt(e,t,n){if(t==="json"){let r={command:n||"billing",...e&&typeof e=="object"&&"exitCode"in e?{exitCode:e.exitCode}:{},...e};console.log(JSON.stringify(r,null,2))}else e&&typeof e=="object"&&"stdout"in e&&e.stdout&&console.log(e.stdout),e&&typeof e=="object"&&"stderr"in e&&e.stderr&&(e.exitCode??0)!==0&&console.error(e.stderr)}function no(e,t){return e?e.length<=8?"***":`${e.substring(0,8)}*** (${t||e.length} chars)`:"not set"}var Je={stripe:{name:"Stripe",keyVars:["STRIPE_SECRET_KEY","STRIPE_PUBLISHABLE_KEY"],webhookUrl:"/api/webhooks/stripe",setupUrl:"https://dashboard.stripe.com"},lemonsqueezy:{name:"Lemon Squeezy",keyVars:["LEMONSQUEEZY_API_KEY"],webhookUrl:"/api/webhooks/lemonsqueezy",setupUrl:"https://app.lemonsqueezy.com/settings/api"},polar:{name:"Polar",keyVars:["POLAR_ACCESS_TOKEN"],webhookUrl:"/api/webhooks/polar",setupUrl:"https://app.polar.sh/settings/api"},chinapay:{name:"ChinaPay",keyVars:["CHINAPAY_MERCHANT_ID","CHINAPAY_SECRET_KEY"],webhookUrl:"/api/webhooks/chinapay",setupUrl:"https://www.chinapay.com"}};async function ro(e){s.info("Checking payment provider configuration...");let t=Object.entries(Je).map(([o,a])=>{let i=a.keyVars.every(l=>process.env[l]);return{id:o,name:a.name,configured:i,keys:a.keyVars.map(l=>({name:l,value:no(process.env[l],"key")}))}}),n=t.filter(o=>o.configured),r={providers:t,configuredCount:n.length,totalProviders:t.length,configuredList:n.map(o=>o.name)};e.dryRun||(r.configuredCount===0?s.warn("No payment providers configured. Set API keys to enable billing."):s.success(`${r.configuredCount} payment provider(s) configured`)),wt(r,e.format,"billing:status")}async function oo(e,t){let n=Object.keys(Je);n.includes(e.toLowerCase())||(s.error(`Invalid provider: ${e}. Choose from: ${n.join(", ")}`),process.exit(c.INVALID_ARGS));let r=e.toLowerCase(),o=Je[r];s.info(`Setting up ${o.name} payment provider...`),s.info(""),s.info("Setup steps:"),s.info(`1. Go to ${o.setupUrl}`),s.info("2. Create or log in to your account"),s.info("3. Generate API credentials"),s.info("4. Add the following to .env.local:"),s.info(""),o.keyVars.forEach(i=>{s.info(` ${i}="<your_${i.toLowerCase()}>"`.padStart(45))}),s.info(""),s.info("5. (Optional) Configure webhook in provider dashboard:"),s.info(` Webhook URL: https://yourdomain.com${o.webhookUrl}`),t.dryRun||s.success(`Setup instructions for ${o.name} displayed above`);let a={provider:r,name:o.name,requiredKeys:o.keyVars,webhookUrl:o.webhookUrl,setupGuide:"Follow the instructions above to complete setup",documentationUrl:o.setupUrl};wt(a,t.format,"billing:setup")}async function so(e){s.info("Checking billing webhook configuration...");let t=process.env.NEXT_PUBLIC_APP_URL||"https://yourdomain.com",n=Object.entries(Je).map(([o,a])=>({provider:o,name:a.name,url:`${t}${a.webhookUrl}`,endpoint:a.webhookUrl,configured:a.keyVars.every(i=>process.env[i])})),r={baseUrl:t,webhooks:n,configurationSteps:["1. Set NEXT_PUBLIC_APP_URL in .env.local to your domain","2. Copy the webhook URL for each provider","3. Configure in the provider's dashboard","4. Ensure the API key is also set"]};e.dryRun||(s.info("Webhook endpoints:"),n.forEach(o=>{o.configured?s.success(`${o.name}: configured`):s.warn(`${o.name}: not configured (missing API key)`)})),wt(r,e.format,"billing:webhooks")}function tn(e){let t=e.command("billing").description("Manage payment providers and billing configuration");t.command("status").description("Check which payment providers are configured").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await ro({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("setup <provider>").description("Configure payment provider (Stripe, Lemon Squeezy, Polar, or ChinaPay)").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async(n,r)=>{let o=r.optsWithGlobals?.()||r;await oo(n,{dryRun:r.dryRun||!1,format:r.format||o.format,interactive:o.yes!==!0})}),t.command("webhooks").description("Show webhook endpoints for each payment provider").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await so({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}function je(e,t,n){if(t==="json"){let r={command:n||"brand",exitCode:e.exitCode,success:e.exitCode===0,...e.stdout&&{stdout:e.stdout},...e.stderr&&{stderr:e.stderr}};console.log(JSON.stringify(r,null,2))}else e.stdout&&console.log(e.stdout),e.stderr&&e.exitCode!==0&&console.error(e.stderr)}function nn(e){return/^#[0-9A-Fa-f]{6}$/.test(e)}async function ao(e){s.info("Initializing brand system...");let t=await _("brand:init",{dryRun:e.dryRun,interactive:e.interactive??!0});je(t,e.format,"brand:init"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand system initialized successfully")}async function io(e){s.info("Applying brand configuration...");let t=await _("brand:apply",{dryRun:e.dryRun,interactive:e.interactive??!0});je(t,e.format,"brand:apply"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand applied successfully")}async function co(e){let t=[];e.primary&&(nn(e.primary)||(s.error(`Invalid primary color format: ${e.primary}. Expected hex format like #0033FE`),process.exit(c.INVALID_ARGS)),t.push(`--primary=${e.primary}`)),e.secondary&&(nn(e.secondary)||(s.error(`Invalid secondary color format: ${e.secondary}. Expected hex format like #0BF1C3`),process.exit(c.INVALID_ARGS)),t.push(`--secondary=${e.secondary}`)),t.length===0&&(s.warn("No colors specified. Usage: nebutra brand palette --primary=#HEX --secondary=#HEX"),s.info("Example: nebutra brand palette --primary=#0033FE --secondary=#0BF1C3"),process.exit(c.INVALID_ARGS)),s.info(`Generating color palette: ${t.join(" ")}`);let n=await A({command:"node",args:["scripts/generate-palette.mjs",...t],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Generate brand palette"});je(n,e.format,"brand:palette"),n.exitCode!==0&&process.exit(n.exitCode),e.dryRun||s.success("Color palette generated successfully")}async function lo(e){s.info("Syncing brand tokens across packages...");let t=await _("brand:sync",{dryRun:e.dryRun,interactive:e.interactive??!0});je(t,e.format,"brand:sync"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand tokens synced successfully")}async function uo(e){s.info("Verifying brand token consistency...");let t=await _("brand:verify-tokens",{dryRun:e.dryRun,interactive:e.interactive??!0});je(t,e.format,"brand:verify-tokens"),t.exitCode!==0&&(s.error("Brand token verification failed. There are inconsistencies in your design tokens."),process.exit(t.exitCode)),e.dryRun||s.success("All brand tokens are consistent")}function rn(e){let t=e.command("brand").description("Manage Nebutra brand system and design tokens");t.command("init").description("Initialize brand system configuration").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await ao({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("apply").description("Apply brand configuration to the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await io({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("palette").description("Generate brand color palette from custom colors").option("--primary <hex>","Primary brand color (hex format, e.g. #0033FE)").option("--secondary <hex>","Secondary brand color (hex format, e.g. #0BF1C3)").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await co({primary:n.primary,secondary:n.secondary,dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("sync").description("Synchronize brand tokens across all packages").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await lo({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("verify").description("Verify brand token consistency across the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await uo({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}import de from"picocolors";var mo=process.env.NEBUTRA_API_URL||"http://localhost:3001/api";async function T(e,t={}){let n=`${mo}${e}`,r=await fetch(n,{...t,headers:{"Content-Type":"application/json",Authorization:`Bearer ${process.env.NEBUTRA_ADMIN_TOKEN||""}`,...t.headers}});if(!r.ok){let o=await r.text().catch(()=>r.statusText);throw new Error(`API Error (${r.status}): ${o}`)}return r.json()}function po(e){let t={dau_retention:.25,engagement_rate:.25,content_velocity:.2,response_time:.15,member_retention:.15},n={},r=0;for(let[o,a]of Object.entries(t)){let i=Math.min(100,(e[o]||0)*100);n[o]=i,r+=i*a}return{score:Math.round(r),breakdown:n}}async function fo(e){try{s.info("Fetching showcase submissions...");let t=new URLSearchParams;e.status&&t.append("status",e.status),e.category&&t.append("category",e.category);let n=await T(`/community/showcase?${t.toString()}`);if(!n.submissions||n.submissions.length===0)return s.warn("No showcase submissions found"),c.SUCCESS;s.info(`
|
|
105
|
+
`;await Se.writeFile(n.path,l,"utf8")}if(t){let o=await Se.readFile(t.path,"utf8"),a=JSON.parse(o);for(let i of t.changes){if(i.action==="keep")continue;let l=i.section==="dependencies"?"dependencies":"devDependencies";a[l]={...a[l]??{},[i.name]:i.to},a[l]=qr(a[l])}await Se.writeFile(t.path,`${JSON.stringify(a,null,2)}
|
|
106
|
+
`,"utf8")}}async function zr(e,t,n){let r=[];for(let o of e){let a=await Xt(o);a||(s.error(`Feature '${o}' not found in the local registry.`),s.info("Use one of the supported local features or choose --21st / --v0 for external UI installs."),process.exit(c.INVALID_ARGS));let i=rn(a,t.provider);if(t.provider&&!i&&(s.error(`Provider '${t.provider}' is not supported for feature '${o}'.`),process.exit(c.INVALID_ARGS)),!i&&a.providers&&a.providers.length>0)if(n)i=a.providers[0];else{let l=await S.select({message:`Select a provider for ${o}`,options:a.providers.map(u=>({value:u.id,label:u.id,hint:u.description}))});S.isCancel(l)&&(S.log.info("Installation cancelled."),process.exit(c.CANCELLED)),i=rn(a,String(l))}r.push({name:o,descriptor:a,provider:i})}return r}function Yr(e){S.log.info(je.yellow("Planned local install:"));for(let t of e.features)S.log.message(` - ${t.name}${t.provider?` (${t.provider})`:""}: ${t.description}`);for(let t of e.operations)if(t.type==="write-file"&&S.log.message(` - ${t.status.toUpperCase()} ${t.relativePath}`),t.type==="merge-env"&&t.add.length>0&&S.log.message(` - APPEND ${t.add.map(n=>n.key).join(", ")} to ${t.relativePath}`),t.type==="update-package-json"){let n=t.changes.filter(r=>r.action==="add"||r.action==="update");n.length>0&&S.log.message(` - UPDATE ${t.relativePath}: ${n.map(r=>`${r.name}@${r.to}`).join(", ")}`)}}async function sn(e,t){t.dryRun||S.intro(je.bgCyan(je.black(" nebutra add ")));let n=t.yes||!process.stdin.isTTY;if(e.length===0&&!t["21st"]&&!t.v0&&(t.dryRun?s.warn("No components specified."):(S.log.warn("No components specified."),S.outro("Operation aborted.")),process.exit(c.INVALID_ARGS)),t.dryRun&&(t["21st"]||t.v0)&&(en(Fr(e,t)),process.exit(c.DRY_RUN_OK)),t.ifNotExists&&(t["21st"]||t.v0)&&(t["21st"]&&Zt(t["21st"])||t.v0&&Zt("shadcn-component"))&&(s.info("Component already exists. Skipping installation."),process.exit(c.SUCCESS)),t["21st"]){let r=n||await S.confirm({message:`Install 21st.dev component: ${t["21st"]}?`,initialValue:!0});(S.isCancel(r)||!r)&&(S.log.info("Installation cancelled."),process.exit(c.CANCELLED)),S.log.info(je.cyan(`Invoking shadcn integration for 21st.dev component: ${t["21st"]}...`));try{let o=t["21st"],a=o.startsWith("http")?o:`https://21st.dev/r/${o}`;St(`pnpm dlx shadcn@latest add "${a}"`,{stdio:"inherit"}),s.success(`Successfully installed ${o}`),process.exit(c.SUCCESS)}catch(o){if(Rt(o))throw o;s.error(`Failed to install component: ${o instanceof Error?o.message:"Unknown error"}`),process.exit(c.ERROR)}}if(t.v0){let r=n||await S.confirm({message:`Install v0 component from: ${t.v0}?`,initialValue:!0});(S.isCancel(r)||!r)&&(S.log.info("Installation cancelled."),process.exit(c.CANCELLED)),S.log.info(je.cyan(`Invoking shadcn integration for v0 URL: ${t.v0}...`));try{St(`pnpm dlx shadcn@latest add "${t.v0}"`,{stdio:"inherit"}),s.success("Successfully installed v0 component"),process.exit(c.SUCCESS)}catch(o){if(Rt(o))throw o;s.error(`Failed to install component: ${o instanceof Error?o.message:"Unknown error"}`),process.exit(c.ERROR)}}try{let r=await zr(e,t,n),o=await Vr(r,process.cwd(),t.dryRun?"dry-run":"apply");if(t.dryRun&&(en(o),process.exit(c.DRY_RUN_OK)),Wr(o)){let i=o.operations.filter(l=>l.type==="write-file"&&l.status==="conflict").map(l=>l.relativePath);t.ifNotExists&&!on(o)&&(s.info("Feature already appears to be installed. Skipping because --if-not-exists was set."),process.exit(c.SUCCESS)),s.error(`Refusing to overwrite existing files: ${i.join(", ")}`),process.exit(c.CONFLICT)}on(o)||(s.info("Feature install already satisfied. No files, env keys, or dependencies needed changes."),process.exit(c.SUCCESS)),Yr(o);let a=n||await S.confirm({message:`Proceed with installing ${o.features.map(i=>i.name).join(", ")}?`,initialValue:!0});(S.isCancel(a)||!a)&&(S.log.info("Installation cancelled."),process.exit(c.CANCELLED)),await Br(o),s.success(`Installed ${o.features.map(i=>i.name).join(", ")} into ${o.cwd}`),process.exit(c.SUCCESS)}catch(r){if(Rt(r))throw r;s.error(r instanceof Error?r.message:String(r)),process.exit(c.CONFIG_ERROR)}}import N from"picocolors";async function W(e,t={}){let n=process.env.NEBUTRA_API_URL||"http://localhost:3100",r=process.env.NEBUTRA_ADMIN_KEY;if(!r)return{ok:!1,status:401,error:"NEBUTRA_ADMIN_KEY environment variable not set. Set it to your admin API key."};let o=new URL(e,n).toString(),a=t.method||"GET",i=t.body?JSON.stringify(t.body):void 0;if(t.dryRun){let l={mode:"dry-run",timestamp:new Date().toISOString(),method:a,url:o,...i&&{body:JSON.parse(i)},headers:{"X-Admin-Key":"***","Content-Type":"application/json"}};return console.log(JSON.stringify(l,null,2)),{ok:!0,status:0,data:l}}try{let l=await fetch(o,{method:a,headers:{"X-Admin-Key":r,"Content-Type":"application/json"},...i&&{body:i}}),u=await l.json().catch(()=>null);return l.ok?{ok:!0,status:l.status,data:u}:{ok:!1,status:l.status,error:u?.error||`HTTP ${l.status}`}}catch(l){return{ok:!1,status:0,error:`Network error: ${l instanceof Error?l.message:String(l)}`}}}function C(e,t,n){if(t==="json"){let r={command:n||"admin",...e};console.log(JSON.stringify(r,null,2))}else console.log(JSON.stringify(e,null,2))}async function Hr(e){s.info("Fetching tenants...");let t=new URLSearchParams;e.limit&&t.append("limit",String(e.limit)),e.offset&&t.append("offset",String(e.offset)),e.status&&t.append("status",e.status);let n=`/api/v1/admin/tenants${t.toString()?`?${t.toString()}`:""}`,r=await W(n,{dryRun:e.dryRun});if(r.ok||(s.error(`Failed to fetch tenants: ${r.error}`),process.exit(c.NETWORK_ERROR)),e.dryRun){C(r.data,e.format,"admin:tenants");return}let o={tenants:r.data?.tenants||[],total:r.data?.total||0,limit:r.data?.limit,offset:r.data?.offset};e.format==="json"?C(o,e.format,"admin:tenants"):(s.info(`Found ${o.total} tenant(s)`),o.tenants.length>0&&(console.log(`
|
|
107
|
+
Tenants:`),o.tenants.forEach(a=>{console.log(` ${N.blue(a.id)} \u2014 ${a.name||"(unnamed)"}`),console.log(` Status: ${a.status}`),a.plan&&console.log(` Plan: ${a.plan}`)})))}async function Jr(e,t){s.info(`Fetching tenant ${e}...`);let n=await W(`/api/v1/admin/tenants/${e}`,{dryRun:t.dryRun});if(n.ok||(s.error(`Failed to fetch tenant: ${n.error}`),process.exit(c.NETWORK_ERROR)),t.dryRun){C(n.data,t.format,"admin:tenant");return}if(t.format==="json")C({tenant:n.data},t.format,"admin:tenant");else{let r=n.data;s.success(`Tenant ${N.blue(r.id)}`),console.log(` Name: ${r.name}`),console.log(` Status: ${r.status}`),console.log(` Plan: ${r.plan}`),console.log(` Created: ${r.createdAt}`),r.suspendedAt&&console.log(` Suspended: ${r.suspendedAt}`)}}async function Qr(e,t){t.yes||(s.warn(`Suspending tenant ${e} will disable all operations`),s.info("Use --yes to confirm"),process.exit(c.CANCELLED)),s.info(`Suspending tenant ${e}...`);let n=await W(`/api/v1/admin/tenants/${e}/suspend`,{method:"POST",dryRun:t.dryRun});if(n.ok||(s.error(`Failed to suspend tenant: ${n.error}`),process.exit(c.NETWORK_ERROR)),t.dryRun){C(n.data,t.format,"admin:suspend");return}s.success(`Tenant ${N.blue(e)} suspended`),t.format==="json"&&C({suspended:!0,tenantId:e},t.format,"admin:suspend")}async function Xr(e,t){s.info(`Unsuspending tenant ${e}...`);let n=await W(`/api/v1/admin/tenants/${e}/unsuspend`,{method:"POST",dryRun:t.dryRun});if(n.ok||(s.error(`Failed to unsuspend tenant: ${n.error}`),process.exit(c.NETWORK_ERROR)),t.dryRun){C(n.data,t.format,"admin:unsuspend");return}s.success(`Tenant ${N.blue(e)} unsuspended`),t.format==="json"&&C({unsuspended:!0,tenantId:e},t.format,"admin:unsuspend")}async function Zr(e){s.info("Fetching usage report...");let t=new URLSearchParams;e.period&&t.append("period",e.period),e.top&&t.append("top",String(e.top));let n=`/api/v1/admin/usage/report${t.toString()?`?${t.toString()}`:""}`,r=await W(n,{dryRun:e.dryRun});if(r.ok||(s.error(`Failed to fetch usage report: ${r.error}`),process.exit(c.NETWORK_ERROR)),e.dryRun){C(r.data,e.format,"admin:usage");return}let o={period:r.data?.period,topTenants:r.data?.topTenants||[],totalUsage:r.data?.totalUsage};e.format==="json"?C(o,e.format,"admin:usage"):(s.info(`Usage report for period: ${o.period}`),console.log(`Total usage: ${o.totalUsage}`),o.topTenants.length>0&&(console.log(`
|
|
108
|
+
Top tenants:`),o.topTenants.forEach((a,i)=>{console.log(` ${i+1}. ${a.tenantName||a.tenantId} \u2014 ${a.usage}`)})))}async function eo(e){s.info("Fetching dead letter queue...");let t=await W("/api/v1/admin/dlq/list",{dryRun:e.dryRun});if(t.ok||(s.error(`Failed to fetch DLQ: ${t.error}`),process.exit(c.NETWORK_ERROR)),e.dryRun){C(t.data,e.format,"admin:dlq:list");return}let n={messages:t.data?.messages||[],count:t.data?.count||0};e.format==="json"?C(n,e.format,"admin:dlq:list"):(s.info(`Found ${n.count} message(s) in DLQ`),n.messages.length>0&&(console.log(`
|
|
109
|
+
Messages:`),n.messages.forEach(r=>{console.log(` ${N.blue(r.id)}`),console.log(` Type: ${r.type}`),console.log(` Error: ${r.error}`),console.log(` Created: ${r.createdAt}`)})))}async function to(e,t){s.info(`Replaying DLQ message ${e}...`);let n=await W(`/api/v1/admin/dlq/replay/${e}`,{method:"POST",dryRun:t.dryRun});if(n.ok||(s.error(`Failed to replay message: ${n.error}`),process.exit(c.NETWORK_ERROR)),t.dryRun){C(n.data,t.format,"admin:dlq:replay");return}s.success(`Message ${N.blue(e)} replayed`),t.format==="json"&&C({replayed:!0,messageId:e},t.format,"admin:dlq:replay")}async function no(e){e.yes||(s.warn("This will permanently delete all messages in the DLQ"),s.info("Use --yes to confirm"),process.exit(c.CANCELLED)),s.info("Purging DLQ...");let t=await W("/api/v1/admin/dlq/purge",{method:"POST",dryRun:e.dryRun});if(t.ok||(s.error(`Failed to purge DLQ: ${t.error}`),process.exit(c.NETWORK_ERROR)),e.dryRun){C(t.data,e.format,"admin:dlq:purge");return}s.success("DLQ purged"),e.format==="json"&&C({purged:!0,count:t.data?.count},e.format,"admin:dlq:purge")}async function ro(e){s.info("Fetching feature flags...");let t=await W("/api/v1/admin/feature-flags",{dryRun:e.dryRun});if(t.ok||(s.error(`Failed to fetch flags: ${t.error}`),process.exit(c.NETWORK_ERROR)),e.dryRun){C(t.data,e.format,"admin:flags:list");return}let n={flags:t.data?.flags||[],total:t.data?.total||0};e.format==="json"?C(n,e.format,"admin:flags:list"):(s.info(`Found ${n.total} flag(s)`),n.flags.length>0&&(console.log(`
|
|
110
|
+
Flags:`),n.flags.forEach(r=>{let o=r.override!==void 0?N.yellow(String(r.override)):N.gray(String(r.default));console.log(` ${N.blue(r.name)} = ${o}`)})))}async function oo(e,t,n){s.info(`Setting flag ${e}...`);let r=t;t==="true"?r=!0:t==="false"?r=!1:Number.isNaN(Number(t))||(r=Number(t));let o=await W(`/api/v1/admin/feature-flags/${e}`,{method:"POST",body:{value:r},dryRun:n.dryRun});if(o.ok||(s.error(`Failed to set flag: ${o.error}`),process.exit(c.NETWORK_ERROR)),n.dryRun){C(o.data,n.format,"admin:flags:set");return}s.success(`Flag ${N.blue(e)} set to ${r}`),n.format==="json"&&C({flag:e,value:r},n.format,"admin:flags:set")}async function so(e,t){s.info(`Unsetting flag ${e}...`);let n=await W(`/api/v1/admin/feature-flags/${e}`,{method:"DELETE",dryRun:t.dryRun});if(n.ok||(s.error(`Failed to unset flag: ${n.error}`),process.exit(c.NETWORK_ERROR)),t.dryRun){C(n.data,t.format,"admin:flags:unset");return}s.success(`Flag ${N.blue(e)} unset`),t.format==="json"&&C({flag:e,unset:!0},t.format,"admin:flags:unset")}async function ao(e){s.info("Fetching audit log...");let t=new URLSearchParams;e.actor&&t.append("actor",e.actor),e.action&&t.append("action",e.action),e.since&&t.append("since",e.since),e.limit&&t.append("limit",String(e.limit));let n=`/api/v1/admin/audit${t.toString()?`?${t.toString()}`:""}`,r=await W(n,{dryRun:e.dryRun});if(r.ok||(s.error(`Failed to fetch audit log: ${r.error}`),process.exit(c.NETWORK_ERROR)),e.dryRun){C(r.data,e.format,"admin:audit");return}let o={entries:r.data?.entries||[],total:r.data?.total||0};e.format==="json"?C(o,e.format,"admin:audit"):(s.info(`Found ${o.total} audit log entries`),o.entries.length>0&&(console.log(`
|
|
111
|
+
Audit entries:`),o.entries.forEach(a=>{console.log(` ${a.timestamp} \u2014 ${N.blue(a.action)} by ${a.actor}`),a.details&&console.log(` ${a.details}`)})))}async function io(e){s.info("Running platform health check...");let t=await W("/api/v1/system/status",{dryRun:e.dryRun});if(t.ok||(s.error(`Failed to fetch health status: ${t.error}`),process.exit(c.NETWORK_ERROR)),e.dryRun){C(t.data,e.format,"admin:health");return}let n={status:t.data?.status||"unknown",services:t.data?.services||{},timestamp:t.data?.timestamp};if(e.format==="json")C(n,e.format,"admin:health");else{let r=n.status==="healthy"?N.green:n.status==="degraded"?N.yellow:N.red;s.info(`Platform status: ${r(n.status)}`),n.services&&Object.keys(n.services).length>0&&(console.log(`
|
|
112
|
+
Services:`),Object.entries(n.services).forEach(([o,a])=>{let i=a.status==="healthy"?N.green("\u2713"):N.red("\u2717");console.log(` ${i} ${o}: ${a.status}`),a.latency&&console.log(` Latency: ${a.latency}ms`),a.error&&console.log(` Error: ${a.error}`)}))}}function an(e){let t=e.command("admin").description("Platform admin operations");t.command("tenants").description("List all tenants/organizations").option("--limit <n>","Maximum number of tenants to return",o=>parseInt(o,10)).option("--offset <n>","Offset for pagination",o=>parseInt(o,10)).option("--status <status>","Filter by status (active|suspended)").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await Hr({limit:o.limit,offset:o.offset,status:o.status,format:o.format||a.format,dryRun:o.dryRun||a.dryRun})}),t.command("tenant <id>").description("Get tenant details").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a)=>{let i=a.optsWithGlobals?.()||a;await Jr(o,{format:a.format||i.format,dryRun:a.dryRun||i.dryRun})}),t.command("suspend <id>").description("Suspend a tenant (requires --yes)").option("--yes","Confirm suspension").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a)=>{let i=a.optsWithGlobals?.()||a;await Qr(o,{yes:a.yes||i.yes,format:a.format||i.format,dryRun:a.dryRun||i.dryRun})}),t.command("unsuspend <id>").description("Unsuspend a tenant").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a)=>{let i=a.optsWithGlobals?.()||a;await Xr(o,{format:a.format||i.format,dryRun:a.dryRun||i.dryRun})}),t.command("usage").description("Cross-tenant usage report").option("--period <period>","Report period (daily|weekly|monthly)").option("--top <n>","Show top N tenants",o=>parseInt(o,10)).option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await Zr({period:o.period,top:o.top,format:o.format||a.format,dryRun:o.dryRun||a.dryRun})});let n=t.command("dlq").description("Dead letter queue management");n.command("list").description("List failed messages in DLQ").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await eo({format:o.format||a.format,dryRun:o.dryRun||a.dryRun})}),n.command("replay <id>").description("Replay a failed message").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a)=>{let i=a.optsWithGlobals?.()||a;await to(o,{format:a.format||i.format,dryRun:a.dryRun||i.dryRun})}),n.command("purge").description("Purge the dead letter queue (requires --yes)").option("--yes","Confirm purge").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await no({yes:o.yes||a.yes,format:o.format||a.format,dryRun:o.dryRun||a.dryRun})});let r=t.command("flags").description("Feature flag management");r.command("list").description("List all feature flags").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await ro({format:o.format||a.format,dryRun:o.dryRun||a.dryRun})}),r.command("set <flag> <value>").description("Set a feature flag override").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a,i)=>{let l=i.optsWithGlobals?.()||i;await oo(o,a,{format:i.format||l.format,dryRun:i.dryRun||l.dryRun})}),r.command("unset <flag>").description("Remove a feature flag override").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async(o,a)=>{let i=a.optsWithGlobals?.()||a;await so(o,{format:a.format||i.format,dryRun:a.dryRun||i.dryRun})}),t.command("audit").description("Query audit log with optional filters").option("--actor <userId>","Filter by actor/user ID").option("--action <type>","Filter by action type").option("--since <date>","Filter entries since date (ISO format)").option("--limit <n>","Maximum number of entries to return",o=>parseInt(o,10)).option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await ao({actor:o.actor,action:o.action,since:o.since,limit:o.limit,format:o.format||a.format,dryRun:o.dryRun||a.dryRun})}),t.command("health").description("Platform-wide health check").option("--format <type>","Output format: json or plain").option("--dry-run","Show request without executing").action(async o=>{let a=o.optsWithGlobals?.()||o;await io({format:o.format||a.format,dryRun:o.dryRun||a.dryRun})})}import{existsSync as lo,readFileSync as uo,writeFileSync as mo}from"fs";import{resolve as Ct}from"path";var v={pm:["npm","pnpm","yarn","bun"],region:["global","cn","hybrid"],orm:["prisma","drizzle","none"],db:["postgres","mysql","sqlite","none"],auth:["clerk","betterauth","none"],socialLogin:["wechat","qq","dingtalk","workweixin","feishu","weibo"],payment:["stripe","lemon","wechat","alipay","none"],ai:["openai","anthropic","deepseek","xai","moonshot","google","mistral","cohere","perplexity","ai21","upstage","siliconflow","volcengine-ark","bailian","zhipu","baichuan","minimax","stepfun","sensetime","tencent","lingyi","openrouter","vercel-gateway","litellm","portkey","aws-bedrock","azure-openai","gcp-vertex","groq","fireworks","together","huggingface","replicate","lepton","anyscale","octoai","deepinfra","novita","custom","none"],deploy:["vercel","railway","cloudflare","selfhost","none"],docs:["fumadocs","mintlify","docusaurus","nextra","vitepress","none"],email:["resend","postmark","ses","aliyun-dm","tencent-ses","netease","none"],storage:["r2","s3","supabase-storage","aliyun-oss","tencent-cos","qiniu","none"],monitoring:["sentry","datadog","bugsnag","aliyun-arms","tingyun","none"],analytics:["posthog","plausible","umami","mixpanel","baidu","sensors","growingio","none"],sms:["twilio","messagebird","plivo","aliyun-sms","tencent-sms","yunpian","none"],queue:["qstash","bullmq","upstash","sqs","none"],search:["meilisearch","typesense","algolia","pgvector","none"],cache:["upstash-redis","vercel-kv","redis","dragonfly","none"],notifications:["novu","knock","custom","none"],webhooks:["svix","custom","none"],cms:["sanity","contentful","strapi","none"],featureFlags:["vercel-flags","growthbook","configcat","none"],captcha:["turnstile","hcaptcha","aliyun-slide","none"],mcp:["on","off"],metering:["auto","on","off"],billingMode:["usage","seat","credits"],idp:["clerk","oauth-server"]},co=[{flags:"-p, --pm <id>",description:"Package manager: npm, pnpm, yarn, or bun"},{flags:"--region <id>",description:"Target region: global, cn, or hybrid"},{flags:"--orm <id>",description:"ORM: prisma, drizzle, or none"},{flags:"--db <id>",description:"Database: postgres, mysql, sqlite, or none"},{flags:"--auth <id>",description:"Auth provider: clerk, betterauth, or none"},{flags:"--social-login <ids>",description:"Comma-separated CN social login providers"},{flags:"--payment <id>",description:"Payment provider: stripe, lemon, wechat, alipay, or none"},{flags:"--ai <ids>",description:"Expert/non-interactive AI provider seed ids"},{flags:"--deploy <target>",description:"Deploy target: vercel, railway, cloudflare, selfhost, or none"},{flags:"--docs <id>",description:"Docs framework: fumadocs, mintlify, docusaurus, nextra, vitepress, or none"},{flags:"--email <id>",description:"Transactional email provider"},{flags:"--storage <id>",description:"Object storage provider"},{flags:"--monitoring <id>",description:"Monitoring provider"},{flags:"--analytics <id>",description:"Analytics provider"},{flags:"--sms <id>",description:"SMS provider"},{flags:"--queue <id>",description:"Queue provider"},{flags:"--search <id>",description:"Search provider"},{flags:"--cache <id>",description:"Cache provider"},{flags:"--notifications <id>",description:"Notifications provider"},{flags:"--webhooks <id>",description:"Outbound webhooks provider"},{flags:"--cms <id>",description:"Headless CMS provider"},{flags:"--feature-flags <id>",description:"Feature flag provider"},{flags:"--captcha <id>",description:"Captcha provider"},{flags:"--mcp <mode>",description:"MCP server mode: on or off"},{flags:"--metering <mode>",description:"Metering mode: auto, on, or off"},{flags:"--billing-mode <mode>",description:"Billing mode: usage, seat, or credits"},{flags:"--idp <id>",description:"Identity provider: clerk or oauth-server"},{flags:"--i18n",description:"Enable i18n",default:!0},{flags:"--no-i18n",description:"Disable i18n"},{flags:"--no-install",description:"Skip package install"},{flags:"--no-git",description:"Skip git init"},{flags:"--dry-run",description:"Preview project scaffolding without creating files (exit code 10)"},{flags:"--json",description:"Emit machine-readable JSON events"},{flags:"--yes",description:"Skip all interactive prompts (Agent mode)"}],ge={name:"nebutra",description:"Governance-first CLI for Nebutra Sailor scaffolding, registry-backed features, and platform operations",usage:"nebutra [command] [options]",options:[{flags:"--format <type>",description:"Output format: json, table, plain"},{flags:"--yes, --no-interactive",description:"Skip all interactive prompts (Agent mode)"},{flags:"--no-color",description:"Disable colored output"},{flags:"--verbose",description:"Enable verbose output"},{flags:"--quiet",description:"Suppress non-essential output"}],subcommands:[{name:"init",description:"Initialize a Nebutra project and create nebutra.config.json",usage:"nebutra init [options]",options:[{flags:"--dry-run",description:"Preview changes without writing files (exits with code 10)"},{flags:"--yes",description:"Skip all interactive prompts (Agent mode)"},{flags:"--if-not-exists",description:"Skip initialization if nebutra.config.json already exists"}],arguments:[],examples:[{command:"nebutra init",description:"Initialize Nebutra configuration in the current directory"},{command:"nebutra init --dry-run",description:"Preview initialization changes without writing files"},{command:"nebutra init --yes --if-not-exists",description:"Initialize without prompts, skip if already configured"}]},{name:"add",description:"Add a registry-backed platform feature or external UI component",usage:"nebutra add [components...] [options]",arguments:[{name:"components",description:"Local feature names to install from the Nebutra registry",required:!1,variadic:!0}],options:[{flags:"--21st <id>",description:"Fetch and install a component from 21st.dev registry"},{flags:"--v0 <url>",description:"Fetch and install a component from v0.dev by URL"},{flags:"--dry-run",description:"Preview what would be installed without making changes (exit code 10)"},{flags:"--yes",description:"Skip all interactive prompts and use defaults (Agent mode)"},{flags:"--if-not-exists",description:"Skip installation if component already exists"}],examples:[{command:"nebutra add cache --provider upstash-redis --yes",description:"Install the local cache feature with the Upstash Redis provider"},{command:"nebutra add --21st button-01",description:"Add a component from 21st.dev (shadcn-style registry)"},{command:'nebutra add --v0 "https://v0.dev/r/..." --dry-run',description:"Preview adding a component from v0.dev without making changes"},{command:"nebutra add webhooks --provider svix --yes --if-not-exists",description:"Install webhooks without prompts, skip if already configured"}]},{name:"create",description:"Scaffold a topology-first Nebutra Sailor project with governed defaults",usage:"nebutra create [dir] [options]",arguments:[{name:"dir",description:"Target directory for the new project (optional, will prompt if not provided)",required:!1}],options:co,examples:[{command:"nebutra create my-saas-app --region=hybrid --ai=openai,deepseek",description:"Create a topology-first project with hybrid defaults and selected AI providers"},{command:"nebutra create my-cn-app --region=cn --payment=wechat --storage=aliyun-oss --deploy=selfhost -y",description:"Create a non-interactive China-ready scaffold"},{command:"nebutra create my-app --storage=supabase-storage --dry-run --json",description:"Preview scaffolding with structured JSON output"}]},{name:"mcp",description:"Start the Nebutra MCP server for AI agents and editors",usage:"nebutra mcp [options]",arguments:[],options:[{flags:"--stdio",description:"Use stdio transport for communication (default: enabled)",default:!0},{flags:"--verbose",description:"Enable verbose logging for MCP server"}],examples:[{command:"nebutra mcp",description:"Start the MCP server for AI-powered project context integration"},{command:"nebutra mcp --verbose",description:"Start the MCP server with detailed logging output"}]},{name:"schema",description:"Show command schema and argument documentation (Agent-friendly JSON output)",usage:"nebutra schema [command] [options]",arguments:[{name:"command",description:"Command name to show schema for (e.g., init, add, create)",required:!1}],options:[{flags:"--all",description:"Show full schema for all commands as JSON",default:!1},{flags:"--list",description:"List all available command names",default:!1},{flags:"--exit-codes",description:"Show exit codes reference",default:!1}],examples:[{command:"nebutra schema --all",description:"Show complete JSON of all commands, args, options, value domains"},{command:"nebutra schema init",description:"Show schema for init command (arguments, options, defaults, examples)"},{command:"nebutra schema add",description:"Show schema for add command with enum values"},{command:"nebutra schema --list",description:"List just the command names (quick discovery)"},{command:"nebutra schema --exit-codes",description:"Show exit codes reference for all possible exit codes"}]},{name:"brand",description:"Manage governed brand tokens, palettes, and visual system outputs",usage:"nebutra brand [subcommand]",examples:[{command:"nebutra brand palette --primary=#0047FF",description:"Generate a token-aligned blue palette"}]},{name:"i18n",description:"Manage localization files and multilingual product copy",usage:"nebutra i18n [subcommand]"},{name:"infra",description:"Manage local infrastructure services for Nebutra development",usage:"nebutra infra [subcommand]",examples:[{command:"nebutra infra up --lite",description:"Start lightweight local infrastructure"}]},{name:"env",description:"Validate and manage environment variables across apps and packages",usage:"nebutra env [subcommand]"},{name:"license",description:"Manage Nebutra Sailor commercial license activation and status",usage:"nebutra license [subcommand]",arguments:[],options:[],subcommands:[{name:"activate",description:"Activate a commercial license key for local development",usage:"nebutra license activate <key>",arguments:[{name:"key",description:"Your Nebutra Sailor commercial license key",required:!0}],options:[{flags:"--quiet",description:"Suppress output"}],examples:[{command:"nebutra license activate liz_1234567890",description:"Activate your license key globally"}]},{name:"status",description:"Check the locally configured license status",usage:"nebutra license status",arguments:[],options:[{flags:"--quiet",description:"Suppress output"}],examples:[{command:"nebutra license status",description:"Check if you have an active license"}]}]},{name:"ai",description:"Configure AI providers, gateway routing, and agent-ready defaults",usage:"nebutra ai [subcommand]"},{name:"auth",description:"Configure authentication providers and tenant access defaults",usage:"nebutra auth [subcommand]"},{name:"billing",description:"Manage billing providers, subscriptions, usage, and return flows",usage:"nebutra billing [subcommand]"},{name:"stats",description:"Show monorepo statistics, health signals, and platform inventory",usage:"nebutra stats [options]"},{name:"db",description:"Manage database schema, migrations, seeds, and generated clients",usage:"nebutra db [subcommand]"},{name:"generate",description:"Generate apps, modules, API surfaces, and typed project artifacts",usage:"nebutra generate [type] [name]"},{name:"preset",description:"List and apply topology presets for governed SaaS projects",usage:"nebutra preset [subcommand]"},{name:"dev",description:"Start development workflows for selected Nebutra apps or presets",usage:"nebutra dev [options]"},{name:"test",description:"Run unit, architecture, and E2E verification workflows",usage:"nebutra test [scope]"},{name:"admin",description:"Operate tenant, platform health, and administrative workflows",usage:"nebutra admin [subcommand]"},{name:"community",description:"Inspect community health, showcases, and external adoption signals",usage:"nebutra community [subcommand]"},{name:"growth",description:"Analyze product growth, funnels, retention, and lifecycle signals",usage:"nebutra growth [subcommand]"},{name:"ecosystem",description:"Manage template marketplace, ideas, project showcase, and ecosystem sync",usage:"nebutra ecosystem [subcommand]"},{name:"services",description:"Inspect and manage microservice health, logs, scaling, and rollouts",usage:"nebutra services [subcommand]"},{name:"search",description:"Manage search indexes, reindexing, and search diagnostics",usage:"nebutra search [subcommand]"},{name:"secrets",description:"Manage encrypted tenant and platform secrets",usage:"nebutra secrets [subcommand]"},{name:"completions",description:"Generate shell completions for the current Nebutra command surface",usage:"nebutra completions [shell]",arguments:[{name:"shell",description:"Shell to generate completions for: bash, zsh, fish, or install",required:!1}]},{name:"doctor",description:"Check local project setup and common Nebutra configuration issues",usage:"nebutra doctor"}]};var ln="nebutra.config.json",po=v.ai.filter(e=>e!=="none"),cn={"aws-bedrock":["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"],"azure-openai":["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"],"gcp-vertex":["GOOGLE_APPLICATION_CREDENTIALS","GOOGLE_CLOUD_PROJECT"],"vercel-gateway":["AI_GATEWAY_API_KEY"],"volcengine-ark":["VOLCENGINE_ARK_API_KEY"],bailian:["DASHSCOPE_API_KEY"],litellm:["LITELLM_API_KEY","LITELLM_BASE_URL"],portkey:["PORTKEY_API_KEY"],custom:["AI_CUSTOM_API_KEY","AI_CUSTOM_BASE_URL"]};function ne(e,t,n){if(t==="json"){let r={command:n||"ai",...e&&typeof e=="object"&&"exitCode"in e?{exitCode:e.exitCode}:{},...e};console.log(JSON.stringify(r,null,2))}else e&&typeof e=="object"&&"stdout"in e&&e.stdout&&console.log(e.stdout),e&&typeof e=="object"&&"stderr"in e&&e.stderr&&(e.exitCode??0)!==0&&console.error(e.stderr)}function fo(e){return e.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function go(e){return cn[e]?cn[e]:[`${e.replace(/-/g,"_").toUpperCase()}_API_KEY`]}function Qe(){return Object.fromEntries(po.map(e=>[e,{id:e,name:fo(e),envVars:go(e)}]))}function se(){return Ct(process.cwd(),ln)}function Ce(){let e=se();if(!lo(e))return{$schema:"https://nebutra.com/schema.json"};try{return JSON.parse(uo(e,"utf-8"))}catch(t){s.error(`Failed to parse ${ln}: ${t instanceof Error?t.message:String(t)}`),process.exit(c.ERROR)}}function Xe(e){mo(se(),`${JSON.stringify(e,null,2)}
|
|
113
|
+
`)}function ye(e){return e.ai={providers:{},routes:{},...e.ai},e.ai.providers={...e.ai.providers},e.ai.routes={...e.ai.routes},e.ai}function dn(e){let t=Qe(),n=[...new Set(e.map(o=>o.toLowerCase()))],r=n.filter(o=>!t[o]);return r.length>0&&(s.error(`Unknown AI provider(s): ${r.join(", ")}. Valid providers: ${Object.keys(t).join(", ")}`),process.exit(c.INVALID_ARGS)),n}function yo(e){let t=e?.split(",").map(r=>r.trim()).filter(Boolean)??[];t.length===0&&(s.error("Route fallback cannot be empty. Use --fallback provider:model,provider:model"),process.exit(c.INVALID_ARGS));let n=t.filter(r=>!/^[a-z0-9-]+:[^,\s]+$/i.test(r));return n.length>0&&(s.error(`Invalid fallback target(s): ${n.join(", ")}. Expected provider:model.`),process.exit(c.INVALID_ARGS)),t}function un(e){let t=ye(e),n=t.providers??{},r=t.routes??{},o=Object.entries(n).filter(([,a])=>a.enabled).map(([a])=>a);return{path:se(),gateway:t.gateway??null,providers:n,enabledProviders:o,routes:r}}function te(e){let t=typeof e.opts=="function"?e.opts():e,n=typeof e.optsWithGlobals=="function"?e.optsWithGlobals():t,r=t.format??n.format??ho();return{...n,...t,...r?{format:r}:{}}}function ho(){let e=process.argv.find(n=>n.startsWith("--format="));if(e)return e.slice(9);let t=process.argv.indexOf("--format");if(t>=0)return process.argv[t+1]}function Ue(e){return e?e.length<=8?"***":`${e.substring(0,8)}***`:"not set"}async function bo(e){e.format!=="json"&&s.info("Checking configured AI models and providers...");let t={openai:{name:"OpenAI",keyVars:["OPENAI_API_KEY"],configured:!1},anthropic:{name:"Anthropic Claude",keyVars:["ANTHROPIC_API_KEY"],configured:!1},openrouter:{name:"OpenRouter",keyVars:["OPENROUTER_API_KEY"],configured:!1},vercel:{name:"Vercel AI SDK",keyVars:["VERCEL_AI_API_KEY"],configured:!1}};for(let r of Object.values(t))r.configured=r.keyVars.some(o=>process.env[o]);let n={providers:Object.entries(t).map(([r,o])=>({id:r,name:o.name,configured:o.configured,keys:o.keyVars.map(a=>({name:a,value:Ue(process.env[a])}))})),configuredCount:Object.values(t).filter(r=>r.configured).length,totalProviders:Object.keys(t).length};e.dryRun||(n.configuredCount===0?s.warn("No AI providers configured. Set API keys to enable AI features."):s.success(`${n.configuredCount} AI provider(s) configured`)),ne(n,e.format,"ai:models")}async function vo(e){e.format!=="json"&&s.info("Scanning for available agents...");try{let t=Ct(process.cwd(),"backends/gateway/src/routes/agents"),n=[];try{let o=await import("fs");o.existsSync(t)&&(n=o.readdirSync(t).filter(i=>i.endsWith(".ts")&&!i.endsWith(".d.ts")).map(i=>({name:i.replace(".ts",""),path:`backends/gateway/src/routes/agents/${i}`})))}catch{n=[]}let r={agents:n.length>0?n:[{name:"No agents found",path:"create agents in backends/gateway/src/routes/agents/"}],count:n.length};e.dryRun||(r.count===0?s.info("No agents configured. Create agents in backends/gateway/src/routes/agents/"):s.success(`Found ${r.count} agent(s)`)),ne(r,e.format,"ai:agents")}catch(t){s.error(`Failed to scan agents: ${t instanceof Error?t.message:String(t)}`),process.exit(c.ERROR)}}async function wo(e,t){(!e||e.trim().length===0)&&(s.error("Prompt cannot be empty"),process.exit(c.INVALID_ARGS)),t.format!=="json"&&(s.info("Testing AI prompt against default model..."),s.info("Note: Ensure API gateway is running on http://localhost:3001"));let n={prompt:e,note:"To execute this test, the API gateway must be running locally. Run: pnpm --filter @nebutra/gateway dev",endpoint:"http://localhost:3001/api/ai/test",method:"POST",payload:{prompt:e.substring(0,100)}};t.dryRun||s.warn("AI test execution requires a running API gateway. Start it with: pnpm --filter @nebutra/gateway dev"),ne(n,t.format,"ai:test")}async function Ro(e){e.format!=="json"&&s.info("Checking AI configuration...");let t={providers:{openai:{key:Ue(process.env.OPENAI_API_KEY),configured:!!process.env.OPENAI_API_KEY},anthropic:{key:Ue(process.env.ANTHROPIC_API_KEY),configured:!!process.env.ANTHROPIC_API_KEY},openrouter:{key:Ue(process.env.OPENROUTER_API_KEY),configured:!!process.env.OPENROUTER_API_KEY}},environment:{NODE_ENV:process.env.NODE_ENV||"development",NEBUTRA_LOG_LEVEL:process.env.NEBUTRA_LOG_LEVEL||"info"}};try{let r=Ct(process.cwd(),"nebutra.config.ts"),o=await import("fs");if(o.existsSync(r)){let i=o.readFileSync(r,"utf-8").match(/preset:\s*["']([^"']+)["']/);i&&(t.preset=i[1])}}catch{}let n=Object.entries(t.providers).filter(([,r])=>r.configured).map(([r])=>r);e.dryRun||(n.length===0?s.warn("No AI providers configured. Set environment variables to enable AI features."):s.success(`Configuration loaded: ${n.join(", ")}`)),ne(t,e.format,"ai:config")}async function So(e){let t=Ce(),n=ye(t),r=Qe(),o=Object.values(r).map(i=>{let l=n.providers?.[i.id];return{id:i.id,name:i.name,enabled:l?.enabled===!0,configured:i.envVars.some(u=>!!process.env[u]),envVars:i.envVars.map(u=>({name:u,value:Ue(process.env[u])}))}}),a={configPath:se(),providers:o,enabledCount:o.filter(i=>i.enabled).length,totalProviders:o.length};if(e.format!=="json"){let i=o.filter(l=>l.enabled);i.length===0?s.warn("No runtime AI providers enabled in nebutra.config.json."):s.success(`Enabled AI providers: ${i.map(l=>l.id).join(", ")}`)}ne(a,e.format,"ai:provider:list")}async function Co(e,t){let n=dn(e),r=Ce(),o=ye(r),a=Qe(),i=new Date().toISOString();for(let u of n)o.providers[u]={enabled:!0,envVars:a[u].envVars,enabledAt:o.providers?.[u]?.enabledAt??i};Xe(r);let l={configPath:se(),enabled:n,env:t.env===!0?n.flatMap(u=>a[u].envVars.map(m=>({provider:u,name:m,example:`${m}=`}))):void 0,ai:un(r)};if(t.format!=="json"&&(s.success(`Enabled AI provider(s): ${n.join(", ")}`),t.env)){s.info("Required environment variables:");for(let u of n)for(let m of a[u].envVars)console.log(`${m}=`)}ne(l,t.format,"ai:provider:enable")}async function Eo(e,t){let n=dn(e),r=Ce(),o=ye(r),a=Qe(),i=new Date().toISOString();for(let u of n)o.providers[u]={enabled:!1,envVars:o.providers?.[u]?.envVars??a[u].envVars,disabledAt:i};Xe(r);let l={configPath:se(),disabled:n,ai:un(r)};t.format!=="json"&&s.success(`Disabled AI provider(s): ${n.join(", ")}`),ne(l,t.format,"ai:provider:disable")}async function ko(e){let t=Ce(),n=ye(t),r=new Date().toISOString();n.gateway={enabled:!0,mode:"local",initializedAt:n.gateway?.initializedAt??r,updatedAt:r},Xe(t);let o={configPath:se(),gateway:n.gateway};e.format!=="json"&&s.success("AI gateway governance initialized in nebutra.config.json"),ne(o,e.format,"ai:gateway:init")}async function Oo(e){let t=Ce(),n=ye(t),r={configPath:se(),gateway:n.gateway??null,initialized:n.gateway?.enabled===!0};e.format!=="json"&&(r.initialized?s.success("AI gateway governance is initialized"):s.warn("AI gateway governance is not initialized. Run: nebutra ai gateway init")),ne(r,e.format,"ai:gateway:status")}async function xo(e,t){(!e||e.trim().length===0)&&(s.error("Route name cannot be empty"),process.exit(c.INVALID_ARGS)),t.policy||(s.error("Route policy is required. Use --policy <policy>."),process.exit(c.INVALID_ARGS));let n=yo(t.fallback),r=Ce(),o=ye(r);o.routes[e]={policy:t.policy,fallback:n,updatedAt:new Date().toISOString()},Xe(r);let a={configPath:se(),route:{name:e,...o.routes[e]},routes:o.routes};t.format!=="json"&&s.success(`AI route '${e}' set with policy '${t.policy}'`),ne(a,t.format,"ai:route:set")}async function $o(e){let t=Ce(),n=ye(t),r=Object.entries(n.routes??{}).map(([a,i])=>({name:a,...i})),o={configPath:se(),routes:r,count:r.length};e.format!=="json"&&(r.length===0?s.warn("No AI routes configured. Run: nebutra ai route set chat --policy cost-latency-balanced --fallback openai:gpt-5.4"):s.success(`Configured AI routes: ${r.map(a=>a.name).join(", ")}`)),ne(o,e.format,"ai:route:list")}function mn(e){let t=e.command("ai").description("Manage AI models, agents, and configuration"),n=t.command("provider").description("Manage runtime AI provider governance");n.command("list").description("List supported and enabled AI providers").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await So({format:i.format,interactive:i.yes!==!0})}),n.command("enable <providers...>").description("Enable one or more runtime AI providers in nebutra.config.json").option("--env","Print required environment variable placeholders").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let l=te(i);await Co(a,{env:l.env===!0,format:l.format,interactive:l.yes!==!0})}),n.command("disable <providers...>").description("Disable one or more runtime AI providers in nebutra.config.json").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let l=te(i);await Eo(a,{format:l.format,interactive:l.yes!==!0})});let r=t.command("gateway").description("Manage local AI gateway governance");r.command("init").description("Initialize the local AI gateway governance block").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await ko({format:i.format,interactive:i.yes!==!0})}),r.command("status").description("Show local AI gateway governance status").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await Oo({format:i.format,interactive:i.yes!==!0})});let o=t.command("route").description("Manage AI routing policies");o.command("set <name>").description("Create or update an AI route policy").requiredOption("--policy <policy>","Routing policy id").requiredOption("--fallback <list>","Comma-separated provider:model fallback chain").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let l=te(i);await xo(a,{policy:l.policy,fallback:l.fallback,format:l.format,interactive:l.yes!==!0})}),o.command("list").description("List configured AI routes").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await $o({format:i.format,interactive:i.yes!==!0})}),t.command("models").description("List configured AI models and providers").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await bo({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})}),t.command("agents").description("List available agents in the project").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await vo({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})}),t.command("test <prompt>").description("Send a test prompt to the default AI model (requires running API gateway)").option("--dry-run","Preview without executing").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let l=te(i);await wo(a,{dryRun:l.dryRun===!0,format:l.format,interactive:l.yes!==!0})}),t.command("config").description("Show current AI configuration and provider status").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async a=>{let i=te(a);await Ro({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})})}import{resolve as Ao}from"path";function Et(e,t,n){if(t==="json"){let r={command:n||"auth",...e&&typeof e=="object"&&"exitCode"in e?{exitCode:e.exitCode}:{},...e};console.log(JSON.stringify(r,null,2))}else e&&typeof e=="object"&&"stdout"in e&&e.stdout&&console.log(e.stdout),e&&typeof e=="object"&&"stderr"in e&&e.stderr&&(e.exitCode??0)!==0&&console.error(e.stderr)}function ae(e,t){return e?e.length<=8?"***":`${e.substring(0,8)}*** (${t||e.length} chars)`:"not set"}function _o(){try{let e=Ao(process.cwd(),"nebutra.config.ts"),t=kr("fs");if(t.existsSync(e)){let n=t.readFileSync(e,"utf-8");if(n.includes("clerk"))return"clerk";if(n.includes("better-auth"))return"better-auth"}}catch{}return"clerk"}async function Po(e){s.info("Checking authentication configuration...");let t=_o(),n={provider:t,configured:!1,keys:{}};t==="clerk"?(n.keys={CLERK_SECRET_KEY:ae(process.env.CLERK_SECRET_KEY,"secret"),NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:ae(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,"publishable"),CLERK_WEBHOOK_SECRET:ae(process.env.CLERK_WEBHOOK_SECRET,"webhook")},n.configured=!!process.env.CLERK_SECRET_KEY&&!!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY):(n.keys={BETTER_AUTH_SECRET:ae(process.env.BETTER_AUTH_SECRET,"secret"),BETTER_AUTH_URL:process.env.BETTER_AUTH_URL||"not set"},n.configured=!!process.env.BETTER_AUTH_SECRET),e.dryRun||(n.configured?s.success(`Auth provider configured: ${t}`):s.warn(`Auth provider ${t} is not fully configured`)),Et(n,e.format,"auth:status")}async function Io(e,t){let n=["clerk","better-auth"];n.includes(e.toLowerCase())||(s.error(`Invalid provider: ${e}. Choose from: ${n.join(", ")}`),process.exit(c.INVALID_ARGS));let r=e.toLowerCase();s.info(`Setting up ${r} authentication...`),r==="clerk"?(s.info("Clerk setup requires:"),s.info("1. A Clerk account (https://clerk.com)"),s.info("2. A Clerk application created"),s.info("3. API keys from the Clerk dashboard"),s.info(""),s.info("To complete setup:"),s.info("1. Go to https://dashboard.clerk.com"),s.info("2. Copy your Secret Key and Publishable Key"),s.info("3. Add them to .env.local:"),s.info(' CLERK_SECRET_KEY="sk_..."'),s.info(' NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_..."'),s.info(""),s.info("Webhook setup (optional):"),s.info("1. In Clerk dashboard, go to Webhooks"),s.info("2. Create endpoint: https://yourdomain.com/api/webhooks/clerk"),s.info("3. Copy the signing secret to .env.local:"),s.info(' CLERK_WEBHOOK_SECRET="whsec_..."')):(s.info("Better Auth setup requires:"),s.info("1. Database URL configured (DATABASE_URL env var)"),s.info("2. A generated auth secret"),s.info(""),s.info("To complete setup:"),s.info("1. Ensure DATABASE_URL is set in .env.local"),s.info("2. Generate a new secret:"),s.info(" openssl rand -base64 32"),s.info("3. Add to .env.local:"),s.info(' BETTER_AUTH_SECRET="<generated_secret>"'),s.info(' BETTER_AUTH_URL="http://localhost:3000"'),s.info(""),s.info("4. Run database migration:"),s.info(" pnpm --filter @nebutra/web db:push")),t.dryRun||s.success(`Setup instructions for ${r} displayed above`),Et({provider:r,setupGuide:"Complete setup by following the instructions above",documentationUrl:r==="clerk"?"https://clerk.com/docs":"https://www.better-auth.com"},t.format,"auth:setup")}async function No(e){s.info("Checking authentication keys and secrets...");let n={keys:{clerk:{CLERK_SECRET_KEY:ae(process.env.CLERK_SECRET_KEY,"secret key"),NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:ae(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,"publishable key"),CLERK_WEBHOOK_SECRET:ae(process.env.CLERK_WEBHOOK_SECRET,"webhook secret")},betterAuth:{BETTER_AUTH_SECRET:ae(process.env.BETTER_AUTH_SECRET,"secret"),BETTER_AUTH_URL:process.env.BETTER_AUTH_URL||"not set"},common:{DATABASE_URL:ae(process.env.DATABASE_URL,"database url"),JWT_SECRET:ae(process.env.JWT_SECRET,"jwt secret")}},configured:{clerk:{secret:!!process.env.CLERK_SECRET_KEY,publishable:!!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY},betterAuth:{secret:!!process.env.BETTER_AUTH_SECRET,url:!!process.env.BETTER_AUTH_URL}}};if(!e.dryRun){let r=n.configured.clerk.secret&&n.configured.clerk.publishable,o=n.configured.betterAuth.secret&&n.configured.betterAuth.url;!r&&!o?s.warn("No authentication keys configured"):(s.info("Auth keys status:"),r&&s.success("Clerk configured"),o&&s.success("Better Auth configured"))}Et(n,e.format,"auth:keys")}function pn(e){let t=e.command("auth").description("Manage authentication configuration (Clerk or Better Auth)");t.command("status").description("Check current authentication provider configuration").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Po({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("setup <provider>").description("Configure Clerk or Better Auth (interactive wizard)").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async(n,r)=>{let o=r.optsWithGlobals?.()||r;await Io(n,{dryRun:r.dryRun||!1,format:r.format||o.format,interactive:o.yes!==!0})}),t.command("keys").description("Show authentication-related environment variables (masked)").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await No({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}import he from"fs";import re from"path";import*as Ze from"@clack/prompts";import yn from"picocolors";var fn=["ts","py"],To=`{
|
|
114
|
+
"name": "@nebutra/gateway",
|
|
115
|
+
"version": "0.1.0",
|
|
116
|
+
"private": true,
|
|
117
|
+
"type": "module",
|
|
118
|
+
"main": "src/index.ts",
|
|
119
|
+
"scripts": {
|
|
120
|
+
"dev": "tsx watch src/index.ts",
|
|
121
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
122
|
+
"start": "node dist/index.js"
|
|
123
|
+
},
|
|
124
|
+
"dependencies": {
|
|
125
|
+
"hono": "^4.6.0"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
`,jo=`import { Hono } from "hono";
|
|
129
|
+
|
|
130
|
+
const app = new Hono();
|
|
131
|
+
|
|
132
|
+
app.get("/healthz", (c) => c.json({ status: "ok" }));
|
|
133
|
+
|
|
134
|
+
export default app;
|
|
135
|
+
`,Uo=`# @nebutra/gateway
|
|
136
|
+
|
|
137
|
+
TypeScript / Hono BFF \u2014 auth, tenancy, rate-limit, and routing.
|
|
138
|
+
|
|
139
|
+
Per ADR 2026-05-10, this is the DEFAULT for new backend work.
|
|
140
|
+
`;function Do(e){return`[project]
|
|
141
|
+
name = "${e}"
|
|
142
|
+
version = "0.1.0"
|
|
143
|
+
description = "Nebutra Python backend service: ${e}"
|
|
144
|
+
requires-python = ">=3.11"
|
|
145
|
+
dependencies = [
|
|
146
|
+
"fastapi>=0.115.0",
|
|
147
|
+
"uvicorn[standard]>=0.32.0",
|
|
148
|
+
"pydantic>=2.9.0",
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
[build-system]
|
|
152
|
+
requires = ["hatchling"]
|
|
153
|
+
build-backend = "hatchling.build"
|
|
154
|
+
|
|
155
|
+
[tool.hatch.build.targets.wheel]
|
|
156
|
+
packages = ["src"]
|
|
157
|
+
`}function Lo(e){return`# backends/python/${e}
|
|
158
|
+
|
|
159
|
+
A Python (FastAPI) backend service.
|
|
160
|
+
|
|
161
|
+
## ADR 2026-05-10 \u2014 TS-by-Default
|
|
162
|
+
|
|
163
|
+
Per [docs/architecture/2026-05-10-ts-by-default-python-only-when-justified.md](../../../docs/architecture/2026-05-10-ts-by-default-python-only-when-justified.md),
|
|
164
|
+
new Python services are acceptable **only** when at least one of the following applies:
|
|
165
|
+
|
|
166
|
+
1. **Batch / queued work** that is too long for edge runtimes (>5s typical)
|
|
167
|
+
2. **ML / scientific compute** depending on the Python ecosystem (transformers, vLLM, etc.)
|
|
168
|
+
3. **Specialized libraries** with no comparable TS port
|
|
169
|
+
|
|
170
|
+
If your service is CRUD, webhooks, billing, content management, or third-party API proxying,
|
|
171
|
+
it belongs in \`backends/gateway\` (TypeScript) instead.
|
|
172
|
+
|
|
173
|
+
## Justification
|
|
174
|
+
|
|
175
|
+
> _Replace this line with the concrete reason this service is Python (cite one of 1/2/3 above)._
|
|
176
|
+
|
|
177
|
+
## Run
|
|
178
|
+
|
|
179
|
+
\`\`\`bash
|
|
180
|
+
uv sync
|
|
181
|
+
uv run uvicorn src.main:app --reload --port 8000
|
|
182
|
+
\`\`\`
|
|
183
|
+
`}var Fo=`from fastapi import FastAPI
|
|
184
|
+
from pydantic import BaseModel
|
|
185
|
+
|
|
186
|
+
app = FastAPI(title="nebutra-python-service")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class HealthResponse(BaseModel):
|
|
190
|
+
status: str
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@app.get("/healthz", response_model=HealthResponse)
|
|
194
|
+
async def healthz() -> HealthResponse:
|
|
195
|
+
return HealthResponse(status="ok")
|
|
196
|
+
`;async function Mo(e,t){let n=re.join(e,"backends/gateway");he.existsSync(n)&&(s.info(`backends/gateway already exists in this monorepo \u2014 nothing to do.
|
|
197
|
+
(This command is primarily for downstream consumers using create-sailor.)`),process.exit(c.SUCCESS)),t.dryRun&&(process.stdout.write(JSON.stringify({mode:"dry-run",command:"backend init ts",files:["backends/gateway/package.json","backends/gateway/src/index.ts","backends/gateway/README.md"]},null,2)+`
|
|
198
|
+
`),process.exit(c.DRY_RUN_OK)),he.mkdirSync(re.join(n,"src"),{recursive:!0}),he.writeFileSync(re.join(n,"package.json"),To),he.writeFileSync(re.join(n,"src/index.ts"),jo),he.writeFileSync(re.join(n,"README.md"),Uo),s.success("Created backends/gateway scaffold"),process.exit(c.SUCCESS)}function gn(e){return/^[a-z][a-z0-9_-]{0,40}$/.test(e)}async function Go(e,t){let n=t.name,r=process.stdout.isTTY===!0&&!t.yes;if(!n&&r){let i=await Ze.text({message:"Service name (lowercase, hyphens or underscores):",placeholder:"translator",validate:l=>gn(l)?void 0:"Invalid name"});Ze.isCancel(i)&&(s.warn("Cancelled."),process.exit(c.CANCELLED)),n=i}n||(s.error("Missing --name. Required in non-interactive mode."),process.exit(c.INVALID_ARGS)),gn(n)||(s.error(`Invalid name: ${yn.red(n)}. Use lowercase letters, digits, hyphen, underscore.`),process.exit(c.INVALID_ARGS));let o=re.join(e,"backends/python",n);he.existsSync(o)&&(s.error(`backends/python/${n} already exists \u2014 refusing to overwrite.`),process.exit(c.CONFLICT));let a=[[re.join(o,"pyproject.toml"),Do(n)],[re.join(o,"README.md"),Lo(n)],[re.join(o,"src/main.py"),Fo],[re.join(o,"src/__init__.py"),""]];t.dryRun&&(process.stdout.write(JSON.stringify({mode:"dry-run",command:`backend init py --name ${n}`,files:a.map(([i])=>i)},null,2)+`
|
|
199
|
+
`),process.exit(c.DRY_RUN_OK));for(let[i,l]of a)he.mkdirSync(re.dirname(i),{recursive:!0}),he.writeFileSync(i,l);s.success(`Created backends/python/${n} scaffold`),s.info("Update README.md to cite which ADR justification applies (batch | ML | specialized libs)."),process.exit(c.SUCCESS)}async function Ko(e,t={}){fn.includes(e)||(s.error(`Invalid runtime: ${yn.red(e)}. Valid: ${fn.join(", ")}`),process.exit(c.INVALID_ARGS));let n=h();e==="ts"?await Mo(n,t):await Go(n,t)}function hn(e){e.command("backend").description("Scaffold backend services (ts | py)").command("init <runtime>").description("Scaffold a backend service (runtime: ts | py)").option("--name <name>","Service name (Python only; prompted if omitted)").option("--dry-run","Preview files that would be created (exit code 10)").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await Ko(n,{name:r.name,dryRun:r.dryRun||!1,yes:o.yes||!1,quiet:o.quiet||!1})})}function kt(e,t,n){if(t==="json"){let r={command:n||"billing",...e&&typeof e=="object"&&"exitCode"in e?{exitCode:e.exitCode}:{},...e};console.log(JSON.stringify(r,null,2))}else e&&typeof e=="object"&&"stdout"in e&&e.stdout&&console.log(e.stdout),e&&typeof e=="object"&&"stderr"in e&&e.stderr&&(e.exitCode??0)!==0&&console.error(e.stderr)}function qo(e,t){return e?e.length<=8?"***":`${e.substring(0,8)}*** (${t||e.length} chars)`:"not set"}var et={stripe:{name:"Stripe",keyVars:["STRIPE_SECRET_KEY","STRIPE_PUBLISHABLE_KEY"],webhookUrl:"/api/webhooks/stripe",setupUrl:"https://dashboard.stripe.com"},lemonsqueezy:{name:"Lemon Squeezy",keyVars:["LEMONSQUEEZY_API_KEY"],webhookUrl:"/api/webhooks/lemonsqueezy",setupUrl:"https://app.lemonsqueezy.com/settings/api"},polar:{name:"Polar",keyVars:["POLAR_ACCESS_TOKEN"],webhookUrl:"/api/webhooks/polar",setupUrl:"https://app.polar.sh/settings/api"},chinapay:{name:"ChinaPay",keyVars:["CHINAPAY_MERCHANT_ID","CHINAPAY_SECRET_KEY"],webhookUrl:"/api/webhooks/chinapay",setupUrl:"https://www.chinapay.com"}};async function Vo(e){s.info("Checking payment provider configuration...");let t=Object.entries(et).map(([o,a])=>{let i=a.keyVars.every(l=>process.env[l]);return{id:o,name:a.name,configured:i,keys:a.keyVars.map(l=>({name:l,value:qo(process.env[l],"key")}))}}),n=t.filter(o=>o.configured),r={providers:t,configuredCount:n.length,totalProviders:t.length,configuredList:n.map(o=>o.name)};e.dryRun||(r.configuredCount===0?s.warn("No payment providers configured. Set API keys to enable billing."):s.success(`${r.configuredCount} payment provider(s) configured`)),kt(r,e.format,"billing:status")}async function Wo(e,t){let n=Object.keys(et);n.includes(e.toLowerCase())||(s.error(`Invalid provider: ${e}. Choose from: ${n.join(", ")}`),process.exit(c.INVALID_ARGS));let r=e.toLowerCase(),o=et[r];s.info(`Setting up ${o.name} payment provider...`),s.info(""),s.info("Setup steps:"),s.info(`1. Go to ${o.setupUrl}`),s.info("2. Create or log in to your account"),s.info("3. Generate API credentials"),s.info("4. Add the following to .env.local:"),s.info(""),o.keyVars.forEach(i=>{s.info(` ${i}="<your_${i.toLowerCase()}>"`.padStart(45))}),s.info(""),s.info("5. (Optional) Configure webhook in provider dashboard:"),s.info(` Webhook URL: https://yourdomain.com${o.webhookUrl}`),t.dryRun||s.success(`Setup instructions for ${o.name} displayed above`);let a={provider:r,name:o.name,requiredKeys:o.keyVars,webhookUrl:o.webhookUrl,setupGuide:"Follow the instructions above to complete setup",documentationUrl:o.setupUrl};kt(a,t.format,"billing:setup")}async function Bo(e){s.info("Checking billing webhook configuration...");let t=process.env.NEXT_PUBLIC_APP_URL||"https://yourdomain.com",n=Object.entries(et).map(([o,a])=>({provider:o,name:a.name,url:`${t}${a.webhookUrl}`,endpoint:a.webhookUrl,configured:a.keyVars.every(i=>process.env[i])})),r={baseUrl:t,webhooks:n,configurationSteps:["1. Set NEXT_PUBLIC_APP_URL in .env.local to your domain","2. Copy the webhook URL for each provider","3. Configure in the provider's dashboard","4. Ensure the API key is also set"]};e.dryRun||(s.info("Webhook endpoints:"),n.forEach(o=>{o.configured?s.success(`${o.name}: configured`):s.warn(`${o.name}: not configured (missing API key)`)})),kt(r,e.format,"billing:webhooks")}function bn(e){let t=e.command("billing").description("Manage payment providers and billing configuration");t.command("status").description("Check which payment providers are configured").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Vo({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("setup <provider>").description("Configure payment provider (Stripe, Lemon Squeezy, Polar, or ChinaPay)").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async(n,r)=>{let o=r.optsWithGlobals?.()||r;await Wo(n,{dryRun:r.dryRun||!1,format:r.format||o.format,interactive:o.yes!==!0})}),t.command("webhooks").description("Show webhook endpoints for each payment provider").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Bo({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}function De(e,t,n){if(t==="json"){let r={command:n||"brand",exitCode:e.exitCode,success:e.exitCode===0,...e.stdout&&{stdout:e.stdout},...e.stderr&&{stderr:e.stderr}};console.log(JSON.stringify(r,null,2))}else e.stdout&&console.log(e.stdout),e.stderr&&e.exitCode!==0&&console.error(e.stderr)}function vn(e){return/^#[0-9A-Fa-f]{6}$/.test(e)}async function zo(e){s.info("Initializing brand system...");let t=await _("brand:init",{dryRun:e.dryRun,interactive:e.interactive??!0});De(t,e.format,"brand:init"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand system initialized successfully")}async function Yo(e){s.info("Applying brand configuration...");let t=await _("brand:apply",{dryRun:e.dryRun,interactive:e.interactive??!0});De(t,e.format,"brand:apply"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand applied successfully")}async function Ho(e){let t=[];e.primary&&(vn(e.primary)||(s.error(`Invalid primary color format: ${e.primary}. Expected hex format like #0033FE`),process.exit(c.INVALID_ARGS)),t.push(`--primary=${e.primary}`)),e.secondary&&(vn(e.secondary)||(s.error(`Invalid secondary color format: ${e.secondary}. Expected hex format like #0BF1C3`),process.exit(c.INVALID_ARGS)),t.push(`--secondary=${e.secondary}`)),t.length===0&&(s.warn("No colors specified. Usage: nebutra brand palette --primary=#HEX --secondary=#HEX"),s.info("Example: nebutra brand palette --primary=#0033FE --secondary=#0BF1C3"),process.exit(c.INVALID_ARGS)),s.info(`Generating color palette: ${t.join(" ")}`);let n=await x({command:"node",args:["scripts/generate-palette.mjs",...t],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Generate brand palette"});De(n,e.format,"brand:palette"),n.exitCode!==0&&process.exit(n.exitCode),e.dryRun||s.success("Color palette generated successfully")}async function Jo(e){s.info("Syncing brand tokens across packages...");let t=await _("brand:sync",{dryRun:e.dryRun,interactive:e.interactive??!0});De(t,e.format,"brand:sync"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand tokens synced successfully")}async function Qo(e){s.info("Verifying brand token consistency...");let t=await _("brand:verify-tokens",{dryRun:e.dryRun,interactive:e.interactive??!0});De(t,e.format,"brand:verify-tokens"),t.exitCode!==0&&(s.error("Brand token verification failed. There are inconsistencies in your design tokens."),process.exit(t.exitCode)),e.dryRun||s.success("All brand tokens are consistent")}function wn(e){let t=e.command("brand").description("Manage Nebutra brand system and design tokens");t.command("init").description("Initialize brand system configuration").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await zo({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("apply").description("Apply brand configuration to the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Yo({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("palette").description("Generate brand color palette from custom colors").option("--primary <hex>","Primary brand color (hex format, e.g. #0033FE)").option("--secondary <hex>","Secondary brand color (hex format, e.g. #0BF1C3)").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Ho({primary:n.primary,secondary:n.secondary,dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("sync").description("Synchronize brand tokens across all packages").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Jo({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("verify").description("Verify brand token consistency across the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Qo({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}import ue from"picocolors";var Xo=process.env.NEBUTRA_API_URL||"http://localhost:3001/api";async function T(e,t={}){let n=`${Xo}${e}`,r=await fetch(n,{...t,headers:{"Content-Type":"application/json",Authorization:`Bearer ${process.env.NEBUTRA_ADMIN_TOKEN||""}`,...t.headers}});if(!r.ok){let o=await r.text().catch(()=>r.statusText);throw new Error(`API Error (${r.status}): ${o}`)}return r.json()}function Zo(e){let t={dau_retention:.25,engagement_rate:.25,content_velocity:.2,response_time:.15,member_retention:.15},n={},r=0;for(let[o,a]of Object.entries(t)){let i=Math.min(100,(e[o]||0)*100);n[o]=i,r+=i*a}return{score:Math.round(r),breakdown:n}}async function es(e){try{s.info("Fetching showcase submissions...");let t=new URLSearchParams;e.status&&t.append("status",e.status),e.category&&t.append("category",e.category);let n=await T(`/community/showcase?${t.toString()}`);if(!n.submissions||n.submissions.length===0)return s.warn("No showcase submissions found"),c.SUCCESS;s.info(`
|
|
114
200
|
Showcase Submissions (${n.submissions.length})
|
|
115
|
-
`);for(let r of n.submissions);return c.SUCCESS}catch(t){return s.error(`Failed to list showcase: ${t.message}`),c.ERROR}}async function
|
|
201
|
+
`);for(let r of n.submissions);return c.SUCCESS}catch(t){return s.error(`Failed to list showcase: ${t.message}`),c.ERROR}}async function ts(e,t){try{if(!t.yes)return s.warn(`Approve showcase submission ${e}? Use --yes to confirm`),c.SUCCESS;s.info(`Approving showcase submission ${e}...`);let n=await T(`/community/showcase/${e}/approve`,{method:"POST"});return s.success(`Approved: ${n.title}`),c.SUCCESS}catch(n){return s.error(`Failed to approve showcase: ${n.message}`),c.ERROR}}async function ns(e,t){try{if(!t.yes)return s.warn(`Reject showcase submission ${e}? Use --yes to confirm`),c.SUCCESS;let n=t.reason||"Does not meet guidelines";s.info(`Rejecting showcase submission ${e}...`);let r=await T(`/community/showcase/${e}/reject`,{method:"POST",body:JSON.stringify({reason:n})});return s.success(`Rejected: ${r.title}`),c.SUCCESS}catch(n){return s.error(`Failed to reject showcase: ${n.message}`),c.ERROR}}async function rs(e,t){try{if(!t.yes)return s.warn(`Mark showcase as featured: ${e}? Use --yes to confirm`),c.SUCCESS;s.info(`Featuring showcase submission ${e}...`);let n=await T(`/community/showcase/${e}/feature`,{method:"POST"});return s.success(`Featured: ${n.title}`),c.SUCCESS}catch(n){return s.error(`Failed to feature showcase: ${n.message}`),c.ERROR}}async function os(e){try{s.info("Fetching showcase statistics...");let t=await T("/community/showcase/stats");return s.info(`
|
|
116
202
|
Showcase Statistics
|
|
117
|
-
`),c.SUCCESS}catch(t){return s.error(`Failed to fetch showcase stats: ${t.message}`),c.ERROR}}async function
|
|
203
|
+
`),c.SUCCESS}catch(t){return s.error(`Failed to fetch showcase stats: ${t.message}`),c.ERROR}}async function ss(e){try{s.info("Fetching community content...");let t=new URLSearchParams;e.status&&t.append("status",e.status),e.type&&t.append("type",e.type);let n=await T(`/community/content?${t.toString()}`);if(!n.items||n.items.length===0)return s.warn("No content found"),c.SUCCESS;s.info(`
|
|
118
204
|
Community Content (${n.items.length})
|
|
119
|
-
`);for(let r of n.items);return c.SUCCESS}catch(t){return s.error(`Failed to list content: ${t.message}`),c.ERROR}}async function
|
|
205
|
+
`);for(let r of n.items);return c.SUCCESS}catch(t){return s.error(`Failed to list content: ${t.message}`),c.ERROR}}async function as(e,t){try{let n=["approve","flag","archive","delete"];if(!n.includes(t.action))return s.error(`Invalid action. Must be one of: ${n.join(", ")}`),c.ERROR;if(!t.yes)return s.warn(`${t.action} content ${e}? Use --yes to confirm`),c.SUCCESS;s.info(`Moderating content ${e} (${t.action})...`);let r=await T(`/community/content/${e}/moderate`,{method:"POST",body:JSON.stringify({action:t.action})});return s.success(`Content ${t.action}d: ${r.title}`),c.SUCCESS}catch(n){return s.error(`Failed to moderate content: ${n.message}`),c.ERROR}}async function is(e,t){try{if(!t.yes)return s.warn(`Publish content ${e}? Use --yes to confirm`),c.SUCCESS;s.info(`Publishing content ${e}...`);let n=await T(`/community/content/${e}/publish`,{method:"POST"});return s.success(`Published: ${n.title}`),c.SUCCESS}catch(n){return s.error(`Failed to publish content: ${n.message}`),c.ERROR}}async function cs(e){try{s.info("Fetching content analytics...");let t=await T("/community/content/analytics");s.info(`
|
|
120
206
|
Content Performance Metrics
|
|
121
|
-
`);for(let n of t.topContent.slice(0,5));return e.format,c.SUCCESS}catch(t){return s.error(`Failed to fetch analytics: ${t.message}`),c.ERROR}}async function
|
|
207
|
+
`);for(let n of t.topContent.slice(0,5));return e.format,c.SUCCESS}catch(t){return s.error(`Failed to fetch analytics: ${t.message}`),c.ERROR}}async function ls(e){try{s.info("Fetching community members...");let t=new URLSearchParams;e.role&&t.append("role",e.role),e.sort&&t.append("sort",e.sort);let n=await T(`/community/members?${t.toString()}`);if(!n.members||n.members.length===0)return s.warn("No members found"),c.SUCCESS;s.info(`
|
|
122
208
|
Community Members (${n.members.length})
|
|
123
|
-
`);for(let r of n.members){let a={owner:
|
|
209
|
+
`);for(let r of n.members){let a={owner:ue.red,admin:ue.yellow,moderator:ue.blue,member:ue.gray}[String(r.role)]??ue.gray}return c.SUCCESS}catch(t){return s.error(`Failed to list members: ${t.message}`),c.ERROR}}async function ds(e,t){try{s.info(`Fetching profile for ${e}...`);let n=await T(`/community/members/${e}`);return s.info(`
|
|
124
210
|
Member Profile
|
|
125
|
-
`),c.SUCCESS}catch(n){return s.error(`Failed to fetch member profile: ${n.message}`),c.ERROR}}async function
|
|
211
|
+
`),c.SUCCESS}catch(n){return s.error(`Failed to fetch member profile: ${n.message}`),c.ERROR}}async function us(e,t,n){try{let r=["member","moderator","admin","owner"];if(!r.includes(t))return s.error(`Invalid role. Must be one of: ${r.join(", ")}`),c.ERROR;if(!n.yes)return s.warn(`Change ${e} role to ${t}? Use --yes to confirm`),c.SUCCESS;s.info("Updating member role...");let o=await T(`/community/members/${e}/role`,{method:"POST",body:JSON.stringify({role:t})});return s.success(`Updated ${o.name} \u2192 ${ue.bold(t)}`),c.SUCCESS}catch(r){return s.error(`Failed to update member role: ${r.message}`),c.ERROR}}async function ms(e,t){try{s.info(`Sending invite to ${e}...`);let n=await T("/community/members/invite",{method:"POST",body:JSON.stringify({email:e})});return s.success(`Invite sent to ${e}`),c.SUCCESS}catch(n){return s.error(`Failed to send invite: ${n.message}`),c.ERROR}}async function ps(e,t){try{if(!t.yes)return s.warn(`Ban member ${e}? Use --yes to confirm`),c.SUCCESS;let n=t.reason||"Violation of community guidelines";s.info(`Banning member ${e}...`);let r=await T(`/community/members/${e}/ban`,{method:"POST",body:JSON.stringify({reason:n})});return s.success(`Banned: ${r.name}`),c.SUCCESS}catch(n){return s.error(`Failed to ban member: ${n.message}`),c.ERROR}}async function fs(e){try{s.info("Computing community health score...");let t=e.period||"30d",n=await T(`/community/health?period=${t}`),{score:r,breakdown:o}=Zo(n.metrics);s.info(`
|
|
126
212
|
Community Health Report (${t})
|
|
127
|
-
`);for(let[a,i]of Object.entries(o)){let l=a.replace(/_/g," ").toLowerCase(),u="\u2588".repeat(Math.round(i/5))+"\u2591".repeat(20-Math.round(i/5))}return e.format,c.SUCCESS}catch(t){return s.error(`Failed to compute health: ${t.message}`),c.ERROR}}async function
|
|
213
|
+
`);for(let[a,i]of Object.entries(o)){let l=a.replace(/_/g," ").toLowerCase(),u="\u2588".repeat(Math.round(i/5))+"\u2591".repeat(20-Math.round(i/5))}return e.format,c.SUCCESS}catch(t){return s.error(`Failed to compute health: ${t.message}`),c.ERROR}}async function gs(e){try{s.info("Fetching moderation queue...");let t=await T("/community/moderation/queue");if(!t.items||t.items.length===0)return s.success("No pending moderation items"),c.SUCCESS;s.info(`
|
|
128
214
|
Moderation Queue (${t.items.length} pending)
|
|
129
|
-
`);for(let n of t.items){let o={low:
|
|
215
|
+
`);for(let n of t.items){let o={low:ue.gray("\u25CF"),medium:ue.yellow("\u25CF"),high:ue.red("\u25CF")}[String(n.severity)]??"\u25CF"}return c.SUCCESS}catch(t){return s.error(`Failed to fetch queue: ${t.message}`),c.ERROR}}async function ys(e){try{s.info("Running AI-assisted moderation sweep..."),e.dryRun&&s.warn("DRY RUN MODE - no changes will be made");let t=await T("/community/moderation/auto",{method:"POST",body:JSON.stringify({dryRun:e.dryRun||!1})});return s.info(`
|
|
130
216
|
Moderation Results
|
|
131
|
-
`),e.dryRun?s.info("DRY RUN COMPLETE - no changes committed"):s.success("Moderation sweep complete"),c.SUCCESS}catch(t){return s.error(`Failed to run moderation: ${t.message}`),c.ERROR}}function
|
|
217
|
+
`),e.dryRun?s.info("DRY RUN COMPLETE - no changes committed"):s.success("Moderation sweep complete"),c.SUCCESS}catch(t){return s.error(`Failed to run moderation: ${t.message}`),c.ERROR}}function Rn(e){let t=e.command("community").description("Manage Nebutra-Sailor community dimension"),n=t.command("showcase").description("Project showcase management");n.command("list").description("List submitted projects").option("--status <status>","Filter by status (pending|approved|rejected|featured)").option("--category <category>","Filter by category").action(i=>es(i).then(process.exit)),n.command("approve <id>").description("Approve a showcase submission").option("--yes","Skip confirmation").action((i,l)=>ts(i,l).then(process.exit)),n.command("reject <id>").description("Reject a showcase submission").option("--reason <text>","Rejection reason").option("--yes","Skip confirmation").action((i,l)=>ns(i,l).then(process.exit)),n.command("feature <id>").description("Mark as featured").option("--yes","Skip confirmation").action((i,l)=>rs(i,l).then(process.exit)),n.command("stats").description("Showcase engagement metrics").action(i=>os(i).then(process.exit));let r=t.command("content").description("Content moderation pipeline");r.command("list").description("List community content").option("--status <status>","Filter by status (draft|published|flagged|archived)").option("--type <type>","Filter by type (article|discussion|announcement)").action(i=>ss(i).then(process.exit)),r.command("moderate <id>").description("Moderate content").option("--action <action>","Action (approve|flag|archive|delete)").option("--yes","Skip confirmation").action((i,l)=>as(i,l).then(process.exit)),r.command("publish <id>").description("Publish approved content").option("--yes","Skip confirmation").action((i,l)=>is(i,l).then(process.exit)),r.command("analytics").description("Content performance metrics").option("--format <format>","Output format (json|text)").action(i=>cs(i).then(process.exit));let o=t.command("members").description("Member management");o.command("list").description("List members").option("--role <role>","Filter by role (member|admin|owner|moderator)").option("--sort <sort>","Sort by (engagement|joined|name)").action(i=>ls(i).then(process.exit)),o.command("profile <userId>").description("View member profile + activity").action((i,l)=>ds(i,l).then(process.exit)),o.command("role <userId> <role>").description("Update member role").option("--yes","Skip confirmation").action((i,l,u)=>us(i,l,u).then(process.exit)),o.command("invite <email>").description("Invite to community").action((i,l)=>ms(i,l).then(process.exit)),o.command("ban <userId>").description("Ban member").option("--reason <text>","Ban reason").option("--yes","Skip confirmation").action((i,l)=>ps(i,l).then(process.exit)),t.command("health").description("Community health dashboard").option("--period <period>","Time period (7d|30d|90d)","30d").option("--format <format>","Output format (json|text)").action(i=>fs(i).then(process.exit));let a=t.command("moderate").description("AI-assisted moderation queue");a.command("queue").description("Show pending moderation items").action(i=>gs(i).then(process.exit)),a.command("auto").description("Run AI moderation sweep").option("--dry-run","Preview without committing").action(i=>ys(i).then(process.exit))}import{appendFileSync as hs,existsSync as bs,readFileSync as vs,writeFileSync as ws}from"fs";import{homedir as Le}from"os";import{join as _e}from"path";import*as K from"@clack/prompts";import G from"picocolors";var tt=["init","add","create","mcp","schema","brand","i18n","infra","env","license","ai","auth","billing","stats","db","generate","preset","dev","test","admin","community","growth","ecosystem","services","search","secrets","completions","doctor"],Sn={init:"Initialize a Nebutra project and create nebutra.config.json",add:"Add a registry-backed platform feature or external UI component",create:"Scaffold a topology-first Nebutra Sailor project",mcp:"Start the Nebutra MCP server for AI agents and editors",completions:"Generate shell completions",doctor:"Check your Nebutra project setup",schema:"Show command schema and argument documentation",brand:"Manage governed brand tokens and palettes",i18n:"Manage localization files and multilingual product copy",infra:"Manage local infrastructure services",env:"Validate and manage environment variables",license:"Manage Nebutra Sailor commercial license activation and status",db:"Database migration and management",generate:"Scaffold apps, modules, and code",preset:"List and apply SaaS presets",dev:"Start development server",test:"Run unit and E2E tests",ai:"AI provider and gateway routing configuration",auth:"Authentication setup",billing:"Billing and subscription management",stats:"Project statistics and health checks",admin:"Platform administration",community:"Community health and showcase",growth:"Growth analytics and insights",ecosystem:"Template marketplace, ideas, showcase, and ecosystem sync",services:"Microservice management",search:"Search index management",secrets:"Encrypted secrets management"},Rs=["--help","--version","--21st","--v0","--stdio","--format","--yes","--no-interactive","--no-color","--verbose","--quiet","--dry-run","--if-not-exists","--provider","-p","--pm","--region","--orm","--db","--auth","--social-login","--payment","--ai","--deploy","--docs","--email","--storage","--monitoring","--analytics","--sms","--queue","--search","--cache","--notifications","--webhooks","--cms","--feature-flags","--captcha","--mcp","--metering","--billing-mode","--idp","--i18n","--no-i18n","--no-install","--no-git","--json"],Ot=["-p","--pm","--region","--orm","--db","--auth","--social-login","--payment","--ai","--deploy","--docs","--email","--storage","--monitoring","--analytics","--sms","--queue","--search","--cache","--notifications","--webhooks","--cms","--feature-flags","--captcha","--mcp","--metering","--billing-mode","--idp","--i18n","--no-i18n","--no-install","--no-git","--dry-run","--json","--yes"],Ss={"-p":v.pm,"--pm":v.pm,"--region":v.region,"--orm":v.orm,"--db":v.db,"--auth":v.auth,"--payment":v.payment,"--deploy":v.deploy,"--docs":v.docs,"--email":v.email,"--storage":v.storage,"--monitoring":v.monitoring,"--analytics":v.analytics,"--sms":v.sms,"--queue":v.queue,"--search":v.search,"--cache":v.cache,"--notifications":v.notifications,"--webhooks":v.webhooks,"--cms":v.cms,"--feature-flags":v.featureFlags,"--captcha":v.captcha,"--mcp":v.mcp,"--metering":v.metering,"--billing-mode":v.billingMode,"--idp":v.idp},Cs=Object.entries(Ss).map(([e,t])=>` ${e})
|
|
132
218
|
COMPREPLY=( $(compgen -W "${t.join(" ")}" -- \${cur}) )
|
|
133
219
|
return 0
|
|
134
220
|
;;`).join(`
|
|
135
|
-
`),
|
|
136
|
-
`),
|
|
137
|
-
`),
|
|
138
|
-
`);function
|
|
221
|
+
`),Es=Ot.map(e=>` "${e}"`).join(`
|
|
222
|
+
`),ks=tt.map(e=>`complete -c nebutra -n "__fish_use_subcommand_only" -f -a "${e}" -d "${Sn[e]}"`).join(`
|
|
223
|
+
`),Os=Ot.map(e=>`complete -c nebutra -n "__fish_seen_subcommand_from create" -f -a "${e}" -d "create-sailor option"`).join(`
|
|
224
|
+
`);function Cn(){return`#!/usr/bin/env bash
|
|
139
225
|
|
|
140
226
|
_nebutra_completions() {
|
|
141
227
|
local cur prev opts cmd
|
|
@@ -144,13 +230,13 @@ _nebutra_completions() {
|
|
|
144
230
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
145
231
|
|
|
146
232
|
# Subcommands
|
|
147
|
-
local subcommands="${
|
|
233
|
+
local subcommands="${tt.join(" ")}"
|
|
148
234
|
|
|
149
235
|
# Flags
|
|
150
|
-
local flags="${
|
|
236
|
+
local flags="${Rs.join(" ")}"
|
|
151
237
|
|
|
152
238
|
case "\${prev}" in
|
|
153
|
-
${
|
|
239
|
+
${Cs}
|
|
154
240
|
esac
|
|
155
241
|
|
|
156
242
|
if [[ \${cur} == -* ]]; then
|
|
@@ -164,7 +250,7 @@ ${Do}
|
|
|
164
250
|
COMPREPLY=( $(compgen -W "--21st --v0 --provider --dry-run --yes --if-not-exists" -- \${cur}) )
|
|
165
251
|
elif [[ "\${prev}" == "create" ]]; then
|
|
166
252
|
# create delegates to create-sailor
|
|
167
|
-
COMPREPLY=( $(compgen -W "${
|
|
253
|
+
COMPREPLY=( $(compgen -W "${Ot.join(" ")}" -- \${cur}) )
|
|
168
254
|
elif [[ "\${prev}" == "completions" ]]; then
|
|
169
255
|
# completions command takes shell type
|
|
170
256
|
COMPREPLY=( $(compgen -W "bash zsh fish install" -- \${cur}) )
|
|
@@ -174,11 +260,11 @@ ${Do}
|
|
|
174
260
|
}
|
|
175
261
|
|
|
176
262
|
complete -o bashdefault -o default -o nospace -F _nebutra_completions nebutra
|
|
177
|
-
`}function
|
|
263
|
+
`}function En(){return`#compdef nebutra
|
|
178
264
|
|
|
179
265
|
_nebutra() {
|
|
180
266
|
local -a commands=(
|
|
181
|
-
${
|
|
267
|
+
${tt.map(e=>`"${e}:${Sn[e]}"`).join(`
|
|
182
268
|
`)}
|
|
183
269
|
)
|
|
184
270
|
|
|
@@ -195,7 +281,7 @@ _nebutra() {
|
|
|
195
281
|
)
|
|
196
282
|
|
|
197
283
|
local -a create_flags=(
|
|
198
|
-
${
|
|
284
|
+
${Es}
|
|
199
285
|
)
|
|
200
286
|
|
|
201
287
|
local -a mcp_flags=(
|
|
@@ -236,12 +322,12 @@ ${Lo}
|
|
|
236
322
|
}
|
|
237
323
|
|
|
238
324
|
_nebutra
|
|
239
|
-
`}function
|
|
325
|
+
`}function kn(){return`# Fish shell completions for nebutra
|
|
240
326
|
|
|
241
|
-
complete -c nebutra -f -n "__fish_seen_subcommand_from; and not __fish_seen_subcommand_from ${
|
|
327
|
+
complete -c nebutra -f -n "__fish_seen_subcommand_from; and not __fish_seen_subcommand_from ${tt.join(" ")}" -d "Nebutra governance CLI"
|
|
242
328
|
|
|
243
329
|
# Subcommands
|
|
244
|
-
${
|
|
330
|
+
${ks}
|
|
245
331
|
|
|
246
332
|
# Flags
|
|
247
333
|
complete -c nebutra -f -a "--help" -d "Show help message"
|
|
@@ -253,7 +339,7 @@ complete -c nebutra -n "__fish_seen_subcommand_from add" -f -a "--v0" -d "Fetch
|
|
|
253
339
|
complete -c nebutra -n "__fish_seen_subcommand_from add" -f -a "--provider" -d "Specify a backend provider"
|
|
254
340
|
|
|
255
341
|
# create command flags
|
|
256
|
-
${
|
|
342
|
+
${Os}
|
|
257
343
|
|
|
258
344
|
# mcp command flags
|
|
259
345
|
complete -c nebutra -n "__fish_seen_subcommand_from mcp" -f -a "--stdio" -d "Use stdio transport (default)"
|
|
@@ -268,25 +354,25 @@ complete -c nebutra -n "__fish_seen_subcommand_from completions" -f -a "install"
|
|
|
268
354
|
complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--all" -d "Show full schema for all commands"
|
|
269
355
|
complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--list" -d "List all available command names"
|
|
270
356
|
complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--exit-codes" -d "Show exit codes reference"
|
|
271
|
-
`}function
|
|
357
|
+
`}function On(e){let n={bash:Cn,zsh:En,fish:kn}[e.toLowerCase()];n||(console.error(G.red(`Error: Unknown shell '${e}'. Supported: bash, zsh, fish`)),process.exit(1)),console.log(n())}async function xs(){let t=(process.env.SHELL||"/bin/bash").split("/").pop()||"bash";K.intro(G.bgCyan(G.black(" nebutra completions install ")));let n=K.spinner();n.start(G.cyan(`Detecting shell: ${t}...`));try{let r=null,o=null,a=null;if(t==="bash"?(r=_e(Le(),".bashrc"),o=_e(Le(),".bash_completion.d"),a="source ~/.bash_completion.d/nebutra"):t==="zsh"?(r=_e(Le(),".zshrc"),o=_e(Le(),".zsh/completions"),a="fpath=(~/.zsh/completions $fpath)"):t==="fish"?(o=_e(Le(),".config/fish/completions"),a="# Completions installed to ~/.config/fish/completions/nebutra.fish"):(n.stop(),K.log.warn(G.yellow(`Warning: Unsupported shell '${t}'. Please install completions manually.`)),On("bash"),K.outro(G.cyan("Run 'nebutra completions bash' (or zsh/fish) to generate completions for your shell.")),process.exit(0)),o){(await import("fs")).mkdirSync(o,{recursive:!0});let l=_e(o,t==="fish"?"nebutra.fish":"nebutra"),u=t==="bash"?Cn():t==="zsh"?En():kn();ws(l,u),n.stop(G.green(`Completion script installed to ${l}`))}r&&a&&t!=="fish"&&(!bs(r)||!vs(r,"utf8").includes("nebutra"))&&(hs(r,(t==="bash"?`
|
|
272
358
|
# nebutra completions
|
|
273
359
|
[[ -f ~/.bash_completion.d/nebutra ]] && source ~/.bash_completion.d/nebutra`:`
|
|
274
360
|
# nebutra completions
|
|
275
361
|
fpath=(~/.zsh/completions $fpath)
|
|
276
362
|
autoload -Uz compinit && compinit`)+`
|
|
277
|
-
`),K.log.info(G.green(`Added nebutra completions to ${r}`))),K.log.info(G.cyan("Completions installed! Restart your shell or run:")),K.log.info(G.dim(` source "${t==="bash"?"~/.bashrc":"~/.zshrc"}"`)),K.outro(G.green("Installation complete!"))}catch(r){n.stop(),K.log.error(G.red("Error installing completions. You can manually add them by running:")),K.log.info(G.cyan(` nebutra completions ${t} >> ~/.${t}rc`)),r instanceof Error&&console.error(G.dim(r.message)),process.exit(1)}}function
|
|
363
|
+
`),K.log.info(G.green(`Added nebutra completions to ${r}`))),K.log.info(G.cyan("Completions installed! Restart your shell or run:")),K.log.info(G.dim(` source "${t==="bash"?"~/.bashrc":"~/.zshrc"}"`)),K.outro(G.green("Installation complete!"))}catch(r){n.stop(),K.log.error(G.red("Error installing completions. You can manually add them by running:")),K.log.info(G.cyan(` nebutra completions ${t} >> ~/.${t}rc`)),r instanceof Error&&console.error(G.dim(r.message)),process.exit(1)}}function xn(e){e.command("completions [shell]").description("Generate or install shell completions").action(async t=>{!t||t==="install"?await xs():On(t)})}import{spawn as $s}from"child_process";import{dirname as As,resolve as _s}from"path";import{fileURLToPath as Ps}from"url";import*as be from"@clack/prompts";import Fe from"picocolors";var Is=As(Ps(import.meta.url));function Ns(){let e=[()=>_s(Is,"../../../create-sailor/dist/index.js")];for(let t of e)try{return t()}catch(n){s.debug("create-sailor binary resolution strategy failed",{error:n})}return"create-sailor"}function $n(e){e.command("create [dir]").description("Scaffold a new Nebutra-Sailor project").allowUnknownOption(!0).action(async(t,n,r)=>{be.intro(Fe.bgCyan(Fe.black(" nebutra create ")));try{let o=Ns(),a=t?[t]:[];if(r.args&&r.args.length>(t?1:0)){let u=r.args.slice(t?1:0);a.push(...u)}let i=be.spinner();i.start(Fe.cyan(`Launching create-sailor${t?` in ${t}`:""}...`));let l=$s(process.execPath,[o,...a],{stdio:"inherit",env:{...process.env}});l.on("close",u=>{i.stop(),u===0&&be.outro(Fe.green("Project created successfully! Run 'npm install && npm run dev' to get started.")),process.exit(u??0)}),l.on("error",u=>{i.stop(),s.error(`
|
|
278
364
|
Failed to launch create-sailor: ${u.message}`),u.code==="ENOENT"&&(s.warn(`
|
|
279
|
-
Tip: Ensure create-sailor is installed or available in your PATH.`),s.info(" Install globally: npm install -g create-sailor")),process.exit(1)})}catch(o){
|
|
280
|
-
`)}function
|
|
281
|
-
`)}function d(e,t="info"){let n;switch(t){case"error":n=
|
|
282
|
-
`)}function k(e,t){if(!(process.env.NEBUTRA_DEBUG||process.argv.includes("--debug")))return;let r=
|
|
283
|
-
`),t&&Object.keys(t).length>0&&
|
|
284
|
-
`)}function
|
|
285
|
-
`),
|
|
286
|
-
`),
|
|
287
|
-
`),
|
|
288
|
-
`)}async function
|
|
289
|
-
`)}catch{process.stdout.write(t.stdout)}}else d("Failed to check migration status","error"),process.exit(t.exitCode||1)}function
|
|
365
|
+
Tip: Ensure create-sailor is installed or available in your PATH.`),s.info(" Install globally: npm install -g create-sailor")),process.exit(1)})}catch(o){be.log.error(Fe.red("Error: Unable to start create-sailor. Ensure it is properly installed.")),o instanceof Error&&s.error(o.message),process.exit(1)}})}import*as rt from"@clack/prompts";import{stderr as ve,stdout as Ts}from"process";import ie from"picocolors";function js(){return process.env.NEBUTRA_NON_TTY||process.env.CI||process.env.GITHUB_ACTIONS?!1:process.stderr.isTTY===!0}function Us(e){if(e)return e;let t=process.env.NEBUTRA_OUTPUT_FORMAT;return t&&["json","table","plain"].includes(t)?t:js()?"plain":"json"}function Ds(e,t){if(e.length===0)return"";let n=t||Object.keys(e[0]),r={};for(let l of n){r[String(l)]=String(l).length;for(let u of e){let m=String(u[l]??"");r[String(l)]=Math.max(r[String(l)],m.length)}}let o=n.map(l=>String(l).padEnd(r[String(l)])).join(" | "),a=n.map(l=>"-".repeat(r[String(l)])).join("-+-"),i=[o,a];for(let l of e){let u=n.map(m=>String(l[m]??"").padEnd(r[String(m)])).join(" | ");i.push(u)}return i.join(`
|
|
366
|
+
`)}function Ls(e){return JSON.stringify(e)}function Fs(e){if(!Array.isArray(e))return xt(e);if(e.length===0)return"(empty)";if(typeof e[0]!="object"||e[0]===null)return xt(e);let t=Ds(e);return ie.gray(t)}function xt(e){return typeof e=="string"?e:typeof e=="number"||typeof e=="boolean"?String(e):e==null?"":Array.isArray(e)?e.length===0?"(empty list)":e.every(t=>typeof t=="string"||typeof t=="number")?e.join(", "):JSON.stringify(e,null,2):typeof e=="object"?JSON.stringify(e,null,2):String(e)}function E(e,t){let n=Us(t?.format),r=t?.stream==="stderr"?ve:Ts,o;switch(n){case"json":o=Ls(e);break;case"table":o=Fs(e);break;default:o=xt(e);break}r.write(o+`
|
|
367
|
+
`)}function d(e,t="info"){let n;switch(t){case"error":n=ie.red("\u2716");break;case"warn":n=ie.yellow("\u26A0");break;case"success":n=ie.green("\u2713");break;default:n=ie.blue("\u2139");break}let r=`${n} ${e}`;ve.write(r+`
|
|
368
|
+
`)}function k(e,t){if(!(process.env.NEBUTRA_DEBUG||process.argv.includes("--debug")))return;let r=ie.dim(`[debug] ${e}`);ve.write(r+`
|
|
369
|
+
`),t&&Object.keys(t).length>0&&ve.write(ie.dim(JSON.stringify(t,null,2))+`
|
|
370
|
+
`)}function nt(e){ve.write(`
|
|
371
|
+
`),ve.write(ie.bold(ie.cyan(e))+`
|
|
372
|
+
`),ve.write(ie.gray("-".repeat(e.length))+`
|
|
373
|
+
`),ve.write(`
|
|
374
|
+
`)}async function Ms(e){d("Generating Prisma client"+"...","info");let n=await de("generate",{dryRun:e.dryRun,interactive:!e.dryRun});n.exitCode===0?d("Prisma client generated successfully","success"):(d("Failed to generate Prisma client","error"),process.exit(n.exitCode||1))}async function $t(e,t){let n=t||{};if(e){d(`Creating migration: ${e}`,"info");let r=await de("migrate",{args:["dev","--name",e],dryRun:n.dryRun,interactive:!n.dryRun});r.exitCode===0?d(`Migration '${e}' created successfully`,"success"):(d(`Failed to create migration '${e}'`,"error"),process.exit(r.exitCode||1))}else{d("Running pending migrations...","info");let r=await de("migrate",{args:["deploy"],dryRun:n.dryRun,interactive:!n.dryRun});r.exitCode===0?d("All pending migrations completed","success"):(d("Migration deployment failed","error"),process.exit(r.exitCode||1))}}async function Gs(e){d("Pushing schema to database...","info");let t=await de("db",{args:["push"],dryRun:e.dryRun,interactive:!e.dryRun});t.exitCode===0?d("Schema pushed successfully","success"):(d("Failed to push schema","error"),process.exit(t.exitCode||1))}async function Ks(e){d("Seeding database...","info");let t=await de("db",{args:["seed"],dryRun:e.dryRun,interactive:!e.dryRun});t.exitCode===0?d("Database seeded successfully","success"):(d("Failed to seed database","error"),process.exit(t.exitCode||1))}async function qs(e){d("Launching Prisma Studio...","info");let t=await de("studio",{dryRun:e.dryRun,interactive:!0});t.exitCode!==0&&(d("Failed to launch Prisma Studio","error"),process.exit(t.exitCode||1))}async function Vs(e){let t=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!e.yes&&!e.dryRun)if(t){let r=await rt.confirm({message:"Reset database? This will delete all data.",initialValue:!1});(rt.isCancel(r)||!r)&&(d("Reset cancelled","warn"),process.exit(0))}else d("Database reset requires explicit --yes confirmation (use: nebutra db reset --yes)","error"),process.exit(1);d("Resetting database...","warn");let n=await de("migrate",{args:["reset",...e.yes?["--force"]:[]],dryRun:e.dryRun,interactive:!e.dryRun});n.exitCode===0?d("Database reset successfully","success"):(d("Failed to reset database","error"),process.exit(n.exitCode||1))}async function Ws(e){d("Checking migration status...","info");let t=await de("migrate",{args:["status"],dryRun:e.dryRun,interactive:!e.dryRun});if(t.exitCode===0){if(e.format==="json")try{let n=JSON.parse(t.stdout);process.stdout.write(JSON.stringify(n,null,2)+`
|
|
375
|
+
`)}catch{process.stdout.write(t.stdout)}}else d("Failed to check migration status","error"),process.exit(t.exitCode||1)}function An(e){e.command("db <verb> [args...]").description("Manage database schema, migrations, and state").option("--dry-run","Show what would be run without executing").option("--yes","Skip confirmations (especially for reset)").option("--format <type>","Output format: json, plain","plain").action(async(n,r,o)=>{let a=o.optsWithGlobals?.(),i={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:o.format||a?.format};try{switch(n){case"generate":await Ms(i);break;case"migrate":r.length>0?r[0]==="create"&&r[1]?await $t(r[1],i):await $t(r[0],i):await $t(void 0,i);break;case"push":await Gs(i);break;case"seed":await Ks(i);break;case"studio":await qs(i);break;case"reset":await Vs(i);break;case"status":await Ws(i);break;default:d(`Unknown db subcommand: ${n}. Valid commands: generate, migrate, migrate create, push, seed, studio, reset, status`,"error"),process.exit(1)}}catch(l){let u=l instanceof Error?l.message:String(l);d(`Database command failed: ${u}`,"error"),k("Full error",{error:l}),process.exit(1)}}).addHelpText("after",`
|
|
290
376
|
Examples:
|
|
291
377
|
nebutra db generate Generate Prisma client
|
|
292
378
|
nebutra db migrate Run all pending migrations
|
|
@@ -301,64 +387,68 @@ Flags:
|
|
|
301
387
|
--dry-run Show what would be run without executing
|
|
302
388
|
--yes Skip confirmations
|
|
303
389
|
--format <type> Output format: json, plain (default: plain)
|
|
304
|
-
`)}import*as O from"@clack/prompts";import
|
|
305
|
-
${n}`)}return t}function
|
|
306
|
-
${t}`)}return e}async function
|
|
307
|
-
`),process.exit(c.DRY_RUN_OK)}try{if(e.app&&e.preset&&(O.log.error(
|
|
308
|
-
`),process.exit(c.DRY_RUN_OK)}try{if(e.app&&e.strict&&(O.log.error(
|
|
309
|
-
`),process.exit(c.DRY_RUN_OK)}try{if(e.app&&e.fix&&(O.log.error(
|
|
310
|
-
`),process.exit(c.DRY_RUN_OK)}try{if(e.app){
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
390
|
+
`)}import*as O from"@clack/prompts";import R from"picocolors";var Pe=["landing-page","web","storybook","design-docs","sailor-docs","studio","docs","idp","mail-preview","gateway"],At=["ai-saas","marketing","dashboard","growth","enterprise"];function ot(e){let t=`@nebutra/${e}`;if(!Pe.includes(e)){let n=R.yellow(`Valid apps: ${Pe.join(", ")}`);throw new Error(`Invalid app: ${R.red(e)}
|
|
391
|
+
${n}`)}return t}function Bs(e){if(!At.includes(e)){let t=R.yellow(`Valid presets: ${At.join(", ")}`);throw new Error(`Invalid preset: ${R.red(e)}
|
|
392
|
+
${t}`)}return e}async function zs(e){if(e.dryRun){let t={mode:"dry-run",timestamp:new Date().toISOString(),command:"dev",options:{app:e.app,preset:e.preset},task:e.app?"turbo dev":e.preset?"pnpm dev":"turbo dev (all apps)"};process.stdout.write(JSON.stringify(t,null,2)+`
|
|
393
|
+
`),process.exit(c.DRY_RUN_OK)}try{if(e.app&&e.preset&&(O.log.error(R.red("Cannot specify both --app and --preset")),process.exit(c.INVALID_ARGS)),e.app){ot(e.app);let n=`@nebutra/${e.app}`;e.quiet||O.log.info(R.cyan(`Starting dev server for ${R.bold(n)}...`));let r=await fe("dev",{filter:n});process.exit(r.exitCode)}if(e.preset){Bs(e.preset),e.quiet||O.log.info(R.cyan(`Starting dev with preset ${R.bold(e.preset)}...`));let n=await _(`dev:${e.preset}`,{interactive:!0});process.exit(n.exitCode)}e.quiet||O.log.info(R.cyan("Starting dev servers for all apps..."));let t=await fe("dev");process.exit(t.exitCode)}catch(t){t instanceof Error?O.log.error(R.red(t.message)):O.log.error(R.red("Unknown error occurred")),process.exit(c.ERROR)}}async function Ys(e){if(e.dryRun){let t={mode:"dry-run",timestamp:new Date().toISOString(),command:"build",options:{app:e.app,strict:e.strict},task:e.strict?"pnpm build:strict":"pnpm build"};process.stdout.write(JSON.stringify(t,null,2)+`
|
|
394
|
+
`),process.exit(c.DRY_RUN_OK)}try{if(e.app&&e.strict&&(O.log.error(R.red("Cannot use --strict with --app filter")),process.exit(c.INVALID_ARGS)),e.app){ot(e.app);let r=`@nebutra/${e.app}`;e.quiet||O.log.info(R.cyan(`Building ${R.bold(r)}...`));let o=await fe("build",{filter:r});process.exit(o.exitCode)}let t=e.strict?"build:strict":"build";if(!e.quiet){let r=e.strict?"Running strict build (with UI governance verification)...":"Building all apps...";O.log.info(R.cyan(r))}let n=await _(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?O.log.error(R.red(t.message)):O.log.error(R.red("Unknown error occurred")),process.exit(c.ERROR)}}async function Hs(e){if(e.dryRun){let t={mode:"dry-run",timestamp:new Date().toISOString(),command:"lint",options:{app:e.app,fix:e.fix},task:e.fix?"pnpm lint:fix":"pnpm lint"};process.stdout.write(JSON.stringify(t,null,2)+`
|
|
395
|
+
`),process.exit(c.DRY_RUN_OK)}try{if(e.app&&e.fix&&(O.log.error(R.red("Cannot use --fix with --app filter (lint:fix is global only)")),process.exit(c.INVALID_ARGS)),e.app){ot(e.app);let r=`@nebutra/${e.app}`;e.quiet||O.log.info(R.cyan(`Linting ${R.bold(r)}...`));let o=await x({command:"turbo",args:["run","lint","--filter",r],interactive:!0,label:`turbo lint --filter ${r}`,dryRun:e.dryRun});process.exit(o.exitCode)}let t=e.fix?"lint:fix":"lint";if(!e.quiet){let r=e.fix?"Fixing linting issues...":"Linting codebase...";O.log.info(R.cyan(r))}let n=await _(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?O.log.error(R.red(t.message)):O.log.error(R.red("Unknown error occurred")),process.exit(c.ERROR)}}async function Js(e){if(e.dryRun){let t={mode:"dry-run",timestamp:new Date().toISOString(),command:"typecheck",options:{app:e.app},task:"turbo typecheck"};process.stdout.write(JSON.stringify(t,null,2)+`
|
|
396
|
+
`),process.exit(c.DRY_RUN_OK)}try{if(e.app){ot(e.app);let n=`@nebutra/${e.app}`;e.quiet||O.log.info(R.cyan(`Type-checking ${R.bold(n)}...`));let r=await fe("typecheck",{filter:n});process.exit(r.exitCode)}e.quiet||O.log.info(R.cyan("Type-checking all packages..."));let t=await fe("typecheck");process.exit(t.exitCode)}catch(t){t instanceof Error?O.log.error(R.red(t.message)):O.log.error(R.red("Unknown error occurred")),process.exit(c.ERROR)}}function _n(e){e.command("dev").description("Start development servers (turbo dev)").option("--app <name>",`App to run: ${Pe.join(", ")}`).option("--preset <name>",`Preset to use: ${At.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async t=>{let n=t.optsWithGlobals?t.optsWithGlobals():t;await zs({...t,quiet:n.quiet||!1,verbose:n.verbose||!1,format:n.format})}),e.command("build").description("Build all apps (pnpm build or turbo build)").option("--app <name>",`App to build: ${Pe.join(", ")}`).option("--strict","Run strict build with UI governance verification (pnpm build:strict)").option("--dry-run","Preview what would run (exit code 10)").action(async t=>{let n=t.optsWithGlobals?t.optsWithGlobals():t;await Ys({...t,quiet:n.quiet||!1,verbose:n.verbose||!1,format:n.format})}),e.command("lint").description("Lint with Biome (pnpm lint or pnpm lint:fix)").option("--app <name>",`App to lint: ${Pe.join(", ")}`).option("--fix","Fix linting issues automatically").option("--dry-run","Preview what would run (exit code 10)").action(async t=>{let n=t.optsWithGlobals?t.optsWithGlobals():t;await Hs({...t,quiet:n.quiet||!1,verbose:n.verbose||!1,format:n.format})}),e.command("typecheck").description("Type-check with TypeScript (turbo typecheck)").option("--app <name>",`App to typecheck: ${Pe.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async t=>{let n=t.optsWithGlobals?t.optsWithGlobals():t;await Js({...t,quiet:n.quiet||!1,verbose:n.verbose||!1,format:n.format})})}import Pn from"fs";import In from"path";import _t from"picocolors";var Pt=["smoke","golden","sleptons"],Qs={smoke:{config:"e2e/playwright.config.ts",suiteDir:"e2e/smoke"},golden:{config:"e2e/playwright.golden.config.ts",suiteDir:"e2e/golden"},sleptons:{config:"e2e/playwright.sleptons.config.ts",suiteDir:"e2e/sleptons"}};async function Xs(e,t={}){Pt.includes(e)||(s.error(`Invalid suite: ${_t.red(e)}. Valid: ${Pt.join(", ")}`),process.exit(c.INVALID_ARGS));let n=h(),{config:r,suiteDir:o}=Qs[e],a=In.join(n,r),i=In.join(n,o);(!Pn.existsSync(i)||!Pn.existsSync(a))&&(s.error(`Suite ${_t.red(e)} not found at ${o}.
|
|
397
|
+
Expected config: ${r}
|
|
398
|
+
Remediation: scaffold the suite (analogous to 'nebutra workflow init <provider>')
|
|
399
|
+
or restore the directory from git.`),process.exit(c.NOT_FOUND)),t.dryRun&&(process.stdout.write(JSON.stringify({mode:"dry-run",command:`e2e ${e}`,task:`pnpm exec playwright test --config ${r}`},null,2)+`
|
|
400
|
+
`),process.exit(c.DRY_RUN_OK)),t.quiet||s.info(`Running Playwright suite: ${_t.bold(e)}`);let l=await x({command:"pnpm",args:["exec","playwright","test","--config",r],interactive:!0,label:`playwright ${e}`});process.exit(l.exitCode)}function Nn(e){e.command("e2e <suite>").description(`Run a Playwright suite (${Pt.join(" | ")})`).option("--dry-run","Preview the command that would run (exit code 10)").action(async(t,n)=>{let r=n.optsWithGlobals?n.optsWithGlobals():n;await Xs(t,{dryRun:n.dryRun||!1,quiet:r.quiet||!1})})}import f from"picocolors";var Zs=process.env.NEBUTRA_ECOSYSTEM_URL||"https://api.nebutra.com/ecosystem",Tn=process.env.NEBUTRA_ECOSYSTEM_TOKEN||"";async function $(e,t={}){let{method:n="GET",body:r,dryRun:o=!1,token:a=Tn}=t,i=`${Zs}${e}`;if(o)return s.info(`[DRY RUN] ${n} ${i}`),r&&s.info(`Body: ${JSON.stringify(r,null,2)}`),{ok:!0,status:200,json:async()=>({dryRun:!0})};let l={"Content-Type":"application/json"};a&&(l.Authorization=`Bearer ${a}`);try{let u=await fetch(i,{method:n,headers:l,...r&&{body:JSON.stringify(r)}});if(!u.ok){let m=await u.text();throw new Error(`${u.status}: ${m}`)}return u}catch(u){throw s.error(`Ecosystem API error: ${u}`),u}}function ea(e={}){let t=[{name:"README.md exists",pass:!0},{name:"License file exists",pass:!0},{name:"package.json has description",pass:!0},{name:"No security vulnerabilities",pass:!0},{name:"TypeScript passes",pass:!0}];return e.skipChecks?{valid:!0,checks:t}:{valid:t.every(r=>r.pass),checks:t}}async function ta(e){s.info(f.cyan(`\u{1F4CA} Ecosystem Status Dashboard
|
|
401
|
+
`));try{let n=await(await $("/status")).json();return e.format,c.SUCCESS}catch(t){return s.error(`Failed to fetch ecosystem status: ${t}`),c.ERROR}}async function na(e){s.info(f.cyan(`\u{1F517} Connect to Nebutra Ecosystem
|
|
402
|
+
`));let t=e.token||Tn;!t&&!e.yes&&(s.warn(`No NEBUTRA_ECOSYSTEM_TOKEN found. Starting OAuth flow...
|
|
403
|
+
`),s.info("Visit: "+f.blue("https://nebutra.com/oauth/authorize?scope=ecosystem")),s.info("Then paste your token below:"),t="token_example_"+Math.random().toString(36).slice(2));let n=e.org||"org_default";try{let o=await(await $("/projects/connect",{method:"POST",body:{projectName:"My Project",repository:"https://github.com/user/project",orgId:n},dryRun:e.dryRun,token:t})).json();return e.dryRun?(s.info(f.green(`\u2713 [DRY RUN] Would connect project to ecosystem
|
|
314
404
|
`)),c.SUCCESS):(s.success(`\u2713 Connected to Nebutra Ecosystem
|
|
315
405
|
`),s.info(`Project ID: ${f.cyan(o.projectId)}`),s.info(`Organization: ${f.cyan(n)}`),s.info(`Ecosystem Config: ${f.cyan(".nebutra/ecosystem.json")}
|
|
316
|
-
`),s.info(`Next steps: Run ${f.yellow("nebutra ecosystem publish")} to share your project`),c.SUCCESS)}catch(r){return s.error(`Connection failed: ${r}`),c.ERROR}}async function
|
|
317
|
-
`));let t=e.tag||"latest",n=e.visibility||"public",r=
|
|
406
|
+
`),s.info(`Next steps: Run ${f.yellow("nebutra ecosystem publish")} to share your project`),c.SUCCESS)}catch(r){return s.error(`Connection failed: ${r}`),c.ERROR}}async function ra(e){s.info(f.cyan(`\u{1F4E4} Publish to Ecosystem Marketplace
|
|
407
|
+
`));let t=e.tag||"latest",n=e.visibility||"public",r=ea();s.info(`${f.bold("Pre-Publish Checklist")}
|
|
318
408
|
`);for(let o of r.checks){let a=o.pass?f.green("\u2713"):f.red("\u2717")}if(!r.valid)return s.error(`
|
|
319
|
-
Fix the checks above before publishing`),c.ERROR;if(n==="public"&&!e.yes&&(s.info(`This project will be ${f.yellow("publicly listed")} in the ecosystem`),s.info(`Continue? Run with ${f.yellow("--yes")} to skip confirmation`),!e.yes))return c.CANCELLED;try{let a=await(await
|
|
409
|
+
Fix the checks above before publishing`),c.ERROR;if(n==="public"&&!e.yes&&(s.info(`This project will be ${f.yellow("publicly listed")} in the ecosystem`),s.info(`Continue? Run with ${f.yellow("--yes")} to skip confirmation`),!e.yes))return c.CANCELLED;try{let a=await(await $("/projects/publish",{method:"POST",body:{projectName:"My Template",description:"A production-ready SaaS template",repository:"https://github.com/user/project",tags:["saas","nextjs","ai"],tag:t,visibility:n},dryRun:e.dryRun})).json();return e.dryRun?(s.info(f.green(`\u2713 [DRY RUN] Would publish to ecosystem
|
|
320
410
|
`)),c.SUCCESS):(s.success(`\u2713 Published successfully!
|
|
321
411
|
`),s.info(`Marketplace URL: ${f.blue(`https://nebutra.com/templates/${a.projectId}`)}`),s.info(`Visibility: ${n}`),s.info(`Tag: ${t}
|
|
322
|
-
`),c.SUCCESS)}catch(o){return s.error(`Publish failed: ${o}`),c.ERROR}}async function
|
|
323
|
-
`));try{let n=await(await
|
|
324
|
-
`));try{let r=await(await
|
|
325
|
-
`));try{let n=await(await
|
|
326
|
-
`));try{let n=await
|
|
412
|
+
`),c.SUCCESS)}catch(o){return s.error(`Publish failed: ${o}`),c.ERROR}}async function oa(e){s.info(f.cyan(`\u{1F4DA} Template Marketplace
|
|
413
|
+
`));try{let n=await(await $(`/templates?category=${e.category||""}&sort=${e.sort||"downloads"}`)).json();return e.format==="json"||s.info(`Run ${f.yellow("nebutra ecosystem templates info <id>")} for details`),c.SUCCESS}catch(t){return s.error(`Failed to list templates: ${t}`),c.ERROR}}async function sa(e,t){s.info(f.cyan(`\u{1F50D} Searching templates for "${e}"
|
|
414
|
+
`));try{let r=await(await $(`/templates/search?q=${encodeURIComponent(e)}`)).json();return t.format,c.SUCCESS}catch(n){return s.error(`Search failed: ${n}`),c.ERROR}}async function aa(e){s.info(f.cyan(`\u{1F4D6} Template Details
|
|
415
|
+
`));try{let n=await(await $(`/templates/${e}`)).json();return s.info(`Install: ${f.yellow("nebutra ecosystem templates install multi-tenant-saas")}`),c.SUCCESS}catch(t){return s.error(`Failed to fetch template info: ${t}`),c.ERROR}}async function ia(e,t){s.info(f.cyan(`\u2B07\uFE0F Installing template "${e}"
|
|
416
|
+
`));try{let n=await $(`/templates/${e}/install`,{method:"POST",body:{projectName:"my-saas"},dryRun:t.dryRun});return t.dryRun?(s.info(f.green(`\u2713 [DRY RUN] Would install template
|
|
327
417
|
`)),c.SUCCESS):(s.success(`\u2713 Template installed!
|
|
328
|
-
`),s.info("Project: ./my-saas"),s.info(`Next: ${f.yellow("cd my-saas && pnpm install && pnpm dev")}`),c.SUCCESS)}catch(n){return s.error(`Installation failed: ${n}`),c.ERROR}}async function
|
|
329
|
-
`));try{let n=await(await
|
|
330
|
-
`)),!e.title||!e.description)return s.error("Missing required fields: --title and --description"),c.INVALID_ARGS;try{let n=await(await
|
|
418
|
+
`),s.info("Project: ./my-saas"),s.info(`Next: ${f.yellow("cd my-saas && pnpm install && pnpm dev")}`),c.SUCCESS)}catch(n){return s.error(`Installation failed: ${n}`),c.ERROR}}async function ca(e){s.info(f.cyan(`\u{1F4A1} Ideas Marketplace
|
|
419
|
+
`));try{let n=await(await $(`/ideas?status=${e.status||""}&sort=${e.sort||"recent"}`)).json();return e.format==="json"||(s.info(`Vote: ${f.yellow("nebutra ecosystem ideas vote <id>")}`),s.info(`Claim: ${f.yellow("nebutra ecosystem ideas claim <id>")}`)),c.SUCCESS}catch(t){return s.error(`Failed to list ideas: ${t}`),c.ERROR}}async function la(e){if(s.info(f.cyan(`\u2728 Submit New Idea
|
|
420
|
+
`)),!e.title||!e.description)return s.error("Missing required fields: --title and --description"),c.INVALID_ARGS;try{let n=await(await $("/ideas",{method:"POST",body:{title:e.title,description:e.description,tags:e.tags?.split(",")||[],authorId:"user_current"},dryRun:e.dryRun})).json();return e.dryRun?(s.info(f.green(`\u2713 [DRY RUN] Would submit idea
|
|
331
421
|
`)),c.SUCCESS):(s.success(`\u2713 Idea submitted!
|
|
332
422
|
`),s.info(`Idea URL: ${f.blue(`https://nebutra.com/ideas/${n.ideaId}`)}`),s.info(`Start voting and discussing! Community will help shape it.
|
|
333
|
-
`),c.SUCCESS)}catch(t){return s.error(`Submission failed: ${t}`),c.ERROR}}async function
|
|
334
|
-
`));try{let n=await
|
|
423
|
+
`),c.SUCCESS)}catch(t){return s.error(`Submission failed: ${t}`),c.ERROR}}async function da(e,t){s.info(f.cyan(`\u{1F44D} Voting on idea "${e}"
|
|
424
|
+
`));try{let n=await $(`/ideas/${e}/vote`,{method:"POST",body:{action:"upvote"},dryRun:t.dryRun});return t.dryRun?(s.info(f.green(`\u2713 [DRY RUN] Would upvote idea
|
|
335
425
|
`)),c.SUCCESS):(s.success(`\u2713 Vote recorded! This idea now has more momentum.
|
|
336
|
-
`),c.SUCCESS)}catch(n){return s.error(`Vote failed: ${n}`),c.ERROR}}async function
|
|
337
|
-
`));try{let n=await
|
|
426
|
+
`),c.SUCCESS)}catch(n){return s.error(`Vote failed: ${n}`),c.ERROR}}async function ua(e,t){s.info(f.cyan(`\u{1F3AF} Claiming idea "${e}"
|
|
427
|
+
`));try{let n=await $(`/ideas/${e}/claim`,{method:"POST",body:{claimerId:"user_current",status:"in-progress"},dryRun:t.dryRun});return t.dryRun?(s.info(f.green(`\u2713 [DRY RUN] Would claim idea
|
|
338
428
|
`)),c.SUCCESS):(s.success(`\u2713 Idea claimed! You're now working on this.
|
|
339
|
-
`),s.info("Post updates in the comments to keep the community informed."),c.SUCCESS)}catch(n){return s.error(`Claim failed: ${n}`),c.ERROR}}async function
|
|
340
|
-
`)),!t.message)return s.error("Missing --message parameter"),c.INVALID_ARGS;try{let n=await
|
|
429
|
+
`),s.info("Post updates in the comments to keep the community informed."),c.SUCCESS)}catch(n){return s.error(`Claim failed: ${n}`),c.ERROR}}async function ma(e,t){if(s.info(f.cyan(`\u{1F4AC} Adding comment to idea "${e}"
|
|
430
|
+
`)),!t.message)return s.error("Missing --message parameter"),c.INVALID_ARGS;try{let n=await $(`/ideas/${e}/comments`,{method:"POST",body:{authorId:"user_current",message:t.message},dryRun:t.dryRun});return t.dryRun?(s.info(f.green(`\u2713 [DRY RUN] Would post comment
|
|
341
431
|
`)),c.SUCCESS):(s.success(`\u2713 Comment posted!
|
|
342
|
-
`),c.SUCCESS)}catch(n){return s.error(`Comment failed: ${n}`),c.ERROR}}async function
|
|
343
|
-
`));try{let n=await(await
|
|
344
|
-
`));try{let t=await(await
|
|
432
|
+
`),c.SUCCESS)}catch(n){return s.error(`Comment failed: ${n}`),c.ERROR}}async function pa(e){s.info(f.cyan(`\u{1F465} Legacy Member Directory
|
|
433
|
+
`));try{let n=await(await $(`/opc?skill=${e.skill||""}&industry=${e.industry||""}&sort=${e.sort||"active"}`)).json();return e.format==="json"||(s.info(`View profile: ${f.yellow("nebutra ecosystem opc profile <handle>")}`),s.info(`Register: ${f.yellow("nebutra ecosystem opc register")}`)),c.SUCCESS}catch(t){return s.error(`Failed to list members: ${t}`),c.ERROR}}async function fa(){s.info(f.cyan(`\u{1F464} Legacy Member Profile
|
|
434
|
+
`));try{let t=await(await $("/opc/me")).json();return s.info(`Edit profile: ${f.yellow("nebutra ecosystem opc register")}`),c.SUCCESS}catch(e){return s.error(`Failed to load profile: ${e}`),c.ERROR}}async function ga(e){s.info(f.cyan(`\u{1F4DD} Register Legacy Member Profile
|
|
345
435
|
`));let t={name:"Your Name",handle:"your-handle",bio:"Building amazing products",skills:["nextjs","ai","devtools"],industry:"startup"};if(e.dryRun)return s.info(`[DRY RUN] Would register profile:
|
|
346
|
-
`),c.SUCCESS;if(!e.yes)return s.info("This will create your public legacy member profile."),s.info(`Run with ${f.yellow("--yes")} to confirm`),c.CANCELLED;try{let n=await
|
|
347
|
-
`),s.info(`View at: ${f.blue("https://nebutra.com/members/your-handle")}`),s.info(`Compatibility command: ${f.yellow("nebutra ecosystem opc profile --share")}`),c.SUCCESS}catch(n){return s.error(`Registration failed: ${n}`),c.ERROR}}async function
|
|
348
|
-
`));try{let n=await(await
|
|
349
|
-
`));let t={projectName:"My Awesome Project",url:"https://example.com",description:"A production product built with Nebutra",monthlyVisitors:1e4,builtWith:["nextjs","tailwind","prisma"]};if(!e.yes)return s.info("This will add your project to the public showcase."),s.info(`Run with ${f.yellow("--yes")} to confirm`),c.CANCELLED;try{let n=await
|
|
436
|
+
`),c.SUCCESS;if(!e.yes)return s.info("This will create your public legacy member profile."),s.info(`Run with ${f.yellow("--yes")} to confirm`),c.CANCELLED;try{let n=await $("/opc/register",{method:"POST",body:t});return s.success(`\u2713 Legacy member profile created!
|
|
437
|
+
`),s.info(`View at: ${f.blue("https://nebutra.com/members/your-handle")}`),s.info(`Compatibility command: ${f.yellow("nebutra ecosystem opc profile --share")}`),c.SUCCESS}catch(n){return s.error(`Registration failed: ${n}`),c.ERROR}}async function ya(e){s.info(f.cyan(`\u2B50 Project Showcase
|
|
438
|
+
`));try{let n=await(await $(`/showcase?category=${e.category||""}&featured=${e.featured?"true":""}`)).json();return e.format==="json"||(s.info(`Vote: ${f.yellow("nebutra ecosystem showcase vote <id>")}`),s.info(`Submit yours: ${f.yellow("nebutra ecosystem showcase submit")}`)),c.SUCCESS}catch(t){return s.error(`Failed to list showcase: ${t}`),c.ERROR}}async function ha(e){s.info(f.cyan(`\u{1F389} Submit Project to Showcase
|
|
439
|
+
`));let t={projectName:"My Awesome Project",url:"https://example.com",description:"A production product built with Nebutra",monthlyVisitors:1e4,builtWith:["nextjs","tailwind","prisma"]};if(!e.yes)return s.info("This will add your project to the public showcase."),s.info(`Run with ${f.yellow("--yes")} to confirm`),c.CANCELLED;try{let n=await $("/showcase/submit",{method:"POST",body:t,dryRun:e.dryRun});return e.dryRun?(s.info(f.green(`\u2713 [DRY RUN] Would submit to showcase
|
|
350
440
|
`)),c.SUCCESS):(s.success(`\u2713 Project added to showcase!
|
|
351
|
-
`),s.info(`View at: ${f.blue("https://nebutra.com/showcase/my-project")}`),s.info("Community can now discover your work!"),c.SUCCESS)}catch(n){return s.error(`Submission failed: ${n}`),c.ERROR}}async function
|
|
352
|
-
`));try{let n=await
|
|
441
|
+
`),s.info(`View at: ${f.blue("https://nebutra.com/showcase/my-project")}`),s.info("Community can now discover your work!"),c.SUCCESS)}catch(n){return s.error(`Submission failed: ${n}`),c.ERROR}}async function ba(e,t){s.info(f.cyan(`\u{1F44D} Voting on showcase project "${e}"
|
|
442
|
+
`));try{let n=await $(`/showcase/${e}/vote`,{method:"POST",body:{action:"upvote"},dryRun:t.dryRun});return t.dryRun?(s.info(f.green(`\u2713 [DRY RUN] Would upvote project
|
|
353
443
|
`)),c.SUCCESS):(s.success(`\u2713 Vote recorded! You're supporting this project.
|
|
354
|
-
`),c.SUCCESS)}catch(n){return s.error(`Vote failed: ${n}`),c.ERROR}}async function
|
|
355
|
-
`));let t=!e.push,n=e.push?"push":"pull";try{let r=await
|
|
444
|
+
`),c.SUCCESS)}catch(n){return s.error(`Vote failed: ${n}`),c.ERROR}}async function va(e){s.info(f.cyan(`\u{1F504} Sync with Ecosystem
|
|
445
|
+
`));let t=!e.push,n=e.push?"push":"pull";try{let r=await $("/sync",{method:"POST",body:{direction:n,timestamp:new Date().toISOString()},dryRun:e.dryRun});return e.dryRun?(s.info(f.green(`\u2713 [DRY RUN] Would sync (${n})
|
|
356
446
|
`)),c.SUCCESS):(s.success(`\u2713 Synced!
|
|
357
|
-
`),t?s.info("Downloaded: ecosystem config, stats, badges"):s.info("Uploaded: project updates, metrics"),c.SUCCESS)}catch(r){return s.error(`Sync failed: ${r}`),c.ERROR}}function
|
|
358
|
-
`)){if(o.trim().startsWith("#")||!o.trim())continue;let a=o.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);if(a){let[,i,l]=a;r[i]=l.replace(/^["'](.*)["']$/,"$1")}}return r}function
|
|
359
|
-
`)){if(i.trim().startsWith("#")||!i.trim())continue;let l=i.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);if(l){let[,u,
|
|
360
|
-
`);e.dryRun?(d("Dry-run: would write .env.local","info"),E(l,{format:"plain"})):(
|
|
361
|
-
`),d(`Environment template written to ${r}`,"success"))}async function
|
|
447
|
+
`),t?s.info("Downloaded: ecosystem config, stats, badges"):s.info("Uploaded: project updates, metrics"),c.SUCCESS)}catch(r){return s.error(`Sync failed: ${r}`),c.ERROR}}function jn(e){let t=e.command("ecosystem").description("Nebutra ecosystem marketplace, ideas, showcase, and sync workflows");t.command("status").description("Ecosystem overview dashboard").option("--format <format>","Output format: json","text").action(async i=>{await ta(i)}),t.command("connect").description("Connect local project to Nebutra ecosystem").option("--token <token>","Ecosystem API token").option("--org <orgId>","Organization ID").option("--dry-run","Preview without making changes").option("--yes","Skip confirmations").action(async i=>{await na(i)}),t.command("publish").description("Publish project/template to ecosystem marketplace").option("--tag <tag>","Release tag: latest|beta|canary","latest").option("--visibility <visibility>","Project visibility: public|private|unlisted","public").option("--dry-run","Preview without making changes").option("--yes","Skip confirmations").action(async i=>{await ra(i)});let n=t.command("templates").description("Template marketplace");n.command("list").description("Browse available templates").option("--category <category>","Filter by category").option("--sort <sort>","Sort by: stars|downloads|recent","downloads").option("--format <format>","Output format","text").action(async i=>{await oa(i)}),n.command("search <query>").description("Search templates").option("--format <format>","Output format","text").action(async(i,l)=>{await sa(i,l)}),n.command("info <id>").description("Template details").action(async i=>{await aa(i)}),n.command("install <id>").description("Install/fork a template").option("--dry-run","Preview without making changes").action(async(i,l)=>{await ia(i,l)});let r=t.command("ideas").description("Ideas marketplace");r.command("list").description("Browse ideas").option("--status <status>","Filter by status").option("--sort <sort>","Sort by: votes|recent|trending","recent").option("--format <format>","Output format","text").action(async i=>{await ca(i)}),r.command("submit").description("Submit new idea").option("--title <title>","Idea title").option("--description <description>","Idea description").option("--tags <tags>","Comma-separated tags").option("--dry-run","Preview without making changes").action(async i=>{await la(i)}),r.command("vote <id>").description("Upvote an idea").option("--dry-run","Preview without making changes").action(async(i,l)=>{await da(i,l)}),r.command("claim <id>").description("Claim an idea to work on").option("--dry-run","Preview without making changes").action(async(i,l)=>{await ua(i,l)}),r.command("comment <id>").description("Comment on an idea").option("--message <message>","Comment text").option("--dry-run","Preview without making changes").action(async(i,l)=>{await ma(i,l)});let o=t.command("opc",{hidden:!0}).description("Legacy member directory");o.command("list").description("Browse legacy member profiles").option("--skill <skill>","Filter by skill tag").option("--industry <industry>","Filter by industry").option("--sort <sort>","Sort by: active|joined","active").option("--format <format>","Output format","text").action(async i=>{await pa(i)}),o.command("profile").description("View/edit your legacy member profile").action(async()=>{await fa()}),o.command("register").description("Register a legacy member profile").option("--yes","Skip confirmations").option("--dry-run","Preview without making changes").action(async i=>{await ga(i)});let a=t.command("showcase").description("Project showcase");a.command("list").description("Browse showcased projects").option("--category <category>","Filter by category").option("--featured","Show featured only").option("--format <format>","Output format","text").action(async i=>{await ya(i)}),a.command("submit").description("Submit your project to showcase").option("--yes","Skip confirmations").option("--dry-run","Preview without making changes").action(async i=>{await ha(i)}),a.command("vote <id>").description("Upvote a project (Product Hunt style)").option("--dry-run","Preview without making changes").action(async(i,l)=>{await ba(i,l)}),t.command("sync").description("Sync local project with ecosystem").option("--pull","Pull remote changes").option("--push","Push local changes").option("--dry-run","Preview without making changes").action(async i=>{await va(i)})}import{existsSync as st,readFileSync as Un,writeFileSync as wa}from"fs";import{resolve as at}from"path";import*as Ee from"@clack/prompts";import Ra from"picocolors";function it(e){let t=at(e,".env.example");if(!st(t))throw new Error(`.env.example not found at ${t}`);let n=Un(t,"utf-8"),r={};for(let o of n.split(`
|
|
448
|
+
`)){if(o.trim().startsWith("#")||!o.trim())continue;let a=o.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);if(a){let[,i,l]=a;r[i]=l.replace(/^["'](.*)["']$/,"$1")}}return r}function It(e){let t=at(e,".env.local"),n=at(e,".env"),r=st(t)?t:n;if(!st(r))return{};let o=Un(r,"utf-8"),a={};for(let i of o.split(`
|
|
449
|
+
`)){if(i.trim().startsWith("#")||!i.trim())continue;let l=i.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);if(l){let[,u,m]=l;a[u]=m.replace(/^["'](.*)["']$/,"$1")}}return a}function Sa(e,t){return["SECRET","KEY","TOKEN","PASSWORD"].some(o=>e.toUpperCase().includes(o))&&t.length>0}function Ca(e){return e.length<=8?"*".repeat(Math.max(e.length,4)):e.substring(0,4)+"*".repeat(e.length-8)+e.substring(e.length-4)}async function Ea(e){d("Validating environment variables...","info");let t=h(),n=it(t),r=It(t),o=process.env,a={},i=!0;for(let l of Object.keys(n)){let u=l in r||l in o;a[l]=u,u||(i=!1)}if(e.format==="json")E({valid:i,checked:Object.keys(n).length,missing:Object.entries(a).filter(([,l])=>!l).map(([l])=>l)},{format:"json"});else{nt("Environment Validation");let l=Object.entries(a).filter(([,u])=>!u).map(([u])=>u);if(l.length===0)d("All required environment variables are set","success");else{d(`${l.length} missing environment variable(s):`,"warn");for(let u of l)d(` - ${u}`,"warn")}}i||process.exit(1)}async function ka(e){let t=h(),n=it(t),r=at(t,".env.local");if(st(r)&&!e.yes&&!e.dryRun){let u=await Ee.confirm({message:".env.local already exists. Overwrite?",initialValue:!1});(Ee.isCancel(u)||!u)&&(d("Template generation cancelled","warn"),process.exit(0))}d("Generating .env.local from template...","info");let o={},a=0,i=Object.keys(n).length;for(let[u,m]of Object.entries(n))if(a++,e.yes)o[u]=m;else if(process.stdin.isTTY===!0){let y=`[${a}/${i}] ${u}`,p=await Ee.text({message:y,defaultValue:m});Ee.isCancel(p)&&(d("Template generation cancelled","warn"),process.exit(0)),o[u]=p}else o[u]=m;let l=Object.entries(o).map(([u,m])=>{let p=/[\s=]/.test(m)?`"${m}"`:m;return`${u}=${p}`}).join(`
|
|
450
|
+
`);e.dryRun?(d("Dry-run: would write .env.local","info"),E(l,{format:"plain"})):(wa(r,l+`
|
|
451
|
+
`),d(`Environment template written to ${r}`,"success"))}async function Oa(e){d("Comparing environment variables...","info");let t=h(),n=it(t),r=It(t),o=[],a=[],i=[];for(let l of Object.keys(n))l in r?i.push(l):o.push(l);for(let l of Object.keys(r))l in n||a.push(l);if(e.format==="json")E({matching:i.length,missing:o.length,extra:a.length,missing_vars:o,extra_vars:a},{format:"json"});else{if(nt("Environment Diff"),i.length>0&&d(`Matching (${i.length}): ${i.join(", ")}`,"success"),o.length>0){d(`Missing (${o.length}):`,"warn");for(let l of o)d(` - ${l}`,"warn")}if(a.length>0){d(`Extra (${a.length}):`,"info");for(let l of a)d(` - ${l}`,"info")}}}async function xa(e){d("Reading environment variables...","info");let t=h(),n=It(t),r=it(t),o={};for(let a of Object.keys(r))if(a in n){let i=n[a];o[a]=Sa(a,i)?Ca(i):i||"(empty)"}else o[a]="(not set)";if(e.format==="json"){let a={};for(let[i,l]of Object.entries(o))a[i]=l;E(a,{format:"json"})}else{nt("Current Environment Variables");let a=Object.entries(o).map(([i,l])=>({Variable:Ra.cyan(i),Value:l}));E(a,{format:"table"}),d("Note: Sensitive values (KEY, TOKEN, SECRET, PASSWORD) are masked","info")}}function Dn(e){e.command("env <verb>").description("Manage environment variables and configuration").option("--dry-run","Show what would be done without making changes").option("--yes","Skip confirmations and use defaults").option("--format <type>","Output format: json, plain","plain").action(async(n,r)=>{let o=r.optsWithGlobals?.(),a={dryRun:r.dryRun||o?.dryRun,yes:r.yes||o?.yes,format:r.format||o?.format};try{switch(n){case"validate":await Ea(a);break;case"template":await ka(a);break;case"diff":await Oa(a);break;case"show":await xa(a);break;default:d(`Unknown env subcommand: ${n}. Valid commands: validate, template, diff, show`,"error"),process.exit(1)}}catch(i){let l=i instanceof Error?i.message:String(i);d(`Environment command failed: ${l}`,"error"),k("Full error",{error:i}),process.exit(1)}}).addHelpText("after",`
|
|
362
452
|
Examples:
|
|
363
453
|
nebutra env validate Check all required vars are set
|
|
364
454
|
nebutra env template Generate .env.local from .env.example with prompts
|
|
@@ -375,7 +465,7 @@ Flags:
|
|
|
375
465
|
Sensitive Variables:
|
|
376
466
|
Variables with KEY, TOKEN, SECRET, PASSWORD in their names are masked in output.
|
|
377
467
|
This prevents accidental exposure of credentials.
|
|
378
|
-
`)}import{promises as
|
|
468
|
+
`)}import{promises as Ln}from"fs";import{join as ke}from"path";import*as Me from"@clack/prompts";import j from"picocolors";function Fn(e){let t=e.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("");return{"package.json":`{
|
|
379
469
|
"name": "@nebutra/${e}",
|
|
380
470
|
"version": "0.0.0",
|
|
381
471
|
"private": true,
|
|
@@ -460,7 +550,7 @@ export default function RootLayout({
|
|
|
460
550
|
|
|
461
551
|
@theme {
|
|
462
552
|
--color-*: var(--neutral-*);
|
|
463
|
-
}`}}function
|
|
553
|
+
}`}}function Mn(e){return{"package.json":`{
|
|
464
554
|
"name": "@nebutra/${e}",
|
|
465
555
|
"version": "0.0.0",
|
|
466
556
|
"type": "module",
|
|
@@ -497,7 +587,7 @@ export default function RootLayout({
|
|
|
497
587
|
|
|
498
588
|
export function hello(name: string): string {
|
|
499
589
|
return \`Hello, \${name}!\`;
|
|
500
|
-
}`}}function
|
|
590
|
+
}`}}function Gn(e){let t=e.replace(/\//g,"_").replace(/-/g,"_");return{[`src/routes/${e}.ts`]:`import { Hono } from "hono";
|
|
501
591
|
import { z } from "zod";
|
|
502
592
|
|
|
503
593
|
type Env = {
|
|
@@ -548,7 +638,7 @@ app.post("/", async (c) => {
|
|
|
548
638
|
);
|
|
549
639
|
});
|
|
550
640
|
|
|
551
|
-
export default app;`}}function
|
|
641
|
+
export default app;`}}function Kn(e){let t=e.replace(/([A-Z])/g,"-$1").toLowerCase().replace(/^-/,""),n=e.split("-").map(r=>r.charAt(0).toUpperCase()+r.slice(1)).join("");return{[`src/components/${t}.tsx`]:`import { cva, type VariantProps } from "class-variance-authority";
|
|
552
642
|
import { cn } from "@nebutra/ui/utils";
|
|
553
643
|
|
|
554
644
|
const ${t}Variants = cva(
|
|
@@ -622,13 +712,13 @@ export const AllSizes: Story = {
|
|
|
622
712
|
<${n} size="lg">Large</\${pascalCase}>
|
|
623
713
|
</div>
|
|
624
714
|
),
|
|
625
|
-
};`}}async function
|
|
626
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
627
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
628
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
629
|
-
`),process.exit(c.DRY_RUN_OK)}Le.intro(j.bgCyan(j.black(" nebutra generate component ")));let n=s.spinner();try{n.start(`Generating component ${j.cyan(e)}...`);let r=await st("component",e);n.stop(`Created component ${j.cyan(e)}`,0),s.success(`Component created with ${r.length} files`),s.info("Files created in packages/ui/src/components/"),s.info("Next steps:"),s.info(" 1. Add export to packages/ui/src/components/index.ts"),s.info(" 2. pnpm --filter @nebutra/ui typecheck"),s.info(" 3. pnpm --filter @nebutra/storybook dev"),process.exit(c.SUCCESS)}catch(r){n.stop(`Failed to generate component: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(c.ERROR)}}function En(e){let t=e.command("generate").description("Scaffold a new app, package, route, or component").alias("gen");t.command("app <name>").description("Scaffold a new Next.js app (Next.js 16 + Tailwind v4)").option("--dry-run","Preview changes without writing files").option("--yes","Skip all prompts").option("--template <type>","App template (default: nextjs)","nextjs").option("--port <number>","Dev server port","3000").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await Ms(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1,interactive:process.stdin.isTTY})}),t.command("package <name>").description("Scaffold a new package (@nebutra/<name>)").option("--dry-run","Preview changes without writing files").option("--yes","Skip all prompts").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await Gs(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1})}),t.command("route <path>").description("Scaffold a Hono API route in api-gateway").option("--dry-run","Preview changes without writing files").option("--yes","Skip all prompts").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await Ks(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1})}),t.command("component <name>").description("Scaffold a UI component with Storybook story").option("--dry-run","Preview changes without writing files").option("--yes","Skip all prompts").option("--variants <list>","Comma-separated variant names (e.g., 'primary,secondary')").option("--sizes <list>","Comma-separated size names (e.g., 'sm,md,lg')").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await qs(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1,variants:r.variants,sizes:r.sizes})})}import _e from"picocolors";async function q(e,t){let n=await R(),r=new URL(e,"http://localhost:3000/api/growth");t?.query&&Object.entries(t.query).forEach(([a,i])=>{r.searchParams.set(a,i)});let o=await fetch(r,{method:t?.method||"GET",headers:{"Content-Type":"application/json"},body:t?.body?JSON.stringify(t.body):void 0});if(!o.ok)throw new Error(`Growth API error (${o.status}): ${await o.text()}`);return o.json()}async function Vs(e){try{let t=e.period||"7d",n=e.compare;s.info(`Fetching growth metrics for ${t}...`);let r=await q("/summary",{query:{period:t,compare:n?"true":"false"}});return c.SUCCESS}catch(t){return s.error(`Dashboard error: ${t instanceof Error?t.message:String(t)}`),c.ERROR}}async function Ws(e){try{let t=e.period||"7d",n=e.segment||"all";s.info(`Fetching funnel data for ${t} (${n})...`);let r=await q("/funnel",{query:{period:t,segment:n}});return r.forEach((a,i)=>{let l="\u2588".repeat(Math.ceil(a.rate*20)),u="\u2591".repeat(20-Math.ceil(a.rate*20)),p=i<r.length-1?_e.red(`\u2193 ${(a.dropoff_rate*100).toFixed(1)}% drop`):""}),r.reduce((a,i)=>i.dropoff_rate>a.dropoff_rate?i:a).dropoff_rate>.2,c.SUCCESS}catch(t){return s.error(`Funnel error: ${t instanceof Error?t.message:String(t)}`),c.ERROR}}async function zs(e){try{let t=e.period||"weekly",n=parseInt(e.cohorts,10)||8;s.info(`Fetching ${t} cohort data (${n} cohorts)...`);let r=await q("/cohort",{query:{period:t,cohorts:String(n)}});if(r.length===0)return c.SUCCESS;let o=Object.keys(r[0]).slice(1);return r.forEach(a=>{let l=String(a.cohort).padEnd(15);o.forEach(u=>{let p=a[u];if(typeof p=="number"){let y=p/100,m=String(Math.round(p)).padEnd(8);y>=.7?m=_e.green(m):y>=.4?m=_e.yellow(m):m=_e.red(m),l+=m+" "}})}),c.SUCCESS}catch(t){return s.error(`Cohort error: ${t instanceof Error?t.message:String(t)}`),c.ERROR}}async function Bs(e,t){try{if(e==="stats"){s.info("Fetching newsletter stats...");let n=await q("/newsletter/stats");return c.SUCCESS}if(e==="subscribers"){let n=parseInt(t.limit,10)||10,r=parseInt(t.offset,10)||0;return s.info(`Fetching ${n} newsletter subscribers (offset ${r})...`),(await q("/newsletter/subscribers",{query:{limit:String(n),offset:String(r)}})).forEach(a=>{}),c.SUCCESS}if(e==="send"){let n=t.template;return n?t.yes?(s.info(`Sending newsletter with template: ${n}`),await q("/newsletter/send",{method:"POST",body:{template_id:n}}),c.SUCCESS):(s.warn("Use --yes to confirm newsletter send"),c.ERROR):(s.error("--template is required"),c.ERROR)}return s.error(`Unknown newsletter subcommand: ${e}`),c.ERROR}catch(n){return s.error(`Newsletter error: ${n instanceof Error?n.message:String(n)}`),c.ERROR}}async function Ys(e,t){try{if(e==="status"){s.info("Fetching referral program status...");let n=await q("/referral/status");return c.SUCCESS}if(e==="config"){s.info("Fetching referral config...");let n=await q("/referral/config");return Object.entries(n).forEach(([r,o])=>{}),c.SUCCESS}return e==="leaderboard"?(s.info("Fetching referral leaderboard..."),(await q("/referral/leaderboard")).forEach(r=>{let o=r.rank===1?"\u{1F947}":r.rank===2?"\u{1F948}":"\u{1F949}"}),c.SUCCESS):(s.error(`Unknown referral subcommand: ${e}`),c.ERROR)}catch(n){return s.error(`Referral error: ${n instanceof Error?n.message:String(n)}`),c.ERROR}}async function Hs(e,t){try{if(e==="list"){s.info("Fetching active experiments...");let n=await q("/experiment");return n.length===0?c.SUCCESS:(n.forEach(r=>{let o=r.status==="running"?_e.yellow("\u25CF"):_e.green("\u2713")}),c.SUCCESS)}if(e==="create"){let n=t.name,r=t.variants?.split(","),o=t.metric;if(!n||!r||!o)return s.error("Required: --name <name> --variants <A,B> --metric <metric>"),c.ERROR;s.info(`Creating experiment: ${n}`);let a=await q("/experiment",{method:"POST",body:{name:n,variants:r,metric:o}});return c.SUCCESS}if(e==="results"){let n=t.id;if(!n)return s.error("--id is required"),c.ERROR;s.info(`Fetching results for experiment ${n}...`);let r=await q(`/experiment/${n}`);return r.winner,r.significance,c.SUCCESS}if(e==="conclude"){let n=t.id,r=t.winner;return!n||!r?(s.error("Required: --id <id> --winner <variant>"),c.ERROR):t.yes?(s.info(`Concluding experiment ${n} with winner: ${r}`),await q(`/experiment/${n}`,{method:"POST",body:{action:"conclude",winner:r}}),c.SUCCESS):(s.warn("Use --yes to confirm experiment conclusion"),c.ERROR)}return s.error(`Unknown experiment subcommand: ${e}`),c.ERROR}catch(n){return s.error(`Experiment error: ${n instanceof Error?n.message:String(n)}`),c.ERROR}}async function Js(e){try{let t=e.focus||"activation",n=e.depth||"quick";s.info(`Generating growth insights (focus: ${t}, depth: ${n})...`);let r=await q("/pulse",{query:{focus:t,depth:n}});return r.insights.forEach(o=>{}),r.recommendations.forEach(o=>{}),c.SUCCESS}catch(t){return s.error(`Pulse error: ${t instanceof Error?t.message:String(t)}`),c.ERROR}}function kn(e){let t=e.command("growth").description("Growth engine operations \u2014 metrics, funnel, cohorts, AI insights");t.command("dashboard").description("Growth metrics overview").option("--period <7d|30d|90d>","Time period","7d").option("--compare","Show period-over-period deltas").action(async a=>{process.exit(await Vs(a))}),t.command("funnel").description("Conversion funnel analysis").option("--period <7d|30d|90d>","Time period","7d").option("--segment <organic|paid|referral>","Traffic segment","all").action(async a=>{process.exit(await Ws(a))}),t.command("cohort").description("Cohort retention analysis").option("--period <weekly|monthly>","Cohort period","weekly").option("--cohorts <n>","Number of cohorts","8").action(async a=>{process.exit(await zs(a))}),t.command("newsletter <subcommand>").description("Newsletter management").action(async(a,i)=>{process.exit(await Bs(a,i))}),t.command("referral <subcommand>").description("Referral program management").action(async(a,i)=>{process.exit(await Ys(a,i))}),t.command("experiment <subcommand>").description("A/B testing and experiment management").option("--id <id>","Experiment ID").option("--name <name>","Experiment name").option("--variants <A,B>","Comma-separated variants").option("--metric <metric>","Success metric").option("--winner <variant>","Winning variant").action(async(a,i)=>{process.exit(await Hs(a,i))}),t.command("pulse").description("AI-generated growth insights (AARRR: Acquisition, Activation, Retention, Revenue, Referral)").option("--focus <aarrr>","Focus area (acquisition|activation|retention|revenue|referral)","activation").option("--depth <quick|deep>","Analysis depth","quick").action(async a=>{process.exit(await Js(a))}),t.option("--dry-run","Preview changes without applying them").option("--format <json|text>","Output format","text").option("--yes","Skip confirmation prompts")}import Ot,{lstatSync as Qs,readdirSync as xt,readFileSync as Xs,writeFileSync as Zs}from"fs";import ie from"path";import{fileURLToPath as ea}from"url";import z from"picocolors";var ta=ie.dirname(ea(import.meta.url));function $t(){let e=new Map,t=ie.join(ta,"../../../apps");if(!Ot.existsSync(t))return e;try{let n=xt(t);for(let r of n){let o=ie.join(t,r),a=ie.join(o,"messages");Ot.existsSync(a)&&Qs(a).isDirectory()&&e.set(r,a)}}catch(n){s.debug(`Error scanning apps directory: ${n instanceof Error?n.message:String(n)}`)}return e}function Fe(e,t=""){let n=new Set;for(let[r,o]of Object.entries(e)){let a=t?`${t}.${r}`:r;if(typeof o=="object"&&o!==null&&!Array.isArray(o)){let i=Fe(o,a);for(let l of i)n.add(l)}else n.add(a)}return n}function Me(e){try{let t=Xs(e,"utf-8");return JSON.parse(t)}catch(t){return s.debug(`Failed to parse ${e}: ${t instanceof Error?t.message:String(t)}`),null}}async function na(e){if(s.info("Syncing language files across the project..."),(await _("translate",{filter:"landing-page",dryRun:e.dryRun,interactive:e.interactive??!0})).exitCode!==0){s.warn("Landing page translate script not found or failed. Trying sync-lang.mjs...");let n=await A({command:"node",args:["scripts/sync-lang.mjs"],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Sync language files"});n.exitCode!==0&&(s.error("Failed to sync language files"),process.exit(n.exitCode)),e.format==="json"?console.log(JSON.stringify({command:"i18n:sync",exitCode:0,success:!0},null,2)):e.dryRun||s.success("Language files synced successfully")}else e.format==="json"?console.log(JSON.stringify({command:"i18n:sync",exitCode:0,success:!0},null,2)):e.dryRun||s.success("Language files synced successfully")}async function ra(e){s.info("Validating translation files...");let t=$t();t.size===0&&(s.warn("No message directories found in the project"),process.exit(c.NOT_FOUND));let n=new Map,r=!1;for(let[o,a]of t){let i=[];try{let l=xt(a).filter(m=>m.endsWith(".json"));if(l.length===0)continue;let u=ie.join(a,"en.json"),p=Me(u);if(!p){s.warn(`Could not load reference locale en.json from ${o}`);continue}let y=Fe(p);for(let m of l){if(m==="en.json")continue;let h=m.replace(".json",""),M=ie.join(a,m),Y=Me(M);if(!Y){s.debug(`Could not parse ${m}`);continue}let H=Fe(Y),ve=Array.from(y).filter(g=>!H.has(g)),me=Array.from(H).filter(g=>!y.has(g)),Ie=y.size>0?(y.size-ve.length)/y.size*100:100;(ve.length>0||me.length>0)&&(r=!0),i.push({locale:h,totalKeys:y.size,missingKeys:ve,extraKeys:me,coverage:Math.round(Ie*100)/100})}i.length>0&&n.set(o,i)}catch(l){s.debug(`Error validating ${o}: ${l instanceof Error?l.message:String(l)}`)}}if(e.format==="json"){let o={command:"i18n:validate",success:!r,results:Array.from(n.entries()).reduce((a,[i,l])=>(a[i]=l,a),{})};console.log(JSON.stringify(o,null,2))}else for(let[o,a]of n){s.info(`${z.bold(o)}:`);for(let i of a){let u=(i.coverage>=100?z.green:i.coverage>=80?z.yellow:z.red)(`${i.coverage}%`);s.info(` ${z.cyan(i.locale.padEnd(5))} ${u.padEnd(20)} [${i.totalKeys} keys]`),i.missingKeys.length>0&&s.warn(` Missing: ${i.missingKeys.slice(0,5).join(", ")}${i.missingKeys.length>5?` (+ ${i.missingKeys.length-5} more)`:""}`),i.extraKeys.length>0&&s.warn(` Extra: ${i.extraKeys.slice(0,5).join(", ")}${i.extraKeys.length>5?` (+ ${i.extraKeys.length-5} more)`:""}`)}}r&&process.exit(c.CONFLICT)}async function oa(e,t){/^[a-z]{2}(-[A-Z]{2})?$/.test(e)||(s.error(`Invalid locale format: ${e}. Use 'en', 'es', 'zh-CN', etc.`),process.exit(c.INVALID_ARGS));let n=$t();n.size===0&&(s.error("No message directories found in the project"),process.exit(c.NOT_FOUND));let r=[],o=!1;for(let[i,l]of n){let u=ie.join(l,`${e}.json`);if(Ot.existsSync(u)){s.warn(`Locale ${e} already exists in ${i}`);continue}try{let h=function(M){for(let Y in M)typeof M[Y]=="string"?M[Y]="":typeof M[Y]=="object"&&M[Y]!==null&&h(M[Y])};var a=h;let p=ie.join(l,"en.json"),y=Me(p);if(!y){s.warn(`Could not load reference en.json from ${i}, skipping...`);continue}let m=JSON.parse(JSON.stringify(y));h(m),t.dryRun?s.info(`Would create ${e} locale in ${i}`):(Zs(u,JSON.stringify(m,null,2)+`
|
|
630
|
-
`),r.push({app:i,path:u}),s.success(`Created ${e} locale in ${i}`))}catch(
|
|
631
|
-
Average coverage: ${i(a.toFixed(1)+"%")}`)}}}function
|
|
715
|
+
};`}}async function ct(e,t,n){let r=h(),o=[],a={};switch(e){case"app":a=Fn(t);break;case"package":a=Mn(t);break;case"route":a=Gn(t);break;case"component":a=Kn(t);break;default:throw new Error(`Unknown generate type: ${e}`)}for(let[i,l]of Object.entries(a)){let u=e==="app"?ke(r,"apps",t,i):e==="package"?ke(r,"packages",t,i):ke(r,"backends/gateway",i);o.push({path:u,size:Buffer.byteLength(l)})}return o}async function lt(e,t){let n=h(),r=[],o={};switch(e){case"app":o=Fn(t);break;case"package":o=Mn(t);break;case"route":o=Gn(t);break;case"component":o=Kn(t);break;default:throw new Error(`Unknown generate type: ${e}`)}for(let[a,i]of Object.entries(o)){let l;e==="app"?l=ke(n,"apps",t,a):e==="package"?l=ke(n,"packages",t,a):e==="component"?l=ke(n,"packages/ui",a):l=ke(n,"backends/gateway",a);let u=l.substring(0,l.lastIndexOf("/"));await Ln.mkdir(u,{recursive:!0}),await Ln.writeFile(l,i,"utf-8"),r.push(l)}return r}async function $a(e,t){if(t.dryRun){let r=await ct("app",e,t),o={mode:"dry-run",timestamp:new Date().toISOString(),type:"app",name:e,location:`apps/${e}`,files:r.map(a=>({path:a.path,sizeBytes:a.size})),instructions:[`cd apps/${e}`,"pnpm install","pnpm run dev"]};process.stdout.write(JSON.stringify(o,null,2)+`
|
|
716
|
+
`),process.exit(c.DRY_RUN_OK)}Me.intro(j.bgCyan(j.black(" nebutra generate app ")));let n=s.spinner();try{n.start(`Generating app ${j.cyan(e)}...`);let r=await lt("app",e);n.stop(`Created app ${j.cyan(e)}`,0),s.success(`App created with ${r.length} files`),s.info("Next steps:"),s.info(` cd apps/${e}`),s.info(" pnpm install"),s.info(" pnpm run dev"),process.exit(c.SUCCESS)}catch(r){n.stop(`Failed to generate app: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(c.ERROR)}}async function Aa(e,t){if(t.dryRun){let r=await ct("package",e,t),o={mode:"dry-run",timestamp:new Date().toISOString(),type:"package",name:e,location:`packages/${e}`,files:r.map(a=>({path:a.path,sizeBytes:a.size}))};process.stdout.write(JSON.stringify(o,null,2)+`
|
|
717
|
+
`),process.exit(c.DRY_RUN_OK)}Me.intro(j.bgCyan(j.black(" nebutra generate package ")));let n=s.spinner();try{n.start(`Generating package ${j.cyan(e)}...`);let r=await lt("package",e);n.stop(`Created package ${j.cyan(e)}`,0),s.success(`Package created with ${r.length} files`),s.info("Next steps:"),s.info(` Edit packages/${e}/src/index.ts`),s.info(` Run: pnpm --filter @nebutra/${e} build`),process.exit(c.SUCCESS)}catch(r){n.stop(`Failed to generate package: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(c.ERROR)}}async function _a(e,t){if(t.dryRun){let r=await ct("route",e,t),o={mode:"dry-run",timestamp:new Date().toISOString(),type:"route",path:e,location:`backends/gateway/src/routes/${e}.ts`,files:r.map(a=>({path:a.path,sizeBytes:a.size}))};process.stdout.write(JSON.stringify(o,null,2)+`
|
|
718
|
+
`),process.exit(c.DRY_RUN_OK)}Me.intro(j.bgCyan(j.black(" nebutra generate route ")));let n=s.spinner();try{n.start(`Generating route ${j.cyan(e)}...`);let r=await lt("route",e);n.stop(`Created route ${j.cyan(e)}`,0),s.success(`Route created with ${r.length} files`),s.info(`File: backends/gateway/src/routes/${e}.ts`),s.info(`Add to your main app.ts: app.route("/${e}", routes)`),process.exit(c.SUCCESS)}catch(r){n.stop(`Failed to generate route: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(c.ERROR)}}async function Pa(e,t){if(t.dryRun){let r=await ct("component",e,t),o={mode:"dry-run",timestamp:new Date().toISOString(),type:"component",name:e,location:"packages/design/ui/src/components/",files:r.map(a=>({path:a.path,sizeBytes:a.size})),nextSteps:["Add export to packages/design/ui/src/components/index.ts","Run: pnpm --filter @nebutra/ui typecheck","View in Storybook: pnpm --filter @nebutra/storybook dev"]};process.stdout.write(JSON.stringify(o,null,2)+`
|
|
719
|
+
`),process.exit(c.DRY_RUN_OK)}Me.intro(j.bgCyan(j.black(" nebutra generate component ")));let n=s.spinner();try{n.start(`Generating component ${j.cyan(e)}...`);let r=await lt("component",e);n.stop(`Created component ${j.cyan(e)}`,0),s.success(`Component created with ${r.length} files`),s.info("Files created in packages/design/ui/src/components/"),s.info("Next steps:"),s.info(" 1. Add export to packages/design/ui/src/components/index.ts"),s.info(" 2. pnpm --filter @nebutra/ui typecheck"),s.info(" 3. pnpm --filter @nebutra/storybook dev"),process.exit(c.SUCCESS)}catch(r){n.stop(`Failed to generate component: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(c.ERROR)}}function qn(e){let t=e.command("generate").description("Scaffold a new app, package, route, or component").alias("gen");t.command("app <name>").description("Scaffold a new Next.js app (Next.js 16 + Tailwind v4)").option("--dry-run","Preview changes without writing files").option("--yes","Skip all prompts").option("--template <type>","App template (default: nextjs)","nextjs").option("--port <number>","Dev server port","3000").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await $a(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1,interactive:process.stdin.isTTY})}),t.command("package <name>").description("Scaffold a new package (@nebutra/<name>)").option("--dry-run","Preview changes without writing files").option("--yes","Skip all prompts").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await Aa(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1})}),t.command("route <path>").description("Scaffold a Hono API route in api-gateway").option("--dry-run","Preview changes without writing files").option("--yes","Skip all prompts").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await _a(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1})}),t.command("component <name>").description("Scaffold a UI component with Storybook story").option("--dry-run","Preview changes without writing files").option("--yes","Skip all prompts").option("--variants <list>","Comma-separated variant names (e.g., 'primary,secondary')").option("--sizes <list>","Comma-separated size names (e.g., 'sm,md,lg')").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await Pa(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1,variants:r.variants,sizes:r.sizes})})}import Ie from"picocolors";async function q(e,t){let n=await h(),r=new URL(e,"http://localhost:3000/api/growth");t?.query&&Object.entries(t.query).forEach(([a,i])=>{r.searchParams.set(a,i)});let o=await fetch(r,{method:t?.method||"GET",headers:{"Content-Type":"application/json"},body:t?.body?JSON.stringify(t.body):void 0});if(!o.ok)throw new Error(`Growth API error (${o.status}): ${await o.text()}`);return o.json()}async function Ia(e){try{let t=e.period||"7d",n=e.compare;s.info(`Fetching growth metrics for ${t}...`);let r=await q("/summary",{query:{period:t,compare:n?"true":"false"}});return c.SUCCESS}catch(t){return s.error(`Dashboard error: ${t instanceof Error?t.message:String(t)}`),c.ERROR}}async function Na(e){try{let t=e.period||"7d",n=e.segment||"all";s.info(`Fetching funnel data for ${t} (${n})...`);let r=await q("/funnel",{query:{period:t,segment:n}});return r.forEach((a,i)=>{let l="\u2588".repeat(Math.ceil(a.rate*20)),u="\u2591".repeat(20-Math.ceil(a.rate*20)),m=i<r.length-1?Ie.red(`\u2193 ${(a.dropoff_rate*100).toFixed(1)}% drop`):""}),r.reduce((a,i)=>i.dropoff_rate>a.dropoff_rate?i:a).dropoff_rate>.2,c.SUCCESS}catch(t){return s.error(`Funnel error: ${t instanceof Error?t.message:String(t)}`),c.ERROR}}async function Ta(e){try{let t=e.period||"weekly",n=parseInt(e.cohorts,10)||8;s.info(`Fetching ${t} cohort data (${n} cohorts)...`);let r=await q("/cohort",{query:{period:t,cohorts:String(n)}});if(r.length===0)return c.SUCCESS;let o=Object.keys(r[0]).slice(1);return r.forEach(a=>{let l=String(a.cohort).padEnd(15);o.forEach(u=>{let m=a[u];if(typeof m=="number"){let y=m/100,p=String(Math.round(m)).padEnd(8);y>=.7?p=Ie.green(p):y>=.4?p=Ie.yellow(p):p=Ie.red(p),l+=p+" "}})}),c.SUCCESS}catch(t){return s.error(`Cohort error: ${t instanceof Error?t.message:String(t)}`),c.ERROR}}async function ja(e,t){try{if(e==="stats"){s.info("Fetching newsletter stats...");let n=await q("/newsletter/stats");return c.SUCCESS}if(e==="subscribers"){let n=parseInt(t.limit,10)||10,r=parseInt(t.offset,10)||0;return s.info(`Fetching ${n} newsletter subscribers (offset ${r})...`),(await q("/newsletter/subscribers",{query:{limit:String(n),offset:String(r)}})).forEach(a=>{}),c.SUCCESS}if(e==="send"){let n=t.template;return n?t.yes?(s.info(`Sending newsletter with template: ${n}`),await q("/newsletter/send",{method:"POST",body:{template_id:n}}),c.SUCCESS):(s.warn("Use --yes to confirm newsletter send"),c.ERROR):(s.error("--template is required"),c.ERROR)}return s.error(`Unknown newsletter subcommand: ${e}`),c.ERROR}catch(n){return s.error(`Newsletter error: ${n instanceof Error?n.message:String(n)}`),c.ERROR}}async function Ua(e,t){try{if(e==="status"){s.info("Fetching referral program status...");let n=await q("/referral/status");return c.SUCCESS}if(e==="config"){s.info("Fetching referral config...");let n=await q("/referral/config");return Object.entries(n).forEach(([r,o])=>{}),c.SUCCESS}return e==="leaderboard"?(s.info("Fetching referral leaderboard..."),(await q("/referral/leaderboard")).forEach(r=>{let o=r.rank===1?"\u{1F947}":r.rank===2?"\u{1F948}":"\u{1F949}"}),c.SUCCESS):(s.error(`Unknown referral subcommand: ${e}`),c.ERROR)}catch(n){return s.error(`Referral error: ${n instanceof Error?n.message:String(n)}`),c.ERROR}}async function Da(e,t){try{if(e==="list"){s.info("Fetching active experiments...");let n=await q("/experiment");return n.length===0?c.SUCCESS:(n.forEach(r=>{let o=r.status==="running"?Ie.yellow("\u25CF"):Ie.green("\u2713")}),c.SUCCESS)}if(e==="create"){let n=t.name,r=t.variants?.split(","),o=t.metric;if(!n||!r||!o)return s.error("Required: --name <name> --variants <A,B> --metric <metric>"),c.ERROR;s.info(`Creating experiment: ${n}`);let a=await q("/experiment",{method:"POST",body:{name:n,variants:r,metric:o}});return c.SUCCESS}if(e==="results"){let n=t.id;if(!n)return s.error("--id is required"),c.ERROR;s.info(`Fetching results for experiment ${n}...`);let r=await q(`/experiment/${n}`);return r.winner,r.significance,c.SUCCESS}if(e==="conclude"){let n=t.id,r=t.winner;return!n||!r?(s.error("Required: --id <id> --winner <variant>"),c.ERROR):t.yes?(s.info(`Concluding experiment ${n} with winner: ${r}`),await q(`/experiment/${n}`,{method:"POST",body:{action:"conclude",winner:r}}),c.SUCCESS):(s.warn("Use --yes to confirm experiment conclusion"),c.ERROR)}return s.error(`Unknown experiment subcommand: ${e}`),c.ERROR}catch(n){return s.error(`Experiment error: ${n instanceof Error?n.message:String(n)}`),c.ERROR}}async function La(e){try{let t=e.focus||"activation",n=e.depth||"quick";s.info(`Generating growth insights (focus: ${t}, depth: ${n})...`);let r=await q("/pulse",{query:{focus:t,depth:n}});return r.insights.forEach(o=>{}),r.recommendations.forEach(o=>{}),c.SUCCESS}catch(t){return s.error(`Pulse error: ${t instanceof Error?t.message:String(t)}`),c.ERROR}}function Vn(e){let t=e.command("growth").description("Growth engine operations \u2014 metrics, funnel, cohorts, AI insights");t.command("dashboard").description("Growth metrics overview").option("--period <7d|30d|90d>","Time period","7d").option("--compare","Show period-over-period deltas").action(async a=>{process.exit(await Ia(a))}),t.command("funnel").description("Conversion funnel analysis").option("--period <7d|30d|90d>","Time period","7d").option("--segment <organic|paid|referral>","Traffic segment","all").action(async a=>{process.exit(await Na(a))}),t.command("cohort").description("Cohort retention analysis").option("--period <weekly|monthly>","Cohort period","weekly").option("--cohorts <n>","Number of cohorts","8").action(async a=>{process.exit(await Ta(a))}),t.command("newsletter <subcommand>").description("Newsletter management").action(async(a,i)=>{process.exit(await ja(a,i))}),t.command("referral <subcommand>").description("Referral program management").action(async(a,i)=>{process.exit(await Ua(a,i))}),t.command("experiment <subcommand>").description("A/B testing and experiment management").option("--id <id>","Experiment ID").option("--name <name>","Experiment name").option("--variants <A,B>","Comma-separated variants").option("--metric <metric>","Success metric").option("--winner <variant>","Winning variant").action(async(a,i)=>{process.exit(await Da(a,i))}),t.command("pulse").description("AI-generated growth insights (AARRR: Acquisition, Activation, Retention, Revenue, Referral)").option("--focus <aarrr>","Focus area (acquisition|activation|retention|revenue|referral)","activation").option("--depth <quick|deep>","Analysis depth","quick").action(async a=>{process.exit(await La(a))}),t.option("--dry-run","Preview changes without applying them").option("--format <json|text>","Output format","text").option("--yes","Skip confirmation prompts")}import Nt,{lstatSync as Fa,readdirSync as Tt,readFileSync as Ma,writeFileSync as Ga}from"fs";import ce from"path";import{fileURLToPath as Ka}from"url";import B from"picocolors";var qa=ce.dirname(Ka(import.meta.url));function jt(){let e=new Map,t=ce.join(qa,"../../../apps");if(!Nt.existsSync(t))return e;try{let n=Tt(t);for(let r of n){let o=ce.join(t,r),a=ce.join(o,"messages");Nt.existsSync(a)&&Fa(a).isDirectory()&&e.set(r,a)}}catch(n){s.debug(`Error scanning apps directory: ${n instanceof Error?n.message:String(n)}`)}return e}function Ge(e,t=""){let n=new Set;for(let[r,o]of Object.entries(e)){let a=t?`${t}.${r}`:r;if(typeof o=="object"&&o!==null&&!Array.isArray(o)){let i=Ge(o,a);for(let l of i)n.add(l)}else n.add(a)}return n}function Ke(e){try{let t=Ma(e,"utf-8");return JSON.parse(t)}catch(t){return s.debug(`Failed to parse ${e}: ${t instanceof Error?t.message:String(t)}`),null}}async function Va(e){if(s.info("Syncing language files across the project..."),(await _("translate",{filter:"landing-page",dryRun:e.dryRun,interactive:e.interactive??!0})).exitCode!==0){s.warn("Landing page translate script not found or failed. Trying sync-lang.mjs...");let n=await x({command:"node",args:["scripts/sync-lang.mjs"],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Sync language files"});n.exitCode!==0&&(s.error("Failed to sync language files"),process.exit(n.exitCode)),e.format==="json"?console.log(JSON.stringify({command:"i18n:sync",exitCode:0,success:!0},null,2)):e.dryRun||s.success("Language files synced successfully")}else e.format==="json"?console.log(JSON.stringify({command:"i18n:sync",exitCode:0,success:!0},null,2)):e.dryRun||s.success("Language files synced successfully")}async function Wa(e){s.info("Validating translation files...");let t=jt();t.size===0&&(s.warn("No message directories found in the project"),process.exit(c.NOT_FOUND));let n=new Map,r=!1;for(let[o,a]of t){let i=[];try{let l=Tt(a).filter(p=>p.endsWith(".json"));if(l.length===0)continue;let u=ce.join(a,"en.json"),m=Ke(u);if(!m){s.warn(`Could not load reference locale en.json from ${o}`);continue}let y=Ge(m);for(let p of l){if(p==="en.json")continue;let b=p.replace(".json",""),M=ce.join(a,p),Y=Ke(M);if(!Y){s.debug(`Could not parse ${p}`);continue}let H=Ge(Y),Re=Array.from(y).filter(g=>!H.has(g)),pe=Array.from(H).filter(g=>!y.has(g)),Te=y.size>0?(y.size-Re.length)/y.size*100:100;(Re.length>0||pe.length>0)&&(r=!0),i.push({locale:b,totalKeys:y.size,missingKeys:Re,extraKeys:pe,coverage:Math.round(Te*100)/100})}i.length>0&&n.set(o,i)}catch(l){s.debug(`Error validating ${o}: ${l instanceof Error?l.message:String(l)}`)}}if(e.format==="json"){let o={command:"i18n:validate",success:!r,results:Array.from(n.entries()).reduce((a,[i,l])=>(a[i]=l,a),{})};console.log(JSON.stringify(o,null,2))}else for(let[o,a]of n){s.info(`${B.bold(o)}:`);for(let i of a){let u=(i.coverage>=100?B.green:i.coverage>=80?B.yellow:B.red)(`${i.coverage}%`);s.info(` ${B.cyan(i.locale.padEnd(5))} ${u.padEnd(20)} [${i.totalKeys} keys]`),i.missingKeys.length>0&&s.warn(` Missing: ${i.missingKeys.slice(0,5).join(", ")}${i.missingKeys.length>5?` (+ ${i.missingKeys.length-5} more)`:""}`),i.extraKeys.length>0&&s.warn(` Extra: ${i.extraKeys.slice(0,5).join(", ")}${i.extraKeys.length>5?` (+ ${i.extraKeys.length-5} more)`:""}`)}}r&&process.exit(c.CONFLICT)}async function Ba(e,t){/^[a-z]{2}(-[A-Z]{2})?$/.test(e)||(s.error(`Invalid locale format: ${e}. Use 'en', 'es', 'zh-CN', etc.`),process.exit(c.INVALID_ARGS));let n=jt();n.size===0&&(s.error("No message directories found in the project"),process.exit(c.NOT_FOUND));let r=[],o=!1;for(let[i,l]of n){let u=ce.join(l,`${e}.json`);if(Nt.existsSync(u)){s.warn(`Locale ${e} already exists in ${i}`);continue}try{let b=function(M){for(let Y in M)typeof M[Y]=="string"?M[Y]="":typeof M[Y]=="object"&&M[Y]!==null&&b(M[Y])};var a=b;let m=ce.join(l,"en.json"),y=Ke(m);if(!y){s.warn(`Could not load reference en.json from ${i}, skipping...`);continue}let p=JSON.parse(JSON.stringify(y));b(p),t.dryRun?s.info(`Would create ${e} locale in ${i}`):(Ga(u,JSON.stringify(p,null,2)+`
|
|
720
|
+
`),r.push({app:i,path:u}),s.success(`Created ${e} locale in ${i}`))}catch(m){s.error(`Failed to create locale in ${i}: ${m instanceof Error?m.message:String(m)}`),o=!0}}t.format==="json"?console.log(JSON.stringify({command:"i18n:add",locale:e,dryRun:t.dryRun||!1,created:r,success:!o},null,2)):t.dryRun||s.success(`Locale '${e}' added to ${r.length} app(s)`),o&&process.exit(c.ERROR)}async function za(e){s.info("Checking translation coverage...");let t=jt();t.size===0&&(s.warn("No message directories found in the project"),process.exit(c.NOT_FOUND));let n=new Map,r=0,o=0;for(let[a,i]of t){let l=[];try{let u=Tt(i).filter(b=>b.endsWith(".json"));if(u.length===0)continue;let m=ce.join(i,"en.json"),y=Ke(m);if(!y){s.debug(`Could not load reference locale from ${a}`);continue}let p=Ge(y);for(let b of u){let M=b.replace(".json",""),Y=ce.join(i,b),H=Ke(Y);if(!H)continue;let Re=Ge(H),pe=Array.from(Re).filter(ee=>{let Be=H[ee.split(".")[0]];return Be!==void 0&&Be!==""}).length,Te=p.size-pe,g=p.size>0?pe/p.size*100:0;l.push({locale:M,coverage:Math.round(g*100)/100,totalKeys:p.size,translatedKeys:pe,missingKeys:Te})}l.length>0&&(n.set(a,l),o++,r+=l.reduce((b,M)=>b+M.coverage,0)/l.length)}catch(u){s.debug(`Error checking status for ${a}: ${u instanceof Error?u.message:String(u)}`)}}if(e.format==="json"){let a={command:"i18n:status",averageCoverage:o>0?Math.round(r/o*100)/100:0,results:Array.from(n.entries()).reduce((i,[l,u])=>(i[l]=u,i),{})};console.log(JSON.stringify(a,null,2))}else{for(let[a,i]of n){s.info(`${B.bold(a)}:`);for(let l of i){let u="",y=Math.round(l.coverage/100*20);u="\u2588".repeat(y)+"\u2591".repeat(20-y);let b=(l.coverage>=100?B.green:l.coverage>=80?B.yellow:B.red)(`${l.coverage.toFixed(1)}%`);s.info(` ${B.cyan(l.locale.padEnd(5))} [${u}] ${b}`),s.info(` ${l.translatedKeys}/${l.totalKeys} keys (${l.missingKeys} missing)`)}}if(o>0){let a=r/o,i=a>=100?B.green:a>=80?B.yellow:B.red;s.info(`
|
|
721
|
+
Average coverage: ${i(a.toFixed(1)+"%")}`)}}}function Wn(e){let t=e.command("i18n").description("Manage internationalization and translations");t.command("sync").description("Synchronize language translations across the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Va({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("validate").description("Validate all locale files for missing or extra keys").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Wa({format:n.format||r.format,interactive:r.yes!==!0})}),t.command("add <locale>").description("Add a new locale with template files (e.g. 'en', 'es', 'zh-CN')").option("--dry-run","Preview what would be created without writing files").option("--format <type>","Output format: json or plain").action(async(n,r)=>{let o=r.optsWithGlobals?.()||r;await Ba(n,{dryRun:r.dryRun||!1,format:r.format||o.format,interactive:o.yes!==!0})}),t.command("status").description("Show translation coverage per locale and app").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await za({format:n.format||r.format,interactive:r.yes!==!0})})}import*as dt from"@clack/prompts";async function Ya(e){let t=e.lite?"docker-compose.lite.yml":void 0,n=e.lite?"Starting lite stack (PostgreSQL + Redis)":"Starting full stack";d(n+"...","info");let r=await $e("up -d",{file:t,profile:e.profile,dryRun:e.dryRun});r.exitCode===0?d("Infrastructure stack started successfully","success"):(d("Failed to start infrastructure stack","error"),k("Docker compose output",{stderr:r.stderr}),process.exit(r.exitCode||1))}async function Ha(e){d("Stopping infrastructure stack...","info");let t=await $e("down",{dryRun:e.dryRun});t.exitCode===0?d("Infrastructure stack stopped","success"):(d("Failed to stop infrastructure stack","error"),k("Docker compose output",{stderr:t.stderr}),process.exit(t.exitCode||1))}async function Ja(e){d("Checking infrastructure status...","info");let t=await $e("ps --format json",{dryRun:e.dryRun});if(t.exitCode===0)try{let n=JSON.parse(t.stdout);if(e.format==="json")E(n,{format:"json"});else{let r=n.map(o=>({name:o.Name,status:o.Status,ports:o.Ports||"-"}));E(r,{format:"table"})}}catch{E(t.stdout,{format:"plain"})}else d("Failed to check infrastructure status","error"),k("Docker compose output",{stderr:t.stderr}),process.exit(t.exitCode||1)}async function Qa(e,t){let n=t||{},r=e?`Fetching logs for ${e}...`:"Fetching infrastructure logs...";d(r,"info");let o=["logs","--tail","50"];e&&o.push(e);let a=await $e(o.join(" "),{dryRun:n.dryRun});a.exitCode===0?E(a.stdout,{format:"plain"}):(d("Failed to fetch logs","error"),k("Docker compose output",{stderr:a.stderr}),process.exit(a.exitCode||1))}async function Xa(e){let t=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!e.yes&&!e.dryRun)if(t){let r=await dt.confirm({message:"Reset infrastructure? This will remove all containers and volumes.",initialValue:!1});(dt.isCancel(r)||!r)&&(d("Reset cancelled","warn"),process.exit(0))}else d("Infrastructure reset requires explicit --yes confirmation (use: nebutra infra reset --yes)","error"),process.exit(1);d("Resetting infrastructure stack...","warn");let n=await $e("down -v",{dryRun:e.dryRun});n.exitCode===0?d("Infrastructure reset successfully","success"):(d("Failed to reset infrastructure","error"),k("Docker compose output",{stderr:n.stderr}),process.exit(n.exitCode||1))}function Bn(e){e.command("infra <verb> [service]").description("Manage Docker Compose infrastructure (PostgreSQL, Redis, Meilisearch, etc.)").option("--dry-run","Show what would be run without executing").option("--yes","Skip confirmations (especially for reset)").option("--lite","Use lite stack (PostgreSQL + Redis only)").option("--profile <name>","Docker Compose profile to use (e.g., search)").option("--format <type>","Output format: json, plain","plain").action(async(n,r,o)=>{let a=o.optsWithGlobals?.(),i={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:o.format||a?.format,lite:o.lite||!1,profile:o.profile};try{switch(n){case"up":await Ya(i);break;case"down":await Ha(i);break;case"status":await Ja(i);break;case"logs":await Qa(r,i);break;case"reset":await Xa(i);break;default:d(`Unknown infra subcommand: ${n}. Valid commands: up, down, status, logs, reset`,"error"),process.exit(1)}}catch(l){let u=l instanceof Error?l.message:String(l);d(`Infrastructure command failed: ${u}`,"error"),k("Full error",{error:l}),process.exit(1)}}).addHelpText("after",`
|
|
632
722
|
Examples:
|
|
633
723
|
nebutra infra up Start full stack
|
|
634
724
|
nebutra infra up --lite Start lite stack (PostgreSQL + Redis)
|
|
@@ -645,36 +735,36 @@ Flags:
|
|
|
645
735
|
--lite Use docker-compose.lite.yml (PostgreSQL + Redis only)
|
|
646
736
|
--profile <name> Enable Docker Compose profile (search, notifications, etc.)
|
|
647
737
|
--format <type> Output format: json, plain (default: plain, auto-json in non-TTY)
|
|
648
|
-
`)}import
|
|
649
|
-
`),o?(s.success("Configuration saved to nebutra.config.json"),s.info('Initialization complete! You can now use "nebutra add" to start injecting components.')):(await new Promise(i=>setTimeout(i,800)),J.spinner().stop(
|
|
650
|
-
`));let r=
|
|
738
|
+
`)}import Ut from"fs";import Za from"path";import*as J from"@clack/prompts";import ut from"picocolors";var ei={$schema:"https://nebutra.com/schema.json",componentsDirectory:"packages/design/ui/src/components",tailwind:{config:"tailwind.config.ts",css:"packages/design/tokens/styles.css",baseColor:"slate",cssVariables:!0},aliases:{components:"@nebutra/ui",utils:"@nebutra/ui/utils"}};function ti(){return process.stdout.isTTY===!0}function ni(e){return e.yes===!0||e.dryRun===!0||!ti()}function ri(e,t,n){return{operation:n?"update":"create",path:e,...n&&{before:n},after:t}}async function zn(e={}){let t=process.cwd(),n=Za.join(t,"nebutra.config.json"),r=Ut.existsSync(n);e.ifNotExists&&r&&(s.info("nebutra.config.json already exists, skipping initialization."),process.exit(c.SUCCESS));let o=ni(e);o||J.intro(ut.bgCyan(ut.black(" nebutra init "))),r&&!o&&(J.log.warn("nebutra.config.json already exists in this directory."),await J.confirm({message:"Do you want to overwrite it?",initialValue:!1})||(J.outro("Operation aborted."),process.exit(c.CANCELLED)));let a={...ei};if(e.dryRun){let i=r?JSON.parse(Ut.readFileSync(n,"utf-8")):void 0,l=ri(n,a,i);console.log(JSON.stringify(l,null,2)),s.info("Dry-run completed. No changes made."),process.exit(c.DRY_RUN_OK)}o?s.info("Initializing Nebutra CLI configuration..."):J.spinner().start("Initializing Nebutra CLI configuration..."),Ut.writeFileSync(n,JSON.stringify(a,null,2)+`
|
|
739
|
+
`),o?(s.success("Configuration saved to nebutra.config.json"),s.info('Initialization complete! You can now use "nebutra add" to start injecting components.')):(await new Promise(i=>setTimeout(i,800)),J.spinner().stop(ut.green("Configuration saved to nebutra.config.json")),J.outro(ut.cyan('Initialization complete! You can now use "nebutra add" to start injecting components.'))),process.exit(c.SUCCESS)}import Dt from"fs/promises";import ai from"os";import Lt from"path";import*as A from"@clack/prompts";import Q from"picocolors";var oi="https://analytics.nebutra.com";function si(e={}){if(e.noTelemetry===!0)return!0;let t=process.env.NEBUTRA_TELEMETRY;return t==="0"||t==="false"}function mt(e,t={}){si(t)||(async()=>{try{let n=await import("@nebutra/analytics");if(typeof n.createAnalyticsClient!="function")return;let r=n.createAnalyticsClient({posthog:{apiKey:process.env.NEBUTRA_POSTHOG_KEY??"",host:process.env.NEBUTRA_POSTHOG_HOST??oi},onError:()=>{}});if(typeof r?.track!="function")return;let o=r.track("license.cli",e);o&&typeof o.then=="function"&&await o.catch(()=>{})}catch{}})()}var Yn=process.env.NEBUTRA_LICENSE_API_URL??"https://nebutra.com";async function Hn(){let e=ai.homedir(),t=Lt.join(e,".config","nebutra");try{await Dt.mkdir(t,{recursive:!0})}catch{}return t}async function ii(e){let t=`${Yn}/api/license/validate?key=${encodeURIComponent(e)}`,n;try{n=await fetch(t,{signal:AbortSignal.timeout(1e4)})}catch{throw new Error(`Could not reach the Nebutra license server (${Yn}). Check your internet connection or set NEBUTRA_LICENSE_API_URL.`)}if(n.status===404)throw new Error("License key not found. Please check the key and try again.");if(n.status===410)throw new Error("License key has expired. Please renew your license at nebutra.com.");if(!n.ok)throw new Error(`License validation failed (HTTP ${n.status}). Please try again later.`);let r=await n.json();if(!r.valid)throw new Error("License key is not valid. Please check the key and try again.");return{tier:r.tier??"UNKNOWN",type:r.type??"UNKNOWN"}}function ci(e){let t=e instanceof Error?e.message:String(e);return/Invalid license key format/i.test(t)?"invalid_format":/license server/i.test(t)?"network_error":/not found/i.test(t)?"not_found":/expired/i.test(t)?"expired":/not valid/i.test(t)?"invalid_key":/HTTP \d+/i.test(t)?"http_error":"unknown"}async function li(e,t){let n=t.quiet===!0;n||A.intro(Q.bgCyan(Q.black(" Nebutra License Activation "))),mt({action:"activate_attempted"});try{if(e.length<10)throw new Error("Invalid license key format.");n||A.log.step("Validating license key with Nebutra...");let{tier:r,type:o}=await ii(e),a=await Hn(),i=Lt.join(a,"license.json");await Dt.writeFile(i,JSON.stringify({licenseKey:e,tier:r,type:o,activatedAt:new Date().toISOString()},null,2),"utf-8"),n||(A.log.success(Q.green(`${r} license activated and saved to ${i}`)),A.outro(Q.cyan("Premium CLI capabilities are now unlocked!"))),mt({action:"activated",tier:r,type:o})}catch(r){mt({action:"failed",error_code:ci(r)}),n||(A.log.error(Q.red("Activation failed")),s.error(r instanceof Error?r.message:String(r)),A.outro("Please check your key and try again.")),process.exit(1)}}async function di(e){let t=e.quiet===!0;t||A.intro(Q.bgCyan(Q.black(" Nebutra License Status ")));try{let n=await Hn(),r=Lt.join(n,"license.json"),o=await Dt.readFile(r,"utf-8"),a=JSON.parse(o);if(a.licenseKey)t||(A.log.success(Q.green("License is active!")),A.log.info(`Key: ${a.licenseKey.substring(0,8)}...`),a.tier&&A.log.info(`Tier: ${Q.bold(a.tier)}`),a.type&&A.log.info(`Type: ${a.type}`),A.log.info(`Activated: ${new Date(a.activatedAt).toLocaleString()}`),A.outro(Q.cyan("You are ready to use premium features.")));else throw new Error("License key not found in config.")}catch{t||(A.log.warn(Q.yellow("No active license found locally.")),A.log.message(Q.dim("Run `nebutra license activate <key>` to unlock premium features.")),A.outro("")),process.exit(1)}}function Jn(e){let t=e.command("license").description("Manage your Nebutra-Sailor commercial license");t.command("activate <key>").description("Activate a commercial license key for local development").option("--quiet","Suppress output").action(async(n,r)=>{await li(n,r)}),t.command("status").description("Check the locally configured license status").option("--quiet","Suppress output").action(async n=>{await di(n)})}import{spawn as ui}from"child_process";import{dirname as mi,resolve as pi}from"path";import{fileURLToPath as fi}from"url";import*as oe from"@clack/prompts";import me from"picocolors";var gi=mi(fi(import.meta.url));function yi(){let e=[()=>pi(gi,"../../../mcp/dist/server/contextServer.js")];for(let t of e)try{return t()}catch(n){s.debug("MCP server binary resolution strategy failed",{error:n})}return"nebutra-mcp"}function Qn(e){e.command("mcp").description("Start the Nebutra MCP server for Cursor/Windsurf").option("--stdio","Use stdio transport (default)",!0).action(async t=>{oe.intro(me.bgCyan(me.black(" nebutra mcp ")));try{let n=yi();oe.log.info(me.cyan("Starting Nebutra MCP server for Cursor/Windsurf integration...")),oe.log.info(me.dim(`Press Ctrl+C to stop the server.
|
|
740
|
+
`));let r=ui(process.execPath,[n],{stdio:"inherit",env:{...process.env}});r.on("close",o=>{o===0?oe.outro(me.green("MCP server stopped gracefully.")):o===null&&oe.outro(me.yellow("MCP server terminated.")),process.exit(o??0)}),r.on("error",o=>{s.error(`
|
|
651
741
|
Failed to start MCP server: ${o.message}`),o.code==="ENOENT"&&(s.warn(`
|
|
652
|
-
Tip: Ensure @nebutra/mcp is installed or available in your PATH.`),s.info(" Install globally: npm install -g @nebutra/mcp")),process.exit(1)}),process.on("SIGINT",()=>{
|
|
653
|
-
Shutting down MCP server...`)),r.kill("SIGINT")}),process.on("SIGTERM",()=>{
|
|
654
|
-
`),process.exit(c.SUCCESS)}
|
|
655
|
-
`);for(let t of Object.values(
|
|
656
|
-
`),process.exit(c.SUCCESS)),
|
|
657
|
-
${
|
|
658
|
-
${
|
|
659
|
-
${
|
|
660
|
-
${
|
|
661
|
-
${
|
|
662
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
742
|
+
Tip: Ensure @nebutra/mcp is installed or available in your PATH.`),s.info(" Install globally: npm install -g @nebutra/mcp")),process.exit(1)}),process.on("SIGINT",()=>{oe.log.info(me.yellow(`
|
|
743
|
+
Shutting down MCP server...`)),r.kill("SIGINT")}),process.on("SIGTERM",()=>{oe.log.info(me.yellow("Terminating MCP server...")),r.kill("SIGTERM")})}catch(n){oe.log.error(me.red("Error: Unable to start MCP server. Ensure @nebutra/mcp is properly installed.")),n instanceof Error&&s.error(n.message),process.exit(1)}})}import{promises as Ft}from"fs";import{resolve as Xn}from"path";import*as Oe from"@clack/prompts";import w from"picocolors";var le={"ai-saas":{name:"ai-saas",description:"Full-stack AI SaaS platform with auth, billing, and analytics",apps:["web","landing-page","api-gateway","studio","admin"],features:["billing","ai","search","analytics","sso"],theme:"vibrant",stack:["Next.js 16","Hono","Prisma","PostgreSQL","OpenAI API"]},marketing:{name:"marketing",description:"Marketing website with blog and CMS integration",apps:["landing-page","blog","studio"],features:["blog","growth","analytics","newsletter"],theme:"minimal",stack:["Next.js 16","Sanity CMS","Mailgun"]},dashboard:{name:"dashboard",description:"Enterprise dashboard with admin panel and RBAC",apps:["web","admin","api-gateway"],features:["billing","admin","analytics","sso"],theme:"dark-dense",stack:["Next.js 16","Hono","Prisma","PostgreSQL"]},overseas:{name:"overseas",description:"Global SaaS with multi-language and localization support",apps:["web","landing-page","api-gateway","blog"],features:["billing","ai","search","i18n"],theme:"ocean",stack:["Next.js 16","i18next","Hono"]},growth:{name:"growth",description:"Growth-focused platform with community and content",apps:["web","landing-page","blog"],features:["growth","analytics","newsletter","community"],theme:"vibrant",stack:["Next.js 16","Mailgun","Mixpanel"]},creative:{name:"creative",description:"Creative portfolio and landing page builder",apps:["landing-page","blog"],features:["blog","growth"],theme:"gradient",stack:["Next.js 16","Framer Motion"]},"blog-portfolio":{name:"blog-portfolio",description:"Personal blog and portfolio site",apps:["landing-page","blog"],features:["blog"],theme:"minimal",stack:["Next.js 16"]},community:{name:"community",description:"Community platform with real-time features",apps:["web","landing-page"],features:["community","growth","realtime"],theme:"vibrant",stack:["Next.js 16","Hono","Redis","Socket.io"]},"one-person":{name:"one-person",description:"Solo founder landing page and quick launch",apps:["landing-page"],features:["blog"],theme:"minimal",stack:["Next.js 16"]},full:{name:"full",description:"Everything included - all apps and all features",apps:["web","landing-page","api-gateway","studio","admin","blog","storybook","design-docs","docs"],features:["billing","ai","ecommerce","web3","community","blog","growth","search","sso","admin","analytics","newsletter","realtime","upload"],theme:"vibrant",stack:["Next.js 16","Hono","Prisma","PostgreSQL","Redis","OpenAI","Web3"]}};async function hi(e){if(e.format==="json"){let t={presets:Object.values(le).map(n=>({name:n.name,description:n.description,appsCount:n.apps.length,featuresCount:n.features.length,apps:n.apps,features:n.features,theme:n.theme}))};process.stdout.write(JSON.stringify(t,null,2)+`
|
|
744
|
+
`),process.exit(c.SUCCESS)}Oe.intro(w.bgCyan(w.black(" nebutra preset list "))),s.info(`Available presets:
|
|
745
|
+
`);for(let t of Object.values(le)){let n=w.bold(t.name),r=w.dim(t.description),o=w.gray(`(${t.apps.length} apps, ${t.features.length} features)`);s.info(`${n} ${o}`),s.info(` ${r}`)}process.exit(c.SUCCESS)}async function bi(e,t){e||(s.error("Please specify a preset name"),s.info("Available presets: "+Object.keys(le).join(", ")),process.exit(c.INVALID_ARGS));let n=le[e];n||(s.error(`Preset not found: ${e}`),s.info("Available presets: "+Object.keys(le).join(", ")),process.exit(c.NOT_FOUND)),t.format==="json"&&(process.stdout.write(JSON.stringify(n,null,2)+`
|
|
746
|
+
`),process.exit(c.SUCCESS)),Oe.intro(w.bgCyan(w.black(` nebutra preset show ${e} `))),s.info(`
|
|
747
|
+
${w.bold(n.name)}`),s.info(n.description),s.info(`
|
|
748
|
+
${w.bold("Apps:")}`),n.apps.forEach(r=>{s.info(` \u2022 ${r}`)}),s.info(`
|
|
749
|
+
${w.bold("Features:")}`),n.features.forEach(r=>{s.info(` \u2022 ${r}`)}),n.theme&&(s.info(`
|
|
750
|
+
${w.bold("Theme:")}`),s.info(` ${n.theme}`)),n.stack&&(s.info(`
|
|
751
|
+
${w.bold("Recommended Stack:")}`),n.stack.forEach(r=>{s.info(` \u2022 ${r}`)})),process.exit(c.SUCCESS)}async function vi(e,t){let n=le[e];n||(s.error(`Preset not found: ${e}`),s.info("Available presets: "+Object.keys(le).join(", ")),process.exit(c.NOT_FOUND));let r=h(),o=Xn(r,"nebutra.config.ts");if(t.dryRun){let i={mode:"dry-run",timestamp:new Date().toISOString(),action:"preset.apply",preset:e,configFile:o,changes:{preset:e,apps:n.apps,features:n.features,theme:n.theme||"default"}};process.stdout.write(JSON.stringify(i,null,2)+`
|
|
752
|
+
`),process.exit(c.DRY_RUN_OK)}Oe.intro(w.bgCyan(w.black(" nebutra preset apply ")));let a=s.spinner();try{let i;try{i=await Ft.readFile(o,"utf-8")}catch{i=`export default {
|
|
663
753
|
preset: "${e}",
|
|
664
754
|
apps: ${JSON.stringify(n.apps,null,2).replace(/\n/g,`
|
|
665
755
|
`)},
|
|
666
756
|
features: ${JSON.stringify(n.features,null,2).replace(/\n/g,`
|
|
667
757
|
`)},
|
|
668
758
|
theme: "${n.theme||"default"}",
|
|
669
|
-
};`}let l=i.replace(/preset:\s*"[^"]*"/,`preset: "${e}"`);t.dryRun===!1&&(a.start(`Applying preset ${
|
|
759
|
+
};`}let l=i.replace(/preset:\s*"[^"]*"/,`preset: "${e}"`);t.dryRun===!1&&(a.start(`Applying preset ${w.cyan(e)}...`),await Ft.writeFile(o,l,"utf-8"),a.stop(`Applied preset ${w.cyan(e)}`,0),s.success("Preset applied successfully"),s.info(`${n.apps.length} apps enabled`),s.info(`${n.features.length} features enabled`),s.info(`Theme: ${n.theme||"default"}`)),process.exit(c.SUCCESS)}catch(i){a.stop(`Failed to apply preset: ${i instanceof Error?i.message:"Unknown error"}`,1),process.exit(c.ERROR)}}async function wi(e){Oe.intro(w.bgCyan(w.black(" nebutra preset env ")));let t=h();try{let n=await _("preset:env",{dryRun:e.dryRun,interactive:!1});n.exitCode===0?(n.stdout&&process.stdout.write(n.stdout),process.exit(c.SUCCESS)):(s.error("Failed to get environment variables"),n.stderr&&process.stderr.write(n.stderr),process.exit(c.ERROR))}catch(n){s.error(`Failed: ${n instanceof Error?n.message:"Unknown error"}`),process.exit(c.ERROR)}}async function Ri(e){Oe.intro(w.bgCyan(w.black(" nebutra preset features ")));let t=h(),n=Xn(t,"nebutra.config.ts");try{let o=(await Ft.readFile(n,"utf-8")).match(/features:\s*\[([\s\S]*?)\]/);o||(s.warn("No features found in nebutra.config.ts"),process.exit(c.CONFIG_ERROR));let i=o[1].split(",").map(l=>l.trim().replace(/['"]/g,"")).filter(l=>l.length>0);if(e.format==="json"){let l={features:i,count:i.length};process.stdout.write(JSON.stringify(l,null,2)+`
|
|
670
760
|
`),process.exit(c.SUCCESS)}s.info(`Features in current preset:
|
|
671
761
|
`),i.forEach(l=>{s.info(` \u2713 ${l}`)}),s.info(`
|
|
672
|
-
Total: ${i.length} features`),process.exit(c.SUCCESS)}catch(r){s.error(`Failed to read config: ${r instanceof Error?r.message:"Unknown error"}`),process.exit(c.ERROR)}}async function
|
|
673
|
-
`),process.exit(c.SUCCESS)}
|
|
674
|
-
${
|
|
675
|
-
`);let a=r.apps.filter(
|
|
676
|
-
${
|
|
677
|
-
`)}function
|
|
762
|
+
Total: ${i.length} features`),process.exit(c.SUCCESS)}catch(r){s.error(`Failed to read config: ${r instanceof Error?r.message:"Unknown error"}`),process.exit(c.ERROR)}}async function Si(e,t,n){(!e||!t)&&(s.error("Please specify two preset names to compare"),s.info("Available presets: "+Object.keys(le).join(", ")),process.exit(c.INVALID_ARGS));let r=le[e],o=le[t];if(r||(s.error(`Preset not found: ${e}`),process.exit(c.NOT_FOUND)),o||(s.error(`Preset not found: ${t}`),process.exit(c.NOT_FOUND)),n.format==="json"){let p={comparison:{presetA:e,presetB:t,appsOnly1:r.apps.filter(b=>!o.apps.includes(b)),appsOnly2:o.apps.filter(b=>!r.apps.includes(b)),appsCommon:r.apps.filter(b=>o.apps.includes(b)),featuresOnly1:r.features.filter(b=>!o.features.includes(b)),featuresOnly2:o.features.filter(b=>!r.features.includes(b)),featuresCommon:r.features.filter(b=>o.features.includes(b))}};process.stdout.write(JSON.stringify(p,null,2)+`
|
|
763
|
+
`),process.exit(c.SUCCESS)}Oe.intro(w.bgCyan(w.black(" nebutra preset diff "))),s.info(`
|
|
764
|
+
${w.bold("Preset Comparison: "+e+" vs "+t)}
|
|
765
|
+
`);let a=r.apps.filter(p=>!o.apps.includes(p)),i=o.apps.filter(p=>!r.apps.includes(p)),l=r.apps.filter(p=>o.apps.includes(p));s.info(w.bold("Apps:")),a.length>0&&(s.info(` Only in ${w.cyan(e)}:`),a.forEach(p=>s.info(` \u2022 ${p}`))),i.length>0&&(s.info(` Only in ${w.cyan(t)}:`),i.forEach(p=>s.info(` \u2022 ${p}`))),l.length>0&&(s.info(` ${w.green("Common")}:`),l.forEach(p=>s.info(` \u2713 ${p}`)));let u=r.features.filter(p=>!o.features.includes(p)),m=o.features.filter(p=>!r.features.includes(p)),y=r.features.filter(p=>o.features.includes(p));s.info(`
|
|
766
|
+
${w.bold("Features:")}`),u.length>0&&(s.info(` Only in ${w.cyan(e)}:`),u.forEach(p=>s.info(` \u2022 ${p}`))),m.length>0&&(s.info(` Only in ${w.cyan(t)}:`),m.forEach(p=>s.info(` \u2022 ${p}`))),y.length>0&&(s.info(` ${w.green("Common")}:`),y.forEach(p=>s.info(` \u2713 ${p}`))),process.exit(c.SUCCESS)}function Zn(e){let t=e.command("preset").description("Manage Nebutra presets");t.command("list").description("List all available presets").option("--format <type>","Output format: json, plain (default: plain)").action(async n=>{let r=n.optsWithGlobals?n.optsWithGlobals():n;await hi({format:n.format,yes:r.yes||!1})}),t.command("show [name]").description("Show details of a specific preset").option("--format <type>","Output format: json, plain (default: plain)").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await bi(n,{format:r.format,yes:o.yes||!1})}),t.command("apply <name>").description("Apply a preset to nebutra.config.ts").option("--dry-run","Preview changes without writing files").option("--yes","Skip confirmation prompts").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await vi(n,{dryRun:r.dryRun||!1,yes:o.yes||!1})}),t.command("env").description("Show environment variables for current preset").option("--dry-run","Preview without executing").action(async n=>{let r=n.optsWithGlobals?n.optsWithGlobals():n;await wi({dryRun:n.dryRun||!1,yes:r.yes||!1})}),t.command("features").description("List features enabled in current preset").option("--format <type>","Output format: json, plain (default: plain)").action(async n=>{let r=n.optsWithGlobals?n.optsWithGlobals():n;await Ri({format:n.format,yes:r.yes||!1})}),t.command("diff <a> <b>").description("Compare two presets side by side").option("--format <type>","Output format: json, plain (default: plain)").action(async(n,r,o)=>{let a=o.optsWithGlobals?o.optsWithGlobals():o;await Si(n,r,{format:o.format,yes:a.yes||!1})})}import*as U from"@clack/prompts";import X from"picocolors";var tr=Object.fromEntries(Object.entries(zt).map(([e,t])=>[e,t]));function nr(e){return typeof e=="boolean"?"boolean":"string"}function Ci(){let e=Qt().map(n=>({name:n.name,description:n.description,envFile:n.envFile??".env.local",providers:(n.providers??[]).map(r=>({id:r.id,description:r.description,dependencies:r.dependencies??[],devDependencies:r.devDependencies??[],env:r.env??[]})),files:(n.files??[]).map(r=>r.path)})),t=Array.from(new Set(e.flatMap(n=>n.providers.map(r=>r.id))));return{localFeatures:e,valueDomains:{components:e.map(n=>n.name),provider:t}}}function Ei(e){let t=e.arguments.map(a=>a.name==="components"?{...a,description:"Local feature names to install from the Nebutra registry"}:a),r=e.options.some(a=>a.flags.includes("--provider"))?e.options:[...e.options,{flags:"--provider <id>",description:"Specify a provider for a local feature install",required:!1,type:"string"}],o=[{command:"nebutra add queue --provider upstash",description:"Install the local queue feature starter with the Upstash provider"},{command:"nebutra add search --provider meilisearch --dry-run",description:"Preview the local search feature install plan as JSON"},...e.examples.filter(a=>a.command.includes("--21st")||a.command.includes("--v0"))];return{...e,arguments:t,options:r,examples:o,extensions:Ci()}}function ki(e){return{...e,extensions:{valueDomains:v,aliases:{storage:{supabase:"supabase-storage"},payment:{lemonsqueezy:"lemon"}}}}}function rr(e){let t={name:e.name,description:e.description,usage:e.usage,arguments:e.arguments||[],options:(e.options||[]).map(n=>({flags:n.flags,description:n.description,default:n.default,required:!1,type:nr(n.default)})),examples:e.examples||[]};return e.name==="add"?Ei(t):e.name==="create"?ki(t):t}function Oi(e){let n=(ge.subcommands||[]).find(r=>r.name===e);return n?rr(n):null}function xi(){return(ge.options??[]).map(e=>({flags:e.flags,description:e.description,required:!1,type:nr(e.default)}))}function $i(e){let t=ge.subcommands||[];return{name:ge.name,version:e,description:ge.description,usage:ge.usage||"nebutra [command] [options]",globalOptions:xi(),commands:t.map(rr),exitCodes:tr,timestamp:new Date().toISOString()}}function er(){return(ge.subcommands||[]).map(t=>t.name)}function pt(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
767
|
+
`)}function Ai(e){return e instanceof Error&&e.message.startsWith("EXIT:")}async function _i(e,t){let n=t.quiet===!0,r="0.1.0";try{if(t.all===!0&&(pt($i(r)),process.exit(0)),t.list===!0&&(pt(er()),process.exit(0)),(t.exitCodes===!0||t["exit-codes"]===!0)&&(pt(tr),process.exit(0)),e){let o=Oi(e);o||(n||(s.error(`Unknown command '${e}'`),s.info(`Available commands: ${er().join(", ")}`)),process.exit(2)),pt(o),process.exit(0)}n||(U.intro(X.bgCyan(X.black(" nebutra schema "))),U.log.info(X.cyan("Agent-friendly introspection of CLI commands, arguments, and value domains.")),U.log.message(""),U.log.info(X.bold("Usage:")),U.log.message(X.dim(" nebutra schema --all Show full command tree as JSON")),U.log.message(X.dim(" nebutra schema <command> Show schema for a specific command")),U.log.message(X.dim(" nebutra schema --list List available command names")),U.log.message(X.dim(" nebutra schema --exit-codes Show exit codes reference")),U.log.message(""),U.log.info(X.bold("Example:")),U.log.message(X.dim(" nebutra schema add")),U.log.message(X.dim(" nebutra schema --all | jq '.commands[0]'")),U.outro(X.cyan("All output is JSON for easy parsing by agents and automation tools."))),process.exit(0)}catch(o){if(Ai(o))throw o;n||s.error(o instanceof Error?o.message:String(o)),process.exit(1)}}function or(e){e.command("schema [command]").description("Show command schema and argument documentation (Agent-friendly JSON output)").option("--all","Show full schema for all commands").option("--list","List all available command names").option("--exit-codes","Show exit codes reference").action(async(t,n)=>{await _i(t,n)})}import*as ft from"@clack/prompts";import Z from"picocolors";function qe(){let e=process.env.MEILISEARCH_URL,t=process.env.TYPESENSE_URL,n=process.env.ALGOLIA_APP_ID;return e?{type:"meilisearch",url:e,health:!0,message:"Meilisearch configured"}:t?{type:"typesense",url:t,health:!0,message:"Typesense configured"}:n?{type:"algolia",health:!0,message:"Algolia configured"}:{type:"none",health:!1,message:"No search provider configured"}}async function Pi(e){d("Detecting search provider...","info");let t=qe();t.type==="none"&&(d("No search provider is configured","warn"),d("Set one of: MEILISEARCH_URL, TYPESENSE_URL, or ALGOLIA_APP_ID","info"),process.exit(0));let n={provider:t.type,configured:!0,url:t.url||"managed",health:t.health,message:t.message};e.format==="json"?E(n,{format:"json"}):(d(`Search Provider: ${Z.cyan(t.type.toUpperCase())}`,"success"),d(`Status: ${t.health?Z.green("Healthy"):Z.red("Unhealthy")}`,t.health?"success":"error"),t.url&&d(`URL: ${t.url}`,"info"))}async function Ii(e){d("Fetching search indexes...","info"),qe().type==="none"&&(d("No search provider configured","error"),process.exit(c.CONFIG_ERROR));let n=[{name:"products",documentCount:15420,size:"2.4 MB",lastUpdated:"2 hours ago"},{name:"articles",documentCount:3240,size:"1.1 MB",lastUpdated:"1 day ago"},{name:"users",documentCount:8900,size:"0.8 MB",lastUpdated:"30 minutes ago"}];if(e.format==="json")E(n,{format:"json"});else{d("Search Indexes","info");for(let r of n)d(`${Z.cyan(r.name)}: ${r.documentCount} documents (${r.size})`,"info")}}async function sr(e,t){let n=t||{},r=qe();r.type==="none"&&(d("No search provider configured","error"),process.exit(c.CONFIG_ERROR));let o=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!e&&!n.force&&!n.dryRun&&o){let i=await ft.confirm({message:"Reindex ALL indexes? This will recreate all indexes from scratch.",initialValue:!1});(ft.isCancel(i)||!i)&&(d("Reindex cancelled","warn"),process.exit(0))}!n.yes&&!n.dryRun&&!e&&(d("Full reindex requires --yes confirmation","error"),process.exit(c.INVALID_ARGS));let a=e||"all";if(d(`Reindexing ${Z.cyan(a)}...`,"info"),n.dryRun){d(`Would reindex ${a} with provider ${r.type}`,"info");return}d(`Reindexing ${a} on ${r.type}...`,"info"),d(`Index ${Z.cyan(a)} reindexed successfully`,"success")}async function Ni(e,t,n){d(`Querying ${Z.cyan(e)} for "${Z.yellow(t)}"...`,"info");let r=qe();r.type==="none"&&(d("No search provider configured","error"),process.exit(c.CONFIG_ERROR));let o=[{id:"1",title:"Sample Result 1",score:.95,_formatted:{title:"Sample <mark>Result</mark> 1",content:"Contains search term..."}},{id:"2",title:"Another Result",score:.87,_formatted:{title:"Another <mark>Result</mark>",content:"Also matches search..."}}],a={index:e,query:t,provider:r.type,hits:o.length,limit:n.limit||10,results:o};if(n.format==="json")E(a,{format:"json"});else{d(`Found ${Z.cyan(String(o.length))} results for "${t}"`,"success");for(let i of o)d(`${Z.dim(String(i.score))}: ${i.title}`,"info")}}async function Ti(e){d("Fetching search statistics...","info");let t=qe();t.type==="none"&&(d("No search provider configured","error"),process.exit(c.CONFIG_ERROR));let n={provider:t.type,indexes:[{name:"products",documents:15420,size:24e5,lastUpdated:"2024-03-30T14:22:00Z"},{name:"articles",documents:3240,size:11e5,lastUpdated:"2024-03-29T08:15:00Z"},{name:"users",documents:8900,size:8e5,lastUpdated:"2024-03-30T14:52:00Z"}],totalDocuments:27560,totalSize:43e5};if(e.format==="json")E(n,{format:"json"});else{d("Search Statistics","info"),d(`Provider: ${Z.cyan(n.provider)}`,"info"),d(`Total Indexes: ${n.indexes.length}`,"info"),d(`Total Documents: ${n.totalDocuments.toLocaleString()}`,"info"),d(`Total Size: ${(n.totalSize/1024/1024).toFixed(2)} MB`,"info"),d("Index Breakdown:","info");for(let r of n.indexes)d(` ${Z.cyan(r.name)}: ${r.documents.toLocaleString()} docs (${(r.size/1024/1024).toFixed(2)} MB)`,"info")}}function ar(e){e.command("search <verb> [args...]").description("Manage search engine (Meilisearch, Typesense, or Algolia)").option("--dry-run","Show what would be run without executing").option("--yes","Skip confirmations").option("--format <type>","Output format: json, plain, table","plain").option("--force","Force operation (e.g., recreate index)").option("--limit <n>","Result limit for queries").option("--filters <json>","Filters for search query (JSON)").option("--tenant <id>","Tenant ID for multi-tenant queries").action(async(n,r,o)=>{let a=o.optsWithGlobals?.(),i={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:o.format||a?.format,force:o.force||!1,limit:o.limit??10,filters:o.filters,tenant:o.tenant};try{switch(n){case"status":await Pi(i);break;case"indexes":await Ii(i);break;case"reindex":r.length>0?await sr(r[0],i):await sr(void 0,i);break;case"query":r.length<2&&(d("query requires an index and search term: nebutra search query <index> <query>","error"),process.exit(c.INVALID_ARGS)),await Ni(r[0],r.slice(1).join(" "),i);break;case"stats":await Ti(i);break;default:d(`Unknown search subcommand: ${n}. Valid commands: status, indexes, reindex, query, stats`,"error"),process.exit(c.ERROR)}}catch(l){let u=l instanceof Error?l.message:String(l);d(`Search command failed: ${u}`,"error"),k("Full error",{error:l}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
678
768
|
Examples:
|
|
679
769
|
nebutra search status Show search provider status
|
|
680
770
|
nebutra search indexes List all indexes
|
|
@@ -697,7 +787,7 @@ Flags:
|
|
|
697
787
|
--limit <n> Result limit for queries (default: 10)
|
|
698
788
|
--filters <json> Filters for search query
|
|
699
789
|
--tenant <id> Tenant ID for multi-tenant searches
|
|
700
|
-
`)}import{stdin as
|
|
790
|
+
`)}import{stdin as Ve}from"process";import*as gt from"@clack/prompts";import P from"picocolors";async function ji(){return new Promise((e,t)=>{let n="";Ve.setEncoding("utf8"),Ve.on("readable",()=>{let r;for(;(r=Ve.read())!==null;)n+=r}),Ve.on("end",()=>{e(n.trim())}),Ve.on("error",t)})}async function Ui(e){d("Fetching secret metadata...","info");let t=[{key:"openai_key",tenant:e.tenant||"org_123",createdAt:"2024-03-15T10:30:00Z",updatedAt:"2024-03-30T14:22:00Z",description:"OpenAI API key for embeddings",masked:!0},{key:"stripe_sk",tenant:e.tenant||"org_123",createdAt:"2024-03-10T08:15:00Z",updatedAt:"2024-03-25T09:45:00Z",description:"Stripe secret key",masked:!0},{key:"github_token",tenant:e.tenant||"org_123",createdAt:"2024-03-20T16:30:00Z",updatedAt:"2024-03-29T11:20:00Z",description:"GitHub API token",masked:!0}];if(e.format==="json")E(t,{format:"json"});else{d("Secrets for tenant: "+(e.tenant||"org_123"),"info");for(let n of t){let r=n.description?` \u2014 ${n.description}`:"";d(`${P.cyan(n.key)}${P.gray(r)}`,"info"),d(` Updated: ${n.updatedAt}`,"info")}}}async function Di(e,t){e||(d("Secret key is required: nebutra secrets set <key>","error"),process.exit(c.INVALID_ARGS));let n;if(t.value)d("WARNING: Using --value flag exposes secret in shell history. Prefer stdin.","warn"),n=t.value;else{d("Enter secret value (will not echo). Press Ctrl+D when done:","info");try{n=await ji()}catch{d("Failed to read secret from stdin","error"),process.exit(c.ERROR)}}if(n||(d("Secret value cannot be empty","error"),process.exit(c.INVALID_ARGS)),d(`Setting secret ${P.cyan(e)}...`,"info"),t.dryRun){d(`Would encrypt and store secret ${e} for tenant ${t.tenant||"org_123"}`,"info");return}d(`Secret ${P.cyan(e)} encrypted and stored successfully`,"success"),t.description&&d(`Description: ${t.description}`,"info")}async function Li(e,t){e||(d("Secret key is required: nebutra secrets get <key>","error"),process.exit(c.INVALID_ARGS)),d(`Retrieving secret ${P.cyan(e)}...`,"info");let r=t.unmask?"sk_live_abc123xyz789...":"***masked***";t.unmask||d(`Secret value is ${P.yellow("masked")}. Use --unmask to display.`,"warn");let o={key:e,tenant:t.tenant||"org_123",value:r,encrypted:!0};t.format==="json"?E(o,{format:"json"}):d(`${P.cyan(e)}: ${r}`,"info")}async function Fi(e,t){e||(d("Secret key is required: nebutra secrets rotate <key>","error"),process.exit(c.INVALID_ARGS));let n=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!t.yes&&!t.dryRun&&n){let r=await gt.confirm({message:`Rotate encryption key for ${P.cyan(e)}? This will re-encrypt with new key material.`,initialValue:!1});(gt.isCancel(r)||!r)&&(d("Rotation cancelled","warn"),process.exit(0))}if(!t.yes&&!t.dryRun&&!n&&(d("Key rotation requires --yes confirmation","error"),process.exit(c.INVALID_ARGS)),d(`Rotating encryption key for ${P.cyan(e)}...`,"info"),t.dryRun){d(`Would rotate encryption key for ${e}`,"info");return}d(`Encryption key for ${P.cyan(e)} rotated successfully`,"success"),d(`New key version: ${P.dim("v2")}`,"info")}async function Mi(e){d("Fetching audit log...","info");let n=[{timestamp:"2024-03-30T14:52:00Z",action:"read",key:"openai_key",actor:"api_service",success:!0},{timestamp:"2024-03-30T14:22:00Z",action:"write",key:"openai_key",actor:"claude_user",success:!0},{timestamp:"2024-03-30T13:45:00Z",action:"read",key:"stripe_sk",actor:"billing_worker",success:!0},{timestamp:"2024-03-30T11:30:00Z",action:"rotate",key:"github_token",actor:"admin_user",success:!0},{timestamp:"2024-03-30T10:15:00Z",action:"read",key:"unknown_key",actor:"unauthorized_service",success:!1,message:"Key not found"}];if(e.limit&&(n=n.slice(0,e.limit)),e.format==="json")E(n,{format:"json"});else{d("Secret Access Audit Log","info");for(let r of n){let o=r.success?P.green("\u2713"):P.red("\u2716");d(`${o} ${r.timestamp} ${P.cyan(r.action)} ${r.key}`,r.success?"success":"error"),d(` Actor: ${r.actor}${r.message?` \u2014 ${r.message}`:""}`,"info")}}}async function Gi(e){d("Verifying vault configuration...","info");let t=[];if(t.push({name:"KMS Connectivity",healthy:!0,message:"Connected to AWS KMS"}),t.push({name:"Master Key",healthy:!0,message:"Master encryption key available"}),t.push({name:"Envelope Encryption",healthy:!0,message:"HKDF envelope encryption configured"}),t.push({name:"Storage Backend",healthy:!0,message:"PostgreSQL backend healthy"}),e.format==="json")E(t,{format:"json"});else{d("Vault Configuration Check","info");for(let r of t){let o=r.healthy?P.green("\u2713"):P.red("\u2716");d(`${o} ${r.name}: ${r.message}`,r.healthy?"success":"error")}let n=t.every(r=>r.healthy);d(`Overall: ${n?P.green("HEALTHY"):P.red("UNHEALTHY")}`,n?"success":"error")}}function ir(e){e.command("secrets <verb> [args...]").description("Manage encrypted secrets via @nebutra/vault (AWS KMS + HKDF envelope encryption)").option("--dry-run","Show what would be run without executing").option("--yes","Skip confirmations").option("--format <type>","Output format: json, plain, table","plain").option("--tenant <id>","Tenant ID for multi-tenant access").option("--unmask","Unmask secret values (required to view)").option("--description <text>","Description for secret").option("--value <text>","Secret value (prefer stdin for security)").option("--since <date>","Audit log start date (ISO 8601)").option("--limit <n>","Maximum audit log entries (default: 50)").action(async(n,r,o)=>{let a=o.optsWithGlobals?.(),i={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:o.format||a?.format,tenant:o.tenant||a?.tenant,unmask:o.unmask||!1,description:o.description,value:o.value,since:o.since,limit:o.limit??50};try{switch(n){case"list":await Ui(i);break;case"set":r.length===0&&(d("set requires a secret key: nebutra secrets set <key>","error"),process.exit(c.INVALID_ARGS)),await Di(r[0],i);break;case"get":r.length===0&&(d("get requires a secret key: nebutra secrets get <key>","error"),process.exit(c.INVALID_ARGS)),await Li(r[0],i);break;case"rotate":r.length===0&&(d("rotate requires a secret key: nebutra secrets rotate <key>","error"),process.exit(c.INVALID_ARGS)),await Fi(r[0],i);break;case"audit":await Mi(i);break;case"verify":await Gi(i);break;default:d(`Unknown secrets subcommand: ${n}. Valid commands: list, set, get, rotate, audit, verify`,"error"),process.exit(c.ERROR)}}catch(l){let u=l instanceof Error?l.message:String(l);d(`Secrets command failed: ${u}`,"error"),k("Full error",{error:l}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
701
791
|
Examples:
|
|
702
792
|
nebutra secrets list List all secrets (metadata only)
|
|
703
793
|
nebutra secrets list --tenant org_456 List secrets for specific tenant
|
|
@@ -732,7 +822,7 @@ Security Notes:
|
|
|
732
822
|
- Never use --value in production (visible in shell history)
|
|
733
823
|
- Always use --unmask when viewing secrets in scripts
|
|
734
824
|
- Audit log tracks all access and modifications
|
|
735
|
-
`)}import*as
|
|
825
|
+
`)}import*as yt from"@clack/prompts";import D from"picocolors";function cr(e){switch(e.toLowerCase()){case"running":return"running";case"exited":return"exited";case"restarting":return"restarting";case"paused":return"paused";default:return"exited"}}function Ki(e){switch(e){case"running":return D.green("\u25CF");case"exited":return D.red("\u25CF");case"restarting":return D.yellow("\u25CF");case"paused":return D.gray("\u25CF");default:return D.gray("\u25CF")}}async function qi(e){d("Checking service status...","info");let t=await x({command:"docker",args:["compose","ps","--format","json"],cwd:h(),interactive:!1,label:"Docker Compose PS",dryRun:e.dryRun});t.exitCode!==0&&(d("Failed to fetch service status","error"),k("Docker error",{stderr:t.stderr}),process.exit(c.ERROR));try{let n=JSON.parse(t.stdout);if(e.format==="json"){let r=n.map(o=>({name:o.Name,status:cr(o.State),exitCode:o.ExitCode}));E(r,{format:"json"})}else{d("Service Status","info");let r=n.map(o=>({Service:Ki(cr(o.State))+" "+o.Name,Status:o.State,"Exit Code":o.ExitCode||"\u2014"}));for(let o of r)d(`${o.Service} (${o.Status})`,"info")}}catch(n){d("Failed to parse service status","error"),k("Parse error",{error:String(n)}),process.exit(c.ERROR)}}async function Vi(e,t){d(`Streaming logs for ${D.cyan(e)}...`,"info");let n=["compose","logs"];t.tail?n.push("--tail",String(t.tail)):n.push("--tail","100"),t.since&&n.push("--since",t.since),n.push("-f",e),(await x({command:"docker",args:n,cwd:h(),interactive:!0,label:`Docker Compose Logs (${e})`,dryRun:t.dryRun})).exitCode!==0&&(d(`Failed to stream logs for ${e}`,"error"),process.exit(c.ERROR))}async function Wi(e,t){let n=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!t.yes&&!t.dryRun&&n){let a=await yt.confirm({message:`Restart service ${D.cyan(e)}?`,initialValue:!1});(yt.isCancel(a)||!a)&&(d("Restart cancelled","warn"),process.exit(0))}d(`Restarting service ${D.cyan(e)}...`,"info");let r=["compose","restart"];t.timeout&&r.push("--timeout",String(t.timeout)),r.push(e);let o=await x({command:"docker",args:r,cwd:h(),interactive:!1,label:`Docker Compose Restart (${e})`,dryRun:t.dryRun});o.exitCode===0?d(`Service ${D.cyan(e)} restarted successfully`,"success"):(d(`Failed to restart service ${e}`,"error"),k("Docker error",{stderr:o.stderr}),process.exit(c.ERROR))}async function Bi(e){d("Running deep health check...","info");let t=[],n=await x({command:"docker",args:["compose","exec","-T","postgres","pg_isready"],cwd:h(),interactive:!1,label:"PostgreSQL Health",dryRun:e.dryRun});t.push({service:"PostgreSQL",healthy:n.exitCode===0,message:n.exitCode===0?"Ready":"Not responding"});let r=await x({command:"docker",args:["compose","exec","-T","redis","redis-cli","ping"],cwd:h(),interactive:!1,label:"Redis Health",dryRun:e.dryRun});t.push({service:"Redis",healthy:r.exitCode===0,message:r.exitCode===0?"Ready":"Not responding"});let o=await x({command:"docker",args:["compose","exec","-T","clickhouse","curl","-s","http://localhost:8123/ping"],cwd:h(),interactive:!1,label:"ClickHouse Health",dryRun:e.dryRun});t.push({service:"ClickHouse",healthy:o.exitCode===0,message:o.exitCode===0?"Ready":"Not responding"});let a=await x({command:"docker",args:["compose","exec","-T","meilisearch","curl","-s","http://localhost:7700/health"],cwd:h(),interactive:!1,label:"Meilisearch Health",dryRun:e.dryRun});if(t.push({service:"Meilisearch",healthy:a.exitCode===0,message:a.exitCode===0?"Healthy":"Not responding"}),e.format==="json")E(t,{format:"json"});else{for(let l of t){let u=l.healthy?D.green("\u2713"):D.red("\u2716");d(`${u} ${l.service}: ${l.message}`,l.healthy?"success":"error")}let i=t.every(l=>l.healthy);d(`Overall health: ${i?D.green("HEALTHY"):D.red("UNHEALTHY")}`,i?"success":"error")}}async function zi(e,t,n){let r=parseInt(t,10);(Number.isNaN(r)||r<0)&&(d("Invalid replica count (must be a non-negative number)","error"),process.exit(c.INVALID_ARGS)),d(`Scaling ${D.cyan(e)} to ${D.cyan(String(r))} replicas...`,"info");let o=await x({command:"docker",args:["compose","up","-d","--scale",`${e}=${r}`],cwd:h(),interactive:!1,label:`Docker Compose Scale (${e})`,dryRun:n.dryRun});o.exitCode===0?d(`Service ${D.cyan(e)} scaled to ${r} replicas`,"success"):(d(`Failed to scale service ${e}`,"error"),k("Docker error",{stderr:o.stderr}),process.exit(c.ERROR))}function lr(e){e.command("services <verb> [args...]").description("Manage Docker Compose microservices (postgres, redis, clickhouse, meilisearch, novu, openfga, etc.)").option("--dry-run","Show what would be run without executing").option("--yes","Skip confirmations").option("--format <type>","Output format: json, plain, table","plain").option("--tail <n>","Number of log lines to show (default: 100)").option("--since <duration>","Show logs since duration (e.g., 10m, 1h)").option("--timeout <seconds>","Timeout for service restart").action(async(n,r,o)=>{let a=o.optsWithGlobals?.(),i={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:o.format||a?.format,tail:o.tail??100,since:o.since,timeout:o.timeout};try{switch(n){case"status":await qi(i);break;case"logs":r.length===0&&(d("logs requires a service name: nebutra services logs <service>","error"),process.exit(c.INVALID_ARGS)),await Vi(r[0],i);break;case"restart":r.length===0&&(d("restart requires a service name: nebutra services restart <service>","error"),process.exit(c.INVALID_ARGS)),await Wi(r[0],i);break;case"health":await Bi(i);break;case"scale":r.length<2&&(d("scale requires a service name and replica count: nebutra services scale <service> <count>","error"),process.exit(c.INVALID_ARGS)),await zi(r[0],r[1],i);break;default:d(`Unknown services subcommand: ${n}. Valid commands: status, logs, restart, health, scale`,"error"),process.exit(c.ERROR)}}catch(l){let u=l instanceof Error?l.message:String(l);d(`Services command failed: ${u}`,"error"),k("Full error",{error:l}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
736
826
|
Examples:
|
|
737
827
|
nebutra services status Show all service status
|
|
738
828
|
nebutra services logs postgres Stream postgres logs
|
|
@@ -753,16 +843,87 @@ Flags:
|
|
|
753
843
|
--tail <n> Log lines to show (default: 100)
|
|
754
844
|
--since <duration> Show logs since (e.g., 10m, 1h)
|
|
755
845
|
--timeout <seconds> Timeout for restart
|
|
756
|
-
`)}import{existsSync as
|
|
757
|
-
${
|
|
758
|
-
`),r.forEach(a=>{console.log(` ${
|
|
759
|
-
${
|
|
760
|
-
`),r.forEach(a=>{let i=a.status==="ready"?
|
|
761
|
-
${n}`)}return t}async function
|
|
762
|
-
`),process.exit(c.DRY_RUN_OK)}try{if(e.app&&(e.watch||e.coverage)&&(F.log.error(I.red("Cannot use --watch or --coverage with --app filter (app-level tests use turbo)")),process.exit(c.INVALID_ARGS)),e.app){
|
|
763
|
-
`),process.exit(c.DRY_RUN_OK)}try{(e.ui&&e.ci||e.ui&&e.ci)&&(F.log.error(I.red("Cannot use --ui and --ci together")),process.exit(c.INVALID_ARGS));let t="e2e";if(e.ci?t="e2e:ci":e.ui&&(t="e2e:ui"),!e.quiet){let r=t==="e2e:ci"?"Running E2E tests (CI mode)...":t==="e2e:ui"?"Opening E2E tests in UI mode...":"Running E2E tests...";F.log.info(I.cyan(r))}let n=await _(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?F.log.error(I.red(t.message)):F.log.error(I.red("Unknown error occurred")),process.exit(c.ERROR)}}async function
|
|
764
|
-
`),process.exit(c.DRY_RUN_OK)}try{let t=e.why?"size:why":"size";if(!e.quiet){let r=e.why?"Analyzing bundle size (showing largest assets)...":"Checking bundle size...";F.log.info(I.cyan(r))}let n=await _(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?F.log.error(I.red(t.message)):F.log.error(I.red("Unknown error occurred")),process.exit(c.ERROR)}}async function
|
|
765
|
-
`),process.exit(c.DRY_RUN_OK)}try{e.quiet||F.log.info(I.cyan("Running architecture tests..."));let t=await _("test:arch",{interactive:!0});process.exit(t.exitCode)}catch(t){t instanceof Error?F.log.error(I.red(t.message)):F.log.error(I.red("Unknown error occurred")),process.exit(c.ERROR)}}function
|
|
846
|
+
`)}import{existsSync as z,readdirSync as We,readFileSync as dr}from"fs";import{resolve as L}from"path";import Ne from"picocolors";function xe(e,t,n){if(t==="json"){let r={command:n||"stats",...e};console.log(JSON.stringify(r,null,2))}else e.stdout&&(console.log(e.stdout),delete e.stdout),Object.keys(e).length>0&&console.log(JSON.stringify(e,null,2))}function ur(e){try{let t=dr(L(e,"package.json"),"utf-8");return JSON.parse(t)}catch{return{}}}function mr(e){try{return We(e,{recursive:!0}).length}catch{return 0}}async function Yi(e){s.info("Gathering monorepo statistics...");let t=h(),n=L(t,"packages"),r=L(t,"apps"),o=[],a=[],i=0,l="unknown";try{z(n)&&(o=We(n).filter(y=>!y.startsWith(".")&&z(L(n,y,"package.json")))),z(r)&&(a=We(r).filter(y=>!y.startsWith(".")&&z(L(r,y,"package.json"))));let m=L(t,"nebutra.config.ts");if(z(m)){let p=dr(m,"utf-8").match(/preset:\s*["']([^"']+)["']/);p&&(l=p[1])}}catch(m){s.warn(`Failed to read monorepo structure: ${m instanceof Error?m.message:String(m)}`)}let u={monorepo:{root:t,packages:o.length,apps:a.length,preset:l},statistics:{totalWorkspaces:o.length+a.length,packageCount:o.length,appCount:a.length}};e.dryRun||(s.success(`Found ${u.statistics.totalWorkspaces} workspaces`),s.info(`Packages: ${u.statistics.packageCount}, Apps: ${u.statistics.appCount}`),s.info(`Preset: ${l}`)),xe(u,e.format,"stats")}async function Hi(e){s.info("Analyzing bundle and dependency sizes...");let t=await x({command:"pnpm",args:["size"],interactive:!1,label:"Analyze package sizes",dryRun:e.dryRun}),n={analysis:"pnpm size",exitCode:t.exitCode,...t.stdout&&{stdout:t.stdout},...t.stderr&&{stderr:t.stderr}};t.exitCode!==0&&s.warn("Size analysis failed. Ensure your project is built."),xe(n,e.format,"stats:size")}async function Ji(e){s.info("Analyzing dependencies...");let t=await x({command:"npx",args:["knip","--no-progress"],interactive:!1,label:"Analyze dependencies",dryRun:e.dryRun}),n={analysis:"knip (unused dependencies)",exitCode:t.exitCode,command:"npx knip",...t.stdout&&{stdout:t.stdout.substring(0,500)}};e.dryRun||(t.exitCode===0?s.success("No unused dependencies detected"):s.info("Run 'npx knip' to see detailed dependency analysis")),xe(n,e.format,"stats:deps")}async function Qi(e){s.info("Listing packages...");let t=h(),n=L(t,"packages"),r=[];try{z(n)&&(r=We(n).filter(i=>!i.startsWith(".")&&z(L(n,i,"package.json"))).map(i=>{let l=L(n,i),u=ur(l);return{name:u.name||i,version:u.version,description:u.description,files:mr(l)}}))}catch(a){s.error(`Failed to list packages: ${a instanceof Error?a.message:String(a)}`),process.exit(c.ERROR)}let o={packages:r,total:r.length};e.dryRun||e.format==="json"?xe(o,e.format,"stats:packages"):(console.log(`
|
|
847
|
+
${Ne.cyan("Packages")} (${r.length} total)
|
|
848
|
+
`),r.forEach(a=>{console.log(` ${Ne.blue(a.name)}`),a.version&&console.log(` version: ${a.version}`),a.description&&console.log(` ${a.description}`),console.log(` files: ${a.files}`)}),console.log(""))}async function Xi(e){s.info("Listing apps...");let t=h(),n=L(t,"apps"),r=[];try{z(n)&&(r=We(n).filter(i=>!i.startsWith(".")&&z(L(n,i,"package.json"))).map(i=>{let l=L(n,i),u=ur(l),m=z(L(l,"next.config.ts"))||z(L(l,"next.config.js")),y=z(L(l,"server.ts"))||z(L(l,"server.js"));return{name:u.name||i,version:u.version,description:u.description,files:mr(l),status:m||y?"ready":"unknown"}}))}catch(a){s.error(`Failed to list apps: ${a instanceof Error?a.message:String(a)}`),process.exit(c.ERROR)}let o={apps:r,total:r.length,ready:r.filter(a=>a.status==="ready").length};e.dryRun||e.format==="json"?xe(o,e.format,"stats:apps"):(console.log(`
|
|
849
|
+
${Ne.cyan("Apps")} (${r.length} total, ${o.ready} ready)
|
|
850
|
+
`),r.forEach(a=>{let i=a.status==="ready"?Ne.green("\u2713"):Ne.gray("\u25CB");console.log(` ${i} ${Ne.blue(a.name)}`),a.version&&console.log(` version: ${a.version}`),a.description&&console.log(` ${a.description}`),console.log(` files: ${a.files}`)}),console.log(""))}function pr(e){let t=e.command("stats").description("Project statistics and health checks");t.command("*",{isDefault:!0}).description("Show monorepo overview").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Yi({format:n.format||r.format})}),t.command("size").description("Analyze bundle and package sizes").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Hi({format:n.format||r.format})}),t.command("deps").description("Analyze dependencies (unused, missing, duplicate)").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Ji({format:n.format||r.format})}),t.command("packages").description("List all packages with metadata").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Qi({format:n.format||r.format})}),t.command("apps").description("List all apps with status").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Xi({format:n.format||r.format})})}import*as F from"@clack/prompts";import I from"picocolors";var Mt=["landing-page","web","storybook","api-gateway","design-docs","studio","docs","ui","tokens","brand","theme","icons","preset","queue","search","notifications","permissions","webhooks","metering","uploads","vault","tenant"];function Zi(e){let t=`@nebutra/${e}`;if(!Mt.includes(e)){let n=I.yellow(`Valid apps: ${Mt.join(", ")}`);throw new Error(`Invalid app: ${I.red(e)}
|
|
851
|
+
${n}`)}return t}async function ec(e){if(e.dryRun){let t={mode:"dry-run",timestamp:new Date().toISOString(),command:"test",options:{app:e.app,watch:e.watch,coverage:e.coverage},task:e.coverage?"pnpm test:coverage":e.watch?"pnpm test:watch":"pnpm test"};process.stdout.write(JSON.stringify(t,null,2)+`
|
|
852
|
+
`),process.exit(c.DRY_RUN_OK)}try{if(e.app&&(e.watch||e.coverage)&&(F.log.error(I.red("Cannot use --watch or --coverage with --app filter (app-level tests use turbo)")),process.exit(c.INVALID_ARGS)),e.app){Zi(e.app);let r=`@nebutra/${e.app}`;e.quiet||F.log.info(I.cyan(`Running tests for ${I.bold(r)}...`));let o=await fe("test",{filter:r});process.exit(o.exitCode)}let t="test";if(e.coverage?t="test:coverage":e.watch&&(t="test:watch"),!e.quiet){let r=t==="test:coverage"?"Running tests with coverage...":t==="test:watch"?"Running tests in watch mode...":"Running tests...";F.log.info(I.cyan(r))}let n=await _(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?F.log.error(I.red(t.message)):F.log.error(I.red("Unknown error occurred")),process.exit(c.ERROR)}}async function fr(e){if(e.dryRun){let t={mode:"dry-run",timestamp:new Date().toISOString(),command:"test e2e",options:{ui:e.ui,ci:e.ci},task:e.ci?"pnpm e2e:ci":e.ui?"pnpm e2e:ui":"pnpm e2e"};process.stdout.write(JSON.stringify(t,null,2)+`
|
|
853
|
+
`),process.exit(c.DRY_RUN_OK)}try{(e.ui&&e.ci||e.ui&&e.ci)&&(F.log.error(I.red("Cannot use --ui and --ci together")),process.exit(c.INVALID_ARGS));let t="e2e";if(e.ci?t="e2e:ci":e.ui&&(t="e2e:ui"),!e.quiet){let r=t==="e2e:ci"?"Running E2E tests (CI mode)...":t==="e2e:ui"?"Opening E2E tests in UI mode...":"Running E2E tests...";F.log.info(I.cyan(r))}let n=await _(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?F.log.error(I.red(t.message)):F.log.error(I.red("Unknown error occurred")),process.exit(c.ERROR)}}async function gr(e){if(e.dryRun){let t={mode:"dry-run",timestamp:new Date().toISOString(),command:"test size",options:{why:e.why},task:e.why?"pnpm size:why":"pnpm size"};process.stdout.write(JSON.stringify(t,null,2)+`
|
|
854
|
+
`),process.exit(c.DRY_RUN_OK)}try{let t=e.why?"size:why":"size";if(!e.quiet){let r=e.why?"Analyzing bundle size (showing largest assets)...":"Checking bundle size...";F.log.info(I.cyan(r))}let n=await _(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?F.log.error(I.red(t.message)):F.log.error(I.red("Unknown error occurred")),process.exit(c.ERROR)}}async function yr(e){if(e.dryRun){let t={mode:"dry-run",timestamp:new Date().toISOString(),command:"test arch",task:"pnpm test:arch"};process.stdout.write(JSON.stringify(t,null,2)+`
|
|
855
|
+
`),process.exit(c.DRY_RUN_OK)}try{e.quiet||F.log.info(I.cyan("Running architecture tests..."));let t=await _("test:arch",{interactive:!0});process.exit(t.exitCode)}catch(t){t instanceof Error?F.log.error(I.red(t.message)):F.log.error(I.red("Unknown error occurred")),process.exit(c.ERROR)}}function hr(e){let t=e.command("test [subcommand]").description("Run tests (Vitest, Playwright, bundle size, architecture)").option("--watch","Run tests in watch mode (unit tests only)").option("--coverage","Generate coverage report (unit tests only)").option("--app <name>",`Run tests for specific app: ${Mt.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;n==="e2e"?await fr({ui:r.ui||!1,ci:r.ci||!1,dryRun:r.dryRun||!1,quiet:o.quiet||!1,verbose:o.verbose||!1,format:o.format}):n==="size"?await gr({why:r.why||!1,dryRun:r.dryRun||!1,quiet:o.quiet||!1,verbose:o.verbose||!1,format:o.format}):n==="arch"?await yr({dryRun:r.dryRun||!1,quiet:o.quiet||!1,verbose:o.verbose||!1,format:o.format}):await ec({...r,quiet:o.quiet||!1,verbose:o.verbose||!1,format:o.format})});t.command("e2e").description("Run end-to-end tests (Playwright)").option("--ui","Open interactive UI mode").option("--ci","Run in CI mode with strict options").option("--dry-run","Preview what would run (exit code 10)").action(async n=>{let r=n.optsWithGlobals?n.optsWithGlobals():n;await fr({...n,quiet:r.quiet||!1,verbose:r.verbose||!1,format:r.format})}),t.command("size").description("Check bundle size (analyze largest assets)").option("--why","Show breakdown of largest bundle assets").option("--dry-run","Preview what would run (exit code 10)").action(async n=>{let r=n.optsWithGlobals?n.optsWithGlobals():n;await gr({...n,quiet:r.quiet||!1,verbose:r.verbose||!1,format:r.format})}),t.command("arch").description("Run architecture tests (verify design layer isolation)").option("--dry-run","Preview what would run (exit code 10)").action(async n=>{let r=n.optsWithGlobals?n.optsWithGlobals():n;await yr({dryRun:n.dryRun||!1,quiet:r.quiet||!1,verbose:r.verbose||!1,format:r.format})})}import Gt from"fs";import Kt from"path";import tc from"picocolors";var qt=["inngest","n8n","pusher"],nc=`import { Inngest } from "inngest";
|
|
856
|
+
|
|
857
|
+
export const inngest = new Inngest({ id: "nebutra-sailor" });
|
|
858
|
+
|
|
859
|
+
export const exampleFn = inngest.createFunction(
|
|
860
|
+
{ id: "example-fn" },
|
|
861
|
+
{ event: "app/example.requested" },
|
|
862
|
+
async ({ event, step }) => {
|
|
863
|
+
await step.run("do-work", async () => {
|
|
864
|
+
return { ok: true, payload: event.data };
|
|
865
|
+
});
|
|
866
|
+
return { status: "completed" };
|
|
867
|
+
},
|
|
868
|
+
);
|
|
869
|
+
`,rc=`# n8n Workflows (Self-hosted Convention)
|
|
870
|
+
|
|
871
|
+
Nebutra Sailor uses n8n in self-hosted mode for visual workflow orchestration.
|
|
872
|
+
|
|
873
|
+
## Convention
|
|
874
|
+
|
|
875
|
+
- Each workflow is exported as a JSON file in this directory: \`workflows/n8n/*.json\`
|
|
876
|
+
- File naming: \`<domain>.<verb>.json\` (e.g. \`billing.invoice-paid.json\`)
|
|
877
|
+
- Credentials live in n8n's encrypted store \u2014 never inline secrets in the JSON
|
|
878
|
+
- Import via n8n UI: Settings \u2192 Workflows \u2192 Import from File
|
|
879
|
+
|
|
880
|
+
## Running n8n locally
|
|
881
|
+
|
|
882
|
+
\`\`\`bash
|
|
883
|
+
docker run -it --rm \\
|
|
884
|
+
--name n8n \\
|
|
885
|
+
-p 5678:5678 \\
|
|
886
|
+
-v ~/.n8n:/home/node/.n8n \\
|
|
887
|
+
n8nio/n8n
|
|
888
|
+
\`\`\`
|
|
889
|
+
|
|
890
|
+
## Webhook endpoints
|
|
891
|
+
|
|
892
|
+
Production webhooks route through \`backends/gateway\` (see \`packages/integrations/webhooks\`).
|
|
893
|
+
`,oc=`{
|
|
894
|
+
"name": "example-workflow",
|
|
895
|
+
"nodes": [
|
|
896
|
+
{
|
|
897
|
+
"parameters": {},
|
|
898
|
+
"name": "Start",
|
|
899
|
+
"type": "n8n-nodes-base.start",
|
|
900
|
+
"typeVersion": 1,
|
|
901
|
+
"position": [240, 300]
|
|
902
|
+
}
|
|
903
|
+
],
|
|
904
|
+
"connections": {},
|
|
905
|
+
"active": false,
|
|
906
|
+
"settings": {},
|
|
907
|
+
"tags": []
|
|
908
|
+
}
|
|
909
|
+
`,sc=`import Pusher from "pusher";
|
|
910
|
+
|
|
911
|
+
export const pusher = new Pusher({
|
|
912
|
+
appId: process.env.PUSHER_APP_ID ?? "",
|
|
913
|
+
key: process.env.PUSHER_KEY ?? "",
|
|
914
|
+
secret: process.env.PUSHER_SECRET ?? "",
|
|
915
|
+
cluster: process.env.PUSHER_CLUSTER ?? "us2",
|
|
916
|
+
useTLS: true,
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
export async function publishExample(channel: string, event: string, data: unknown) {
|
|
920
|
+
if (!process.env.PUSHER_APP_ID) {
|
|
921
|
+
throw new Error("PUSHER_APP_ID not configured");
|
|
922
|
+
}
|
|
923
|
+
return pusher.trigger(channel, event, data);
|
|
924
|
+
}
|
|
925
|
+
`;function ac(e){switch(e){case"inngest":return[{relPath:"workflows/inngest/example.ts",content:nc}];case"n8n":return[{relPath:"workflows/n8n/README.md",content:rc},{relPath:"workflows/n8n/example.json",content:oc}];case"pusher":return[{relPath:"workflows/pusher/example.ts",content:sc}]}}async function ic(e,t={}){qt.includes(e)||(s.error(`Invalid provider: ${tc.red(e)}. Valid: ${qt.join(", ")}`),process.exit(c.INVALID_ARGS));let n=h(),r=ac(e);t.dryRun&&(process.stdout.write(JSON.stringify({mode:"dry-run",command:`workflow init ${e}`,files:r.map(i=>({path:Kt.join(n,i.relPath)}))},null,2)+`
|
|
926
|
+
`),process.exit(c.DRY_RUN_OK));let o=0,a=0;for(let i of r){let l=Kt.join(n,i.relPath);if(Gt.existsSync(l)){s.warn(`Skipping ${i.relPath} \u2014 already exists.`),a++;continue}Gt.mkdirSync(Kt.dirname(l),{recursive:!0}),Gt.writeFileSync(l,i.content),t.quiet||s.success(`Created ${i.relPath}`),o++}o===0&&a>0&&(s.info(`No files created \u2014 ${e} scaffold already present.`),process.exit(c.CONFLICT)),process.exit(c.SUCCESS)}function br(e){e.command("workflow").description("Manage workflow provider scaffolding (inngest | n8n | pusher)").command("init <provider>").description(`Scaffold a starter workflow file for a provider (${qt.join(" | ")})`).option("--dry-run","Preview files that would be created (exit code 10)").action(async(n,r)=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await ic(n,{dryRun:r.dryRun||!1,quiet:o.quiet||!1})})}import{promises as ht}from"fs";import{homedir as cc}from"os";import{join as wr}from"path";import we from"picocolors";var lc=1440*60*1e3,Rr=wr(cc(),".nebutra"),Vt=wr(Rr,"update-check.json"),dc="https://registry.npmjs.org/nebutra";async function uc(){try{await ht.mkdir(Rr,{recursive:!0})}catch{}}async function mc(){try{let e=await ht.readFile(Vt,"utf-8"),t=JSON.parse(e);return Date.now()-t.timestamp<lc?t:(await ht.unlink(Vt).catch(r=>{s.debug("Failed to remove expired update cache file",{error:r})}),null)}catch{return null}}async function pc(e){try{await uc();let t={timestamp:Date.now(),latestVersion:e};await ht.writeFile(Vt,JSON.stringify(t,null,2),"utf-8")}catch{}}async function fc(){try{let e=new AbortController,t=setTimeout(()=>e.abort(),5e3),n=await fetch(dc,{signal:e.signal,headers:{Accept:"application/json"}});if(clearTimeout(t),!n.ok)return null;let o=(await n.json())["dist-tags"];return!o||typeof o.latest!="string"?null:o.latest}catch{return null}}function vr(e){return e.split(".").map(t=>parseInt(t.replace(/[^0-9]/g,""),10)||0).slice(0,3)}function gc(e,t){let n=vr(e),r=vr(t);for(let o=0;o<3;o++){let a=n[o]??0,i=r[o]??0;if(i>a)return!0;if(i<a)return!1}return!1}async function yc(e){let t=await mc(),n=null;return t?n=t.latestVersion:(n=await fc(),n&&await pc(n)),!n||!gc(e,n)?null:{currentVersion:e,latestVersion:n,isUpdate:!0}}function hc(e){let n=`Update available: ${e.currentVersion} \u2192 ${e.latestVersion}`,r="Run `npm i -g nebutra` to update",o=" ".repeat((43-n.length)/2),a=" ".repeat((43-r.length)/2),i=[we.cyan("\u256D"+"\u2500".repeat(41)+"\u256E"),we.cyan("\u2502")+we.yellow(o+n+o)+we.cyan("\u2502"),we.cyan("\u2502")+we.gray(a+r+a)+we.cyan("\u2502"),we.cyan("\u2570"+"\u2500".repeat(41)+"\u256F")]}async function Sr(e){let t=null;return yc(e).then(n=>{t=n}),async()=>{!process.stdout.isTTY||process.env.CI||(t||await new Promise(n=>setTimeout(n,100)),t&&hc(t))}}var Cr="0.1.0";async function vc(){let e=await Sr(Cr),t=new bc,n=process.stdin.isTTY!==!1&&process.stdout.isTTY!==!1;t.name("nebutra").description("Nebutra \u2014 governance-first CLI for topology scaffolding, registry features, and platform operations").version(Cr).option("--verbose","Enable verbose output").option("--quiet","Suppress non-essential output").option("--format <type>","Output format: json, table, plain").option("--yes","Skip all interactive prompts (Agent mode)").option("--no-interactive","Alias for --yes").option("--no-color","Disable colored output"),t.command("init").description("Initialize a Nebutra project and create nebutra.config.json").option("--dry-run","Preview changes without writing files (exits with code 10)").option("--if-not-exists","Skip initialization if nebutra.config.json already exists").action(async r=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await zn({dryRun:r.dryRun||!1,yes:o.yes||!1,ifNotExists:r.ifNotExists||!1})}),t.command("add [components...]").description("Add a registry-backed platform feature or external UI component").option("--21st <id>","Fetch and install a component from 21st.dev").option("--v0 <url>","Fetch and install a component from v0.dev").option("--provider <id>","Specify a backend provider for a system feature (e.g. upstash)").option("--dry-run","Preview what would be installed without making changes (exit code 10)").option("--yes","Skip all interactive prompts and use defaults (Agent mode)").option("--if-not-exists","Skip installation if component already exists").action(async(r,o)=>{let a=o.optsWithGlobals?o.optsWithGlobals():o;await sn(r,{...o,yes:a.yes||!n})}),$n(t),Qn(t),or(t),wn(t),Wn(t),Bn(t),Dn(t),Jn(t),mn(t),pn(t),bn(t),pr(t),An(t),qn(t),Zn(t),_n(t),hr(t),br(t),hn(t),Nn(t),an(t),Rn(t),Vn(t),jn(t),lr(t),ar(t),ir(t),xn(t),t.command("doctor").description("Check your Nebutra project setup for common issues").action(async()=>{let{logger:r}=await import("./logger-KLED6BEE.js"),{findMonorepoRoot:o}=await import("./delegate-YL6SAZNF.js"),a=await import("fs"),i=await import("path");r.info("Running project health check...");let l=!1,u=o(),m=process.version;parseInt(m.replace("v","").split(".")[0]??"0",10)<22?(r.error(`Node.js >= 22 required, found ${m}`),l=!0):r.success(`Node.js version OK (${m})`),a.existsSync(i.join(u,"nebutra.config.json"))?r.success("nebutra.config.json found"):r.warn("nebutra.config.json missing. Try running 'nebutra init'"),a.existsSync(i.join(u,"package.json"))?r.success("package.json found"):(r.error("No package.json found in root directory"),l=!0),a.existsSync(i.join(u,".env"))||a.existsSync(i.join(u,".env.local"))?r.success("Environment variables configured"):r.warn("No .env or .env.local found. Some features may not work properly."),l?(r.error("Doctor found critical issues that require your attention."),process.exit(1)):r.success("Doctor is happy. Your project looks healthy!")}),t.hook("preAction",r=>{let o=r.optsWithGlobals();(!n||o.noInteractive)&&(o.yes=!0)}),t.addHelpText("after",`
|
|
766
927
|
Examples:
|
|
767
928
|
$ nebutra init Initialize a new project
|
|
768
929
|
$ nebutra add cache --provider upstash-redis --yes Install a registry feature
|
|
@@ -803,4 +964,4 @@ Environment:
|
|
|
803
964
|
NEBUTRA_OUTPUT_FORMAT Output format (json|table|plain)
|
|
804
965
|
NO_COLOR Disable colored output
|
|
805
966
|
CI Auto-enable non-interactive mode
|
|
806
|
-
`),process.argv.length<=2&&(t.outputHelp(),process.exit(0)),await t.parseAsync(process.argv),await e()}
|
|
967
|
+
`),process.argv.length<=2&&(t.outputHelp(),process.exit(0)),await t.parseAsync(process.argv),await e()}vc().catch(e=>s.error(e instanceof Error?e.message:String(e)));
|