nebutra 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +135 -135
- package/package.json +3 -3
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 b,b as $,c as N,d as ye,e as Ne,f as pe}from"./chunk-LGUMYJ4Q.js";import{a as oo}from"./chunk-I7AUKTXE.js";import{readFileSync as jl}from"fs";import{dirname as Ul,join as Ll}from"path";import{fileURLToPath as Dl}from"url";import{Command as Fl}from"commander";import{execSync as It}from"child_process";import{promises as ke}from"fs";import J from"path";import*as R from"@clack/prompts";import Fe 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},rn={[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 ao,readdirSync as an,readFileSync as io,statSync as co}from"fs";import{dirname as sn,join as $t,parse as lo}from"path";import{fileURLToPath as uo}from"url";var so={"@nebutra/ui":"^0.1.0","@nebutra/tokens":"^0.1.0","@nebutra/icons":"^0.1.0","@nebutra/brand":"^0.1.0","@nebutra/design-tokens":"^0.1.0","@nebutra/design-sync":"^0.1.0","@nebutra/identity":"^0.1.0","@nebutra/tenant":"^0.1.0","@nebutra/permissions":"^0.1.0","@nebutra/vault":"^0.1.0","@nebutra/audit":"^0.1.0","@nebutra/billing":"^0.1.0","@nebutra/contracts":"^0.1.0","@nebutra/license":"^0.1.0","@nebutra/metering":"^0.1.0","@nebutra/queue":"^0.1.0","@nebutra/search":"^0.1.0","@nebutra/cache":"^0.1.0","@nebutra/notifications":"^0.1.0","@nebutra/webhooks":"^0.1.0","@nebutra/uploads":"^0.1.0","@nebutra/email":"^0.1.0","@nebutra/agents":"^0.1.0","@nebutra/mcp":"^0.1.0","@nebutra/logger":"^0.1.0"};function on(e){return so[e]??null}var mo=`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
|
+
`,po=`# {{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
|
+
`,fo="integrations";function Te(e,t=fo){let n=`packages/${t}/${e}`;return[{path:`${n}/src/index.ts`,content:mo},{path:`${n}/README.md`,content:po}]}var go={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",
|
|
@@ -95,32 +95,32 @@ 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:Te("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:Te("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:Te("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:Te("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:Te("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:Te("captcha","iam")}},yo=["iam","commerce","integrations","ai","design","platform","ops"];function ho(){let e;try{e=sn(uo(import.meta.url))}catch{e=process.cwd()}for(;e&&e!==lo(e).root;){try{let t=an(e);if(t.includes("pnpm-workspace.yaml")||t.includes("packages")){let n=$t(e,"packages");if(ao(n)&&co(n).isDirectory())return n}}catch{}e=sn(e)}return null}function bo(e,t,n,r,o){let a=(t.env??[]).map(l=>({key:l,description:`${l} (declared by ${o})`})),i=on(o)??"latest";return{name:e,description:t.summary??n.description??`${e} feature`,group:t.category??r,dependencies:[{name:o,version:i}],env:a,envFile:t.envFile??".env.local",files:[]}}var nt=null;function vo(){if(nt)return nt;let e={},t=ho();if(!t)return nt=e,e;for(let n of yo){let r=$t(t,n),o;try{o=an(r)}catch{continue}for(let a of o){let i=$t(r,a,"package.json"),l;try{l=io(i,"utf-8")}catch{continue}let u;try{u=JSON.parse(l)}catch{continue}let m=u.nebutra,g=m?.featureId;!m||!g||!u.name||(e[g]=bo(g,m,u,n,u.name))}}return nt=e,e}function cn(){return{...vo(),...go}}function ln(){return Object.values(cn())}async function dn(e){return cn()[e]??null}function Pt(e){return e instanceof Error&&e.message.startsWith("EXIT:")}function un(e){try{return It(`npm list ${e}`,{stdio:"pipe"}),!0}catch{return!1}}function mn(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
99
|
+
`)}function wo(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 pn(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 fn(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 So(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 rt(e){try{return await ke.readFile(e,"utf8")}catch(t){if(t.code==="ENOENT")return null;throw t}}async function Ro(e,t){if(t)return J.join(e,t);let n=[".env.local",".env"];for(let r of n)try{return await ke.access(J.join(e,r)),J.join(e,r)}catch{}return J.join(e,".env.local")}function Eo(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 Co(e){return e?Object.fromEntries(Object.entries(e).sort(([t],[n])=>t.localeCompare(n))):{}}function gn(e,t){if(!(!e.providers||e.providers.length===0))return t?e.providers.find(n=>n.id===t):e.providers[0]}async function ko(e,t,n){let r=J.join(t,"package.json"),o=await rt(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=fn(e.flatMap(f=>[...f.descriptor.env??[],...f.provider?.env??[]])),l=await Ro(t,e[0]?.descriptor.envFile),u=await rt(l)??"",m=Eo(u),g=i.filter(f=>!m.has(f.key)),p=i.filter(f=>m.has(f.key)),h=[];for(let f of e){let M=fn([...f.descriptor.env??[],...f.provider?.env??[]]),Ie={feature:f.name,provider:f.provider?.id,providerDescription:f.provider?.description,envFile:J.relative(t,l)||J.basename(l),envEntries:M};for(let At of f.descriptor.files??[]){let _t=J.join(t,At.path),ro=J.relative(t,_t)||At.path,tn=So(At.content,Ie),nn=await rt(_t),et,tt;nn===null?(et="create",tt="file missing"):nn===tn?(et="skip",tt="already matches registry template"):(et="conflict",tt="file exists with different content"),h.push({type:"write-file",feature:f.name,path:_t,relativePath:ro,status:et,reason:tt,content:tn})}}let F=pn("dependencies",e.flatMap(f=>[...f.descriptor.dependencies??[],...f.provider?.dependencies??[]])),G=pn("devDependencies",e.flatMap(f=>[...f.descriptor.devDependencies??[],...f.provider?.devDependencies??[]])),W=[];for(let f of F){let M=a.dependencies?.[f.name];W.push({section:"dependencies",name:f.name,action:M?M===f.version?"keep":"update":"add",from:M,to:f.version})}for(let f of G){let M=a.devDependencies?.[f.name];W.push({section:"devDependencies",name:f.name,action:M?M===f.version?"keep":"update":"add",from:M,to:f.version})}let me={type:"merge-env",path:l,relativePath:J.relative(t,l)||J.basename(l),created:u.length===0,add:g,keep:p},ie={type:"update-package-json",path:r,relativePath:J.relative(t,r)||"package.json",changes:W},Ce={create:h.filter(f=>f.status==="create").length,update:W.filter(f=>f.action==="add"||f.action==="update").length,append:g.length,skip:h.filter(f=>f.status==="skip").length+W.filter(f=>f.action==="keep").length+p.length,conflict:h.filter(f=>f.status==="conflict").length};return{mode:n,planType:"local-feature-install",cwd:t,timestamp:new Date().toISOString(),features:e.map(f=>({name:f.name,description:f.descriptor.description,provider:f.provider?.id,providerDescription:f.provider?.description})),operations:[...h,me,ie],summary:Ce}}function yn(e){return e.summary.create>0||e.summary.update>0||e.summary.append>0}function Oo(e){return e.summary.conflict>0}async function xo(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 ke.mkdir(J.dirname(o.path),{recursive:!0}),await ke.writeFile(o.path,o.content,"utf8"));if(n&&n.add.length>0){let o=await rt(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
|
-
`)}function
|
|
108
|
-
`)}function x(e,t={}){let n=
|
|
109
|
-
`)}function d(e,t="info"){let n;switch(t){case"error":n=
|
|
110
|
-
`)}function
|
|
111
|
-
`),t&&Object.keys(t).length>0&&
|
|
112
|
-
`)}function
|
|
113
|
-
`),
|
|
114
|
-
`),
|
|
115
|
-
`),
|
|
116
|
-
`)}async function
|
|
117
|
-
Tenants:`),o.tenants.forEach(a=>{console.log(` ${
|
|
118
|
-
Top tenants:`),o.topTenants.forEach((a,i)=>{console.log(` ${i+1}. ${a.tenantName||a.tenantId} \u2014 ${a.usage}`)})))}async function
|
|
119
|
-
Messages:`),n.messages.forEach(r=>{console.log(` ${
|
|
120
|
-
Flags:`),n.flags.forEach(r=>{let o=r.override!==void 0?
|
|
121
|
-
Audit entries:`),o.entries.forEach(a=>{console.log(` ${a.timestamp} \u2014 ${
|
|
122
|
-
Services:`),Object.entries(n.services).forEach(([o,a])=>{let i=a.status==="healthy"?j.green("\u2713"):j.red("\u2717");console.log(` ${i} ${o}: ${a.status}`),a.latency&&console.log(` Latency: ${a.latency}ms`),a.error&&console.log(` Error: ${a.error}`)}))}}function vn(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 Ao({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 $o(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 Po(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 No(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 Io({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 To({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 Uo(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 jo({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 Lo({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 Do(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 Fo(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 Mo({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 Go({format:o.format||a.format,dryRun:o.dryRun||a.dryRun})})}import{existsSync as Ko,readFileSync as Bo,writeFileSync as qo}from"fs";import{resolve as It}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"]},Vo=[{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)"}],he={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:Vo,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 Sn="nebutra.config.json",Wo=v.ai.filter(e=>e!=="none"),wn={"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 oe(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 Yo(e){return e.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function Ho(e){return wn[e]?wn[e]:[`${e.replace(/-/g,"_").toUpperCase()}_API_KEY`]}function rt(){return Object.fromEntries(Wo.map(e=>[e,{id:e,name:Yo(e),envVars:Ho(e)}]))}function ce(){return It(process.cwd(),Sn)}function Ce(){let e=ce();if(!Ko(e))return{$schema:"https://nebutra.com/schema.json"};try{return JSON.parse(Bo(e,"utf-8"))}catch(t){s.error(`Failed to parse ${Sn}: ${t instanceof Error?t.message:String(t)}`),process.exit(c.ERROR)}}function ot(e){qo(ce(),`${JSON.stringify(e,null,2)}
|
|
123
|
-
`)}function be(e){return e.ai={providers:{},routes:{},...e.ai},e.ai.providers={...e.ai.providers},e.ai.routes={...e.ai.routes},e.ai}function Rn(e){let t=rt(),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 zo(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 En(e){let t=be(e),n=t.providers??{},r=t.routes??{},o=Object.entries(n).filter(([,a])=>a.enabled).map(([a])=>a);return{path:ce(),gateway:t.gateway??null,providers:n,enabledProviders:o,routes:r}}function re(e){let t=typeof e.opts=="function"?e.opts():e,n=typeof e.optsWithGlobals=="function"?e.optsWithGlobals():t,r=t.format??n.format??Jo();return{...n,...t,...r?{format:r}:{}}}function Jo(){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 De(e){return e?e.length<=8?"***":`${e.substring(0,8)}***`:"not set"}async function Xo(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:De(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`)),oe(n,e.format,"ai:models")}async function Qo(e){e.format!=="json"&&s.info("Scanning for available agents...");try{let t=It(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)`)),oe(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 Zo(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"),oe(n,t.format,"ai:test")}async function es(e){e.format!=="json"&&s.info("Checking AI configuration...");let t={providers:{openai:{key:De(process.env.OPENAI_API_KEY),configured:!!process.env.OPENAI_API_KEY},anthropic:{key:De(process.env.ANTHROPIC_API_KEY),configured:!!process.env.ANTHROPIC_API_KEY},openrouter:{key:De(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=It(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(", ")}`)),oe(t,e.format,"ai:config")}async function ts(e){let t=Ce(),n=be(t),r=rt(),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:De(process.env[u])}))}}),a={configPath:ce(),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(", ")}`)}oe(a,e.format,"ai:provider:list")}async function ns(e,t){let n=Rn(e),r=Ce(),o=be(r),a=rt(),i=new Date().toISOString();for(let u of n)o.providers[u]={enabled:!0,envVars:a[u].envVars,enabledAt:o.providers?.[u]?.enabledAt??i};ot(r);let l={configPath:ce(),enabled:n,env:t.env===!0?n.flatMap(u=>a[u].envVars.map(m=>({provider:u,name:m,example:`${m}=`}))):void 0,ai:En(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}=`)}oe(l,t.format,"ai:provider:enable")}async function rs(e,t){let n=Rn(e),r=Ce(),o=be(r),a=rt(),i=new Date().toISOString();for(let u of n)o.providers[u]={enabled:!1,envVars:o.providers?.[u]?.envVars??a[u].envVars,disabledAt:i};ot(r);let l={configPath:ce(),disabled:n,ai:En(r)};t.format!=="json"&&s.success(`Disabled AI provider(s): ${n.join(", ")}`),oe(l,t.format,"ai:provider:disable")}async function os(e){let t=Ce(),n=be(t),r=new Date().toISOString();n.gateway={enabled:!0,mode:"local",initializedAt:n.gateway?.initializedAt??r,updatedAt:r},ot(t);let o={configPath:ce(),gateway:n.gateway};e.format!=="json"&&s.success("AI gateway governance initialized in nebutra.config.json"),oe(o,e.format,"ai:gateway:init")}async function ss(e){let t=Ce(),n=be(t),r={configPath:ce(),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")),oe(r,e.format,"ai:gateway:status")}async function as(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=zo(t.fallback),r=Ce(),o=be(r);o.routes[e]={policy:t.policy,fallback:n,updatedAt:new Date().toISOString()},ot(r);let a={configPath:ce(),route:{name:e,...o.routes[e]},routes:o.routes};t.format!=="json"&&s.success(`AI route '${e}' set with policy '${t.policy}'`),oe(a,t.format,"ai:route:set")}async function is(e){let t=Ce(),n=be(t),r=Object.entries(n.routes??{}).map(([a,i])=>({name:a,...i})),o={configPath:ce(),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(", ")}`)),oe(o,e.format,"ai:route:list")}function Cn(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=re(a);await ts({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=re(i);await ns(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=re(i);await rs(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=re(a);await os({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=re(a);await ss({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=re(i);await as(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=re(a);await is({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=re(a);await Xo({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=re(a);await Qo({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=re(i);await Zo(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=re(a);await es({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})})}import{resolve as cs}from"path";function Tt(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 C(e,t){return e?e.length<=8?"***":`${e.substring(0,8)}*** (${t||e.length} chars)`:"not set"}function ls(){try{let e=cs(process.cwd(),"nebutra.config.ts"),t=eo("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";if(n.includes("nextauth"))return"nextauth";if(n.includes("supabase"))return"supabase"}}catch{}return"clerk"}async function ds(e){s.info("Checking authentication configuration...");let t=ls(),n={provider:t,configured:!1,keys:{}};t==="clerk"?(n.keys={CLERK_SECRET_KEY:C(process.env.CLERK_SECRET_KEY,"secret"),NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:C(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,"publishable"),CLERK_WEBHOOK_SECRET:C(process.env.CLERK_WEBHOOK_SECRET,"webhook")},n.configured=!!process.env.CLERK_SECRET_KEY&&!!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY):t==="better-auth"?(n.keys={BETTER_AUTH_SECRET:C(process.env.BETTER_AUTH_SECRET,"secret"),BETTER_AUTH_URL:process.env.BETTER_AUTH_URL||"not set"},n.configured=!!process.env.BETTER_AUTH_SECRET):t==="nextauth"?(n.keys={AUTH_SECRET:C(process.env.AUTH_SECRET,"secret"),NEXTAUTH_SECRET:C(process.env.NEXTAUTH_SECRET,"legacy secret"),NEXTAUTH_URL:process.env.NEXTAUTH_URL||"not set",AUTH_URL:process.env.AUTH_URL||"not set",GOOGLE_CLIENT_ID:C(process.env.GOOGLE_CLIENT_ID,"google client"),GITHUB_CLIENT_ID:C(process.env.GITHUB_CLIENT_ID,"github client")},n.configured=!!(process.env.AUTH_SECRET||process.env.NEXTAUTH_SECRET)):(n.keys={NEXT_PUBLIC_SUPABASE_URL:process.env.NEXT_PUBLIC_SUPABASE_URL||"not set",NEXT_PUBLIC_SUPABASE_ANON_KEY:C(process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,"anon"),SUPABASE_URL:process.env.SUPABASE_URL||"not set",SUPABASE_ANON_KEY:C(process.env.SUPABASE_ANON_KEY,"anon"),SUPABASE_SERVICE_ROLE_KEY:C(process.env.SUPABASE_SERVICE_ROLE_KEY,"service role"),SUPABASE_WEBHOOK_SECRET:C(process.env.SUPABASE_WEBHOOK_SECRET,"webhook")},n.configured=!!((process.env.SUPABASE_URL||process.env.NEXT_PUBLIC_SUPABASE_URL)&&(process.env.SUPABASE_ANON_KEY||process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY)&&process.env.SUPABASE_SERVICE_ROLE_KEY)),e.dryRun||(n.configured?s.success(`Auth provider configured: ${t}`):s.warn(`Auth provider ${t} is not fully configured`)),Tt(n,e.format,"auth:status")}async function us(e,t){let n=["clerk","better-auth","nextauth","supabase"];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_..."')):r==="better-auth"?(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")):r==="nextauth"?(s.info("NextAuth / Auth.js setup requires:"),s.info("1. A generated Auth.js secret"),s.info("2. NEXTAUTH_URL or AUTH_URL pointing at the web app origin"),s.info("3. OAuth provider credentials for each enabled provider"),s.info(""),s.info("To complete setup:"),s.info("1. Generate a new secret:"),s.info(" openssl rand -base64 32"),s.info("2. Add to the production environment:"),s.info(' AUTH_PROVIDER="nextauth"'),s.info(' NEXT_PUBLIC_AUTH_PROVIDER="nextauth"'),s.info(' AUTH_SECRET="<generated_secret>"'),s.info(' NEXTAUTH_URL="https://app.nebutra.com"'),s.info(' GOOGLE_CLIENT_ID="..."'),s.info(' GOOGLE_CLIENT_SECRET="..."')):(s.info("Supabase Auth setup requires:"),s.info("1. Supabase project URL"),s.info("2. Public anon key for browser auth"),s.info("3. Service role key for server-side user/session validation"),s.info(""),s.info("To complete setup:"),s.info(' AUTH_PROVIDER="supabase"'),s.info(' NEXT_PUBLIC_AUTH_PROVIDER="supabase"'),s.info(' NEXT_PUBLIC_SUPABASE_URL="https://xxx.supabase.co"'),s.info(' NEXT_PUBLIC_SUPABASE_ANON_KEY="..."'),s.info(' SUPABASE_URL="https://xxx.supabase.co"'),s.info(' SUPABASE_ANON_KEY="..."'),s.info(' SUPABASE_SERVICE_ROLE_KEY="..."')),t.dryRun||s.success(`Setup instructions for ${r} displayed above`),Tt({provider:r,setupGuide:"Complete setup by following the instructions above",documentationUrl:r==="clerk"?"https://clerk.com/docs":r==="better-auth"?"https://www.better-auth.com":r==="nextauth"?"https://authjs.dev":"https://supabase.com/docs/guides/auth"},t.format,"auth:setup")}async function ms(e){s.info("Checking authentication keys and secrets...");let n={keys:{clerk:{CLERK_SECRET_KEY:C(process.env.CLERK_SECRET_KEY,"secret key"),NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:C(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,"publishable key"),CLERK_WEBHOOK_SECRET:C(process.env.CLERK_WEBHOOK_SECRET,"webhook secret")},betterAuth:{BETTER_AUTH_SECRET:C(process.env.BETTER_AUTH_SECRET,"secret"),BETTER_AUTH_URL:process.env.BETTER_AUTH_URL||"not set"},nextAuth:{AUTH_SECRET:C(process.env.AUTH_SECRET,"secret"),NEXTAUTH_SECRET:C(process.env.NEXTAUTH_SECRET,"legacy secret"),NEXTAUTH_URL:process.env.NEXTAUTH_URL||"not set",AUTH_URL:process.env.AUTH_URL||"not set",GOOGLE_CLIENT_ID:C(process.env.GOOGLE_CLIENT_ID,"google client"),GITHUB_CLIENT_ID:C(process.env.GITHUB_CLIENT_ID,"github client")},supabase:{NEXT_PUBLIC_SUPABASE_URL:process.env.NEXT_PUBLIC_SUPABASE_URL||"not set",NEXT_PUBLIC_SUPABASE_ANON_KEY:C(process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,"anon"),SUPABASE_URL:process.env.SUPABASE_URL||"not set",SUPABASE_ANON_KEY:C(process.env.SUPABASE_ANON_KEY,"anon"),SUPABASE_SERVICE_ROLE_KEY:C(process.env.SUPABASE_SERVICE_ROLE_KEY,"service role")},common:{DATABASE_URL:C(process.env.DATABASE_URL,"database url"),JWT_SECRET:C(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},nextAuth:{secret:!!(process.env.AUTH_SECRET||process.env.NEXTAUTH_SECRET),url:!!(process.env.NEXTAUTH_URL||process.env.AUTH_URL)},supabase:{url:!!(process.env.SUPABASE_URL||process.env.NEXT_PUBLIC_SUPABASE_URL),anonKey:!!(process.env.SUPABASE_ANON_KEY||process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY),serviceRole:!!process.env.SUPABASE_SERVICE_ROLE_KEY}}};if(!e.dryRun){let r=n.configured.clerk.secret&&n.configured.clerk.publishable,o=n.configured.betterAuth.secret&&n.configured.betterAuth.url,a=n.configured.nextAuth.secret&&n.configured.nextAuth.url,i=n.configured.supabase.url&&n.configured.supabase.anonKey&&n.configured.supabase.serviceRole;!r&&!o&&!a&&!i?s.warn("No authentication keys configured"):(s.info("Auth keys status:"),r&&s.success("Clerk configured"),o&&s.success("Better Auth configured"),a&&s.success("NextAuth configured"),i&&s.success("Supabase configured"))}Tt(n,e.format,"auth:keys")}function kn(e){let t=e.command("auth").description("Manage authentication configuration (Clerk, Better Auth, NextAuth, or Supabase)");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 ds({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("setup <provider>").description("Configure Clerk, Better Auth, NextAuth, or Supabase (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 us(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 ms({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}import ve from"fs";import se from"path";import*as st from"@clack/prompts";import _n from"picocolors";var On=["ts","py"],ps=`{
|
|
105
|
+
`;await ke.writeFile(n.path,l,"utf8")}if(t){let o=await ke.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]=Co(a[l])}await ke.writeFile(t.path,`${JSON.stringify(a,null,2)}
|
|
106
|
+
`,"utf8")}}async function Ao(e,t,n){let r=[];for(let o of e){let a=await dn(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=gn(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 R.select({message:`Select a provider for ${o}`,options:a.providers.map(u=>({value:u.id,label:u.id,hint:u.description}))});R.isCancel(l)&&(R.log.info("Installation cancelled."),process.exit(c.CANCELLED)),i=gn(a,String(l))}r.push({name:o,descriptor:a,provider:i})}return r}function _o(e){R.log.info(Fe.yellow("Planned local install:"));for(let t of e.features)R.log.message(` - ${t.name}${t.provider?` (${t.provider})`:""}: ${t.description}`);for(let t of e.operations)if(t.type==="write-file"&&R.log.message(` - ${t.status.toUpperCase()} ${t.relativePath}`),t.type==="merge-env"&&t.add.length>0&&R.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&&R.log.message(` - UPDATE ${t.relativePath}: ${n.map(r=>`${r.name}@${r.to}`).join(", ")}`)}}async function hn(e,t){t.dryRun||R.intro(Fe.bgCyan(Fe.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."):(R.log.warn("No components specified."),R.outro("Operation aborted.")),process.exit(c.INVALID_ARGS)),t.dryRun&&(t["21st"]||t.v0)&&(mn(wo(e,t)),process.exit(c.DRY_RUN_OK)),t.ifNotExists&&(t["21st"]||t.v0)&&(t["21st"]&&un(t["21st"])||t.v0&&un("shadcn-component"))&&(s.info("Component already exists. Skipping installation."),process.exit(c.SUCCESS)),t["21st"]){let r=n||await R.confirm({message:`Install 21st.dev component: ${t["21st"]}?`,initialValue:!0});(R.isCancel(r)||!r)&&(R.log.info("Installation cancelled."),process.exit(c.CANCELLED)),R.log.info(Fe.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}`;It(`pnpm dlx shadcn@latest add "${a}"`,{stdio:"inherit"}),s.success(`Successfully installed ${o}`),process.exit(c.SUCCESS)}catch(o){if(Pt(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 R.confirm({message:`Install v0 component from: ${t.v0}?`,initialValue:!0});(R.isCancel(r)||!r)&&(R.log.info("Installation cancelled."),process.exit(c.CANCELLED)),R.log.info(Fe.cyan(`Invoking shadcn integration for v0 URL: ${t.v0}...`));try{It(`pnpm dlx shadcn@latest add "${t.v0}"`,{stdio:"inherit"}),s.success("Successfully installed v0 component"),process.exit(c.SUCCESS)}catch(o){if(Pt(o))throw o;s.error(`Failed to install component: ${o instanceof Error?o.message:"Unknown error"}`),process.exit(c.ERROR)}}try{let r=await Ao(e,t,n),o=await ko(r,process.cwd(),t.dryRun?"dry-run":"apply");if(t.dryRun&&(mn(o),process.exit(c.DRY_RUN_OK)),Oo(o)){let i=o.operations.filter(l=>l.type==="write-file"&&l.status==="conflict").map(l=>l.relativePath);t.ifNotExists&&!yn(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)}yn(o)||(s.info("Feature install already satisfied. No files, env keys, or dependencies needed changes."),process.exit(c.SUCCESS)),_o(o);let a=n||await R.confirm({message:`Proceed with installing ${o.features.map(i=>i.name).join(", ")}?`,initialValue:!0});(R.isCancel(a)||!a)&&(R.log.info("Installation cancelled."),process.exit(c.CANCELLED)),await xo(o),s.success(`Installed ${o.features.map(i=>i.name).join(", ")} into ${o.cwd}`),process.exit(c.SUCCESS)}catch(r){if(Pt(r))throw r;s.error(r instanceof Error?r.message:String(r)),process.exit(c.CONFIG_ERROR)}}import U from"picocolors";import{stderr as he,stdout as bn}from"process";import ce from"picocolors";function $o(){return process.env.NEBUTRA_NON_TTY||process.env.CI||process.env.GITHUB_ACTIONS?!1:process.stderr.isTTY===!0}function vn(e){if(e)return e;let t=process.env.NEBUTRA_OUTPUT_FORMAT;return t&&["json","table","plain"].includes(t)?t:$o()?"plain":"json"}function Po(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(`
|
|
107
|
+
`)}function Io(e){return JSON.stringify(e)}function wn(e){if(!Array.isArray(e))return Nt(e);if(e.length===0)return"(empty)";if(typeof e[0]!="object"||e[0]===null)return Nt(e);let t=Po(e);return ce.gray(t)}function Nt(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 O(e,t){let n=vn(t?.format),r=t?.stream==="stderr"?he:bn,o;switch(n){case"json":o=Io(e);break;case"table":o=wn(e);break;default:o=Nt(e);break}r.write(o+`
|
|
108
|
+
`)}function x(e,t={}){let n=vn(t.format),r;switch(n){case"json":r=JSON.stringify(e);break;case"table":r=wn(e);break;default:r=JSON.stringify(e,null,2);break}bn.write(r+`
|
|
109
|
+
`)}function d(e,t="info"){let n;switch(t){case"error":n=ce.red("\u2716");break;case"warn":n=ce.yellow("\u26A0");break;case"success":n=ce.green("\u2713");break;default:n=ce.blue("\u2139");break}let r=`${n} ${e}`;he.write(r+`
|
|
110
|
+
`)}function A(e,t){if(!(process.env.NEBUTRA_DEBUG||process.argv.includes("--debug")))return;let r=ce.dim(`[debug] ${e}`);he.write(r+`
|
|
111
|
+
`),t&&Object.keys(t).length>0&&he.write(ce.dim(JSON.stringify(t,null,2))+`
|
|
112
|
+
`)}function ot(e){he.write(`
|
|
113
|
+
`),he.write(ce.bold(ce.cyan(e))+`
|
|
114
|
+
`),he.write(ce.gray("-".repeat(e.length))+`
|
|
115
|
+
`),he.write(`
|
|
116
|
+
`)}async function X(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 x(l),{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 E(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 No(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 X(n,{dryRun:e.dryRun});if(r.ok||(s.error(`Failed to fetch tenants: ${r.error}`),process.exit(c.NETWORK_ERROR)),e.dryRun){E(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"?E(o,e.format,"admin:tenants"):(s.info(`Found ${o.total} tenant(s)`),o.tenants.length>0&&(console.log(`
|
|
117
|
+
Tenants:`),o.tenants.forEach(a=>{console.log(` ${U.blue(a.id)} \u2014 ${a.name||"(unnamed)"}`),console.log(` Status: ${a.status}`),a.plan&&console.log(` Plan: ${a.plan}`)})))}async function To(e,t){s.info(`Fetching tenant ${e}...`);let n=await X(`/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){E(n.data,t.format,"admin:tenant");return}if(t.format==="json")E({tenant:n.data},t.format,"admin:tenant");else{let r=n.data;s.success(`Tenant ${U.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 jo(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 X(`/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){E(n.data,t.format,"admin:suspend");return}s.success(`Tenant ${U.blue(e)} suspended`),t.format==="json"&&E({suspended:!0,tenantId:e},t.format,"admin:suspend")}async function Uo(e,t){s.info(`Unsuspending tenant ${e}...`);let n=await X(`/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){E(n.data,t.format,"admin:unsuspend");return}s.success(`Tenant ${U.blue(e)} unsuspended`),t.format==="json"&&E({unsuspended:!0,tenantId:e},t.format,"admin:unsuspend")}async function Lo(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 X(n,{dryRun:e.dryRun});if(r.ok||(s.error(`Failed to fetch usage report: ${r.error}`),process.exit(c.NETWORK_ERROR)),e.dryRun){E(r.data,e.format,"admin:usage");return}let o={period:r.data?.period,topTenants:r.data?.topTenants||[],totalUsage:r.data?.totalUsage};e.format==="json"?E(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(`
|
|
118
|
+
Top tenants:`),o.topTenants.forEach((a,i)=>{console.log(` ${i+1}. ${a.tenantName||a.tenantId} \u2014 ${a.usage}`)})))}async function Do(e){s.info("Fetching dead letter queue...");let t=await X("/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){E(t.data,e.format,"admin:dlq:list");return}let n={messages:t.data?.messages||[],count:t.data?.count||0};e.format==="json"?E(n,e.format,"admin:dlq:list"):(s.info(`Found ${n.count} message(s) in DLQ`),n.messages.length>0&&(console.log(`
|
|
119
|
+
Messages:`),n.messages.forEach(r=>{console.log(` ${U.blue(r.id)}`),console.log(` Type: ${r.type}`),console.log(` Error: ${r.error}`),console.log(` Created: ${r.createdAt}`)})))}async function Fo(e,t){s.info(`Replaying DLQ message ${e}...`);let n=await X(`/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){E(n.data,t.format,"admin:dlq:replay");return}s.success(`Message ${U.blue(e)} replayed`),t.format==="json"&&E({replayed:!0,messageId:e},t.format,"admin:dlq:replay")}async function Go(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 X("/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){E(t.data,e.format,"admin:dlq:purge");return}s.success("DLQ purged"),e.format==="json"&&E({purged:!0,count:t.data?.count},e.format,"admin:dlq:purge")}async function Mo(e){s.info("Fetching feature flags...");let t=await X("/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){E(t.data,e.format,"admin:flags:list");return}let n={flags:t.data?.flags||[],total:t.data?.total||0};e.format==="json"?E(n,e.format,"admin:flags:list"):(s.info(`Found ${n.total} flag(s)`),n.flags.length>0&&(console.log(`
|
|
120
|
+
Flags:`),n.flags.forEach(r=>{let o=r.override!==void 0?U.yellow(String(r.override)):U.gray(String(r.default));console.log(` ${U.blue(r.name)} = ${o}`)})))}async function Vo(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 X(`/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){E(o.data,n.format,"admin:flags:set");return}s.success(`Flag ${U.blue(e)} set to ${r}`),n.format==="json"&&E({flag:e,value:r},n.format,"admin:flags:set")}async function Ko(e,t){s.info(`Unsetting flag ${e}...`);let n=await X(`/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){E(n.data,t.format,"admin:flags:unset");return}s.success(`Flag ${U.blue(e)} unset`),t.format==="json"&&E({flag:e,unset:!0},t.format,"admin:flags:unset")}async function Bo(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 X(n,{dryRun:e.dryRun});if(r.ok||(s.error(`Failed to fetch audit log: ${r.error}`),process.exit(c.NETWORK_ERROR)),e.dryRun){E(r.data,e.format,"admin:audit");return}let o={entries:r.data?.entries||[],total:r.data?.total||0};e.format==="json"?E(o,e.format,"admin:audit"):(s.info(`Found ${o.total} audit log entries`),o.entries.length>0&&(console.log(`
|
|
121
|
+
Audit entries:`),o.entries.forEach(a=>{console.log(` ${a.timestamp} \u2014 ${U.blue(a.action)} by ${a.actor}`),a.details&&console.log(` ${a.details}`)})))}async function qo(e){s.info("Running platform health check...");let t=await X("/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){E(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")E(n,e.format,"admin:health");else{let r=n.status==="healthy"?U.green:n.status==="degraded"?U.yellow:U.red;s.info(`Platform status: ${r(n.status)}`),n.services&&Object.keys(n.services).length>0&&(console.log(`
|
|
122
|
+
Services:`),Object.entries(n.services).forEach(([o,a])=>{let i=a.status==="healthy"?U.green("\u2713"):U.red("\u2717");console.log(` ${i} ${o}: ${a.status}`),a.latency&&console.log(` Latency: ${a.latency}ms`),a.error&&console.log(` Error: ${a.error}`)}))}}function Sn(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 No({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 To(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 jo(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 Uo(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 Lo({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 Do({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 Fo(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 Go({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 Mo({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 Vo(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 Ko(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 Bo({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 qo({format:o.format||a.format,dryRun:o.dryRun||a.dryRun})})}import{existsSync as Yo,readFileSync as Ho,writeFileSync as zo}from"fs";import{resolve as Tt}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"]},Wo=[{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)"}],be={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:Wo,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 En="nebutra.config.json",Jo=v.ai.filter(e=>e!=="none"),Rn={"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 oe(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 Xo(e){return e.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function Qo(e){return Rn[e]?Rn[e]:[`${e.replace(/-/g,"_").toUpperCase()}_API_KEY`]}function st(){return Object.fromEntries(Jo.map(e=>[e,{id:e,name:Xo(e),envVars:Qo(e)}]))}function le(){return Tt(process.cwd(),En)}function Oe(){let e=le();if(!Yo(e))return{$schema:"https://nebutra.com/schema.json"};try{return JSON.parse(Ho(e,"utf-8"))}catch(t){s.error(`Failed to parse ${En}: ${t instanceof Error?t.message:String(t)}`),process.exit(c.ERROR)}}function at(e){zo(le(),`${JSON.stringify(e,null,2)}
|
|
123
|
+
`)}function ve(e){return e.ai={providers:{},routes:{},...e.ai},e.ai.providers={...e.ai.providers},e.ai.routes={...e.ai.routes},e.ai}function Cn(e){let t=st(),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 Zo(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 kn(e){let t=ve(e),n=t.providers??{},r=t.routes??{},o=Object.entries(n).filter(([,a])=>a.enabled).map(([a])=>a);return{path:le(),gateway:t.gateway??null,providers:n,enabledProviders:o,routes:r}}function re(e){let t=typeof e.opts=="function"?e.opts():e,n=typeof e.optsWithGlobals=="function"?e.optsWithGlobals():t,r=t.format??n.format??es();return{...n,...t,...r?{format:r}:{}}}function es(){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 Ge(e){return e?e.length<=8?"***":`${e.substring(0,8)}***`:"not set"}async function ts(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:Ge(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`)),oe(n,e.format,"ai:models")}async function ns(e){e.format!=="json"&&s.info("Scanning for available agents...");try{let t=Tt(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)`)),oe(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 rs(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"),oe(n,t.format,"ai:test")}async function os(e){e.format!=="json"&&s.info("Checking AI configuration...");let t={providers:{openai:{key:Ge(process.env.OPENAI_API_KEY),configured:!!process.env.OPENAI_API_KEY},anthropic:{key:Ge(process.env.ANTHROPIC_API_KEY),configured:!!process.env.ANTHROPIC_API_KEY},openrouter:{key:Ge(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=Tt(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(", ")}`)),oe(t,e.format,"ai:config")}async function ss(e){let t=Oe(),n=ve(t),r=st(),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:Ge(process.env[u])}))}}),a={configPath:le(),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(", ")}`)}oe(a,e.format,"ai:provider:list")}async function as(e,t){let n=Cn(e),r=Oe(),o=ve(r),a=st(),i=new Date().toISOString();for(let u of n)o.providers[u]={enabled:!0,envVars:a[u].envVars,enabledAt:o.providers?.[u]?.enabledAt??i};at(r);let l={configPath:le(),enabled:n,env:t.env===!0?n.flatMap(u=>a[u].envVars.map(m=>({provider:u,name:m,example:`${m}=`}))):void 0,ai:kn(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}=`)}oe(l,t.format,"ai:provider:enable")}async function is(e,t){let n=Cn(e),r=Oe(),o=ve(r),a=st(),i=new Date().toISOString();for(let u of n)o.providers[u]={enabled:!1,envVars:o.providers?.[u]?.envVars??a[u].envVars,disabledAt:i};at(r);let l={configPath:le(),disabled:n,ai:kn(r)};t.format!=="json"&&s.success(`Disabled AI provider(s): ${n.join(", ")}`),oe(l,t.format,"ai:provider:disable")}async function cs(e){let t=Oe(),n=ve(t),r=new Date().toISOString();n.gateway={enabled:!0,mode:"local",initializedAt:n.gateway?.initializedAt??r,updatedAt:r},at(t);let o={configPath:le(),gateway:n.gateway};e.format!=="json"&&s.success("AI gateway governance initialized in nebutra.config.json"),oe(o,e.format,"ai:gateway:init")}async function ls(e){let t=Oe(),n=ve(t),r={configPath:le(),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")),oe(r,e.format,"ai:gateway:status")}async function ds(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=Zo(t.fallback),r=Oe(),o=ve(r);o.routes[e]={policy:t.policy,fallback:n,updatedAt:new Date().toISOString()},at(r);let a={configPath:le(),route:{name:e,...o.routes[e]},routes:o.routes};t.format!=="json"&&s.success(`AI route '${e}' set with policy '${t.policy}'`),oe(a,t.format,"ai:route:set")}async function us(e){let t=Oe(),n=ve(t),r=Object.entries(n.routes??{}).map(([a,i])=>({name:a,...i})),o={configPath:le(),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(", ")}`)),oe(o,e.format,"ai:route:list")}function On(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=re(a);await ss({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=re(i);await as(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=re(i);await is(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=re(a);await cs({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=re(a);await ls({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=re(i);await ds(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=re(a);await us({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=re(a);await ts({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=re(a);await ns({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=re(i);await rs(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=re(a);await os({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})})}import{resolve as ms}from"path";function jt(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 C(e,t){return e?e.length<=8?"***":`${e.substring(0,8)}*** (${t||e.length} chars)`:"not set"}function ps(){try{let e=ms(process.cwd(),"nebutra.config.ts"),t=oo("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";if(n.includes("nextauth"))return"nextauth";if(n.includes("supabase"))return"supabase"}}catch{}return"clerk"}async function fs(e){s.info("Checking authentication configuration...");let t=ps(),n={provider:t,configured:!1,keys:{}};t==="clerk"?(n.keys={CLERK_SECRET_KEY:C(process.env.CLERK_SECRET_KEY,"secret"),NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:C(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,"publishable"),CLERK_WEBHOOK_SECRET:C(process.env.CLERK_WEBHOOK_SECRET,"webhook")},n.configured=!!process.env.CLERK_SECRET_KEY&&!!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY):t==="better-auth"?(n.keys={BETTER_AUTH_SECRET:C(process.env.BETTER_AUTH_SECRET,"secret"),BETTER_AUTH_URL:process.env.BETTER_AUTH_URL||"not set"},n.configured=!!process.env.BETTER_AUTH_SECRET):t==="nextauth"?(n.keys={AUTH_SECRET:C(process.env.AUTH_SECRET,"secret"),NEXTAUTH_SECRET:C(process.env.NEXTAUTH_SECRET,"legacy secret"),NEXTAUTH_URL:process.env.NEXTAUTH_URL||"not set",AUTH_URL:process.env.AUTH_URL||"not set",GOOGLE_CLIENT_ID:C(process.env.GOOGLE_CLIENT_ID,"google client"),GITHUB_CLIENT_ID:C(process.env.GITHUB_CLIENT_ID,"github client")},n.configured=!!(process.env.AUTH_SECRET||process.env.NEXTAUTH_SECRET)):(n.keys={NEXT_PUBLIC_SUPABASE_URL:process.env.NEXT_PUBLIC_SUPABASE_URL||"not set",NEXT_PUBLIC_SUPABASE_ANON_KEY:C(process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,"anon"),SUPABASE_URL:process.env.SUPABASE_URL||"not set",SUPABASE_ANON_KEY:C(process.env.SUPABASE_ANON_KEY,"anon"),SUPABASE_SERVICE_ROLE_KEY:C(process.env.SUPABASE_SERVICE_ROLE_KEY,"service role"),SUPABASE_WEBHOOK_SECRET:C(process.env.SUPABASE_WEBHOOK_SECRET,"webhook")},n.configured=!!((process.env.SUPABASE_URL||process.env.NEXT_PUBLIC_SUPABASE_URL)&&(process.env.SUPABASE_ANON_KEY||process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY)&&process.env.SUPABASE_SERVICE_ROLE_KEY)),e.dryRun||(n.configured?s.success(`Auth provider configured: ${t}`):s.warn(`Auth provider ${t} is not fully configured`)),jt(n,e.format,"auth:status")}async function gs(e,t){let n=["clerk","better-auth","nextauth","supabase"];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_..."')):r==="better-auth"?(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")):r==="nextauth"?(s.info("NextAuth / Auth.js setup requires:"),s.info("1. A generated Auth.js secret"),s.info("2. NEXTAUTH_URL or AUTH_URL pointing at the web app origin"),s.info("3. OAuth provider credentials for each enabled provider"),s.info(""),s.info("To complete setup:"),s.info("1. Generate a new secret:"),s.info(" openssl rand -base64 32"),s.info("2. Add to the production environment:"),s.info(' AUTH_PROVIDER="nextauth"'),s.info(' NEXT_PUBLIC_AUTH_PROVIDER="nextauth"'),s.info(' AUTH_SECRET="<generated_secret>"'),s.info(' NEXTAUTH_URL="https://app.nebutra.com"'),s.info(' GOOGLE_CLIENT_ID="..."'),s.info(' GOOGLE_CLIENT_SECRET="..."')):(s.info("Supabase Auth setup requires:"),s.info("1. Supabase project URL"),s.info("2. Public anon key for browser auth"),s.info("3. Service role key for server-side user/session validation"),s.info(""),s.info("To complete setup:"),s.info(' AUTH_PROVIDER="supabase"'),s.info(' NEXT_PUBLIC_AUTH_PROVIDER="supabase"'),s.info(' NEXT_PUBLIC_SUPABASE_URL="https://xxx.supabase.co"'),s.info(' NEXT_PUBLIC_SUPABASE_ANON_KEY="..."'),s.info(' SUPABASE_URL="https://xxx.supabase.co"'),s.info(' SUPABASE_ANON_KEY="..."'),s.info(' SUPABASE_SERVICE_ROLE_KEY="..."')),t.dryRun||s.success(`Setup instructions for ${r} displayed above`),jt({provider:r,setupGuide:"Complete setup by following the instructions above",documentationUrl:r==="clerk"?"https://clerk.com/docs":r==="better-auth"?"https://www.better-auth.com":r==="nextauth"?"https://authjs.dev":"https://supabase.com/docs/guides/auth"},t.format,"auth:setup")}async function ys(e){s.info("Checking authentication keys and secrets...");let n={keys:{clerk:{CLERK_SECRET_KEY:C(process.env.CLERK_SECRET_KEY,"secret key"),NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:C(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,"publishable key"),CLERK_WEBHOOK_SECRET:C(process.env.CLERK_WEBHOOK_SECRET,"webhook secret")},betterAuth:{BETTER_AUTH_SECRET:C(process.env.BETTER_AUTH_SECRET,"secret"),BETTER_AUTH_URL:process.env.BETTER_AUTH_URL||"not set"},nextAuth:{AUTH_SECRET:C(process.env.AUTH_SECRET,"secret"),NEXTAUTH_SECRET:C(process.env.NEXTAUTH_SECRET,"legacy secret"),NEXTAUTH_URL:process.env.NEXTAUTH_URL||"not set",AUTH_URL:process.env.AUTH_URL||"not set",GOOGLE_CLIENT_ID:C(process.env.GOOGLE_CLIENT_ID,"google client"),GITHUB_CLIENT_ID:C(process.env.GITHUB_CLIENT_ID,"github client")},supabase:{NEXT_PUBLIC_SUPABASE_URL:process.env.NEXT_PUBLIC_SUPABASE_URL||"not set",NEXT_PUBLIC_SUPABASE_ANON_KEY:C(process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,"anon"),SUPABASE_URL:process.env.SUPABASE_URL||"not set",SUPABASE_ANON_KEY:C(process.env.SUPABASE_ANON_KEY,"anon"),SUPABASE_SERVICE_ROLE_KEY:C(process.env.SUPABASE_SERVICE_ROLE_KEY,"service role")},common:{DATABASE_URL:C(process.env.DATABASE_URL,"database url"),JWT_SECRET:C(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},nextAuth:{secret:!!(process.env.AUTH_SECRET||process.env.NEXTAUTH_SECRET),url:!!(process.env.NEXTAUTH_URL||process.env.AUTH_URL)},supabase:{url:!!(process.env.SUPABASE_URL||process.env.NEXT_PUBLIC_SUPABASE_URL),anonKey:!!(process.env.SUPABASE_ANON_KEY||process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY),serviceRole:!!process.env.SUPABASE_SERVICE_ROLE_KEY}}};if(!e.dryRun){let r=n.configured.clerk.secret&&n.configured.clerk.publishable,o=n.configured.betterAuth.secret&&n.configured.betterAuth.url,a=n.configured.nextAuth.secret&&n.configured.nextAuth.url,i=n.configured.supabase.url&&n.configured.supabase.anonKey&&n.configured.supabase.serviceRole;!r&&!o&&!a&&!i?s.warn("No authentication keys configured"):(s.info("Auth keys status:"),r&&s.success("Clerk configured"),o&&s.success("Better Auth configured"),a&&s.success("NextAuth configured"),i&&s.success("Supabase configured"))}jt(n,e.format,"auth:keys")}function xn(e){let t=e.command("auth").description("Manage authentication configuration (Clerk, Better Auth, NextAuth, or Supabase)");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 fs({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("setup <provider>").description("Configure Clerk, Better Auth, NextAuth, or Supabase (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 gs(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 ys({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}import we from"fs";import se from"path";import*as it from"@clack/prompts";import $n from"picocolors";var An=["ts","py"],hs=`{
|
|
124
124
|
"name": "@nebutra/gateway",
|
|
125
125
|
"version": "0.1.0",
|
|
126
126
|
"private": true,
|
|
@@ -135,19 +135,19 @@ Services:`),Object.entries(n.services).forEach(([o,a])=>{let i=a.status==="healt
|
|
|
135
135
|
"hono": "^4.6.0"
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
-
`,
|
|
138
|
+
`,bs=`import { Hono } from "hono";
|
|
139
139
|
|
|
140
140
|
const app = new Hono();
|
|
141
141
|
|
|
142
142
|
app.get("/healthz", (c) => c.json({ status: "ok" }));
|
|
143
143
|
|
|
144
144
|
export default app;
|
|
145
|
-
`,
|
|
145
|
+
`,vs=`# @nebutra/gateway
|
|
146
146
|
|
|
147
147
|
TypeScript / Hono BFF \u2014 auth, tenancy, rate-limit, and routing.
|
|
148
148
|
|
|
149
149
|
Per ADR 2026-05-10, this is the DEFAULT for new backend work.
|
|
150
|
-
`;function
|
|
150
|
+
`;function ws(e){return`[project]
|
|
151
151
|
name = "${e}"
|
|
152
152
|
version = "0.1.0"
|
|
153
153
|
description = "Nebutra Python backend service: ${e}"
|
|
@@ -164,7 +164,7 @@ build-backend = "hatchling.build"
|
|
|
164
164
|
|
|
165
165
|
[tool.hatch.build.targets.wheel]
|
|
166
166
|
packages = ["src"]
|
|
167
|
-
`}function
|
|
167
|
+
`}function Ss(e){return`# backends/python/${e}
|
|
168
168
|
|
|
169
169
|
A Python (FastAPI) backend service.
|
|
170
170
|
|
|
@@ -190,7 +190,7 @@ it belongs in \`backends/gateway\` (TypeScript) instead.
|
|
|
190
190
|
uv sync
|
|
191
191
|
uv run uvicorn src.main:app --reload --port 8000
|
|
192
192
|
\`\`\`
|
|
193
|
-
`}var
|
|
193
|
+
`}var Rs=`from fastapi import FastAPI
|
|
194
194
|
from pydantic import BaseModel
|
|
195
195
|
|
|
196
196
|
app = FastAPI(title="nebutra-python-service")
|
|
@@ -203,35 +203,35 @@ class HealthResponse(BaseModel):
|
|
|
203
203
|
@app.get("/healthz", response_model=HealthResponse)
|
|
204
204
|
async def healthz() -> HealthResponse:
|
|
205
205
|
return HealthResponse(status="ok")
|
|
206
|
-
`;async function
|
|
206
|
+
`;async function Es(e,t){let n=se.join(e,"backends/gateway");we.existsSync(n)&&(s.info(`backends/gateway already exists in this monorepo \u2014 nothing to do.
|
|
207
207
|
(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)+`
|
|
208
|
-
`),process.exit(c.DRY_RUN_OK)),
|
|
209
|
-
`),process.exit(c.DRY_RUN_OK));for(let[i,l]of a)ve.mkdirSync(se.dirname(i),{recursive:!0}),ve.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 Ss(e,t={}){On.includes(e)||(s.error(`Invalid runtime: ${_n.red(e)}. Valid: ${On.join(", ")}`),process.exit(c.INVALID_ARGS));let n=h();e==="ts"?await vs(n,t):await ws(n,t)}function An(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 Ss(n,{name:r.name,dryRun:r.dryRun||!1,yes:o.yes||!1,quiet:o.quiet||!1})})}function Ut(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 Rs(e,t){return e?e.length<=8?"***":`${e.substring(0,8)}*** (${t||e.length} chars)`:"not set"}var at={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 Es(e){s.info("Checking payment provider configuration...");let t=Object.entries(at).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:Rs(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`)),Ut(r,e.format,"billing:status")}async function Cs(e,t){let n=Object.keys(at);n.includes(e.toLowerCase())||(s.error(`Invalid provider: ${e}. Choose from: ${n.join(", ")}`),process.exit(c.INVALID_ARGS));let r=e.toLowerCase(),o=at[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};Ut(a,t.format,"billing:setup")}async function ks(e){s.info("Checking billing webhook configuration...");let t=process.env.NEXT_PUBLIC_APP_URL||"https://yourdomain.com",n=Object.entries(at).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)`)})),Ut(r,e.format,"billing:webhooks")}function $n(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 Es({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 Cs(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 ks({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}function Fe(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 Pn(e){return/^#[0-9A-Fa-f]{6}$/.test(e)}async function Os(e){s.info("Initializing brand system...");let t=await I("brand:init",{dryRun:e.dryRun,interactive:e.interactive??!0});Fe(t,e.format,"brand:init"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand system initialized successfully")}async function xs(e){s.info("Applying brand configuration...");let t=await I("brand:apply",{dryRun:e.dryRun,interactive:e.interactive??!0});Fe(t,e.format,"brand:apply"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand applied successfully")}async function _s(e){let t=[];e.primary&&(Pn(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&&(Pn(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 $({command:"node",args:["scripts/generate-palette.mjs",...t],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Generate brand palette"});Fe(n,e.format,"brand:palette"),n.exitCode!==0&&process.exit(n.exitCode),e.dryRun||s.success("Color palette generated successfully")}async function As(e){s.info("Syncing brand tokens across packages...");let t=await I("brand:sync",{dryRun:e.dryRun,interactive:e.interactive??!0});Fe(t,e.format,"brand:sync"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand tokens synced successfully")}async function $s(e){s.info("Verifying brand token consistency...");let t=await I("brand:verify-tokens",{dryRun:e.dryRun,interactive:e.interactive??!0});Fe(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 Nn(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 Os({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 xs({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 _s({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 As({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 $s({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}import me from"picocolors";var Ps=process.env.NEBUTRA_API_URL||"http://localhost:3001/api";async function L(e,t={}){let n=`${Ps}${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 Ns(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 Is(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 L(`/community/showcase?${t.toString()}`);if(!n.submissions||n.submissions.length===0)return s.warn("No showcase submissions found"),c.SUCCESS;s.info(`
|
|
208
|
+
`),process.exit(c.DRY_RUN_OK)),we.mkdirSync(se.join(n,"src"),{recursive:!0}),we.writeFileSync(se.join(n,"package.json"),hs),we.writeFileSync(se.join(n,"src/index.ts"),bs),we.writeFileSync(se.join(n,"README.md"),vs),s.success("Created backends/gateway scaffold"),process.exit(c.SUCCESS)}function _n(e){return/^[a-z][a-z0-9_-]{0,40}$/.test(e)}async function Cs(e,t){let n=t.name,r=process.stdout.isTTY===!0&&!t.yes;if(!n&&r){let i=await it.text({message:"Service name (lowercase, hyphens or underscores):",placeholder:"translator",validate:l=>_n(l)?void 0:"Invalid name"});it.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)),_n(n)||(s.error(`Invalid name: ${$n.red(n)}. Use lowercase letters, digits, hyphen, underscore.`),process.exit(c.INVALID_ARGS));let o=se.join(e,"backends/python",n);we.existsSync(o)&&(s.error(`backends/python/${n} already exists \u2014 refusing to overwrite.`),process.exit(c.CONFLICT));let a=[[se.join(o,"pyproject.toml"),ws(n)],[se.join(o,"README.md"),Ss(n)],[se.join(o,"src/main.py"),Rs],[se.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)+`
|
|
209
|
+
`),process.exit(c.DRY_RUN_OK));for(let[i,l]of a)we.mkdirSync(se.dirname(i),{recursive:!0}),we.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 ks(e,t={}){An.includes(e)||(s.error(`Invalid runtime: ${$n.red(e)}. Valid: ${An.join(", ")}`),process.exit(c.INVALID_ARGS));let n=b();e==="ts"?await Es(n,t):await Cs(n,t)}function Pn(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 ks(n,{name:r.name,dryRun:r.dryRun||!1,yes:o.yes||!1,quiet:o.quiet||!1})})}function Ut(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 Os(e,t){return e?e.length<=8?"***":`${e.substring(0,8)}*** (${t||e.length} chars)`:"not set"}var ct={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 xs(e){s.info("Checking payment provider configuration...");let t=Object.entries(ct).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:Os(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`)),Ut(r,e.format,"billing:status")}async function As(e,t){let n=Object.keys(ct);n.includes(e.toLowerCase())||(s.error(`Invalid provider: ${e}. Choose from: ${n.join(", ")}`),process.exit(c.INVALID_ARGS));let r=e.toLowerCase(),o=ct[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};Ut(a,t.format,"billing:setup")}async function _s(e){s.info("Checking billing webhook configuration...");let t=process.env.NEXT_PUBLIC_APP_URL||"https://yourdomain.com",n=Object.entries(ct).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)`)})),Ut(r,e.format,"billing:webhooks")}function In(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 xs({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 As(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 _s({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}function Me(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 $s(e){s.info("Initializing brand system...");let t=await N("brand:init",{dryRun:e.dryRun,interactive:e.interactive??!0});Me(t,e.format,"brand:init"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand system initialized successfully")}async function Ps(e){s.info("Applying brand configuration...");let t=await N("brand:apply",{dryRun:e.dryRun,interactive:e.interactive??!0});Me(t,e.format,"brand:apply"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand applied successfully")}async function Is(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 $({command:"node",args:["scripts/generate-palette.mjs",...t],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Generate brand palette"});Me(n,e.format,"brand:palette"),n.exitCode!==0&&process.exit(n.exitCode),e.dryRun||s.success("Color palette generated successfully")}async function Ns(e){s.info("Syncing brand tokens across packages...");let t=await N("brand:sync",{dryRun:e.dryRun,interactive:e.interactive??!0});Me(t,e.format,"brand:sync"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||s.success("Brand tokens synced successfully")}async function Ts(e){s.info("Verifying brand token consistency...");let t=await N("brand:verify-tokens",{dryRun:e.dryRun,interactive:e.interactive??!0});Me(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 Tn(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 $s({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 Ps({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 Is({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 Ns({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 Ts({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}import fe from"picocolors";var js=process.env.NEBUTRA_API_URL||"http://localhost:3001/api";async function L(e,t={}){let n=`${js}${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 Us(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 Ls(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 L(`/community/showcase?${t.toString()}`);if(!n.submissions||n.submissions.length===0)return s.warn("No showcase submissions found"),c.SUCCESS;s.info(`
|
|
210
210
|
Showcase Submissions (${n.submissions.length})
|
|
211
|
-
`);for(let r of n.submissions);return c.SUCCESS}catch(t){return s.error(`Failed to list showcase: ${t.message}`),c.ERROR}}async function
|
|
211
|
+
`);for(let r of n.submissions);return c.SUCCESS}catch(t){return s.error(`Failed to list showcase: ${t.message}`),c.ERROR}}async function Ds(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 L(`/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 Fs(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 L(`/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 Gs(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 L(`/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 Ms(e){try{s.info("Fetching showcase statistics...");let t=await L("/community/showcase/stats");return s.info(`
|
|
212
212
|
Showcase Statistics
|
|
213
|
-
`),c.SUCCESS}catch(t){return s.error(`Failed to fetch showcase stats: ${t.message}`),c.ERROR}}async function
|
|
213
|
+
`),c.SUCCESS}catch(t){return s.error(`Failed to fetch showcase stats: ${t.message}`),c.ERROR}}async function Vs(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 L(`/community/content?${t.toString()}`);if(!n.items||n.items.length===0)return s.warn("No content found"),c.SUCCESS;s.info(`
|
|
214
214
|
Community Content (${n.items.length})
|
|
215
|
-
`);for(let r of n.items);return c.SUCCESS}catch(t){return s.error(`Failed to list content: ${t.message}`),c.ERROR}}async function
|
|
215
|
+
`);for(let r of n.items);return c.SUCCESS}catch(t){return s.error(`Failed to list content: ${t.message}`),c.ERROR}}async function Ks(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 L(`/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 Bs(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 L(`/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 qs(e){try{s.info("Fetching content analytics...");let t=await L("/community/content/analytics");s.info(`
|
|
216
216
|
Content Performance Metrics
|
|
217
|
-
`);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
|
|
217
|
+
`);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 Ws(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 L(`/community/members?${t.toString()}`);if(!n.members||n.members.length===0)return s.warn("No members found"),c.SUCCESS;s.info(`
|
|
218
218
|
Community Members (${n.members.length})
|
|
219
|
-
`);for(let r of n.members){let a={owner:
|
|
219
|
+
`);for(let r of n.members){let a={owner:fe.red,admin:fe.yellow,moderator:fe.blue,member:fe.gray}[String(r.role)]??fe.gray}return c.SUCCESS}catch(t){return s.error(`Failed to list members: ${t.message}`),c.ERROR}}async function Ys(e,t){try{s.info(`Fetching profile for ${e}...`);let n=await L(`/community/members/${e}`);return s.info(`
|
|
220
220
|
Member Profile
|
|
221
|
-
`),c.SUCCESS}catch(n){return s.error(`Failed to fetch member profile: ${n.message}`),c.ERROR}}async function
|
|
221
|
+
`),c.SUCCESS}catch(n){return s.error(`Failed to fetch member profile: ${n.message}`),c.ERROR}}async function Hs(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 L(`/community/members/${e}/role`,{method:"POST",body:JSON.stringify({role:t})});return s.success(`Updated ${o.name} \u2192 ${fe.bold(t)}`),c.SUCCESS}catch(r){return s.error(`Failed to update member role: ${r.message}`),c.ERROR}}async function zs(e,t){try{s.info(`Sending invite to ${e}...`);let n=await L("/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 Js(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 L(`/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 Xs(e){try{s.info("Computing community health score...");let t=e.period||"30d",n=await L(`/community/health?period=${t}`),{score:r,breakdown:o}=Us(n.metrics);s.info(`
|
|
222
222
|
Community Health Report (${t})
|
|
223
|
-
`);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
|
|
223
|
+
`);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 Qs(e){try{s.info("Fetching moderation queue...");let t=await L("/community/moderation/queue");if(!t.items||t.items.length===0)return s.success("No pending moderation items"),c.SUCCESS;s.info(`
|
|
224
224
|
Moderation Queue (${t.items.length} pending)
|
|
225
|
-
`);for(let n of t.items){let o={low:
|
|
225
|
+
`);for(let n of t.items){let o={low:fe.gray("\u25CF"),medium:fe.yellow("\u25CF"),high:fe.red("\u25CF")}[String(n.severity)]??"\u25CF"}return c.SUCCESS}catch(t){return s.error(`Failed to fetch queue: ${t.message}`),c.ERROR}}async function Zs(e){try{s.info("Running AI-assisted moderation sweep..."),e.dryRun&&s.warn("DRY RUN MODE - no changes will be made");let t=await L("/community/moderation/auto",{method:"POST",body:JSON.stringify({dryRun:e.dryRun||!1})});return s.info(`
|
|
226
226
|
Moderation Results
|
|
227
|
-
`),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
|
|
227
|
+
`),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 jn(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=>Ls(i).then(process.exit)),n.command("approve <id>").description("Approve a showcase submission").option("--yes","Skip confirmation").action((i,l)=>Ds(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)=>Fs(i,l).then(process.exit)),n.command("feature <id>").description("Mark as featured").option("--yes","Skip confirmation").action((i,l)=>Gs(i,l).then(process.exit)),n.command("stats").description("Showcase engagement metrics").action(i=>Ms(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=>Vs(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)=>Ks(i,l).then(process.exit)),r.command("publish <id>").description("Publish approved content").option("--yes","Skip confirmation").action((i,l)=>Bs(i,l).then(process.exit)),r.command("analytics").description("Content performance metrics").option("--format <format>","Output format (json|text)").action(i=>qs(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=>Ws(i).then(process.exit)),o.command("profile <userId>").description("View member profile + activity").action((i,l)=>Ys(i,l).then(process.exit)),o.command("role <userId> <role>").description("Update member role").option("--yes","Skip confirmation").action((i,l,u)=>Hs(i,l,u).then(process.exit)),o.command("invite <email>").description("Invite to community").action((i,l)=>zs(i,l).then(process.exit)),o.command("ban <userId>").description("Ban member").option("--reason <text>","Ban reason").option("--yes","Skip confirmation").action((i,l)=>Js(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=>Xs(i).then(process.exit));let a=t.command("moderate").description("AI-assisted moderation queue");a.command("queue").description("Show pending moderation items").action(i=>Qs(i).then(process.exit)),a.command("auto").description("Run AI moderation sweep").option("--dry-run","Preview without committing").action(i=>Zs(i).then(process.exit))}import{appendFileSync as ea,existsSync as ta,readFileSync as na,writeFileSync as ra}from"fs";import{homedir as Ve}from"os";import{join as je}from"path";import*as H from"@clack/prompts";import Y from"picocolors";var lt=["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"],Un={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"},oa=["--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"],Lt=["-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"],sa={"-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},aa=Object.entries(sa).map(([e,t])=>` ${e})
|
|
228
228
|
COMPREPLY=( $(compgen -W "${t.join(" ")}" -- \${cur}) )
|
|
229
229
|
return 0
|
|
230
230
|
;;`).join(`
|
|
231
|
-
`),
|
|
232
|
-
`),
|
|
233
|
-
`),
|
|
234
|
-
`);function
|
|
231
|
+
`),ia=Lt.map(e=>` "${e}"`).join(`
|
|
232
|
+
`),ca=lt.map(e=>`complete -c nebutra -n "__fish_use_subcommand_only" -f -a "${e}" -d "${Un[e]}"`).join(`
|
|
233
|
+
`),la=Lt.map(e=>`complete -c nebutra -n "__fish_seen_subcommand_from create" -f -a "${e}" -d "create-sailor option"`).join(`
|
|
234
|
+
`);function Ln(){return`#!/usr/bin/env bash
|
|
235
235
|
|
|
236
236
|
_nebutra_completions() {
|
|
237
237
|
local cur prev opts cmd
|
|
@@ -240,13 +240,13 @@ _nebutra_completions() {
|
|
|
240
240
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
241
241
|
|
|
242
242
|
# Subcommands
|
|
243
|
-
local subcommands="${
|
|
243
|
+
local subcommands="${lt.join(" ")}"
|
|
244
244
|
|
|
245
245
|
# Flags
|
|
246
|
-
local flags="${
|
|
246
|
+
local flags="${oa.join(" ")}"
|
|
247
247
|
|
|
248
248
|
case "\${prev}" in
|
|
249
|
-
${
|
|
249
|
+
${aa}
|
|
250
250
|
esac
|
|
251
251
|
|
|
252
252
|
if [[ \${cur} == -* ]]; then
|
|
@@ -260,7 +260,7 @@ ${na}
|
|
|
260
260
|
COMPREPLY=( $(compgen -W "--21st --v0 --provider --dry-run --yes --if-not-exists" -- \${cur}) )
|
|
261
261
|
elif [[ "\${prev}" == "create" ]]; then
|
|
262
262
|
# create delegates to create-sailor
|
|
263
|
-
COMPREPLY=( $(compgen -W "${
|
|
263
|
+
COMPREPLY=( $(compgen -W "${Lt.join(" ")}" -- \${cur}) )
|
|
264
264
|
elif [[ "\${prev}" == "completions" ]]; then
|
|
265
265
|
# completions command takes shell type
|
|
266
266
|
COMPREPLY=( $(compgen -W "bash zsh fish install" -- \${cur}) )
|
|
@@ -270,11 +270,11 @@ ${na}
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
complete -o bashdefault -o default -o nospace -F _nebutra_completions nebutra
|
|
273
|
-
`}function
|
|
273
|
+
`}function Dn(){return`#compdef nebutra
|
|
274
274
|
|
|
275
275
|
_nebutra() {
|
|
276
276
|
local -a commands=(
|
|
277
|
-
${
|
|
277
|
+
${lt.map(e=>`"${e}:${Un[e]}"`).join(`
|
|
278
278
|
`)}
|
|
279
279
|
)
|
|
280
280
|
|
|
@@ -291,7 +291,7 @@ _nebutra() {
|
|
|
291
291
|
)
|
|
292
292
|
|
|
293
293
|
local -a create_flags=(
|
|
294
|
-
${
|
|
294
|
+
${ia}
|
|
295
295
|
)
|
|
296
296
|
|
|
297
297
|
local -a mcp_flags=(
|
|
@@ -332,12 +332,12 @@ ${ra}
|
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
_nebutra
|
|
335
|
-
`}function
|
|
335
|
+
`}function Fn(){return`# Fish shell completions for nebutra
|
|
336
336
|
|
|
337
|
-
complete -c nebutra -f -n "__fish_seen_subcommand_from; and not __fish_seen_subcommand_from ${
|
|
337
|
+
complete -c nebutra -f -n "__fish_seen_subcommand_from; and not __fish_seen_subcommand_from ${lt.join(" ")}" -d "Nebutra governance CLI"
|
|
338
338
|
|
|
339
339
|
# Subcommands
|
|
340
|
-
${
|
|
340
|
+
${ca}
|
|
341
341
|
|
|
342
342
|
# Flags
|
|
343
343
|
complete -c nebutra -f -a "--help" -d "Show help message"
|
|
@@ -349,7 +349,7 @@ complete -c nebutra -n "__fish_seen_subcommand_from add" -f -a "--v0" -d "Fetch
|
|
|
349
349
|
complete -c nebutra -n "__fish_seen_subcommand_from add" -f -a "--provider" -d "Specify a backend provider"
|
|
350
350
|
|
|
351
351
|
# create command flags
|
|
352
|
-
${
|
|
352
|
+
${la}
|
|
353
353
|
|
|
354
354
|
# mcp command flags
|
|
355
355
|
complete -c nebutra -n "__fish_seen_subcommand_from mcp" -f -a "--stdio" -d "Use stdio transport (default)"
|
|
@@ -364,16 +364,16 @@ complete -c nebutra -n "__fish_seen_subcommand_from completions" -f -a "install"
|
|
|
364
364
|
complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--all" -d "Show full schema for all commands"
|
|
365
365
|
complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--list" -d "List all available command names"
|
|
366
366
|
complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--exit-codes" -d "Show exit codes reference"
|
|
367
|
-
`}function
|
|
367
|
+
`}function Gn(e){let n={bash:Ln,zsh:Dn,fish:Fn}[e.toLowerCase()];n||(console.error(Y.red(`Error: Unknown shell '${e}'. Supported: bash, zsh, fish`)),process.exit(c.INVALID_ARGS)),console.log(n())}async function da(){let t=(process.env.SHELL||"/bin/bash").split("/").pop()||"bash";H.intro(Y.bgCyan(Y.black(" nebutra completions install ")));let n=H.spinner();n.start(Y.cyan(`Detecting shell: ${t}...`));try{let r=null,o=null,a=null;if(t==="bash"?(r=je(Ve(),".bashrc"),o=je(Ve(),".bash_completion.d"),a="source ~/.bash_completion.d/nebutra"):t==="zsh"?(r=je(Ve(),".zshrc"),o=je(Ve(),".zsh/completions"),a="fpath=(~/.zsh/completions $fpath)"):t==="fish"?(o=je(Ve(),".config/fish/completions"),a="# Completions installed to ~/.config/fish/completions/nebutra.fish"):(n.stop(),H.log.warn(Y.yellow(`Warning: Unsupported shell '${t}'. Please install completions manually.`)),Gn("bash"),H.outro(Y.cyan("Run 'nebutra completions bash' (or zsh/fish) to generate completions for your shell.")),process.exit(c.SUCCESS)),o){(await import("fs")).mkdirSync(o,{recursive:!0});let l=je(o,t==="fish"?"nebutra.fish":"nebutra"),u=t==="bash"?Ln():t==="zsh"?Dn():Fn();ra(l,u),n.stop(Y.green(`Completion script installed to ${l}`))}r&&a&&t!=="fish"&&(!ta(r)||!na(r,"utf8").includes("nebutra"))&&(ea(r,(t==="bash"?`
|
|
368
368
|
# nebutra completions
|
|
369
369
|
[[ -f ~/.bash_completion.d/nebutra ]] && source ~/.bash_completion.d/nebutra`:`
|
|
370
370
|
# nebutra completions
|
|
371
371
|
fpath=(~/.zsh/completions $fpath)
|
|
372
372
|
autoload -Uz compinit && compinit`)+`
|
|
373
|
-
`),
|
|
373
|
+
`),H.log.info(Y.green(`Added nebutra completions to ${r}`))),H.log.info(Y.cyan("Completions installed! Restart your shell or run:")),H.log.info(Y.dim(` source "${t==="bash"?"~/.bashrc":"~/.zshrc"}"`)),H.outro(Y.green("Installation complete!"))}catch(r){n.stop(),H.log.error(Y.red("Error installing completions. You can manually add them by running:")),H.log.info(Y.cyan(` nebutra completions ${t} >> ~/.${t}rc`)),r instanceof Error&&console.error(Y.dim(r.message)),process.exit(c.ERROR)}}function Mn(e){e.command("completions [shell]").description("Generate or install shell completions").action(async t=>{!t||t==="install"?await da():Gn(t)})}import{spawn as ua}from"child_process";import{dirname as ma,resolve as pa}from"path";import{fileURLToPath as fa}from"url";import*as Se from"@clack/prompts";import Ke from"picocolors";var ga=ma(fa(import.meta.url));function ya(){let e=[()=>pa(ga,"../../../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 Vn(e){e.command("create [dir]").description("Scaffold a new Nebutra-Sailor project").allowUnknownOption(!0).action(async(t,n,r)=>{Se.intro(Ke.bgCyan(Ke.black(" nebutra create ")));try{let o=ya(),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=Se.spinner();i.start(Ke.cyan(`Launching create-sailor${t?` in ${t}`:""}...`));let l=ua(process.execPath,[o,...a],{stdio:"inherit",env:{...process.env}});l.on("close",u=>{i.stop(),u===0&&Se.outro(Ke.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(`
|
|
374
374
|
Failed to launch create-sailor: ${u.message}`),u.code==="ENOENT"&&(s.warn(`
|
|
375
|
-
Tip: Ensure create-sailor is installed or available in your PATH.`),s.info(" Install globally: npm install -g create-sailor"),process.exit(c.NOT_FOUND)),process.exit(c.ERROR)})}catch(o){
|
|
376
|
-
`)}catch{process.stdout.write(t.stdout)}}else d("Failed to check migration status","error"),process.exit(t.exitCode||1)}function
|
|
375
|
+
Tip: Ensure create-sailor is installed or available in your PATH.`),s.info(" Install globally: npm install -g create-sailor"),process.exit(c.NOT_FOUND)),process.exit(c.ERROR)})}catch(o){Se.log.error(Ke.red("Error: Unable to start create-sailor. Ensure it is properly installed.")),o instanceof Error&&s.error(o.message),process.exit(c.ERROR)}})}import*as dt from"@clack/prompts";async function ha(e){d("Generating Prisma client"+"...","info");let n=await pe("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 Dt(e,t){let n=t||{};if(e){d(`Creating migration: ${e}`,"info");let r=await pe("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 pe("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 ba(e){d("Pushing schema to database...","info");let t=await pe("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 va(e){d("Seeding database...","info");let t=await pe("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 wa(e){d("Launching Prisma Studio...","info");let t=await pe("studio",{dryRun:e.dryRun,interactive:!0});t.exitCode!==0&&(d("Failed to launch Prisma Studio","error"),process.exit(t.exitCode||1))}async function Sa(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 database? This will delete all data.",initialValue:!1});(dt.isCancel(r)||!r)&&(d("Reset cancelled","warn"),process.exit(c.CANCELLED))}else d("Database reset requires explicit --yes confirmation (use: nebutra db reset --yes)","error"),process.exit(c.INVALID_ARGS);d("Resetting database...","warn");let n=await pe("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 Ra(e){d("Checking migration status...","info");let t=await pe("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)+`
|
|
376
|
+
`)}catch{process.stdout.write(t.stdout)}}else d("Failed to check migration status","error"),process.exit(t.exitCode||1)}function Kn(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 ha(i);break;case"migrate":r.length>0?r[0]==="create"&&r[1]?await Dt(r[1],i):await Dt(r[0],i):await Dt(void 0,i);break;case"push":await ba(i);break;case"seed":await va(i);break;case"studio":await wa(i);break;case"reset":await Sa(i);break;case"status":await Ra(i);break;default:d(`Unknown db subcommand: ${n}. Valid commands: generate, migrate, migrate create, push, seed, studio, reset, status`,"error"),process.exit(c.INVALID_ARGS)}}catch(l){let u=l instanceof Error?l.message:String(l);d(`Database command failed: ${u}`,"error"),A("Full error",{error:l}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
377
377
|
Examples:
|
|
378
378
|
nebutra db generate Generate Prisma client
|
|
379
379
|
nebutra db migrate Run all pending migrations
|
|
@@ -388,54 +388,54 @@ Flags:
|
|
|
388
388
|
--dry-run Show what would be run without executing
|
|
389
389
|
--yes Skip confirmations
|
|
390
390
|
--format <type> Output format: json, plain (default: plain)
|
|
391
|
-
`)}import*as
|
|
392
|
-
${n}`)}return t}function
|
|
393
|
-
${t}`)}return e}async function
|
|
391
|
+
`)}import*as _ from"@clack/prompts";import S from"picocolors";var Ue=["landing-page","web","storybook","design-docs","sailor-docs","studio","docs","idp","mail-preview","gateway"],Ft=["ai-saas","marketing","dashboard","growth","enterprise"];function ut(e){let t=`@nebutra/${e}`;if(!Ue.includes(e)){let n=S.yellow(`Valid apps: ${Ue.join(", ")}`);throw new Error(`Invalid app: ${S.red(e)}
|
|
392
|
+
${n}`)}return t}function Ea(e){if(!Ft.includes(e)){let t=S.yellow(`Valid presets: ${Ft.join(", ")}`);throw new Error(`Invalid preset: ${S.red(e)}
|
|
393
|
+
${t}`)}return e}async function Ca(e){e.dryRun&&(x({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)"},{format:e.format}),process.exit(c.DRY_RUN_OK));try{if(e.app&&e.preset&&(_.log.error(S.red("Cannot specify both --app and --preset")),process.exit(c.INVALID_ARGS)),e.app){ut(e.app);let n=`@nebutra/${e.app}`;e.quiet||_.log.info(S.cyan(`Starting dev server for ${S.bold(n)}...`));let r=await ye("dev",{filter:n});process.exit(r.exitCode)}if(e.preset){Ea(e.preset),e.quiet||_.log.info(S.cyan(`Starting dev with preset ${S.bold(e.preset)}...`));let n=await N(`dev:${e.preset}`,{interactive:!0});process.exit(n.exitCode)}e.quiet||_.log.info(S.cyan("Starting dev servers for all apps..."));let t=await ye("dev");process.exit(t.exitCode)}catch(t){t instanceof Error?_.log.error(S.red(t.message)):_.log.error(S.red("Unknown error occurred")),process.exit(c.ERROR)}}async function ka(e){e.dryRun&&(x({mode:"dry-run",timestamp:new Date().toISOString(),command:"build",options:{app:e.app,strict:e.strict},task:e.strict?"pnpm build:strict":"pnpm build"},{format:e.format}),process.exit(c.DRY_RUN_OK));try{if(e.app&&e.strict&&(_.log.error(S.red("Cannot use --strict with --app filter")),process.exit(c.INVALID_ARGS)),e.app){ut(e.app);let r=`@nebutra/${e.app}`;e.quiet||_.log.info(S.cyan(`Building ${S.bold(r)}...`));let o=await ye("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...";_.log.info(S.cyan(r))}let n=await N(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?_.log.error(S.red(t.message)):_.log.error(S.red("Unknown error occurred")),process.exit(c.ERROR)}}async function Oa(e){e.dryRun&&(x({mode:"dry-run",timestamp:new Date().toISOString(),command:"lint",options:{app:e.app,fix:e.fix},task:e.fix?"pnpm lint:fix":"pnpm lint"},{format:e.format}),process.exit(c.DRY_RUN_OK));try{if(e.app&&e.fix&&(_.log.error(S.red("Cannot use --fix with --app filter (lint:fix is global only)")),process.exit(c.INVALID_ARGS)),e.app){ut(e.app);let r=`@nebutra/${e.app}`;e.quiet||_.log.info(S.cyan(`Linting ${S.bold(r)}...`));let o=await $({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...";_.log.info(S.cyan(r))}let n=await N(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?_.log.error(S.red(t.message)):_.log.error(S.red("Unknown error occurred")),process.exit(c.ERROR)}}async function xa(e){e.dryRun&&(x({mode:"dry-run",timestamp:new Date().toISOString(),command:"typecheck",options:{app:e.app},task:"turbo typecheck"},{format:e.format}),process.exit(c.DRY_RUN_OK));try{if(e.app){ut(e.app);let n=`@nebutra/${e.app}`;e.quiet||_.log.info(S.cyan(`Type-checking ${S.bold(n)}...`));let r=await ye("typecheck",{filter:n});process.exit(r.exitCode)}e.quiet||_.log.info(S.cyan("Type-checking all packages..."));let t=await ye("typecheck");process.exit(t.exitCode)}catch(t){t instanceof Error?_.log.error(S.red(t.message)):_.log.error(S.red("Unknown error occurred")),process.exit(c.ERROR)}}function Bn(e){e.command("dev").description("Start development servers (turbo dev)").option("--app <name>",`App to run: ${Ue.join(", ")}`).option("--preset <name>",`Preset to use: ${Ft.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async t=>{let n=t.optsWithGlobals?t.optsWithGlobals():t;await Ca({...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: ${Ue.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 ka({...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: ${Ue.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 Oa({...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: ${Ue.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async t=>{let n=t.optsWithGlobals?t.optsWithGlobals():t;await xa({...t,quiet:n.quiet||!1,verbose:n.verbose||!1,format:n.format})})}import qn from"fs";import Wn from"path";import Gt from"picocolors";var Mt=["smoke","golden","sleptons"],Aa={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 _a(e,t={}){Mt.includes(e)||(s.error(`Invalid suite: ${Gt.red(e)}. Valid: ${Mt.join(", ")}`),process.exit(c.INVALID_ARGS));let n=b(),{config:r,suiteDir:o}=Aa[e],a=Wn.join(n,r),i=Wn.join(n,o);(!qn.existsSync(i)||!qn.existsSync(a))&&(s.error(`Suite ${Gt.red(e)} not found at ${o}.
|
|
394
394
|
Expected config: ${r}
|
|
395
395
|
Remediation: scaffold the suite (analogous to 'nebutra workflow init <provider>')
|
|
396
|
-
or restore the directory from git.`),process.exit(c.NOT_FOUND)),t.dryRun&&(x({mode:"dry-run",command:`e2e ${e}`,task:`pnpm exec playwright test --config ${r}`}),process.exit(c.DRY_RUN_OK)),t.quiet||s.info(`Running Playwright suite: ${
|
|
397
|
-
`));try{let n=await(await P("/status")).json();return e.format,c.SUCCESS}catch(t){return s.error(`Failed to fetch ecosystem status: ${t}`),c.ERROR}}async function
|
|
398
|
-
`));let t=e.token||
|
|
396
|
+
or restore the directory from git.`),process.exit(c.NOT_FOUND)),t.dryRun&&(x({mode:"dry-run",command:`e2e ${e}`,task:`pnpm exec playwright test --config ${r}`}),process.exit(c.DRY_RUN_OK)),t.quiet||s.info(`Running Playwright suite: ${Gt.bold(e)}`);let l=await $({command:"pnpm",args:["exec","playwright","test","--config",r],interactive:!0,label:`playwright ${e}`});process.exit(l.exitCode)}function Yn(e){e.command("e2e <suite>").description(`Run a Playwright suite (${Mt.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 _a(t,{dryRun:n.dryRun||!1,quiet:r.quiet||!1})})}import y from"picocolors";var $a=process.env.NEBUTRA_ECOSYSTEM_URL||"https://api.nebutra.com/ecosystem",Hn=process.env.NEBUTRA_ECOSYSTEM_TOKEN||"";async function P(e,t={}){let{method:n="GET",body:r,dryRun:o=!1,token:a=Hn}=t,i=`${$a}${e}`;if(o)return d(`[DRY RUN] ${n} ${i}`),r&&d(`Body: ${JSON.stringify(r)}`),x({mode:"dry-run",method:n,url:i,...r&&{body:r}}),{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 Pa(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 Ia(e){s.info(y.cyan(`\u{1F4CA} Ecosystem Status Dashboard
|
|
397
|
+
`));try{let n=await(await P("/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(y.cyan(`\u{1F517} Connect to Nebutra Ecosystem
|
|
398
|
+
`));let t=e.token||Hn;!t&&!e.yes&&(s.warn(`No NEBUTRA_ECOSYSTEM_TOKEN found. Starting OAuth flow...
|
|
399
399
|
`),s.info("Visit: "+y.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 P("/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?(d("[DRY RUN] Would connect project to ecosystem","success"),c.SUCCESS):(s.success(`\u2713 Connected to Nebutra Ecosystem
|
|
400
400
|
`),s.info(`Project ID: ${y.cyan(o.projectId)}`),s.info(`Organization: ${y.cyan(n)}`),s.info(`Ecosystem Config: ${y.cyan(".nebutra/ecosystem.json")}
|
|
401
|
-
`),s.info(`Next steps: Run ${y.yellow("nebutra ecosystem publish")} to share your project`),c.SUCCESS)}catch(r){return s.error(`Connection failed: ${r}`),c.ERROR}}async function
|
|
402
|
-
`));let t=e.tag||"latest",n=e.visibility||"public",r=
|
|
401
|
+
`),s.info(`Next steps: Run ${y.yellow("nebutra ecosystem publish")} to share your project`),c.SUCCESS)}catch(r){return s.error(`Connection failed: ${r}`),c.ERROR}}async function Ta(e){s.info(y.cyan(`\u{1F4E4} Publish to Ecosystem Marketplace
|
|
402
|
+
`));let t=e.tag||"latest",n=e.visibility||"public",r=Pa();s.info(`${y.bold("Pre-Publish Checklist")}
|
|
403
403
|
`);for(let o of r.checks){let a=o.pass?y.green("\u2713"):y.red("\u2717")}if(!r.valid)return s.error(`
|
|
404
404
|
Fix the checks above before publishing`),c.ERROR;if(n==="public"&&!e.yes&&(s.info(`This project will be ${y.yellow("publicly listed")} in the ecosystem`),s.info(`Continue? Run with ${y.yellow("--yes")} to skip confirmation`),!e.yes))return c.CANCELLED;try{let a=await(await P("/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?(d("[DRY RUN] Would publish to ecosystem","success"),c.SUCCESS):(s.success(`\u2713 Published successfully!
|
|
405
405
|
`),s.info(`Marketplace URL: ${y.blue(`https://nebutra.com/templates/${a.projectId}`)}`),s.info(`Visibility: ${n}`),s.info(`Tag: ${t}
|
|
406
|
-
`),c.SUCCESS)}catch(o){return s.error(`Publish failed: ${o}`),c.ERROR}}async function
|
|
407
|
-
`));try{let n=await(await P(`/templates?category=${e.category||""}&sort=${e.sort||"downloads"}`)).json();return e.format==="json"||s.info(`Run ${y.yellow("nebutra ecosystem templates info <id>")} for details`),c.SUCCESS}catch(t){return s.error(`Failed to list templates: ${t}`),c.ERROR}}async function
|
|
408
|
-
`));try{let r=await(await P(`/templates/search?q=${encodeURIComponent(e)}`)).json();return t.format,c.SUCCESS}catch(n){return s.error(`Search failed: ${n}`),c.ERROR}}async function
|
|
409
|
-
`));try{let n=await(await P(`/templates/${e}`)).json();return s.info(`Install: ${y.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
|
|
406
|
+
`),c.SUCCESS)}catch(o){return s.error(`Publish failed: ${o}`),c.ERROR}}async function ja(e){s.info(y.cyan(`\u{1F4DA} Template Marketplace
|
|
407
|
+
`));try{let n=await(await P(`/templates?category=${e.category||""}&sort=${e.sort||"downloads"}`)).json();return e.format==="json"||s.info(`Run ${y.yellow("nebutra ecosystem templates info <id>")} for details`),c.SUCCESS}catch(t){return s.error(`Failed to list templates: ${t}`),c.ERROR}}async function Ua(e,t){s.info(y.cyan(`\u{1F50D} Searching templates for "${e}"
|
|
408
|
+
`));try{let r=await(await P(`/templates/search?q=${encodeURIComponent(e)}`)).json();return t.format,c.SUCCESS}catch(n){return s.error(`Search failed: ${n}`),c.ERROR}}async function La(e){s.info(y.cyan(`\u{1F4D6} Template Details
|
|
409
|
+
`));try{let n=await(await P(`/templates/${e}`)).json();return s.info(`Install: ${y.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 Da(e,t){s.info(y.cyan(`\u2B07\uFE0F Installing template "${e}"
|
|
410
410
|
`));try{let n=await P(`/templates/${e}/install`,{method:"POST",body:{projectName:"my-saas"},dryRun:t.dryRun});return t.dryRun?(d("[DRY RUN] Would install template","success"),c.SUCCESS):(s.success(`\u2713 Template installed!
|
|
411
|
-
`),s.info("Project: ./my-saas"),s.info(`Next: ${y.yellow("cd my-saas && pnpm install && pnpm dev")}`),c.SUCCESS)}catch(n){return s.error(`Installation failed: ${n}`),c.ERROR}}async function
|
|
412
|
-
`));try{let n=await(await P(`/ideas?status=${e.status||""}&sort=${e.sort||"recent"}`)).json();return e.format==="json"||(s.info(`Vote: ${y.yellow("nebutra ecosystem ideas vote <id>")}`),s.info(`Claim: ${y.yellow("nebutra ecosystem ideas claim <id>")}`)),c.SUCCESS}catch(t){return s.error(`Failed to list ideas: ${t}`),c.ERROR}}async function
|
|
411
|
+
`),s.info("Project: ./my-saas"),s.info(`Next: ${y.yellow("cd my-saas && pnpm install && pnpm dev")}`),c.SUCCESS)}catch(n){return s.error(`Installation failed: ${n}`),c.ERROR}}async function Fa(e){s.info(y.cyan(`\u{1F4A1} Ideas Marketplace
|
|
412
|
+
`));try{let n=await(await P(`/ideas?status=${e.status||""}&sort=${e.sort||"recent"}`)).json();return e.format==="json"||(s.info(`Vote: ${y.yellow("nebutra ecosystem ideas vote <id>")}`),s.info(`Claim: ${y.yellow("nebutra ecosystem ideas claim <id>")}`)),c.SUCCESS}catch(t){return s.error(`Failed to list ideas: ${t}`),c.ERROR}}async function Ga(e){if(s.info(y.cyan(`\u2728 Submit New Idea
|
|
413
413
|
`)),!e.title||!e.description)return s.error("Missing required fields: --title and --description"),c.INVALID_ARGS;try{let n=await(await P("/ideas",{method:"POST",body:{title:e.title,description:e.description,tags:e.tags?.split(",")||[],authorId:"user_current"},dryRun:e.dryRun})).json();return e.dryRun?(d("[DRY RUN] Would submit idea","success"),c.SUCCESS):(s.success(`\u2713 Idea submitted!
|
|
414
414
|
`),s.info(`Idea URL: ${y.blue(`https://nebutra.com/ideas/${n.ideaId}`)}`),s.info(`Start voting and discussing! Community will help shape it.
|
|
415
|
-
`),c.SUCCESS)}catch(t){return s.error(`Submission failed: ${t}`),c.ERROR}}async function
|
|
415
|
+
`),c.SUCCESS)}catch(t){return s.error(`Submission failed: ${t}`),c.ERROR}}async function Ma(e,t){s.info(y.cyan(`\u{1F44D} Voting on idea "${e}"
|
|
416
416
|
`));try{let n=await P(`/ideas/${e}/vote`,{method:"POST",body:{action:"upvote"},dryRun:t.dryRun});return t.dryRun?(d("[DRY RUN] Would upvote idea","success"),c.SUCCESS):(s.success(`\u2713 Vote recorded! This idea now has more momentum.
|
|
417
|
-
`),c.SUCCESS)}catch(n){return s.error(`Vote failed: ${n}`),c.ERROR}}async function
|
|
417
|
+
`),c.SUCCESS)}catch(n){return s.error(`Vote failed: ${n}`),c.ERROR}}async function Va(e,t){s.info(y.cyan(`\u{1F3AF} Claiming idea "${e}"
|
|
418
418
|
`));try{let n=await P(`/ideas/${e}/claim`,{method:"POST",body:{claimerId:"user_current",status:"in-progress"},dryRun:t.dryRun});return t.dryRun?(d("[DRY RUN] Would claim idea","success"),c.SUCCESS):(s.success(`\u2713 Idea claimed! You're now working on this.
|
|
419
|
-
`),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
|
|
419
|
+
`),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 Ka(e,t){if(s.info(y.cyan(`\u{1F4AC} Adding comment to idea "${e}"
|
|
420
420
|
`)),!t.message)return s.error("Missing --message parameter"),c.INVALID_ARGS;try{let n=await P(`/ideas/${e}/comments`,{method:"POST",body:{authorId:"user_current",message:t.message},dryRun:t.dryRun});return t.dryRun?(d("[DRY RUN] Would post comment","success"),c.SUCCESS):(s.success(`\u2713 Comment posted!
|
|
421
|
-
`),c.SUCCESS)}catch(n){return s.error(`Comment failed: ${n}`),c.ERROR}}async function
|
|
422
|
-
`));try{let n=await(await P(`/opc?skill=${e.skill||""}&industry=${e.industry||""}&sort=${e.sort||"active"}`)).json();return e.format==="json"||(s.info(`View profile: ${y.yellow("nebutra ecosystem opc profile <handle>")}`),s.info(`Register: ${y.yellow("nebutra ecosystem opc register")}`)),c.SUCCESS}catch(t){return s.error(`Failed to list members: ${t}`),c.ERROR}}async function
|
|
423
|
-
`));try{let t=await(await P("/opc/me")).json();return s.info(`Edit profile: ${y.yellow("nebutra ecosystem opc register")}`),c.SUCCESS}catch(e){return s.error(`Failed to load profile: ${e}`),c.ERROR}}async function
|
|
421
|
+
`),c.SUCCESS)}catch(n){return s.error(`Comment failed: ${n}`),c.ERROR}}async function Ba(e){s.info(y.cyan(`\u{1F465} Legacy Member Directory
|
|
422
|
+
`));try{let n=await(await P(`/opc?skill=${e.skill||""}&industry=${e.industry||""}&sort=${e.sort||"active"}`)).json();return e.format==="json"||(s.info(`View profile: ${y.yellow("nebutra ecosystem opc profile <handle>")}`),s.info(`Register: ${y.yellow("nebutra ecosystem opc register")}`)),c.SUCCESS}catch(t){return s.error(`Failed to list members: ${t}`),c.ERROR}}async function qa(){s.info(y.cyan(`\u{1F464} Legacy Member Profile
|
|
423
|
+
`));try{let t=await(await P("/opc/me")).json();return s.info(`Edit profile: ${y.yellow("nebutra ecosystem opc register")}`),c.SUCCESS}catch(e){return s.error(`Failed to load profile: ${e}`),c.ERROR}}async function Wa(e){s.info(y.cyan(`\u{1F4DD} Register Legacy Member Profile
|
|
424
424
|
`));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:
|
|
425
425
|
`),c.SUCCESS;if(!e.yes)return s.info("This will create your public legacy member profile."),s.info(`Run with ${y.yellow("--yes")} to confirm`),c.CANCELLED;try{let n=await P("/opc/register",{method:"POST",body:t});return s.success(`\u2713 Legacy member profile created!
|
|
426
|
-
`),s.info(`View at: ${y.blue("https://nebutra.com/members/your-handle")}`),s.info(`Compatibility command: ${y.yellow("nebutra ecosystem opc profile --share")}`),c.SUCCESS}catch(n){return s.error(`Registration failed: ${n}`),c.ERROR}}async function
|
|
427
|
-
`));try{let n=await(await P(`/showcase?category=${e.category||""}&featured=${e.featured?"true":""}`)).json();return e.format==="json"||(s.info(`Vote: ${y.yellow("nebutra ecosystem showcase vote <id>")}`),s.info(`Submit yours: ${y.yellow("nebutra ecosystem showcase submit")}`)),c.SUCCESS}catch(t){return s.error(`Failed to list showcase: ${t}`),c.ERROR}}async function
|
|
426
|
+
`),s.info(`View at: ${y.blue("https://nebutra.com/members/your-handle")}`),s.info(`Compatibility command: ${y.yellow("nebutra ecosystem opc profile --share")}`),c.SUCCESS}catch(n){return s.error(`Registration failed: ${n}`),c.ERROR}}async function Ya(e){s.info(y.cyan(`\u2B50 Project Showcase
|
|
427
|
+
`));try{let n=await(await P(`/showcase?category=${e.category||""}&featured=${e.featured?"true":""}`)).json();return e.format==="json"||(s.info(`Vote: ${y.yellow("nebutra ecosystem showcase vote <id>")}`),s.info(`Submit yours: ${y.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(y.cyan(`\u{1F389} Submit Project to Showcase
|
|
428
428
|
`));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 ${y.yellow("--yes")} to confirm`),c.CANCELLED;try{let n=await P("/showcase/submit",{method:"POST",body:t,dryRun:e.dryRun});return e.dryRun?(d("[DRY RUN] Would submit to showcase","success"),c.SUCCESS):(s.success(`\u2713 Project added to showcase!
|
|
429
|
-
`),s.info(`View at: ${y.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
|
|
429
|
+
`),s.info(`View at: ${y.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 za(e,t){s.info(y.cyan(`\u{1F44D} Voting on showcase project "${e}"
|
|
430
430
|
`));try{let n=await P(`/showcase/${e}/vote`,{method:"POST",body:{action:"upvote"},dryRun:t.dryRun});return t.dryRun?(d("[DRY RUN] Would upvote project","success"),c.SUCCESS):(s.success(`\u2713 Vote recorded! You're supporting this project.
|
|
431
|
-
`),c.SUCCESS)}catch(n){return s.error(`Vote failed: ${n}`),c.ERROR}}async function
|
|
431
|
+
`),c.SUCCESS)}catch(n){return s.error(`Vote failed: ${n}`),c.ERROR}}async function Ja(e){s.info(y.cyan(`\u{1F504} Sync with Ecosystem
|
|
432
432
|
`));let t=!e.push,n=e.push?"push":"pull";try{let r=await P("/sync",{method:"POST",body:{direction:n,timestamp:new Date().toISOString()},dryRun:e.dryRun});return e.dryRun?(s.info(y.green(`\u2713 [DRY RUN] Would sync (${n})
|
|
433
433
|
`)),c.SUCCESS):(s.success(`\u2713 Synced!
|
|
434
|
-
`),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
|
|
435
|
-
`)){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
|
|
436
|
-
`)){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
|
|
437
|
-
`);e.dryRun?(d("Dry-run: would write .env.local","info"),O(l,{format:"plain"})):(
|
|
438
|
-
`),d(`Environment template written to ${r}`,"success"))}async function
|
|
434
|
+
`),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 zn(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 Ia(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 Ta(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 ja(i)}),n.command("search <query>").description("Search templates").option("--format <format>","Output format","text").action(async(i,l)=>{await Ua(i,l)}),n.command("info <id>").description("Template details").action(async i=>{await La(i)}),n.command("install <id>").description("Install/fork a template").option("--dry-run","Preview without making changes").action(async(i,l)=>{await Da(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 Fa(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 Ga(i)}),r.command("vote <id>").description("Upvote an idea").option("--dry-run","Preview without making changes").action(async(i,l)=>{await Ma(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 Va(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 Ka(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 Ba(i)}),o.command("profile").description("View/edit your legacy member profile").action(async()=>{await qa()}),o.command("register").description("Register a legacy member profile").option("--yes","Skip confirmations").option("--dry-run","Preview without making changes").action(async i=>{await Wa(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 za(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 Ja(i)})}import{existsSync as mt,readFileSync as Jn,writeFileSync as Xa}from"fs";import{resolve as pt}from"path";import*as xe from"@clack/prompts";import Qa from"picocolors";function ft(e){let t=pt(e,".env.example");if(!mt(t))throw new Error(`.env.example not found at ${t}`);let n=Jn(t,"utf-8"),r={};for(let o of n.split(`
|
|
435
|
+
`)){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 Vt(e){let t=pt(e,".env.local"),n=pt(e,".env"),r=mt(t)?t:n;if(!mt(r))return{};let o=Jn(r,"utf-8"),a={};for(let i of o.split(`
|
|
436
|
+
`)){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 Za(e,t){return["SECRET","KEY","TOKEN","PASSWORD"].some(o=>e.toUpperCase().includes(o))&&t.length>0}function ei(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 ti(e){d("Validating environment variables...","info");let t=b(),n=ft(t),r=Vt(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")O({valid:i,checked:Object.keys(n).length,missing:Object.entries(a).filter(([,l])=>!l).map(([l])=>l)},{format:"json"});else{ot("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(c.CONFIG_ERROR)}async function ni(e){let t=b(),n=ft(t),r=pt(t,".env.local");if(mt(r)&&!e.yes&&!e.dryRun){let u=await xe.confirm({message:".env.local already exists. Overwrite?",initialValue:!1});(xe.isCancel(u)||!u)&&(d("Template generation cancelled","warn"),process.exit(c.CANCELLED))}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 g=`[${a}/${i}] ${u}`,p=await xe.text({message:g,defaultValue:m});xe.isCancel(p)&&(d("Template generation cancelled","warn"),process.exit(c.CANCELLED)),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(`
|
|
437
|
+
`);e.dryRun?(d("Dry-run: would write .env.local","info"),O(l,{format:"plain"})):(Xa(r,l+`
|
|
438
|
+
`),d(`Environment template written to ${r}`,"success"))}async function ri(e){d("Comparing environment variables...","info");let t=b(),n=ft(t),r=Vt(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")O({matching:i.length,missing:o.length,extra:a.length,missing_vars:o,extra_vars:a},{format:"json"});else{if(ot("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 oi(e){d("Reading environment variables...","info");let t=b(),n=Vt(t),r=ft(t),o={};for(let a of Object.keys(r))if(a in n){let i=n[a];o[a]=Za(a,i)?ei(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;O(a,{format:"json"})}else{ot("Current Environment Variables");let a=Object.entries(o).map(([i,l])=>({Variable:Qa.cyan(i),Value:l}));O(a,{format:"table"}),d("Note: Sensitive values (KEY, TOKEN, SECRET, PASSWORD) are masked","info")}}function Xn(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 ti(a);break;case"template":await ni(a);break;case"diff":await ri(a);break;case"show":await oi(a);break;default:d(`Unknown env subcommand: ${n}. Valid commands: validate, template, diff, show`,"error"),process.exit(c.INVALID_ARGS)}}catch(i){let l=i instanceof Error?i.message:String(i);d(`Environment command failed: ${l}`,"error"),A("Full error",{error:i}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
439
439
|
Examples:
|
|
440
440
|
nebutra env validate Check all required vars are set
|
|
441
441
|
nebutra env template Generate .env.local from .env.example with prompts
|
|
@@ -452,7 +452,7 @@ Flags:
|
|
|
452
452
|
Sensitive Variables:
|
|
453
453
|
Variables with KEY, TOKEN, SECRET, PASSWORD in their names are masked in output.
|
|
454
454
|
This prevents accidental exposure of credentials.
|
|
455
|
-
`)}import{promises as
|
|
455
|
+
`)}import{promises as Qn}from"fs";import{join as Ae}from"path";import*as qe from"@clack/prompts";import D from"picocolors";var Be=["design","iam","commerce","integrations","platform","ops","ai"];function si(e){return Be.includes(e)}function Kt(e,t){if(!t)throw new Error(`--category is required. Valid categories: ${Be.join(", ")}. Example: nebutra generate package ${e} --category iam`);if(!si(t))throw new Error(`Unknown category "${t}". Valid categories: ${Be.join(", ")}.`);return`packages/${t}/${e}`}function Zn(e){let t=e.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("");return{"package.json":`{
|
|
456
456
|
"name": "@nebutra/${e}",
|
|
457
457
|
"version": "0.0.0",
|
|
458
458
|
"private": true,
|
|
@@ -537,7 +537,7 @@ export default function RootLayout({
|
|
|
537
537
|
|
|
538
538
|
@theme {
|
|
539
539
|
--color-*: var(--neutral-*);
|
|
540
|
-
}`}}function
|
|
540
|
+
}`}}function er(e){return{"package.json":`{
|
|
541
541
|
"name": "@nebutra/${e}",
|
|
542
542
|
"version": "0.0.0",
|
|
543
543
|
"type": "module",
|
|
@@ -574,7 +574,7 @@ export default function RootLayout({
|
|
|
574
574
|
|
|
575
575
|
export function hello(name: string): string {
|
|
576
576
|
return \`Hello, \${name}!\`;
|
|
577
|
-
}`}}function
|
|
577
|
+
}`}}function tr(e){let t=e.replace(/\//g,"_").replace(/-/g,"_");return{[`src/routes/${e}.ts`]:`import { Hono } from "hono";
|
|
578
578
|
import { z } from "zod";
|
|
579
579
|
|
|
580
580
|
type Env = {
|
|
@@ -625,7 +625,7 @@ app.post("/", async (c) => {
|
|
|
625
625
|
);
|
|
626
626
|
});
|
|
627
627
|
|
|
628
|
-
export default app;`}}function
|
|
628
|
+
export default app;`}}function nr(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";
|
|
629
629
|
import { cn } from "@nebutra/ui/utils";
|
|
630
630
|
|
|
631
631
|
const ${t}Variants = cva(
|
|
@@ -699,13 +699,13 @@ export const AllSizes: Story = {
|
|
|
699
699
|
<${n} size="lg">Large</\${pascalCase}>
|
|
700
700
|
</div>
|
|
701
701
|
),
|
|
702
|
-
};`}}async function
|
|
703
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
704
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
705
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
706
|
-
`),process.exit(c.DRY_RUN_OK)}Ve.intro(D.bgCyan(D.black(" nebutra generate component ")));let n=s.spinner();try{n.start(`Generating component ${D.cyan(e)}...`);let r=await ft("component",e);n.stop(`Created component ${D.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 tr(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 ti(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 ni(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 ri(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 oi(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1,variants:r.variants,sizes:r.sizes})})}import Te from"picocolors";async function W(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 si(e){try{let t=e.period||"7d",n=e.compare;s.info(`Fetching growth metrics for ${t}...`);let r=await W("/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 ai(e){try{let t=e.period||"7d",n=e.segment||"all";s.info(`Fetching funnel data for ${t} (${n})...`);let r=await W("/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?Te.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 ii(e){try{let t=e.period||"weekly",n=parseInt(e.cohorts,10)||8;s.info(`Fetching ${t} cohort data (${n} cohorts)...`);let r=await W("/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 g=m/100,p=String(Math.round(m)).padEnd(8);g>=.7?p=Te.green(p):g>=.4?p=Te.yellow(p):p=Te.red(p),l+=p+" "}})}),c.SUCCESS}catch(t){return s.error(`Cohort error: ${t instanceof Error?t.message:String(t)}`),c.ERROR}}async function ci(e,t){try{if(e==="stats"){s.info("Fetching newsletter stats...");let n=await W("/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 W("/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 W("/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 li(e,t){try{if(e==="status"){s.info("Fetching referral program status...");let n=await W("/referral/status");return c.SUCCESS}if(e==="config"){s.info("Fetching referral config...");let n=await W("/referral/config");return Object.entries(n).forEach(([r,o])=>{}),c.SUCCESS}return e==="leaderboard"?(s.info("Fetching referral leaderboard..."),(await W("/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 di(e,t){try{if(e==="list"){s.info("Fetching active experiments...");let n=await W("/experiment");return n.length===0?c.SUCCESS:(n.forEach(r=>{let o=r.status==="running"?Te.yellow("\u25CF"):Te.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 W("/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 W(`/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 W(`/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 ui(e){try{let t=e.focus||"activation",n=e.depth||"quick";s.info(`Generating growth insights (focus: ${t}, depth: ${n})...`);let r=await W("/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 nr(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 si(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 ai(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 ii(a))}),t.command("newsletter <subcommand>").description("Newsletter management").action(async(a,i)=>{process.exit(await ci(a,i))}),t.command("referral <subcommand>").description("Referral program management").action(async(a,i)=>{process.exit(await li(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 di(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 ui(a))}),t.option("--dry-run","Preview changes without applying them").option("--format <json|text>","Output format","text").option("--yes","Skip confirmation prompts")}import Vt,{lstatSync as mi,readdirSync as Kt,readFileSync as pi,writeFileSync as fi}from"fs";import le from"path";import{fileURLToPath as gi}from"url";import z from"picocolors";var yi=le.dirname(gi(import.meta.url));function Bt(){let e=new Map,t=le.join(yi,"../../../apps");if(!Vt.existsSync(t))return e;try{let n=Kt(t);for(let r of n){let o=le.join(t,r),a=le.join(o,"messages");Vt.existsSync(a)&&mi(a).isDirectory()&&e.set(r,a)}}catch(n){s.debug(`Error scanning apps directory: ${n instanceof Error?n.message:String(n)}`)}return e}function Ke(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=Ke(o,a);for(let l of i)n.add(l)}else n.add(a)}return n}function Be(e){try{let t=pi(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 hi(e){if(s.info("Syncing language files across the project..."),(await I("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 $({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 bi(e){s.info("Validating translation files...");let t=Bt();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=Kt(a).filter(p=>p.endsWith(".json"));if(l.length===0)continue;let u=le.join(a,"en.json"),m=Be(u);if(!m){s.warn(`Could not load reference locale en.json from ${o}`);continue}let g=Ke(m);for(let p of l){if(p==="en.json")continue;let b=p.replace(".json",""),K=le.join(a,p),X=Be(K);if(!X){s.debug(`Could not parse ${p}`);continue}let Q=Ke(X),Re=Array.from(g).filter(f=>!Q.has(f)),fe=Array.from(Q).filter(f=>!g.has(f)),je=g.size>0?(g.size-Re.length)/g.size*100:100;(Re.length>0||fe.length>0)&&(r=!0),i.push({locale:b,totalKeys:g.size,missingKeys:Re,extraKeys:fe,coverage:Math.round(je*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 vi(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=Bt();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=le.join(l,`${e}.json`);if(Vt.existsSync(u)){s.warn(`Locale ${e} already exists in ${i}`);continue}try{let b=function(K){for(let X in K)typeof K[X]=="string"?K[X]="":typeof K[X]=="object"&&K[X]!==null&&b(K[X])};var a=b;let m=le.join(l,"en.json"),g=Be(m);if(!g){s.warn(`Could not load reference en.json from ${i}, skipping...`);continue}let p=JSON.parse(JSON.stringify(g));b(p),t.dryRun?s.info(`Would create ${e} locale in ${i}`):(fi(u,JSON.stringify(p,null,2)+`
|
|
707
|
-
`),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
|
|
708
|
-
Average coverage: ${i(a.toFixed(1)+"%")}`)}}}function
|
|
702
|
+
};`}}async function gt(e,t,n){let r=b(),o=[],a={};switch(e){case"app":a=Zn(t);break;case"package":a=er(t);break;case"route":a=tr(t);break;case"component":a=nr(t);break;default:throw new Error(`Unknown generate type: ${e}`)}let i=e==="package"?Kt(t,n.category):void 0;for(let[l,u]of Object.entries(a)){let m=e==="app"?Ae(r,"apps",t,l):e==="package"?Ae(r,i??`packages/${t}`,l):Ae(r,"backends/gateway",l);o.push({path:m,size:Buffer.byteLength(u)})}return o}async function yt(e,t,n={}){let r=b(),o=[],a={};switch(e){case"app":a=Zn(t);break;case"package":a=er(t);break;case"route":a=tr(t);break;case"component":a=nr(t);break;default:throw new Error(`Unknown generate type: ${e}`)}let i=e==="package"?Kt(t,n.category):void 0;for(let[l,u]of Object.entries(a)){let m;e==="app"?m=Ae(r,"apps",t,l):e==="package"?m=Ae(r,i??`packages/${t}`,l):e==="component"?m=Ae(r,"packages/design/ui",l):m=Ae(r,"backends/gateway",l);let g=m.substring(0,m.lastIndexOf("/"));await Qn.mkdir(g,{recursive:!0}),await Qn.writeFile(m,u,"utf-8"),o.push(m)}return o}async function ai(e,t){if(t.dryRun){let r=await gt("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)+`
|
|
703
|
+
`),process.exit(c.DRY_RUN_OK)}qe.intro(D.bgCyan(D.black(" nebutra generate app ")));let n=s.spinner();try{n.start(`Generating app ${D.cyan(e)}...`);let r=await yt("app",e);n.stop(`Created app ${D.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 ii(e,t){let n;try{n=Kt(e,t.category)}catch(o){s.error(o instanceof Error?o.message:String(o)),process.exit(c.INVALID_ARGS)}if(t.dryRun){let o=await gt("package",e,t),a={mode:"dry-run",timestamp:new Date().toISOString(),type:"package",name:e,category:t.category,location:n,files:o.map(i=>({path:i.path,sizeBytes:i.size}))};process.stdout.write(JSON.stringify(a,null,2)+`
|
|
704
|
+
`),process.exit(c.DRY_RUN_OK)}qe.intro(D.bgCyan(D.black(" nebutra generate package ")));let r=s.spinner();try{r.start(`Generating package ${D.cyan(e)} in ${D.dim(n)}...`);let o=await yt("package",e,{category:t.category});r.stop(`Created package ${D.cyan(e)}`,0),s.success(`Package created with ${o.length} files`),s.info("Next steps:"),s.info(` Edit ${n}/src/index.ts`),s.info(` Run: pnpm --filter @nebutra/${e} build`),process.exit(c.SUCCESS)}catch(o){r.stop(`Failed to generate package: ${o instanceof Error?o.message:"Unknown error"}`,1),process.exit(c.ERROR)}}async function ci(e,t){if(t.dryRun){let r=await gt("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)+`
|
|
705
|
+
`),process.exit(c.DRY_RUN_OK)}qe.intro(D.bgCyan(D.black(" nebutra generate route ")));let n=s.spinner();try{n.start(`Generating route ${D.cyan(e)}...`);let r=await yt("route",e);n.stop(`Created route ${D.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 li(e,t){if(t.dryRun){let r=await gt("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)+`
|
|
706
|
+
`),process.exit(c.DRY_RUN_OK)}qe.intro(D.bgCyan(D.black(" nebutra generate component ")));let n=s.spinner();try{n.start(`Generating component ${D.cyan(e)}...`);let r=await yt("component",e);n.stop(`Created component ${D.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 rr(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 ai(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1,interactive:process.stdin.isTTY})}),t.command("package <name>").description(`Scaffold a new categorized package (@nebutra/<name>). Categories: ${Be.join(", ")}`).requiredOption("--category <category>",`Package category: ${Be.join(" | ")}`).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 ii(n,{...r,category:r.category,dryRun:r.dryRun||!1,yes:o.yes||!1})}),t.command("route <path>").description("Scaffold a Hono API route in backends/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 ci(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 li(n,{...r,dryRun:r.dryRun||!1,yes:o.yes||!1,variants:r.variants,sizes:r.sizes})})}import Le from"picocolors";async function z(e,t){let n=await b(),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 di(e){try{let t=e.period||"7d",n=e.compare;s.info(`Fetching growth metrics for ${t}...`);let r=await z("/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 ui(e){try{let t=e.period||"7d",n=e.segment||"all";s.info(`Fetching funnel data for ${t} (${n})...`);let r=await z("/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?Le.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 mi(e){try{let t=e.period||"weekly",n=parseInt(e.cohorts,10)||8;s.info(`Fetching ${t} cohort data (${n} cohorts)...`);let r=await z("/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 g=m/100,p=String(Math.round(m)).padEnd(8);g>=.7?p=Le.green(p):g>=.4?p=Le.yellow(p):p=Le.red(p),l+=p+" "}})}),c.SUCCESS}catch(t){return s.error(`Cohort error: ${t instanceof Error?t.message:String(t)}`),c.ERROR}}async function pi(e,t){try{if(e==="stats"){s.info("Fetching newsletter stats...");let n=await z("/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 z("/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 z("/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 fi(e,t){try{if(e==="status"){s.info("Fetching referral program status...");let n=await z("/referral/status");return c.SUCCESS}if(e==="config"){s.info("Fetching referral config...");let n=await z("/referral/config");return Object.entries(n).forEach(([r,o])=>{}),c.SUCCESS}return e==="leaderboard"?(s.info("Fetching referral leaderboard..."),(await z("/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 gi(e,t){try{if(e==="list"){s.info("Fetching active experiments...");let n=await z("/experiment");return n.length===0?c.SUCCESS:(n.forEach(r=>{let o=r.status==="running"?Le.yellow("\u25CF"):Le.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 z("/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 z(`/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 z(`/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 yi(e){try{let t=e.focus||"activation",n=e.depth||"quick";s.info(`Generating growth insights (focus: ${t}, depth: ${n})...`);let r=await z("/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 or(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 di(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 ui(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 mi(a))}),t.command("newsletter <subcommand>").description("Newsletter management").action(async(a,i)=>{process.exit(await pi(a,i))}),t.command("referral <subcommand>").description("Referral program management").action(async(a,i)=>{process.exit(await fi(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 gi(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 yi(a))}),t.option("--dry-run","Preview changes without applying them").option("--format <json|text>","Output format","text").option("--yes","Skip confirmation prompts")}import Bt,{lstatSync as hi,readdirSync as qt,readFileSync as bi,writeFileSync as vi}from"fs";import de from"path";import{fileURLToPath as wi}from"url";import Q from"picocolors";var Si=de.dirname(wi(import.meta.url));function Wt(){let e=new Map,t=de.join(Si,"../../../apps");if(!Bt.existsSync(t))return e;try{let n=qt(t);for(let r of n){let o=de.join(t,r),a=de.join(o,"messages");Bt.existsSync(a)&&hi(a).isDirectory()&&e.set(r,a)}}catch(n){s.debug(`Error scanning apps directory: ${n instanceof Error?n.message:String(n)}`)}return e}function We(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=We(o,a);for(let l of i)n.add(l)}else n.add(a)}return n}function Ye(e){try{let t=bi(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 Ri(e){if(s.info("Syncing language files across the project..."),(await N("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 $({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 Ei(e){s.info("Validating translation files...");let t=Wt();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=qt(a).filter(p=>p.endsWith(".json"));if(l.length===0)continue;let u=de.join(a,"en.json"),m=Ye(u);if(!m){s.warn(`Could not load reference locale en.json from ${o}`);continue}let g=We(m);for(let p of l){if(p==="en.json")continue;let h=p.replace(".json",""),F=de.join(a,p),G=Ye(F);if(!G){s.debug(`Could not parse ${p}`);continue}let W=We(G),me=Array.from(g).filter(f=>!W.has(f)),ie=Array.from(W).filter(f=>!g.has(f)),Ce=g.size>0?(g.size-me.length)/g.size*100:100;(me.length>0||ie.length>0)&&(r=!0),i.push({locale:h,totalKeys:g.size,missingKeys:me,extraKeys:ie,coverage:Math.round(Ce*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(`${Q.bold(o)}:`);for(let i of a){let u=(i.coverage>=100?Q.green:i.coverage>=80?Q.yellow:Q.red)(`${i.coverage}%`);s.info(` ${Q.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 Ci(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=Wt();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=de.join(l,`${e}.json`);if(Bt.existsSync(u)){s.warn(`Locale ${e} already exists in ${i}`);continue}try{let h=function(F){for(let G in F)typeof F[G]=="string"?F[G]="":typeof F[G]=="object"&&F[G]!==null&&h(F[G])};var a=h;let m=de.join(l,"en.json"),g=Ye(m);if(!g){s.warn(`Could not load reference en.json from ${i}, skipping...`);continue}let p=JSON.parse(JSON.stringify(g));h(p),t.dryRun?s.info(`Would create ${e} locale in ${i}`):(vi(u,JSON.stringify(p,null,2)+`
|
|
707
|
+
`),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 ki(e){s.info("Checking translation coverage...");let t=Wt();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=qt(i).filter(h=>h.endsWith(".json"));if(u.length===0)continue;let m=de.join(i,"en.json"),g=Ye(m);if(!g){s.debug(`Could not load reference locale from ${a}`);continue}let p=We(g);for(let h of u){let F=h.replace(".json",""),G=de.join(i,h),W=Ye(G);if(!W)continue;let me=We(W),ie=Array.from(me).filter(M=>{let Ie=W[M.split(".")[0]];return Ie!==void 0&&Ie!==""}).length,Ce=p.size-ie,f=p.size>0?ie/p.size*100:0;l.push({locale:F,coverage:Math.round(f*100)/100,totalKeys:p.size,translatedKeys:ie,missingKeys:Ce})}l.length>0&&(n.set(a,l),o++,r+=l.reduce((h,F)=>h+F.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(`${Q.bold(a)}:`);for(let l of i){let u="",g=Math.round(l.coverage/100*20);u="\u2588".repeat(g)+"\u2591".repeat(20-g);let h=(l.coverage>=100?Q.green:l.coverage>=80?Q.yellow:Q.red)(`${l.coverage.toFixed(1)}%`);s.info(` ${Q.cyan(l.locale.padEnd(5))} [${u}] ${h}`),s.info(` ${l.translatedKeys}/${l.totalKeys} keys (${l.missingKeys} missing)`)}}if(o>0){let a=r/o,i=a>=100?Q.green:a>=80?Q.yellow:Q.red;s.info(`
|
|
708
|
+
Average coverage: ${i(a.toFixed(1)+"%")}`)}}}function sr(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 Ri({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 Ei({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 Ci(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 ki({format:n.format||r.format,interactive:r.yes!==!0})})}import*as ht from"@clack/prompts";async function Oi(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 Ne("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"),A("Docker compose output",{stderr:r.stderr}),process.exit(r.exitCode||1))}async function xi(e){d("Stopping infrastructure stack...","info");let t=await Ne("down",{dryRun:e.dryRun});t.exitCode===0?d("Infrastructure stack stopped","success"):(d("Failed to stop infrastructure stack","error"),A("Docker compose output",{stderr:t.stderr}),process.exit(t.exitCode||1))}async function Ai(e){d("Checking infrastructure status...","info");let t=await Ne("ps --format json",{dryRun:e.dryRun});if(t.exitCode===0)try{let n=JSON.parse(t.stdout);if(e.format==="json")O(n,{format:"json"});else{let r=n.map(o=>({name:o.Name,status:o.Status,ports:o.Ports||"-"}));O(r,{format:"table"})}}catch{O(t.stdout,{format:"plain"})}else d("Failed to check infrastructure status","error"),A("Docker compose output",{stderr:t.stderr}),process.exit(t.exitCode||1)}async function _i(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 Ne(o.join(" "),{dryRun:n.dryRun});a.exitCode===0?O(a.stdout,{format:"plain"}):(d("Failed to fetch logs","error"),A("Docker compose output",{stderr:a.stderr}),process.exit(a.exitCode||1))}async function $i(e){let t=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!e.yes&&!e.dryRun)if(t){let r=await ht.confirm({message:"Reset infrastructure? This will remove all containers and volumes.",initialValue:!1});(ht.isCancel(r)||!r)&&(d("Reset cancelled","warn"),process.exit(c.CANCELLED))}else d("Infrastructure reset requires explicit --yes confirmation (use: nebutra infra reset --yes)","error"),process.exit(c.INVALID_ARGS);d("Resetting infrastructure stack...","warn");let n=await Ne("down -v",{dryRun:e.dryRun});n.exitCode===0?d("Infrastructure reset successfully","success"):(d("Failed to reset infrastructure","error"),A("Docker compose output",{stderr:n.stderr}),process.exit(n.exitCode||1))}function ar(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 Oi(i);break;case"down":await xi(i);break;case"status":await Ai(i);break;case"logs":await _i(r,i);break;case"reset":await $i(i);break;default:d(`Unknown infra subcommand: ${n}. Valid commands: up, down, status, logs, reset`,"error"),process.exit(c.INVALID_ARGS)}}catch(l){let u=l instanceof Error?l.message:String(l);d(`Infrastructure command failed: ${u}`,"error"),A("Full error",{error:l}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
709
709
|
Examples:
|
|
710
710
|
nebutra infra up Start full stack
|
|
711
711
|
nebutra infra up --lite Start lite stack (PostgreSQL + Redis)
|
|
@@ -722,46 +722,46 @@ Flags:
|
|
|
722
722
|
--lite Use docker-compose.lite.yml (PostgreSQL + Redis only)
|
|
723
723
|
--profile <name> Enable Docker Compose profile (search, notifications, etc.)
|
|
724
724
|
--format <type> Output format: json, plain (default: plain, auto-json in non-TTY)
|
|
725
|
-
`)}import
|
|
726
|
-
`),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)),
|
|
725
|
+
`)}import Yt from"fs";import Pi from"path";import*as ee from"@clack/prompts";import bt from"picocolors";var Ii={$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 Ni(){return process.stdout.isTTY===!0}function Ti(e){return e.yes===!0||e.dryRun===!0||!Ni()}function ji(e,t,n){return{operation:n?"update":"create",path:e,...n&&{before:n},after:t}}async function ir(e={}){let t=process.cwd(),n=Pi.join(t,"nebutra.config.json"),r=Yt.existsSync(n);e.ifNotExists&&r&&(s.info("nebutra.config.json already exists, skipping initialization."),process.exit(c.SUCCESS));let o=Ti(e);o||ee.intro(bt.bgCyan(bt.black(" nebutra init "))),r&&!o&&(ee.log.warn("nebutra.config.json already exists in this directory."),await ee.confirm({message:"Do you want to overwrite it?",initialValue:!1})||(ee.outro("Operation aborted."),process.exit(c.CANCELLED)));let a={...Ii};if(e.dryRun){let i=r?JSON.parse(Yt.readFileSync(n,"utf-8")):void 0,l=ji(n,a,i);x(l),d("Dry-run completed. No changes made."),process.exit(c.DRY_RUN_OK)}o?s.info("Initializing Nebutra CLI configuration..."):ee.spinner().start("Initializing Nebutra CLI configuration..."),Yt.writeFileSync(n,JSON.stringify(a,null,2)+`
|
|
726
|
+
`),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)),ee.spinner().stop(bt.green("Configuration saved to nebutra.config.json")),ee.outro(bt.cyan('Initialization complete! You can now use "nebutra add" to start injecting components.'))),process.exit(c.SUCCESS)}import St from"fs/promises";import Ki from"os";import He from"path";import*as I from"@clack/prompts";import k from"picocolors";var Ui="https://analytics.nebutra.com";function Li(e={}){if(e.noTelemetry===!0)return!0;let t=process.env.NEBUTRA_TELEMETRY;return t==="0"||t==="false"}function vt(e,t={}){Li(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??Ui},onError:()=>{}});if(typeof r?.track!="function")return;let o=r.track("license.cli",e);o&&typeof o.then=="function"&&await o.catch(()=>{})}catch{}})()}import{createHmac as Di}from"crypto";var Fi=[{id:"v1",key:"nebutra-sailor:scaffold-marker:v1"}],Gi="v1";function Mi(e){if(e)return Fi.find(t=>t.id===e)}function Vi(e,t){let n=`${e.cliVersion}|${e.scaffoldedAt}|${e.projectName}|${e.nonce}`;return Di("sha256",t).update(n).digest("hex")}function cr(e){if(!e)return{valid:!1,reason:"missing_meta"};if(e.schemaVersion!==1)return{valid:!1,reason:"schema_mismatch"};if(typeof e.cliVersion!="string"||typeof e.scaffoldedAt!="string"||typeof e.projectName!="string"||typeof e.nonce!="string"||typeof e.signature!="string")return{valid:!1,reason:"schema_mismatch"};let t=e.signingKeyId??Gi,n=Mi(t);return n?Vi({cliVersion:e.cliVersion,scaffoldedAt:e.scaffoldedAt,projectName:e.projectName,nonce:e.nonce},n.key)!==e.signature?{valid:!1,reason:"signature_mismatch"}:{valid:!0,reason:"ok"}:{valid:!1,reason:"unknown_signing_key"}}var wt={ok:"valid",missing_meta:".nebutra/scaffold-meta.json not found",schema_mismatch:"scaffold-meta.json schema mismatch (corrupt or future format)",unknown_signing_key:"signed with an unknown key \u2014 upgrade `nebutra` to a newer release that recognises it",signature_mismatch:"signature does not match \u2014 file has been tampered with or hand-edited"};var lr=process.env.NEBUTRA_LICENSE_API_URL??"https://nebutra.com";async function dr(){let e=Ki.homedir(),t=He.join(e,".config","nebutra");try{await St.mkdir(t,{recursive:!0})}catch{}return t}async function Bi(e){let t=`${lr}/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 (${lr}). 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 qi(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 Wi(e,t){let n=t.quiet===!0;n||I.intro(k.bgCyan(k.black(" Nebutra License Activation "))),vt({action:"activate_attempted"});try{if(e.length<10)throw new Error("Invalid license key format.");n||I.log.step("Validating license key with Nebutra...");let{tier:r,type:o}=await Bi(e),a=await dr(),i=He.join(a,"license.json");await St.writeFile(i,JSON.stringify({licenseKey:e,tier:r,type:o,activatedAt:new Date().toISOString()},null,2),"utf-8"),n||(I.log.success(k.green(`${r} license activated and saved to ${i}`)),I.outro(k.cyan("Premium CLI capabilities are now unlocked!"))),vt({action:"activated",tier:r,type:o})}catch(r){vt({action:"failed",error_code:qi(r)}),n||(I.log.error(k.red("Activation failed")),s.error(r instanceof Error?r.message:String(r)),I.outro("Please check your key and try again.")),process.exit(c.ERROR)}}async function Yi(e){let t=e.quiet===!0;t||I.intro(k.bgCyan(k.black(" Nebutra License Status ")));try{let n=await dr(),r=He.join(n,"license.json"),o=await St.readFile(r,"utf-8"),a=JSON.parse(o);if(a.licenseKey)t||(I.log.success(k.green("License is active!")),I.log.info(`Key: ${a.licenseKey.substring(0,8)}...`),a.tier&&I.log.info(`Tier: ${k.bold(a.tier)}`),a.type&&I.log.info(`Type: ${a.type}`),I.log.info(`Activated: ${new Date(a.activatedAt).toLocaleString()}`),I.outro(k.cyan("You are ready to use premium features.")));else throw new Error("License key not found in config.")}catch{t||(I.log.warn(k.yellow("No active license found locally.")),I.log.message(k.dim("Run `nebutra license activate <key>` to unlock premium features.")),I.outro("")),process.exit(c.NOT_FOUND)}}async function Hi(e,t){let n=t.format==="json",r=t.quiet===!0,o=e?He.resolve(process.cwd(),e):process.cwd(),a=He.join(o,".nebutra","scaffold-meta.json"),i;try{i=await St.readFile(a,"utf-8")}catch{let p={valid:!1,tier:null,cliVersion:null,scaffoldedAt:null,projectName:null,reason:"missing_meta",reasonText:wt.missing_meta,path:a};n?process.stdout.write(`${JSON.stringify(p,null,2)}
|
|
727
727
|
`):r||process.stderr.write(`${k.red("\u2718")} No scaffold marker found at ${k.dim(a)}.
|
|
728
728
|
This project does not appear to be scaffolded by ${k.bold("create-sailor")}.
|
|
729
729
|
Run ${k.cyan("npm create sailor@latest")} to scaffold a licensed project.
|
|
730
|
-
`),process.exit(c.NOT_FOUND)}let l=null;try{l=JSON.parse(i)}catch{}let u=
|
|
730
|
+
`),process.exit(c.NOT_FOUND)}let l=null;try{l=JSON.parse(i)}catch{}let u=cr(l),m={valid:u.valid,tier:l?.license?.tier??null,cliVersion:l?.cliVersion??null,scaffoldedAt:l?.scaffoldedAt??null,projectName:l?.projectName??null,signingKeyId:l?.signingKeyId??null,reason:u.reason,reasonText:wt[u.reason],path:a};if(n&&(process.stdout.write(`${JSON.stringify(m,null,2)}
|
|
731
731
|
`),process.exit(u.valid?c.SUCCESS:c.INCOMPATIBLE)),!r)if(u.valid&&l){let g=new Date(l.scaffoldedAt).toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}),p=[`${k.green("\u2713")} Project "${k.bold(l.projectName)}" scaffolded with ${k.cyan(`create-sailor@${l.cliVersion}`)} on ${g}`,` Signing key: ${k.dim(l.signingKeyId??"v1 (legacy, no keyId)")}`,` Tier: ${k.bold(l.license?.tier??"independent")}`,` ${k.green("Independent Developer License valid.")}`];process.stderr.write(`${p.join(`
|
|
732
732
|
`)}
|
|
733
|
-
`)}else{let g=
|
|
733
|
+
`)}else{let g=wt[u.reason];process.stderr.write(`${k.red("\u2718")} Invalid scaffold marker.
|
|
734
734
|
Reason: ${k.yellow(g)}
|
|
735
735
|
File: ${k.dim(a)}
|
|
736
|
-
`)}process.exit(u.valid?c.SUCCESS:c.INCOMPATIBLE)}function
|
|
737
|
-
`)}async function
|
|
738
|
-
`));let r=
|
|
736
|
+
`)}process.exit(u.valid?c.SUCCESS:c.INCOMPATIBLE)}function ur(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 Wi(n,r)}),t.command("status").description("Check the locally configured license status").option("--quiet","Suppress output").action(async n=>{await Yi(n)}),t.command("verify [path]").description("Verify the scaffold-meta HMAC signature in a create-sailor project (defaults to cwd)").option("--format <type>","Output format: json | plain (default: plain)").option("--quiet","Suppress non-essential output").action(async function(n,r,o){let a=o&&typeof o.optsWithGlobals=="function"?o.optsWithGlobals()??{}:{...r},i=typeof r?.format=="string"?r.format:typeof a.format=="string"?a.format:void 0,l=!!r?.quiet||!!a.quiet;await Hi(n,{format:i,quiet:l})})}import{existsSync as mr,readFileSync as zi,writeFileSync as Ji}from"fs";import{dirname as Xi,join as pr,parse as Qi}from"path";import*as Rt from"@clack/prompts";function Zi(e){let t=e,{root:n}=Qi(t);for(;;){if(mr(pr(t,"package.json")))return t;if(t===n)return null;let r=Xi(t);if(r===t)return null;t=r}}function ec(e){if(!mr(e))return{};try{return JSON.parse(zi(e,"utf-8"))}catch{return{}}}function tc(e,t){Ji(e,JSON.stringify(t,null,2)+`
|
|
737
|
+
`)}async function nc(e,t){let n=t.format==="json",r=Zi(process.cwd());r||(n?console.log(JSON.stringify({command:"link",success:!1,error:"No package.json found upstream"},null,2)):s.error("No package.json found in current or parent directories."),process.exit(c.NOT_FOUND));let o=e;if(!o){t.yes&&(n?console.log(JSON.stringify({command:"link",success:!1,error:"project-id required in --yes mode"},null,2)):s.error("project-id is required when running with --yes / non-interactive."),process.exit(c.INVALID_ARGS));let u=await Rt.text({message:"Project ID to link to:",placeholder:"proj_abc123",validate:m=>m&&m.trim().length>0?void 0:"Project ID cannot be empty"});Rt.isCancel(u)&&(s.warn("Cancelled"),process.exit(c.CANCELLED)),o=String(u).trim()}let a=pr(r,"nebutra.config.json"),l={...ec(a),projectId:o};tc(a,l),n?console.log(JSON.stringify({command:"link",success:!0,projectId:o,configPath:a},null,2)):s.success(`Linked to project: ${o}`)}function fr(e){e.command("link [project-id]").description("Link the current project to a Nebutra project id").option("--format <type>","Output format: json or plain").action(async(t,n,r)=>{let o=r?.optsWithGlobals?.()||n;await nc(t,{yes:o.yes||!1,format:n.format||o.format})})}import{existsSync as gr,rmSync as rc,unlinkSync as oc}from"fs";import{homedir as yr,platform as sc}from"os";import{join as ze}from"path";function ac(){if(sc()==="win32"){let t=process.env.LOCALAPPDATA||ze(yr(),"AppData","Local");return ze(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?ze(e,"nebutra"):ze(yr(),".config","nebutra")}var ic=["auth.json","session.json","token","credentials.json"];async function cc(e){let t=ac(),n=e.format==="json",r=0,o=[];if(e.all){if(gr(t))try{rc(t,{recursive:!0,force:!0}),r=1,o.push(t)}catch(a){let i=a instanceof Error?a.message:String(a);n?console.log(JSON.stringify({command:"logout",success:!1,error:i},null,2)):s.error(`Failed to clear config directory: ${i}`),process.exit(c.PERMISSION_DENIED)}}else for(let a of ic){let i=ze(t,a);if(gr(i))try{oc(i),r++,o.push(i)}catch{}}r===0&&(n?console.log(JSON.stringify({command:"logout",success:!0,alreadyLoggedOut:!0,removed:[]},null,2)):s.info("Already logged out"),process.exit(c.SUCCESS)),n?console.log(JSON.stringify({command:"logout",success:!0,removed:o,clearedAll:!!e.all},null,2)):s.success(e.all?"Cleared all nebutra config":"Logged out of nebutra.com")}function hr(e){e.command("logout").description("Clear stored authentication credentials").option("--all","Clear ALL local config, not just auth tokens").option("--format <type>","Output format: json or plain").action(async(t,n)=>{let r=n?.optsWithGlobals?.()||t;await cc({all:t.all||!1,format:t.format||r.format})})}import{spawn as lc}from"child_process";import{dirname as dc,resolve as uc}from"path";import{fileURLToPath as mc}from"url";import*as ae from"@clack/prompts";import ge from"picocolors";var pc=dc(mc(import.meta.url));function fc(){let e=[()=>uc(pc,"../../../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 br(e){e.command("mcp").description("Start the Nebutra MCP server for Cursor/Windsurf").option("--stdio","Use stdio transport (default)",!0).action(async t=>{ae.intro(ge.bgCyan(ge.black(" nebutra mcp ")));try{let n=fc();ae.log.info(ge.cyan("Starting Nebutra MCP server for Cursor/Windsurf integration...")),ae.log.info(ge.dim(`Press Ctrl+C to stop the server.
|
|
738
|
+
`));let r=lc(process.execPath,[n],{stdio:"inherit",env:{...process.env}});r.on("close",o=>{o===0?ae.outro(ge.green("MCP server stopped gracefully.")):o===null&&ae.outro(ge.yellow("MCP server terminated.")),process.exit(o??0)}),r.on("error",o=>{s.error(`
|
|
739
739
|
Failed to start MCP server: ${o.message}`),o.code==="ENOENT"&&(s.warn(`
|
|
740
|
-
Tip: Ensure @nebutra/mcp is installed or available in your PATH.`),s.info(" Install globally: npm install -g @nebutra/mcp"),process.exit(c.NOT_FOUND)),process.exit(c.ERROR)}),process.on("SIGINT",()=>{ae.log.info(
|
|
741
|
-
Shutting down MCP server...`)),r.kill("SIGINT")}),process.on("SIGTERM",()=>{ae.log.info(
|
|
742
|
-
`),process.exit(c.SUCCESS)}
|
|
743
|
-
`);for(let t of Object.values(
|
|
744
|
-
`),process.exit(c.SUCCESS)),
|
|
740
|
+
Tip: Ensure @nebutra/mcp is installed or available in your PATH.`),s.info(" Install globally: npm install -g @nebutra/mcp"),process.exit(c.NOT_FOUND)),process.exit(c.ERROR)}),process.on("SIGINT",()=>{ae.log.info(ge.yellow(`
|
|
741
|
+
Shutting down MCP server...`)),r.kill("SIGINT")}),process.on("SIGTERM",()=>{ae.log.info(ge.yellow("Terminating MCP server...")),r.kill("SIGTERM")})}catch(n){ae.log.error(ge.red("Error: Unable to start MCP server. Ensure @nebutra/mcp is properly installed.")),n instanceof Error&&s.error(n.message),process.exit(c.ERROR)}})}import{promises as Ht}from"fs";import{resolve as vr}from"path";import*as _e from"@clack/prompts";import w from"picocolors";var ue={"ai-saas":{name:"ai-saas",description:"Full-stack AI SaaS platform with auth, billing, and analytics",apps:["web","landing-page","backends/gateway","studio"],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","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","backends/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","backends/gateway"],features:["billing","ai","search","i18n","blog"],theme:"ocean",stack:["Next.js 16","i18next","Hono"]},growth:{name:"growth",description:"Growth-focused platform with community and content",apps:["web","landing-page"],features:["growth","analytics","newsletter","community","blog"],theme:"vibrant",stack:["Next.js 16","Mailgun","Mixpanel"]},creative:{name:"creative",description:"Creative portfolio and landing page builder",apps:["landing-page"],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"],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","backends/gateway","studio","storybook","design-docs","sailor-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 gc(e){if(e.format==="json"){let t={presets:Object.values(ue).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)+`
|
|
742
|
+
`),process.exit(c.SUCCESS)}_e.intro(w.bgCyan(w.black(" nebutra preset list "))),s.info(`Available presets:
|
|
743
|
+
`);for(let t of Object.values(ue)){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 yc(e,t){e||(s.error("Please specify a preset name"),s.info("Available presets: "+Object.keys(ue).join(", ")),process.exit(c.INVALID_ARGS));let n=ue[e];n||(s.error(`Preset not found: ${e}`),s.info("Available presets: "+Object.keys(ue).join(", ")),process.exit(c.NOT_FOUND)),t.format==="json"&&(process.stdout.write(JSON.stringify(n,null,2)+`
|
|
744
|
+
`),process.exit(c.SUCCESS)),_e.intro(w.bgCyan(w.black(` nebutra preset show ${e} `))),s.info(`
|
|
745
745
|
${w.bold(n.name)}`),s.info(n.description),s.info(`
|
|
746
746
|
${w.bold("Apps:")}`),n.apps.forEach(r=>{s.info(` \u2022 ${r}`)}),s.info(`
|
|
747
747
|
${w.bold("Features:")}`),n.features.forEach(r=>{s.info(` \u2022 ${r}`)}),n.theme&&(s.info(`
|
|
748
748
|
${w.bold("Theme:")}`),s.info(` ${n.theme}`)),n.stack&&(s.info(`
|
|
749
|
-
${w.bold("Recommended Stack:")}`),n.stack.forEach(r=>{s.info(` \u2022 ${r}`)})),process.exit(c.SUCCESS)}async function
|
|
749
|
+
${w.bold("Recommended Stack:")}`),n.stack.forEach(r=>{s.info(` \u2022 ${r}`)})),process.exit(c.SUCCESS)}async function hc(e,t){let n=ue[e];n||(s.error(`Preset not found: ${e}`),s.info("Available presets: "+Object.keys(ue).join(", ")),process.exit(c.NOT_FOUND));let r=b(),o=vr(r,"nebutra.config.ts");t.dryRun&&(x({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"}},{format:t.format}),process.exit(c.DRY_RUN_OK)),_e.intro(w.bgCyan(w.black(" nebutra preset apply ")));let a=s.spinner();try{let i;try{i=await Ht.readFile(o,"utf-8")}catch{i=`export default {
|
|
750
750
|
preset: "${e}",
|
|
751
751
|
apps: ${JSON.stringify(n.apps,null,2).replace(/\n/g,`
|
|
752
752
|
`)},
|
|
753
753
|
features: ${JSON.stringify(n.features,null,2).replace(/\n/g,`
|
|
754
754
|
`)},
|
|
755
755
|
theme: "${n.theme||"default"}",
|
|
756
|
-
};`}let l=i.replace(/preset:\s*"[^"]*"/,`preset: "${e}"`);t.dryRun===!1&&(a.start(`Applying preset ${w.cyan(e)}...`),await
|
|
756
|
+
};`}let l=i.replace(/preset:\s*"[^"]*"/,`preset: "${e}"`);t.dryRun===!1&&(a.start(`Applying preset ${w.cyan(e)}...`),await Ht.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 bc(e){_e.intro(w.bgCyan(w.black(" nebutra preset env ")));let t=b();try{let n=await N("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 vc(e){_e.intro(w.bgCyan(w.black(" nebutra preset features ")));let t=b(),n=vr(t,"nebutra.config.ts");try{let o=(await Ht.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)+`
|
|
757
757
|
`),process.exit(c.SUCCESS)}s.info(`Features in current preset:
|
|
758
758
|
`),i.forEach(l=>{s.info(` \u2713 ${l}`)}),s.info(`
|
|
759
|
-
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
|
|
760
|
-
`),process.exit(c.SUCCESS)}
|
|
759
|
+
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 wc(e,t,n){(!e||!t)&&(s.error("Please specify two preset names to compare"),s.info("Available presets: "+Object.keys(ue).join(", ")),process.exit(c.INVALID_ARGS));let r=ue[e],o=ue[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(h=>!o.apps.includes(h)),appsOnly2:o.apps.filter(h=>!r.apps.includes(h)),appsCommon:r.apps.filter(h=>o.apps.includes(h)),featuresOnly1:r.features.filter(h=>!o.features.includes(h)),featuresOnly2:o.features.filter(h=>!r.features.includes(h)),featuresCommon:r.features.filter(h=>o.features.includes(h))}};process.stdout.write(JSON.stringify(p,null,2)+`
|
|
760
|
+
`),process.exit(c.SUCCESS)}_e.intro(w.bgCyan(w.black(" nebutra preset diff "))),s.info(`
|
|
761
761
|
${w.bold("Preset Comparison: "+e+" vs "+t)}
|
|
762
762
|
`);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)),g=r.features.filter(p=>o.features.includes(p));s.info(`
|
|
763
|
-
${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}`))),g.length>0&&(s.info(` ${w.green("Common")}:`),g.forEach(p=>s.info(` \u2713 ${p}`))),process.exit(c.SUCCESS)}function
|
|
764
|
-
`)}function
|
|
763
|
+
${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}`))),g.length>0&&(s.info(` ${w.green("Common")}:`),g.forEach(p=>s.info(` \u2713 ${p}`))),process.exit(c.SUCCESS)}function wr(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 gc({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 yc(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 hc(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 bc({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 vc({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 wc(n,r,{format:o.format,yes:a.yes||!1})})}import*as V from"@clack/prompts";import te from"picocolors";var Rr=Object.fromEntries(Object.entries(rn).map(([e,t])=>[e,t]));function Er(e){return typeof e=="boolean"?"boolean":"string"}function Sc(){let e=ln().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 Rc(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:Sc()}}function Ec(e){return{...e,extensions:{valueDomains:v,aliases:{storage:{supabase:"supabase-storage"},payment:{lemonsqueezy:"lemon"}}}}}function Cr(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:Er(n.default)})),examples:e.examples||[]};return e.name==="add"?Rc(t):e.name==="create"?Ec(t):t}function Cc(e){let n=(be.subcommands||[]).find(r=>r.name===e);return n?Cr(n):null}function kc(){return(be.options??[]).map(e=>({flags:e.flags,description:e.description,required:!1,type:Er(e.default)}))}function Oc(e){let t=be.subcommands||[];return{name:be.name,version:e,description:be.description,usage:be.usage||"nebutra [command] [options]",globalOptions:kc(),commands:t.map(Cr),exitCodes:Rr,timestamp:new Date().toISOString()}}function Sr(){return(be.subcommands||[]).map(t=>t.name)}function Et(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
764
|
+
`)}function xc(e){return e instanceof Error&&e.message.startsWith("EXIT:")}async function Ac(e,t){let n=t.quiet===!0,r="0.1.0";try{if(t.all===!0&&(Et(Oc(r)),process.exit(0)),t.list===!0&&(Et(Sr()),process.exit(0)),(t.exitCodes===!0||t["exit-codes"]===!0)&&(Et(Rr),process.exit(0)),e){let o=Cc(e);o||(n||(s.error(`Unknown command '${e}'`),s.info(`Available commands: ${Sr().join(", ")}`)),process.exit(c.NOT_FOUND)),Et(o),process.exit(0)}n||(V.intro(te.bgCyan(te.black(" nebutra schema "))),V.log.info(te.cyan("Agent-friendly introspection of CLI commands, arguments, and value domains.")),V.log.message(""),V.log.info(te.bold("Usage:")),V.log.message(te.dim(" nebutra schema --all Show full command tree as JSON")),V.log.message(te.dim(" nebutra schema <command> Show schema for a specific command")),V.log.message(te.dim(" nebutra schema --list List available command names")),V.log.message(te.dim(" nebutra schema --exit-codes Show exit codes reference")),V.log.message(""),V.log.info(te.bold("Example:")),V.log.message(te.dim(" nebutra schema add")),V.log.message(te.dim(" nebutra schema --all | jq '.commands[0]'")),V.outro(te.cyan("All output is JSON for easy parsing by agents and automation tools."))),process.exit(0)}catch(o){if(xc(o))throw o;n||s.error(o instanceof Error?o.message:String(o)),process.exit(c.ERROR)}}function kr(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 Ac(t,n)})}import*as Ct from"@clack/prompts";import ne from"picocolors";function Je(){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 _c(e){d("Detecting search provider...","info");let t=Je();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"?O(n,{format:"json"}):(d(`Search Provider: ${ne.cyan(t.type.toUpperCase())}`,"success"),d(`Status: ${t.health?ne.green("Healthy"):ne.red("Unhealthy")}`,t.health?"success":"error"),t.url&&d(`URL: ${t.url}`,"info"))}async function $c(e){d("Fetching search indexes...","info"),Je().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")O(n,{format:"json"});else{d("Search Indexes","info");for(let r of n)d(`${ne.cyan(r.name)}: ${r.documentCount} documents (${r.size})`,"info")}}async function Or(e,t){let n=t||{},r=Je();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 Ct.confirm({message:"Reindex ALL indexes? This will recreate all indexes from scratch.",initialValue:!1});(Ct.isCancel(i)||!i)&&(d("Reindex cancelled","warn"),process.exit(c.CANCELLED))}!n.yes&&!n.dryRun&&!e&&(d("Full reindex requires --yes confirmation","error"),process.exit(c.INVALID_ARGS));let a=e||"all";if(d(`Reindexing ${ne.cyan(a)}...`,"info"),n.dryRun){d(`Would reindex ${a} with provider ${r.type}`,"info");return}d(`Reindexing ${a} on ${r.type}...`,"info"),d(`Index ${ne.cyan(a)} reindexed successfully`,"success")}async function Pc(e,t,n){d(`Querying ${ne.cyan(e)} for "${ne.yellow(t)}"...`,"info");let r=Je();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")O(a,{format:"json"});else{d(`Found ${ne.cyan(String(o.length))} results for "${t}"`,"success");for(let i of o)d(`${ne.dim(String(i.score))}: ${i.title}`,"info")}}async function Ic(e){d("Fetching search statistics...","info");let t=Je();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")O(n,{format:"json"});else{d("Search Statistics","info"),d(`Provider: ${ne.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(` ${ne.cyan(r.name)}: ${r.documents.toLocaleString()} docs (${(r.size/1024/1024).toFixed(2)} MB)`,"info")}}function xr(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 _c(i);break;case"indexes":await $c(i);break;case"reindex":r.length>0?await Or(r[0],i):await Or(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 Pc(r[0],r.slice(1).join(" "),i);break;case"stats":await Ic(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"),A("Full error",{error:l}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
765
765
|
Examples:
|
|
766
766
|
nebutra search status Show search provider status
|
|
767
767
|
nebutra search indexes List all indexes
|
|
@@ -784,7 +784,7 @@ Flags:
|
|
|
784
784
|
--limit <n> Result limit for queries (default: 10)
|
|
785
785
|
--filters <json> Filters for search query
|
|
786
786
|
--tenant <id> Tenant ID for multi-tenant searches
|
|
787
|
-
`)}import{stdin as
|
|
787
|
+
`)}import{stdin as Xe}from"process";import*as kt from"@clack/prompts";import T from"picocolors";async function Nc(){return new Promise((e,t)=>{let n="";Xe.setEncoding("utf8"),Xe.on("readable",()=>{let r;for(;(r=Xe.read())!==null;)n+=r}),Xe.on("end",()=>{e(n.trim())}),Xe.on("error",t)})}async function Tc(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")O(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(`${T.cyan(n.key)}${T.gray(r)}`,"info"),d(` Updated: ${n.updatedAt}`,"info")}}}async function jc(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 Nc()}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 ${T.cyan(e)}...`,"info"),t.dryRun){d(`Would encrypt and store secret ${e} for tenant ${t.tenant||"org_123"}`,"info");return}d(`Secret ${T.cyan(e)} encrypted and stored successfully`,"success"),t.description&&d(`Description: ${t.description}`,"info")}async function Uc(e,t){e||(d("Secret key is required: nebutra secrets get <key>","error"),process.exit(c.INVALID_ARGS)),d(`Retrieving secret ${T.cyan(e)}...`,"info");let r=t.unmask?"sk_live_abc123xyz789...":"***masked***";t.unmask||d(`Secret value is ${T.yellow("masked")}. Use --unmask to display.`,"warn");let o={key:e,tenant:t.tenant||"org_123",value:r,encrypted:!0};t.format==="json"?O(o,{format:"json"}):d(`${T.cyan(e)}: ${r}`,"info")}async function Lc(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 kt.confirm({message:`Rotate encryption key for ${T.cyan(e)}? This will re-encrypt with new key material.`,initialValue:!1});(kt.isCancel(r)||!r)&&(d("Rotation cancelled","warn"),process.exit(c.CANCELLED))}if(!t.yes&&!t.dryRun&&!n&&(d("Key rotation requires --yes confirmation","error"),process.exit(c.INVALID_ARGS)),d(`Rotating encryption key for ${T.cyan(e)}...`,"info"),t.dryRun){d(`Would rotate encryption key for ${e}`,"info");return}d(`Encryption key for ${T.cyan(e)} rotated successfully`,"success"),d(`New key version: ${T.dim("v2")}`,"info")}async function Dc(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")O(n,{format:"json"});else{d("Secret Access Audit Log","info");for(let r of n){let o=r.success?T.green("\u2713"):T.red("\u2716");d(`${o} ${r.timestamp} ${T.cyan(r.action)} ${r.key}`,r.success?"success":"error"),d(` Actor: ${r.actor}${r.message?` \u2014 ${r.message}`:""}`,"info")}}}async function Fc(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")O(t,{format:"json"});else{d("Vault Configuration Check","info");for(let r of t){let o=r.healthy?T.green("\u2713"):T.red("\u2716");d(`${o} ${r.name}: ${r.message}`,r.healthy?"success":"error")}let n=t.every(r=>r.healthy);d(`Overall: ${n?T.green("HEALTHY"):T.red("UNHEALTHY")}`,n?"success":"error")}}function Ar(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 Tc(i);break;case"set":r.length===0&&(d("set requires a secret key: nebutra secrets set <key>","error"),process.exit(c.INVALID_ARGS)),await jc(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 Uc(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 Lc(r[0],i);break;case"audit":await Dc(i);break;case"verify":await Fc(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"),A("Full error",{error:l}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
788
788
|
Examples:
|
|
789
789
|
nebutra secrets list List all secrets (metadata only)
|
|
790
790
|
nebutra secrets list --tenant org_456 List secrets for specific tenant
|
|
@@ -819,7 +819,7 @@ Security Notes:
|
|
|
819
819
|
- Never use --value in production (visible in shell history)
|
|
820
820
|
- Always use --unmask when viewing secrets in scripts
|
|
821
821
|
- Audit log tracks all access and modifications
|
|
822
|
-
`)}import*as
|
|
822
|
+
`)}import*as Ot from"@clack/prompts";import K from"picocolors";function _r(e){switch(e.toLowerCase()){case"running":return"running";case"exited":return"exited";case"restarting":return"restarting";case"paused":return"paused";default:return"exited"}}function Gc(e){switch(e){case"running":return K.green("\u25CF");case"exited":return K.red("\u25CF");case"restarting":return K.yellow("\u25CF");case"paused":return K.gray("\u25CF");default:return K.gray("\u25CF")}}async function Mc(e){d("Checking service status...","info");let t=await $({command:"docker",args:["compose","ps","--format","json"],cwd:b(),interactive:!1,label:"Docker Compose PS",dryRun:e.dryRun});t.exitCode!==0&&(d("Failed to fetch service status","error"),A("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:_r(o.State),exitCode:o.ExitCode}));O(r,{format:"json"})}else{d("Service Status","info");let r=n.map(o=>({Service:Gc(_r(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"),A("Parse error",{error:String(n)}),process.exit(c.ERROR)}}async function Vc(e,t){d(`Streaming logs for ${K.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 $({command:"docker",args:n,cwd:b(),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 Kc(e,t){let n=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!t.yes&&!t.dryRun&&n){let a=await Ot.confirm({message:`Restart service ${K.cyan(e)}?`,initialValue:!1});(Ot.isCancel(a)||!a)&&(d("Restart cancelled","warn"),process.exit(c.CANCELLED))}d(`Restarting service ${K.cyan(e)}...`,"info");let r=["compose","restart"];t.timeout&&r.push("--timeout",String(t.timeout)),r.push(e);let o=await $({command:"docker",args:r,cwd:b(),interactive:!1,label:`Docker Compose Restart (${e})`,dryRun:t.dryRun});o.exitCode===0?d(`Service ${K.cyan(e)} restarted successfully`,"success"):(d(`Failed to restart service ${e}`,"error"),A("Docker error",{stderr:o.stderr}),process.exit(c.ERROR))}async function Bc(e){d("Running deep health check...","info");let t=[],n=await $({command:"docker",args:["compose","exec","-T","postgres","pg_isready"],cwd:b(),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 $({command:"docker",args:["compose","exec","-T","redis","redis-cli","ping"],cwd:b(),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 $({command:"docker",args:["compose","exec","-T","clickhouse","curl","-s","http://localhost:8123/ping"],cwd:b(),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 $({command:"docker",args:["compose","exec","-T","meilisearch","curl","-s","http://localhost:7700/health"],cwd:b(),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")O(t,{format:"json"});else{for(let l of t){let u=l.healthy?K.green("\u2713"):K.red("\u2716");d(`${u} ${l.service}: ${l.message}`,l.healthy?"success":"error")}let i=t.every(l=>l.healthy);d(`Overall health: ${i?K.green("HEALTHY"):K.red("UNHEALTHY")}`,i?"success":"error")}}async function qc(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 ${K.cyan(e)} to ${K.cyan(String(r))} replicas...`,"info");let o=await $({command:"docker",args:["compose","up","-d","--scale",`${e}=${r}`],cwd:b(),interactive:!1,label:`Docker Compose Scale (${e})`,dryRun:n.dryRun});o.exitCode===0?d(`Service ${K.cyan(e)} scaled to ${r} replicas`,"success"):(d(`Failed to scale service ${e}`,"error"),A("Docker error",{stderr:o.stderr}),process.exit(c.ERROR))}function $r(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 Mc(i);break;case"logs":r.length===0&&(d("logs requires a service name: nebutra services logs <service>","error"),process.exit(c.INVALID_ARGS)),await Vc(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 Kc(r[0],i);break;case"health":await Bc(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 qc(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"),A("Full error",{error:l}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
823
823
|
Examples:
|
|
824
824
|
nebutra services status Show all service status
|
|
825
825
|
nebutra services logs postgres Stream postgres logs
|
|
@@ -840,15 +840,15 @@ Flags:
|
|
|
840
840
|
--tail <n> Log lines to show (default: 100)
|
|
841
841
|
--since <duration> Show logs since (e.g., 10m, 1h)
|
|
842
842
|
--timeout <seconds> Timeout for restart
|
|
843
|
-
`)}import{existsSync as
|
|
844
|
-
${
|
|
845
|
-
`),r.forEach(a=>{console.log(` ${
|
|
846
|
-
${
|
|
847
|
-
`),r.forEach(a=>{let i=a.status==="ready"?
|
|
848
|
-
${n}`)}return t}async function
|
|
849
|
-
`)}function
|
|
850
|
-
`)}function
|
|
851
|
-
`),t?console.log(JSON.stringify({command:"unlink",success:!0,configPath:r},null,2)):s.success("Unlinked from project")}function
|
|
843
|
+
`)}import{existsSync as Z,readdirSync as Qe,readFileSync as Pr}from"fs";import{resolve as B}from"path";import De from"picocolors";function $e(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 Ir(e){try{let t=Pr(B(e,"package.json"),"utf-8");return JSON.parse(t)}catch{return{}}}function Nr(e){try{return Qe(e,{recursive:!0}).length}catch{return 0}}async function Wc(e){s.info("Gathering monorepo statistics...");let t=b(),n=B(t,"packages"),r=B(t,"apps"),o=[],a=[],i=0,l="unknown";try{Z(n)&&(o=Qe(n).filter(g=>!g.startsWith(".")&&Z(B(n,g,"package.json")))),Z(r)&&(a=Qe(r).filter(g=>!g.startsWith(".")&&Z(B(r,g,"package.json"))));let m=B(t,"nebutra.config.ts");if(Z(m)){let p=Pr(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}`)),$e(u,e.format,"stats")}async function Yc(e){s.info("Analyzing bundle and dependency sizes...");let t=await $({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."),$e(n,e.format,"stats:size")}async function Hc(e){s.info("Analyzing dependencies...");let t=await $({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")),$e(n,e.format,"stats:deps")}async function zc(e){s.info("Listing packages...");let t=b(),n=B(t,"packages"),r=[];try{Z(n)&&(r=Qe(n).filter(i=>!i.startsWith(".")&&Z(B(n,i,"package.json"))).map(i=>{let l=B(n,i),u=Ir(l);return{name:u.name||i,version:u.version,description:u.description,files:Nr(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"?$e(o,e.format,"stats:packages"):(console.log(`
|
|
844
|
+
${De.cyan("Packages")} (${r.length} total)
|
|
845
|
+
`),r.forEach(a=>{console.log(` ${De.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 Jc(e){s.info("Listing apps...");let t=b(),n=B(t,"apps"),r=[];try{Z(n)&&(r=Qe(n).filter(i=>!i.startsWith(".")&&Z(B(n,i,"package.json"))).map(i=>{let l=B(n,i),u=Ir(l),m=Z(B(l,"next.config.ts"))||Z(B(l,"next.config.js")),g=Z(B(l,"server.ts"))||Z(B(l,"server.js"));return{name:u.name||i,version:u.version,description:u.description,files:Nr(l),status:m||g?"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"?$e(o,e.format,"stats:apps"):(console.log(`
|
|
846
|
+
${De.cyan("Apps")} (${r.length} total, ${o.ready} ready)
|
|
847
|
+
`),r.forEach(a=>{let i=a.status==="ready"?De.green("\u2713"):De.gray("\u25CB");console.log(` ${i} ${De.blue(a.name)}`),a.version&&console.log(` version: ${a.version}`),a.description&&console.log(` ${a.description}`),console.log(` files: ${a.files}`)}),console.log(""))}function Tr(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 Wc({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 Yc({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 Hc({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 zc({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 Jc({format:n.format||r.format})})}import*as q from"@clack/prompts";import j from"picocolors";var zt=["landing-page","web","storybook","backends/gateway","design-docs","studio","docs","ui","tokens","brand","theme","icons","preset","queue","search","notifications","permissions","webhooks","metering","uploads","vault","tenant"];function Xc(e){let t=`@nebutra/${e}`;if(!zt.includes(e)){let n=j.yellow(`Valid apps: ${zt.join(", ")}`);throw new Error(`Invalid app: ${j.red(e)}
|
|
848
|
+
${n}`)}return t}async function Qc(e){e.dryRun&&(x({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"},{format:e.format}),process.exit(c.DRY_RUN_OK));try{if(e.app&&(e.watch||e.coverage)&&(q.log.error(j.red("Cannot use --watch or --coverage with --app filter (app-level tests use turbo)")),process.exit(c.INVALID_ARGS)),e.app){Xc(e.app);let r=`@nebutra/${e.app}`;e.quiet||q.log.info(j.cyan(`Running tests for ${j.bold(r)}...`));let o=await ye("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...";q.log.info(j.cyan(r))}let n=await N(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?q.log.error(j.red(t.message)):q.log.error(j.red("Unknown error occurred")),process.exit(c.ERROR)}}async function jr(e){e.dryRun&&(x({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"},{format:e.format}),process.exit(c.DRY_RUN_OK));try{(e.ui&&e.ci||e.ui&&e.ci)&&(q.log.error(j.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...";q.log.info(j.cyan(r))}let n=await N(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?q.log.error(j.red(t.message)):q.log.error(j.red("Unknown error occurred")),process.exit(c.ERROR)}}async function Ur(e){e.dryRun&&(x({mode:"dry-run",timestamp:new Date().toISOString(),command:"test size",options:{why:e.why},task:e.why?"pnpm size:why":"pnpm size"},{format:e.format}),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...";q.log.info(j.cyan(r))}let n=await N(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?q.log.error(j.red(t.message)):q.log.error(j.red("Unknown error occurred")),process.exit(c.ERROR)}}async function Lr(e){e.dryRun&&(x({mode:"dry-run",timestamp:new Date().toISOString(),command:"test arch",task:"pnpm test:arch"},{format:e.format}),process.exit(c.DRY_RUN_OK));try{e.quiet||q.log.info(j.cyan("Running architecture tests..."));let t=await N("test:arch",{interactive:!0});process.exit(t.exitCode)}catch(t){t instanceof Error?q.log.error(j.red(t.message)):q.log.error(j.red("Unknown error occurred")),process.exit(c.ERROR)}}function Dr(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: ${zt.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 jr({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 Ur({why:r.why||!1,dryRun:r.dryRun||!1,quiet:o.quiet||!1,verbose:o.verbose||!1,format:o.format}):n==="arch"?await Lr({dryRun:r.dryRun||!1,quiet:o.quiet||!1,verbose:o.verbose||!1,format:o.format}):await Qc({...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 jr({...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 Ur({...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 Lr({dryRun:n.dryRun||!1,quiet:r.quiet||!1,verbose:r.verbose||!1,format:r.format})})}var Fr={$schema:"https://nebutra.com/schemas/theme-registry.json",version:"2026.05",defaultTheme:"neon",themes:[{id:"neon",name:"Neon",category:"ai-native",mood:"AI SaaS default - dark, electric, high contrast",tokenPath:"tokens/themes/neon.json",install:{command:"nebutra theme add neon",registryUrl:"https://ui.nebutra.com/themes/neon.json"},compatibility:{tailwind:"4",cssVariables:!0,figmaVariables:!0,shadcnRegistry:!0},governance:{wcag:"AA",requiredTokens:["color.primary","color.primary-foreground","color.background","color.foreground","color.card","color.border","color.ring","radius.md","fontFamily.sans"],visualSuites:["forms","pricing","dashboard","ai-chat","charts"]}},{id:"gradient",name:"Gradient",category:"marketing",mood:"Growth and marketing - bright, polished, conversion-oriented",tokenPath:"tokens/themes/gradient.json",install:{command:"nebutra theme add gradient",registryUrl:"https://ui.nebutra.com/themes/gradient.json"},compatibility:{tailwind:"4",cssVariables:!0,figmaVariables:!0,shadcnRegistry:!0},governance:{wcag:"AA",requiredTokens:["color.primary","color.primary-foreground","color.background","color.foreground","color.card","color.border","color.ring","radius.md","fontFamily.sans"],visualSuites:["forms","pricing","dashboard","ai-chat","charts"]}},{id:"dark-dense",name:"Dark Dense",category:"pro-tools",mood:"Dense operator surfaces - dark, compact, low distraction",tokenPath:"tokens/themes/dark-dense.json",install:{command:"nebutra theme add dark-dense",registryUrl:"https://ui.nebutra.com/themes/dark-dense.json"},compatibility:{tailwind:"4",cssVariables:!0,figmaVariables:!0,shadcnRegistry:!0},governance:{wcag:"AA",requiredTokens:["color.primary","color.primary-foreground","color.background","color.foreground","color.card","color.border","color.ring","radius.md","fontFamily.sans"],visualSuites:["forms","pricing","dashboard","ai-chat","charts"]}},{id:"minimal",name:"Minimal",category:"productivity",mood:"Document and workspace apps - neutral, calm, readable",tokenPath:"tokens/themes/minimal.json",install:{command:"nebutra theme add minimal",registryUrl:"https://ui.nebutra.com/themes/minimal.json"},compatibility:{tailwind:"4",cssVariables:!0,figmaVariables:!0,shadcnRegistry:!0},governance:{wcag:"AA",requiredTokens:["color.primary","color.primary-foreground","color.background","color.foreground","color.card","color.border","color.ring","radius.md","fontFamily.sans"],visualSuites:["forms","pricing","dashboard","ai-chat","charts"]}},{id:"vibrant",name:"Vibrant",category:"creative",mood:"Creator and consumer surfaces - saturated, expressive, energetic",tokenPath:"tokens/themes/vibrant.json",install:{command:"nebutra theme add vibrant",registryUrl:"https://ui.nebutra.com/themes/vibrant.json"},compatibility:{tailwind:"4",cssVariables:!0,figmaVariables:!0,shadcnRegistry:!0},governance:{wcag:"AA",requiredTokens:["color.primary","color.primary-foreground","color.background","color.foreground","color.card","color.border","color.ring","radius.md","fontFamily.sans"],visualSuites:["forms","pricing","dashboard","ai-chat","charts"]}},{id:"ocean",name:"Ocean",category:"enterprise",mood:"B2B infrastructure and finance - cool, stable, trustworthy",tokenPath:"tokens/themes/ocean.json",install:{command:"nebutra theme add ocean",registryUrl:"https://ui.nebutra.com/themes/ocean.json"},compatibility:{tailwind:"4",cssVariables:!0,figmaVariables:!0,shadcnRegistry:!0},governance:{wcag:"AA",requiredTokens:["color.primary","color.primary-foreground","color.background","color.foreground","color.card","color.border","color.ring","radius.md","fontFamily.sans"],visualSuites:["forms","pricing","dashboard","ai-chat","charts"]}}]};var Re=Fr,kp=Re.themes.map(e=>e.id);var Op=Re.defaultTheme;function Gr(e){return Re.themes.find(t=>t.id===e)}function Mr(e){let t=e.opts?e.opts():e,n=e.optsWithGlobals?e.optsWithGlobals():e;return t.format??n.format??el()}function el(){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 tl(e){return{id:e.id,name:e.name,category:e.category,mood:e.mood,tokenPath:e.tokenPath,installCommand:e.install.command,registryUrl:e.install.registryUrl,wcag:e.governance.wcag,visualSuites:e.governance.visualSuites}}function nl(e="table"){let t=Re.themes.map(tl);return e==="json"?JSON.stringify({version:Re.version,defaultTheme:Re.defaultTheme,count:t.length,themes:t},null,2):[`Nebutra themes (${t.length})`,`Default: ${Re.defaultTheme}`,"",...t.map(r=>`${r.id.padEnd(12)} ${r.category.padEnd(14)} ${r.tokenPath.padEnd(34)} ${r.mood}`)].join(`
|
|
849
|
+
`)}function rl(e,t="table"){let n=Gr(e);if(n)return t==="json"?JSON.stringify(n,null,2):[`${n.name} (${n.id})`,`Category: ${n.category}`,`Mood: ${n.mood}`,`Token path: ${n.tokenPath}`,`Install: ${n.install.command}`,`Registry: ${n.install.registryUrl}`,`WCAG: ${n.governance.wcag}`,`Visual suites: ${n.governance.visualSuites.join(", ")}`,`Required tokens: ${n.governance.requiredTokens.join(", ")}`].join(`
|
|
850
|
+
`)}function Vr(e){let t=e.command("theme").description("Inspect registry-backed Nebutra themes and governance metadata");t.command("list").description("List built-in themes from the shared theme registry").option("--format <type>","Output format: json or table").action(n=>{console.log(nl(Mr(n)))}),t.command("inspect <id>").description("Show governance metadata for one theme").option("--format <type>","Output format: json or table").action((n,r)=>{let o=rl(n,Mr(r));o||(s.error(`Theme '${n}' was not found in the Nebutra theme registry.`),process.exit(c.NOT_FOUND)),console.log(o)})}import{existsSync as Kr,readFileSync as ol,unlinkSync as sl,writeFileSync as al}from"fs";import{dirname as il,join as Br,parse as cl}from"path";function ll(e){let t=e,{root:n}=cl(t);for(;;){if(Kr(Br(t,"package.json")))return t;if(t===n)return null;let r=il(t);if(r===t)return null;t=r}}async function dl(e){let t=e.format==="json",n=ll(process.cwd())??process.cwd(),r=Br(n,"nebutra.config.json");if(Kr(r)||(t?console.log(JSON.stringify({command:"unlink",success:!0,alreadyUnlinked:!0},null,2)):s.info("Already unlinked"),process.exit(c.SUCCESS)),e.force){try{sl(r)}catch(l){let u=l instanceof Error?l.message:String(l);t?console.log(JSON.stringify({command:"unlink",success:!1,error:u},null,2)):s.error(`Failed to delete config: ${u}`),process.exit(c.PERMISSION_DENIED)}t?console.log(JSON.stringify({command:"unlink",success:!0,deletedConfig:!0,configPath:r},null,2)):s.success("Removed nebutra.config.json");return}let o={};try{o=JSON.parse(ol(r,"utf-8"))}catch{o={}}"projectId"in o||(t?console.log(JSON.stringify({command:"unlink",success:!0,alreadyUnlinked:!0},null,2)):s.info("Already unlinked"),process.exit(c.SUCCESS));let{projectId:a,...i}=o;al(r,JSON.stringify(i,null,2)+`
|
|
851
|
+
`),t?console.log(JSON.stringify({command:"unlink",success:!0,configPath:r},null,2)):s.success("Unlinked from project")}function qr(e){e.command("unlink").description("Remove the project link from nebutra.config.json").option("--force","Delete the entire nebutra.config.json").option("--format <type>","Output format: json or plain").action(async(t,n)=>{let r=n?.optsWithGlobals?.()||t;await dl({force:t.force||!1,format:t.format||r.format})})}import{spawnSync as wl}from"child_process";import{promises as xt}from"fs";import{homedir as ul}from"os";import{join as Yr}from"path";import Ee from"picocolors";var ml=1440*60*1e3,Hr=Yr(ul(),".nebutra"),Jt=Yr(Hr,"update-check.json"),pl="https://registry.npmjs.org/nebutra";async function fl(){try{await xt.mkdir(Hr,{recursive:!0})}catch{}}async function gl(){try{let e=await xt.readFile(Jt,"utf-8"),t=JSON.parse(e);return Date.now()-t.timestamp<ml?t:(await xt.unlink(Jt).catch(r=>{s.debug("Failed to remove expired update cache file",{error:r})}),null)}catch{return null}}async function yl(e){try{await fl();let t={timestamp:Date.now(),latestVersion:e};await xt.writeFile(Jt,JSON.stringify(t,null,2),"utf-8")}catch{}}async function hl(){try{let e=new AbortController,t=setTimeout(()=>e.abort(),5e3),n=await fetch(pl,{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 Wr(e){return e.split(".").map(t=>parseInt(t.replace(/[^0-9]/g,""),10)||0).slice(0,3)}function bl(e,t){let n=Wr(e),r=Wr(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 Xt(e){let t=await gl(),n=null;return t?n=t.latestVersion:(n=await hl(),n&&await yl(n)),!n||!bl(e,n)?null:{currentVersion:e,latestVersion:n,isUpdate:!0}}function vl(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=[Ee.cyan("\u256D"+"\u2500".repeat(41)+"\u256E"),Ee.cyan("\u2502")+Ee.yellow(o+n+o)+Ee.cyan("\u2502"),Ee.cyan("\u2502")+Ee.gray(a+r+a)+Ee.cyan("\u2502"),Ee.cyan("\u2570"+"\u2500".repeat(41)+"\u256F")]}async function zr(e){let t=null;return Xt(e).then(n=>{t=n}),async()=>{!process.stdout.isTTY||process.env.CI||(t||await new Promise(n=>setTimeout(n,100)),t&&vl(t))}}function Sl(){let t=(process.argv[1]||"").toLowerCase();if(t.includes("/homebrew/")||t.includes("/cellar/"))return"brew";if(t.includes("/.bun/")||t.includes("/bun/"))return"bun";if(t.includes("/pnpm/")||t.includes("/.pnpm/"))return"pnpm";if(t.includes("/yarn/")||t.includes("/.yarn/"))return"yarn";if(t.includes("/npm/")||t.includes("/node_modules/"))return"npm";let n=process.env.npm_config_user_agent||"";return n.startsWith("pnpm")?"pnpm":n.startsWith("yarn")?"yarn":n.startsWith("bun")?"bun":n.startsWith("npm")?"npm":"unknown"}function Rl(e){switch(e){case"pnpm":return"pnpm add -g nebutra@latest";case"yarn":return"yarn global add nebutra@latest";case"bun":return"bun add -g nebutra@latest";case"brew":return"brew upgrade nebutra";case"npm":return"npm i -g nebutra@latest";default:return"npm i -g nebutra@latest"}}var Pe="0.1.0";async function El(e){let t=Sl(),n=Rl(t),r=e.format==="json",o=await Xt(Pe),a=o?.latestVersion??null,i=!!o?.isUpdate;e.dryRun&&(r?console.log(JSON.stringify({command:"upgrade",dryRun:!0,installer:t,command_to_run:n,currentVersion:Pe,latestVersion:a,hasUpdate:i},null,2)):(s.info(`Detected installer: ${t}`),s.info(`Current: ${Pe}${a?` \u2192 Latest: ${a}`:""}`),s.info(`Upgrade command: ${n}`)),process.exit(c.DRY_RUN_OK)),!i&&a&&(r?console.log(JSON.stringify({command:"upgrade",success:!0,alreadyLatest:!0,version:Pe},null,2)):s.success(`Already on the latest version (${Pe})`),process.exit(c.SUCCESS)),e.yes||(r?console.log(JSON.stringify({command:"upgrade",success:!0,installer:t,command_to_run:n,currentVersion:Pe,latestVersion:a,requiresConfirmation:!0},null,2)):(s.info(`Current: ${Pe}${a?` \u2192 Latest: ${a}`:""}`),s.info("To upgrade, run:"),s.info(` ${n}`),s.info("Or re-run this command with --yes to execute it now.")),process.exit(c.SUCCESS));let[l,...u]=n.split(" ");r||s.info(`Running: ${n}`);let m=wl(l,u,{stdio:r?"pipe":"inherit"});m.status===0&&(r?console.log(JSON.stringify({command:"upgrade",success:!0,installer:t,ran:n},null,2)):s.success("Upgrade complete"),process.exit(c.SUCCESS));let g=m.stderr?.toString()||`Upgrade failed (exit ${m.status})`;r?console.log(JSON.stringify({command:"upgrade",success:!1,installer:t,ran:n,error:g},null,2)):s.error(g),process.exit(c.ERROR)}function Jr(e){let t=(n,r)=>{let o=r?.optsWithGlobals?.()||n;return El({yes:n.yes||o.yes||!1,dryRun:n.dryRun||!1,format:n.format||o.format})};e.command("upgrade").alias("self-update").description("Upgrade the nebutra CLI to the latest version").option("--yes","Execute the upgrade command without confirmation").option("--dry-run","Show the upgrade command without executing (exit 10)").option("--format <type>","Output format: json or plain").action(t)}import Qt from"fs";import Zt from"path";import Cl from"picocolors";var en=["inngest","n8n","pusher"],kl=`import { Inngest } from "inngest";
|
|
852
852
|
|
|
853
853
|
export const inngest = new Inngest({ id: "nebutra-sailor" });
|
|
854
854
|
|
|
@@ -862,7 +862,7 @@ export const exampleFn = inngest.createFunction(
|
|
|
862
862
|
return { status: "completed" };
|
|
863
863
|
},
|
|
864
864
|
);
|
|
865
|
-
`,
|
|
865
|
+
`,Ol=`# n8n Workflows (Self-hosted Convention)
|
|
866
866
|
|
|
867
867
|
Nebutra Sailor uses n8n in self-hosted mode for visual workflow orchestration.
|
|
868
868
|
|
|
@@ -886,7 +886,7 @@ docker run -it --rm \\
|
|
|
886
886
|
## Webhook endpoints
|
|
887
887
|
|
|
888
888
|
Production webhooks route through \`backends/gateway\` (see \`packages/integrations/webhooks\`).
|
|
889
|
-
`,
|
|
889
|
+
`,xl=`{
|
|
890
890
|
"name": "example-workflow",
|
|
891
891
|
"nodes": [
|
|
892
892
|
{
|
|
@@ -902,7 +902,7 @@ Production webhooks route through \`backends/gateway\` (see \`packages/integrati
|
|
|
902
902
|
"settings": {},
|
|
903
903
|
"tags": []
|
|
904
904
|
}
|
|
905
|
-
`,
|
|
905
|
+
`,Al=`import Pusher from "pusher";
|
|
906
906
|
|
|
907
907
|
export const pusher = new Pusher({
|
|
908
908
|
appId: process.env.PUSHER_APP_ID ?? "",
|
|
@@ -918,8 +918,8 @@ export async function publishExample(channel: string, event: string, data: unkno
|
|
|
918
918
|
}
|
|
919
919
|
return pusher.trigger(channel, event, data);
|
|
920
920
|
}
|
|
921
|
-
`;function
|
|
922
|
-
`))}catch{}try{
|
|
921
|
+
`;function _l(e){switch(e){case"inngest":return[{relPath:"workflows/inngest/example.ts",content:kl}];case"n8n":return[{relPath:"workflows/n8n/README.md",content:Ol},{relPath:"workflows/n8n/example.json",content:xl}];case"pusher":return[{relPath:"workflows/pusher/example.ts",content:Al}]}}async function $l(e,t={}){en.includes(e)||(s.error(`Invalid provider: ${Cl.red(e)}. Valid: ${en.join(", ")}`),process.exit(c.INVALID_ARGS));let n=b(),r=_l(e);t.dryRun&&(x({mode:"dry-run",command:`workflow init ${e}`,files:r.map(i=>({path:Zt.join(n,i.relPath)}))}),process.exit(c.DRY_RUN_OK));let o=0,a=0;for(let i of r){let l=Zt.join(n,i.relPath);if(Qt.existsSync(l)){s.warn(`Skipping ${i.relPath} \u2014 already exists.`),a++;continue}Qt.mkdirSync(Zt.dirname(l),{recursive:!0}),Qt.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 Xr(e){e.command("workflow").description("Manage workflow provider scaffolding (inngest | n8n | pusher)").command("init <provider>").description(`Scaffold a starter workflow file for a provider (${en.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 $l(n,{dryRun:r.dryRun||!1,quiet:o.quiet||!1})})}import{existsSync as Pl,mkdirSync as Qr,writeFileSync as Zr}from"fs";import{homedir as eo,platform as Il}from"os";import{join as Ze}from"path";function Nl(){if(Il()==="win32"){let t=process.env.LOCALAPPDATA||Ze(eo(),"AppData","Local");return Ze(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?Ze(e,"nebutra"):Ze(eo(),".config","nebutra")}function Tl(){let e=process.env.NEBUTRA_TELEMETRY;return e==="0"||e==="false"||e==="no"}function to(){if(!process.stderr.isTTY||!process.stdin.isTTY||process.env.CI)return;let e=Nl(),t=Ze(e,"first-run-acked");try{if(Pl(t))return}catch{return}if(Tl()){try{Qr(e,{recursive:!0}),Zr(t,"")}catch{}return}let n=["\x1B[36m\u2139\x1B[0m Nebutra collects anonymous usage analytics to improve the CLI."," Opt out at any time: export NEBUTRA_TELEMETRY=0"," Privacy policy: https://nebutra.com/legal/cli-analytics",""];try{process.stderr.write(n.join(`
|
|
922
|
+
`))}catch{}try{Qr(e,{recursive:!0}),Zr(t,"")}catch{}}var Gl=Ll(Ul(Dl(import.meta.url)),"..","package.json"),no=JSON.parse(jl(Gl,"utf8")).version;async function Ml(){to();let e=await zr(no),t=new Fl,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(no).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 ir({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 hn(r,{...o,yes:a.yes||!n})}),Vn(t),br(t),kr(t),Tn(t),sr(t),ar(t),Xn(t),ur(t),On(t),xn(t),In(t),Tr(t),Kn(t),rr(t),wr(t),Bn(t),Dr(t),Xr(t),Pn(t),Yn(t),Vr(t),Sn(t),jn(t),or(t),zn(t),$r(t),xr(t),Ar(t),Mn(t),hr(t),Jr(t),fr(t),qr(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.");let h=i.join(u,"apps/api-gateway");a.existsSync(h)&&r.warn("Legacy `apps/api-gateway/` present. Expected `backends/gateway/` \u2014 see CLAUDE.md for the W2.x restructure.");let F=i.join(u,"backends/gateway");a.existsSync(F)?r.success("backends/gateway/ present"):a.existsSync(i.join(u,"backends"));let G=i.join(u,"packages");if(a.existsSync(G)){let ie=new Set(["design","iam","commerce","integrations","platform","ops","ai"]);try{let Ce=a.readdirSync(G,{withFileTypes:!0}),f=[];for(let M of Ce){if(!M.isDirectory()||ie.has(M.name))continue;let Ie=i.join(G,M.name,"package.json");a.existsSync(Ie)&&f.push(M.name)}f.length>0?r.warn(`Found ${f.length} uncategorized package(s) under packages/: ${f.join(", ")}. Expected packages/<category>/<name>/ \u2014 see CLAUDE.md.`):r.success("Packages follow categorized layout")}catch{}}let W=i.join(u,".nebutra"),me=i.join(W,"scaffold-meta.json");a.existsSync(me)?r.success(".nebutra/scaffold-meta.json present (CLI-scaffolded project)"):a.existsSync(W)&&r.warn(".nebutra/ directory exists but scaffold-meta.json is missing. Run `nebutra license verify` for details."),l?(r.error("Doctor found critical issues that require your attention."),process.exit(c.CONFIG_ERROR)):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",`
|
|
923
923
|
Examples:
|
|
924
924
|
$ nebutra init Initialize a new project
|
|
925
925
|
$ nebutra add cache --provider upstash-redis --yes Install a registry feature
|
|
@@ -967,4 +967,4 @@ Environment:
|
|
|
967
967
|
NEBUTRA_OUTPUT_FORMAT Output format (json|table|plain)
|
|
968
968
|
NO_COLOR Disable colored output
|
|
969
969
|
CI Auto-enable non-interactive mode
|
|
970
|
-
`),process.argv.length<=2&&(t.outputHelp(),process.exit(c.SUCCESS)),await t.parseAsync(process.argv),await e()}
|
|
970
|
+
`),process.argv.length<=2&&(t.outputHelp(),process.exit(c.SUCCESS)),await t.parseAsync(process.argv),await e()}Ml().catch(e=>s.error(e instanceof Error?e.message:String(e)));
|