nebutra 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +80 -73
  2. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import{b as c}from"./chunk-JDX5NXDX.js";import{a as l,b as ln,c as Ye,d as un,e as ie,f as St,h as dn,i as y,j as E,k as j,l as ae,m as ge,n as ee}from"./chunk-DTSPVLT5.js";import{realpathSync as vo}from"fs";import{resolve as ko}from"path";import{fileURLToPath as wo}from"url";import{Command as Uc}from"commander";import{existsSync as Co,readFileSync as So,writeFileSync as Eo}from"fs";import{resolve as Et}from"path";var Ro=["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"],pn="nebutra.config.json",Oo=Ro,mn={"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 J(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 Ao(e){return e.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function _o(e){return mn[e]?mn[e]:[`${e.replace(/-/g,"_").toUpperCase()}_API_KEY`]}function Je(){return Object.fromEntries(Oo.map(e=>[e,{id:e,name:Ao(e),envVars:_o(e)}]))}function H(){return Et(process.cwd(),pn)}function fe(){let e=H();if(!Co(e))return{$schema:"https://nebutra.com/schema.json"};try{return JSON.parse(So(e,"utf-8"))}catch(t){c.error(`Failed to parse ${pn}: ${t instanceof Error?t.message:String(t)}`),process.exit(l.ERROR)}}function qe(e){Eo(H(),`${JSON.stringify(e,null,2)}
3
- `)}function se(e){return e.ai={providers:{},routes:{},...e.ai},e.ai.providers={...e.ai.providers},e.ai.routes={...e.ai.routes},e.ai}function gn(e){let t=Je(),n=[...new Set(e.map(o=>o.toLowerCase()))],r=n.filter(o=>!t[o]);return r.length>0&&(c.error(`Unknown AI provider(s): ${r.join(", ")}. Valid providers: ${Object.keys(t).join(", ")}`),process.exit(l.INVALID_ARGS)),n}function Io(e){let t=e?.split(",").map(r=>r.trim()).filter(Boolean)??[];t.length===0&&(c.error("Route fallback cannot be empty. Use --fallback provider:model,provider:model"),process.exit(l.INVALID_ARGS));let n=t.filter(r=>!/^[a-z0-9-]+:[^,\s]+$/i.test(r));return n.length>0&&(c.error(`Invalid fallback target(s): ${n.join(", ")}. Expected provider:model.`),process.exit(l.INVALID_ARGS)),t}function fn(e){let t=se(e),n=t.providers??{},r=t.routes??{},o=Object.entries(n).filter(([,i])=>i.enabled).map(([i])=>i);return{path:H(),gateway:t.gateway??null,providers:n,enabledProviders:o,routes:r}}function Y(e,t){let n=typeof e.opts=="function"?e.opts():e,r=typeof t?.optsWithGlobals=="function"?t.optsWithGlobals():typeof e.optsWithGlobals=="function"?e.optsWithGlobals():n,o=n.format??r.format??Po();return{...r,...n,...o?{format:o}:{}}}function Po(){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 je(e){return e?e.length<=8?"***":`${e.substring(0,8)}***`:"not set"}async function No(e){e.format!=="json"&&c.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(i=>({name:i,value:je(process.env[i])}))})),configuredCount:Object.values(t).filter(r=>r.configured).length,totalProviders:Object.keys(t).length};e.dryRun||(n.configuredCount===0?c.warn("No AI providers configured. Set API keys to enable AI features."):c.success(`${n.configuredCount} AI provider(s) configured`)),J(n,e.format,"ai:models")}async function $o(e){e.format!=="json"&&c.info("Scanning for available agents...");try{let t=Et(process.cwd(),"backends/gateway/src/routes/agents"),n=[];try{let o=await import("fs");o.existsSync(t)&&(n=o.readdirSync(t).filter(a=>a.endsWith(".ts")&&!a.endsWith(".d.ts")).map(a=>({name:a.replace(".ts",""),path:`backends/gateway/src/routes/agents/${a}`})))}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?c.info("No agents configured. Create agents in backends/gateway/src/routes/agents/"):c.success(`Found ${r.count} agent(s)`)),J(r,e.format,"ai:agents")}catch(t){c.error(`Failed to scan agents: ${t instanceof Error?t.message:String(t)}`),process.exit(l.ERROR)}}async function jo(e,t){(!e||e.trim().length===0)&&(c.error("Prompt cannot be empty"),process.exit(l.INVALID_ARGS)),t.format!=="json"&&(c.info("Testing AI prompt against default model..."),c.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||c.warn("AI test execution requires a running API gateway. Start it with: pnpm --filter @nebutra/gateway dev"),J(n,t.format,"ai:test")}async function Fo(e){e.format!=="json"&&c.info("Checking AI configuration...");let t={providers:{openai:{key:je(process.env.OPENAI_API_KEY),configured:!!process.env.OPENAI_API_KEY},anthropic:{key:je(process.env.ANTHROPIC_API_KEY),configured:!!process.env.ANTHROPIC_API_KEY},openrouter:{key:je(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=Et(process.cwd(),"nebutra.config.ts"),o=await import("fs");if(o.existsSync(r)){let a=o.readFileSync(r,"utf-8").match(/preset:\s*["']([^"']+)["']/);a&&(t.preset=a[1])}}catch{}let n=Object.entries(t.providers).filter(([,r])=>r.configured).map(([r])=>r);e.dryRun||(n.length===0?c.warn("No AI providers configured. Set environment variables to enable AI features."):c.success(`Configuration loaded: ${n.join(", ")}`)),J(t,e.format,"ai:config")}async function Do(e){let t=fe(),n=se(t),r=Je(),o=Object.values(r).map(a=>{let s=n.providers?.[a.id];return{id:a.id,name:a.name,enabled:s?.enabled===!0,configured:a.envVars.some(u=>!!process.env[u]),envVars:a.envVars.map(u=>({name:u,value:je(process.env[u])}))}}),i={configPath:H(),providers:o,enabledCount:o.filter(a=>a.enabled).length,totalProviders:o.length};if(e.format!=="json"){let a=o.filter(s=>s.enabled);a.length===0?c.warn("No runtime AI providers enabled in nebutra.config.json."):c.success(`Enabled AI providers: ${a.map(s=>s.id).join(", ")}`)}J(i,e.format,"ai:provider:list")}async function To(e,t){let n=gn(e),r=fe(),o=se(r),i=Je(),a=new Date().toISOString();for(let u of n)o.providers[u]={enabled:!0,envVars:i[u].envVars,enabledAt:o.providers?.[u]?.enabledAt??a};qe(r);let s={configPath:H(),enabled:n,env:t.env===!0?n.flatMap(u=>i[u].envVars.map(m=>({provider:u,name:m,example:`${m}=`}))):void 0,ai:fn(r)};if(t.format!=="json"&&(c.success(`Enabled AI provider(s): ${n.join(", ")}`),t.env)){c.info("Required environment variables:");for(let u of n)for(let m of i[u].envVars)console.log(`${m}=`)}J(s,t.format,"ai:provider:enable")}async function Lo(e,t){let n=gn(e),r=fe(),o=se(r),i=Je(),a=new Date().toISOString();for(let u of n)o.providers[u]={enabled:!1,envVars:o.providers?.[u]?.envVars??i[u].envVars,disabledAt:a};qe(r);let s={configPath:H(),disabled:n,ai:fn(r)};t.format!=="json"&&c.success(`Disabled AI provider(s): ${n.join(", ")}`),J(s,t.format,"ai:provider:disable")}async function Uo(e){let t=fe(),n=se(t),r=new Date().toISOString();n.gateway={enabled:!0,mode:"local",initializedAt:n.gateway?.initializedAt??r,updatedAt:r},qe(t);let o={configPath:H(),gateway:n.gateway};e.format!=="json"&&c.success("AI gateway governance initialized in nebutra.config.json"),J(o,e.format,"ai:gateway:init")}async function Bo(e){let t=fe(),n=se(t),r={configPath:H(),gateway:n.gateway??null,initialized:n.gateway?.enabled===!0};e.format!=="json"&&(r.initialized?c.success("AI gateway governance is initialized"):c.warn("AI gateway governance is not initialized. Run: nebutra ai gateway init")),J(r,e.format,"ai:gateway:status")}async function Mo(e,t){(!e||e.trim().length===0)&&(c.error("Route name cannot be empty"),process.exit(l.INVALID_ARGS)),t.policy||(c.error("Route policy is required. Use --policy <policy>."),process.exit(l.INVALID_ARGS));let n=Io(t.fallback),r=fe(),o=se(r);o.routes[e]={policy:t.policy,fallback:n,updatedAt:new Date().toISOString()},qe(r);let i={configPath:H(),route:{name:e,...o.routes[e]},routes:o.routes};t.format!=="json"&&c.success(`AI route '${e}' set with policy '${t.policy}'`),J(i,t.format,"ai:route:set")}async function Go(e){let t=fe(),n=se(t),r=Object.entries(n.routes??{}).map(([i,a])=>({name:i,...a})),o={configPath:H(),routes:r,count:r.length};e.format!=="json"&&(r.length===0?c.warn("No AI routes configured. Run: nebutra ai route set chat --policy cost-latency-balanced --fallback openai:gpt-5.4"):c.success(`Configured AI routes: ${r.map(i=>i.name).join(", ")}`)),J(o,e.format,"ai:route:list")}function hn(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(i,a)=>{let s=Y(i,a);await Do({format:s.format,interactive:s.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(i,a,s)=>{let u=Y(a,s);await To(i,{env:u.env===!0,format:u.format,interactive:u.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(i,a,s)=>{let u=Y(a,s);await Lo(i,{format:u.format,interactive:u.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(i,a)=>{let s=Y(i,a);await Uo({format:s.format,interactive:s.yes!==!0})}),r.command("status").description("Show local AI gateway governance status").option("--format <type>","Output format: json or plain").action(async(i,a)=>{let s=Y(i,a);await Bo({format:s.format,interactive:s.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(i,a,s)=>{let u=Y(a,s);await Mo(i,{policy:u.policy,fallback:u.fallback,format:u.format,interactive:u.yes!==!0})}),o.command("list").description("List configured AI routes").option("--format <type>","Output format: json or plain").action(async(i,a)=>{let s=Y(i,a);await Go({format:s.format,interactive:s.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(i,a)=>{let s=Y(i,a);await No({dryRun:s.dryRun===!0,format:s.format,interactive:s.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(i,a)=>{let s=Y(i,a);await $o({dryRun:s.dryRun===!0,format:s.format,interactive:s.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(i,a,s)=>{let u=Y(a,s);await jo(i,{dryRun:u.dryRun===!0,format:u.format,interactive:u.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(i,a)=>{let s=Y(i,a);await Fo({dryRun:s.dryRun===!0,format:s.format,interactive:s.yes!==!0})})}import{existsSync as _t,mkdirSync as Xo,readFileSync as ei,rmSync as ti,writeFileSync as ni}from"fs";import{dirname as ri,join as Fe}from"path";var yn=["factory","linear","gsap","notion","raycast","stripe","vanta","vercel","cosmos"],Vo=["base","cool","neutral","warm"],zo=["base","none","sm","md","lg","full"],Ko=["base","compact","comfortable","spacious"],Wo=["base","Geist","Inter","Inter Tight","DM Sans","Manrope","Plus Jakarta Sans","Figtree","Work Sans","Space Grotesk","Outfit","Sora","Lexend","Montserrat"],Yo=["base","sans","DM Sans","Inter Tight","Space Grotesk","Sora","Outfit","Manrope","Playfair Display","Fraunces","Source Serif 4"],Jo=["base","Geist Mono","JetBrains Mono","Fira Code","Roboto Mono","Source Code Pro"],qo=["base",400,500,600,700],Ho=["base","light","dark"];var Rt=1,bn="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",ce=class extends Error{constructor(e){super(e),this.name="PresetCodeError"}},Ee={version:{bits:3},base:{bits:5,list:yn},neutral:{bits:2,list:Vo},radius:{bits:3,list:zo},density:{bits:2,list:Ko},sans:{bits:4,list:Wo},heading:{bits:4,list:Yo},mono:{bits:3,list:Jo},headingWeight:{bits:3,list:qo},mode:{bits:2,list:Ho},hasColor:{bits:1},color:{bits:24}},Ot=Object.keys(Ee);for(let e of Ot){let t=Ee[e];if(t.list&&t.list.length>2**t.bits)throw new Error(`preset knob "${e}" outgrew its ${t.bits} bits`)}var Zo=/^#[0-9a-f]{6}$/i;function te(e,t){let n=Ee[e].list;if(!n)throw new Error(`"${e}" is not a list knob`);let r=n.indexOf(t??"base");if(r<0)throw new ce(`Unknown ${e}: ${String(t)}`);return r}function vn(e){let t={version:Rt,base:te("base",e.base),neutral:te("neutral",e.neutral),radius:te("radius",e.radius),density:te("density",e.density),sans:te("sans",e.sans),heading:te("heading",e.heading),mono:te("mono",e.mono),headingWeight:te("headingWeight",e.headingWeight),mode:te("mode",e.mode),hasColor:e.brandColor?1:0,color:0};if(e.brandColor){if(!Zo.test(e.brandColor))throw new ce(`Brand colour must be #rrggbb, got ${e.brandColor}`);t.color=Number.parseInt(e.brandColor.slice(1),16)}let n=0n,r=0n;for(let i of Ot)n|=BigInt(t[i])<<r,r+=BigInt(Ee[i].bits);let o="";for(;n>0n;)o=bn[Number(n%62n)]+o,n/=62n;return o}function Qo(e){if(!/^[0-9a-zA-Z]{1,12}$/.test(e))throw new ce(`"${e}" is not a preset code`);let t=0n;for(let a of e)t=t*62n+BigInt(bn.indexOf(a));let n={};for(let a of Ot){let s=BigInt(Ee[a].bits);n[a]=Number(t&(1n<<s)-1n),t>>=s}if(t!==0n)throw new ce(`"${e}" is longer than any preset`);if(n.version!==Rt)throw new ce(`"${e}" is preset format v${n.version}; this Sailor reads v${Rt}. Upgrade @nebutra/tokens.`);let r=a=>{let u=Ee[a].list[n[a]];if(u===void 0)throw new ce(`"${e}" names an unknown ${a}`);return u},o={base:r("base")},i=["neutral","radius","density","sans","heading","mono","headingWeight","mode"];for(let a of i){let s=r(a);s!=="base"&&(o[a]=s)}return n.hasColor&&(o.brandColor=`#${n.color.toString(16).padStart(6,"0")}`),o}function At(e){let t=e.trim();return yn.includes(t)?{base:t}:Qo(t)}var He=["packages","design","tokens"],kn={theme:["base","brandColor","neutral","radius","density","mode"],fonts:["sans","heading","mono","headingWeight"]};function oi(e=process.cwd()){let t=e;for(;;){if(_t(Fe(t,...He,"package.json")))return t;let n=ri(t);if(n===t)return null;t=n}}function ii(e,t,n){if(!n)return t;let r={...e??{base:"factory"}};for(let o of kn[n]){let i=t[o];i===void 0?delete r[o]:r[o]=i}return r}var ai=e=>e.base==="factory"&&Object.keys(e).length===1;function si(e,t){let n=Fe(e,...He,"project"),r=Fe(n,"preset");if(ai(t))return ti(r,{force:!0}),{root:e,code:null,preset:t};let o=vn(t);return Xo(n,{recursive:!0}),ni(r,`${o}
4
- `),{root:e,code:o,preset:t}}function ci(e){let t=Fe(e,...He,"project","preset");return _t(t)?At(ei(t,"utf8")):null}function wn(e){e.command("apply").description("Put a Sailor Studio preset on this project (its look, in one code)").requiredOption("--preset <code>","Preset code from Sailor Studio, or a design language id").option("--only <group>","Change only one group of knobs: theme | fonts").option("--no-build","Write the preset without rebuilding the tokens").option("--format <type>","Output format: json or table").action(async t=>{let n=t.only;n&&!(n in kn)&&(c.error(`--only takes "theme" or "fonts", not "${t.only}".`),process.exit(l.INVALID_ARGS));let r;try{r=At(t.preset??"")}catch(a){c.error(a instanceof ce?a.message:String(a)),process.exit(l.INVALID_ARGS)}let o=oi();o||(c.error("Run this inside a Sailor project (no packages/design/tokens found above here)."),process.exit(l.NOT_FOUND)),_t(Fe(o,...He,"project","brand.json"))&&(c.error("packages/design/tokens/project/brand.json is hand-authored and takes precedence over any preset. Remove it to use presets."),process.exit(l.CONFLICT));let i=si(o,ii(ci(o),r,n));if(t.build!==!1){let a=await E({command:"pnpm",args:["--filter","@nebutra/tokens","build"],cwd:o,label:"Rebuilding the tokens"});a.exitCode!==0&&(c.error(a.stderr||"The tokens build failed."),process.exit(l.ERROR))}if(t.format==="json"){console.log(JSON.stringify({code:i.code,preset:i.preset},null,2));return}c.success(i.code?`Applied preset ${i.code} (base: ${i.preset.base}).`:"Back to factory: the House tokens, unchanged.")})}function De(e,t,n){if(t==="json"){let r={command:n||"brand",exitCode:e.exitCode,success:e.exitCode===0,...e.stdout&&{stdout:e.stdout},...e.stderr&&{stderr:e.stderr}};console.log(JSON.stringify(r,null,2))}else e.stdout&&console.log(e.stdout),e.stderr&&e.exitCode!==0&&console.error(e.stderr)}function xn(e){return/^#[0-9A-Fa-f]{6}$/.test(e)}async function li(e){c.info("Initializing brand system...");let t=await j("brand:init",{dryRun:e.dryRun,interactive:e.interactive??!0});De(t,e.format,"brand:init"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||c.success("Brand system initialized successfully")}async function ui(e){c.info("Applying brand configuration...");let t=await j("brand:apply",{dryRun:e.dryRun,interactive:e.interactive??!0});De(t,e.format,"brand:apply"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||c.success("Brand applied successfully")}async function di(e){let t=[];e.primary&&(xn(e.primary)||(c.error(`Invalid primary color format: ${e.primary}. Expected hex format like #0033FE`),process.exit(l.INVALID_ARGS)),t.push(`--primary=${e.primary}`)),e.secondary&&(xn(e.secondary)||(c.error(`Invalid secondary color format: ${e.secondary}. Expected hex format like #0BF1C3`),process.exit(l.INVALID_ARGS)),t.push(`--secondary=${e.secondary}`)),t.length===0&&(c.warn("No colors specified. Usage: nebutra brand palette --primary=#HEX --secondary=#HEX"),c.info("Example: nebutra brand palette --primary=#0033FE --secondary=#0BF1C3"),process.exit(l.INVALID_ARGS)),c.info(`Generating color palette: ${t.join(" ")}`);let n=await E({command:"node",args:["scripts/generate-palette.mjs",...t],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Generate brand palette"});De(n,e.format,"brand:palette"),n.exitCode!==0&&process.exit(n.exitCode),e.dryRun||c.success("Color palette generated successfully")}async function mi(e){c.info("Syncing brand tokens across packages...");let t=await j("brand:sync",{dryRun:e.dryRun,interactive:e.interactive??!0});De(t,e.format,"brand:sync"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||c.success("Brand tokens synced successfully")}async function pi(e){c.info("Verifying brand token consistency...");let t=await j("brand:verify-tokens",{dryRun:e.dryRun,interactive:e.interactive??!0});De(t,e.format,"brand:verify-tokens"),t.exitCode!==0&&(c.error("Brand token verification failed. There are inconsistencies in your design tokens."),process.exit(t.exitCode)),e.dryRun||c.success("All brand tokens are consistent")}function Cn(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,r)=>{let o=r.optsWithGlobals?.()||n;await li({dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.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,r)=>{let o=r.optsWithGlobals?.()||n;await ui({dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.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,r)=>{let o=r.optsWithGlobals?.()||n;await di({primary:n.primary,secondary:n.secondary,dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.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,r)=>{let o=r.optsWithGlobals?.()||n;await mi({dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.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,r)=>{let o=r.optsWithGlobals?.()||n;await pi({dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.yes!==!0})})}import{appendFileSync as gi,existsSync as fi,readFileSync as hi,writeFileSync as yi}from"fs";import{homedir as Te}from"os";import{join as Re}from"path";import*as T from"@clack/prompts";import D from"picocolors";var Z={name:"nebutra",description:"Nebutra \u2014 the CLI for Sailor projects: status, dev, db, and codegen",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:"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, dev, db)",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 --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:"Record a Nebutra support/billing license key locally (Sailor scaffolds are MIT \u2014 nothing in the CLI is gated by this)",usage:"nebutra license [subcommand]",arguments:[],options:[],subcommands:[{name:"activate",description:"Validate and record a license key locally (support/billing record, not a feature unlock)",usage:"nebutra license activate <key>",arguments:[{name:"key",description:"Your Nebutra support/billing license key",required:!0}],options:[{flags:"--quiet",description:"Suppress output"}],examples:[{command:"nebutra license activate liz_1234567890",description:"Record your license key locally"}]},{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:"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:"dev",description:"Start development workflows for selected Nebutra apps or presets",usage:"nebutra dev [options]"},{name:"build",description:"Run build workflows through the Nebutra CLI",usage:"nebutra build [options]"},{name:"lint",description:"Run lint workflows through the Nebutra CLI",usage:"nebutra lint [options]"},{name:"typecheck",description:"Run TypeScript checks through the Nebutra CLI",usage:"nebutra typecheck [options]"},{name:"test",description:"Run unit, architecture, and E2E verification workflows",usage:"nebutra test [scope]"},{name:"e2e",description:"Run browser end-to-end verification suites",usage:"nebutra e2e [suite]"},{name:"theme",description:"Inspect registry-backed Nebutra themes and governance metadata",usage:"nebutra theme [subcommand]"},{name:"apply",description:"Put a Sailor Studio preset on this project (its look, in one code)",usage:"nebutra apply --preset <code> [--only theme|fonts]"},{name:"ui",description:"Search, inspect, validate, and plan migrations for @nebutra/ui components",usage:"nebutra ui [subcommand]"},{name:"services",description:"Inspect and manage microservice health, logs, scaling, and rollouts",usage:"nebutra services [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:"login",description:"Authorize this machine with your Nebutra account (device flow)",usage:"nebutra login [options]",options:[{flags:"--json",description:"Print verification details as JSON and exit immediately (agent mode)"},{flags:"--no-browser",description:"Don't try to open a browser"},{flags:"--poll",description:"Poll until approved/denied/expired, resuming a pending `login --json` if present"},{flags:"--format <type>",description:"Output format: json or plain"}],examples:[{command:"nebutra login",description:"Human flow: open browser, wait for approval"},{command:"nebutra login --json",description:"Agent flow: print verification link/code and exit immediately"},{command:"nebutra login --poll --json",description:"Resume and wait for a pending login"}]},{name:"logout",description:"Clear local Nebutra CLI session state",usage:"nebutra logout"},{name:"whoami",description:"Show the Nebutra identity this machine is authorized as",usage:"nebutra whoami [options]",options:[{flags:"--json",description:"Output as JSON"},{flags:"--format <type>",description:"Output format: json or plain"}]},{name:"upgrade",description:"Upgrade local Nebutra CLI tooling",usage:"nebutra upgrade"},{name:"link",description:"Link a local project to Nebutra platform metadata",usage:"nebutra link"},{name:"unlink",description:"Unlink local Nebutra project metadata",usage:"nebutra unlink"},{name:"status",description:"Show capability readiness \u2014 live, local-fallback, or missing-key, per environment",usage:"nebutra status [options]",arguments:[],options:[{flags:"--json",description:"Emit machine-readable JSON"}],examples:[{command:"nebutra status",description:"Show a readiness table for every capability in nebutra.config.json"},{command:"nebutra status --json",description:"Emit { stack, locale, capabilities } as JSON for agents"}]},{name:"sync",description:"Make .env.example / .env.local agree with the declared capabilities (idempotent)",usage:"nebutra sync [options]",arguments:[],options:[{flags:"--dry-run",description:"Preview planned additions without writing files (exits with code 10)"},{flags:"--json",description:"Emit machine-readable JSON"}],examples:[{command:"nebutra sync",description:"Add any env keys required by declared capabilities to .env.example, create .env.local if missing"},{command:"nebutra sync --dry-run",description:"Preview planned additions without writing files"},{command:"nebutra sync --json",description:"Emit { added, unchanged, warnings } as JSON for agents"}]},{name:"doctor",description:"Check local project setup and common Nebutra configuration issues",usage:"nebutra doctor"}]};var Ze=(Z.subcommands??[]).map(e=>e.name),Sn={init:"Initialize a Nebutra project and create nebutra.config.json",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",dev:"Start development server",build:"Run build workflows",lint:"Run lint workflows",typecheck:"Run TypeScript checks",test:"Run unit and E2E tests",e2e:"Run browser E2E suites",theme:"Theme registry and governance metadata",apply:"Put a Sailor Studio preset on this project",ai:"AI provider and gateway routing configuration",services:"Microservice management",secrets:"Encrypted secrets management",login:"Authorize this machine with your Nebutra account (device flow)",logout:"Clear local session state",whoami:"Show the authorized Nebutra identity",upgrade:"Upgrade local CLI tooling",link:"Link local project metadata",unlink:"Unlink local project metadata",status:"Show capability readiness (live/local-fallback/missing-key)",sync:"Sync .env.example/.env.local with declared capabilities"},bi=["--help","--version","--stdio","--format","--yes","--no-interactive","--no-color","--verbose","--quiet","--dry-run","--if-not-exists","--json","--no-browser","--poll"],vi=Ze.map(e=>`complete -c nebutra -n "__fish_use_subcommand_only" -f -a "${e}" -d "${Sn[e]}"`).join(`
5
- `);function En(){return`#!/usr/bin/env bash
2
+ import{b as d}from"./chunk-JDX5NXDX.js";import{a as l,b as yn,c as He,d as bn,e as ae,f as _t,h as vn,i as y,j as R,k as F,l as se,m as ge,n as ne}from"./chunk-DTSPVLT5.js";import{realpathSync as Ho}from"fs";import{resolve as Zo}from"path";import{fileURLToPath as Xo}from"url";import{Command as El}from"commander";import*as $n from"@clack/prompts";var E=class extends Error{code;exitCode;suggestion;retryable;context;cause;constructor(t){super(t.message),this.name="CommandError",this.code=t.code,this.exitCode=t.exitCode??l.ERROR,this.suggestion=t.suggestion,this.retryable=t.retryable??!1,this.context=t.context,this.cause=t.cause}};function ei(e){return e instanceof E}function ti(e){return ei(e)?He({error:e.code,message:e.message,suggestion:e.suggestion,retryable:e.retryable,exitCode:e.exitCode,context:e.context,cause:e.cause instanceof Error?e.cause:e}):e instanceof Error?He({error:"unexpected_error",message:e.message,suggestion:"Re-run with NEBUTRA_DEBUG=1 for a stack trace.",exitCode:l.ERROR,cause:e}):He({error:"unexpected_error",message:String(e),suggestion:"Re-run with NEBUTRA_DEBUG=1 for a stack trace.",exitCode:l.ERROR})}function It(e){let t=ti(e);return bn(t),t.exitCode}async function z(e){try{await e()}catch(t){process.exitCode=It(t)}}var ni={name:"Nebutra",nameCn:"\u4E91\u6BD3\u667A\u80FD",nameFull:"\u65E0\u9521\u4E91\u6BD3\u667A\u80FD\u79D1\u6280\u6709\u9650\u516C\u53F8",nameFullEn:"Wuxi Nebutra Intelligence Technology Co., Ltd.",tagline:"Ship AI products, not boilerplate.",taglineCn:"AI\u539F\u751F\xB7\u5FEB\u901F\u51FA\u6D77\xB7\u5373\u523B\u4EA4\u4ED8",description:"Production-ready Next.js monorepo template for AI SaaS products. Auth, billing, multi-tenancy, AI services, design system, and enterprise infrastructure \u2014 pre-configured.",descriptionCn:"\u9762\u5411AI\u521B\u4E1A\u8005\u7684\u4E00\u4F53\u5316SaaS\u57FA\u7840\u8BBE\u65BD\u6A21\u677F\uFF0C\u8986\u76D6\u8BA4\u8BC1\u3001\u8BA1\u8D39\u3001\u591A\u79DF\u6237\u3001AI\u670D\u52A1\u4E0E\u8BBE\u8BA1\u7CFB\u7EDF\uFF0C\u5F00\u7BB1\u5373\u4EA7\u54C1",logo:"image",story:{concept:"Logo\u4EE5\u9996\u5B57\u6BCDN\u7684\u57FA\u7840\u9020\u578B\u6982\u5FF5\u4E3A\u4E3B\u8981\u8BBE\u8BA1\u6846\u67B6\uFF0C\u901A\u8FC7\u51E0\u4F55\u6B63\u8D1F\u7A7A\u95F4\u6784\u5EFA\u9690\u5F62'N'\uFF0C\u5F62\u6210\u8FD1\u4F3C\u516D\u8FB9\u5F62\u7684\u7A33\u5B9A\u7ED3\u6784",colorMeaning:"\u84DD\u7EFF\u6E10\u53D8\u4F53\u73B0\u672A\u6765\u611F\u4E0E\u79D1\u6280\u950B\u8292\uFF0C'\u4E91'\u4EE3\u8868\u4E91\u7AEF\u5E73\u53F0\uFF0C'\u6BD3'\u5BD3\u610F\u5B55\u80B2\u4E0E\u8F6C\u5316",values:["AI Native","Ship Fast","Open by Default","Global-Ready","Enterprise-Grade"],missionStatement:"Help AI founders and SaaS teams go from idea to production 10x faster by providing the infrastructure layer they shouldn't have to build."},domains:{landing:"nebutra.com",app:"app.nebutra.com",api:"api.nebutra.com",auth:"auth.nebutra.com",sso:"sso.nebutra.com",studio:"studio.nebutra.com",cdn:"cdn.nebutra.com",router:"router.nebutra.com",forge:"forge.nebutra.com",design:"design.nebutra.com",status:"status.nebutra.com",open:"open.nebutra.com",admin:"admin.nebutra.com",analytics:"analytics.nebutra.com",pebble:"pebble.nebutra.com",carina:"carina.nebutra.com",kuanlan:"kuanlan.nebutra.com",kcq:"kcq.nebutra.com",origin:"origin.nebutra.com"},social:{twitter:"https://twitter.com/nebutra",github:"https://github.com/nebutra",discord:"https://discord.gg/nebutra",linkedin:"https://linkedin.com/company/nebutra"}};function Z(e){let t=ni.domains[e];if(!t?.trim())throw new Error(`brand.domains.${String(e)} is empty \u2014 run brand:apply`);return`https://${t.replace(/^https?:\/\//,"").replace(/\/+$/,"")}`}import{execFile as oi}from"child_process";import{chmodSync as ii,existsSync as Oe,readFileSync as kn,rmSync as ai,unlinkSync as Pt,writeFileSync as xn}from"fs";import{mkdir as si}from"fs/promises";import{platform as he}from"os";import{join as Cn}from"path";import{promisify as ci}from"util";var M={name:"Nebutra",nameCn:"\u4E91\u6BD3\u667A\u80FD",nameFull:"\u65E0\u9521\u4E91\u6BD3\u667A\u80FD\u79D1\u6280\u6709\u9650\u516C\u53F8",nameFullEn:"Wuxi Nebutra Intelligence Technology Co., Ltd.",tagline:"Ship AI products, not boilerplate.",taglineCn:"AI\u539F\u751F\xB7\u5FEB\u901F\u51FA\u6D77\xB7\u5373\u523B\u4EA4\u4ED8",description:"Production-ready Next.js monorepo template for AI SaaS products. Auth, billing, multi-tenancy, AI services, design system, and enterprise infrastructure \u2014 pre-configured.",descriptionCn:"\u9762\u5411AI\u521B\u4E1A\u8005\u7684\u4E00\u4F53\u5316SaaS\u57FA\u7840\u8BBE\u65BD\u6A21\u677F\uFF0C\u8986\u76D6\u8BA4\u8BC1\u3001\u8BA1\u8D39\u3001\u591A\u79DF\u6237\u3001AI\u670D\u52A1\u4E0E\u8BBE\u8BA1\u7CFB\u7EDF\uFF0C\u5F00\u7BB1\u5373\u4EA7\u54C1",logo:"image",story:{concept:"Logo\u4EE5\u9996\u5B57\u6BCDN\u7684\u57FA\u7840\u9020\u578B\u6982\u5FF5\u4E3A\u4E3B\u8981\u8BBE\u8BA1\u6846\u67B6\uFF0C\u901A\u8FC7\u51E0\u4F55\u6B63\u8D1F\u7A7A\u95F4\u6784\u5EFA\u9690\u5F62'N'\uFF0C\u5F62\u6210\u8FD1\u4F3C\u516D\u8FB9\u5F62\u7684\u7A33\u5B9A\u7ED3\u6784",colorMeaning:"\u84DD\u7EFF\u6E10\u53D8\u4F53\u73B0\u672A\u6765\u611F\u4E0E\u79D1\u6280\u950B\u8292\uFF0C'\u4E91'\u4EE3\u8868\u4E91\u7AEF\u5E73\u53F0\uFF0C'\u6BD3'\u5BD3\u610F\u5B55\u80B2\u4E0E\u8F6C\u5316",values:["AI Native","Ship Fast","Open by Default","Global-Ready","Enterprise-Grade"],missionStatement:"Help AI founders and SaaS teams go from idea to production 10x faster by providing the infrastructure layer they shouldn't have to build."},domains:{landing:"nebutra.com",app:"app.nebutra.com",api:"api.nebutra.com",auth:"auth.nebutra.com",sso:"sso.nebutra.com",studio:"studio.nebutra.com",cdn:"cdn.nebutra.com",router:"router.nebutra.com",forge:"forge.nebutra.com",design:"design.nebutra.com",status:"status.nebutra.com",open:"open.nebutra.com",admin:"admin.nebutra.com",analytics:"analytics.nebutra.com",pebble:"pebble.nebutra.com",carina:"carina.nebutra.com",kuanlan:"kuanlan.nebutra.com",kcq:"kcq.nebutra.com",origin:"origin.nebutra.com"},social:{twitter:"https://twitter.com/nebutra",github:"https://github.com/nebutra",discord:"https://discord.gg/nebutra",linkedin:"https://linkedin.com/company/nebutra"}};import{homedir as wn,platform as ri}from"os";import{join as Ze}from"path";function Re(){if(ri()==="win32"){let t=process.env.LOCALAPPDATA||Ze(wn(),"AppData","Local");return Ze(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?Ze(e,"nebutra"):Ze(wn(),".config","nebutra")}var ye=ci(oi),be="nebutra-cli",Ae="default";function Ee(){return Cn(Re(),"credentials.json")}function Xe(){return Cn(Re(),"device-pending.json")}async function Sn(){await si(Re(),{recursive:!0})}async function Nt(e){try{return await ye(he()==="win32"?"where":"which",[e]),!0}catch{return!1}}function $t(){return process.env.NEBUTRA_NO_KEYCHAIN==="1"}async function li(e){if($t())return!1;try{if(he()==="darwin")return await ye("security",["add-generic-password","-a",Ae,"-s",be,"-w",e,"-U"]),!0;if(he()==="linux"&&await Nt("secret-tool"))return await ye("secret-tool",["store","--label",`${M.name} CLI credentials`,"service",be,"account",Ae]),!0}catch{}return!1}async function Rn(){if($t())return null;try{if(he()==="darwin"){let{stdout:e}=await ye("security",["find-generic-password","-a",Ae,"-s",be,"-w"]);return e.trim()||null}if(he()==="linux"&&await Nt("secret-tool")){let{stdout:e}=await ye("secret-tool",["lookup","service",be,"account",Ae]);return e.trim()||null}}catch{}return null}async function di(){if($t())return!1;let e=!1;try{he()==="darwin"?(await ye("security",["delete-generic-password","-a",Ae,"-s",be]),e=!0):he()==="linux"&&await Nt("secret-tool")&&(await ye("secret-tool",["clear","service",be,"account",Ae]),e=!0)}catch{}return e}async function En(e){await Sn();let t=JSON.stringify(e);if(await li(t)){try{Oe(Ee())&&Pt(Ee())}catch{}return{storedIn:"keychain"}}xn(Ee(),`${t}
3
+ `,{mode:384});try{ii(Ee(),384)}catch{}return{storedIn:"file"}}async function ui(){let e=await Rn();if(e)try{return JSON.parse(e)}catch{return null}let t=Ee();if(!Oe(t))return null;try{return JSON.parse(kn(t,"utf-8"))}catch{return null}}async function On(){let e=[];await di()&&e.push(`keychain:${be}`);let t=Ee();if(Oe(t))try{Pt(t),e.push(t)}catch{}let n=Xe();if(Oe(n))try{ai(n,{force:!0}),e.push(n)}catch{}return e}async function jt(e){await Sn(),xn(Xe(),`${JSON.stringify(e)}
4
+ `,{mode:384})}function An(){let e=Xe();if(!Oe(e))return null;try{return JSON.parse(kn(e,"utf-8"))}catch{return null}}function Qe(){let e=Xe();if(Oe(e))try{Pt(e)}catch{}}async function ce(){let e=process.env.NEBUTRA_TOKEN?.trim();if(e)return{token:e,source:"env",expired:!1};let t=await ui();if(!t)return null;let n=!!t.expiresAt&&new Date(t.expiresAt).getTime()<=Date.now(),r=await Rn()!==null;return{token:t.accessToken,source:r?"keychain":"file",expired:n}}var et=["platform_owner","platform_operator","platform_support","platform_readonly"];function _n(e){let t=e.trim().toLowerCase(),n=t.startsWith("platform_")?t:`platform_${t}`;return et.includes(n)?n:null}var _e=class extends Error{constructor(n,r,o){super(o);this.status=n;this.code=r;this.name="StaffApiError"}status;code};async function mi(){let e=await ce();return e&&!e.expired?e.token:null}function pi(){return(process.env.NEBUTRA_API_URL??Z("api")).replace(/\/+$/,"")}function In(e={}){let t=e.fetch??fetch,n=e.token??mi;async function r(o,i,a){let s=await n();if(!s)throw new _e(401,"not_logged_in","Not logged in. Run `nebutra login` first.");let c=await t(`${e.baseUrl??pi()}/api/v1/platform/staff${i}`,{method:o,headers:{authorization:`Bearer ${s}`,...a===void 0?{}:{"content-type":"application/json"}},...a===void 0?{}:{body:JSON.stringify(a)},signal:AbortSignal.timeout(15e3)}),m=await c.json().catch(()=>null);if(!c.ok){let p=typeof m?.code=="string"?m.code:`http_${c.status}`,g=typeof m?.error=="string"?m.error:`The gateway answered ${c.status}.`;throw new _e(c.status,p,g)}return m}return{list:async()=>(await r("GET","")).staff,me:()=>r("GET","/me"),grant:o=>r("POST","",o),revoke:(o,i)=>r("POST",`/${encodeURIComponent(o)}/revoke`,{note:i})}}var fi=e=>e===400?l.INVALID_ARGS:e===401||e===403?l.PERMISSION_DENIED:e===404?l.NOT_FOUND:e===409?l.CONFLICT:l.ERROR;function tt(e){return e instanceof _e?new E({code:e.code,message:e.message,exitCode:fi(e.status),...e.code==="not_logged_in"||e.status===401?{suggestion:"Run `nebutra login`."}:{}}):e}function gi(e){let t=_n(e);if(!t)throw new E({code:"invalid_role",message:`Unknown role "${e}".`,exitCode:l.INVALID_ARGS,suggestion:`Use one of: ${et.join(", ")} (the platform_ prefix is optional).`});return t}function Pn(e){let t=e?.trim()??"";if(t.length<3)throw new E({code:"note_required",message:"A note is required: say why (ticket, rotation, handover).",exitCode:l.INVALID_ARGS,suggestion:'Add --note "<reason>".'});return t}function hi(e){if(e.length===0)return"No staff grants.";let t=n=>[(n.email??n.userId).padEnd(28),n.role.padEnd(18),(n.active?"active":`revoked ${n.revokedAt?.slice(0,10)}`).padEnd(22),`by ${n.grantedBy?.email??n.grantedBy?.userId??"bootstrap"}`.padEnd(28),n.note??""].join(" ");return e.map(t).join(`
5
+ `)}async function Nn(e,t,n){if(t!==!0){if(!e.isInteractive())throw new E({code:"confirmation_required",message:"This changes who can operate the platform and needs confirmation.",exitCode:l.INVALID_ARGS,suggestion:"Re-run with --yes once the person has agreed."});if(!await e.confirm(n))throw new E({code:"cancelled",message:"Cancelled. Nothing changed.",exitCode:l.CANCELLED})}}function jn(e,t={}){let n=t.out??(c=>console.log(c)),r=()=>t.client??In(),o={confirm:t.confirm??(async c=>await $n.confirm({message:c})===!0),isInteractive:t.isInteractive??(()=>process.stdin.isTTY===!0&&process.stdout.isTTY===!0)},i=(c,m,p)=>n(c.json?JSON.stringify(m,null,2):p),s=e.command("admin").description("Platform administration (platform staff only)").command("staff").description("Manage who may operate the platform");s.command("list").description("List staff grants, tombstones included").option("--json","Machine-readable output").action(c=>z(async()=>{try{let m=await r().list();i(c,{staff:m},hi(m))}catch(m){throw tt(m)}})),s.command("whoami").description("Your own platform standing").option("--json","Machine-readable output").action(c=>z(async()=>{try{let m=await r().me();i(c,m,`${m.email??m.userId} ${m.role}${m.canGrant?" (can grant and revoke)":""}`)}catch(m){throw tt(m)}})),s.command("grant <email>").description("Grant or change a platform role (platform_owner only)").requiredOption("--role <role>",`One of: ${et.join(", ")}`).requiredOption("--note <text>","Why this person has this role").option("--yes","Confirm without asking (agents: only after the person agreed)").option("--json","Machine-readable output").action((c,m)=>z(async()=>{let p=gi(m.role??""),g=Pn(m.note);await Nn(o,m.yes,`Grant ${p} to ${c}?`);try{let h=await r().grant({email:c,role:p,note:g});i(m,h,`Granted ${h.role} to ${c}. Audit entry: ${h.auditId??"not recorded"}`)}catch(h){throw tt(h)}})),s.command("revoke <email>").description("Revoke a grant by tombstone (platform_owner only)").requiredOption("--note <text>","Why the access ends").option("--yes","Confirm without asking (agents: only after the person agreed)").option("--json","Machine-readable output").action((c,m)=>z(async()=>{let p=Pn(m.note);await Nn(o,m.yes,`Revoke platform access for ${c}?`);try{let g=await r().revoke(c,p);i(m,g,`Revoked ${c}. Audit entry: ${g.auditId??"not recorded"}`)}catch(g){throw tt(g)}}))}import{existsSync as yi,readFileSync as bi,writeFileSync as vi}from"fs";import{resolve as Tt}from"path";var wi=["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"],Fn="nebutra.config.json",ki=wi,Tn={"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 H(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 xi(e){return e.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function Ci(e){return Tn[e]?Tn[e]:[`${e.replace(/-/g,"_").toUpperCase()}_API_KEY`]}function nt(){return Object.fromEntries(ki.map(e=>[e,{id:e,name:xi(e),envVars:Ci(e)}]))}function X(){return Tt(process.cwd(),Fn)}function ve(){let e=X();if(!yi(e))return{$schema:"https://nebutra.com/schema.json"};try{return JSON.parse(bi(e,"utf-8"))}catch(t){d.error(`Failed to parse ${Fn}: ${t instanceof Error?t.message:String(t)}`),process.exit(l.ERROR)}}function rt(e){vi(X(),`${JSON.stringify(e,null,2)}
6
+ `)}function le(e){return e.ai={providers:{},routes:{},...e.ai},e.ai.providers={...e.ai.providers},e.ai.routes={...e.ai.routes},e.ai}function Dn(e){let t=nt(),n=[...new Set(e.map(o=>o.toLowerCase()))],r=n.filter(o=>!t[o]);return r.length>0&&(d.error(`Unknown AI provider(s): ${r.join(", ")}. Valid providers: ${Object.keys(t).join(", ")}`),process.exit(l.INVALID_ARGS)),n}function Si(e){let t=e?.split(",").map(r=>r.trim()).filter(Boolean)??[];t.length===0&&(d.error("Route fallback cannot be empty. Use --fallback provider:model,provider:model"),process.exit(l.INVALID_ARGS));let n=t.filter(r=>!/^[a-z0-9-]+:[^,\s]+$/i.test(r));return n.length>0&&(d.error(`Invalid fallback target(s): ${n.join(", ")}. Expected provider:model.`),process.exit(l.INVALID_ARGS)),t}function Ln(e){let t=le(e),n=t.providers??{},r=t.routes??{},o=Object.entries(n).filter(([,i])=>i.enabled).map(([i])=>i);return{path:X(),gateway:t.gateway??null,providers:n,enabledProviders:o,routes:r}}function q(e,t){let n=typeof e.opts=="function"?e.opts():e,r=typeof t?.optsWithGlobals=="function"?t.optsWithGlobals():typeof e.optsWithGlobals=="function"?e.optsWithGlobals():n,o=n.format??r.format??Ri();return{...r,...n,...o?{format:o}:{}}}function Ri(){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 Fe(e){return e?e.length<=8?"***":`${e.substring(0,8)}***`:"not set"}async function Ei(e){e.format!=="json"&&d.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(i=>({name:i,value:Fe(process.env[i])}))})),configuredCount:Object.values(t).filter(r=>r.configured).length,totalProviders:Object.keys(t).length};e.dryRun||(n.configuredCount===0?d.warn("No AI providers configured. Set API keys to enable AI features."):d.success(`${n.configuredCount} AI provider(s) configured`)),H(n,e.format,"ai:models")}async function Oi(e){e.format!=="json"&&d.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(a=>a.endsWith(".ts")&&!a.endsWith(".d.ts")).map(a=>({name:a.replace(".ts",""),path:`backends/gateway/src/routes/agents/${a}`})))}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?d.info("No agents configured. Create agents in backends/gateway/src/routes/agents/"):d.success(`Found ${r.count} agent(s)`)),H(r,e.format,"ai:agents")}catch(t){d.error(`Failed to scan agents: ${t instanceof Error?t.message:String(t)}`),process.exit(l.ERROR)}}async function Ai(e,t){(!e||e.trim().length===0)&&(d.error("Prompt cannot be empty"),process.exit(l.INVALID_ARGS)),t.format!=="json"&&(d.info("Testing AI prompt against default model..."),d.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||d.warn("AI test execution requires a running API gateway. Start it with: pnpm --filter @nebutra/gateway dev"),H(n,t.format,"ai:test")}async function _i(e){e.format!=="json"&&d.info("Checking AI configuration...");let t={providers:{openai:{key:Fe(process.env.OPENAI_API_KEY),configured:!!process.env.OPENAI_API_KEY},anthropic:{key:Fe(process.env.ANTHROPIC_API_KEY),configured:!!process.env.ANTHROPIC_API_KEY},openrouter:{key:Fe(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 a=o.readFileSync(r,"utf-8").match(/preset:\s*["']([^"']+)["']/);a&&(t.preset=a[1])}}catch{}let n=Object.entries(t.providers).filter(([,r])=>r.configured).map(([r])=>r);e.dryRun||(n.length===0?d.warn("No AI providers configured. Set environment variables to enable AI features."):d.success(`Configuration loaded: ${n.join(", ")}`)),H(t,e.format,"ai:config")}async function Ii(e){let t=ve(),n=le(t),r=nt(),o=Object.values(r).map(a=>{let s=n.providers?.[a.id];return{id:a.id,name:a.name,enabled:s?.enabled===!0,configured:a.envVars.some(c=>!!process.env[c]),envVars:a.envVars.map(c=>({name:c,value:Fe(process.env[c])}))}}),i={configPath:X(),providers:o,enabledCount:o.filter(a=>a.enabled).length,totalProviders:o.length};if(e.format!=="json"){let a=o.filter(s=>s.enabled);a.length===0?d.warn("No runtime AI providers enabled in nebutra.config.json."):d.success(`Enabled AI providers: ${a.map(s=>s.id).join(", ")}`)}H(i,e.format,"ai:provider:list")}async function Pi(e,t){let n=Dn(e),r=ve(),o=le(r),i=nt(),a=new Date().toISOString();for(let c of n)o.providers[c]={enabled:!0,envVars:i[c].envVars,enabledAt:o.providers?.[c]?.enabledAt??a};rt(r);let s={configPath:X(),enabled:n,env:t.env===!0?n.flatMap(c=>i[c].envVars.map(m=>({provider:c,name:m,example:`${m}=`}))):void 0,ai:Ln(r)};if(t.format!=="json"&&(d.success(`Enabled AI provider(s): ${n.join(", ")}`),t.env)){d.info("Required environment variables:");for(let c of n)for(let m of i[c].envVars)console.log(`${m}=`)}H(s,t.format,"ai:provider:enable")}async function Ni(e,t){let n=Dn(e),r=ve(),o=le(r),i=nt(),a=new Date().toISOString();for(let c of n)o.providers[c]={enabled:!1,envVars:o.providers?.[c]?.envVars??i[c].envVars,disabledAt:a};rt(r);let s={configPath:X(),disabled:n,ai:Ln(r)};t.format!=="json"&&d.success(`Disabled AI provider(s): ${n.join(", ")}`),H(s,t.format,"ai:provider:disable")}async function $i(e){let t=ve(),n=le(t),r=new Date().toISOString();n.gateway={enabled:!0,mode:"local",initializedAt:n.gateway?.initializedAt??r,updatedAt:r},rt(t);let o={configPath:X(),gateway:n.gateway};e.format!=="json"&&d.success("AI gateway governance initialized in nebutra.config.json"),H(o,e.format,"ai:gateway:init")}async function ji(e){let t=ve(),n=le(t),r={configPath:X(),gateway:n.gateway??null,initialized:n.gateway?.enabled===!0};e.format!=="json"&&(r.initialized?d.success("AI gateway governance is initialized"):d.warn("AI gateway governance is not initialized. Run: nebutra ai gateway init")),H(r,e.format,"ai:gateway:status")}async function Ti(e,t){(!e||e.trim().length===0)&&(d.error("Route name cannot be empty"),process.exit(l.INVALID_ARGS)),t.policy||(d.error("Route policy is required. Use --policy <policy>."),process.exit(l.INVALID_ARGS));let n=Si(t.fallback),r=ve(),o=le(r);o.routes[e]={policy:t.policy,fallback:n,updatedAt:new Date().toISOString()},rt(r);let i={configPath:X(),route:{name:e,...o.routes[e]},routes:o.routes};t.format!=="json"&&d.success(`AI route '${e}' set with policy '${t.policy}'`),H(i,t.format,"ai:route:set")}async function Fi(e){let t=ve(),n=le(t),r=Object.entries(n.routes??{}).map(([i,a])=>({name:i,...a})),o={configPath:X(),routes:r,count:r.length};e.format!=="json"&&(r.length===0?d.warn("No AI routes configured. Run: nebutra ai route set chat --policy cost-latency-balanced --fallback openai:gpt-5.4"):d.success(`Configured AI routes: ${r.map(i=>i.name).join(", ")}`)),H(o,e.format,"ai:route:list")}function Un(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(i,a)=>{let s=q(i,a);await Ii({format:s.format,interactive:s.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(i,a,s)=>{let c=q(a,s);await Pi(i,{env:c.env===!0,format:c.format,interactive:c.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(i,a,s)=>{let c=q(a,s);await Ni(i,{format:c.format,interactive:c.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(i,a)=>{let s=q(i,a);await $i({format:s.format,interactive:s.yes!==!0})}),r.command("status").description("Show local AI gateway governance status").option("--format <type>","Output format: json or plain").action(async(i,a)=>{let s=q(i,a);await ji({format:s.format,interactive:s.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(i,a,s)=>{let c=q(a,s);await Ti(i,{policy:c.policy,fallback:c.fallback,format:c.format,interactive:c.yes!==!0})}),o.command("list").description("List configured AI routes").option("--format <type>","Output format: json or plain").action(async(i,a)=>{let s=q(i,a);await Fi({format:s.format,interactive:s.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(i,a)=>{let s=q(i,a);await Ei({dryRun:s.dryRun===!0,format:s.format,interactive:s.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(i,a)=>{let s=q(i,a);await Oi({dryRun:s.dryRun===!0,format:s.format,interactive:s.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(i,a,s)=>{let c=q(a,s);await Ai(i,{dryRun:c.dryRun===!0,format:c.format,interactive:c.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(i,a)=>{let s=q(i,a);await _i({dryRun:s.dryRun===!0,format:s.format,interactive:s.yes!==!0})})}import{existsSync as Gt,mkdirSync as Mi,readFileSync as Gi,rmSync as Vi,writeFileSync as zi}from"fs";import{dirname as Ki,join as Le}from"path";var Ut=["factory","linear","gsap","notion","raycast","stripe","vanta","vercel","cosmos"],Mn=["base","cool","neutral","warm"],Gn=["base","none","sm","md","lg","full"],Vn=["base","compact","comfortable","spacious"],zn=["base","Geist","Inter","Inter Tight","DM Sans","Manrope","Plus Jakarta Sans","Figtree","Work Sans","Space Grotesk","Outfit","Sora","Lexend","Montserrat"],Kn=["base","sans","DM Sans","Inter Tight","Space Grotesk","Sora","Outfit","Manrope","Playfair Display","Fraunces","Source Serif 4"],Wn=["base","Geist Mono","JetBrains Mono","Fira Code","Roboto Mono","Source Code Pro"],Yn=["base",400,500,600,700],Jn=["base","light","dark"];var Dt=1,qn="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",O=class extends Error{constructor(e){super(e),this.name="PresetCodeError"}},Ie={version:{bits:3},base:{bits:5,list:Ut},neutral:{bits:2,list:Mn},radius:{bits:3,list:Gn},density:{bits:2,list:Vn},sans:{bits:4,list:zn},heading:{bits:4,list:Kn},mono:{bits:3,list:Wn},headingWeight:{bits:3,list:Yn},mode:{bits:2,list:Jn},hasColor:{bits:1},color:{bits:24},tintLogo:{bits:1}},Bt=Object.keys(Ie);for(let e of Bt){let t=Ie[e];if(t.list&&t.list.length>2**t.bits)throw new Error(`preset knob "${e}" outgrew its ${t.bits} bits`)}var Di=/^#[0-9a-f]{6}$/i;function re(e,t){let n=Ie[e].list;if(!n)throw new Error(`"${e}" is not a list knob`);let r=n.indexOf(t??"base");if(r<0)throw new O(`Unknown ${e}: ${String(t)}`);return r}function Mt(e){let t={version:Dt,base:re("base",e.base),neutral:re("neutral",e.neutral),radius:re("radius",e.radius),density:re("density",e.density),sans:re("sans",e.sans),heading:re("heading",e.heading),mono:re("mono",e.mono),headingWeight:re("headingWeight",e.headingWeight),mode:re("mode",e.mode),hasColor:e.brandColor?1:0,color:0,tintLogo:e.tintLogo?1:0};if(e.brandColor){if(!Di.test(e.brandColor))throw new O(`Brand colour must be #rrggbb, got ${e.brandColor}`);t.color=Number.parseInt(e.brandColor.slice(1),16)}let n=0n,r=0n;for(let i of Bt)n|=BigInt(t[i])<<r,r+=BigInt(Ie[i].bits);let o="";for(;n>0n;)o=qn[Number(n%62n)]+o,n/=62n;return o}function Li(e){if(!/^[0-9a-zA-Z]{1,12}$/.test(e))throw new O(`"${e}" is not a preset code`);let t=0n;for(let a of e)t=t*62n+BigInt(qn.indexOf(a));let n={};for(let a of Bt){let s=BigInt(Ie[a].bits);n[a]=Number(t&(1n<<s)-1n),t>>=s}if(t!==0n)throw new O(`"${e}" is longer than any preset`);if(n.version!==Dt)throw new O(`"${e}" is preset format v${n.version}; this Sailor reads v${Dt}. Upgrade @nebutra/tokens.`);let r=a=>{let c=Ie[a].list[n[a]];if(c===void 0)throw new O(`"${e}" names an unknown ${a}`);return c},o={base:r("base")},i=["neutral","radius","density","sans","heading","mono","headingWeight","mode"];for(let a of i){let s=r(a);s!=="base"&&(o[a]=s)}return n.tintLogo&&(o.tintLogo=!0),n.hasColor&&(o.brandColor=`#${n.color.toString(16).padStart(6,"0")}`),o}function De(e){let t=e.trim();return Ut.includes(t)?{base:t}:Li(t)}var Lt={base:Ut,neutral:Mn,radius:Gn,density:Vn,sans:zn,heading:Kn,mono:Wn,headingWeight:Yn,mode:Jn},Ft={base:"The design language to start from. `factory` is the House tokens; the others are complete brand packages.",brandColor:"#rrggbb. Becomes the action fill (buttons) and the ring (focus, links). Omit to keep the base's.",neutral:"Temperature of neutral surfaces and text.",radius:"Corner radius scale for buttons, cards, badges and inputs.",density:"Control height and spacing scale.",sans:"Body face. Only self-hosted faces are offered.",heading:"Heading face. `sans` sets headings in the body face.",mono:"Code face.",headingWeight:"Heading weight.",mode:"Which mode a visitor sees first. Only a dual-mode base can change it.",tintLogo:"Paint the wordmark in the base's brand colour. Off by default: the wordmark reads in ink."},Hn=Z("landing"),Ui=`${Hn}/studio/preset.schema.json`;function Zn(){let e={brandColor:{type:"string",pattern:"^#[0-9a-fA-F]{6}$",description:Ft.brandColor}};for(let[t,n]of Object.entries(Lt))e[t]={enum:[...n],description:Ft[t]};return e.tintLogo={type:"boolean",default:!1,description:Ft.tintLogo},{$schema:"https://json-schema.org/draft/2020-12/schema",$id:Ui,title:"Sailor Studio preset",description:"A Sailor project's whole look in one object. Encode it with `nebutra studio preview` to review it in Sailor Studio, then `nebutra studio pull` or `create-sailor --preset` to apply it.",type:"object",required:["base"],additionalProperties:!1,properties:e}}function Bn(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new O("A preset is a JSON object with at least `base`.");let t=e,n=new Set(["brandColor","tintLogo",...Object.keys(Lt)]);for(let o of Object.keys(t))if(!n.has(o))throw new O(`Unknown field "${o}". Fields: ${[...n].join(", ")}.`);let r={};for(let[o,i]of Object.entries(Lt)){let a=t[o];if(a!==void 0){if(!i.includes(a))throw new O(`"${o}" must be one of ${i.map(s=>JSON.stringify(s)).join(", ")}; got ${JSON.stringify(a)}.`);a!=="base"&&(r[o]=a)}}if(r.base===void 0)throw new O('"base" is required.');if(t.tintLogo!==void 0){if(typeof t.tintLogo!="boolean")throw new O(`"tintLogo" must be true or false; got ${JSON.stringify(t.tintLogo)}.`);t.tintLogo&&(r.tintLogo=!0)}if(t.brandColor!==void 0){if(typeof t.brandColor!="string"||!/^#[0-9a-fA-F]{6}$/.test(t.brandColor))throw new O(`"brandColor" must be #rrggbb; got ${JSON.stringify(t.brandColor)}.`);r.brandColor=t.brandColor.toLowerCase()}return r}function Xn(e){if(typeof e!="string")return Bn(e);let t=e.trim();if(t.startsWith("{")){let n;try{n=JSON.parse(t)}catch{throw new O("That looks like JSON but does not parse.")}return Bn(n)}if(/^https?:\/\//.test(t)){let n=new URL(t).searchParams.get("preset");if(!n)throw new O("That URL carries no ?preset= to read.");return De(n)}return De(t)}var Bi=`${Hn}/sailor/studio`;function ot(e){return Object.keys(e).length===1?e.base:Mt(e)}function Qn(e){let t=new URL(Bi);return t.searchParams.set("preset",ot(e)),t.searchParams.set("proposed","1"),t.toString()}var it=["packages","design","tokens"],er={theme:["base","brandColor","neutral","radius","density","mode"],fonts:["sans","heading","mono","headingWeight"]};function Wi(e=process.cwd()){let t=e;for(;;){if(Gt(Le(t,...it,"package.json")))return t;let n=Ki(t);if(n===t)return null;t=n}}function Yi(e,t,n){if(!n)return t;let r={...e??{base:"factory"}};for(let o of er[n]){let i=t[o];i===void 0?delete r[o]:r[o]=i}return r}var Ji=e=>e.base==="factory"&&Object.keys(e).length===1;function qi(e,t){let n=Le(e,...it,"project"),r=Le(n,"preset");if(Ji(t))return Vi(r,{force:!0}),{root:e,code:null,preset:t};let o=Mt(t);return Mi(n,{recursive:!0}),zi(r,`${o}
7
+ `),{root:e,code:o,preset:t}}function Hi(e){let t=Le(e,...it,"project","preset");return Gt(t)?De(Gi(t,"utf8")):null}function tr(e){e.command("apply").description("Put a Sailor Studio preset on this project (its look, in one code)").requiredOption("--preset <code>","Preset code from Sailor Studio, or a design language id").option("--only <group>","Change only one group of knobs: theme | fonts").option("--no-build","Write the preset without rebuilding the tokens").option("--format <type>","Output format: json or table").action(async t=>{let n=t.only;n&&!(n in er)&&(d.error(`--only takes "theme" or "fonts", not "${t.only}".`),process.exit(l.INVALID_ARGS));let r;try{r=De(t.preset??"")}catch(a){d.error(a instanceof O?a.message:String(a)),process.exit(l.INVALID_ARGS)}let o=Wi();o||(d.error("Run this inside a Sailor project (no packages/design/tokens found above here)."),process.exit(l.NOT_FOUND)),Gt(Le(o,...it,"project","brand.json"))&&(d.error("packages/design/tokens/project/brand.json is hand-authored and takes precedence over any preset. Remove it to use presets."),process.exit(l.CONFLICT));let i=qi(o,Yi(Hi(o),r,n));if(t.build!==!1){let a=await R({command:"pnpm",args:["--filter","@nebutra/tokens","build"],cwd:o,label:"Rebuilding the tokens"});a.exitCode!==0&&(d.error(a.stderr||"The tokens build failed."),process.exit(l.ERROR))}if(t.format==="json"){console.log(JSON.stringify({code:i.code,preset:i.preset},null,2));return}d.success(i.code?`Applied preset ${i.code} (base: ${i.preset.base}).`:"Back to factory: the House tokens, unchanged.")})}function Ue(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 nr(e){return/^#[0-9A-Fa-f]{6}$/.test(e)}async function Zi(e){d.info("Initializing brand system...");let t=await F("brand:init",{dryRun:e.dryRun,interactive:e.interactive??!0});Ue(t,e.format,"brand:init"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||d.success("Brand system initialized successfully")}async function Xi(e){d.info("Applying brand configuration...");let t=await F("brand:apply",{dryRun:e.dryRun,interactive:e.interactive??!0});Ue(t,e.format,"brand:apply"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||d.success("Brand applied successfully")}async function Qi(e){let t=[];e.primary&&(nr(e.primary)||(d.error(`Invalid primary color format: ${e.primary}. Expected hex format like #0033FE`),process.exit(l.INVALID_ARGS)),t.push(`--primary=${e.primary}`)),e.secondary&&(nr(e.secondary)||(d.error(`Invalid secondary color format: ${e.secondary}. Expected hex format like #0BF1C3`),process.exit(l.INVALID_ARGS)),t.push(`--secondary=${e.secondary}`)),t.length===0&&(d.warn("No colors specified. Usage: nebutra brand palette --primary=#HEX --secondary=#HEX"),d.info("Example: nebutra brand palette --primary=#0033FE --secondary=#0BF1C3"),process.exit(l.INVALID_ARGS)),d.info(`Generating color palette: ${t.join(" ")}`);let n=await R({command:"node",args:["scripts/generate-palette.mjs",...t],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Generate brand palette"});Ue(n,e.format,"brand:palette"),n.exitCode!==0&&process.exit(n.exitCode),e.dryRun||d.success("Color palette generated successfully")}async function ea(e){d.info("Syncing brand tokens across packages...");let t=await F("brand:sync",{dryRun:e.dryRun,interactive:e.interactive??!0});Ue(t,e.format,"brand:sync"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||d.success("Brand tokens synced successfully")}async function ta(e){d.info("Verifying brand token consistency...");let t=await F("brand:verify-tokens",{dryRun:e.dryRun,interactive:e.interactive??!0});Ue(t,e.format,"brand:verify-tokens"),t.exitCode!==0&&(d.error("Brand token verification failed. There are inconsistencies in your design tokens."),process.exit(t.exitCode)),e.dryRun||d.success("All brand tokens are consistent")}function rr(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,r)=>{let o=r.optsWithGlobals?.()||n;await Zi({dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.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,r)=>{let o=r.optsWithGlobals?.()||n;await Xi({dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.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,r)=>{let o=r.optsWithGlobals?.()||n;await Qi({primary:n.primary,secondary:n.secondary,dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.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,r)=>{let o=r.optsWithGlobals?.()||n;await ea({dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.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,r)=>{let o=r.optsWithGlobals?.()||n;await ta({dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.yes!==!0})})}import{appendFileSync as na,existsSync as ra,readFileSync as oa,writeFileSync as ia}from"fs";import{homedir as Be}from"os";import{join as Pe}from"path";import*as U from"@clack/prompts";import L from"picocolors";var Q={name:"nebutra",description:"Nebutra \u2014 the CLI for Sailor projects: status, dev, db, and codegen",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:"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, dev, db)",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 --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:"Record a Nebutra support/billing license key locally (Sailor scaffolds are MIT \u2014 nothing in the CLI is gated by this)",usage:"nebutra license [subcommand]",arguments:[],options:[],subcommands:[{name:"activate",description:"Validate and record a license key locally (support/billing record, not a feature unlock)",usage:"nebutra license activate <key>",arguments:[{name:"key",description:"Your Nebutra support/billing license key",required:!0}],options:[{flags:"--quiet",description:"Suppress output"}],examples:[{command:"nebutra license activate liz_1234567890",description:"Record your license key locally"}]},{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:"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:"dev",description:"Start development workflows for selected Nebutra apps or presets",usage:"nebutra dev [options]"},{name:"build",description:"Run build workflows through the Nebutra CLI",usage:"nebutra build [options]"},{name:"lint",description:"Run lint workflows through the Nebutra CLI",usage:"nebutra lint [options]"},{name:"typecheck",description:"Run TypeScript checks through the Nebutra CLI",usage:"nebutra typecheck [options]"},{name:"test",description:"Run unit, architecture, and E2E verification workflows",usage:"nebutra test [scope]"},{name:"e2e",description:"Run browser end-to-end verification suites",usage:"nebutra e2e [suite]"},{name:"theme",description:"Inspect registry-backed Nebutra themes and governance metadata",usage:"nebutra theme [subcommand]"},{name:"apply",description:"Put a Sailor Studio preset on this project (its look, in one code)",usage:"nebutra apply --preset <code> [--only theme|fonts]"},{name:"studio",description:"Sailor Studio for agents: preset schema, review link in the browser, pull onto the project",usage:"nebutra studio <schema|preview|list|pull> [preset] [--latest] [--json]"},{name:"admin",description:"Platform administration for platform staff: list, grant and revoke staff access (confirmation required)",usage:"nebutra admin staff <list|whoami|grant <email> --role <role> --note <why>|revoke <email> --note <why>> [--yes] [--json]"},{name:"ui",description:"Search, inspect, validate, and plan migrations for @nebutra/ui components",usage:"nebutra ui [subcommand]"},{name:"services",description:"Inspect and manage microservice health, logs, scaling, and rollouts",usage:"nebutra services [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:"login",description:"Authorize this machine with your Nebutra account (device flow)",usage:"nebutra login [options]",options:[{flags:"--json",description:"Print verification details as JSON and exit immediately (agent mode)"},{flags:"--no-browser",description:"Don't try to open a browser"},{flags:"--poll",description:"Poll until approved/denied/expired, resuming a pending `login --json` if present"},{flags:"--format <type>",description:"Output format: json or plain"}],examples:[{command:"nebutra login",description:"Human flow: open browser, wait for approval"},{command:"nebutra login --json",description:"Agent flow: print verification link/code and exit immediately"},{command:"nebutra login --poll --json",description:"Resume and wait for a pending login"}]},{name:"logout",description:"Clear local Nebutra CLI session state",usage:"nebutra logout"},{name:"whoami",description:"Show the Nebutra identity this machine is authorized as",usage:"nebutra whoami [options]",options:[{flags:"--json",description:"Output as JSON"},{flags:"--format <type>",description:"Output format: json or plain"}]},{name:"upgrade",description:"Upgrade local Nebutra CLI tooling",usage:"nebutra upgrade"},{name:"link",description:"Link a local project to Nebutra platform metadata",usage:"nebutra link"},{name:"unlink",description:"Unlink local Nebutra project metadata",usage:"nebutra unlink"},{name:"status",description:"Show capability readiness \u2014 live, local-fallback, or missing-key, per environment",usage:"nebutra status [options]",arguments:[],options:[{flags:"--json",description:"Emit machine-readable JSON"}],examples:[{command:"nebutra status",description:"Show a readiness table for every capability in nebutra.config.json"},{command:"nebutra status --json",description:"Emit { stack, locale, capabilities } as JSON for agents"}]},{name:"sync",description:"Make .env.example / .env.local agree with the declared capabilities (idempotent)",usage:"nebutra sync [options]",arguments:[],options:[{flags:"--dry-run",description:"Preview planned additions without writing files (exits with code 10)"},{flags:"--json",description:"Emit machine-readable JSON"}],examples:[{command:"nebutra sync",description:"Add any env keys required by declared capabilities to .env.example, create .env.local if missing"},{command:"nebutra sync --dry-run",description:"Preview planned additions without writing files"},{command:"nebutra sync --json",description:"Emit { added, unchanged, warnings } as JSON for agents"}]},{name:"doctor",description:"Check local project setup and common Nebutra configuration issues",usage:"nebutra doctor"}]};var at=(Q.subcommands??[]).map(e=>e.name),or={init:"Initialize a Nebutra project and create nebutra.config.json",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",dev:"Start development server",build:"Run build workflows",lint:"Run lint workflows",typecheck:"Run TypeScript checks",test:"Run unit and E2E tests",e2e:"Run browser E2E suites",theme:"Theme registry and governance metadata",apply:"Put a Sailor Studio preset on this project",studio:"Sailor Studio for agents: preset schema, review link, pull",admin:"Platform staff administration: list, grant, revoke",ai:"AI provider and gateway routing configuration",services:"Microservice management",secrets:"Encrypted secrets management",login:"Authorize this machine with your Nebutra account (device flow)",logout:"Clear local session state",whoami:"Show the authorized Nebutra identity",upgrade:"Upgrade local CLI tooling",link:"Link local project metadata",unlink:"Unlink local project metadata",status:"Show capability readiness (live/local-fallback/missing-key)",sync:"Sync .env.example/.env.local with declared capabilities"},aa=["--help","--version","--stdio","--format","--yes","--no-interactive","--no-color","--verbose","--quiet","--dry-run","--if-not-exists","--json","--no-browser","--poll"],sa=at.map(e=>`complete -c nebutra -n "__fish_use_subcommand_only" -f -a "${e}" -d "${or[e]}"`).join(`
8
+ `);function ir(){return`#!/usr/bin/env bash
6
9
 
7
10
  _nebutra_completions() {
8
11
  local cur prev opts cmd
@@ -11,10 +14,10 @@ _nebutra_completions() {
11
14
  prev="\${COMP_WORDS[COMP_CWORD-1]}"
12
15
 
13
16
  # Subcommands
14
- local subcommands="${Ze.join(" ")}"
17
+ local subcommands="${at.join(" ")}"
15
18
 
16
19
  # Flags
17
- local flags="${bi.join(" ")}"
20
+ local flags="${aa.join(" ")}"
18
21
 
19
22
  if [[ \${cur} == -* ]]; then
20
23
  # Complete flags
@@ -31,11 +34,11 @@ _nebutra_completions() {
31
34
  }
32
35
 
33
36
  complete -o bashdefault -o default -o nospace -F _nebutra_completions nebutra
34
- `}function Rn(){return`#compdef nebutra
37
+ `}function ar(){return`#compdef nebutra
35
38
 
36
39
  _nebutra() {
37
40
  local -a commands=(
38
- ${Ze.map(e=>`"${e}:${Sn[e]}"`).join(`
41
+ ${at.map(e=>`"${e}:${or[e]}"`).join(`
39
42
  `)}
40
43
  )
41
44
 
@@ -91,12 +94,12 @@ _nebutra() {
91
94
  }
92
95
 
93
96
  _nebutra
94
- `}function On(){return`# Fish shell completions for nebutra
97
+ `}function sr(){return`# Fish shell completions for nebutra
95
98
 
96
- complete -c nebutra -f -n "__fish_seen_subcommand_from; and not __fish_seen_subcommand_from ${Ze.join(" ")}" -d "Nebutra governance CLI"
99
+ complete -c nebutra -f -n "__fish_seen_subcommand_from; and not __fish_seen_subcommand_from ${at.join(" ")}" -d "Nebutra governance CLI"
97
100
 
98
101
  # Subcommands
99
- ${vi}
102
+ ${sa}
100
103
 
101
104
  # Flags
102
105
  complete -c nebutra -f -a "--help" -d "Show help message"
@@ -122,24 +125,24 @@ complete -c nebutra -n "__fish_seen_subcommand_from completions" -f -a "install"
122
125
  complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--all" -d "Show full schema for all commands"
123
126
  complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--list" -d "List all available command names"
124
127
  complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--exit-codes" -d "Show exit codes reference"
125
- `}function An(e){let n={bash:En,zsh:Rn,fish:On}[e.toLowerCase()];n||(console.error(D.red(`Error: Unknown shell '${e}'. Supported: bash, zsh, fish`)),process.exit(l.INVALID_ARGS)),console.log(n())}async function ki(){let t=(process.env.SHELL||"/bin/bash").split("/").pop()||"bash";T.intro(D.bgCyan(D.black(" nebutra completions install ")));let n=T.spinner();n.start(D.cyan(`Detecting shell: ${t}...`));try{let r=null,o=null,i=null;if(t==="bash"?(r=Re(Te(),".bashrc"),o=Re(Te(),".bash_completion.d"),i="source ~/.bash_completion.d/nebutra"):t==="zsh"?(r=Re(Te(),".zshrc"),o=Re(Te(),".zsh/completions"),i="fpath=(~/.zsh/completions $fpath)"):t==="fish"?(o=Re(Te(),".config/fish/completions"),i="# Completions installed to ~/.config/fish/completions/nebutra.fish"):(n.stop(),T.log.warn(D.yellow(`Warning: Unsupported shell '${t}'. Please install completions manually.`)),An("bash"),T.outro(D.cyan("Run 'nebutra completions bash' (or zsh/fish) to generate completions for your shell.")),process.exit(l.SUCCESS)),o){(await import("fs")).mkdirSync(o,{recursive:!0});let s=Re(o,t==="fish"?"nebutra.fish":"nebutra"),u=t==="bash"?En():t==="zsh"?Rn():On();yi(s,u),n.stop(D.green(`Completion script installed to ${s}`))}r&&i&&t!=="fish"&&(!fi(r)||!hi(r,"utf8").includes("nebutra"))&&(gi(r,(t==="bash"?`
128
+ `}function cr(e){let n={bash:ir,zsh:ar,fish:sr}[e.toLowerCase()];n||(console.error(L.red(`Error: Unknown shell '${e}'. Supported: bash, zsh, fish`)),process.exit(l.INVALID_ARGS)),console.log(n())}async function ca(){let t=(process.env.SHELL||"/bin/bash").split("/").pop()||"bash";U.intro(L.bgCyan(L.black(" nebutra completions install ")));let n=U.spinner();n.start(L.cyan(`Detecting shell: ${t}...`));try{let r=null,o=null,i=null;if(t==="bash"?(r=Pe(Be(),".bashrc"),o=Pe(Be(),".bash_completion.d"),i="source ~/.bash_completion.d/nebutra"):t==="zsh"?(r=Pe(Be(),".zshrc"),o=Pe(Be(),".zsh/completions"),i="fpath=(~/.zsh/completions $fpath)"):t==="fish"?(o=Pe(Be(),".config/fish/completions"),i="# Completions installed to ~/.config/fish/completions/nebutra.fish"):(n.stop(),U.log.warn(L.yellow(`Warning: Unsupported shell '${t}'. Please install completions manually.`)),cr("bash"),U.outro(L.cyan("Run 'nebutra completions bash' (or zsh/fish) to generate completions for your shell.")),process.exit(l.SUCCESS)),o){(await import("fs")).mkdirSync(o,{recursive:!0});let s=Pe(o,t==="fish"?"nebutra.fish":"nebutra"),c=t==="bash"?ir():t==="zsh"?ar():sr();ia(s,c),n.stop(L.green(`Completion script installed to ${s}`))}r&&i&&t!=="fish"&&(!ra(r)||!oa(r,"utf8").includes("nebutra"))&&(na(r,(t==="bash"?`
126
129
  # nebutra completions
127
130
  [[ -f ~/.bash_completion.d/nebutra ]] && source ~/.bash_completion.d/nebutra`:`
128
131
  # nebutra completions
129
132
  fpath=(~/.zsh/completions $fpath)
130
133
  autoload -Uz compinit && compinit`)+`
131
- `),T.log.info(D.green(`Added nebutra completions to ${r}`))),T.log.info(D.cyan("Completions installed! Restart your shell or run:")),T.log.info(D.dim(` source "${t==="bash"?"~/.bashrc":"~/.zshrc"}"`)),T.outro(D.green("Installation complete!"))}catch(r){n.stop(),T.log.error(D.red("Error installing completions. You can manually add them by running:")),T.log.info(D.cyan(` nebutra completions ${t} >> ~/.${t}rc`)),r instanceof Error&&console.error(D.dim(r.message)),process.exit(l.ERROR)}}function _n(e){e.command("completions [shell]").description("Generate or install shell completions").action(async t=>{!t||t==="install"?await ki():An(t)})}import{existsSync as Si,readFileSync as Ei}from"fs";import{resolve as Ri}from"path";import*as Xe from"@clack/prompts";import{stderr as le,stdout as In}from"process";import Q from"picocolors";function wi(){return process.env.NEBUTRA_NON_TTY||process.env.CI||process.env.GITHUB_ACTIONS?!1:process.stderr.isTTY===!0}function Pn(e){if(e)return e;let t=process.env.NEBUTRA_OUTPUT_FORMAT;return t&&["json","table","plain"].includes(t)?t:wi()?"plain":"json"}function xi(e,t){if(e.length===0)return"";let n=t||Object.keys(e[0]),r={};for(let s of n){r[String(s)]=String(s).length;for(let u of e){let m=String(u[s]??"");r[String(s)]=Math.max(r[String(s)],m.length)}}let o=n.map(s=>String(s).padEnd(r[String(s)])).join(" | "),i=n.map(s=>"-".repeat(r[String(s)])).join("-+-"),a=[o,i];for(let s of e){let u=n.map(m=>String(s[m]??"").padEnd(r[String(m)])).join(" | ");a.push(u)}return a.join(`
132
- `)}function Ci(e){return JSON.stringify(e)}function Nn(e){if(!Array.isArray(e))return It(e);if(e.length===0)return"(empty)";if(typeof e[0]!="object"||e[0]===null)return It(e);let t=xi(e);return Q.gray(t)}function It(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 b(e,t){let n=Pn(t?.format),r=t?.stream==="stderr"?le:In,o;switch(n){case"json":o=Ci(e);break;case"table":o=Nn(e);break;default:o=It(e);break}r.write(o+`
133
- `)}function I(e,t={}){let n=Pn(t.format),r;switch(n){case"json":r=JSON.stringify(e);break;case"table":r=Nn(e);break;default:r=JSON.stringify(e,null,2);break}In.write(r+`
134
- `)}function d(e,t="info"){let n;switch(t){case"error":n=Q.red("\u2716");break;case"warn":n=Q.yellow("\u26A0");break;case"success":n=Q.green("\u2713");break;default:n=Q.blue("\u2139");break}let r=`${n} ${e}`;le.write(r+`
135
- `)}function x(e,t){if(!(process.env.NEBUTRA_DEBUG||process.argv.includes("--debug")))return;let r=Q.dim(`[debug] ${e}`);le.write(r+`
136
- `),t&&Object.keys(t).length>0&&le.write(Q.dim(JSON.stringify(t,null,2))+`
137
- `)}function Qe(e){le.write(`
138
- `),le.write(Q.bold(Q.cyan(e))+`
139
- `),le.write(Q.gray("-".repeat(e.length))+`
140
- `),le.write(`
141
- `)}var G=2e4;function Oi(e){if(process.env.DATABASE_URL)return!0;for(let t of[".env.local",".env"]){let n=Ri(e,t);if(Si(n))try{let r=Ei(n,"utf-8");if(/^DATABASE_URL\s*=\s*\S+/m.test(r))return!0}catch{}}return!1}function Oe(){let e=y();Oi(e)||ie("DATABASE_URL is not set (checked the environment, .env.local, and .env).","Set DATABASE_URL, or run `pnpm infra:up` for a local Postgres and add it to .env.local.")}async function Ai(e){d("Generating Prisma client"+"...","info");let n=await ee("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 Pt(e,t){let n=t||{};if(n.dryRun||Oe(),e){d(`Creating migration: ${e}`,"info");let r=await ee("migrate",{args:["dev","--name",e],dryRun:n.dryRun,interactive:!n.dryRun,timeoutMs:n.dryRun?void 0:G});r.timedOut&&ie(`Creating migration '${e}' timed out after ${G}ms.`,"Check that DATABASE_URL points to a reachable database."),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 ee("migrate",{args:["deploy"],dryRun:n.dryRun,interactive:!n.dryRun,timeoutMs:n.dryRun?void 0:G});r.timedOut&&ie(`Migration deploy timed out after ${G}ms.`,"Check that DATABASE_URL points to a reachable database."),r.exitCode===0?d("All pending migrations completed","success"):(d("Migration deployment failed","error"),process.exit(r.exitCode||1))}}async function _i(e){e.dryRun||Oe(),d("Pushing schema to database...","info");let t=await ee("db",{args:["push"],dryRun:e.dryRun,interactive:!e.dryRun,timeoutMs:e.dryRun?void 0:G});t.timedOut&&ie(`Schema push timed out after ${G}ms.`,"Check that DATABASE_URL points to a reachable database."),t.exitCode===0?d("Schema pushed successfully","success"):(d("Failed to push schema","error"),process.exit(t.exitCode||1))}async function Ii(e){e.dryRun||Oe(),d("Seeding database...","info");let t=await ee("db",{args:["seed"],dryRun:e.dryRun,interactive:!e.dryRun,timeoutMs:e.dryRun?void 0:G});t.timedOut&&ie(`Seeding timed out after ${G}ms.`,"Check that DATABASE_URL points to a reachable database."),t.exitCode===0?d("Database seeded successfully","success"):(d("Failed to seed database","error"),process.exit(t.exitCode||1))}async function Pi(e){e.dryRun||Oe(),d("Launching Prisma Studio...","info");let t=await ee("studio",{dryRun:e.dryRun,interactive:!0});t.exitCode!==0&&(d("Failed to launch Prisma Studio","error"),process.exit(t.exitCode||1))}async function Ni(e){let t=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!e.yes&&!e.dryRun)if(t){let r=await Xe.confirm({message:"Reset database? This will delete all data.",initialValue:!1});(Xe.isCancel(r)||!r)&&(d("Reset cancelled","warn"),process.exit(l.CANCELLED))}else d("Database reset requires explicit --yes confirmation (use: nebutra db reset --yes)","error"),process.exit(l.INVALID_ARGS);e.dryRun||Oe(),d("Resetting database...","warn");let n=await ee("migrate",{args:["reset",...e.yes?["--force"]:[]],dryRun:e.dryRun,interactive:!e.dryRun,timeoutMs:e.dryRun?void 0:G});n.timedOut&&ie(`Database reset timed out after ${G}ms.`,"Check that DATABASE_URL points to a reachable database."),n.exitCode===0?d("Database reset successfully","success"):(d("Failed to reset database","error"),process.exit(n.exitCode||1))}async function $i(e){e.dryRun||Oe(),d("Checking migration status...","info");let t=await ee("migrate",{args:["status"],dryRun:e.dryRun,interactive:!1,timeoutMs:e.dryRun?void 0:G});if(t.timedOut&&ie(`\`db status\` timed out after ${G}ms waiting on the database.`,"Check that DATABASE_URL points to a reachable database."),t.exitCode===0)if(e.format==="json")try{let n=JSON.parse(t.stdout);process.stdout.write(JSON.stringify(n,null,2)+`
142
- `)}catch{process.stdout.write(t.stdout)}else e.dryRun||(t.stdout.trim()&&process.stdout.write(t.stdout),t.stderr.trim()&&process.stderr.write(t.stderr));else d("Failed to check migration status","error"),t.stderr.trim()&&x("Prisma stderr",{stderr:t.stderr}),process.exit(t.exitCode||1)}function $n(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,i)=>{let a=i.optsWithGlobals?.(),s={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:a?.format||o.format};try{switch(n){case"generate":await Ai(s);break;case"migrate":r.length>0?r[0]==="create"&&r[1]?await Pt(r[1],s):await Pt(r[0],s):await Pt(void 0,s);break;case"push":await _i(s);break;case"seed":await Ii(s);break;case"studio":await Pi(s);break;case"reset":await Ni(s);break;case"status":await $i(s);break;default:d(`Unknown db subcommand: ${n}. Valid commands: generate, migrate, migrate create, push, seed, studio, reset, status`,"error"),process.exit(l.INVALID_ARGS)}}catch(u){let m=u instanceof Error?u.message:String(u);d(`Database command failed: ${m}`,"error"),x("Full error",{error:u}),process.exit(l.ERROR)}}).addHelpText("after",`
134
+ `),U.log.info(L.green(`Added nebutra completions to ${r}`))),U.log.info(L.cyan("Completions installed! Restart your shell or run:")),U.log.info(L.dim(` source "${t==="bash"?"~/.bashrc":"~/.zshrc"}"`)),U.outro(L.green("Installation complete!"))}catch(r){n.stop(),U.log.error(L.red("Error installing completions. You can manually add them by running:")),U.log.info(L.cyan(` nebutra completions ${t} >> ~/.${t}rc`)),r instanceof Error&&console.error(L.dim(r.message)),process.exit(l.ERROR)}}function lr(e){e.command("completions [shell]").description("Generate or install shell completions").action(async t=>{!t||t==="install"?await ca():cr(t)})}import{existsSync as ma,readFileSync as pa}from"fs";import{resolve as fa}from"path";import*as ct from"@clack/prompts";import{stderr as de,stdout as dr}from"process";import ee from"picocolors";function la(){return process.env.NEBUTRA_NON_TTY||process.env.CI||process.env.GITHUB_ACTIONS?!1:process.stderr.isTTY===!0}function ur(e){if(e)return e;let t=process.env.NEBUTRA_OUTPUT_FORMAT;return t&&["json","table","plain"].includes(t)?t:la()?"plain":"json"}function da(e,t){if(e.length===0)return"";let n=t||Object.keys(e[0]),r={};for(let s of n){r[String(s)]=String(s).length;for(let c of e){let m=String(c[s]??"");r[String(s)]=Math.max(r[String(s)],m.length)}}let o=n.map(s=>String(s).padEnd(r[String(s)])).join(" | "),i=n.map(s=>"-".repeat(r[String(s)])).join("-+-"),a=[o,i];for(let s of e){let c=n.map(m=>String(s[m]??"").padEnd(r[String(m)])).join(" | ");a.push(c)}return a.join(`
135
+ `)}function ua(e){return JSON.stringify(e)}function mr(e){if(!Array.isArray(e))return Vt(e);if(e.length===0)return"(empty)";if(typeof e[0]!="object"||e[0]===null)return Vt(e);let t=da(e);return ee.gray(t)}function Vt(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 b(e,t){let n=ur(t?.format),r=t?.stream==="stderr"?de:dr,o;switch(n){case"json":o=ua(e);break;case"table":o=mr(e);break;default:o=Vt(e);break}r.write(o+`
136
+ `)}function N(e,t={}){let n=ur(t.format),r;switch(n){case"json":r=JSON.stringify(e);break;case"table":r=mr(e);break;default:r=JSON.stringify(e,null,2);break}dr.write(r+`
137
+ `)}function u(e,t="info"){let n;switch(t){case"error":n=ee.red("\u2716");break;case"warn":n=ee.yellow("\u26A0");break;case"success":n=ee.green("\u2713");break;default:n=ee.blue("\u2139");break}let r=`${n} ${e}`;de.write(r+`
138
+ `)}function x(e,t){if(!(process.env.NEBUTRA_DEBUG||process.argv.includes("--debug")))return;let r=ee.dim(`[debug] ${e}`);de.write(r+`
139
+ `),t&&Object.keys(t).length>0&&de.write(ee.dim(JSON.stringify(t,null,2))+`
140
+ `)}function st(e){de.write(`
141
+ `),de.write(ee.bold(ee.cyan(e))+`
142
+ `),de.write(ee.gray("-".repeat(e.length))+`
143
+ `),de.write(`
144
+ `)}var K=2e4;function ga(e){if(process.env.DATABASE_URL)return!0;for(let t of[".env.local",".env"]){let n=fa(e,t);if(ma(n))try{let r=pa(n,"utf-8");if(/^DATABASE_URL\s*=\s*\S+/m.test(r))return!0}catch{}}return!1}function Ne(){let e=y();ga(e)||ae("DATABASE_URL is not set (checked the environment, .env.local, and .env).","Set DATABASE_URL, or run `pnpm infra:up` for a local Postgres and add it to .env.local.")}async function ha(e){u("Generating Prisma client"+"...","info");let n=await ne("generate",{dryRun:e.dryRun,interactive:!e.dryRun});n.exitCode===0?u("Prisma client generated successfully","success"):(u("Failed to generate Prisma client","error"),process.exit(n.exitCode||1))}async function zt(e,t){let n=t||{};if(n.dryRun||Ne(),e){u(`Creating migration: ${e}`,"info");let r=await ne("migrate",{args:["dev","--name",e],dryRun:n.dryRun,interactive:!n.dryRun,timeoutMs:n.dryRun?void 0:K});r.timedOut&&ae(`Creating migration '${e}' timed out after ${K}ms.`,"Check that DATABASE_URL points to a reachable database."),r.exitCode===0?u(`Migration '${e}' created successfully`,"success"):(u(`Failed to create migration '${e}'`,"error"),process.exit(r.exitCode||1))}else{u("Running pending migrations...","info");let r=await ne("migrate",{args:["deploy"],dryRun:n.dryRun,interactive:!n.dryRun,timeoutMs:n.dryRun?void 0:K});r.timedOut&&ae(`Migration deploy timed out after ${K}ms.`,"Check that DATABASE_URL points to a reachable database."),r.exitCode===0?u("All pending migrations completed","success"):(u("Migration deployment failed","error"),process.exit(r.exitCode||1))}}async function ya(e){e.dryRun||Ne(),u("Pushing schema to database...","info");let t=await ne("db",{args:["push"],dryRun:e.dryRun,interactive:!e.dryRun,timeoutMs:e.dryRun?void 0:K});t.timedOut&&ae(`Schema push timed out after ${K}ms.`,"Check that DATABASE_URL points to a reachable database."),t.exitCode===0?u("Schema pushed successfully","success"):(u("Failed to push schema","error"),process.exit(t.exitCode||1))}async function ba(e){e.dryRun||Ne(),u("Seeding database...","info");let t=await ne("db",{args:["seed"],dryRun:e.dryRun,interactive:!e.dryRun,timeoutMs:e.dryRun?void 0:K});t.timedOut&&ae(`Seeding timed out after ${K}ms.`,"Check that DATABASE_URL points to a reachable database."),t.exitCode===0?u("Database seeded successfully","success"):(u("Failed to seed database","error"),process.exit(t.exitCode||1))}async function va(e){e.dryRun||Ne(),u("Launching Prisma Studio...","info");let t=await ne("studio",{dryRun:e.dryRun,interactive:!0});t.exitCode!==0&&(u("Failed to launch Prisma Studio","error"),process.exit(t.exitCode||1))}async function wa(e){let t=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!e.yes&&!e.dryRun)if(t){let r=await ct.confirm({message:"Reset database? This will delete all data.",initialValue:!1});(ct.isCancel(r)||!r)&&(u("Reset cancelled","warn"),process.exit(l.CANCELLED))}else u("Database reset requires explicit --yes confirmation (use: nebutra db reset --yes)","error"),process.exit(l.INVALID_ARGS);e.dryRun||Ne(),u("Resetting database...","warn");let n=await ne("migrate",{args:["reset",...e.yes?["--force"]:[]],dryRun:e.dryRun,interactive:!e.dryRun,timeoutMs:e.dryRun?void 0:K});n.timedOut&&ae(`Database reset timed out after ${K}ms.`,"Check that DATABASE_URL points to a reachable database."),n.exitCode===0?u("Database reset successfully","success"):(u("Failed to reset database","error"),process.exit(n.exitCode||1))}async function ka(e){e.dryRun||Ne(),u("Checking migration status...","info");let t=await ne("migrate",{args:["status"],dryRun:e.dryRun,interactive:!1,timeoutMs:e.dryRun?void 0:K});if(t.timedOut&&ae(`\`db status\` timed out after ${K}ms waiting on the database.`,"Check that DATABASE_URL points to a reachable database."),t.exitCode===0)if(e.format==="json")try{let n=JSON.parse(t.stdout);process.stdout.write(JSON.stringify(n,null,2)+`
145
+ `)}catch{process.stdout.write(t.stdout)}else e.dryRun||(t.stdout.trim()&&process.stdout.write(t.stdout),t.stderr.trim()&&process.stderr.write(t.stderr));else u("Failed to check migration status","error"),t.stderr.trim()&&x("Prisma stderr",{stderr:t.stderr}),process.exit(t.exitCode||1)}function pr(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,i)=>{let a=i.optsWithGlobals?.(),s={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:a?.format||o.format};try{switch(n){case"generate":await ha(s);break;case"migrate":r.length>0?r[0]==="create"&&r[1]?await zt(r[1],s):await zt(r[0],s):await zt(void 0,s);break;case"push":await ya(s);break;case"seed":await ba(s);break;case"studio":await va(s);break;case"reset":await wa(s);break;case"status":await ka(s);break;default:u(`Unknown db subcommand: ${n}. Valid commands: generate, migrate, migrate create, push, seed, studio, reset, status`,"error"),process.exit(l.INVALID_ARGS)}}catch(c){let m=c instanceof Error?c.message:String(c);u(`Database command failed: ${m}`,"error"),x("Full error",{error:c}),process.exit(l.ERROR)}}).addHelpText("after",`
143
146
  Examples:
144
147
  nebutra db generate Generate Prisma client
145
148
  nebutra db migrate Run all pending migrations
@@ -154,15 +157,15 @@ Flags:
154
157
  --dry-run Show what would be run without executing
155
158
  --yes Skip confirmations
156
159
  --format <type> Output format: json, plain (default: plain)
157
- `)}import*as S from"@clack/prompts";import v from"picocolors";function A(e,t){let n=t&&typeof t.optsWithGlobals=="function"?t.optsWithGlobals():void 0,r=typeof e.optsWithGlobals=="function"?e.optsWithGlobals():void 0,o=n??r??{},i={};for(let[a,s]of Object.entries(e))a==="optsWithGlobals"||s===void 0||t?.getOptionValueSource?.(a)==="default"||(i[a]=s);return{...o,...i}}function ue(e){if(e==="json"||e==="plain"||e==="table")return e}var Ae=["landing","web","storybook","design-docs","idp","mail-preview","gateway","admin","auth","design","forge","router","pebble","typelens","sailor-docs","studio","sleptons"];function et(e){let t=`@nebutra/${e}`;if(!Ae.includes(e)){let n=v.yellow(`Valid apps: ${Ae.join(", ")}`);throw new Error(`Invalid app: ${v.red(e)}
158
- ${n}`)}return t}async function ji(e){e.dryRun&&(I({mode:"dry-run",timestamp:new Date().toISOString(),command:"dev",options:{app:e.app},task:e.app?"turbo dev":"turbo dev (all apps)"},{format:ue(e.format)}),process.exit(l.DRY_RUN_OK));try{if(e.app){et(e.app);let n=`@nebutra/${e.app}`;e.quiet||S.log.info(v.cyan(`Starting dev server for ${v.bold(n)}...`));let r=await ae("dev",{filter:n});process.exit(r.exitCode)}e.quiet||S.log.info(v.cyan("Starting dev servers for all apps..."));let t=await ae("dev");process.exit(t.exitCode)}catch(t){t instanceof Error?S.log.error(v.red(t.message)):S.log.error(v.red("Unknown error occurred")),process.exit(l.ERROR)}}async function Fi(e){e.dryRun&&(I({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:ue(e.format)}),process.exit(l.DRY_RUN_OK));try{if(e.app&&e.strict&&(S.log.error(v.red("Cannot use --strict with --app filter")),process.exit(l.INVALID_ARGS)),e.app){et(e.app);let r=`@nebutra/${e.app}`;e.quiet||S.log.info(v.cyan(`Building ${v.bold(r)}...`));let o=await ae("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...";S.log.info(v.cyan(r))}let n=await j(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?S.log.error(v.red(t.message)):S.log.error(v.red("Unknown error occurred")),process.exit(l.ERROR)}}async function Di(e){e.dryRun&&(I({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:ue(e.format)}),process.exit(l.DRY_RUN_OK));try{if(e.app&&e.fix&&(S.log.error(v.red("Cannot use --fix with --app filter (lint:fix is global only)")),process.exit(l.INVALID_ARGS)),e.app){et(e.app);let r=`@nebutra/${e.app}`;e.quiet||S.log.info(v.cyan(`Linting ${v.bold(r)}...`));let o=await E({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...";S.log.info(v.cyan(r))}let n=await j(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?S.log.error(v.red(t.message)):S.log.error(v.red("Unknown error occurred")),process.exit(l.ERROR)}}async function Ti(e){e.dryRun&&(I({mode:"dry-run",timestamp:new Date().toISOString(),command:"typecheck",options:{app:e.app},task:"turbo typecheck"},{format:ue(e.format)}),process.exit(l.DRY_RUN_OK));try{if(e.app){et(e.app);let n=`@nebutra/${e.app}`;e.quiet||S.log.info(v.cyan(`Type-checking ${v.bold(n)}...`));let r=await ae("typecheck",{filter:n});process.exit(r.exitCode)}e.quiet||S.log.info(v.cyan("Type-checking all packages..."));let t=await ae("typecheck");process.exit(t.exitCode)}catch(t){t instanceof Error?S.log.error(v.red(t.message)):S.log.error(v.red("Unknown error occurred")),process.exit(l.ERROR)}}var jn=e=>{e.command("dev").description("Start development servers (turbo dev)").option("--app <name>",`App to run: ${Ae.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async(t,n)=>{let r=A(t,n);await ji({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,quiet:!!r.quiet,verbose:!!r.verbose,format:ue(r.format)})}),e.command("build").description("Build all apps (pnpm build or turbo build)").option("--app <name>",`App to build: ${Ae.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,n)=>{let r=A(t,n);await Fi({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,strict:!!t.strict,quiet:!!r.quiet,verbose:!!r.verbose,format:ue(r.format)})}),e.command("lint").description("Lint with Biome (pnpm lint or pnpm lint:fix)").option("--app <name>",`App to lint: ${Ae.join(", ")}`).option("--fix","Fix linting issues automatically").option("--dry-run","Preview what would run (exit code 10)").action(async(t,n)=>{let r=A(t,n);await Di({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,fix:!!t.fix,quiet:!!r.quiet,verbose:!!r.verbose,format:ue(r.format)})}),e.command("typecheck").description("Type-check with TypeScript (turbo typecheck)").option("--app <name>",`App to typecheck: ${Ae.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async(t,n)=>{let r=A(t,n);await Ti({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,quiet:!!r.quiet,verbose:!!r.verbose,format:ue(r.format)})})};import Fn from"fs";import Dn from"path";import Nt from"picocolors";var $t=["smoke","golden","sleptons"],Li={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 Ui(e,t={}){$t.includes(e)||(c.error(`Invalid suite: ${Nt.red(e)}. Valid: ${$t.join(", ")}`),process.exit(l.INVALID_ARGS));let n=y(),{config:r,suiteDir:o}=Li[e],i=Dn.join(n,r),a=Dn.join(n,o);(!Fn.existsSync(a)||!Fn.existsSync(i))&&(c.error(`Suite ${Nt.red(e)} not found at ${o}.
160
+ `)}import*as S from"@clack/prompts";import v from"picocolors";function I(e,t){let n=t&&typeof t.optsWithGlobals=="function"?t.optsWithGlobals():void 0,r=typeof e.optsWithGlobals=="function"?e.optsWithGlobals():void 0,o=n??r??{},i={};for(let[a,s]of Object.entries(e))a==="optsWithGlobals"||s===void 0||t?.getOptionValueSource?.(a)==="default"||(i[a]=s);return{...o,...i}}function ue(e){if(e==="json"||e==="plain"||e==="table")return e}var $e=["landing","web","storybook","design-docs","idp","mail-preview","gateway","admin","auth","design","forge","router","pebble","typelens","sailor-docs","studio","sleptons"];function lt(e){let t=`@nebutra/${e}`;if(!$e.includes(e)){let n=v.yellow(`Valid apps: ${$e.join(", ")}`);throw new Error(`Invalid app: ${v.red(e)}
161
+ ${n}`)}return t}async function xa(e){e.dryRun&&(N({mode:"dry-run",timestamp:new Date().toISOString(),command:"dev",options:{app:e.app},task:e.app?"turbo dev":"turbo dev (all apps)"},{format:ue(e.format)}),process.exit(l.DRY_RUN_OK));try{if(e.app){lt(e.app);let n=`@nebutra/${e.app}`;e.quiet||S.log.info(v.cyan(`Starting dev server for ${v.bold(n)}...`));let r=await se("dev",{filter:n});process.exit(r.exitCode)}e.quiet||S.log.info(v.cyan("Starting dev servers for all apps..."));let t=await se("dev");process.exit(t.exitCode)}catch(t){t instanceof Error?S.log.error(v.red(t.message)):S.log.error(v.red("Unknown error occurred")),process.exit(l.ERROR)}}async function Ca(e){e.dryRun&&(N({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:ue(e.format)}),process.exit(l.DRY_RUN_OK));try{if(e.app&&e.strict&&(S.log.error(v.red("Cannot use --strict with --app filter")),process.exit(l.INVALID_ARGS)),e.app){lt(e.app);let r=`@nebutra/${e.app}`;e.quiet||S.log.info(v.cyan(`Building ${v.bold(r)}...`));let o=await se("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...";S.log.info(v.cyan(r))}let n=await F(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?S.log.error(v.red(t.message)):S.log.error(v.red("Unknown error occurred")),process.exit(l.ERROR)}}async function Sa(e){e.dryRun&&(N({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:ue(e.format)}),process.exit(l.DRY_RUN_OK));try{if(e.app&&e.fix&&(S.log.error(v.red("Cannot use --fix with --app filter (lint:fix is global only)")),process.exit(l.INVALID_ARGS)),e.app){lt(e.app);let r=`@nebutra/${e.app}`;e.quiet||S.log.info(v.cyan(`Linting ${v.bold(r)}...`));let o=await R({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...";S.log.info(v.cyan(r))}let n=await F(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?S.log.error(v.red(t.message)):S.log.error(v.red("Unknown error occurred")),process.exit(l.ERROR)}}async function Ra(e){e.dryRun&&(N({mode:"dry-run",timestamp:new Date().toISOString(),command:"typecheck",options:{app:e.app},task:"turbo typecheck"},{format:ue(e.format)}),process.exit(l.DRY_RUN_OK));try{if(e.app){lt(e.app);let n=`@nebutra/${e.app}`;e.quiet||S.log.info(v.cyan(`Type-checking ${v.bold(n)}...`));let r=await se("typecheck",{filter:n});process.exit(r.exitCode)}e.quiet||S.log.info(v.cyan("Type-checking all packages..."));let t=await se("typecheck");process.exit(t.exitCode)}catch(t){t instanceof Error?S.log.error(v.red(t.message)):S.log.error(v.red("Unknown error occurred")),process.exit(l.ERROR)}}var fr=e=>{e.command("dev").description("Start development servers (turbo dev)").option("--app <name>",`App to run: ${$e.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async(t,n)=>{let r=I(t,n);await xa({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,quiet:!!r.quiet,verbose:!!r.verbose,format:ue(r.format)})}),e.command("build").description("Build all apps (pnpm build or turbo build)").option("--app <name>",`App to build: ${$e.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,n)=>{let r=I(t,n);await Ca({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,strict:!!t.strict,quiet:!!r.quiet,verbose:!!r.verbose,format:ue(r.format)})}),e.command("lint").description("Lint with Biome (pnpm lint or pnpm lint:fix)").option("--app <name>",`App to lint: ${$e.join(", ")}`).option("--fix","Fix linting issues automatically").option("--dry-run","Preview what would run (exit code 10)").action(async(t,n)=>{let r=I(t,n);await Sa({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,fix:!!t.fix,quiet:!!r.quiet,verbose:!!r.verbose,format:ue(r.format)})}),e.command("typecheck").description("Type-check with TypeScript (turbo typecheck)").option("--app <name>",`App to typecheck: ${$e.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async(t,n)=>{let r=I(t,n);await Ra({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,quiet:!!r.quiet,verbose:!!r.verbose,format:ue(r.format)})})};import gr from"fs";import hr from"path";import Kt from"picocolors";var Wt=["smoke","golden","sleptons"],Ea={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 Oa(e,t={}){Wt.includes(e)||(d.error(`Invalid suite: ${Kt.red(e)}. Valid: ${Wt.join(", ")}`),process.exit(l.INVALID_ARGS));let n=y(),{config:r,suiteDir:o}=Ea[e],i=hr.join(n,r),a=hr.join(n,o);(!gr.existsSync(a)||!gr.existsSync(i))&&(d.error(`Suite ${Kt.red(e)} not found at ${o}.
159
162
  Expected config: ${r}
160
163
  Remediation: scaffold the suite (analogous to 'nebutra workflow init <provider>')
161
- or restore the directory from git.`),process.exit(l.NOT_FOUND)),t.dryRun&&(I({mode:"dry-run",command:`e2e ${e}`,task:`pnpm exec playwright test --config ${r}`}),process.exit(l.DRY_RUN_OK)),t.quiet||c.info(`Running Playwright suite: ${Nt.bold(e)}`);let s=await E({command:"pnpm",args:["exec","playwright","test","--config",r],interactive:!0,label:`playwright ${e}`});process.exit(s.exitCode)}var Tn=e=>{e.command("e2e <suite>").description(`Run a Playwright suite (${$t.join(" | ")})`).option("--dry-run","Preview the command that would run (exit code 10)").action(async(t,n,r)=>{let o=A(n,r);await Ui(t,{dryRun:!!n.dryRun,quiet:!!o.quiet})})};import{existsSync as tt,readFileSync as Ln,writeFileSync as Bi}from"fs";import{resolve as nt}from"path";import*as he from"@clack/prompts";import Mi from"picocolors";function rt(e){let t=nt(e,".env.example");if(!tt(t))throw new Error(`.env.example not found at ${t}`);let n=Ln(t,"utf-8"),r={};for(let o of n.split(`
162
- `)){if(o.trim().startsWith("#")||!o.trim())continue;let i=o.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);if(i){let[,a,s]=i;r[a]=s.replace(/^["'](.*)["']$/,"$1")}}return r}function jt(e){let t=nt(e,".env.local"),n=nt(e,".env"),r=tt(t)?t:n;if(!tt(r))return{};let o=Ln(r,"utf-8"),i={};for(let a of o.split(`
163
- `)){if(a.trim().startsWith("#")||!a.trim())continue;let s=a.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);if(s){let[,u,m]=s;i[u]=m.replace(/^["'](.*)["']$/,"$1")}}return i}function Gi(e,t){return["SECRET","KEY","TOKEN","PASSWORD"].some(o=>e.toUpperCase().includes(o))&&t.length>0}function Vi(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 zi(e){d("Validating environment variables...","info");let t=y(),n=rt(t),r=jt(t),o=process.env,i={},a=!0;for(let s of Object.keys(n)){let u=s in r||s in o;i[s]=u,u||(a=!1)}if(e.format==="json")b({valid:a,checked:Object.keys(n).length,missing:Object.entries(i).filter(([,s])=>!s).map(([s])=>s)},{format:"json"});else{Qe("Environment Validation");let s=Object.entries(i).filter(([,u])=>!u).map(([u])=>u);if(s.length===0)d("All required environment variables are set","success");else{d(`${s.length} missing environment variable(s):`,"warn");for(let u of s)d(` - ${u}`,"warn")}}a||process.exit(l.CONFIG_ERROR)}async function Ki(e){let t=y(),n=rt(t),r=nt(t,".env.local");if(tt(r)&&!e.yes&&!e.dryRun){let u=await he.confirm({message:".env.local already exists. Overwrite?",initialValue:!1});(he.isCancel(u)||!u)&&(d("Template generation cancelled","warn"),process.exit(l.CANCELLED))}d("Generating .env.local from template...","info");let o={},i=0,a=Object.keys(n).length;for(let[u,m]of Object.entries(n))if(i++,e.yes)o[u]=m;else if(process.stdin.isTTY===!0){let p=`[${i}/${a}] ${u}`,f=await he.text({message:p,defaultValue:m});he.isCancel(f)&&(d("Template generation cancelled","warn"),process.exit(l.CANCELLED)),o[u]=f}else o[u]=m;let s=Object.entries(o).map(([u,m])=>{let f=/[\s=]/.test(m)?`"${m}"`:m;return`${u}=${f}`}).join(`
164
- `);e.dryRun?(d("Dry-run: would write .env.local","info"),b(s,{format:"plain"})):(Bi(r,s+`
165
- `),d(`Environment template written to ${r}`,"success"))}async function Wi(e){d("Comparing environment variables...","info");let t=y(),n=rt(t),r=jt(t),o=[],i=[],a=[];for(let s of Object.keys(n))s in r?a.push(s):o.push(s);for(let s of Object.keys(r))s in n||i.push(s);if(e.format==="json")b({matching:a.length,missing:o.length,extra:i.length,missing_vars:o,extra_vars:i},{format:"json"});else{if(Qe("Environment Diff"),a.length>0&&d(`Matching (${a.length}): ${a.join(", ")}`,"success"),o.length>0){d(`Missing (${o.length}):`,"warn");for(let s of o)d(` - ${s}`,"warn")}if(i.length>0){d(`Extra (${i.length}):`,"info");for(let s of i)d(` - ${s}`,"info")}}}async function Yi(e){d("Reading environment variables...","info");let t=y(),n=jt(t),r=rt(t),o={};for(let i of Object.keys(r))if(i in n){let a=n[i];o[i]=Gi(i,a)?Vi(a):a||"(empty)"}else o[i]="(not set)";if(e.format==="json"){let i={};for(let[a,s]of Object.entries(o))i[a]=s;b(i,{format:"json"})}else{Qe("Current Environment Variables");let i=Object.entries(o).map(([a,s])=>({Variable:Mi.cyan(a),Value:s}));b(i,{format:"table"}),d("Note: Sensitive values (KEY, TOKEN, SECRET, PASSWORD) are masked","info")}}function Un(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,o)=>{let i=o.optsWithGlobals?.(),a={dryRun:r.dryRun||i?.dryRun,yes:r.yes||i?.yes,format:i?.format||r.format};try{switch(n){case"validate":await zi(a);break;case"template":await Ki(a);break;case"diff":await Wi(a);break;case"show":await Yi(a);break;default:d(`Unknown env subcommand: ${n}. Valid commands: validate, template, diff, show`,"error"),process.exit(l.INVALID_ARGS)}}catch(s){let u=s instanceof Error?s.message:String(s);d(`Environment command failed: ${u}`,"error"),x("Full error",{error:s}),process.exit(l.ERROR)}}).addHelpText("after",`
164
+ or restore the directory from git.`),process.exit(l.NOT_FOUND)),t.dryRun&&(N({mode:"dry-run",command:`e2e ${e}`,task:`pnpm exec playwright test --config ${r}`}),process.exit(l.DRY_RUN_OK)),t.quiet||d.info(`Running Playwright suite: ${Kt.bold(e)}`);let s=await R({command:"pnpm",args:["exec","playwright","test","--config",r],interactive:!0,label:`playwright ${e}`});process.exit(s.exitCode)}var yr=e=>{e.command("e2e <suite>").description(`Run a Playwright suite (${Wt.join(" | ")})`).option("--dry-run","Preview the command that would run (exit code 10)").action(async(t,n,r)=>{let o=I(n,r);await Oa(t,{dryRun:!!n.dryRun,quiet:!!o.quiet})})};import{existsSync as dt,readFileSync as br,writeFileSync as Aa}from"fs";import{resolve as ut}from"path";import*as we from"@clack/prompts";import _a from"picocolors";function mt(e){let t=ut(e,".env.example");if(!dt(t))throw new Error(`.env.example not found at ${t}`);let n=br(t,"utf-8"),r={};for(let o of n.split(`
165
+ `)){if(o.trim().startsWith("#")||!o.trim())continue;let i=o.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);if(i){let[,a,s]=i;r[a]=s.replace(/^["'](.*)["']$/,"$1")}}return r}function Yt(e){let t=ut(e,".env.local"),n=ut(e,".env"),r=dt(t)?t:n;if(!dt(r))return{};let o=br(r,"utf-8"),i={};for(let a of o.split(`
166
+ `)){if(a.trim().startsWith("#")||!a.trim())continue;let s=a.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);if(s){let[,c,m]=s;i[c]=m.replace(/^["'](.*)["']$/,"$1")}}return i}function Ia(e,t){return["SECRET","KEY","TOKEN","PASSWORD"].some(o=>e.toUpperCase().includes(o))&&t.length>0}function Pa(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 Na(e){u("Validating environment variables...","info");let t=y(),n=mt(t),r=Yt(t),o=process.env,i={},a=!0;for(let s of Object.keys(n)){let c=s in r||s in o;i[s]=c,c||(a=!1)}if(e.format==="json")b({valid:a,checked:Object.keys(n).length,missing:Object.entries(i).filter(([,s])=>!s).map(([s])=>s)},{format:"json"});else{st("Environment Validation");let s=Object.entries(i).filter(([,c])=>!c).map(([c])=>c);if(s.length===0)u("All required environment variables are set","success");else{u(`${s.length} missing environment variable(s):`,"warn");for(let c of s)u(` - ${c}`,"warn")}}a||process.exit(l.CONFIG_ERROR)}async function $a(e){let t=y(),n=mt(t),r=ut(t,".env.local");if(dt(r)&&!e.yes&&!e.dryRun){let c=await we.confirm({message:".env.local already exists. Overwrite?",initialValue:!1});(we.isCancel(c)||!c)&&(u("Template generation cancelled","warn"),process.exit(l.CANCELLED))}u("Generating .env.local from template...","info");let o={},i=0,a=Object.keys(n).length;for(let[c,m]of Object.entries(n))if(i++,e.yes)o[c]=m;else if(process.stdin.isTTY===!0){let p=`[${i}/${a}] ${c}`,g=await we.text({message:p,defaultValue:m});we.isCancel(g)&&(u("Template generation cancelled","warn"),process.exit(l.CANCELLED)),o[c]=g}else o[c]=m;let s=Object.entries(o).map(([c,m])=>{let g=/[\s=]/.test(m)?`"${m}"`:m;return`${c}=${g}`}).join(`
167
+ `);e.dryRun?(u("Dry-run: would write .env.local","info"),b(s,{format:"plain"})):(Aa(r,s+`
168
+ `),u(`Environment template written to ${r}`,"success"))}async function ja(e){u("Comparing environment variables...","info");let t=y(),n=mt(t),r=Yt(t),o=[],i=[],a=[];for(let s of Object.keys(n))s in r?a.push(s):o.push(s);for(let s of Object.keys(r))s in n||i.push(s);if(e.format==="json")b({matching:a.length,missing:o.length,extra:i.length,missing_vars:o,extra_vars:i},{format:"json"});else{if(st("Environment Diff"),a.length>0&&u(`Matching (${a.length}): ${a.join(", ")}`,"success"),o.length>0){u(`Missing (${o.length}):`,"warn");for(let s of o)u(` - ${s}`,"warn")}if(i.length>0){u(`Extra (${i.length}):`,"info");for(let s of i)u(` - ${s}`,"info")}}}async function Ta(e){u("Reading environment variables...","info");let t=y(),n=Yt(t),r=mt(t),o={};for(let i of Object.keys(r))if(i in n){let a=n[i];o[i]=Ia(i,a)?Pa(a):a||"(empty)"}else o[i]="(not set)";if(e.format==="json"){let i={};for(let[a,s]of Object.entries(o))i[a]=s;b(i,{format:"json"})}else{st("Current Environment Variables");let i=Object.entries(o).map(([a,s])=>({Variable:_a.cyan(a),Value:s}));b(i,{format:"table"}),u("Note: Sensitive values (KEY, TOKEN, SECRET, PASSWORD) are masked","info")}}function vr(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,o)=>{let i=o.optsWithGlobals?.(),a={dryRun:r.dryRun||i?.dryRun,yes:r.yes||i?.yes,format:i?.format||r.format};try{switch(n){case"validate":await Na(a);break;case"template":await $a(a);break;case"diff":await ja(a);break;case"show":await Ta(a);break;default:u(`Unknown env subcommand: ${n}. Valid commands: validate, template, diff, show`,"error"),process.exit(l.INVALID_ARGS)}}catch(s){let c=s instanceof Error?s.message:String(s);u(`Environment command failed: ${c}`,"error"),x("Full error",{error:s}),process.exit(l.ERROR)}}).addHelpText("after",`
166
169
  Examples:
167
170
  nebutra env validate Check all required vars are set
168
171
  nebutra env template Generate .env.local from .env.example with prompts
@@ -179,7 +182,7 @@ Flags:
179
182
  Sensitive Variables:
180
183
  Variables with KEY, TOKEN, SECRET, PASSWORD in their names are masked in output.
181
184
  This prevents accidental exposure of credentials.
182
- `)}import{promises as Bn}from"fs";import{join as ye}from"path";import*as Ue from"@clack/prompts";import _ from"picocolors";var Le=["design","iam","commerce","integrations","platform","ops","ai"];function Ji(e){return Le.includes(e)}function Ft(e,t){if(!t)throw new Error(`--category is required. Valid categories: ${Le.join(", ")}. Example: nebutra generate package ${e} --category iam`);if(!Ji(t))throw new Error(`Unknown category "${t}". Valid categories: ${Le.join(", ")}.`);return`packages/${t}/${e}`}function Mn(e){let t=e.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("");return{"package.json":`{
185
+ `)}import{promises as wr}from"fs";import{join as ke}from"path";import*as Ge from"@clack/prompts";import P from"picocolors";var Me=["design","iam","commerce","integrations","platform","ops","ai"];function Fa(e){return Me.includes(e)}function Jt(e,t){if(!t)throw new Error(`--category is required. Valid categories: ${Me.join(", ")}. Example: nebutra generate package ${e} --category iam`);if(!Fa(t))throw new Error(`Unknown category "${t}". Valid categories: ${Me.join(", ")}.`);return`packages/${t}/${e}`}function kr(e){let t=e.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("");return{"package.json":`{
183
186
  "name": "@nebutra/${e}",
184
187
  "version": "0.0.0",
185
188
  "private": true,
@@ -264,7 +267,7 @@ export default function RootLayout({
264
267
 
265
268
  @theme {
266
269
  --color-*: var(--neutral-*);
267
- }`}}function Gn(e){return{"package.json":`{
270
+ }`}}function xr(e){return{"package.json":`{
268
271
  "name": "@nebutra/${e}",
269
272
  "version": "0.0.0",
270
273
  "type": "module",
@@ -301,7 +304,7 @@ export default function RootLayout({
301
304
 
302
305
  export function hello(name: string): string {
303
306
  return \`Hello, \${name}!\`;
304
- }`}}function Vn(e){let t=e.replace(/\//g,"_").replace(/-/g,"_");return{[`src/routes/${e}.ts`]:`import { Hono } from "hono";
307
+ }`}}function Cr(e){let t=e.replace(/\//g,"_").replace(/-/g,"_");return{[`src/routes/${e}.ts`]:`import { Hono } from "hono";
305
308
  import { z } from "zod";
306
309
 
307
310
  type Env = {
@@ -352,7 +355,7 @@ app.post("/", async (c) => {
352
355
  );
353
356
  });
354
357
 
355
- export default app;`}}function zn(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";
358
+ export default app;`}}function Sr(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";
356
359
  import { cn } from "@nebutra/ui/utils";
357
360
 
358
361
  const ${t}Variants = cva(
@@ -426,13 +429,13 @@ export const AllSizes: Story = {
426
429
  <${n} size="lg">Large</\${pascalCase}>
427
430
  </div>
428
431
  ),
429
- };`}}async function ot(e,t,n){let r=y(),o=[],i={};switch(e){case"app":i=Mn(t);break;case"package":i=Gn(t);break;case"route":i=Vn(t);break;case"component":i=zn(t);break;default:throw new Error(`Unknown generate type: ${e}`)}let a=e==="package"?Ft(t,n.category):void 0;for(let[s,u]of Object.entries(i)){let m=e==="app"?ye(r,"apps",t,s):e==="package"?ye(r,a??`packages/${t}`,s):ye(r,"backends/gateway",s);o.push({path:m,size:Buffer.byteLength(u)})}return o}async function it(e,t,n={}){let r=y(),o=[],i={};switch(e){case"app":i=Mn(t);break;case"package":i=Gn(t);break;case"route":i=Vn(t);break;case"component":i=zn(t);break;default:throw new Error(`Unknown generate type: ${e}`)}let a=e==="package"?Ft(t,n.category):void 0;for(let[s,u]of Object.entries(i)){let m;e==="app"?m=ye(r,"apps",t,s):e==="package"?m=ye(r,a??`packages/${t}`,s):e==="component"?m=ye(r,"packages/design/ui",s):m=ye(r,"backends/gateway",s);let p=m.substring(0,m.lastIndexOf("/"));await Bn.mkdir(p,{recursive:!0}),await Bn.writeFile(m,u,"utf-8"),o.push(m)}return o}async function qi(e,t){if(t.dryRun){let r=await ot("app",e,t),o={mode:"dry-run",timestamp:new Date().toISOString(),type:"app",name:e,location:`apps/${e}`,files:r.map(i=>({path:i.path,sizeBytes:i.size})),instructions:[`cd apps/${e}`,"pnpm install","pnpm run dev"]};process.stdout.write(JSON.stringify(o,null,2)+`
430
- `),process.exit(l.DRY_RUN_OK)}Ue.intro(_.bgCyan(_.black(" nebutra generate app ")));let n=c.spinner();try{n.start(`Generating app ${_.cyan(e)}...`);let r=await it("app",e);n.stop(`Created app ${_.cyan(e)}`,0),c.success(`App created with ${r.length} files`),c.info("Next steps:"),c.info(` cd apps/${e}`),c.info(" pnpm install"),c.info(" pnpm run dev"),process.exit(l.SUCCESS)}catch(r){n.stop(`Failed to generate app: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(l.ERROR)}}async function Hi(e,t){let n;try{n=Ft(e,t.category)}catch(o){c.error(o instanceof Error?o.message:String(o)),process.exit(l.INVALID_ARGS)}if(t.dryRun){let o=await ot("package",e,t),i={mode:"dry-run",timestamp:new Date().toISOString(),type:"package",name:e,category:t.category,location:n,files:o.map(a=>({path:a.path,sizeBytes:a.size}))};process.stdout.write(JSON.stringify(i,null,2)+`
431
- `),process.exit(l.DRY_RUN_OK)}Ue.intro(_.bgCyan(_.black(" nebutra generate package ")));let r=c.spinner();try{r.start(`Generating package ${_.cyan(e)} in ${_.dim(n)}...`);let o=await it("package",e,{category:t.category});r.stop(`Created package ${_.cyan(e)}`,0),c.success(`Package created with ${o.length} files`),c.info("Next steps:"),c.info(` Edit ${n}/src/index.ts`),c.info(` Run: pnpm --filter @nebutra/${e} build`),process.exit(l.SUCCESS)}catch(o){r.stop(`Failed to generate package: ${o instanceof Error?o.message:"Unknown error"}`,1),process.exit(l.ERROR)}}async function Zi(e,t){if(t.dryRun){let r=await ot("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(i=>({path:i.path,sizeBytes:i.size}))};process.stdout.write(JSON.stringify(o,null,2)+`
432
- `),process.exit(l.DRY_RUN_OK)}Ue.intro(_.bgCyan(_.black(" nebutra generate route ")));let n=c.spinner();try{n.start(`Generating route ${_.cyan(e)}...`);let r=await it("route",e);n.stop(`Created route ${_.cyan(e)}`,0),c.success(`Route created with ${r.length} files`),c.info(`File: backends/gateway/src/routes/${e}.ts`),c.info(`Add to your main app.ts: app.route("/${e}", routes)`),process.exit(l.SUCCESS)}catch(r){n.stop(`Failed to generate route: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(l.ERROR)}}async function Qi(e,t){if(t.dryRun){let r=await ot("component",e,t),o={mode:"dry-run",timestamp:new Date().toISOString(),type:"component",name:e,location:"packages/design/ui/src/components/",files:r.map(i=>({path:i.path,sizeBytes:i.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)+`
433
- `),process.exit(l.DRY_RUN_OK)}Ue.intro(_.bgCyan(_.black(" nebutra generate component ")));let n=c.spinner();try{n.start(`Generating component ${_.cyan(e)}...`);let r=await it("component",e);n.stop(`Created component ${_.cyan(e)}`,0),c.success(`Component created with ${r.length} files`),c.info("Files created in packages/design/ui/src/components/"),c.info("Next steps:"),c.info(" 1. Add export to packages/design/ui/src/components/index.ts"),c.info(" 2. pnpm --filter @nebutra/ui typecheck"),c.info(" 3. pnpm --filter @nebutra/storybook dev"),process.exit(l.SUCCESS)}catch(r){n.stop(`Failed to generate component: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(l.ERROR)}}var Kn=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,o)=>{let i=A(r,o);await qi(n,{dryRun:!!r.dryRun,yes:!!i.yes,interactive:!!process.stdin.isTTY,template:(r.template==="nextjs","nextjs"),port:typeof r.port=="string"?Number(r.port):void 0})}),t.command("package <name>").description(`Scaffold a new categorized package (@nebutra/<name>). Categories: ${Le.join(", ")}`).requiredOption("--category <category>",`Package category: ${Le.join(" | ")}`).option("--dry-run","Preview changes without writing files").option("--yes","Skip all prompts").action(async(n,r,o)=>{let i=A(r,o);await Hi(n,{category:String(r.category??""),dryRun:!!r.dryRun,yes:!!i.yes})}),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,o)=>{let i=A(r,o);await Zi(n,{dryRun:!!r.dryRun,yes:!!i.yes})}),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,o)=>{let i=A(r,o);await Qi(n,{dryRun:!!r.dryRun,yes:!!i.yes,variants:typeof r.variants=="string"?r.variants:void 0,sizes:typeof r.sizes=="string"?r.sizes:void 0})})};import Dt,{lstatSync as Xi,readdirSync as Tt,readFileSync as ea,writeFileSync as ta}from"fs";import ne from"path";import U from"picocolors";function Lt(){let e=new Map,t=ne.join(y(),"apps");if(!Dt.existsSync(t))return e;try{let n=Tt(t);for(let r of n){let o=ne.join(t,r),i=ne.join(o,"messages");Dt.existsSync(i)&&Xi(i).isDirectory()&&e.set(r,i)}}catch(n){c.debug(`Error scanning apps directory: ${n instanceof Error?n.message:String(n)}`)}return e}function Be(e,t=""){let n=new Set;for(let[r,o]of Object.entries(e)){let i=t?`${t}.${r}`:r;if(typeof o=="object"&&o!==null&&!Array.isArray(o)){let a=Be(o,i);for(let s of a)n.add(s)}else n.add(i)}return n}function Me(e){try{let t=ea(e,"utf-8");return JSON.parse(t)}catch(t){return c.debug(`Failed to parse ${e}: ${t instanceof Error?t.message:String(t)}`),null}}async function na(e){if(c.info("Syncing language files across the project..."),(await j("translate",{filter:"landing",dryRun:e.dryRun,interactive:e.interactive??!0})).exitCode!==0){c.warn("Landing page translate script not found or failed. Trying sync-lang.mjs...");let n=await E({command:"node",args:["scripts/sync-lang.mjs"],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Sync language files"});n.exitCode!==0&&(c.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||c.success("Language files synced successfully")}else e.format==="json"?console.log(JSON.stringify({command:"i18n:sync",exitCode:0,success:!0},null,2)):e.dryRun||c.success("Language files synced successfully")}async function ra(e){c.info("Validating translation files...");let t=Lt();t.size===0&&(c.warn("No message directories found in the project"),process.exit(l.NOT_FOUND));let n=new Map,r=!1;for(let[o,i]of t){let a=[];try{let s=Tt(i).filter(f=>f.endsWith(".json"));if(s.length===0)continue;let u=ne.join(i,"en.json"),m=Me(u);if(!m){c.warn(`Could not load reference locale en.json from ${o}`);continue}let p=Be(m);for(let f of s){if(f==="en.json")continue;let h=f.replace(".json",""),g=ne.join(i,f),k=Me(g);if(!k){c.debug(`Could not parse ${f}`);continue}let F=Be(k),w=Array.from(p).filter(L=>!F.has(L)),W=Array.from(F).filter(L=>!p.has(L)),X=p.size>0?(p.size-w.length)/p.size*100:100;(w.length>0||W.length>0)&&(r=!0),a.push({locale:h,totalKeys:p.size,missingKeys:w,extraKeys:W,coverage:Math.round(X*100)/100})}a.length>0&&n.set(o,a)}catch(s){c.debug(`Error validating ${o}: ${s instanceof Error?s.message:String(s)}`)}}if(e.format==="json"){let o={command:"i18n:validate",success:!r,results:Array.from(n.entries()).reduce((i,[a,s])=>(i[a]=s,i),{})};console.log(JSON.stringify(o,null,2))}else for(let[o,i]of n){c.info(`${U.bold(o)}:`);for(let a of i){let u=(a.coverage>=100?U.green:a.coverage>=80?U.yellow:U.red)(`${a.coverage}%`);c.info(` ${U.cyan(a.locale.padEnd(5))} ${u.padEnd(20)} [${a.totalKeys} keys]`),a.missingKeys.length>0&&c.warn(` Missing: ${a.missingKeys.slice(0,5).join(", ")}${a.missingKeys.length>5?` (+ ${a.missingKeys.length-5} more)`:""}`),a.extraKeys.length>0&&c.warn(` Extra: ${a.extraKeys.slice(0,5).join(", ")}${a.extraKeys.length>5?` (+ ${a.extraKeys.length-5} more)`:""}`)}}r&&process.exit(l.CONFLICT)}async function oa(e,t){/^[a-z]{2}(-[A-Z]{2})?$/.test(e)||(c.error(`Invalid locale format: ${e}. Use 'en', 'es', 'zh-CN', etc.`),process.exit(l.INVALID_ARGS));let n=Lt();n.size===0&&(c.error("No message directories found in the project"),process.exit(l.NOT_FOUND));let r=[],o=!1;for(let[a,s]of n){let u=ne.join(s,`${e}.json`);if(Dt.existsSync(u)){c.warn(`Locale ${e} already exists in ${a}`);continue}try{let h=function(g){for(let k in g)typeof g[k]=="string"?g[k]="":typeof g[k]=="object"&&g[k]!==null&&h(g[k])};var i=h;let m=ne.join(s,"en.json"),p=Me(m);if(!p){c.warn(`Could not load reference en.json from ${a}, skipping...`);continue}let f=JSON.parse(JSON.stringify(p));h(f),t.dryRun?c.info(`Would create ${e} locale in ${a}`):(ta(u,JSON.stringify(f,null,2)+`
434
- `),r.push({app:a,path:u}),c.success(`Created ${e} locale in ${a}`))}catch(m){c.error(`Failed to create locale in ${a}: ${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||c.success(`Locale '${e}' added to ${r.length} app(s)`),o&&process.exit(l.ERROR)}async function ia(e){c.info("Checking translation coverage...");let t=Lt();t.size===0&&(c.warn("No message directories found in the project"),process.exit(l.NOT_FOUND));let n=new Map,r=0,o=0;for(let[i,a]of t){let s=[];try{let u=Tt(a).filter(h=>h.endsWith(".json"));if(u.length===0)continue;let m=ne.join(a,"en.json"),p=Me(m);if(!p){c.debug(`Could not load reference locale from ${i}`);continue}let f=Be(p);for(let h of u){let g=h.replace(".json",""),k=ne.join(a,h),F=Me(k);if(!F)continue;let w=Be(F),W=Array.from(w).filter(Se=>{let We=F[Se.split(".")[0]];return We!==void 0&&We!==""}).length,X=f.size-W,L=f.size>0?W/f.size*100:0;s.push({locale:g,coverage:Math.round(L*100)/100,totalKeys:f.size,translatedKeys:W,missingKeys:X})}s.length>0&&(n.set(i,s),o++,r+=s.reduce((h,g)=>h+g.coverage,0)/s.length)}catch(u){c.debug(`Error checking status for ${i}: ${u instanceof Error?u.message:String(u)}`)}}if(e.format==="json"){let i={command:"i18n:status",averageCoverage:o>0?Math.round(r/o*100)/100:0,results:Array.from(n.entries()).reduce((a,[s,u])=>(a[s]=u,a),{})};console.log(JSON.stringify(i,null,2))}else{for(let[i,a]of n){c.info(`${U.bold(i)}:`);for(let s of a){let u="",p=Math.round(s.coverage/100*20);u="\u2588".repeat(p)+"\u2591".repeat(20-p);let h=(s.coverage>=100?U.green:s.coverage>=80?U.yellow:U.red)(`${s.coverage.toFixed(1)}%`);c.info(` ${U.cyan(s.locale.padEnd(5))} [${u}] ${h}`),c.info(` ${s.translatedKeys}/${s.totalKeys} keys (${s.missingKeys} missing)`)}}if(o>0){let i=r/o,a=i>=100?U.green:i>=80?U.yellow:U.red;c.info(`
435
- Average coverage: ${a(i.toFixed(1)+"%")}`)}}}function Wn(e){let t=e.command("i18n").description("Manage internationalization and translations");t.command("sync").description("Synchronize language translations across the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async(n,r)=>{let o=r.optsWithGlobals?.()||n;await na({dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.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,r)=>{let o=r.optsWithGlobals?.()||n;await ra({format:n.format||o.format,interactive:o.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,o)=>{let i=o.optsWithGlobals?.()||r;await oa(n,{dryRun:r.dryRun||!1,format:r.format||i.format,interactive:i.yes!==!0})}),t.command("status").description("Show translation coverage per locale and app").option("--format <type>","Output format: json or plain").action(async(n,r)=>{let o=r.optsWithGlobals?.()||n;await ia({format:n.format||o.format,interactive:o.yes!==!0})})}import*as at from"@clack/prompts";async function aa(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 ge("up -d",{file:t,profile:e.profile,dryRun:e.dryRun});r.exitCode===0?d("Infrastructure stack started successfully","success"):(d(Ge(r)??"Failed to start infrastructure stack","error"),x("Docker compose output",{stderr:r.stderr}),process.exit(r.exitCode||1))}async function sa(e){d("Stopping infrastructure stack...","info");let t=await ge("down",{dryRun:e.dryRun});t.exitCode===0?d("Infrastructure stack stopped","success"):(d(Ge(t)??"Failed to stop infrastructure stack","error"),x("Docker compose output",{stderr:t.stderr}),process.exit(t.exitCode||1))}function Ge(e){let t=e.stderr||"";if(e.exitCode===127||/command not found|ENOENT/i.test(t))return"Docker CLI not found. Install Docker Desktop (or the docker CLI) and make sure it's on your PATH.";if(/cannot connect to the docker daemon|is the docker daemon running/i.test(t))return"Docker daemon is not running. Start Docker Desktop (or `dockerd`) and try again."}async function ca(e){d("Checking infrastructure status...","info");let t=await ge("ps --format json",{dryRun:e.dryRun,interactive:!1});if(e.dryRun)return;let n=t.exitCode!==0&&/unknown (flag|shorthand flag)/i.test(t.stderr);if(n&&(x("docker compose ps --format json unsupported, falling back to plain ps",{stderr:t.stderr}),t=await ge("ps",{interactive:!1})),t.exitCode!==0){let r=Ge(t);d(r??"Failed to check infrastructure status","error"),x("Docker compose output",{stderr:t.stderr}),process.exit(t.exitCode||1)}if(n){e.format==="json"&&d("This Docker Compose version doesn't support `--format json`; showing plain text instead.","warn"),b(t.stdout,{format:"plain"});return}try{let r=JSON.parse(t.stdout);if(e.format==="json")b(r,{format:"json"});else{let o=r.map(i=>({name:i.Name,status:i.Status,ports:i.Ports||"-"}));b(o,{format:"table"})}}catch{b(t.stdout,{format:"plain"})}}async function la(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 i=await ge(o.join(" "),{dryRun:n.dryRun});i.exitCode===0?b(i.stdout,{format:"plain"}):(d(Ge(i)??"Failed to fetch logs","error"),x("Docker compose output",{stderr:i.stderr}),process.exit(i.exitCode||1))}async function ua(e){let t=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!e.yes&&!e.dryRun)if(t){let r=await at.confirm({message:"Reset infrastructure? This will remove all containers and volumes.",initialValue:!1});(at.isCancel(r)||!r)&&(d("Reset cancelled","warn"),process.exit(l.CANCELLED))}else d("Infrastructure reset requires explicit --yes confirmation (use: nebutra infra reset --yes)","error"),process.exit(l.INVALID_ARGS);d("Resetting infrastructure stack...","warn");let n=await ge("down -v",{dryRun:e.dryRun});n.exitCode===0?d("Infrastructure reset successfully","success"):(d(Ge(n)??"Failed to reset infrastructure","error"),x("Docker compose output",{stderr:n.stderr}),process.exit(n.exitCode||1))}function Yn(e){e.command("infra <verb> [service]").description("Manage Docker Compose infrastructure (PostgreSQL, Redis, 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,i)=>{let a=i.optsWithGlobals?.(),s={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:a?.format||o.format,lite:o.lite||!1,profile:o.profile};try{switch(n){case"up":await aa(s);break;case"down":await sa(s);break;case"status":await ca(s);break;case"logs":await la(r,s);break;case"reset":await ua(s);break;default:d(`Unknown infra subcommand: ${n}. Valid commands: up, down, status, logs, reset`,"error"),process.exit(l.INVALID_ARGS)}}catch(u){let m=u instanceof Error?u.message:String(u);d(`Infrastructure command failed: ${m}`,"error"),x("Full error",{error:u}),process.exit(l.ERROR)}}).addHelpText("after",`
432
+ };`}}async function pt(e,t,n){let r=y(),o=[],i={};switch(e){case"app":i=kr(t);break;case"package":i=xr(t);break;case"route":i=Cr(t);break;case"component":i=Sr(t);break;default:throw new Error(`Unknown generate type: ${e}`)}let a=e==="package"?Jt(t,n.category):void 0;for(let[s,c]of Object.entries(i)){let m=e==="app"?ke(r,"apps",t,s):e==="package"?ke(r,a??`packages/${t}`,s):ke(r,"backends/gateway",s);o.push({path:m,size:Buffer.byteLength(c)})}return o}async function ft(e,t,n={}){let r=y(),o=[],i={};switch(e){case"app":i=kr(t);break;case"package":i=xr(t);break;case"route":i=Cr(t);break;case"component":i=Sr(t);break;default:throw new Error(`Unknown generate type: ${e}`)}let a=e==="package"?Jt(t,n.category):void 0;for(let[s,c]of Object.entries(i)){let m;e==="app"?m=ke(r,"apps",t,s):e==="package"?m=ke(r,a??`packages/${t}`,s):e==="component"?m=ke(r,"packages/design/ui",s):m=ke(r,"backends/gateway",s);let p=m.substring(0,m.lastIndexOf("/"));await wr.mkdir(p,{recursive:!0}),await wr.writeFile(m,c,"utf-8"),o.push(m)}return o}async function Da(e,t){if(t.dryRun){let r=await pt("app",e,t),o={mode:"dry-run",timestamp:new Date().toISOString(),type:"app",name:e,location:`apps/${e}`,files:r.map(i=>({path:i.path,sizeBytes:i.size})),instructions:[`cd apps/${e}`,"pnpm install","pnpm run dev"]};process.stdout.write(JSON.stringify(o,null,2)+`
433
+ `),process.exit(l.DRY_RUN_OK)}Ge.intro(P.bgCyan(P.black(" nebutra generate app ")));let n=d.spinner();try{n.start(`Generating app ${P.cyan(e)}...`);let r=await ft("app",e);n.stop(`Created app ${P.cyan(e)}`,0),d.success(`App created with ${r.length} files`),d.info("Next steps:"),d.info(` cd apps/${e}`),d.info(" pnpm install"),d.info(" pnpm run dev"),process.exit(l.SUCCESS)}catch(r){n.stop(`Failed to generate app: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(l.ERROR)}}async function La(e,t){let n;try{n=Jt(e,t.category)}catch(o){d.error(o instanceof Error?o.message:String(o)),process.exit(l.INVALID_ARGS)}if(t.dryRun){let o=await pt("package",e,t),i={mode:"dry-run",timestamp:new Date().toISOString(),type:"package",name:e,category:t.category,location:n,files:o.map(a=>({path:a.path,sizeBytes:a.size}))};process.stdout.write(JSON.stringify(i,null,2)+`
434
+ `),process.exit(l.DRY_RUN_OK)}Ge.intro(P.bgCyan(P.black(" nebutra generate package ")));let r=d.spinner();try{r.start(`Generating package ${P.cyan(e)} in ${P.dim(n)}...`);let o=await ft("package",e,{category:t.category});r.stop(`Created package ${P.cyan(e)}`,0),d.success(`Package created with ${o.length} files`),d.info("Next steps:"),d.info(` Edit ${n}/src/index.ts`),d.info(` Run: pnpm --filter @nebutra/${e} build`),process.exit(l.SUCCESS)}catch(o){r.stop(`Failed to generate package: ${o instanceof Error?o.message:"Unknown error"}`,1),process.exit(l.ERROR)}}async function Ua(e,t){if(t.dryRun){let r=await pt("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(i=>({path:i.path,sizeBytes:i.size}))};process.stdout.write(JSON.stringify(o,null,2)+`
435
+ `),process.exit(l.DRY_RUN_OK)}Ge.intro(P.bgCyan(P.black(" nebutra generate route ")));let n=d.spinner();try{n.start(`Generating route ${P.cyan(e)}...`);let r=await ft("route",e);n.stop(`Created route ${P.cyan(e)}`,0),d.success(`Route created with ${r.length} files`),d.info(`File: backends/gateway/src/routes/${e}.ts`),d.info(`Add to your main app.ts: app.route("/${e}", routes)`),process.exit(l.SUCCESS)}catch(r){n.stop(`Failed to generate route: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(l.ERROR)}}async function Ba(e,t){if(t.dryRun){let r=await pt("component",e,t),o={mode:"dry-run",timestamp:new Date().toISOString(),type:"component",name:e,location:"packages/design/ui/src/components/",files:r.map(i=>({path:i.path,sizeBytes:i.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)+`
436
+ `),process.exit(l.DRY_RUN_OK)}Ge.intro(P.bgCyan(P.black(" nebutra generate component ")));let n=d.spinner();try{n.start(`Generating component ${P.cyan(e)}...`);let r=await ft("component",e);n.stop(`Created component ${P.cyan(e)}`,0),d.success(`Component created with ${r.length} files`),d.info("Files created in packages/design/ui/src/components/"),d.info("Next steps:"),d.info(" 1. Add export to packages/design/ui/src/components/index.ts"),d.info(" 2. pnpm --filter @nebutra/ui typecheck"),d.info(" 3. pnpm --filter @nebutra/storybook dev"),process.exit(l.SUCCESS)}catch(r){n.stop(`Failed to generate component: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(l.ERROR)}}var 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,o)=>{let i=I(r,o);await Da(n,{dryRun:!!r.dryRun,yes:!!i.yes,interactive:!!process.stdin.isTTY,template:(r.template==="nextjs","nextjs"),port:typeof r.port=="string"?Number(r.port):void 0})}),t.command("package <name>").description(`Scaffold a new categorized package (@nebutra/<name>). Categories: ${Me.join(", ")}`).requiredOption("--category <category>",`Package category: ${Me.join(" | ")}`).option("--dry-run","Preview changes without writing files").option("--yes","Skip all prompts").action(async(n,r,o)=>{let i=I(r,o);await La(n,{category:String(r.category??""),dryRun:!!r.dryRun,yes:!!i.yes})}),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,o)=>{let i=I(r,o);await Ua(n,{dryRun:!!r.dryRun,yes:!!i.yes})}),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,o)=>{let i=I(r,o);await Ba(n,{dryRun:!!r.dryRun,yes:!!i.yes,variants:typeof r.variants=="string"?r.variants:void 0,sizes:typeof r.sizes=="string"?r.sizes:void 0})})};import qt,{lstatSync as Ma,readdirSync as Ht,readFileSync as Ga,writeFileSync as Va}from"fs";import oe from"path";import G from"picocolors";function Zt(){let e=new Map,t=oe.join(y(),"apps");if(!qt.existsSync(t))return e;try{let n=Ht(t);for(let r of n){let o=oe.join(t,r),i=oe.join(o,"messages");qt.existsSync(i)&&Ma(i).isDirectory()&&e.set(r,i)}}catch(n){d.debug(`Error scanning apps directory: ${n instanceof Error?n.message:String(n)}`)}return e}function Ve(e,t=""){let n=new Set;for(let[r,o]of Object.entries(e)){let i=t?`${t}.${r}`:r;if(typeof o=="object"&&o!==null&&!Array.isArray(o)){let a=Ve(o,i);for(let s of a)n.add(s)}else n.add(i)}return n}function ze(e){try{let t=Ga(e,"utf-8");return JSON.parse(t)}catch(t){return d.debug(`Failed to parse ${e}: ${t instanceof Error?t.message:String(t)}`),null}}async function za(e){if(d.info("Syncing language files across the project..."),(await F("translate",{filter:"landing",dryRun:e.dryRun,interactive:e.interactive??!0})).exitCode!==0){d.warn("Landing page translate script not found or failed. Trying sync-lang.mjs...");let n=await R({command:"node",args:["scripts/sync-lang.mjs"],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Sync language files"});n.exitCode!==0&&(d.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||d.success("Language files synced successfully")}else e.format==="json"?console.log(JSON.stringify({command:"i18n:sync",exitCode:0,success:!0},null,2)):e.dryRun||d.success("Language files synced successfully")}async function Ka(e){d.info("Validating translation files...");let t=Zt();t.size===0&&(d.warn("No message directories found in the project"),process.exit(l.NOT_FOUND));let n=new Map,r=!1;for(let[o,i]of t){let a=[];try{let s=Ht(i).filter(g=>g.endsWith(".json"));if(s.length===0)continue;let c=oe.join(i,"en.json"),m=ze(c);if(!m){d.warn(`Could not load reference locale en.json from ${o}`);continue}let p=Ve(m);for(let g of s){if(g==="en.json")continue;let h=g.replace(".json",""),f=oe.join(i,g),w=ze(f);if(!w){d.debug(`Could not parse ${g}`);continue}let D=Ve(w),k=Array.from(p).filter(B=>!D.has(B)),J=Array.from(D).filter(B=>!p.has(B)),te=p.size>0?(p.size-k.length)/p.size*100:100;(k.length>0||J.length>0)&&(r=!0),a.push({locale:h,totalKeys:p.size,missingKeys:k,extraKeys:J,coverage:Math.round(te*100)/100})}a.length>0&&n.set(o,a)}catch(s){d.debug(`Error validating ${o}: ${s instanceof Error?s.message:String(s)}`)}}if(e.format==="json"){let o={command:"i18n:validate",success:!r,results:Array.from(n.entries()).reduce((i,[a,s])=>(i[a]=s,i),{})};console.log(JSON.stringify(o,null,2))}else for(let[o,i]of n){d.info(`${G.bold(o)}:`);for(let a of i){let c=(a.coverage>=100?G.green:a.coverage>=80?G.yellow:G.red)(`${a.coverage}%`);d.info(` ${G.cyan(a.locale.padEnd(5))} ${c.padEnd(20)} [${a.totalKeys} keys]`),a.missingKeys.length>0&&d.warn(` Missing: ${a.missingKeys.slice(0,5).join(", ")}${a.missingKeys.length>5?` (+ ${a.missingKeys.length-5} more)`:""}`),a.extraKeys.length>0&&d.warn(` Extra: ${a.extraKeys.slice(0,5).join(", ")}${a.extraKeys.length>5?` (+ ${a.extraKeys.length-5} more)`:""}`)}}r&&process.exit(l.CONFLICT)}async function Wa(e,t){/^[a-z]{2}(-[A-Z]{2})?$/.test(e)||(d.error(`Invalid locale format: ${e}. Use 'en', 'es', 'zh-CN', etc.`),process.exit(l.INVALID_ARGS));let n=Zt();n.size===0&&(d.error("No message directories found in the project"),process.exit(l.NOT_FOUND));let r=[],o=!1;for(let[a,s]of n){let c=oe.join(s,`${e}.json`);if(qt.existsSync(c)){d.warn(`Locale ${e} already exists in ${a}`);continue}try{let h=function(f){for(let w in f)typeof f[w]=="string"?f[w]="":typeof f[w]=="object"&&f[w]!==null&&h(f[w])};var i=h;let m=oe.join(s,"en.json"),p=ze(m);if(!p){d.warn(`Could not load reference en.json from ${a}, skipping...`);continue}let g=JSON.parse(JSON.stringify(p));h(g),t.dryRun?d.info(`Would create ${e} locale in ${a}`):(Va(c,JSON.stringify(g,null,2)+`
437
+ `),r.push({app:a,path:c}),d.success(`Created ${e} locale in ${a}`))}catch(m){d.error(`Failed to create locale in ${a}: ${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||d.success(`Locale '${e}' added to ${r.length} app(s)`),o&&process.exit(l.ERROR)}async function Ya(e){d.info("Checking translation coverage...");let t=Zt();t.size===0&&(d.warn("No message directories found in the project"),process.exit(l.NOT_FOUND));let n=new Map,r=0,o=0;for(let[i,a]of t){let s=[];try{let c=Ht(a).filter(h=>h.endsWith(".json"));if(c.length===0)continue;let m=oe.join(a,"en.json"),p=ze(m);if(!p){d.debug(`Could not load reference locale from ${i}`);continue}let g=Ve(p);for(let h of c){let f=h.replace(".json",""),w=oe.join(a,h),D=ze(w);if(!D)continue;let k=Ve(D),J=Array.from(k).filter(Se=>{let qe=D[Se.split(".")[0]];return qe!==void 0&&qe!==""}).length,te=g.size-J,B=g.size>0?J/g.size*100:0;s.push({locale:f,coverage:Math.round(B*100)/100,totalKeys:g.size,translatedKeys:J,missingKeys:te})}s.length>0&&(n.set(i,s),o++,r+=s.reduce((h,f)=>h+f.coverage,0)/s.length)}catch(c){d.debug(`Error checking status for ${i}: ${c instanceof Error?c.message:String(c)}`)}}if(e.format==="json"){let i={command:"i18n:status",averageCoverage:o>0?Math.round(r/o*100)/100:0,results:Array.from(n.entries()).reduce((a,[s,c])=>(a[s]=c,a),{})};console.log(JSON.stringify(i,null,2))}else{for(let[i,a]of n){d.info(`${G.bold(i)}:`);for(let s of a){let c="",p=Math.round(s.coverage/100*20);c="\u2588".repeat(p)+"\u2591".repeat(20-p);let h=(s.coverage>=100?G.green:s.coverage>=80?G.yellow:G.red)(`${s.coverage.toFixed(1)}%`);d.info(` ${G.cyan(s.locale.padEnd(5))} [${c}] ${h}`),d.info(` ${s.translatedKeys}/${s.totalKeys} keys (${s.missingKeys} missing)`)}}if(o>0){let i=r/o,a=i>=100?G.green:i>=80?G.yellow:G.red;d.info(`
438
+ Average coverage: ${a(i.toFixed(1)+"%")}`)}}}function Er(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,r)=>{let o=r.optsWithGlobals?.()||n;await za({dryRun:n.dryRun||!1,format:n.format||o.format,interactive:o.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,r)=>{let o=r.optsWithGlobals?.()||n;await Ka({format:n.format||o.format,interactive:o.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,o)=>{let i=o.optsWithGlobals?.()||r;await Wa(n,{dryRun:r.dryRun||!1,format:r.format||i.format,interactive:i.yes!==!0})}),t.command("status").description("Show translation coverage per locale and app").option("--format <type>","Output format: json or plain").action(async(n,r)=>{let o=r.optsWithGlobals?.()||n;await Ya({format:n.format||o.format,interactive:o.yes!==!0})})}import*as gt from"@clack/prompts";async function Ja(e){let t=e.lite?"docker-compose.lite.yml":void 0,n=e.lite?"Starting lite stack (PostgreSQL + Redis)":"Starting full stack";u(n+"...","info");let r=await ge("up -d",{file:t,profile:e.profile,dryRun:e.dryRun});r.exitCode===0?u("Infrastructure stack started successfully","success"):(u(Ke(r)??"Failed to start infrastructure stack","error"),x("Docker compose output",{stderr:r.stderr}),process.exit(r.exitCode||1))}async function qa(e){u("Stopping infrastructure stack...","info");let t=await ge("down",{dryRun:e.dryRun});t.exitCode===0?u("Infrastructure stack stopped","success"):(u(Ke(t)??"Failed to stop infrastructure stack","error"),x("Docker compose output",{stderr:t.stderr}),process.exit(t.exitCode||1))}function Ke(e){let t=e.stderr||"";if(e.exitCode===127||/command not found|ENOENT/i.test(t))return"Docker CLI not found. Install Docker Desktop (or the docker CLI) and make sure it's on your PATH.";if(/cannot connect to the docker daemon|is the docker daemon running/i.test(t))return"Docker daemon is not running. Start Docker Desktop (or `dockerd`) and try again."}async function Ha(e){u("Checking infrastructure status...","info");let t=await ge("ps --format json",{dryRun:e.dryRun,interactive:!1});if(e.dryRun)return;let n=t.exitCode!==0&&/unknown (flag|shorthand flag)/i.test(t.stderr);if(n&&(x("docker compose ps --format json unsupported, falling back to plain ps",{stderr:t.stderr}),t=await ge("ps",{interactive:!1})),t.exitCode!==0){let r=Ke(t);u(r??"Failed to check infrastructure status","error"),x("Docker compose output",{stderr:t.stderr}),process.exit(t.exitCode||1)}if(n){e.format==="json"&&u("This Docker Compose version doesn't support `--format json`; showing plain text instead.","warn"),b(t.stdout,{format:"plain"});return}try{let r=JSON.parse(t.stdout);if(e.format==="json")b(r,{format:"json"});else{let o=r.map(i=>({name:i.Name,status:i.Status,ports:i.Ports||"-"}));b(o,{format:"table"})}}catch{b(t.stdout,{format:"plain"})}}async function Za(e,t){let n=t||{},r=e?`Fetching logs for ${e}...`:"Fetching infrastructure logs...";u(r,"info");let o=["logs","--tail","50"];e&&o.push(e);let i=await ge(o.join(" "),{dryRun:n.dryRun});i.exitCode===0?b(i.stdout,{format:"plain"}):(u(Ke(i)??"Failed to fetch logs","error"),x("Docker compose output",{stderr:i.stderr}),process.exit(i.exitCode||1))}async function Xa(e){let t=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!e.yes&&!e.dryRun)if(t){let r=await gt.confirm({message:"Reset infrastructure? This will remove all containers and volumes.",initialValue:!1});(gt.isCancel(r)||!r)&&(u("Reset cancelled","warn"),process.exit(l.CANCELLED))}else u("Infrastructure reset requires explicit --yes confirmation (use: nebutra infra reset --yes)","error"),process.exit(l.INVALID_ARGS);u("Resetting infrastructure stack...","warn");let n=await ge("down -v",{dryRun:e.dryRun});n.exitCode===0?u("Infrastructure reset successfully","success"):(u(Ke(n)??"Failed to reset infrastructure","error"),x("Docker compose output",{stderr:n.stderr}),process.exit(n.exitCode||1))}function Or(e){e.command("infra <verb> [service]").description("Manage Docker Compose infrastructure (PostgreSQL, Redis, 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,i)=>{let a=i.optsWithGlobals?.(),s={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:a?.format||o.format,lite:o.lite||!1,profile:o.profile};try{switch(n){case"up":await Ja(s);break;case"down":await qa(s);break;case"status":await Ha(s);break;case"logs":await Za(r,s);break;case"reset":await Xa(s);break;default:u(`Unknown infra subcommand: ${n}. Valid commands: up, down, status, logs, reset`,"error"),process.exit(l.INVALID_ARGS)}}catch(c){let m=c instanceof Error?c.message:String(c);u(`Infrastructure command failed: ${m}`,"error"),x("Full error",{error:c}),process.exit(l.ERROR)}}).addHelpText("after",`
436
439
  Examples:
437
440
  nebutra infra up Start full stack
438
441
  nebutra infra up --lite Start lite stack (PostgreSQL + Redis)
@@ -449,19 +452,21 @@ Flags:
449
452
  --lite Use docker-compose.lite.yml (PostgreSQL + Redis only)
450
453
  --profile <name> Enable Docker Compose profile (search, notifications, etc.)
451
454
  --format <type> Output format: json, plain (default: plain, auto-json in non-TTY)
452
- `)}import Ut from"fs";import da from"path";import*as V from"@clack/prompts";import st from"picocolors";var ma=["auth","billing","email","storage","queue","cache","notifications","webhooks","ai","mcp"],pa={stack:"sailor-2026-09",capabilities:[...ma]};function ga(){return process.stdout.isTTY===!0}function fa(e){return e.yes===!0||e.dryRun===!0||!ga()}function ha(e,t,n){return{operation:n?"update":"create",path:e,...n&&{before:n},after:t}}async function Jn(e={}){let t=process.cwd(),n=da.join(t,"nebutra.config.json"),r=Ut.existsSync(n);e.ifNotExists&&r&&(c.info("nebutra.config.json already exists, skipping initialization."),process.exit(l.SUCCESS));let o=fa(e);o||V.intro(st.bgCyan(st.black(" nebutra init "))),r&&!o&&(V.log.warn("nebutra.config.json already exists in this directory."),await V.confirm({message:"Do you want to overwrite it?",initialValue:!1})||(V.outro("Operation aborted."),process.exit(l.CANCELLED)));let i={...pa};if(e.dryRun){let a=r?JSON.parse(Ut.readFileSync(n,"utf-8")):void 0,s=ha(n,i,a);I(s),d("Dry-run completed. No changes made."),process.exit(l.DRY_RUN_OK)}o?c.info("Initializing Nebutra CLI configuration..."):V.spinner().start("Initializing Nebutra CLI configuration..."),Ut.writeFileSync(n,JSON.stringify(i,null,2)+`
453
- `),o?(c.success("Configuration saved to nebutra.config.json"),c.info('Initialization complete! Run "nebutra status" to check capability readiness.')):(await new Promise(a=>setTimeout(a,800)),V.spinner().stop(st.green("Configuration saved to nebutra.config.json")),V.outro(st.cyan('Initialization complete! Run "nebutra status" to check capability readiness.'))),process.exit(l.SUCCESS)}import Bt from"fs/promises";import ka from"os";import Mt from"path";import*as C from"@clack/prompts";import B from"picocolors";var ya="https://analytics.nebutra.com";function ba(e={}){if(e.noTelemetry===!0)return!0;let t=process.env.NEBUTRA_TELEMETRY;return t==="0"||t==="false"}function va(){return process.env.npm_package_version??"0.0.0-dev"}function ct(e,t={}){ba(t)||(async()=>{try{let n=await import("@nebutra/analytics");if(typeof n.createProductAnalyticsClient!="function")return;let r=n.createProductAnalyticsClient({posthog:{apiKey:process.env.POSTHOG_KEY??process.env.NEXT_PUBLIC_POSTHOG_KEY??process.env.NEBUTRA_POSTHOG_KEY??"",host:process.env.POSTHOG_HOST??process.env.NEXT_PUBLIC_POSTHOG_HOST??process.env.NEBUTRA_POSTHOG_HOST??ya},onError:()=>{}});if(typeof r?.track!="function")return;let o=r.track("license.cli",{action:e.action,cli_version:va(),...e.tier?{license_tier:e.tier}:{},...e.type?{license_type:e.type}:{},...e.error_code?{error_code:e.error_code}:{}});o&&typeof o.then=="function"&&await o.catch(()=>{})}catch{}})()}var qn=process.env.NEBUTRA_LICENSE_API_URL??"https://nebutra.com";async function Hn(){let e=ka.homedir(),t=Mt.join(e,".config","nebutra");try{await Bt.mkdir(t,{recursive:!0})}catch{}return t}async function wa(e){let t=`${qn}/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 (${qn}). 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 xa(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 Ca(e,t){let n=t.quiet===!0;n||C.intro(B.bgCyan(B.black(" Nebutra License Activation "))),ct({action:"activate_attempted"});try{if(e.length<10)throw new Error("Invalid license key format.");n||C.log.step("Validating license key with Nebutra...");let{tier:r,type:o}=await wa(e),i=await Hn(),a=Mt.join(i,"license.json");await Bt.writeFile(a,JSON.stringify({licenseKey:e,tier:r,type:o,activatedAt:new Date().toISOString()},null,2),"utf-8"),n||(C.log.success(B.green(`${r} license activated and saved to ${a}`)),C.outro(B.cyan("Recorded locally. Sailor scaffolds are MIT-licensed \u2014 this doesn't unlock CLI features; paid tiers are support and hosting, see nebutra.com/pricing."))),ct({action:"activated",tier:r,type:o})}catch(r){ct({action:"failed",error_code:xa(r)}),n||(C.log.error(B.red("Activation failed")),c.error(r instanceof Error?r.message:String(r)),C.outro("Please check your key and try again.")),process.exit(l.ERROR)}}async function Sa(e){let t=e.quiet===!0;t||C.intro(B.bgCyan(B.black(" Nebutra License Status ")));try{let n=await Hn(),r=Mt.join(n,"license.json"),o=await Bt.readFile(r,"utf-8"),i=JSON.parse(o);if(i.licenseKey)t||(C.log.success(B.green("License is active!")),C.log.info(`Key: ${i.licenseKey.substring(0,8)}...`),i.tier&&C.log.info(`Tier: ${B.bold(i.tier)}`),i.type&&C.log.info(`Type: ${i.type}`),C.log.info(`Activated: ${new Date(i.activatedAt).toLocaleString()}`),C.log.message(B.dim("Note: this record is informational. Sailor scaffolds are MIT-licensed and the CLI has no premium features gated behind a license key.")),C.outro(B.cyan("See nebutra.com/pricing for paid support and hosting tiers.")));else throw new Error("License key not found in config.")}catch{t||(C.log.warn(B.yellow("No active license found locally.")),C.log.message(B.dim("This is expected \u2014 Sailor scaffolds are MIT-licensed and nothing in the CLI is gated behind a license. `nebutra license activate <key>` only records a key for support/billing purposes.")),C.outro("")),process.exit(l.NOT_FOUND)}}function Zn(e){let t=e.command("license").description("Record a Nebutra support/billing license key locally (Sailor scaffolds are MIT \u2014 nothing in the CLI is gated by this)");t.command("activate <key>").description("Validate and record a license key locally (support/billing record, not a feature unlock)").option("--quiet","Suppress output").action(async(n,r)=>{await Ca(n,r)}),t.command("status").description("Check the locally configured license status").option("--quiet","Suppress output").action(async n=>{await Sa(n)})}import{existsSync as Qn,readFileSync as Ea,writeFileSync as Ra}from"fs";import{dirname as Oa,join as Xn,parse as Aa}from"path";import*as lt from"@clack/prompts";function _a(e){let t=e,{root:n}=Aa(t);for(;;){if(Qn(Xn(t,"package.json")))return t;if(t===n)return null;let r=Oa(t);if(r===t)return null;t=r}}function Ia(e){if(!Qn(e))return{};try{let t=JSON.parse(Ea(e,"utf-8"));return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return{}}}function Pa(e,t){Ra(e,JSON.stringify(t,null,2)+`
454
- `)}async function Na(e,t){let n=t.format==="json",r=_a(process.cwd());r||(n?console.log(JSON.stringify({command:"link",success:!1,error:"No package.json found upstream"},null,2)):c.error("No package.json found in current or parent directories."),process.exit(l.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)):c.error("project-id is required when running with --yes / non-interactive."),process.exit(l.INVALID_ARGS));let u=await lt.text({message:"Project ID to link to:",placeholder:"proj_abc123",validate:m=>m&&m.trim().length>0?void 0:"Project ID cannot be empty"});lt.isCancel(u)&&(c.warn("Cancelled"),process.exit(l.CANCELLED)),o=String(u).trim()}let i=Xn(r,"nebutra.config.json"),s={...Ia(i),projectId:o};Pa(i,s),n?console.log(JSON.stringify({command:"link",success:!0,projectId:o,configPath:i},null,2)):c.success(`Linked to project: ${o}`)}function er(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 Na(t,{yes:o.yes||!1,format:n.format||o.format})})}import{spawn as Ka}from"child_process";import{platform as Wa}from"os";var M={name:"Nebutra",nameCn:"\u4E91\u6BD3\u667A\u80FD",nameFull:"\u65E0\u9521\u4E91\u6BD3\u667A\u80FD\u79D1\u6280\u6709\u9650\u516C\u53F8",nameFullEn:"Wuxi Nebutra Intelligence Technology Co., Ltd.",tagline:"Ship AI products, not boilerplate.",taglineCn:"AI\u539F\u751F\xB7\u5FEB\u901F\u51FA\u6D77\xB7\u5373\u523B\u4EA4\u4ED8",description:"Production-ready Next.js monorepo template for AI SaaS products. Auth, billing, multi-tenancy, AI services, design system, and enterprise infrastructure \u2014 pre-configured.",descriptionCn:"\u9762\u5411AI\u521B\u4E1A\u8005\u7684\u4E00\u4F53\u5316SaaS\u57FA\u7840\u8BBE\u65BD\u6A21\u677F\uFF0C\u8986\u76D6\u8BA4\u8BC1\u3001\u8BA1\u8D39\u3001\u591A\u79DF\u6237\u3001AI\u670D\u52A1\u4E0E\u8BBE\u8BA1\u7CFB\u7EDF\uFF0C\u5F00\u7BB1\u5373\u4EA7\u54C1",logo:"image",story:{concept:"Logo\u4EE5\u9996\u5B57\u6BCDN\u7684\u57FA\u7840\u9020\u578B\u6982\u5FF5\u4E3A\u4E3B\u8981\u8BBE\u8BA1\u6846\u67B6\uFF0C\u901A\u8FC7\u51E0\u4F55\u6B63\u8D1F\u7A7A\u95F4\u6784\u5EFA\u9690\u5F62'N'\uFF0C\u5F62\u6210\u8FD1\u4F3C\u516D\u8FB9\u5F62\u7684\u7A33\u5B9A\u7ED3\u6784",colorMeaning:"\u84DD\u7EFF\u6E10\u53D8\u4F53\u73B0\u672A\u6765\u611F\u4E0E\u79D1\u6280\u950B\u8292\uFF0C'\u4E91'\u4EE3\u8868\u4E91\u7AEF\u5E73\u53F0\uFF0C'\u6BD3'\u5BD3\u610F\u5B55\u80B2\u4E0E\u8F6C\u5316",values:["AI Native","Ship Fast","Open by Default","Global-Ready","Enterprise-Grade"],missionStatement:"Help AI founders and SaaS teams go from idea to production 10x faster by providing the infrastructure layer they shouldn't have to build."},domains:{landing:"nebutra.com",app:"app.nebutra.com",api:"api.nebutra.com",auth:"auth.nebutra.com",sso:"sso.nebutra.com",studio:"studio.nebutra.com",cdn:"cdn.nebutra.com",router:"router.nebutra.com",forge:"forge.nebutra.com",design:"design.nebutra.com",status:"status.nebutra.com",open:"open.nebutra.com",admin:"admin.nebutra.com",analytics:"analytics.nebutra.com",pebble:"pebble.nebutra.com",carina:"carina.nebutra.com",kuanlan:"kuanlan.nebutra.com",origin:"origin.nebutra.com"},social:{twitter:"https://twitter.com/nebutra",github:"https://github.com/nebutra",discord:"https://discord.gg/nebutra",linkedin:"https://linkedin.com/company/nebutra"}};import{execFile as ja}from"child_process";import{chmodSync as Fa,existsSync as Pe,readFileSync as nr,rmSync as Da,unlinkSync as Gt,writeFileSync as rr}from"fs";import{mkdir as Ta}from"fs/promises";import{platform as be}from"os";import{join as or}from"path";import{promisify as La}from"util";import{homedir as tr,platform as $a}from"os";import{join as ut}from"path";function _e(){if($a()==="win32"){let t=process.env.LOCALAPPDATA||ut(tr(),"AppData","Local");return ut(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?ut(e,"nebutra"):ut(tr(),".config","nebutra")}var ve=La(ja),ke="nebutra-cli",Ne="default";function Ie(){return or(_e(),"credentials.json")}function dt(){return or(_e(),"device-pending.json")}async function ir(){await Ta(_e(),{recursive:!0})}async function Vt(e){try{return await ve(be()==="win32"?"where":"which",[e]),!0}catch{return!1}}function zt(){return process.env.NEBUTRA_NO_KEYCHAIN==="1"}async function Ua(e){if(zt())return!1;try{if(be()==="darwin")return await ve("security",["add-generic-password","-a",Ne,"-s",ke,"-w",e,"-U"]),!0;if(be()==="linux"&&await Vt("secret-tool"))return await ve("secret-tool",["store","--label",`${M.name} CLI credentials`,"service",ke,"account",Ne]),!0}catch{}return!1}async function ar(){if(zt())return null;try{if(be()==="darwin"){let{stdout:e}=await ve("security",["find-generic-password","-a",Ne,"-s",ke,"-w"]);return e.trim()||null}if(be()==="linux"&&await Vt("secret-tool")){let{stdout:e}=await ve("secret-tool",["lookup","service",ke,"account",Ne]);return e.trim()||null}}catch{}return null}async function Ba(){if(zt())return!1;let e=!1;try{be()==="darwin"?(await ve("security",["delete-generic-password","-a",Ne,"-s",ke]),e=!0):be()==="linux"&&await Vt("secret-tool")&&(await ve("secret-tool",["clear","service",ke,"account",Ne]),e=!0)}catch{}return e}async function sr(e){await ir();let t=JSON.stringify(e);if(await Ua(t)){try{Pe(Ie())&&Gt(Ie())}catch{}return{storedIn:"keychain"}}rr(Ie(),`${t}
455
- `,{mode:384});try{Fa(Ie(),384)}catch{}return{storedIn:"file"}}async function Ma(){let e=await ar();if(e)try{return JSON.parse(e)}catch{return null}let t=Ie();if(!Pe(t))return null;try{return JSON.parse(nr(t,"utf-8"))}catch{return null}}async function cr(){let e=[];await Ba()&&e.push(`keychain:${ke}`);let t=Ie();if(Pe(t))try{Gt(t),e.push(t)}catch{}let n=dt();if(Pe(n))try{Da(n,{force:!0}),e.push(n)}catch{}return e}async function Kt(e){await ir(),rr(dt(),`${JSON.stringify(e)}
456
- `,{mode:384})}function lr(){let e=dt();if(!Pe(e))return null;try{return JSON.parse(nr(e,"utf-8"))}catch{return null}}function mt(){let e=dt();if(Pe(e))try{Gt(e)}catch{}}async function ur(){let e=process.env.NEBUTRA_TOKEN?.trim();if(e)return{token:e,source:"env",expired:!1};let t=await Ma();if(!t)return null;let n=!!t.expiresAt&&new Date(t.expiresAt).getTime()<=Date.now(),r=await ar()!==null;return{token:t.accessToken,source:r?"keychain":"file",expired:n}}var Ga={name:"Nebutra",nameCn:"\u4E91\u6BD3\u667A\u80FD",nameFull:"\u65E0\u9521\u4E91\u6BD3\u667A\u80FD\u79D1\u6280\u6709\u9650\u516C\u53F8",nameFullEn:"Wuxi Nebutra Intelligence Technology Co., Ltd.",tagline:"Ship AI products, not boilerplate.",taglineCn:"AI\u539F\u751F\xB7\u5FEB\u901F\u51FA\u6D77\xB7\u5373\u523B\u4EA4\u4ED8",description:"Production-ready Next.js monorepo template for AI SaaS products. Auth, billing, multi-tenancy, AI services, design system, and enterprise infrastructure \u2014 pre-configured.",descriptionCn:"\u9762\u5411AI\u521B\u4E1A\u8005\u7684\u4E00\u4F53\u5316SaaS\u57FA\u7840\u8BBE\u65BD\u6A21\u677F\uFF0C\u8986\u76D6\u8BA4\u8BC1\u3001\u8BA1\u8D39\u3001\u591A\u79DF\u6237\u3001AI\u670D\u52A1\u4E0E\u8BBE\u8BA1\u7CFB\u7EDF\uFF0C\u5F00\u7BB1\u5373\u4EA7\u54C1",logo:"image",story:{concept:"Logo\u4EE5\u9996\u5B57\u6BCDN\u7684\u57FA\u7840\u9020\u578B\u6982\u5FF5\u4E3A\u4E3B\u8981\u8BBE\u8BA1\u6846\u67B6\uFF0C\u901A\u8FC7\u51E0\u4F55\u6B63\u8D1F\u7A7A\u95F4\u6784\u5EFA\u9690\u5F62'N'\uFF0C\u5F62\u6210\u8FD1\u4F3C\u516D\u8FB9\u5F62\u7684\u7A33\u5B9A\u7ED3\u6784",colorMeaning:"\u84DD\u7EFF\u6E10\u53D8\u4F53\u73B0\u672A\u6765\u611F\u4E0E\u79D1\u6280\u950B\u8292\uFF0C'\u4E91'\u4EE3\u8868\u4E91\u7AEF\u5E73\u53F0\uFF0C'\u6BD3'\u5BD3\u610F\u5B55\u80B2\u4E0E\u8F6C\u5316",values:["AI Native","Ship Fast","Open by Default","Global-Ready","Enterprise-Grade"],missionStatement:"Help AI founders and SaaS teams go from idea to production 10x faster by providing the infrastructure layer they shouldn't have to build."},domains:{landing:"nebutra.com",app:"app.nebutra.com",api:"api.nebutra.com",auth:"auth.nebutra.com",sso:"sso.nebutra.com",studio:"studio.nebutra.com",cdn:"cdn.nebutra.com",router:"router.nebutra.com",forge:"forge.nebutra.com",design:"design.nebutra.com",status:"status.nebutra.com",open:"open.nebutra.com",admin:"admin.nebutra.com",analytics:"analytics.nebutra.com",pebble:"pebble.nebutra.com",carina:"carina.nebutra.com",kuanlan:"kuanlan.nebutra.com",origin:"origin.nebutra.com"},social:{twitter:"https://twitter.com/nebutra",github:"https://github.com/nebutra",discord:"https://discord.gg/nebutra",linkedin:"https://linkedin.com/company/nebutra"}};function pt(e){let t=Ga.domains[e];if(!t?.trim())throw new Error(`brand.domains.${String(e)} is empty \u2014 run brand:apply`);return`https://${t.replace(/^https?:\/\//,"").replace(/\/+$/,"")}`}var dr="nebutra-cli";function gt(){let e=process.env.NEBUTRA_AUTH_URL?.trim();return e?e.replace(/\/+$/,""):pt("auth")}var re=class extends Error{constructor(n,r){super(r);this.code=n;this.name="DeviceAuthError"}code};async function Wt(e){try{return await e.json()}catch{return{}}}async function mr(e,t,n,r={}){let o=r.attempts??3,i=r.sleep??gr,a;for(let s=1;s<=o;s++){try{let u=await e(t,{...n,signal:AbortSignal.timeout(r.timeoutMs??1e4)});if(u.status<500||s===o)return u;a=new Error(`HTTP ${u.status}`)}catch(u){if(!fr(u)||s===o)throw u;a=u}await i(1e3*s)}throw a}async function pr(e,t=fetch){let n=await mr(t,`${e}/api/auth/device/code`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({client_id:dr})}),r=await Wt(n);if(!n.ok)throw new re(typeof r.error=="string"?r.error:"request_failed",typeof r.error_description=="string"?r.error_description:`Could not reach ${M.name}'s auth center (${n.status}).`);return r}async function Va(e,t,n=fetch){let r=await n(`${e}/api/auth/device/token`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({grant_type:"urn:ietf:params:oauth:grant-type:device_code",device_code:t,client_id:dr}),signal:AbortSignal.timeout(1e4)}),o=await Wt(r);if(!r.ok)throw new re(typeof o.error=="string"?o.error:"request_failed",typeof o.error_description=="string"?o.error_description:`HTTP ${r.status}`);return o}var gr=e=>new Promise(t=>setTimeout(t,e)),za=30;function fr(e){if(e instanceof re)return e.code==="request_failed";if(e instanceof TypeError)return!0;let t=e?.name;return t==="TimeoutError"||t==="AbortError"}async function hr(e,t,n){let r=n.fetchImpl??fetch,o=n.sleep??gr,i=n.intervalSeconds;for(;;){if(Date.now()>=n.deadline)throw new re("expired_token","The device code expired before it was approved.");n.onWaiting?.(i),await o(i*1e3);try{return await Va(e,t,r)}catch(a){if(fr(a)){i=Math.min(i+5,za),n.onTransientFailure?.(a);continue}if(!(a instanceof re))throw a;if(a.code==="authorization_pending")continue;if(a.code==="slow_down"){i+=5;continue}throw a}}}async function ft(e,t,n=fetch){let r=await mr(n,`${e}/api/auth/get-session`,{method:"GET",headers:{authorization:`Bearer ${t}`}});if(!r.ok)return null;let i=(await Wt(r)).user;return!i||typeof i.id!="string"?null:{userId:i.id,email:typeof i.email=="string"?i.email:null,name:typeof i.name=="string"?i.name:null}}function Ya(e,t){return!!e.json||e.format==="json"||t==="json"}function Ja(e){try{let t=Wa(),[n,r]=t==="darwin"?["open",[e]]:t==="win32"?["cmd",["/c","start","",e]]:["xdg-open",[e]],o=Ka(n,r,{stdio:"ignore",detached:!0});o.on("error",()=>{}),o.unref()}catch{}}async function qa(e,t){let n=new Date;await sr({accessToken:t.access_token,tokenType:t.token_type,expiresAt:new Date(n.getTime()+t.expires_in*1e3).toISOString(),scope:t.scope,obtainedAt:n.toISOString()}),mt()}async function yr(e,t,n){let r=0,o=await hr(e,t.deviceCode,{intervalSeconds:t.interval,deadline:t.expiresAt,onWaiting:()=>{n||(r++,process.stderr.write(r===1?"Waiting for approval":"."))},onTransientFailure:()=>{n||process.stderr.write("!")}});n||process.stderr.write(`
457
- `),await qa(e,o);let i=await ft(e,o.access_token);return i?{userId:i.userId,email:i.email}:null}async function Ha(e,t){let n=Ya(e,t.format),r=gt();if(e.poll){let s=lr();if(s&&s.expiresAt>Date.now()&&s.baseUrl===r)try{let u=await yr(r,{deviceCode:s.deviceCode,interval:s.interval,expiresAt:s.expiresAt},n);n?console.log(JSON.stringify({command:"login",success:!0,...u},null,2)):c.success(u?.email?`Logged in as ${u.email}`:`Logged in to ${M.name}.`);return}catch(u){return mt(),Yt(u,n)}}let o;try{o=await pr(r)}catch(s){return Yt(s,n)}let i=Date.now()+o.expires_in*1e3;if(n&&!e.poll){await Kt({deviceCode:o.device_code,userCode:o.user_code,verificationUri:o.verification_uri,verificationUriComplete:o.verification_uri_complete,interval:o.interval,expiresAt:i,baseUrl:r}),console.log(JSON.stringify({command:"login",success:!0,pending:!0,verification_uri:o.verification_uri,verification_uri_complete:o.verification_uri_complete,user_code:o.user_code,expires_in:o.expires_in,interval:o.interval},null,2));return}n||(c.info(`First, confirm this code matches what's shown in the browser:
458
- `),c.info(` ${o.user_code}
459
- `),c.info(`Visiting: ${o.verification_uri_complete}`)),e.browser!==!1&&process.stdout.isTTY!==!1&&!t.yes&&Ja(o.verification_uri_complete);try{await Kt({deviceCode:o.device_code,userCode:o.user_code,verificationUri:o.verification_uri,verificationUriComplete:o.verification_uri_complete,interval:o.interval,expiresAt:i,baseUrl:r});let s=await yr(r,{deviceCode:o.device_code,interval:o.interval,expiresAt:i},n);n?console.log(JSON.stringify({command:"login",success:!0,...s},null,2)):c.success(s?.email?`Logged in as ${s.email}`:`Logged in to ${M.name}.`)}catch(s){return mt(),Yt(s,n)}}function Yt(e,t){let n=e instanceof re?e.code:"login_failed",r=e instanceof Error?e.message:String(e),o=n==="access_denied"?l.PERMISSION_DENIED:n==="expired_token"?l.TIMEOUT:l.NETWORK_ERROR;t?console.log(JSON.stringify({command:"login",success:!1,error:n,message:r},null,2)):c.error(r),process.exit(o)}function br(e){e.command("login").description(`Authorize this machine with your ${M.name} account (device flow)`).option("--json","Print verification details as JSON and exit immediately (agent mode)").option("--no-browser","Don't try to open a browser").option("--poll","Poll until approved/denied/expired, resuming a pending `login --json` if present").option("--format <type>","Output format: json or plain").action(async(t,n)=>{let r=n?.optsWithGlobals?.()||t;await Ha(t,{format:r.format,yes:r.yes})})}import{existsSync as vr,rmSync as Za,unlinkSync as Qa}from"fs";import{join as Xa}from"path";var es=["auth.json","session.json","token","credentials.json"];async function ts(e){let t=_e(),n=e.format==="json",r=0,o=[],i=await cr();if(r+=i.length,o.push(...i),e.all){if(vr(t))try{Za(t,{recursive:!0,force:!0}),r=1,o.push(t)}catch(a){let s=a instanceof Error?a.message:String(a);n?console.log(JSON.stringify({command:"logout",success:!1,error:s},null,2)):c.error(`Failed to clear config directory: ${s}`),process.exit(l.PERMISSION_DENIED)}}else for(let a of es){let s=Xa(t,a);if(vr(s))try{Qa(s),r++,o.push(s)}catch{}}r===0&&(n?console.log(JSON.stringify({command:"logout",success:!0,alreadyLoggedOut:!0,removed:[]},null,2)):c.info("Already logged out"),process.exit(l.SUCCESS)),n?console.log(JSON.stringify({command:"logout",success:!0,removed:o,clearedAll:!!e.all},null,2)):c.success(e.all?"Cleared all nebutra config":"Logged out of nebutra.com")}function kr(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 ts({all:t.all||!1,format:t.format||r.format})})}import{spawn as ns}from"child_process";import{dirname as rs,resolve as os}from"path";import{fileURLToPath as is}from"url";import*as q from"@clack/prompts";import oe from"picocolors";var as=rs(is(import.meta.url));function ss(){let e=[()=>os(as,"../../../mcp/dist/server/contextServer.js")];for(let t of e)try{return t()}catch(n){c.debug("MCP server binary resolution strategy failed",{error:n})}return"nebutra-mcp"}function wr(e){e.command("mcp").description("Start the Nebutra MCP server for Cursor/Windsurf").option("--stdio","Use stdio transport (default)",!0).action(async t=>{q.intro(oe.bgCyan(oe.black(" nebutra mcp ")));try{let n=ss();q.log.info(oe.cyan("Starting Nebutra MCP server for Cursor/Windsurf integration...")),q.log.info(oe.dim(`Press Ctrl+C to stop the server.
460
- `));let r=ns(process.execPath,[n],{stdio:"inherit",env:{...process.env}});r.on("close",o=>{o===0?q.outro(oe.green("MCP server stopped gracefully.")):o===null&&q.outro(oe.yellow("MCP server terminated.")),process.exit(o??0)}),r.on("error",o=>{c.error(`
461
- Failed to start MCP server: ${o.message}`),o.code==="ENOENT"&&(c.warn(`
462
- Tip: Ensure @nebutra/mcp is installed or available in your PATH.`),c.info(" Install globally: npm install -g @nebutra/mcp"),process.exit(l.NOT_FOUND)),process.exit(l.ERROR)}),process.on("SIGINT",()=>{q.log.info(oe.yellow(`
463
- Shutting down MCP server...`)),r.kill("SIGINT")}),process.on("SIGTERM",()=>{q.log.info(oe.yellow("Terminating MCP server...")),r.kill("SIGTERM")})}catch(n){q.log.error(oe.red("Error: Unable to start MCP server. Ensure @nebutra/mcp is properly installed.")),n instanceof Error&&c.error(n.message),process.exit(l.ERROR)}})}import*as P from"@clack/prompts";import z from"picocolors";var Cr=Object.fromEntries(Object.entries(ln).map(([e,t])=>[e,t]));function Sr(e){return typeof e=="boolean"?"boolean":"string"}function Er(e){return{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:Sr(n.default)})),examples:e.examples||[]}}function cs(e){let n=(Z.subcommands||[]).find(r=>r.name===e);return n?Er(n):null}function ls(){return(Z.options??[]).map(e=>({flags:e.flags,description:e.description,required:!1,type:Sr(e.default)}))}function us(e){let t=Z.subcommands||[];return{name:Z.name,version:e,description:Z.description,usage:Z.usage||"nebutra [command] [options]",globalOptions:ls(),commands:t.map(Er),exitCodes:Cr,timestamp:new Date().toISOString()}}function xr(){return(Z.subcommands||[]).map(t=>t.name)}function ht(e){process.stdout.write(`${JSON.stringify(e,null,2)}
464
- `)}function ds(e){return e instanceof Error&&e.message.startsWith("EXIT:")}async function ms(e,t){let n=t.quiet===!0,r="0.1.0";try{if(t.all===!0&&(ht(us(r)),process.exit(0)),t.list===!0&&(ht(xr()),process.exit(0)),(t.exitCodes===!0||t["exit-codes"]===!0)&&(ht(Cr),process.exit(0)),e){let o=cs(e);o||(n||(c.error(`Unknown command '${e}'`),c.info(`Available commands: ${xr().join(", ")}`)),process.exit(l.NOT_FOUND)),ht(o),process.exit(0)}n||(P.intro(z.bgCyan(z.black(" nebutra schema "))),P.log.info(z.cyan("Agent-friendly introspection of CLI commands, arguments, and value domains.")),P.log.message(""),P.log.info(z.bold("Usage:")),P.log.message(z.dim(" nebutra schema --all Show full command tree as JSON")),P.log.message(z.dim(" nebutra schema <command> Show schema for a specific command")),P.log.message(z.dim(" nebutra schema --list List available command names")),P.log.message(z.dim(" nebutra schema --exit-codes Show exit codes reference")),P.log.message(""),P.log.info(z.bold("Example:")),P.log.message(z.dim(" nebutra schema status")),P.log.message(z.dim(" nebutra schema --all | jq '.commands[0]'")),P.outro(z.cyan("All output is JSON for easy parsing by agents and automation tools."))),process.exit(0)}catch(o){if(ds(o))throw o;n||c.error(o instanceof Error?o.message:String(o)),process.exit(l.ERROR)}}function Rr(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 ms(t,n)})}import{stdin as Ve}from"process";import*as yt from"@clack/prompts";import R from"picocolors";async function ps(){return new Promise((e,t)=>{let n="";Ve.setEncoding("utf8"),Ve.on("readable",()=>{let r;for(;(r=Ve.read())!==null;)n+=r}),Ve.on("end",()=>{e(n.trim())}),Ve.on("error",t)})}async function gs(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")b(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(`${R.cyan(n.key)}${R.gray(r)}`,"info"),d(` Updated: ${n.updatedAt}`,"info")}}}async function fs(e,t){e||(d("Secret key is required: nebutra secrets set <key>","error"),process.exit(l.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 ps()}catch{d("Failed to read secret from stdin","error"),process.exit(l.ERROR)}}if(n||(d("Secret value cannot be empty","error"),process.exit(l.INVALID_ARGS)),d(`Setting secret ${R.cyan(e)}...`,"info"),t.dryRun){d(`Would encrypt and store secret ${e} for tenant ${t.tenant||"org_123"}`,"info");return}d(`Secret ${R.cyan(e)} encrypted and stored successfully`,"success"),t.description&&d(`Description: ${t.description}`,"info")}async function hs(e,t){e||(d("Secret key is required: nebutra secrets get <key>","error"),process.exit(l.INVALID_ARGS)),d(`Retrieving secret ${R.cyan(e)}...`,"info");let r=t.unmask?"sk_live_abc123xyz789...":"***masked***";t.unmask||d(`Secret value is ${R.yellow("masked")}. Use --unmask to display.`,"warn");let o={key:e,tenant:t.tenant||"org_123",value:r,encrypted:!0};t.format==="json"?b(o,{format:"json"}):d(`${R.cyan(e)}: ${r}`,"info")}async function ys(e,t){e||(d("Secret key is required: nebutra secrets rotate <key>","error"),process.exit(l.INVALID_ARGS));let n=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!t.yes&&!t.dryRun&&n){let r=await yt.confirm({message:`Rotate encryption key for ${R.cyan(e)}? This will re-encrypt with new key material.`,initialValue:!1});(yt.isCancel(r)||!r)&&(d("Rotation cancelled","warn"),process.exit(l.CANCELLED))}if(!t.yes&&!t.dryRun&&!n&&(d("Key rotation requires --yes confirmation","error"),process.exit(l.INVALID_ARGS)),d(`Rotating encryption key for ${R.cyan(e)}...`,"info"),t.dryRun){d(`Would rotate encryption key for ${e}`,"info");return}d(`Encryption key for ${R.cyan(e)} rotated successfully`,"success"),d(`New key version: ${R.dim("v2")}`,"info")}async function bs(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")b(n,{format:"json"});else{d("Secret Access Audit Log","info");for(let r of n){let o=r.success?R.green("\u2713"):R.red("\u2716");d(`${o} ${r.timestamp} ${R.cyan(r.action)} ${r.key}`,r.success?"success":"error"),d(` Actor: ${r.actor}${r.message?` \u2014 ${r.message}`:""}`,"info")}}}async function vs(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")b(t,{format:"json"});else{d("Vault Configuration Check","info");for(let r of t){let o=r.healthy?R.green("\u2713"):R.red("\u2716");d(`${o} ${r.name}: ${r.message}`,r.healthy?"success":"error")}let n=t.every(r=>r.healthy);d(`Overall: ${n?R.green("HEALTHY"):R.red("UNHEALTHY")}`,n?"success":"error")}}function Or(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,i)=>{let a=i.optsWithGlobals?.(),s={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:a?.format||o.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 gs(s);break;case"set":r.length===0&&(d("set requires a secret key: nebutra secrets set <key>","error"),process.exit(l.INVALID_ARGS)),await fs(r[0],s);break;case"get":r.length===0&&(d("get requires a secret key: nebutra secrets get <key>","error"),process.exit(l.INVALID_ARGS)),await hs(r[0],s);break;case"rotate":r.length===0&&(d("rotate requires a secret key: nebutra secrets rotate <key>","error"),process.exit(l.INVALID_ARGS)),await ys(r[0],s);break;case"audit":await bs(s);break;case"verify":await vs(s);break;default:d(`Unknown secrets subcommand: ${n}. Valid commands: list, set, get, rotate, audit, verify`,"error"),process.exit(l.ERROR)}}catch(u){let m=u instanceof Error?u.message:String(u);d(`Secrets command failed: ${m}`,"error"),x("Full error",{error:u}),process.exit(l.ERROR)}}).addHelpText("after",`
455
+ `)}import Xt from"fs";import Qa from"path";import*as W from"@clack/prompts";import ht from"picocolors";var es=["auth","billing","email","storage","queue","cache","notifications","webhooks","ai","mcp"],ts={stack:"sailor-2026-09",capabilities:[...es]};function ns(){return process.stdout.isTTY===!0}function rs(e){return e.yes===!0||e.dryRun===!0||!ns()}function os(e,t,n){return{operation:n?"update":"create",path:e,...n&&{before:n},after:t}}async function Ar(e={}){let t=process.cwd(),n=Qa.join(t,"nebutra.config.json"),r=Xt.existsSync(n);e.ifNotExists&&r&&(d.info("nebutra.config.json already exists, skipping initialization."),process.exit(l.SUCCESS));let o=rs(e);o||W.intro(ht.bgCyan(ht.black(" nebutra init "))),r&&!o&&(W.log.warn("nebutra.config.json already exists in this directory."),await W.confirm({message:"Do you want to overwrite it?",initialValue:!1})||(W.outro("Operation aborted."),process.exit(l.CANCELLED)));let i={...ts};if(e.dryRun){let a=r?JSON.parse(Xt.readFileSync(n,"utf-8")):void 0,s=os(n,i,a);N(s),u("Dry-run completed. No changes made."),process.exit(l.DRY_RUN_OK)}o?d.info("Initializing Nebutra CLI configuration..."):W.spinner().start("Initializing Nebutra CLI configuration..."),Xt.writeFileSync(n,JSON.stringify(i,null,2)+`
456
+ `),o?(d.success("Configuration saved to nebutra.config.json"),d.info('Initialization complete! Run "nebutra status" to check capability readiness.')):(await new Promise(a=>setTimeout(a,800)),W.spinner().stop(ht.green("Configuration saved to nebutra.config.json")),W.outro(ht.cyan('Initialization complete! Run "nebutra status" to check capability readiness.'))),process.exit(l.SUCCESS)}import Qt from"fs/promises";import cs from"os";import en from"path";import*as C from"@clack/prompts";import V from"picocolors";var is="https://analytics.nebutra.com";function as(e={}){if(e.noTelemetry===!0)return!0;let t=process.env.NEBUTRA_TELEMETRY;return t==="0"||t==="false"}function ss(){return process.env.npm_package_version??"0.0.0-dev"}function yt(e,t={}){as(t)||(async()=>{try{let n=await import("@nebutra/analytics");if(typeof n.createProductAnalyticsClient!="function")return;let r=n.createProductAnalyticsClient({posthog:{apiKey:process.env.POSTHOG_KEY??process.env.NEXT_PUBLIC_POSTHOG_KEY??process.env.NEBUTRA_POSTHOG_KEY??"",host:process.env.POSTHOG_HOST??process.env.NEXT_PUBLIC_POSTHOG_HOST??process.env.NEBUTRA_POSTHOG_HOST??is},onError:()=>{}});if(typeof r?.track!="function")return;let o=r.track("license.cli",{action:e.action,cli_version:ss(),...e.tier?{license_tier:e.tier}:{},...e.type?{license_type:e.type}:{},...e.error_code?{error_code:e.error_code}:{}});o&&typeof o.then=="function"&&await o.catch(()=>{})}catch{}})()}var _r=process.env.NEBUTRA_LICENSE_API_URL??"https://nebutra.com";async function Ir(){let e=cs.homedir(),t=en.join(e,".config","nebutra");try{await Qt.mkdir(t,{recursive:!0})}catch{}return t}async function ls(e){let t=`${_r}/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 (${_r}). 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 ds(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 us(e,t){let n=t.quiet===!0;n||C.intro(V.bgCyan(V.black(" Nebutra License Activation "))),yt({action:"activate_attempted"});try{if(e.length<10)throw new Error("Invalid license key format.");n||C.log.step("Validating license key with Nebutra...");let{tier:r,type:o}=await ls(e),i=await Ir(),a=en.join(i,"license.json");await Qt.writeFile(a,JSON.stringify({licenseKey:e,tier:r,type:o,activatedAt:new Date().toISOString()},null,2),"utf-8"),n||(C.log.success(V.green(`${r} license activated and saved to ${a}`)),C.outro(V.cyan("Recorded locally. Sailor scaffolds are MIT-licensed \u2014 this doesn't unlock CLI features; paid tiers are support and hosting, see nebutra.com/pricing."))),yt({action:"activated",tier:r,type:o})}catch(r){yt({action:"failed",error_code:ds(r)}),n||(C.log.error(V.red("Activation failed")),d.error(r instanceof Error?r.message:String(r)),C.outro("Please check your key and try again.")),process.exit(l.ERROR)}}async function ms(e){let t=e.quiet===!0;t||C.intro(V.bgCyan(V.black(" Nebutra License Status ")));try{let n=await Ir(),r=en.join(n,"license.json"),o=await Qt.readFile(r,"utf-8"),i=JSON.parse(o);if(i.licenseKey)t||(C.log.success(V.green("License is active!")),C.log.info(`Key: ${i.licenseKey.substring(0,8)}...`),i.tier&&C.log.info(`Tier: ${V.bold(i.tier)}`),i.type&&C.log.info(`Type: ${i.type}`),C.log.info(`Activated: ${new Date(i.activatedAt).toLocaleString()}`),C.log.message(V.dim("Note: this record is informational. Sailor scaffolds are MIT-licensed and the CLI has no premium features gated behind a license key.")),C.outro(V.cyan("See nebutra.com/pricing for paid support and hosting tiers.")));else throw new Error("License key not found in config.")}catch{t||(C.log.warn(V.yellow("No active license found locally.")),C.log.message(V.dim("This is expected \u2014 Sailor scaffolds are MIT-licensed and nothing in the CLI is gated behind a license. `nebutra license activate <key>` only records a key for support/billing purposes.")),C.outro("")),process.exit(l.NOT_FOUND)}}function Pr(e){let t=e.command("license").description("Record a Nebutra support/billing license key locally (Sailor scaffolds are MIT \u2014 nothing in the CLI is gated by this)");t.command("activate <key>").description("Validate and record a license key locally (support/billing record, not a feature unlock)").option("--quiet","Suppress output").action(async(n,r)=>{await us(n,r)}),t.command("status").description("Check the locally configured license status").option("--quiet","Suppress output").action(async n=>{await ms(n)})}import{existsSync as Nr,readFileSync as ps,writeFileSync as fs}from"fs";import{dirname as gs,join as $r,parse as hs}from"path";import*as bt from"@clack/prompts";function ys(e){let t=e,{root:n}=hs(t);for(;;){if(Nr($r(t,"package.json")))return t;if(t===n)return null;let r=gs(t);if(r===t)return null;t=r}}function bs(e){if(!Nr(e))return{};try{let t=JSON.parse(ps(e,"utf-8"));return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return{}}}function vs(e,t){fs(e,JSON.stringify(t,null,2)+`
457
+ `)}async function ws(e,t){let n=t.format==="json",r=ys(process.cwd());r||(n?console.log(JSON.stringify({command:"link",success:!1,error:"No package.json found upstream"},null,2)):d.error("No package.json found in current or parent directories."),process.exit(l.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)):d.error("project-id is required when running with --yes / non-interactive."),process.exit(l.INVALID_ARGS));let c=await bt.text({message:"Project ID to link to:",placeholder:"proj_abc123",validate:m=>m&&m.trim().length>0?void 0:"Project ID cannot be empty"});bt.isCancel(c)&&(d.warn("Cancelled"),process.exit(l.CANCELLED)),o=String(c).trim()}let i=$r(r,"nebutra.config.json"),s={...bs(i),projectId:o};vs(i,s),n?console.log(JSON.stringify({command:"link",success:!0,projectId:o,configPath:i},null,2)):d.success(`Linked to project: ${o}`)}function jr(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 ws(t,{yes:o.yes||!1,format:n.format||o.format})})}import{spawn as Cs}from"child_process";import{platform as Ss}from"os";var Tr="nebutra-cli";function vt(){let e=process.env.NEBUTRA_AUTH_URL?.trim();return e?e.replace(/\/+$/,""):Z("auth")}var ie=class extends Error{constructor(n,r){super(r);this.code=n;this.name="DeviceAuthError"}code};async function tn(e){try{return await e.json()}catch{return{}}}async function Fr(e,t,n,r={}){let o=r.attempts??3,i=r.sleep??Lr,a;for(let s=1;s<=o;s++){try{let c=await e(t,{...n,signal:AbortSignal.timeout(r.timeoutMs??1e4)});if(c.status<500||s===o)return c;a=new Error(`HTTP ${c.status}`)}catch(c){if(!Ur(c)||s===o)throw c;a=c}await i(1e3*s)}throw a}async function Dr(e,t=fetch){let n=await Fr(t,`${e}/api/auth/device/code`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({client_id:Tr})}),r=await tn(n);if(!n.ok)throw new ie(typeof r.error=="string"?r.error:"request_failed",typeof r.error_description=="string"?r.error_description:`Could not reach ${M.name}'s auth center (${n.status}).`);return r}async function ks(e,t,n=fetch){let r=await n(`${e}/api/auth/device/token`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({grant_type:"urn:ietf:params:oauth:grant-type:device_code",device_code:t,client_id:Tr}),signal:AbortSignal.timeout(1e4)}),o=await tn(r);if(!r.ok)throw new ie(typeof o.error=="string"?o.error:"request_failed",typeof o.error_description=="string"?o.error_description:`HTTP ${r.status}`);return o}var Lr=e=>new Promise(t=>setTimeout(t,e)),xs=30;function Ur(e){if(e instanceof ie)return e.code==="request_failed";if(e instanceof TypeError)return!0;let t=e?.name;return t==="TimeoutError"||t==="AbortError"}async function Br(e,t,n){let r=n.fetchImpl??fetch,o=n.sleep??Lr,i=n.intervalSeconds;for(;;){if(Date.now()>=n.deadline)throw new ie("expired_token","The device code expired before it was approved.");n.onWaiting?.(i),await o(i*1e3);try{return await ks(e,t,r)}catch(a){if(Ur(a)){i=Math.min(i+5,xs),n.onTransientFailure?.(a);continue}if(!(a instanceof ie))throw a;if(a.code==="authorization_pending")continue;if(a.code==="slow_down"){i+=5;continue}throw a}}}async function wt(e,t,n=fetch){let r=await Fr(n,`${e}/api/auth/get-session`,{method:"GET",headers:{authorization:`Bearer ${t}`}});if(!r.ok)return null;let i=(await tn(r)).user;return!i||typeof i.id!="string"?null:{userId:i.id,email:typeof i.email=="string"?i.email:null,name:typeof i.name=="string"?i.name:null}}function Rs(e,t){return!!e.json||e.format==="json"||t==="json"}function Es(e){try{let t=Ss(),[n,r]=t==="darwin"?["open",[e]]:t==="win32"?["cmd",["/c","start","",e]]:["xdg-open",[e]],o=Cs(n,r,{stdio:"ignore",detached:!0});o.on("error",()=>{}),o.unref()}catch{}}async function Os(e,t){let n=new Date;await En({accessToken:t.access_token,tokenType:t.token_type,expiresAt:new Date(n.getTime()+t.expires_in*1e3).toISOString(),scope:t.scope,obtainedAt:n.toISOString()}),Qe()}async function Mr(e,t,n){let r=0,o=await Br(e,t.deviceCode,{intervalSeconds:t.interval,deadline:t.expiresAt,onWaiting:()=>{n||(r++,process.stderr.write(r===1?"Waiting for approval":"."))},onTransientFailure:()=>{n||process.stderr.write("!")}});n||process.stderr.write(`
458
+ `),await Os(e,o);let i=await wt(e,o.access_token);return i?{userId:i.userId,email:i.email}:null}async function As(e,t){let n=Rs(e,t.format),r=vt();if(e.poll){let s=An();if(s&&s.expiresAt>Date.now()&&s.baseUrl===r)try{let c=await Mr(r,{deviceCode:s.deviceCode,interval:s.interval,expiresAt:s.expiresAt},n);n?console.log(JSON.stringify({command:"login",success:!0,...c},null,2)):d.success(c?.email?`Logged in as ${c.email}`:`Logged in to ${M.name}.`);return}catch(c){return Qe(),nn(c,n)}}let o;try{o=await Dr(r)}catch(s){return nn(s,n)}let i=Date.now()+o.expires_in*1e3;if(n&&!e.poll){await jt({deviceCode:o.device_code,userCode:o.user_code,verificationUri:o.verification_uri,verificationUriComplete:o.verification_uri_complete,interval:o.interval,expiresAt:i,baseUrl:r}),console.log(JSON.stringify({command:"login",success:!0,pending:!0,verification_uri:o.verification_uri,verification_uri_complete:o.verification_uri_complete,user_code:o.user_code,expires_in:o.expires_in,interval:o.interval},null,2));return}n||(d.info(`First, confirm this code matches what's shown in the browser:
459
+ `),d.info(` ${o.user_code}
460
+ `),d.info(`Visiting: ${o.verification_uri_complete}`)),e.browser!==!1&&process.stdout.isTTY!==!1&&!t.yes&&Es(o.verification_uri_complete);try{await jt({deviceCode:o.device_code,userCode:o.user_code,verificationUri:o.verification_uri,verificationUriComplete:o.verification_uri_complete,interval:o.interval,expiresAt:i,baseUrl:r});let s=await Mr(r,{deviceCode:o.device_code,interval:o.interval,expiresAt:i},n);n?console.log(JSON.stringify({command:"login",success:!0,...s},null,2)):d.success(s?.email?`Logged in as ${s.email}`:`Logged in to ${M.name}.`)}catch(s){return Qe(),nn(s,n)}}function nn(e,t){let n=e instanceof ie?e.code:"login_failed",r=e instanceof Error?e.message:String(e),o=n==="access_denied"?l.PERMISSION_DENIED:n==="expired_token"?l.TIMEOUT:l.NETWORK_ERROR;t?console.log(JSON.stringify({command:"login",success:!1,error:n,message:r},null,2)):d.error(r),process.exit(o)}function Gr(e){e.command("login").description(`Authorize this machine with your ${M.name} account (device flow)`).option("--json","Print verification details as JSON and exit immediately (agent mode)").option("--no-browser","Don't try to open a browser").option("--poll","Poll until approved/denied/expired, resuming a pending `login --json` if present").option("--format <type>","Output format: json or plain").action(async(t,n)=>{let r=n?.optsWithGlobals?.()||t;await As(t,{format:r.format,yes:r.yes})})}import{existsSync as Vr,rmSync as _s,unlinkSync as Is}from"fs";import{join as Ps}from"path";var Ns=["auth.json","session.json","token","credentials.json"];async function $s(e){let t=Re(),n=e.format==="json",r=0,o=[],i=await On();if(r+=i.length,o.push(...i),e.all){if(Vr(t))try{_s(t,{recursive:!0,force:!0}),r=1,o.push(t)}catch(a){let s=a instanceof Error?a.message:String(a);n?console.log(JSON.stringify({command:"logout",success:!1,error:s},null,2)):d.error(`Failed to clear config directory: ${s}`),process.exit(l.PERMISSION_DENIED)}}else for(let a of Ns){let s=Ps(t,a);if(Vr(s))try{Is(s),r++,o.push(s)}catch{}}r===0&&(n?console.log(JSON.stringify({command:"logout",success:!0,alreadyLoggedOut:!0,removed:[]},null,2)):d.info("Already logged out"),process.exit(l.SUCCESS)),n?console.log(JSON.stringify({command:"logout",success:!0,removed:o,clearedAll:!!e.all},null,2)):d.success(e.all?"Cleared all nebutra config":"Logged out of nebutra.com")}function zr(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 $s({all:t.all||!1,format:t.format||r.format})})}import{spawn as js}from"child_process";import{existsSync as Ts}from"fs";import{createRequire as Fs}from"module";import{dirname as Ds,resolve as Kr}from"path";import{fileURLToPath as Ls}from"url";import*as Yr from"@clack/prompts";import je from"picocolors";var Wr=Ds(Ls(import.meta.url));function Us(){let e=[Kr(Wr,"../../../../ai/mcp/dist/server/contextServer.js"),Kr(Wr,"../../../ai/mcp/dist/server/contextServer.js")];for(let t of e)if(Ts(t))return{command:process.execPath,args:[t]};try{let t=Fs(import.meta.url).resolve("@nebutra/mcp/bin/nebutra-mcp.js");return{command:process.execPath,args:[t]}}catch(t){d.debug("@nebutra/mcp is not installed next to the CLI",{error:t})}return{command:"nebutra-mcp",args:[]}}function Jr(e){e.command("mcp").description("Start the Nebutra MCP server for Cursor/Windsurf").option("--stdio","Use stdio transport (default)",!0).action(async t=>{try{let n=Us();process.stderr.write(je.dim(`nebutra mcp: stdio server running \u2014 Ctrl+C to stop
461
+ `));let r={...process.env};if(!r.NEBUTRA_TOKEN){let i=await ce();i&&!i.expired&&(r.NEBUTRA_TOKEN=i.token)}let o=js(n.command,n.args,{stdio:"inherit",env:r});o.on("close",i=>{i===0?process.stderr.write(je.green(`MCP server stopped.
462
+ `)):i===null&&process.stderr.write(je.yellow(`MCP server terminated.
463
+ `)),process.exit(i??0)}),o.on("error",i=>{d.error(`
464
+ Failed to start MCP server: ${i.message}`),i.code==="ENOENT"&&(d.warn(`
465
+ Tip: Ensure @nebutra/mcp is installed or available in your PATH.`),d.info(" Install globally: npm install -g @nebutra/mcp"),process.exit(l.NOT_FOUND)),process.exit(l.ERROR)}),process.on("SIGINT",()=>{process.stderr.write(je.yellow(`
466
+ Shutting down MCP server...
467
+ `)),o.kill("SIGINT")}),process.on("SIGTERM",()=>{process.stderr.write(je.yellow(`Terminating MCP server...
468
+ `)),o.kill("SIGTERM")})}catch(n){Yr.log.error(je.red("Error: Unable to start MCP server. Ensure @nebutra/mcp is properly installed.")),n instanceof Error&&d.error(n.message),process.exit(l.ERROR)}})}import*as $ from"@clack/prompts";import Y from"picocolors";var Hr=Object.fromEntries(Object.entries(yn).map(([e,t])=>[e,t]));function Zr(e){return typeof e=="boolean"?"boolean":"string"}function Xr(e){return{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:Zr(n.default)})),examples:e.examples||[]}}function Bs(e){let n=(Q.subcommands||[]).find(r=>r.name===e);return n?Xr(n):null}function Ms(){return(Q.options??[]).map(e=>({flags:e.flags,description:e.description,required:!1,type:Zr(e.default)}))}function Gs(e){let t=Q.subcommands||[];return{name:Q.name,version:e,description:Q.description,usage:Q.usage||"nebutra [command] [options]",globalOptions:Ms(),commands:t.map(Xr),exitCodes:Hr,timestamp:new Date().toISOString()}}function qr(){return(Q.subcommands||[]).map(t=>t.name)}function kt(e){process.stdout.write(`${JSON.stringify(e,null,2)}
469
+ `)}function Vs(e){return e instanceof Error&&e.message.startsWith("EXIT:")}async function zs(e,t){let n=t.quiet===!0,r="0.1.0";try{if(t.all===!0&&(kt(Gs(r)),process.exit(0)),t.list===!0&&(kt(qr()),process.exit(0)),(t.exitCodes===!0||t["exit-codes"]===!0)&&(kt(Hr),process.exit(0)),e){let o=Bs(e);o||(n||(d.error(`Unknown command '${e}'`),d.info(`Available commands: ${qr().join(", ")}`)),process.exit(l.NOT_FOUND)),kt(o),process.exit(0)}n||($.intro(Y.bgCyan(Y.black(" nebutra schema "))),$.log.info(Y.cyan("Agent-friendly introspection of CLI commands, arguments, and value domains.")),$.log.message(""),$.log.info(Y.bold("Usage:")),$.log.message(Y.dim(" nebutra schema --all Show full command tree as JSON")),$.log.message(Y.dim(" nebutra schema <command> Show schema for a specific command")),$.log.message(Y.dim(" nebutra schema --list List available command names")),$.log.message(Y.dim(" nebutra schema --exit-codes Show exit codes reference")),$.log.message(""),$.log.info(Y.bold("Example:")),$.log.message(Y.dim(" nebutra schema status")),$.log.message(Y.dim(" nebutra schema --all | jq '.commands[0]'")),$.outro(Y.cyan("All output is JSON for easy parsing by agents and automation tools."))),process.exit(0)}catch(o){if(Vs(o))throw o;n||d.error(o instanceof Error?o.message:String(o)),process.exit(l.ERROR)}}function Qr(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 zs(t,n)})}import{stdin as We}from"process";import*as xt from"@clack/prompts";import A from"picocolors";async function Ks(){return new Promise((e,t)=>{let n="";We.setEncoding("utf8"),We.on("readable",()=>{let r;for(;(r=We.read())!==null;)n+=r}),We.on("end",()=>{e(n.trim())}),We.on("error",t)})}async function Ws(e){u("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")b(t,{format:"json"});else{u("Secrets for tenant: "+(e.tenant||"org_123"),"info");for(let n of t){let r=n.description?` \u2014 ${n.description}`:"";u(`${A.cyan(n.key)}${A.gray(r)}`,"info"),u(` Updated: ${n.updatedAt}`,"info")}}}async function Ys(e,t){e||(u("Secret key is required: nebutra secrets set <key>","error"),process.exit(l.INVALID_ARGS));let n;if(t.value)u("WARNING: Using --value flag exposes secret in shell history. Prefer stdin.","warn"),n=t.value;else{u("Enter secret value (will not echo). Press Ctrl+D when done:","info");try{n=await Ks()}catch{u("Failed to read secret from stdin","error"),process.exit(l.ERROR)}}if(n||(u("Secret value cannot be empty","error"),process.exit(l.INVALID_ARGS)),u(`Setting secret ${A.cyan(e)}...`,"info"),t.dryRun){u(`Would encrypt and store secret ${e} for tenant ${t.tenant||"org_123"}`,"info");return}u(`Secret ${A.cyan(e)} encrypted and stored successfully`,"success"),t.description&&u(`Description: ${t.description}`,"info")}async function Js(e,t){e||(u("Secret key is required: nebutra secrets get <key>","error"),process.exit(l.INVALID_ARGS)),u(`Retrieving secret ${A.cyan(e)}...`,"info");let r=t.unmask?"sk_live_abc123xyz789...":"***masked***";t.unmask||u(`Secret value is ${A.yellow("masked")}. Use --unmask to display.`,"warn");let o={key:e,tenant:t.tenant||"org_123",value:r,encrypted:!0};t.format==="json"?b(o,{format:"json"}):u(`${A.cyan(e)}: ${r}`,"info")}async function qs(e,t){e||(u("Secret key is required: nebutra secrets rotate <key>","error"),process.exit(l.INVALID_ARGS));let n=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!t.yes&&!t.dryRun&&n){let r=await xt.confirm({message:`Rotate encryption key for ${A.cyan(e)}? This will re-encrypt with new key material.`,initialValue:!1});(xt.isCancel(r)||!r)&&(u("Rotation cancelled","warn"),process.exit(l.CANCELLED))}if(!t.yes&&!t.dryRun&&!n&&(u("Key rotation requires --yes confirmation","error"),process.exit(l.INVALID_ARGS)),u(`Rotating encryption key for ${A.cyan(e)}...`,"info"),t.dryRun){u(`Would rotate encryption key for ${e}`,"info");return}u(`Encryption key for ${A.cyan(e)} rotated successfully`,"success"),u(`New key version: ${A.dim("v2")}`,"info")}async function Hs(e){u("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")b(n,{format:"json"});else{u("Secret Access Audit Log","info");for(let r of n){let o=r.success?A.green("\u2713"):A.red("\u2716");u(`${o} ${r.timestamp} ${A.cyan(r.action)} ${r.key}`,r.success?"success":"error"),u(` Actor: ${r.actor}${r.message?` \u2014 ${r.message}`:""}`,"info")}}}async function Zs(e){u("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")b(t,{format:"json"});else{u("Vault Configuration Check","info");for(let r of t){let o=r.healthy?A.green("\u2713"):A.red("\u2716");u(`${o} ${r.name}: ${r.message}`,r.healthy?"success":"error")}let n=t.every(r=>r.healthy);u(`Overall: ${n?A.green("HEALTHY"):A.red("UNHEALTHY")}`,n?"success":"error")}}function eo(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,i)=>{let a=i.optsWithGlobals?.(),s={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:a?.format||o.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 Ws(s);break;case"set":r.length===0&&(u("set requires a secret key: nebutra secrets set <key>","error"),process.exit(l.INVALID_ARGS)),await Ys(r[0],s);break;case"get":r.length===0&&(u("get requires a secret key: nebutra secrets get <key>","error"),process.exit(l.INVALID_ARGS)),await Js(r[0],s);break;case"rotate":r.length===0&&(u("rotate requires a secret key: nebutra secrets rotate <key>","error"),process.exit(l.INVALID_ARGS)),await qs(r[0],s);break;case"audit":await Hs(s);break;case"verify":await Zs(s);break;default:u(`Unknown secrets subcommand: ${n}. Valid commands: list, set, get, rotate, audit, verify`,"error"),process.exit(l.ERROR)}}catch(c){let m=c instanceof Error?c.message:String(c);u(`Secrets command failed: ${m}`,"error"),x("Full error",{error:c}),process.exit(l.ERROR)}}).addHelpText("after",`
465
470
  Examples:
466
471
  nebutra secrets list List all secrets (metadata only)
467
472
  nebutra secrets list --tenant org_456 List secrets for specific tenant
@@ -496,8 +501,8 @@ Security Notes:
496
501
  - Never use --value in production (visible in shell history)
497
502
  - Always use --unmask when viewing secrets in scripts
498
503
  - Audit log tracks all access and modifications
499
- `)}import{existsSync as ks,readFileSync as ws}from"fs";import{resolve as xs}from"path";import*as bt from"@clack/prompts";import N from"picocolors";function Cs(e){let t=["postgres","redis","clickhouse"],n=xs(e,"docker-compose.yml");if(!ks(n))return t;try{let o=ws(n,"utf-8").split(`
500
- `),i=o.findIndex(s=>/^services:\s*$/.test(s));if(i===-1)return t;let a=[];for(let s=i+1;s<o.length;s++){let u=o[s];if(/^[a-zA-Z]/.test(u))break;let m=u.match(/^ {2}([a-zA-Z0-9_-]+):\s*$/);m&&a.push(m[1])}return a.length>0?a:t}catch{return t}}function Ar(e){switch(e.toLowerCase()){case"running":return"running";case"exited":return"exited";case"restarting":return"restarting";case"paused":return"paused";default:return"exited"}}function Ss(e){switch(e){case"running":return N.green("\u25CF");case"exited":return N.red("\u25CF");case"restarting":return N.yellow("\u25CF");case"paused":return N.gray("\u25CF");default:return N.gray("\u25CF")}}async function Es(e){d("Checking service status...","info");let t=await E({command:"docker",args:["compose","ps","--format","json"],cwd:y(),interactive:!1,label:"Docker Compose PS",dryRun:e.dryRun});t.exitCode!==0&&(d("Failed to fetch service status","error"),x("Docker error",{stderr:t.stderr}),process.exit(l.ERROR));try{let n=JSON.parse(t.stdout);if(e.format==="json"){let r=n.map(o=>({name:o.Name,status:Ar(o.State),exitCode:o.ExitCode}));b(r,{format:"json"})}else{d("Service Status","info");let r=n.map(o=>({Service:Ss(Ar(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"),x("Parse error",{error:String(n)}),process.exit(l.ERROR)}}async function Rs(e,t){d(`Streaming logs for ${N.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 E({command:"docker",args:n,cwd:y(),interactive:!0,label:`Docker Compose Logs (${e})`,dryRun:t.dryRun})).exitCode!==0&&(d(`Failed to stream logs for ${e}`,"error"),process.exit(l.ERROR))}async function Os(e,t){let n=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!t.yes&&!t.dryRun&&n){let i=await bt.confirm({message:`Restart service ${N.cyan(e)}?`,initialValue:!1});(bt.isCancel(i)||!i)&&(d("Restart cancelled","warn"),process.exit(l.CANCELLED))}d(`Restarting service ${N.cyan(e)}...`,"info");let r=["compose","restart"];t.timeout&&r.push("--timeout",String(t.timeout)),r.push(e);let o=await E({command:"docker",args:r,cwd:y(),interactive:!1,label:`Docker Compose Restart (${e})`,dryRun:t.dryRun});o.exitCode===0?d(`Service ${N.cyan(e)} restarted successfully`,"success"):(d(`Failed to restart service ${e}`,"error"),x("Docker error",{stderr:o.stderr}),process.exit(l.ERROR))}async function As(e){d("Running deep health check...","info");let t=[],n=1e4,r=await E({command:"docker",args:["compose","exec","-T","postgres","pg_isready"],cwd:y(),interactive:!1,label:"PostgreSQL Health",dryRun:e.dryRun,timeoutMs:n});t.push({service:"PostgreSQL",healthy:r.exitCode===0,message:r.timedOut?"Timed out":r.exitCode===0?"Ready":"Not responding"});let o=await E({command:"docker",args:["compose","exec","-T","redis","redis-cli","ping"],cwd:y(),interactive:!1,label:"Redis Health",dryRun:e.dryRun,timeoutMs:n});t.push({service:"Redis",healthy:o.exitCode===0,message:o.timedOut?"Timed out":o.exitCode===0?"Ready":"Not responding"});let i=await E({command:"docker",args:["compose","exec","-T","clickhouse","curl","-s","http://localhost:8123/ping"],cwd:y(),interactive:!1,label:"ClickHouse Health",dryRun:e.dryRun,timeoutMs:n});if(t.push({service:"ClickHouse",healthy:i.exitCode===0,message:i.timedOut?"Timed out":i.exitCode===0?"Ready":"Not responding"}),e.format==="json")b(t,{format:"json"});else{for(let s of t){let u=s.healthy?N.green("\u2713"):N.red("\u2716");d(`${u} ${s.service}: ${s.message}`,s.healthy?"success":"error")}let a=t.every(s=>s.healthy);d(`Overall health: ${a?N.green("HEALTHY"):N.red("UNHEALTHY")}`,a?"success":"error")}}async function _s(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(l.INVALID_ARGS)),d(`Scaling ${N.cyan(e)} to ${N.cyan(String(r))} replicas...`,"info");let o=await E({command:"docker",args:["compose","up","-d","--scale",`${e}=${r}`],cwd:y(),interactive:!1,label:`Docker Compose Scale (${e})`,dryRun:n.dryRun});o.exitCode===0?d(`Service ${N.cyan(e)} scaled to ${r} replicas`,"success"):(d(`Failed to scale service ${e}`,"error"),x("Docker error",{stderr:o.stderr}),process.exit(l.ERROR))}function _r(e){e.command("services <verb> [args...]").description("Manage Docker Compose microservices (see `nebutra services --help` for the list)").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,i)=>{let a=i.optsWithGlobals?.(),s={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:a?.format||o.format,tail:o.tail??100,since:o.since,timeout:o.timeout};try{switch(n){case"status":case"list":await Es(s);break;case"logs":r.length===0&&(d("logs requires a service name: nebutra services logs <service>","error"),process.exit(l.INVALID_ARGS)),await Rs(r[0],s);break;case"restart":r.length===0&&(d("restart requires a service name: nebutra services restart <service>","error"),process.exit(l.INVALID_ARGS)),await Os(r[0],s);break;case"health":await As(s);break;case"scale":r.length<2&&(d("scale requires a service name and replica count: nebutra services scale <service> <count>","error"),process.exit(l.INVALID_ARGS)),await _s(r[0],r[1],s);break;default:d(`Unknown services subcommand: ${n}. Valid commands: status, list, logs, restart, health, scale`,"error"),process.exit(l.ERROR)}}catch(u){let m=u instanceof Error?u.message:String(u);d(`Services command failed: ${m}`,"error"),x("Full error",{error:u}),process.exit(l.ERROR)}}).addHelpText("after",()=>{let n="postgres, redis, clickhouse (run `nebutra services status` for the live list)";try{n=Cs(dn()).join(", ")}catch{}return`
504
+ `)}import{existsSync as Xs,readFileSync as Qs}from"fs";import{resolve as ec}from"path";import*as Ct from"@clack/prompts";import j from"picocolors";function tc(e){let t=["postgres","redis","clickhouse"],n=ec(e,"docker-compose.yml");if(!Xs(n))return t;try{let o=Qs(n,"utf-8").split(`
505
+ `),i=o.findIndex(s=>/^services:\s*$/.test(s));if(i===-1)return t;let a=[];for(let s=i+1;s<o.length;s++){let c=o[s];if(/^[a-zA-Z]/.test(c))break;let m=c.match(/^ {2}([a-zA-Z0-9_-]+):\s*$/);m&&a.push(m[1])}return a.length>0?a:t}catch{return t}}function to(e){switch(e.toLowerCase()){case"running":return"running";case"exited":return"exited";case"restarting":return"restarting";case"paused":return"paused";default:return"exited"}}function nc(e){switch(e){case"running":return j.green("\u25CF");case"exited":return j.red("\u25CF");case"restarting":return j.yellow("\u25CF");case"paused":return j.gray("\u25CF");default:return j.gray("\u25CF")}}async function rc(e){u("Checking service status...","info");let t=await R({command:"docker",args:["compose","ps","--format","json"],cwd:y(),interactive:!1,label:"Docker Compose PS",dryRun:e.dryRun});t.exitCode!==0&&(u("Failed to fetch service status","error"),x("Docker error",{stderr:t.stderr}),process.exit(l.ERROR));try{let n=JSON.parse(t.stdout);if(e.format==="json"){let r=n.map(o=>({name:o.Name,status:to(o.State),exitCode:o.ExitCode}));b(r,{format:"json"})}else{u("Service Status","info");let r=n.map(o=>({Service:nc(to(o.State))+" "+o.Name,Status:o.State,"Exit Code":o.ExitCode||"\u2014"}));for(let o of r)u(`${o.Service} (${o.Status})`,"info")}}catch(n){u("Failed to parse service status","error"),x("Parse error",{error:String(n)}),process.exit(l.ERROR)}}async function oc(e,t){u(`Streaming logs for ${j.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 R({command:"docker",args:n,cwd:y(),interactive:!0,label:`Docker Compose Logs (${e})`,dryRun:t.dryRun})).exitCode!==0&&(u(`Failed to stream logs for ${e}`,"error"),process.exit(l.ERROR))}async function ic(e,t){let n=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!t.yes&&!t.dryRun&&n){let i=await Ct.confirm({message:`Restart service ${j.cyan(e)}?`,initialValue:!1});(Ct.isCancel(i)||!i)&&(u("Restart cancelled","warn"),process.exit(l.CANCELLED))}u(`Restarting service ${j.cyan(e)}...`,"info");let r=["compose","restart"];t.timeout&&r.push("--timeout",String(t.timeout)),r.push(e);let o=await R({command:"docker",args:r,cwd:y(),interactive:!1,label:`Docker Compose Restart (${e})`,dryRun:t.dryRun});o.exitCode===0?u(`Service ${j.cyan(e)} restarted successfully`,"success"):(u(`Failed to restart service ${e}`,"error"),x("Docker error",{stderr:o.stderr}),process.exit(l.ERROR))}async function ac(e){u("Running deep health check...","info");let t=[],n=1e4,r=await R({command:"docker",args:["compose","exec","-T","postgres","pg_isready"],cwd:y(),interactive:!1,label:"PostgreSQL Health",dryRun:e.dryRun,timeoutMs:n});t.push({service:"PostgreSQL",healthy:r.exitCode===0,message:r.timedOut?"Timed out":r.exitCode===0?"Ready":"Not responding"});let o=await R({command:"docker",args:["compose","exec","-T","redis","redis-cli","ping"],cwd:y(),interactive:!1,label:"Redis Health",dryRun:e.dryRun,timeoutMs:n});t.push({service:"Redis",healthy:o.exitCode===0,message:o.timedOut?"Timed out":o.exitCode===0?"Ready":"Not responding"});let i=await R({command:"docker",args:["compose","exec","-T","clickhouse","curl","-s","http://localhost:8123/ping"],cwd:y(),interactive:!1,label:"ClickHouse Health",dryRun:e.dryRun,timeoutMs:n});if(t.push({service:"ClickHouse",healthy:i.exitCode===0,message:i.timedOut?"Timed out":i.exitCode===0?"Ready":"Not responding"}),e.format==="json")b(t,{format:"json"});else{for(let s of t){let c=s.healthy?j.green("\u2713"):j.red("\u2716");u(`${c} ${s.service}: ${s.message}`,s.healthy?"success":"error")}let a=t.every(s=>s.healthy);u(`Overall health: ${a?j.green("HEALTHY"):j.red("UNHEALTHY")}`,a?"success":"error")}}async function sc(e,t,n){let r=parseInt(t,10);(Number.isNaN(r)||r<0)&&(u("Invalid replica count (must be a non-negative number)","error"),process.exit(l.INVALID_ARGS)),u(`Scaling ${j.cyan(e)} to ${j.cyan(String(r))} replicas...`,"info");let o=await R({command:"docker",args:["compose","up","-d","--scale",`${e}=${r}`],cwd:y(),interactive:!1,label:`Docker Compose Scale (${e})`,dryRun:n.dryRun});o.exitCode===0?u(`Service ${j.cyan(e)} scaled to ${r} replicas`,"success"):(u(`Failed to scale service ${e}`,"error"),x("Docker error",{stderr:o.stderr}),process.exit(l.ERROR))}function no(e){e.command("services <verb> [args...]").description("Manage Docker Compose microservices (see `nebutra services --help` for the list)").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,i)=>{let a=i.optsWithGlobals?.(),s={dryRun:o.dryRun||a?.dryRun,yes:o.yes||a?.yes,format:a?.format||o.format,tail:o.tail??100,since:o.since,timeout:o.timeout};try{switch(n){case"status":case"list":await rc(s);break;case"logs":r.length===0&&(u("logs requires a service name: nebutra services logs <service>","error"),process.exit(l.INVALID_ARGS)),await oc(r[0],s);break;case"restart":r.length===0&&(u("restart requires a service name: nebutra services restart <service>","error"),process.exit(l.INVALID_ARGS)),await ic(r[0],s);break;case"health":await ac(s);break;case"scale":r.length<2&&(u("scale requires a service name and replica count: nebutra services scale <service> <count>","error"),process.exit(l.INVALID_ARGS)),await sc(r[0],r[1],s);break;default:u(`Unknown services subcommand: ${n}. Valid commands: status, list, logs, restart, health, scale`,"error"),process.exit(l.ERROR)}}catch(c){let m=c instanceof Error?c.message:String(c);u(`Services command failed: ${m}`,"error"),x("Full error",{error:c}),process.exit(l.ERROR)}}).addHelpText("after",()=>{let n="postgres, redis, clickhouse (run `nebutra services status` for the live list)";try{n=tc(vn()).join(", ")}catch{}return`
501
506
  Examples:
502
507
  nebutra services status Show all service status
503
508
  nebutra services list Alias for \`status\`
@@ -517,26 +522,28 @@ Flags:
517
522
  --tail <n> Log lines to show (default: 100)
518
523
  --since <duration> Show logs since (e.g., 10m, 1h)
519
524
  --timeout <seconds> Timeout for restart
520
- `})}import{join as Fs}from"path";import xe from"picocolors";import{existsSync as Ir,readFileSync as Pr}from"fs";import{dirname as Ns,join as Nr,parse as $s}from"path";var K=class extends Error{code;exitCode;suggestion;retryable;context;cause;constructor(t){super(t.message),this.name="CommandError",this.code=t.code,this.exitCode=t.exitCode??l.ERROR,this.suggestion=t.suggestion,this.retryable=t.retryable??!1,this.context=t.context,this.cause=t.cause}};function Is(e){return e instanceof K}function Ps(e){return Is(e)?Ye({error:e.code,message:e.message,suggestion:e.suggestion,retryable:e.retryable,exitCode:e.exitCode,context:e.context,cause:e.cause instanceof Error?e.cause:e}):e instanceof Error?Ye({error:"unexpected_error",message:e.message,suggestion:"Re-run with NEBUTRA_DEBUG=1 for a stack trace.",exitCode:l.ERROR,cause:e}):Ye({error:"unexpected_error",message:String(e),suggestion:"Re-run with NEBUTRA_DEBUG=1 for a stack trace.",exitCode:l.ERROR})}function Jt(e){let t=Ps(e);return un(t),t.exitCode}async function we(e){try{await e()}catch(t){process.exitCode=Jt(t)}}var $e={auth:{providers:[{id:"better-auth",envKeys:["BETTER_AUTH_SECRET"]}],degrade:{kind:"fallback",provider:"dev"},next:"set BETTER_AUTH_SECRET to run Better Auth (dev fallback runs without it)"},billing:{providers:[{id:"creem",envKeys:["CREEM_API_KEY","CREEM_PRODUCT_ID"]},{id:"wechat-pay",envKeys:["WECHATPAY_MCHID","WECHATPAY_APP_ID","WECHATPAY_PRIVATE_KEY","WECHATPAY_SERIAL_NO","WECHATPAY_API_V3_KEY"]},{id:"alipay",envKeys:["ALIPAY_APP_ID","ALIPAY_PRIVATE_KEY","ALIPAY_PUBLIC_KEY"]}],degrade:{kind:"missing-key"},next:"set CREEM_API_KEY/CREEM_PRODUCT_ID (or WECHATPAY_* / ALIPAY_* for mainland China)"},email:{providers:[{id:"resend",envKeys:["RESEND_API_KEY"]}],degrade:{kind:"fallback",provider:"console"},next:"set RESEND_API_KEY to send real email (console fallback logs instead)"},sms:{providers:[{id:"twilio-verify",envKeys:["TWILIO_ACCOUNT_SID","TWILIO_AUTH_TOKEN","TWILIO_VERIFY_SERVICE_SID"]},{id:"aliyun",envKeys:["ALIYUN_SMS_ACCESS_KEY_ID","ALIYUN_SMS_ACCESS_KEY_SECRET"]}],degrade:{kind:"missing-key"},next:"set TWILIO_ACCOUNT_SID/TWILIO_AUTH_TOKEN/TWILIO_VERIFY_SERVICE_SID (or ALIYUN_SMS_* for mainland China)"},storage:{providers:[{id:"s3-compatible",envKeys:["S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY","S3_ENDPOINT"]}],degrade:{kind:"fallback",provider:"local"},next:"set S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY/S3_ENDPOINT (or AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY) for S3/R2 storage"},queue:{providers:[{id:"qstash",envKeys:["QSTASH_TOKEN"]}],degrade:{kind:"fallback",provider:"memory"},next:"set QSTASH_TOKEN to run the QStash queue (memory fallback is dev/test only)"},cache:{providers:[{id:"redis",envKeys:["UPSTASH_REDIS_REST_URL","UPSTASH_REDIS_REST_TOKEN"]}],degrade:{kind:"fallback",provider:"memory"},next:"set UPSTASH_REDIS_REST_URL/UPSTASH_REDIS_REST_TOKEN to run Redis (memory fallback is dev/test only)"},notifications:{providers:[{id:"direct",envKeys:[]}],degrade:{kind:"missing-key"},next:"always live \u2014 no configuration required"},webhooks:{providers:[{id:"custom",envKeys:[]}],degrade:{kind:"missing-key"},next:"always live \u2014 no configuration required"},ai:{providers:[{id:"openai",envKeys:["OPENAI_API_KEY"]},{id:"anthropic",envKeys:["ANTHROPIC_API_KEY"]},{id:"deepseek",envKeys:["DEEPSEEK_API_KEY"]},{id:"bailian",envKeys:["DASHSCOPE_API_KEY"]}],degrade:{kind:"missing-key"},next:"set one of OPENAI_API_KEY / ANTHROPIC_API_KEY / DEEPSEEK_API_KEY / DASHSCOPE_API_KEY"},mcp:{providers:[{id:"built-in",envKeys:[]}],degrade:{kind:"missing-key"},next:"always live \u2014 no configuration required"},monitoring:{providers:[{id:"sentry",envKeys:["SENTRY_DSN"]}],degrade:{kind:"missing-key"},next:"set SENTRY_DSN to enable error monitoring"},analytics:{providers:[{id:"posthog",envKeys:["NEXT_PUBLIC_POSTHOG_KEY"]}],degrade:{kind:"missing-key"},next:"set NEXT_PUBLIC_POSTHOG_KEY to enable analytics"},captcha:{providers:[{id:"turnstile",envKeys:["TURNSTILE_SECRET_KEY"]}],degrade:{kind:"missing-key"},next:"set TURNSTILE_SECRET_KEY to enable captcha"}};function vt(e){let t=e,{root:n}=$s(t);for(;;){if(Ir(Nr(t,"nebutra.config.json")))return t;if(t===n)return null;let r=Ns(t);if(r===t)return null;t=r}}function kt(e){let t=Pr(e,"utf-8"),n=JSON.parse(t);if(!n||typeof n!="object"||Array.isArray(n))throw new K({code:"invalid_manifest",message:`nebutra.config.json at ${e} is not a JSON object.`,suggestion:"Re-run `create-sailor` or fix the file by hand.",exitCode:l.CONFIG_ERROR});return n}function js(e){let t={};for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;let o=r.startsWith("export ")?r.slice(7):r,i=o.indexOf("=");if(i===-1)continue;let a=o.slice(0,i).trim();if(!a)continue;let s=o.slice(i+1).trim();(s.startsWith('"')&&s.endsWith('"')&&s.length>=2||s.startsWith("'")&&s.endsWith("'")&&s.length>=2)&&(s=s.slice(1,-1)),t[a]=s}return t}function $r(e){let t={...process.env};for(let n of[".env",".env.local"]){let r=Nr(e,n);if(Ir(r))try{let o=js(Pr(r,"utf-8"));for(let[i,a]of Object.entries(o))process.env[i]===void 0&&(t[i]=a)}catch{}}return t}function jr(e,t,n){let r=[],o=[];for(let i of t.providers){let a=i.envKeys.filter(s=>!n[s]);a.length===0?r.push(i.id):o.length===0&&i.envKeys.length>0&&(o=a)}return r.length>0?{name:e,state:"live",provider:r,missing:[],next:t.next}:t.degrade.kind==="fallback"?{name:e,state:"local-fallback",provider:[t.degrade.provider],missing:o,next:t.next}:{name:e,state:"missing-key",provider:[],missing:o,next:t.next}}function Fr(e){let t=Object.keys($e),n=e.filter(a=>!$e[a]);if(n.length>0)throw new K({code:"unknown_capability",message:`Unknown capabilit${n.length===1?"y":"ies"} in nebutra.config.json: ${n.join(", ")}.`,suggestion:`Valid capabilities: ${t.join(", ")}.`,exitCode:l.CONFIG_ERROR,context:{unknown:n,valid:t}});let r=new Set,o=[],i=[];for(let a of e){if(r.has(a)){o.push(`duplicate capability "${a}" in nebutra.config.json \u2014 ignoring repeat`);continue}r.add(a),i.push(a)}return{capabilities:i,warnings:o}}function Ds(e,t){switch(e){case"live":return xe.green(t);case"local-fallback":return xe.yellow(t);case"missing-key":return xe.red(t);default:return t}}async function Ts(e){let t=vt(process.cwd());if(!t)throw new K({code:"manifest_not_found",message:"No nebutra.config.json found in this directory or any parent directory.",suggestion:"Run `npx create-sailor` to scaffold a Sailor project first.",exitCode:l.CONFIG_ERROR});let n=Fs(t,"nebutra.config.json"),r=kt(n),o=Array.isArray(r.capabilities)?r.capabilities:[],i=$r(t),a=i.NEBUTRA_LOCALE||"global",s=typeof r.stack=="string"?r.stack:"unknown",u=o.map(g=>{let k=$e[g];return k?jr(g,k,i):{name:g,state:"missing-key",provider:[],missing:[],next:"unknown capability \u2014 no readiness rule registered in `nebutra status`"}});if(e.json===!0||e.format==="json"){b({stack:s,locale:a,capabilities:u},{format:"json"});return}let p=[];p.push(xe.bold("nebutra status")),p.push(xe.dim(`stack: ${s} locale: ${a} project: ${t}`)),p.push("");let f=Math.max(10,...u.map(g=>g.name.length)),h=Math.max(14,...u.map(g=>g.state.length));for(let g of u){let k=g.name.padEnd(f),F=Ds(g.state,g.state.padEnd(h)),w=g.provider.length>0?g.provider.join(", "):"-";p.push(`${k} ${F} ${xe.dim(w)}`),g.state!=="live"&&p.push(`${" ".repeat(f)} ${xe.dim(`\u2192 ${g.next}`)}`)}console.log(p.join(`
521
- `))}function Dr(e){e.command("status").description("Show capability readiness \u2014 live, local-fallback, or missing-key, per env").option("--json","Emit machine-readable JSON").action((t,n)=>we(async()=>{let r=n.optsWithGlobals?n.optsWithGlobals():{};await Ts({json:t.json,format:r.format})}))}import{existsSync as Tr,readFileSync as Ls,writeFileSync as Lr}from"fs";import{join as qt}from"path";import ze from"picocolors";var Ur=".env.example",Br=".env.local",Us=["# Local secrets for this machine \u2014 never commit real values.","# `nebutra sync` created this file; add your own keys below.",""].join(`
522
- `);function Bs(e){let t=new Set;for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;let o=r.startsWith("export ")?r.slice(7):r,i=o.indexOf("=");if(i===-1)continue;let a=o.slice(0,i).trim();a&&t.add(a)}return t}function Ms(e,t){let n=Bs(e),r=[],o=[],i=[];for(let m of t){let p=$e[m];if(p)for(let f of p.providers){if(f.envKeys.length===0)continue;let h=[];for(let g of f.envKeys)n.has(g)?o.push(g):h.push(g);if(h.length!==0){i.push([`# ${m} (${f.id})`,...h.map(g=>`${g}=`)].join(`
523
- `));for(let g of h)n.add(g),r.push(g)}}}if(i.length===0)return{content:e,added:r,unchanged:o};let a=e.replace(/\s+$/,"");return{content:`${a.length>0?`${a}
525
+ `})}import{join as uc}from"path";import xe from"picocolors";import{existsSync as ro,readFileSync as oo}from"fs";import{dirname as cc,join as io,parse as lc}from"path";var Te={auth:{providers:[{id:"better-auth",envKeys:["BETTER_AUTH_SECRET"]}],degrade:{kind:"fallback",provider:"dev"},next:"set BETTER_AUTH_SECRET to run Better Auth (dev fallback runs without it)"},billing:{providers:[{id:"creem",envKeys:["CREEM_API_KEY","CREEM_PRODUCT_ID"]},{id:"wechat-pay",envKeys:["WECHATPAY_MCHID","WECHATPAY_APP_ID","WECHATPAY_PRIVATE_KEY","WECHATPAY_SERIAL_NO","WECHATPAY_API_V3_KEY"]},{id:"alipay",envKeys:["ALIPAY_APP_ID","ALIPAY_PRIVATE_KEY","ALIPAY_PUBLIC_KEY"]}],degrade:{kind:"missing-key"},next:"set CREEM_API_KEY/CREEM_PRODUCT_ID (or WECHATPAY_* / ALIPAY_* for mainland China)"},email:{providers:[{id:"resend",envKeys:["RESEND_API_KEY"]}],degrade:{kind:"fallback",provider:"console"},next:"set RESEND_API_KEY to send real email (console fallback logs instead)"},sms:{providers:[{id:"twilio-verify",envKeys:["TWILIO_ACCOUNT_SID","TWILIO_AUTH_TOKEN","TWILIO_VERIFY_SERVICE_SID"]},{id:"aliyun",envKeys:["ALIYUN_SMS_ACCESS_KEY_ID","ALIYUN_SMS_ACCESS_KEY_SECRET"]}],degrade:{kind:"missing-key"},next:"set TWILIO_ACCOUNT_SID/TWILIO_AUTH_TOKEN/TWILIO_VERIFY_SERVICE_SID (or ALIYUN_SMS_* for mainland China)"},storage:{providers:[{id:"s3-compatible",envKeys:["S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY","S3_ENDPOINT"]}],degrade:{kind:"fallback",provider:"local"},next:"set S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY/S3_ENDPOINT (or AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY) for S3/R2 storage"},queue:{providers:[{id:"qstash",envKeys:["QSTASH_TOKEN"]}],degrade:{kind:"fallback",provider:"memory"},next:"set QSTASH_TOKEN to run the QStash queue (memory fallback is dev/test only)"},cache:{providers:[{id:"redis",envKeys:["UPSTASH_REDIS_REST_URL","UPSTASH_REDIS_REST_TOKEN"]}],degrade:{kind:"fallback",provider:"memory"},next:"set UPSTASH_REDIS_REST_URL/UPSTASH_REDIS_REST_TOKEN to run Redis (memory fallback is dev/test only)"},notifications:{providers:[{id:"direct",envKeys:[]}],degrade:{kind:"missing-key"},next:"always live \u2014 no configuration required"},webhooks:{providers:[{id:"custom",envKeys:[]}],degrade:{kind:"missing-key"},next:"always live \u2014 no configuration required"},ai:{providers:[{id:"openai",envKeys:["OPENAI_API_KEY"]},{id:"anthropic",envKeys:["ANTHROPIC_API_KEY"]},{id:"deepseek",envKeys:["DEEPSEEK_API_KEY"]},{id:"bailian",envKeys:["DASHSCOPE_API_KEY"]}],degrade:{kind:"missing-key"},next:"set one of OPENAI_API_KEY / ANTHROPIC_API_KEY / DEEPSEEK_API_KEY / DASHSCOPE_API_KEY"},mcp:{providers:[{id:"built-in",envKeys:[]}],degrade:{kind:"missing-key"},next:"always live \u2014 no configuration required"},monitoring:{providers:[{id:"sentry",envKeys:["SENTRY_DSN"]}],degrade:{kind:"missing-key"},next:"set SENTRY_DSN to enable error monitoring"},analytics:{providers:[{id:"posthog",envKeys:["NEXT_PUBLIC_POSTHOG_KEY"]}],degrade:{kind:"missing-key"},next:"set NEXT_PUBLIC_POSTHOG_KEY to enable analytics"},captcha:{providers:[{id:"turnstile",envKeys:["TURNSTILE_SECRET_KEY"]}],degrade:{kind:"missing-key"},next:"set TURNSTILE_SECRET_KEY to enable captcha"}};function St(e){let t=e,{root:n}=lc(t);for(;;){if(ro(io(t,"nebutra.config.json")))return t;if(t===n)return null;let r=cc(t);if(r===t)return null;t=r}}function Rt(e){let t=oo(e,"utf-8"),n=JSON.parse(t);if(!n||typeof n!="object"||Array.isArray(n))throw new E({code:"invalid_manifest",message:`nebutra.config.json at ${e} is not a JSON object.`,suggestion:"Re-run `create-sailor` or fix the file by hand.",exitCode:l.CONFIG_ERROR});return n}function dc(e){let t={};for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;let o=r.startsWith("export ")?r.slice(7):r,i=o.indexOf("=");if(i===-1)continue;let a=o.slice(0,i).trim();if(!a)continue;let s=o.slice(i+1).trim();(s.startsWith('"')&&s.endsWith('"')&&s.length>=2||s.startsWith("'")&&s.endsWith("'")&&s.length>=2)&&(s=s.slice(1,-1)),t[a]=s}return t}function ao(e){let t={...process.env};for(let n of[".env",".env.local"]){let r=io(e,n);if(ro(r))try{let o=dc(oo(r,"utf-8"));for(let[i,a]of Object.entries(o))process.env[i]===void 0&&(t[i]=a)}catch{}}return t}function so(e,t,n){let r=[],o=[];for(let i of t.providers){let a=i.envKeys.filter(s=>!n[s]);a.length===0?r.push(i.id):o.length===0&&i.envKeys.length>0&&(o=a)}return r.length>0?{name:e,state:"live",provider:r,missing:[],next:t.next}:t.degrade.kind==="fallback"?{name:e,state:"local-fallback",provider:[t.degrade.provider],missing:o,next:t.next}:{name:e,state:"missing-key",provider:[],missing:o,next:t.next}}function co(e){let t=Object.keys(Te),n=e.filter(a=>!Te[a]);if(n.length>0)throw new E({code:"unknown_capability",message:`Unknown capabilit${n.length===1?"y":"ies"} in nebutra.config.json: ${n.join(", ")}.`,suggestion:`Valid capabilities: ${t.join(", ")}.`,exitCode:l.CONFIG_ERROR,context:{unknown:n,valid:t}});let r=new Set,o=[],i=[];for(let a of e){if(r.has(a)){o.push(`duplicate capability "${a}" in nebutra.config.json \u2014 ignoring repeat`);continue}r.add(a),i.push(a)}return{capabilities:i,warnings:o}}function mc(e,t){switch(e){case"live":return xe.green(t);case"local-fallback":return xe.yellow(t);case"missing-key":return xe.red(t);default:return t}}async function pc(e){let t=St(process.cwd());if(!t)throw new E({code:"manifest_not_found",message:"No nebutra.config.json found in this directory or any parent directory.",suggestion:"Run `npx create-sailor` to scaffold a Sailor project first.",exitCode:l.CONFIG_ERROR});let n=uc(t,"nebutra.config.json"),r=Rt(n),o=Array.isArray(r.capabilities)?r.capabilities:[],i=ao(t),a=i.NEBUTRA_LOCALE||"global",s=typeof r.stack=="string"?r.stack:"unknown",c=o.map(f=>{let w=Te[f];return w?so(f,w,i):{name:f,state:"missing-key",provider:[],missing:[],next:"unknown capability \u2014 no readiness rule registered in `nebutra status`"}});if(e.json===!0||e.format==="json"){b({stack:s,locale:a,capabilities:c},{format:"json"});return}let p=[];p.push(xe.bold("nebutra status")),p.push(xe.dim(`stack: ${s} locale: ${a} project: ${t}`)),p.push("");let g=Math.max(10,...c.map(f=>f.name.length)),h=Math.max(14,...c.map(f=>f.state.length));for(let f of c){let w=f.name.padEnd(g),D=mc(f.state,f.state.padEnd(h)),k=f.provider.length>0?f.provider.join(", "):"-";p.push(`${w} ${D} ${xe.dim(k)}`),f.state!=="live"&&p.push(`${" ".repeat(g)} ${xe.dim(`\u2192 ${f.next}`)}`)}console.log(p.join(`
526
+ `))}function lo(e){e.command("status").description("Show capability readiness \u2014 live, local-fallback, or missing-key, per env").option("--json","Emit machine-readable JSON").action((t,n)=>z(async()=>{let r=n.optsWithGlobals?n.optsWithGlobals():{};await pc({json:t.json,format:r.format})}))}import{spawn as fc}from"child_process";import{existsSync as gc,readFileSync as hc}from"fs";function uo(e){let t=e.trim(),n=!t.startsWith("{")&&!/^https?:/.test(t)&&gc(t)?hc(t,"utf8"):t;return Xn(n)}function yc(e){let t=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";fc(t,[e],{stdio:"ignore",detached:!0,shell:process.platform==="win32"}).on("error",()=>d.warn(`Could not open a browser. Open ${e} yourself.`)).unref()}function mo(e){d.error(e instanceof O?e.message:String(e)),process.exit(l.INVALID_ARGS)}var bc=()=>(process.env.NEBUTRA_API_URL??Z("api")).replace(/\/+$/,"");async function vc(){let e=await ce();return e&&!e.expired?e.token:null}async function wc(e,t,n={}){return fetch(`${bc()}/api/v1/studio${e}`,{...n,headers:{authorization:`Bearer ${t}`,"content-type":"application/json",...n.headers},signal:AbortSignal.timeout(1e4)})}async function kc(){let e=await vc();if(!e)return null;let t=await wc("/presets",e);if(!t.ok)throw new Error(`The gateway answered ${t.status}.`);return(await t.json()).presets}function xc(e){let t=ot(e);return{code:t,preset:e,reviewUrl:Qn(e),apply:`nebutra studio pull ${t}`,create:`npx create-sailor@latest my-app --preset ${t}`}}function po(e){let t=e.command("studio").description("Sailor Studio for agents: write a preset, review it in the browser, pull it");t.command("schema").description("Print the preset JSON Schema (what a preset may contain)").action(()=>{console.log(JSON.stringify(Zn(),null,2))}),t.command("preview <preset>").description("Validate a preset and print the Sailor Studio link to review it").option("--open","Open the link in the browser").option("--json","Machine-readable output").action((n,r)=>{let o;try{o=xc(uo(n))}catch(i){mo(i)}if(r.open&&yc(o.reviewUrl),r.json){console.log(JSON.stringify(o,null,2));return}d.success(`Preset ${o.code} is valid.`),console.log(`
527
+ Review it: ${o.reviewUrl}`),console.log(` Apply here: ${o.apply}`),console.log(` New project: ${o.create}
528
+ `)}),t.command("list").description("The looks you saved in Studio (nebutra login), newest first").option("--json","Machine-readable output").action(async n=>{let r;try{r=await kc()}catch(o){d.error(String(o instanceof Error?o.message:o)),process.exit(l.ERROR)}if(r===null&&(d.error("Not logged in. Run `nebutra login` to sync presets with Studio."),process.exit(l.PERMISSION_DENIED)),n.json){console.log(JSON.stringify({presets:r},null,2));return}if(r.length===0){console.log("No saved looks yet. Save one in Sailor Studio.");return}for(let o of r)console.log(` ${o.code.padEnd(12)} ${o.name}`)}),t.command("pull [preset]").description("Put a preset (code, JSON, file or Studio link) on this project; --latest takes your newest saved one").option("--latest","Use the newest preset saved to your account").option("--only <group>","Change only one group of knobs: theme | fonts").option("--no-build","Write the preset without rebuilding the tokens").option("--json","Machine-readable output").action(async(n,r)=>{let o;try{o=ot(uo(n))}catch(a){mo(a)}let i=["apply","--preset",o];r.only&&i.push("--only",r.only),r.build===!1&&i.push("--no-build"),r.json&&i.push("--format","json"),await e.parseAsync(i,{from:"user"})})}import{existsSync as fo,readFileSync as Cc,writeFileSync as go}from"fs";import{join as rn}from"path";import Ye from"picocolors";var ho=".env.example",yo=".env.local",Sc=["# Local secrets for this machine \u2014 never commit real values.","# `nebutra sync` created this file; add your own keys below.",""].join(`
529
+ `);function Rc(e){let t=new Set;for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;let o=r.startsWith("export ")?r.slice(7):r,i=o.indexOf("=");if(i===-1)continue;let a=o.slice(0,i).trim();a&&t.add(a)}return t}function Ec(e,t){let n=Rc(e),r=[],o=[],i=[];for(let m of t){let p=Te[m];if(p)for(let g of p.providers){if(g.envKeys.length===0)continue;let h=[];for(let f of g.envKeys)n.has(f)?o.push(f):h.push(f);if(h.length!==0){i.push([`# ${m} (${g.id})`,...h.map(f=>`${f}=`)].join(`
530
+ `));for(let f of h)n.add(f),r.push(f)}}}if(i.length===0)return{content:e,added:r,unchanged:o};let a=e.replace(/\s+$/,"");return{content:`${a.length>0?`${a}
524
531
 
525
532
  `:""}${i.join(`
526
533
 
527
534
  `)}
528
- `,added:r,unchanged:o}}async function Gs(e){let t=vt(process.cwd());if(!t)throw new K({code:"manifest_not_found",message:"No nebutra.config.json found in this directory or any parent directory.",suggestion:"Run `npx create-sailor` to scaffold a Sailor project first.",exitCode:l.CONFIG_ERROR});let n=qt(t,"nebutra.config.json"),r=kt(n),o=Array.isArray(r.capabilities)?r.capabilities:[],{capabilities:i,warnings:a}=Fr(o),s=qt(t,Ur),u=Tr(s),m=u?Ls(s,"utf-8"):"",p=Ms(m,i),f=qt(t,Br),h=Tr(f),g={};p.added.length>0&&(g[Ur]=p.added),h||(g[Br]=[]);let k={added:g,unchanged:p.unchanged,warnings:a},F=e.json===!0||e.format==="json";if(e.dryRun){I({mode:"dry-run",command:"sync",projectRoot:t,...k},{format:F?"json":e.format});for(let w of a)d(w,"warn");process.exit(l.DRY_RUN_OK)}if((p.added.length>0||!u)&&Lr(s,p.content),h||Lr(f,Us),F)b(k,{format:"json"});else{let w=[];w.push(ze.bold("nebutra sync")),w.push(ze.dim(`project: ${t}`)),w.push("");let W=Object.keys(g);if(W.length===0)w.push(ze.dim("Env files already match declared capabilities. Nothing to do."));else for(let X of W){let L=g[X]??[];L.length>0?w.push(`${ze.green("+")} ${X}: added ${L.join(", ")}`):w.push(`${ze.green("+")} ${X}: created`)}console.log(w.join(`
529
- `))}for(let w of a)d(w,"warn");d("Run `nebutra status` to check capability readiness.")}function Mr(e){e.command("sync").description("Make .env.example / .env.local agree with the declared capabilities (idempotent)").option("--dry-run","Preview planned additions without writing files (exits with code 10)").option("--json","Emit machine-readable JSON").action((t,n)=>we(async()=>{let r=n.optsWithGlobals?n.optsWithGlobals():{};await Gs({dryRun:t.dryRun,json:t.json,format:r.format})}))}import*as $ from"@clack/prompts";import O from"picocolors";function de(e){if(e==="json"||e==="plain"||e==="table")return e}var Ht=["landing","web","storybook","backends/gateway","design-docs","studio","docs","ui","tokens","brand","theme","icons","preset","queue","search","notifications","permissions","webhooks","metering","uploads","vault","tenant"];function Vs(e){let t=`@nebutra/${e}`;if(!Ht.includes(e)){let n=O.yellow(`Valid apps: ${Ht.join(", ")}`);throw new Error(`Invalid app: ${O.red(e)}
530
- ${n}`)}return t}async function zs(e){e.dryRun&&(I({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:de(e.format)}),process.exit(l.DRY_RUN_OK));try{if(e.app&&(e.watch||e.coverage)&&($.log.error(O.red("Cannot use --watch or --coverage with --app filter (app-level tests use turbo)")),process.exit(l.INVALID_ARGS)),e.app){Vs(e.app);let r=`@nebutra/${e.app}`;e.quiet||$.log.info(O.cyan(`Running tests for ${O.bold(r)}...`));let o=await ae("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...";$.log.info(O.cyan(r))}let n=await j(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?$.log.error(O.red(t.message)):$.log.error(O.red("Unknown error occurred")),process.exit(l.ERROR)}}async function Gr(e){e.dryRun&&(I({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:de(e.format)}),process.exit(l.DRY_RUN_OK));try{e.ui&&e.ci&&($.log.error(O.red("Cannot use --ui and --ci together")),process.exit(l.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...";$.log.info(O.cyan(r))}let n=await j(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?$.log.error(O.red(t.message)):$.log.error(O.red("Unknown error occurred")),process.exit(l.ERROR)}}async function Vr(e){e.dryRun&&(I({mode:"dry-run",timestamp:new Date().toISOString(),command:"test size",options:{why:e.why},task:e.why?"pnpm size:why":"pnpm size"},{format:de(e.format)}),process.exit(l.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...";$.log.info(O.cyan(r))}let n=await j(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?$.log.error(O.red(t.message)):$.log.error(O.red("Unknown error occurred")),process.exit(l.ERROR)}}async function zr(e){e.dryRun&&(I({mode:"dry-run",timestamp:new Date().toISOString(),command:"test arch",task:"pnpm test:arch"},{format:de(e.format)}),process.exit(l.DRY_RUN_OK));try{e.quiet||$.log.info(O.cyan("Running architecture tests..."));let t=await j("test:arch",{interactive:!0});process.exit(t.exitCode)}catch(t){t instanceof Error?$.log.error(O.red(t.message)):$.log.error(O.red("Unknown error occurred")),process.exit(l.ERROR)}}var Kr=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: ${Ht.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async(n,r,o)=>{let i=A(r,o),a={dryRun:!!r.dryRun,quiet:!!i.quiet,verbose:!!i.verbose,format:de(i.format)};n==="e2e"?await Gr({...a,ui:!!r.ui,ci:!!r.ci}):n==="size"?await Vr({...a,why:!!r.why}):n==="arch"?await zr(a):await zs({...a,watch:!!r.watch,coverage:!!r.coverage,app:typeof r.app=="string"?r.app:void 0})});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,r)=>{let o=A(n,r);await Gr({ui:!!n.ui,ci:!!n.ci,dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:de(o.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,r)=>{let o=A(n,r);await Vr({why:!!n.why,dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:de(o.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,r)=>{let o=A(n,r);await zr({dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:de(o.format)})})};var Ks={$schema:"https://nebutra.com/schemas/design-language-registry.json",version:"2026.07",defaultLanguage:"factory",description:"Design-language catalog for global product chrome swap (Create Center / SaaS theme). Not color moods \u2014 full Brand Packages (roles + recipe + elev + zones).",languages:[{id:"factory",name:"Nebutra Factory",kind:"design-language",description:"Default product chrome from @nebutra/tokens (no skin). Clear data-brand to restore.",tagline:"the token source, unskinned",shortName:"Factory",proves:["factory HSL SSOT","recipe defaults","action\u2261brand-mark until skin"],darkDefault:!0,brandPath:null,skinPath:null,install:{command:"nebutra apply --preset factory",cssImport:null},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"cosmos",name:"Cosmos",kind:"design-language",description:"Linen gallery canvas with a monochrome chrome: the filled Ink button and the brand mark are the same colour, there is no accent role at all. One 16px radius everywhere, zero elevation, serif display at weight 350.",tagline:"images sit; chrome recedes",shortName:"Cosmos",proves:["monochrome chrome (action \u2261 brand-mark, no accent role)","single-radius system (16px everywhere; 9999px only for the floating nav)","elevation=none by contract (flat surfaces, contrast-only depth)","serif display below weight 400 (headingWeight 350)","light-first dual mode (dark inverts the gallery)"],darkDefault:!1,brandPath:"brands/cosmos/brand.json",skinPath:"skins/cosmos.css",install:{command:"nebutra apply --preset cosmos",cssImport:"@nebutra/tokens/skins/cosmos.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"gsap",name:"GSAP",kind:"design-language",description:"Dark cream-on-void; shockingly-green is accent \u2014 CTA is gradient-stroke/outline, not solid green fill.",tagline:"saturated, fast easing",shortName:"GSAP",proves:["outline / gradient-stroke recipe","category extensions","marketing display zone"],darkDefault:!0,brandPath:"brands/gsap/brand.json",skinPath:"skins/gsap.css",install:{command:"nebutra apply --preset gsap",cssImport:"@nebutra/tokens/skins/gsap.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"linear",name:"Linear",kind:"design-language",description:"Dual-mode: dark void default + light paper; solid acid-lime CTA, compact density.",tagline:"tight radii, low-contrast chrome",shortName:"Linear",proves:["chromatic solid action","dark product chrome","dual-mode light/dark palettes"],darkDefault:!0,brandPath:"brands/linear/brand.json",skinPath:"skins/linear.css",install:{command:"nebutra apply --preset linear",cssImport:"@nebutra/tokens/skins/linear.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"notion",name:"Notion",kind:"design-language",description:"Warm paper notebook; Notion Blue sole filled CTA \u2260 Ink brand-mark; 8px buttons / 12px cards; elev=none + hairline; accent hues decorative only.",tagline:"paper surfaces, muted ink",shortName:"Notion",proves:["action \u2260 brand-mark (blue vs ink)","canvas\u2260card (paper-warmth vs pure-white)","elevation=none content cards + raised nav shadow slot","table radius priority (8/12 over tertiary 4px)","decorative accents never product CTA"],darkDefault:!1,brandPath:"brands/notion/brand.json",skinPath:"skins/notion.css",install:{command:"nebutra apply --preset notion",cssImport:"@nebutra/tokens/skins/notion.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"raycast",name:"Raycast",kind:"design-language",description:"Neutral Mist/Iron filled CTA; Coral is brand-mark only. Key elevation stack.",tagline:"dark glass and a coloured halo",shortName:"Raycast",proves:["action \u2260 brand-mark","elevation=key free CSS"],darkDefault:!0,brandPath:"brands/raycast/brand.json",skinPath:"skins/raycast.css",install:{command:"nebutra apply --preset raycast",cssImport:"@nebutra/tokens/skins/raycast.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"stripe",name:"Stripe",kind:"design-language",description:"Indigo-ink ledger on frost glass; Indigo action \u2260 Midnight brand-mark; 4px controls; elevation=none (tint + frost rules).",tagline:"soft elevation, generous type",shortName:"Stripe",proves:["action \u2260 brand-mark (chromatic indigo vs midnight)","elevation=none tint-ladder depth","4px squared controls (not pill)","whisper heading weight 300"],darkDefault:!1,brandPath:"brands/stripe/brand.json",skinPath:"skins/stripe.css",install:{command:"nebutra apply --preset stripe",cssImport:"@nebutra/tokens/skins/stripe.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"vanta",name:"Vanta",kind:"design-language",description:"Editorial violet on parchment; Vivid Violet action \u2260 Indigo Ink brand-mark; full pills; elevation=none (carbon hairlines).",tagline:"high-trust enterprise blues",shortName:"Vanta",proves:["action \u2260 brand-mark (chromatic)","elevation=none border-only","999px pill controls","serif display + sans UI zones"],darkDefault:!1,brandPath:"brands/vanta/brand.json",skinPath:"skins/vanta.css",install:{command:"nebutra apply --preset vanta",cssImport:"@nebutra/tokens/skins/vanta.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"vercel",name:"Vercel",kind:"design-language",description:"Monochrome dual-mode: light paper + Obsidian CTA; dark carbon + white CTA; hairline elevation (no chromatic CTA).",tagline:"monochrome, hard edges, lit top edge",shortName:"Vercel",proves:["light monochrome action","elevation=hairline","dual-mode light/dark palettes"],darkDefault:!1,brandPath:"brands/vercel/brand.json",skinPath:"skins/vercel.css",install:{command:"nebutra apply --preset vercel",cssImport:"@nebutra/tokens/skins/vercel.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}}]},me=Ks,Ws=me.languages.map(e=>e.id),Zt=me.defaultLanguage;function Qt(e){return me.languages.find(t=>t.id===e)}function Wr(e){let t=e.opts?e.opts():e,n=e.optsWithGlobals?e.optsWithGlobals():e;return t.format??n.format??Ys()}function Ys(){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 Js(e="table"){let t=me.languages;return e==="json"?JSON.stringify({version:me.version,defaultLanguage:me.defaultLanguage,count:t.length,languages:t},null,2):[`Nebutra design languages (${t.length}) \u2014 global product chrome swap`,`Default: ${Zt} (factory tokens; no skin)`,"Contract: roles.action \u2192 CTA; roles.brand \u2192 brand-mark; free elev/radii/zones","",...t.map(r=>`${r.id.padEnd(10)} ${r.darkDefault?"dark ":"light"} ${(r.proves[0]??"").slice(0,48)} ${r.description.slice(0,56)}`)].join(`
531
- `)}function qs(e,t="table"){let n=Qt(e);if(n)return t==="json"?JSON.stringify(n,null,2):[`${n.name} (${n.id}) \u2014 design language`,`Description: ${n.description}`,`Dark default: ${n.darkDefault}`,`Brand JSON: ${n.brandPath??"(factory \u2014 none)"}`,`Skin CSS: ${n.skinPath??"(factory \u2014 none)"}`,`Install: ${n.install.command}`,`CSS import: ${n.install.cssImport??"(clear data-brand)"}`,`Proves: ${n.proves.join("; ")}`].join(`
532
- `)}function Yr(e){let t=e.command("theme").description("Design languages (Brand Package global product chrome swap)");t.command("list").description("List design languages").option("--format <type>","Output format: json or table").action(n=>{console.log(Js(Wr(n)))}),t.command("inspect <id>").description("Show a design language by id").option("--format <type>","Output format: json or table").action((n,r)=>{let o=qs(n,Wr(r));o||(c.error(`Design language '${n}' not found. Run: nebutra theme list`),process.exit(l.NOT_FOUND)),console.log(o)})}import{existsSync as Hr,readFileSync as Zr}from"fs";import{resolve as Jr,join as Xt}from"path";function Qr(e={}){let t=e.manifestPath?Jr(e.manifestPath):Xt(Jr(e.root??process.cwd()),"apps","design-docs","public","agent-manifest.json");return{manifestPath:t,componentDir:Xt(t,"..","agent","components")}}function Hs(e){return e.trim().toLowerCase()}function Zs(e){let t=Hs(e);if(!/^[a-z0-9-]+$/.test(t))throw new Error(`Invalid UI component name "${e}". Use a registry kebab-case id such as "button".`);return t}function Xr(e={}){let{manifestPath:t}=Qr(e);if(!Hr(t))throw new Error(`Missing Nebutra UI agent manifest at ${t}. Run pnpm --filter @nebutra/design-docs build:registry.`);return JSON.parse(Zr(t,"utf8"))}function en(e,t={}){let n=Zs(e),{componentDir:r}=Qr(t),o=Xt(r,`${n}.json`);if(!Hr(o))throw new Error(`UI component contract not found: ${n}`);return JSON.parse(Zr(o,"utf8"))}function eo(e,t="",n={}){let r=t.trim().toLowerCase(),o=qr(n.limit??20,1,100),i=qr(n.offset??0,0,Number.MAX_SAFE_INTEGER),a=n.tag?.trim().toLowerCase(),s=e.components.filter(p=>n.status&&p.status!==n.status||n.maturity&&p.maturity!==n.maturity||a&&!p.tags.includes(a)?!1:r?Qs(p).includes(r):!0).map(p=>({component:p,score:Xs(p,r)})).sort((p,f)=>f.score-p.score||p.component.name.localeCompare(f.component.name)).map(({component:p})=>p),u=s.slice(i,i+o),m=i+u.length;return{query:t,total:s.length,count:u.length,offset:i,limit:o,hasMore:m<s.length,...m<s.length&&{nextOffset:m},items:u}}function tn(e){let t=[],n=[];return e.evidence.source||t.push("Missing source evidence."),e.evidence.docs||t.push("Missing docs evidence."),e.evidence.registry||t.push("Missing registry evidence."),(e.status==="stable"||e.maturity==="canonical")&&!e.evidence.storybook&&t.push("Stable/canonical components must have Storybook evidence."),!e.evidence.tokens&&e.package==="@nebutra/ui"&&n.push("No token evidence was detected; confirm this primitive intentionally has no CSS variables."),e.migration.requiredForBreakingChanges&&e.migration.hints.length===0&&n.push("Breaking-change migration policy is enabled but no migration hints are present."),{name:e.name,valid:t.length===0,errors:t,warnings:n,evidence:e.evidence,migration:e.migration}}function Qs(e){return[e.name,e.title,e.description,e.status,e.maturity,e.layer,e.source,...e.tags].join(" ").toLowerCase()}function Xs(e,t){return t?e.name===t?100:e.name.startsWith(t)?80:e.title.toLowerCase().startsWith(t)?70:e.tags.includes(t)?60:e.name.includes(t)?50:e.title.toLowerCase().includes(t)?40:e.description.toLowerCase().includes(t)?20:1:e.maturity==="canonical"?20:10}function qr(e,t,n){return Number.isFinite(e)?Math.min(Math.max(Math.trunc(e),t),n):t}function wt(e){let t=e.opts?e.opts():e,n=e.optsWithGlobals?e.optsWithGlobals():e;return t.format??n.format??ec()}function ec(){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 rn(e){let t=e.opts?e.opts():e;return{root:y(),manifestPath:t.manifest}}function to(e,t){if(!e)return t;let n=Number(e);return Number.isFinite(n)?n:t}function tc(e,t="json"){if(t==="plain"||t==="table"){let n=[`Nebutra UI components (${e.total})`,""];for(let r of e.items)n.push(`${r.name.padEnd(22)} ${r.status.padEnd(12)} ${r.maturity.padEnd(10)} ${r.title}`);return e.hasMore&&n.push("",`Next offset: ${e.nextOffset}`),n.join(`
533
- `)}return JSON.stringify(e,null,2)}function nc(e,t="json"){return t==="plain"||t==="table"?[`${e.title} (${e.name})`,`Status: ${e.status}`,`Maturity: ${e.maturity}`,`Source: ${e.source}`,`Package import: ${e.imports.package}`,`Registry: ${e.imports.registry}`,`Docs: ${e.docs.routes.join(", ")||"missing"}`,`Storybook: ${e.docs.storybook??"missing"}`,`Tokens: ${e.tokens.join(", ")||"none detected"}`].join(`
534
- `):JSON.stringify(e,null,2)}function rc(e,t="json"){if(t==="plain"||t==="table"){if("results"in e){let n=[`Nebutra UI validation: ${e.valid?"valid":"invalid"}`,`Total: ${e.total}`,`Invalid: ${e.invalid}`];for(let r of e.results.filter(o=>!o.valid))n.push(`- ${r.name}: ${r.errors.join("; ")}`);return n.join(`
535
+ `,added:r,unchanged:o}}async function Oc(e){let t=St(process.cwd());if(!t)throw new E({code:"manifest_not_found",message:"No nebutra.config.json found in this directory or any parent directory.",suggestion:"Run `npx create-sailor` to scaffold a Sailor project first.",exitCode:l.CONFIG_ERROR});let n=rn(t,"nebutra.config.json"),r=Rt(n),o=Array.isArray(r.capabilities)?r.capabilities:[],{capabilities:i,warnings:a}=co(o),s=rn(t,ho),c=fo(s),m=c?Cc(s,"utf-8"):"",p=Ec(m,i),g=rn(t,yo),h=fo(g),f={};p.added.length>0&&(f[ho]=p.added),h||(f[yo]=[]);let w={added:f,unchanged:p.unchanged,warnings:a},D=e.json===!0||e.format==="json";if(e.dryRun){N({mode:"dry-run",command:"sync",projectRoot:t,...w},{format:D?"json":e.format});for(let k of a)u(k,"warn");process.exit(l.DRY_RUN_OK)}if((p.added.length>0||!c)&&go(s,p.content),h||go(g,Sc),D)b(w,{format:"json"});else{let k=[];k.push(Ye.bold("nebutra sync")),k.push(Ye.dim(`project: ${t}`)),k.push("");let J=Object.keys(f);if(J.length===0)k.push(Ye.dim("Env files already match declared capabilities. Nothing to do."));else for(let te of J){let B=f[te]??[];B.length>0?k.push(`${Ye.green("+")} ${te}: added ${B.join(", ")}`):k.push(`${Ye.green("+")} ${te}: created`)}console.log(k.join(`
536
+ `))}for(let k of a)u(k,"warn");u("Run `nebutra status` to check capability readiness.")}function bo(e){e.command("sync").description("Make .env.example / .env.local agree with the declared capabilities (idempotent)").option("--dry-run","Preview planned additions without writing files (exits with code 10)").option("--json","Emit machine-readable JSON").action((t,n)=>z(async()=>{let r=n.optsWithGlobals?n.optsWithGlobals():{};await Oc({dryRun:t.dryRun,json:t.json,format:r.format})}))}import*as T from"@clack/prompts";import _ from"picocolors";function me(e){if(e==="json"||e==="plain"||e==="table")return e}var on=["landing","web","storybook","backends/gateway","design-docs","studio","docs","ui","tokens","brand","theme","icons","preset","queue","search","notifications","permissions","webhooks","metering","uploads","vault","tenant"];function Ac(e){let t=`@nebutra/${e}`;if(!on.includes(e)){let n=_.yellow(`Valid apps: ${on.join(", ")}`);throw new Error(`Invalid app: ${_.red(e)}
537
+ ${n}`)}return t}async function _c(e){e.dryRun&&(N({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:me(e.format)}),process.exit(l.DRY_RUN_OK));try{if(e.app&&(e.watch||e.coverage)&&(T.log.error(_.red("Cannot use --watch or --coverage with --app filter (app-level tests use turbo)")),process.exit(l.INVALID_ARGS)),e.app){Ac(e.app);let r=`@nebutra/${e.app}`;e.quiet||T.log.info(_.cyan(`Running tests for ${_.bold(r)}...`));let o=await se("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...";T.log.info(_.cyan(r))}let n=await F(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?T.log.error(_.red(t.message)):T.log.error(_.red("Unknown error occurred")),process.exit(l.ERROR)}}async function vo(e){e.dryRun&&(N({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:me(e.format)}),process.exit(l.DRY_RUN_OK));try{e.ui&&e.ci&&(T.log.error(_.red("Cannot use --ui and --ci together")),process.exit(l.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...";T.log.info(_.cyan(r))}let n=await F(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?T.log.error(_.red(t.message)):T.log.error(_.red("Unknown error occurred")),process.exit(l.ERROR)}}async function wo(e){e.dryRun&&(N({mode:"dry-run",timestamp:new Date().toISOString(),command:"test size",options:{why:e.why},task:e.why?"pnpm size:why":"pnpm size"},{format:me(e.format)}),process.exit(l.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...";T.log.info(_.cyan(r))}let n=await F(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?T.log.error(_.red(t.message)):T.log.error(_.red("Unknown error occurred")),process.exit(l.ERROR)}}async function ko(e){e.dryRun&&(N({mode:"dry-run",timestamp:new Date().toISOString(),command:"test arch",task:"pnpm test:arch"},{format:me(e.format)}),process.exit(l.DRY_RUN_OK));try{e.quiet||T.log.info(_.cyan("Running architecture tests..."));let t=await F("test:arch",{interactive:!0});process.exit(t.exitCode)}catch(t){t instanceof Error?T.log.error(_.red(t.message)):T.log.error(_.red("Unknown error occurred")),process.exit(l.ERROR)}}var xo=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: ${on.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async(n,r,o)=>{let i=I(r,o),a={dryRun:!!r.dryRun,quiet:!!i.quiet,verbose:!!i.verbose,format:me(i.format)};n==="e2e"?await vo({...a,ui:!!r.ui,ci:!!r.ci}):n==="size"?await wo({...a,why:!!r.why}):n==="arch"?await ko(a):await _c({...a,watch:!!r.watch,coverage:!!r.coverage,app:typeof r.app=="string"?r.app:void 0})});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,r)=>{let o=I(n,r);await vo({ui:!!n.ui,ci:!!n.ci,dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:me(o.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,r)=>{let o=I(n,r);await wo({why:!!n.why,dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:me(o.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,r)=>{let o=I(n,r);await ko({dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:me(o.format)})})};var Ic={$schema:"https://nebutra.com/schemas/design-language-registry.json",version:"2026.07",defaultLanguage:"factory",description:"Design-language catalog for global product chrome swap (Create Center / SaaS theme). Not color moods \u2014 full Brand Packages (roles + recipe + elev + zones).",languages:[{id:"factory",name:"Nebutra Factory",kind:"design-language",description:"Default product chrome from @nebutra/tokens (no skin). Clear data-brand to restore.",tagline:"the token source, unskinned",shortName:"Factory",proves:["factory HSL SSOT","recipe defaults","action\u2261brand-mark until skin"],darkDefault:!0,brandPath:null,skinPath:null,install:{command:"nebutra apply --preset factory",cssImport:null},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"cosmos",name:"Cosmos",kind:"design-language",description:"Linen gallery canvas with a monochrome chrome: the filled Ink button and the brand mark are the same colour, there is no accent role at all. One 16px radius everywhere, zero elevation, serif display at weight 350.",tagline:"images sit; chrome recedes",shortName:"Cosmos",proves:["monochrome chrome (action \u2261 brand-mark, no accent role)","single-radius system (16px everywhere; 9999px only for the floating nav)","elevation=none by contract (flat surfaces, contrast-only depth)","serif display below weight 400 (headingWeight 350)","light-first dual mode (dark inverts the gallery)"],darkDefault:!1,brandPath:"brands/cosmos/brand.json",skinPath:"skins/cosmos.css",install:{command:"nebutra apply --preset cosmos",cssImport:"@nebutra/tokens/skins/cosmos.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"gsap",name:"GSAP",kind:"design-language",description:"Dark cream-on-void; shockingly-green is accent \u2014 CTA is gradient-stroke/outline, not solid green fill.",tagline:"saturated, fast easing",shortName:"GSAP",proves:["outline / gradient-stroke recipe","category extensions","marketing display zone"],darkDefault:!0,brandPath:"brands/gsap/brand.json",skinPath:"skins/gsap.css",install:{command:"nebutra apply --preset gsap",cssImport:"@nebutra/tokens/skins/gsap.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"linear",name:"Linear",kind:"design-language",description:"Dual-mode: dark void default + light paper; solid acid-lime CTA, compact density.",tagline:"tight radii, low-contrast chrome",shortName:"Linear",proves:["chromatic solid action","dark product chrome","dual-mode light/dark palettes"],darkDefault:!0,brandPath:"brands/linear/brand.json",skinPath:"skins/linear.css",install:{command:"nebutra apply --preset linear",cssImport:"@nebutra/tokens/skins/linear.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"notion",name:"Notion",kind:"design-language",description:"Warm paper notebook; Notion Blue sole filled CTA \u2260 Ink brand-mark; 8px buttons / 12px cards; elev=none + hairline; accent hues decorative only.",tagline:"paper surfaces, muted ink",shortName:"Notion",proves:["action \u2260 brand-mark (blue vs ink)","canvas\u2260card (paper-warmth vs pure-white)","elevation=none content cards + raised nav shadow slot","table radius priority (8/12 over tertiary 4px)","decorative accents never product CTA"],darkDefault:!1,brandPath:"brands/notion/brand.json",skinPath:"skins/notion.css",install:{command:"nebutra apply --preset notion",cssImport:"@nebutra/tokens/skins/notion.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"raycast",name:"Raycast",kind:"design-language",description:"Neutral Mist/Iron filled CTA; Coral is brand-mark only. Key elevation stack.",tagline:"dark glass and a coloured halo",shortName:"Raycast",proves:["action \u2260 brand-mark","elevation=key free CSS"],darkDefault:!0,brandPath:"brands/raycast/brand.json",skinPath:"skins/raycast.css",install:{command:"nebutra apply --preset raycast",cssImport:"@nebutra/tokens/skins/raycast.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"stripe",name:"Stripe",kind:"design-language",description:"Indigo-ink ledger on frost glass; Indigo action \u2260 Midnight brand-mark; 4px controls; elevation=none (tint + frost rules).",tagline:"soft elevation, generous type",shortName:"Stripe",proves:["action \u2260 brand-mark (chromatic indigo vs midnight)","elevation=none tint-ladder depth","4px squared controls (not pill)","whisper heading weight 300"],darkDefault:!1,brandPath:"brands/stripe/brand.json",skinPath:"skins/stripe.css",install:{command:"nebutra apply --preset stripe",cssImport:"@nebutra/tokens/skins/stripe.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"vanta",name:"Vanta",kind:"design-language",description:"Editorial violet on parchment; Vivid Violet action \u2260 Indigo Ink brand-mark; full pills; elevation=none (carbon hairlines).",tagline:"high-trust enterprise blues",shortName:"Vanta",proves:["action \u2260 brand-mark (chromatic)","elevation=none border-only","999px pill controls","serif display + sans UI zones"],darkDefault:!1,brandPath:"brands/vanta/brand.json",skinPath:"skins/vanta.css",install:{command:"nebutra apply --preset vanta",cssImport:"@nebutra/tokens/skins/vanta.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}},{id:"vercel",name:"Vercel",kind:"design-language",description:"Monochrome dual-mode: light paper + Obsidian CTA; dark carbon + white CTA; hairline elevation (no chromatic CTA).",tagline:"monochrome, hard edges, lit top edge",shortName:"Vercel",proves:["light monochrome action","elevation=hairline","dual-mode light/dark palettes"],darkDefault:!1,brandPath:"brands/vercel/brand.json",skinPath:"skins/vercel.css",install:{command:"nebutra apply --preset vercel",cssImport:"@nebutra/tokens/skins/vercel.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}}]},pe=Ic,Pc=pe.languages.map(e=>e.id),an=pe.defaultLanguage;function sn(e){return pe.languages.find(t=>t.id===e)}function Co(e){let t=e.opts?e.opts():e,n=e.optsWithGlobals?e.optsWithGlobals():e;return t.format??n.format??Nc()}function Nc(){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 $c(e="table"){let t=pe.languages;return e==="json"?JSON.stringify({version:pe.version,defaultLanguage:pe.defaultLanguage,count:t.length,languages:t},null,2):[`Nebutra design languages (${t.length}) \u2014 global product chrome swap`,`Default: ${an} (factory tokens; no skin)`,"Contract: roles.action \u2192 CTA; roles.brand \u2192 brand-mark; free elev/radii/zones","",...t.map(r=>`${r.id.padEnd(10)} ${r.darkDefault?"dark ":"light"} ${(r.proves[0]??"").slice(0,48)} ${r.description.slice(0,56)}`)].join(`
538
+ `)}function jc(e,t="table"){let n=sn(e);if(n)return t==="json"?JSON.stringify(n,null,2):[`${n.name} (${n.id}) \u2014 design language`,`Description: ${n.description}`,`Dark default: ${n.darkDefault}`,`Brand JSON: ${n.brandPath??"(factory \u2014 none)"}`,`Skin CSS: ${n.skinPath??"(factory \u2014 none)"}`,`Install: ${n.install.command}`,`CSS import: ${n.install.cssImport??"(clear data-brand)"}`,`Proves: ${n.proves.join("; ")}`].join(`
539
+ `)}function So(e){let t=e.command("theme").description("Design languages (Brand Package global product chrome swap)");t.command("list").description("List design languages").option("--format <type>","Output format: json or table").action(n=>{console.log($c(Co(n)))}),t.command("inspect <id>").description("Show a design language by id").option("--format <type>","Output format: json or table").action((n,r)=>{let o=jc(n,Co(r));o||(d.error(`Design language '${n}' not found. Run: nebutra theme list`),process.exit(l.NOT_FOUND)),console.log(o)})}import{existsSync as Oo,readFileSync as Ao}from"fs";import{resolve as Ro,join as cn}from"path";function _o(e={}){let t=e.manifestPath?Ro(e.manifestPath):cn(Ro(e.root??process.cwd()),"apps","design-docs","public","agent-manifest.json");return{manifestPath:t,componentDir:cn(t,"..","agent","components")}}function Tc(e){return e.trim().toLowerCase()}function Fc(e){let t=Tc(e);if(!/^[a-z0-9-]+$/.test(t))throw new Error(`Invalid UI component name "${e}". Use a registry kebab-case id such as "button".`);return t}function Io(e={}){let{manifestPath:t}=_o(e);if(!Oo(t))throw new Error(`Missing Nebutra UI agent manifest at ${t}. Run pnpm --filter @nebutra/design-docs build:registry.`);return JSON.parse(Ao(t,"utf8"))}function ln(e,t={}){let n=Fc(e),{componentDir:r}=_o(t),o=cn(r,`${n}.json`);if(!Oo(o))throw new Error(`UI component contract not found: ${n}`);return JSON.parse(Ao(o,"utf8"))}function Po(e,t="",n={}){let r=t.trim().toLowerCase(),o=Eo(n.limit??20,1,100),i=Eo(n.offset??0,0,Number.MAX_SAFE_INTEGER),a=n.tag?.trim().toLowerCase(),s=e.components.filter(p=>n.status&&p.status!==n.status||n.maturity&&p.maturity!==n.maturity||a&&!p.tags.includes(a)?!1:r?Dc(p).includes(r):!0).map(p=>({component:p,score:Lc(p,r)})).sort((p,g)=>g.score-p.score||p.component.name.localeCompare(g.component.name)).map(({component:p})=>p),c=s.slice(i,i+o),m=i+c.length;return{query:t,total:s.length,count:c.length,offset:i,limit:o,hasMore:m<s.length,...m<s.length&&{nextOffset:m},items:c}}function dn(e){let t=[],n=[];return e.evidence.source||t.push("Missing source evidence."),e.evidence.docs||t.push("Missing docs evidence."),e.evidence.registry||t.push("Missing registry evidence."),(e.status==="stable"||e.maturity==="canonical")&&!e.evidence.storybook&&t.push("Stable/canonical components must have Storybook evidence."),!e.evidence.tokens&&e.package==="@nebutra/ui"&&n.push("No token evidence was detected; confirm this primitive intentionally has no CSS variables."),e.migration.requiredForBreakingChanges&&e.migration.hints.length===0&&n.push("Breaking-change migration policy is enabled but no migration hints are present."),{name:e.name,valid:t.length===0,errors:t,warnings:n,evidence:e.evidence,migration:e.migration}}function Dc(e){return[e.name,e.title,e.description,e.status,e.maturity,e.layer,e.source,...e.tags].join(" ").toLowerCase()}function Lc(e,t){return t?e.name===t?100:e.name.startsWith(t)?80:e.title.toLowerCase().startsWith(t)?70:e.tags.includes(t)?60:e.name.includes(t)?50:e.title.toLowerCase().includes(t)?40:e.description.toLowerCase().includes(t)?20:1:e.maturity==="canonical"?20:10}function Eo(e,t,n){return Number.isFinite(e)?Math.min(Math.max(Math.trunc(e),t),n):t}function Et(e){let t=e.opts?e.opts():e,n=e.optsWithGlobals?e.optsWithGlobals():e;return t.format??n.format??Uc()}function Uc(){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 mn(e){let t=e.opts?e.opts():e;return{root:y(),manifestPath:t.manifest}}function No(e,t){if(!e)return t;let n=Number(e);return Number.isFinite(n)?n:t}function Bc(e,t="json"){if(t==="plain"||t==="table"){let n=[`Nebutra UI components (${e.total})`,""];for(let r of e.items)n.push(`${r.name.padEnd(22)} ${r.status.padEnd(12)} ${r.maturity.padEnd(10)} ${r.title}`);return e.hasMore&&n.push("",`Next offset: ${e.nextOffset}`),n.join(`
540
+ `)}return JSON.stringify(e,null,2)}function Mc(e,t="json"){return t==="plain"||t==="table"?[`${e.title} (${e.name})`,`Status: ${e.status}`,`Maturity: ${e.maturity}`,`Source: ${e.source}`,`Package import: ${e.imports.package}`,`Registry: ${e.imports.registry}`,`Docs: ${e.docs.routes.join(", ")||"missing"}`,`Storybook: ${e.docs.storybook??"missing"}`,`Tokens: ${e.tokens.join(", ")||"none detected"}`].join(`
541
+ `):JSON.stringify(e,null,2)}function Gc(e,t="json"){if(t==="plain"||t==="table"){if("results"in e){let n=[`Nebutra UI validation: ${e.valid?"valid":"invalid"}`,`Total: ${e.total}`,`Invalid: ${e.invalid}`];for(let r of e.results.filter(o=>!o.valid))n.push(`- ${r.name}: ${r.errors.join("; ")}`);return n.join(`
535
542
  `)}return[`${e.name}: ${e.valid?"valid":"invalid"}`,...e.errors.map(n=>`error: ${n}`),...e.warnings.map(n=>`warning: ${n}`)].join(`
536
- `)}return JSON.stringify(e,null,2)}function oc(e,t="json"){let n={component:e.name,dryRun:!0,codemods:e.migration.codemods,hints:e.migration.hints,requiredForBreakingChanges:e.migration.requiredForBreakingChanges};return t==="plain"||t==="table"?[`Migration hints for ${e.name}`,`Codemods: ${n.codemods.length===0?"none registered":n.codemods.join(", ")}`,...n.hints.map(r=>`- ${r}`)].join(`
537
- `):JSON.stringify(n,null,2)}function nn(e,t){try{return en(e,rn(t))}catch{St(`UI component contract not found: ${e}`,"Run `nebutra ui search <query> --format json` to discover available component ids.")}}function no(e){try{return Xr(rn(e))}catch{St("The Nebutra UI component catalog (agent-manifest.json) isn't available in this project.","This is an internal Nebutra doc-site feature, not part of the Sailor scaffold \u2014 `nebutra ui <search|component|validate|migrate>` only works inside the Nebutra-Sailor monorepo itself.")}}function ic(e,t){let n=e.components.map(o=>tn(en(o.name,rn(t)))),r=n.filter(o=>!o.valid).length;return{valid:r===0,total:n.length,invalid:r,results:n}}function ro(e){let t=e.command("ui").description("Inspect Nebutra UI agent contracts, production evidence, and migration hints");t.command("search [query]").description("Search the agent-readable @nebutra/ui component contract").option("--limit <number>","Maximum number of results","20").option("--offset <number>","Pagination offset","0").option("--tag <tag>","Filter by generated tag").option("--status <status>","Filter by status").option("--maturity <maturity>","Filter by maturity").option("--manifest <path>","Read a specific agent-manifest.json").option("--format <type>","Output format: json, table, plain").action((n,r)=>{let o=no(r),i=eo(o,n??"",{limit:to(r.limit,20),offset:to(r.offset,0),tag:r.tag,status:r.status,maturity:r.maturity});console.log(tc(i,wt(r)))}),t.command("component <name>").description("Print one component contract with imports, evidence, and migration policy").option("--manifest <path>","Read a specific agent-manifest.json").option("--format <type>","Output format: json, table, plain").action((n,r)=>{let o=nn(n,r);console.log(nc(o,wt(r)))}),t.command("validate [name]").description("Validate production evidence for one component or the full UI contract").option("--manifest <path>","Read a specific agent-manifest.json").option("--warn-only","Report validation failures without a non-zero exit").option("--format <type>","Output format: json, table, plain").action((n,r)=>{let o=n?tn(nn(n,r)):ic(no(r),r);console.log(rc(o,wt(r))),!o.valid&&!r.warnOnly&&process.exit(l.INCOMPATIBLE)}),t.command("migrate <name>").description("Print dry-run codemod and migration hints for a component").option("--manifest <path>","Read a specific agent-manifest.json").option("--apply","Reserved for future codemod execution").option("--format <type>","Output format: json, table, plain").action((n,r)=>{r.apply&&(console.error("Codemod execution is not implemented yet. Use `nebutra ui migrate <name>` for the dry-run plan."),process.exit(l.INCOMPATIBLE));let o=nn(n,r);console.log(oc(o,wt(r)))})}import{existsSync as oo,readFileSync as ac,unlinkSync as sc,writeFileSync as cc}from"fs";import{dirname as lc,join as io,parse as uc}from"path";function dc(e){let t=e,{root:n}=uc(t);for(;;){if(oo(io(t,"package.json")))return t;if(t===n)return null;let r=lc(t);if(r===t)return null;t=r}}async function mc(e){let t=e.format==="json",n=dc(process.cwd())??process.cwd(),r=io(n,"nebutra.config.json");if(oo(r)||(t?console.log(JSON.stringify({command:"unlink",success:!0,alreadyUnlinked:!0},null,2)):c.info("Already unlinked"),process.exit(l.SUCCESS)),e.force){try{sc(r)}catch(s){let u=s instanceof Error?s.message:String(s);t?console.log(JSON.stringify({command:"unlink",success:!1,error:u},null,2)):c.error(`Failed to delete config: ${u}`),process.exit(l.PERMISSION_DENIED)}t?console.log(JSON.stringify({command:"unlink",success:!0,deletedConfig:!0,configPath:r},null,2)):c.success("Removed nebutra.config.json");return}let o={};try{o=JSON.parse(ac(r,"utf-8"))}catch{o={}}"projectId"in o||(t?console.log(JSON.stringify({command:"unlink",success:!0,alreadyUnlinked:!0},null,2)):c.info("Already unlinked"),process.exit(l.SUCCESS));let{projectId:i,...a}=o;cc(r,JSON.stringify(a,null,2)+`
538
- `),t?console.log(JSON.stringify({command:"unlink",success:!0,configPath:r},null,2)):c.success("Unlinked from project")}function ao(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 mc({force:t.force||!1,format:t.format||r.format})})}import{spawnSync as Ac}from"child_process";import{existsSync as pc,readFileSync as co}from"fs";import{dirname as so,join as gc}from"path";import{fileURLToPath as fc}from"url";function hc(e=import.meta.url){let t=so(fc(e));for(let n=0;n<6;n+=1){let r=gc(t,"package.json");if(pc(r))try{if(JSON.parse(co(r,"utf8")).name==="nebutra")return r}catch{}let o=so(t);if(o===t)break;t=o}throw new Error("Unable to locate the nebutra CLI package.json")}function xt(e=import.meta.url){return JSON.parse(co(hc(e),"utf8")).version}import{promises as Ct}from"fs";import{homedir as yc}from"os";import{join as uo}from"path";import bc from"p-timeout";import pe from"picocolors";var vc=1440*60*1e3,mo=uo(yc(),".nebutra"),on=uo(mo,"update-check.json"),kc="https://registry.npmjs.org/nebutra",wc=5e3;async function xc(){try{await Ct.mkdir(mo,{recursive:!0})}catch{}}async function Cc(){try{let e=await Ct.readFile(on,"utf-8"),t=JSON.parse(e);return Date.now()-t.timestamp<vc?t:(await Ct.unlink(on).catch(r=>{c.debug("Failed to remove expired update cache file",{error:r})}),null)}catch{return null}}async function Sc(e){try{await xc();let t={timestamp:Date.now(),latestVersion:e};await Ct.writeFile(on,JSON.stringify(t,null,2),"utf-8")}catch{}}async function Ec(){try{let e=new AbortController,t=await bc(fetch(kc,{signal:e.signal,headers:{Accept:"application/json"}}),{milliseconds:wc,fallback:()=>(e.abort(),null)});if(!t?.ok)return null;let r=(await t.json())["dist-tags"];return!r||typeof r.latest!="string"?null:r.latest}catch{return null}}function lo(e){return e.split(".").map(t=>parseInt(t.replace(/[^0-9]/g,""),10)||0).slice(0,3)}function Rc(e,t){let n=lo(e),r=lo(t);for(let o=0;o<3;o++){let i=n[o]??0,a=r[o]??0;if(a>i)return!0;if(a<i)return!1}return!1}async function an(e){let t=await Cc(),n=null;return t?n=t.latestVersion:(n=await Ec(),n&&await Sc(n)),!n||!Rc(e,n)?null:{currentVersion:e,latestVersion:n,isUpdate:!0}}function Oc(e){let n=`Update available: ${e.currentVersion} \u2192 ${e.latestVersion}`,r="Run `npm i -g nebutra` to update",o=" ".repeat((43-n.length)/2),i=" ".repeat((43-r.length)/2),a=[pe.cyan("\u256D"+"\u2500".repeat(41)+"\u256E"),pe.cyan("\u2502")+pe.yellow(o+n+o)+pe.cyan("\u2502"),pe.cyan("\u2502")+pe.gray(i+r+i)+pe.cyan("\u2502"),pe.cyan("\u2570"+"\u2500".repeat(41)+"\u256F")]}async function po(e){let t=null;return an(e).then(n=>{t=n}),async()=>{!process.stdout.isTTY||process.env.CI||(t||await new Promise(n=>setTimeout(n,100)),t&&Oc(t))}}var Ce=xt();function _c(){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 Ic(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"}}async function Pc(e){let t=_c(),n=Ic(t),r=e.format==="json",o=await an(Ce),i=o?.latestVersion??null,a=!!o?.isUpdate;e.dryRun&&(r?console.log(JSON.stringify({command:"upgrade",dryRun:!0,installer:t,command_to_run:n,currentVersion:Ce,latestVersion:i,hasUpdate:a},null,2)):(c.info(`Detected installer: ${t}`),c.info(`Current: ${Ce}${i?` \u2192 Latest: ${i}`:""}`),c.info(`Upgrade command: ${n}`)),process.exit(l.DRY_RUN_OK)),!a&&i&&(r?console.log(JSON.stringify({command:"upgrade",success:!0,alreadyLatest:!0,version:Ce},null,2)):c.success(`Already on the latest version (${Ce})`),process.exit(l.SUCCESS)),e.yes||(r?console.log(JSON.stringify({command:"upgrade",success:!0,installer:t,command_to_run:n,currentVersion:Ce,latestVersion:i,requiresConfirmation:!0},null,2)):(c.info(`Current: ${Ce}${i?` \u2192 Latest: ${i}`:""}`),c.info("To upgrade, run:"),c.info(` ${n}`),c.info("Or re-run this command with --yes to execute it now.")),process.exit(l.SUCCESS));let[s,...u]=n.split(" ");r||c.info(`Running: ${n}`);let m=Ac(s,u,{stdio:r?"pipe":"inherit"});m.status===0&&(r?console.log(JSON.stringify({command:"upgrade",success:!0,installer:t,ran:n},null,2)):c.success("Upgrade complete"),process.exit(l.SUCCESS));let p=m.stderr?.toString()||`Upgrade failed (exit ${m.status})`;r?console.log(JSON.stringify({command:"upgrade",success:!1,installer:t,ran:n,error:p},null,2)):c.error(p),process.exit(l.ERROR)}function go(e){let t=(n,r)=>{let o=r?.optsWithGlobals?.()||n;return Pc({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)}function sn(e,t){let n=t==="expired"?`Your ${M.name} session has expired. Run \`nebutra login\` to sign in again.`:"Not logged in. Run `nebutra login` to authorize this machine.";e?console.log(JSON.stringify({command:"whoami",success:!1,error:t,message:n},null,2)):c.error(n),process.exit(l.PERMISSION_DENIED)}async function Nc(e,t){let n=!!e.json||e.format==="json"||t==="json",r=await ur();if(!r)return sn(n,"missing");if(r.expired)return sn(n,"expired");let o=gt(),i=await ft(o,r.token);if(!i)return sn(n,"expired");n?console.log(JSON.stringify({command:"whoami",success:!0,userId:i.userId,email:i.email,name:i.name,tokenSource:r.source},null,2)):(c.success(i.email??i.userId),i.name&&c.info(i.name))}function fo(e){e.command("whoami").description(`Show the ${M.name} identity this machine is authorized as`).option("--json","Output as JSON").option("--format <type>","Output format: json or plain").action(async(t,n)=>{let r=n?.optsWithGlobals?.()||t;await Nc(t,r.format)})}import{existsSync as $c,mkdirSync as jc,writeFileSync as Fc}from"fs";import{homedir as ho,platform as Dc}from"os";import{join as Ke}from"path";import cn from"picocolors";function Tc(){if(Dc()==="win32"){let t=process.env.LOCALAPPDATA||Ke(ho(),"AppData","Local");return Ke(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?Ke(e,"nebutra"):Ke(ho(),".config","nebutra")}function Lc(){let e=process.env.NEBUTRA_TELEMETRY;return e==="0"||e==="false"||e==="no"}function yo(e,t){try{jc(e,{recursive:!0}),Fc(t,"")}catch{}}function bo(){if(!process.stderr.isTTY||!process.stdin.isTTY||process.env.CI)return;let e=Tc(),t=Ke(e,"first-run-acked");try{if($c(t))return}catch{return}if(Lc()){yo(e,t);return}let r=[`${cn.cyan("\u2139")} ${M.name} collects anonymous usage analytics to improve the CLI.`,` Opt out at any time: ${cn.dim("export NEBUTRA_TELEMETRY=0")}`,` Privacy policy: ${cn.dim(`${pt("landing")}/legal/cli-analytics`)}`,""];try{process.stderr.write(r.join(`
539
- `))}catch{}yo(e,t)}var xo=xt();function Bc(e){let t=new Uc,n=e.isInteractive??(process.stdin.isTTY!==!1&&process.stdout.isTTY!==!1);return t.name("nebutra").description("Nebutra \u2014 the CLI for Sailor projects: status, dev, db, and codegen").version(e.version).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(r=>we(async()=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await Jn({dryRun:r.dryRun||!1,yes:o.yes||!1,ifNotExists:r.ifNotExists||!1})})),wr(t),Rr(t),Cn(t),Wn(t),Yn(t),Un(t),Zn(t),hn(t),$n(t),Kn(t),jn(t),Kr(t),Tn(t),Yr(t),wn(t),ro(t),_r(t),Or(t),_n(t),br(t),kr(t),fo(t),go(t),er(t),ao(t),Dr(t),Mr(t),t.command("doctor").description("Check your Nebutra project setup for common issues").action(()=>we(async()=>{let{logger:r}=await import("./logger-QMYN56HK.js"),{findMonorepoRootOrExit:o}=await import("./delegate-MQWQZT5V.js"),i=await import("fs"),a=await import("path");r.info("Running project health check...");let s=!1,u=o(),m=process.version;parseInt(m.replace("v","").split(".")[0]??"0",10)<22?(r.error(`Node.js >= 22 required, found ${m}`),s=!0):r.success(`Node.js version OK (${m})`),i.existsSync(a.join(u,"nebutra.config.json"))?r.success("nebutra.config.json found"):r.warn("nebutra.config.json missing. Try running 'nebutra init'"),i.existsSync(a.join(u,"package.json"))?r.success("package.json found"):(r.error("No package.json found in root directory"),s=!0),i.existsSync(a.join(u,".env"))||i.existsSync(a.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=a.join(u,"apps/api-gateway");i.existsSync(h)&&r.warn("Legacy `apps/api-gateway/` present. Expected `backends/gateway/` \u2014 see CLAUDE.md for the W2.x restructure.");let g=a.join(u,"backends/gateway");i.existsSync(g)?r.success("backends/gateway/ present"):i.existsSync(a.join(u,"backends"));let k=a.join(u,"packages");if(i.existsSync(k)){let W=new Set(["design","iam","commerce","integrations","platform","ops","ai"]);try{let X=i.readdirSync(k,{withFileTypes:!0}),L=[];for(let Se of X){if(!Se.isDirectory()||W.has(Se.name))continue;let We=a.join(k,Se.name,"package.json");i.existsSync(We)&&L.push(Se.name)}L.length>0?r.warn(`Found ${L.length} uncategorized package(s) under packages/: ${L.join(", ")}. Expected packages/<category>/<name>/ \u2014 see CLAUDE.md.`):r.success("Packages follow categorized layout")}catch{}}let F=a.join(u,".nebutra"),w=a.join(F,"scaffold-meta.json");if(i.existsSync(w)?r.success(".nebutra/scaffold-meta.json present (CLI-scaffolded project)"):i.existsSync(F)&&r.warn(".nebutra/ directory exists but scaffold-meta.json is missing. Harmless \u2014 the marker is provenance only and grants nothing."),(i.existsSync(w)||i.existsSync(a.join(u,"nebutra.config.json")))&&(r.info(""),r.info("Golden path to a running app:"),r.info(" 1. nebutra status # what is live, what needs a key"),r.info(" 2. pnpm dev # http://localhost:3001"),r.info(""),r.info("Optional, once you want your own Postgres:"),r.info(" pnpm infra:up && pnpm db:migrate")),s)throw new K({code:"doctor_failed",message:"Doctor found critical issues that require your attention.",suggestion:"Fix the errors above, then re-run `nebutra doctor`.",exitCode:l.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",`
543
+ `)}return JSON.stringify(e,null,2)}function Vc(e,t="json"){let n={component:e.name,dryRun:!0,codemods:e.migration.codemods,hints:e.migration.hints,requiredForBreakingChanges:e.migration.requiredForBreakingChanges};return t==="plain"||t==="table"?[`Migration hints for ${e.name}`,`Codemods: ${n.codemods.length===0?"none registered":n.codemods.join(", ")}`,...n.hints.map(r=>`- ${r}`)].join(`
544
+ `):JSON.stringify(n,null,2)}function un(e,t){try{return ln(e,mn(t))}catch{_t(`UI component contract not found: ${e}`,"Run `nebutra ui search <query> --format json` to discover available component ids.")}}function $o(e){try{return Io(mn(e))}catch{_t("The Nebutra UI component catalog (agent-manifest.json) isn't available in this project.","This is an internal Nebutra doc-site feature, not part of the Sailor scaffold \u2014 `nebutra ui <search|component|validate|migrate>` only works inside the Nebutra-Sailor monorepo itself.")}}function zc(e,t){let n=e.components.map(o=>dn(ln(o.name,mn(t)))),r=n.filter(o=>!o.valid).length;return{valid:r===0,total:n.length,invalid:r,results:n}}function jo(e){let t=e.command("ui").description("Inspect Nebutra UI agent contracts, production evidence, and migration hints");t.command("search [query]").description("Search the agent-readable @nebutra/ui component contract").option("--limit <number>","Maximum number of results","20").option("--offset <number>","Pagination offset","0").option("--tag <tag>","Filter by generated tag").option("--status <status>","Filter by status").option("--maturity <maturity>","Filter by maturity").option("--manifest <path>","Read a specific agent-manifest.json").option("--format <type>","Output format: json, table, plain").action((n,r)=>{let o=$o(r),i=Po(o,n??"",{limit:No(r.limit,20),offset:No(r.offset,0),tag:r.tag,status:r.status,maturity:r.maturity});console.log(Bc(i,Et(r)))}),t.command("component <name>").description("Print one component contract with imports, evidence, and migration policy").option("--manifest <path>","Read a specific agent-manifest.json").option("--format <type>","Output format: json, table, plain").action((n,r)=>{let o=un(n,r);console.log(Mc(o,Et(r)))}),t.command("validate [name]").description("Validate production evidence for one component or the full UI contract").option("--manifest <path>","Read a specific agent-manifest.json").option("--warn-only","Report validation failures without a non-zero exit").option("--format <type>","Output format: json, table, plain").action((n,r)=>{let o=n?dn(un(n,r)):zc($o(r),r);console.log(Gc(o,Et(r))),!o.valid&&!r.warnOnly&&process.exit(l.INCOMPATIBLE)}),t.command("migrate <name>").description("Print dry-run codemod and migration hints for a component").option("--manifest <path>","Read a specific agent-manifest.json").option("--apply","Reserved for future codemod execution").option("--format <type>","Output format: json, table, plain").action((n,r)=>{r.apply&&(console.error("Codemod execution is not implemented yet. Use `nebutra ui migrate <name>` for the dry-run plan."),process.exit(l.INCOMPATIBLE));let o=un(n,r);console.log(Vc(o,Et(r)))})}import{existsSync as To,readFileSync as Kc,unlinkSync as Wc,writeFileSync as Yc}from"fs";import{dirname as Jc,join as Fo,parse as qc}from"path";function Hc(e){let t=e,{root:n}=qc(t);for(;;){if(To(Fo(t,"package.json")))return t;if(t===n)return null;let r=Jc(t);if(r===t)return null;t=r}}async function Zc(e){let t=e.format==="json",n=Hc(process.cwd())??process.cwd(),r=Fo(n,"nebutra.config.json");if(To(r)||(t?console.log(JSON.stringify({command:"unlink",success:!0,alreadyUnlinked:!0},null,2)):d.info("Already unlinked"),process.exit(l.SUCCESS)),e.force){try{Wc(r)}catch(s){let c=s instanceof Error?s.message:String(s);t?console.log(JSON.stringify({command:"unlink",success:!1,error:c},null,2)):d.error(`Failed to delete config: ${c}`),process.exit(l.PERMISSION_DENIED)}t?console.log(JSON.stringify({command:"unlink",success:!0,deletedConfig:!0,configPath:r},null,2)):d.success("Removed nebutra.config.json");return}let o={};try{o=JSON.parse(Kc(r,"utf-8"))}catch{o={}}"projectId"in o||(t?console.log(JSON.stringify({command:"unlink",success:!0,alreadyUnlinked:!0},null,2)):d.info("Already unlinked"),process.exit(l.SUCCESS));let{projectId:i,...a}=o;Yc(r,JSON.stringify(a,null,2)+`
545
+ `),t?console.log(JSON.stringify({command:"unlink",success:!0,configPath:r},null,2)):d.success("Unlinked from project")}function Do(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 Zc({force:t.force||!1,format:t.format||r.format})})}import{spawnSync as pl}from"child_process";import{existsSync as Xc,readFileSync as Uo}from"fs";import{dirname as Lo,join as Qc}from"path";import{fileURLToPath as el}from"url";function tl(e=import.meta.url){let t=Lo(el(e));for(let n=0;n<6;n+=1){let r=Qc(t,"package.json");if(Xc(r))try{if(JSON.parse(Uo(r,"utf8")).name==="nebutra")return r}catch{}let o=Lo(t);if(o===t)break;t=o}throw new Error("Unable to locate the nebutra CLI package.json")}function Ot(e=import.meta.url){return JSON.parse(Uo(tl(e),"utf8")).version}import{promises as At}from"fs";import{homedir as nl}from"os";import{join as Mo}from"path";import rl from"p-timeout";import fe from"picocolors";var ol=1440*60*1e3,Go=Mo(nl(),".nebutra"),pn=Mo(Go,"update-check.json"),il="https://registry.npmjs.org/nebutra",al=5e3;async function sl(){try{await At.mkdir(Go,{recursive:!0})}catch{}}async function cl(){try{let e=await At.readFile(pn,"utf-8"),t=JSON.parse(e);return Date.now()-t.timestamp<ol?t:(await At.unlink(pn).catch(r=>{d.debug("Failed to remove expired update cache file",{error:r})}),null)}catch{return null}}async function ll(e){try{await sl();let t={timestamp:Date.now(),latestVersion:e};await At.writeFile(pn,JSON.stringify(t,null,2),"utf-8")}catch{}}async function dl(){try{let e=new AbortController,t=await rl(fetch(il,{signal:e.signal,headers:{Accept:"application/json"}}),{milliseconds:al,fallback:()=>(e.abort(),null)});if(!t?.ok)return null;let r=(await t.json())["dist-tags"];return!r||typeof r.latest!="string"?null:r.latest}catch{return null}}function Bo(e){return e.split(".").map(t=>parseInt(t.replace(/[^0-9]/g,""),10)||0).slice(0,3)}function ul(e,t){let n=Bo(e),r=Bo(t);for(let o=0;o<3;o++){let i=n[o]??0,a=r[o]??0;if(a>i)return!0;if(a<i)return!1}return!1}async function fn(e){let t=await cl(),n=null;return t?n=t.latestVersion:(n=await dl(),n&&await ll(n)),!n||!ul(e,n)?null:{currentVersion:e,latestVersion:n,isUpdate:!0}}function ml(e){let n=`Update available: ${e.currentVersion} \u2192 ${e.latestVersion}`,r="Run `npm i -g nebutra` to update",o=" ".repeat((43-n.length)/2),i=" ".repeat((43-r.length)/2),a=[fe.cyan("\u256D"+"\u2500".repeat(41)+"\u256E"),fe.cyan("\u2502")+fe.yellow(o+n+o)+fe.cyan("\u2502"),fe.cyan("\u2502")+fe.gray(i+r+i)+fe.cyan("\u2502"),fe.cyan("\u2570"+"\u2500".repeat(41)+"\u256F")]}async function Vo(e){let t=null;return fn(e).then(n=>{t=n}),async()=>{!process.stdout.isTTY||process.env.CI||(t||await new Promise(n=>setTimeout(n,100)),t&&ml(t))}}var Ce=Ot();function fl(){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 gl(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"}}async function hl(e){let t=fl(),n=gl(t),r=e.format==="json",o=await fn(Ce),i=o?.latestVersion??null,a=!!o?.isUpdate;e.dryRun&&(r?console.log(JSON.stringify({command:"upgrade",dryRun:!0,installer:t,command_to_run:n,currentVersion:Ce,latestVersion:i,hasUpdate:a},null,2)):(d.info(`Detected installer: ${t}`),d.info(`Current: ${Ce}${i?` \u2192 Latest: ${i}`:""}`),d.info(`Upgrade command: ${n}`)),process.exit(l.DRY_RUN_OK)),!a&&i&&(r?console.log(JSON.stringify({command:"upgrade",success:!0,alreadyLatest:!0,version:Ce},null,2)):d.success(`Already on the latest version (${Ce})`),process.exit(l.SUCCESS)),e.yes||(r?console.log(JSON.stringify({command:"upgrade",success:!0,installer:t,command_to_run:n,currentVersion:Ce,latestVersion:i,requiresConfirmation:!0},null,2)):(d.info(`Current: ${Ce}${i?` \u2192 Latest: ${i}`:""}`),d.info("To upgrade, run:"),d.info(` ${n}`),d.info("Or re-run this command with --yes to execute it now.")),process.exit(l.SUCCESS));let[s,...c]=n.split(" ");r||d.info(`Running: ${n}`);let m=pl(s,c,{stdio:r?"pipe":"inherit"});m.status===0&&(r?console.log(JSON.stringify({command:"upgrade",success:!0,installer:t,ran:n},null,2)):d.success("Upgrade complete"),process.exit(l.SUCCESS));let p=m.stderr?.toString()||`Upgrade failed (exit ${m.status})`;r?console.log(JSON.stringify({command:"upgrade",success:!1,installer:t,ran:n,error:p},null,2)):d.error(p),process.exit(l.ERROR)}function zo(e){let t=(n,r)=>{let o=r?.optsWithGlobals?.()||n;return hl({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)}function gn(e,t){let n=t==="expired"?`Your ${M.name} session has expired. Run \`nebutra login\` to sign in again.`:"Not logged in. Run `nebutra login` to authorize this machine.";e?console.log(JSON.stringify({command:"whoami",success:!1,error:t,message:n},null,2)):d.error(n),process.exit(l.PERMISSION_DENIED)}async function yl(e,t){let n=!!e.json||e.format==="json"||t==="json",r=await ce();if(!r)return gn(n,"missing");if(r.expired)return gn(n,"expired");let o=vt(),i=await wt(o,r.token);if(!i)return gn(n,"expired");n?console.log(JSON.stringify({command:"whoami",success:!0,userId:i.userId,email:i.email,name:i.name,tokenSource:r.source},null,2)):(d.success(i.email??i.userId),i.name&&d.info(i.name))}function Ko(e){e.command("whoami").description(`Show the ${M.name} identity this machine is authorized as`).option("--json","Output as JSON").option("--format <type>","Output format: json or plain").action(async(t,n)=>{let r=n?.optsWithGlobals?.()||t;await yl(t,r.format)})}import bl from"http";var vl=["HTTPS_PROXY","https_proxy","HTTP_PROXY","http_proxy"];function Wo(e=process.env,t=bl){return!vl.some(n=>e[n])||typeof t.setGlobalProxyFromEnv!="function"?!1:(t.setGlobalProxyFromEnv(e),!0)}import{existsSync as wl,mkdirSync as kl,writeFileSync as xl}from"fs";import{homedir as Yo,platform as Cl}from"os";import{join as Je}from"path";import hn from"picocolors";function Sl(){if(Cl()==="win32"){let t=process.env.LOCALAPPDATA||Je(Yo(),"AppData","Local");return Je(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?Je(e,"nebutra"):Je(Yo(),".config","nebutra")}function Rl(){let e=process.env.NEBUTRA_TELEMETRY;return e==="0"||e==="false"||e==="no"}function Jo(e,t){try{kl(e,{recursive:!0}),xl(t,"")}catch{}}function qo(){if(!process.stderr.isTTY||!process.stdin.isTTY||process.env.CI)return;let e=Sl(),t=Je(e,"first-run-acked");try{if(wl(t))return}catch{return}if(Rl()){Jo(e,t);return}let r=[`${hn.cyan("\u2139")} ${M.name} collects anonymous usage analytics to improve the CLI.`,` Opt out at any time: ${hn.dim("export NEBUTRA_TELEMETRY=0")}`,` Privacy policy: ${hn.dim(`${Z("landing")}/legal/cli-analytics`)}`,""];try{process.stderr.write(r.join(`
546
+ `))}catch{}Jo(e,t)}Wo();var Qo=Ot();function Ol(e){let t=new El,n=e.isInteractive??(process.stdin.isTTY!==!1&&process.stdout.isTTY!==!1);return t.name("nebutra").description("Nebutra \u2014 the CLI for Sailor projects: status, dev, db, and codegen").version(e.version).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(r=>z(async()=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await Ar({dryRun:r.dryRun||!1,yes:o.yes||!1,ifNotExists:r.ifNotExists||!1})})),Jr(t),Qr(t),rr(t),Er(t),Or(t),vr(t),Pr(t),Un(t),pr(t),Rr(t),fr(t),xo(t),yr(t),So(t),tr(t),po(t),jn(t),jo(t),no(t),eo(t),lr(t),Gr(t),zr(t),Ko(t),zo(t),jr(t),Do(t),lo(t),bo(t),t.command("doctor").description("Check your Nebutra project setup for common issues").action(()=>z(async()=>{let{logger:r}=await import("./logger-QMYN56HK.js"),{findMonorepoRootOrExit:o}=await import("./delegate-MQWQZT5V.js"),i=await import("fs"),a=await import("path");r.info("Running project health check...");let s=!1,c=o(),m=process.version;parseInt(m.replace("v","").split(".")[0]??"0",10)<22?(r.error(`Node.js >= 22 required, found ${m}`),s=!0):r.success(`Node.js version OK (${m})`),i.existsSync(a.join(c,"nebutra.config.json"))?r.success("nebutra.config.json found"):r.warn("nebutra.config.json missing. Try running 'nebutra init'"),i.existsSync(a.join(c,"package.json"))?r.success("package.json found"):(r.error("No package.json found in root directory"),s=!0),i.existsSync(a.join(c,".env"))||i.existsSync(a.join(c,".env.local"))?r.success("Environment variables configured"):r.warn("No .env or .env.local found. Some features may not work properly.");let h=a.join(c,"apps/api-gateway");i.existsSync(h)&&r.warn("Legacy `apps/api-gateway/` present. Expected `backends/gateway/` \u2014 see CLAUDE.md for the W2.x restructure.");let f=a.join(c,"backends/gateway");i.existsSync(f)?r.success("backends/gateway/ present"):i.existsSync(a.join(c,"backends"));let w=a.join(c,"packages");if(i.existsSync(w)){let J=new Set(["design","iam","commerce","integrations","platform","ops","ai"]);try{let te=i.readdirSync(w,{withFileTypes:!0}),B=[];for(let Se of te){if(!Se.isDirectory()||J.has(Se.name))continue;let qe=a.join(w,Se.name,"package.json");i.existsSync(qe)&&B.push(Se.name)}B.length>0?r.warn(`Found ${B.length} uncategorized package(s) under packages/: ${B.join(", ")}. Expected packages/<category>/<name>/ \u2014 see CLAUDE.md.`):r.success("Packages follow categorized layout")}catch{}}let D=a.join(c,".nebutra"),k=a.join(D,"scaffold-meta.json");if(i.existsSync(k)?r.success(".nebutra/scaffold-meta.json present (CLI-scaffolded project)"):i.existsSync(D)&&r.warn(".nebutra/ directory exists but scaffold-meta.json is missing. Harmless \u2014 the marker is provenance only and grants nothing."),(i.existsSync(k)||i.existsSync(a.join(c,"nebutra.config.json")))&&(r.info(""),r.info("Golden path to a running app:"),r.info(" 1. nebutra status # what is live, what needs a key"),r.info(" 2. pnpm dev # http://localhost:3001"),r.info(""),r.info("Optional, once you want your own Postgres:"),r.info(" pnpm infra:up && pnpm db:migrate")),s)throw new E({code:"doctor_failed",message:"Doctor found critical issues that require your attention.",suggestion:"Fix the errors above, then re-run `nebutra doctor`.",exitCode:l.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",`
540
547
  Examples:
541
548
  $ nebutra init Initialize a new project
542
549
  $ nebutra status Show capability readiness (live/local-fallback/missing-key)
@@ -571,4 +578,4 @@ Environment:
571
578
  NEBUTRA_OUTPUT_FORMAT Output format (json|table|plain)
572
579
  NO_COLOR Disable colored output
573
580
  CI Auto-enable non-interactive mode
574
- `),t}async function Mc(){bo();let e=await po(xo),t=Bc({version:xo,isInteractive:process.stdin.isTTY!==!1&&process.stdout.isTTY!==!1});process.argv.length<=2&&(t.outputHelp(),process.exit(l.SUCCESS)),await t.parseAsync(process.argv),await e()}function Gc(){if(!process.argv[1])return!1;try{return vo(wo(import.meta.url))===vo(ko(process.argv[1]))}catch{return wo(import.meta.url)===ko(process.argv[1])}}var Vc=Gc();Vc&&Mc().catch(e=>{process.exitCode=Jt(e)});export{Bc as buildProgram};
581
+ `),t}async function Al(){qo();let e=await Vo(Qo),t=Ol({version:Qo,isInteractive:process.stdin.isTTY!==!1&&process.stdout.isTTY!==!1});process.argv.length<=2&&(t.outputHelp(),process.exit(l.SUCCESS)),await t.parseAsync(process.argv),await e()}function _l(){if(!process.argv[1])return!1;try{return Ho(Xo(import.meta.url))===Ho(Zo(process.argv[1]))}catch{return Xo(import.meta.url)===Zo(process.argv[1])}}var Il=_l();Il&&Al().catch(e=>{process.exitCode=It(e)});export{Ol as buildProgram};