nebutra 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.js +90 -63
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -19,6 +19,9 @@ nebutra init
|
|
|
19
19
|
# Check capability readiness (live / local-fallback / missing-key)
|
|
20
20
|
nebutra status
|
|
21
21
|
|
|
22
|
+
# Sync .env.example / .env.local with the capabilities declared in nebutra.config.json
|
|
23
|
+
nebutra sync
|
|
24
|
+
|
|
22
25
|
# Scaffold a new project (separate tool — one creation entry point)
|
|
23
26
|
npx create-sailor ./my-app
|
|
24
27
|
|
|
@@ -44,6 +47,7 @@ nebutra doctor
|
|
|
44
47
|
|---------|-------------|
|
|
45
48
|
| `init` | Initialize project with `nebutra.config.json` |
|
|
46
49
|
| `status` | Show capability readiness — live, local-fallback, or missing-key, per env (`--json`) |
|
|
50
|
+
| `sync` | Make `.env.example`/`.env.local` agree with declared capabilities, idempotently (`--dry-run`, `--json`) |
|
|
47
51
|
| `mcp` | Start the MCP server for AI agents and editors |
|
|
48
52
|
| `dev` | Start development server |
|
|
49
53
|
| `generate` | Scaffold apps, modules, and code |
|
|
@@ -61,6 +65,8 @@ nebutra doctor
|
|
|
61
65
|
| `secrets` | Encrypted secrets management |
|
|
62
66
|
| `completions` | Generate shell completions for the current command surface |
|
|
63
67
|
| `link` / `unlink` | Link or unlink the project to Nebutra platform metadata |
|
|
68
|
+
| `login` | Authorize this machine with your Nebutra account (device flow, agent-friendly with `--json`) |
|
|
69
|
+
| `whoami` | Show the Nebutra identity this machine is authorized as |
|
|
64
70
|
| `logout` / `upgrade` | Session and CLI lifecycle |
|
|
65
71
|
|
|
66
72
|
Scaffolding, registry, and platform-operations commands (`create`, `add`,
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{b as l}from"./chunk-JDX5NXDX.js";import{a as
|
|
3
|
-
`)}function ee(e){return e.ai={providers:{},routes:{},...e.ai},e.ai.providers={...e.ai.providers},e.ai.routes={...e.ai.routes},e.ai}function It(e){let t=Ne(),n=[...new Set(e.map(o=>o.toLowerCase()))],r=n.filter(o=>!t[o]);return r.length>0&&(l.error(`Unknown AI provider(s): ${r.join(", ")}. Valid providers: ${Object.keys(t).join(", ")}`),process.exit(c.INVALID_ARGS)),n}function gr(e){let t=e?.split(",").map(r=>r.trim()).filter(Boolean)??[];t.length===0&&(l.error("Route fallback cannot be empty. Use --fallback provider:model,provider:model"),process.exit(c.INVALID_ARGS));let n=t.filter(r=>!/^[a-z0-9-]+:[^,\s]+$/i.test(r));return n.length>0&&(l.error(`Invalid fallback target(s): ${n.join(", ")}. Expected provider:model.`),process.exit(c.INVALID_ARGS)),t}function _t(e){let t=ee(e),n=t.providers??{},r=t.routes??{},o=Object.entries(n).filter(([,a])=>a.enabled).map(([a])=>a);return{path:Y(),gateway:t.gateway??null,providers:n,enabledProviders:o,routes:r}}function G(e){let t=typeof e.opts=="function"?e.opts():e,n=typeof e.optsWithGlobals=="function"?e.optsWithGlobals():t,r=t.format??n.format??yr();return{...n,...t,...r?{format:r}:{}}}function yr(){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 he(e){return e?e.length<=8?"***":`${e.substring(0,8)}***`:"not set"}async function br(e){e.format!=="json"&&l.info("Checking configured AI models and providers...");let t={openai:{name:"OpenAI",keyVars:["OPENAI_API_KEY"],configured:!1},anthropic:{name:"Anthropic Claude",keyVars:["ANTHROPIC_API_KEY"],configured:!1},openrouter:{name:"OpenRouter",keyVars:["OPENROUTER_API_KEY"],configured:!1},vercel:{name:"Vercel AI SDK",keyVars:["VERCEL_AI_API_KEY"],configured:!1}};for(let r of Object.values(t))r.configured=r.keyVars.some(o=>process.env[o]);let n={providers:Object.entries(t).map(([r,o])=>({id:r,name:o.name,configured:o.configured,keys:o.keyVars.map(a=>({name:a,value:he(process.env[a])}))})),configuredCount:Object.values(t).filter(r=>r.configured).length,totalProviders:Object.keys(t).length};e.dryRun||(n.configuredCount===0?l.warn("No AI providers configured. Set API keys to enable AI features."):l.success(`${n.configuredCount} AI provider(s) configured`)),M(n,e.format,"ai:models")}async function hr(e){e.format!=="json"&&l.info("Scanning for available agents...");try{let t=Xe(process.cwd(),"backends/gateway/src/routes/agents"),n=[];try{let o=await import("fs");o.existsSync(t)&&(n=o.readdirSync(t).filter(i=>i.endsWith(".ts")&&!i.endsWith(".d.ts")).map(i=>({name:i.replace(".ts",""),path:`backends/gateway/src/routes/agents/${i}`})))}catch{n=[]}let r={agents:n.length>0?n:[{name:"No agents found",path:"create agents in backends/gateway/src/routes/agents/"}],count:n.length};e.dryRun||(r.count===0?l.info("No agents configured. Create agents in backends/gateway/src/routes/agents/"):l.success(`Found ${r.count} agent(s)`)),M(r,e.format,"ai:agents")}catch(t){l.error(`Failed to scan agents: ${t instanceof Error?t.message:String(t)}`),process.exit(c.ERROR)}}async function vr(e,t){(!e||e.trim().length===0)&&(l.error("Prompt cannot be empty"),process.exit(c.INVALID_ARGS)),t.format!=="json"&&(l.info("Testing AI prompt against default model..."),l.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||l.warn("AI test execution requires a running API gateway. Start it with: pnpm --filter @nebutra/gateway dev"),M(n,t.format,"ai:test")}async function Cr(e){e.format!=="json"&&l.info("Checking AI configuration...");let t={providers:{openai:{key:he(process.env.OPENAI_API_KEY),configured:!!process.env.OPENAI_API_KEY},anthropic:{key:he(process.env.ANTHROPIC_API_KEY),configured:!!process.env.ANTHROPIC_API_KEY},openrouter:{key:he(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=Xe(process.cwd(),"nebutra.config.ts"),o=await import("fs");if(o.existsSync(r)){let i=o.readFileSync(r,"utf-8").match(/preset:\s*["']([^"']+)["']/);i&&(t.preset=i[1])}}catch{}let n=Object.entries(t.providers).filter(([,r])=>r.configured).map(([r])=>r);e.dryRun||(n.length===0?l.warn("No AI providers configured. Set environment variables to enable AI features."):l.success(`Configuration loaded: ${n.join(", ")}`)),M(t,e.format,"ai:config")}async function wr(e){let t=ce(),n=ee(t),r=Ne(),o=Object.values(r).map(i=>{let s=n.providers?.[i.id];return{id:i.id,name:i.name,enabled:s?.enabled===!0,configured:i.envVars.some(d=>!!process.env[d]),envVars:i.envVars.map(d=>({name:d,value:he(process.env[d])}))}}),a={configPath:Y(),providers:o,enabledCount:o.filter(i=>i.enabled).length,totalProviders:o.length};if(e.format!=="json"){let i=o.filter(s=>s.enabled);i.length===0?l.warn("No runtime AI providers enabled in nebutra.config.json."):l.success(`Enabled AI providers: ${i.map(s=>s.id).join(", ")}`)}M(a,e.format,"ai:provider:list")}async function Sr(e,t){let n=It(e),r=ce(),o=ee(r),a=Ne(),i=new Date().toISOString();for(let d of n)o.providers[d]={enabled:!0,envVars:a[d].envVars,enabledAt:o.providers?.[d]?.enabledAt??i};Pe(r);let s={configPath:Y(),enabled:n,env:t.env===!0?n.flatMap(d=>a[d].envVars.map(m=>({provider:d,name:m,example:`${m}=`}))):void 0,ai:_t(r)};if(t.format!=="json"&&(l.success(`Enabled AI provider(s): ${n.join(", ")}`),t.env)){l.info("Required environment variables:");for(let d of n)for(let m of a[d].envVars)console.log(`${m}=`)}M(s,t.format,"ai:provider:enable")}async function Er(e,t){let n=It(e),r=ce(),o=ee(r),a=Ne(),i=new Date().toISOString();for(let d of n)o.providers[d]={enabled:!1,envVars:o.providers?.[d]?.envVars??a[d].envVars,disabledAt:i};Pe(r);let s={configPath:Y(),disabled:n,ai:_t(r)};t.format!=="json"&&l.success(`Disabled AI provider(s): ${n.join(", ")}`),M(s,t.format,"ai:provider:disable")}async function Rr(e){let t=ce(),n=ee(t),r=new Date().toISOString();n.gateway={enabled:!0,mode:"local",initializedAt:n.gateway?.initializedAt??r,updatedAt:r},Pe(t);let o={configPath:Y(),gateway:n.gateway};e.format!=="json"&&l.success("AI gateway governance initialized in nebutra.config.json"),M(o,e.format,"ai:gateway:init")}async function xr(e){let t=ce(),n=ee(t),r={configPath:Y(),gateway:n.gateway??null,initialized:n.gateway?.enabled===!0};e.format!=="json"&&(r.initialized?l.success("AI gateway governance is initialized"):l.warn("AI gateway governance is not initialized. Run: nebutra ai gateway init")),M(r,e.format,"ai:gateway:status")}async function kr(e,t){(!e||e.trim().length===0)&&(l.error("Route name cannot be empty"),process.exit(c.INVALID_ARGS)),t.policy||(l.error("Route policy is required. Use --policy <policy>."),process.exit(c.INVALID_ARGS));let n=gr(t.fallback),r=ce(),o=ee(r);o.routes[e]={policy:t.policy,fallback:n,updatedAt:new Date().toISOString()},Pe(r);let a={configPath:Y(),route:{name:e,...o.routes[e]},routes:o.routes};t.format!=="json"&&l.success(`AI route '${e}' set with policy '${t.policy}'`),M(a,t.format,"ai:route:set")}async function Or(e){let t=ce(),n=ee(t),r=Object.entries(n.routes??{}).map(([a,i])=>({name:a,...i})),o={configPath:Y(),routes:r,count:r.length};e.format!=="json"&&(r.length===0?l.warn("No AI routes configured. Run: nebutra ai route set chat --policy cost-latency-balanced --fallback openai:gpt-5.4"):l.success(`Configured AI routes: ${r.map(a=>a.name).join(", ")}`)),M(o,e.format,"ai:route:list")}function Nt(e){let t=e.command("ai").description("Manage AI models, agents, and configuration"),n=t.command("provider").description("Manage runtime AI provider governance");n.command("list").description("List supported and enabled AI providers").option("--format <type>","Output format: json or plain").action(async a=>{let i=G(a);await wr({format:i.format,interactive:i.yes!==!0})}),n.command("enable <providers...>").description("Enable one or more runtime AI providers in nebutra.config.json").option("--env","Print required environment variable placeholders").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let s=G(i);await Sr(a,{env:s.env===!0,format:s.format,interactive:s.yes!==!0})}),n.command("disable <providers...>").description("Disable one or more runtime AI providers in nebutra.config.json").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let s=G(i);await Er(a,{format:s.format,interactive:s.yes!==!0})});let r=t.command("gateway").description("Manage local AI gateway governance");r.command("init").description("Initialize the local AI gateway governance block").option("--format <type>","Output format: json or plain").action(async a=>{let i=G(a);await Rr({format:i.format,interactive:i.yes!==!0})}),r.command("status").description("Show local AI gateway governance status").option("--format <type>","Output format: json or plain").action(async a=>{let i=G(a);await xr({format:i.format,interactive:i.yes!==!0})});let o=t.command("route").description("Manage AI routing policies");o.command("set <name>").description("Create or update an AI route policy").requiredOption("--policy <policy>","Routing policy id").requiredOption("--fallback <list>","Comma-separated provider:model fallback chain").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let s=G(i);await kr(a,{policy:s.policy,fallback:s.fallback,format:s.format,interactive:s.yes!==!0})}),o.command("list").description("List configured AI routes").option("--format <type>","Output format: json or plain").action(async a=>{let i=G(a);await Or({format:i.format,interactive:i.yes!==!0})}),t.command("models").description("List configured AI models and providers").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async a=>{let i=G(a);await br({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})}),t.command("agents").description("List available agents in the project").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async a=>{let i=G(a);await hr({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})}),t.command("test <prompt>").description("Send a test prompt to the default AI model (requires running API gateway)").option("--dry-run","Preview without executing").option("--format <type>","Output format: json or plain").action(async(a,i)=>{let s=G(i);await vr(a,{dryRun:s.dryRun===!0,format:s.format,interactive:s.yes!==!0})}),t.command("config").description("Show current AI configuration and provider status").option("--dry-run","Preview without making changes").option("--format <type>","Output format: json or plain").action(async a=>{let i=G(a);await Cr({dryRun:i.dryRun===!0,format:i.format,interactive:i.yes!==!0})})}function ve(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 Pt(e){return/^#[0-9A-Fa-f]{6}$/.test(e)}async function Ar(e){l.info("Initializing brand system...");let t=await P("brand:init",{dryRun:e.dryRun,interactive:e.interactive??!0});ve(t,e.format,"brand:init"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||l.success("Brand system initialized successfully")}async function Ir(e){l.info("Applying brand configuration...");let t=await P("brand:apply",{dryRun:e.dryRun,interactive:e.interactive??!0});ve(t,e.format,"brand:apply"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||l.success("Brand applied successfully")}async function _r(e){let t=[];e.primary&&(Pt(e.primary)||(l.error(`Invalid primary color format: ${e.primary}. Expected hex format like #0033FE`),process.exit(c.INVALID_ARGS)),t.push(`--primary=${e.primary}`)),e.secondary&&(Pt(e.secondary)||(l.error(`Invalid secondary color format: ${e.secondary}. Expected hex format like #0BF1C3`),process.exit(c.INVALID_ARGS)),t.push(`--secondary=${e.secondary}`)),t.length===0&&(l.warn("No colors specified. Usage: nebutra brand palette --primary=#HEX --secondary=#HEX"),l.info("Example: nebutra brand palette --primary=#0033FE --secondary=#0BF1C3"),process.exit(c.INVALID_ARGS)),l.info(`Generating color palette: ${t.join(" ")}`);let n=await k({command:"node",args:["scripts/generate-palette.mjs",...t],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Generate brand palette"});ve(n,e.format,"brand:palette"),n.exitCode!==0&&process.exit(n.exitCode),e.dryRun||l.success("Color palette generated successfully")}async function Nr(e){l.info("Syncing brand tokens across packages...");let t=await P("brand:sync",{dryRun:e.dryRun,interactive:e.interactive??!0});ve(t,e.format,"brand:sync"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||l.success("Brand tokens synced successfully")}async function Pr(e){l.info("Verifying brand token consistency...");let t=await P("brand:verify-tokens",{dryRun:e.dryRun,interactive:e.interactive??!0});ve(t,e.format,"brand:verify-tokens"),t.exitCode!==0&&(l.error("Brand token verification failed. There are inconsistencies in your design tokens."),process.exit(t.exitCode)),e.dryRun||l.success("All brand tokens are consistent")}function $t(e){let t=e.command("brand").description("Manage Nebutra brand system and design tokens");t.command("init").description("Initialize brand system configuration").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Ar({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("apply").description("Apply brand configuration to the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Ir({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("palette").description("Generate brand color palette from custom colors").option("--primary <hex>","Primary brand color (hex format, e.g. #0033FE)").option("--secondary <hex>","Secondary brand color (hex format, e.g. #0BF1C3)").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await _r({primary:n.primary,secondary:n.secondary,dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("sync").description("Synchronize brand tokens across all packages").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Nr({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("verify").description("Verify brand token consistency across the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Pr({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}import{appendFileSync as $r,existsSync as jr,readFileSync as Tr,writeFileSync as Dr}from"fs";import{homedir as Ce}from"os";import{join as ge}from"path";import*as j from"@clack/prompts";import $ from"picocolors";var W={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:"Manage Nebutra Sailor commercial license activation and status",usage:"nebutra license [subcommand]",arguments:[],options:[],subcommands:[{name:"activate",description:"Activate a commercial license key for local development",usage:"nebutra license activate <key>",arguments:[{name:"key",description:"Your Nebutra Sailor commercial license key",required:!0}],options:[{flags:"--quiet",description:"Suppress output"}],examples:[{command:"nebutra license activate liz_1234567890",description:"Activate your license key globally"}]},{name:"status",description:"Check the locally configured license status",usage:"nebutra license status",arguments:[],options:[{flags:"--quiet",description:"Suppress output"}],examples:[{command:"nebutra license status",description:"Check if you have an active license"}]}]},{name:"ai",description:"Configure AI providers, gateway routing, and agent-ready defaults",usage:"nebutra ai [subcommand]"},{name:"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:"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:"logout",description:"Clear local Nebutra CLI session state",usage:"nebutra logout"},{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:"doctor",description:"Check local project setup and common Nebutra configuration issues",usage:"nebutra doctor"}]};var $e=(W.subcommands??[]).map(e=>e.name),jt={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",ai:"AI provider and gateway routing configuration",services:"Microservice management",secrets:"Encrypted secrets management",logout:"Clear local session state",upgrade:"Upgrade local CLI tooling",link:"Link local project metadata",unlink:"Unlink local project metadata",status:"Show capability readiness (live/local-fallback/missing-key)"},Lr=["--help","--version","--stdio","--format","--yes","--no-interactive","--no-color","--verbose","--quiet","--dry-run","--if-not-exists","--json"],Fr=$e.map(e=>`complete -c nebutra -n "__fish_use_subcommand_only" -f -a "${e}" -d "${jt[e]}"`).join(`
|
|
4
|
-
`);function
|
|
2
|
+
import{b as l}from"./chunk-JDX5NXDX.js";import{a as C,b as O,c as $,d as re,e as Ce,f as ee}from"./chunk-LGUMYJ4Q.js";import{realpathSync as eo}from"fs";import{resolve as to}from"path";import{fileURLToPath as no}from"url";import{Command as ec}from"commander";import{existsSync as oo,readFileSync as io,writeFileSync as ao}from"fs";import{resolve as yt}from"path";var c={SUCCESS:0,ERROR:1,INVALID_ARGS:2,NOT_FOUND:3,PERMISSION_DENIED:4,CONFLICT:5,NETWORK_ERROR:6,TIMEOUT:7,CANCELLED:8,CONFIG_ERROR:9,DRY_RUN_OK:10,INCOMPATIBLE:11,RESOURCE_EXHAUSTED:12},Zt={[c.SUCCESS]:"Command completed successfully",[c.ERROR]:"General or unknown error occurred",[c.INVALID_ARGS]:"Invalid arguments or options provided",[c.NOT_FOUND]:"Requested resource not found",[c.PERMISSION_DENIED]:"Permission denied or authentication required",[c.CONFLICT]:"Resource already exists",[c.NETWORK_ERROR]:"Network or external service error (may be retryable)",[c.TIMEOUT]:"Operation timed out (may be retryable)",[c.CANCELLED]:"Operation cancelled by user",[c.CONFIG_ERROR]:"Configuration error or missing configuration",[c.DRY_RUN_OK]:"Dry-run completed successfully",[c.INCOMPATIBLE]:"Incompatible version or dependency conflict",[c.RESOURCE_EXHAUSTED]:"System resource exhausted"};var so=["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"],Qt="nebutra.config.json",co=so,Xt={"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 Y(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 lo(e){return e.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function uo(e){return Xt[e]?Xt[e]:[`${e.replace(/-/g,"_").toUpperCase()}_API_KEY`]}function Me(){return Object.fromEntries(co.map(e=>[e,{id:e,name:lo(e),envVars:uo(e)}]))}function q(){return yt(process.cwd(),Qt)}function de(){let e=q();if(!oo(e))return{$schema:"https://nebutra.com/schema.json"};try{return JSON.parse(io(e,"utf-8"))}catch(t){l.error(`Failed to parse ${Qt}: ${t instanceof Error?t.message:String(t)}`),process.exit(c.ERROR)}}function Ge(e){ao(q(),`${JSON.stringify(e,null,2)}
|
|
3
|
+
`)}function oe(e){return e.ai={providers:{},routes:{},...e.ai},e.ai.providers={...e.ai.providers},e.ai.routes={...e.ai.routes},e.ai}function en(e){let t=Me(),n=[...new Set(e.map(o=>o.toLowerCase()))],r=n.filter(o=>!t[o]);return r.length>0&&(l.error(`Unknown AI provider(s): ${r.join(", ")}. Valid providers: ${Object.keys(t).join(", ")}`),process.exit(c.INVALID_ARGS)),n}function mo(e){let t=e?.split(",").map(r=>r.trim()).filter(Boolean)??[];t.length===0&&(l.error("Route fallback cannot be empty. Use --fallback provider:model,provider:model"),process.exit(c.INVALID_ARGS));let n=t.filter(r=>!/^[a-z0-9-]+:[^,\s]+$/i.test(r));return n.length>0&&(l.error(`Invalid fallback target(s): ${n.join(", ")}. Expected provider:model.`),process.exit(c.INVALID_ARGS)),t}function tn(e){let t=oe(e),n=t.providers??{},r=t.routes??{},o=Object.entries(n).filter(([,i])=>i.enabled).map(([i])=>i);return{path:q(),gateway:t.gateway??null,providers:n,enabledProviders:o,routes:r}}function W(e){let t=typeof e.opts=="function"?e.opts():e,n=typeof e.optsWithGlobals=="function"?e.optsWithGlobals():t,r=t.format??n.format??po();return{...n,...t,...r?{format:r}:{}}}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 _e(e){return e?e.length<=8?"***":`${e.substring(0,8)}***`:"not set"}async function fo(e){e.format!=="json"&&l.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:_e(process.env[i])}))})),configuredCount:Object.values(t).filter(r=>r.configured).length,totalProviders:Object.keys(t).length};e.dryRun||(n.configuredCount===0?l.warn("No AI providers configured. Set API keys to enable AI features."):l.success(`${n.configuredCount} AI provider(s) configured`)),Y(n,e.format,"ai:models")}async function go(e){e.format!=="json"&&l.info("Scanning for available agents...");try{let t=yt(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?l.info("No agents configured. Create agents in backends/gateway/src/routes/agents/"):l.success(`Found ${r.count} agent(s)`)),Y(r,e.format,"ai:agents")}catch(t){l.error(`Failed to scan agents: ${t instanceof Error?t.message:String(t)}`),process.exit(c.ERROR)}}async function yo(e,t){(!e||e.trim().length===0)&&(l.error("Prompt cannot be empty"),process.exit(c.INVALID_ARGS)),t.format!=="json"&&(l.info("Testing AI prompt against default model..."),l.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||l.warn("AI test execution requires a running API gateway. Start it with: pnpm --filter @nebutra/gateway dev"),Y(n,t.format,"ai:test")}async function ho(e){e.format!=="json"&&l.info("Checking AI configuration...");let t={providers:{openai:{key:_e(process.env.OPENAI_API_KEY),configured:!!process.env.OPENAI_API_KEY},anthropic:{key:_e(process.env.ANTHROPIC_API_KEY),configured:!!process.env.ANTHROPIC_API_KEY},openrouter:{key:_e(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=yt(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?l.warn("No AI providers configured. Set environment variables to enable AI features."):l.success(`Configuration loaded: ${n.join(", ")}`)),Y(t,e.format,"ai:config")}async function bo(e){let t=de(),n=oe(t),r=Me(),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(d=>!!process.env[d]),envVars:a.envVars.map(d=>({name:d,value:_e(process.env[d])}))}}),i={configPath:q(),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?l.warn("No runtime AI providers enabled in nebutra.config.json."):l.success(`Enabled AI providers: ${a.map(s=>s.id).join(", ")}`)}Y(i,e.format,"ai:provider:list")}async function vo(e,t){let n=en(e),r=de(),o=oe(r),i=Me(),a=new Date().toISOString();for(let d of n)o.providers[d]={enabled:!0,envVars:i[d].envVars,enabledAt:o.providers?.[d]?.enabledAt??a};Ge(r);let s={configPath:q(),enabled:n,env:t.env===!0?n.flatMap(d=>i[d].envVars.map(m=>({provider:d,name:m,example:`${m}=`}))):void 0,ai:tn(r)};if(t.format!=="json"&&(l.success(`Enabled AI provider(s): ${n.join(", ")}`),t.env)){l.info("Required environment variables:");for(let d of n)for(let m of i[d].envVars)console.log(`${m}=`)}Y(s,t.format,"ai:provider:enable")}async function wo(e,t){let n=en(e),r=de(),o=oe(r),i=Me(),a=new Date().toISOString();for(let d of n)o.providers[d]={enabled:!1,envVars:o.providers?.[d]?.envVars??i[d].envVars,disabledAt:a};Ge(r);let s={configPath:q(),disabled:n,ai:tn(r)};t.format!=="json"&&l.success(`Disabled AI provider(s): ${n.join(", ")}`),Y(s,t.format,"ai:provider:disable")}async function Co(e){let t=de(),n=oe(t),r=new Date().toISOString();n.gateway={enabled:!0,mode:"local",initializedAt:n.gateway?.initializedAt??r,updatedAt:r},Ge(t);let o={configPath:q(),gateway:n.gateway};e.format!=="json"&&l.success("AI gateway governance initialized in nebutra.config.json"),Y(o,e.format,"ai:gateway:init")}async function xo(e){let t=de(),n=oe(t),r={configPath:q(),gateway:n.gateway??null,initialized:n.gateway?.enabled===!0};e.format!=="json"&&(r.initialized?l.success("AI gateway governance is initialized"):l.warn("AI gateway governance is not initialized. Run: nebutra ai gateway init")),Y(r,e.format,"ai:gateway:status")}async function So(e,t){(!e||e.trim().length===0)&&(l.error("Route name cannot be empty"),process.exit(c.INVALID_ARGS)),t.policy||(l.error("Route policy is required. Use --policy <policy>."),process.exit(c.INVALID_ARGS));let n=mo(t.fallback),r=de(),o=oe(r);o.routes[e]={policy:t.policy,fallback:n,updatedAt:new Date().toISOString()},Ge(r);let i={configPath:q(),route:{name:e,...o.routes[e]},routes:o.routes};t.format!=="json"&&l.success(`AI route '${e}' set with policy '${t.policy}'`),Y(i,t.format,"ai:route:set")}async function Eo(e){let t=de(),n=oe(t),r=Object.entries(n.routes??{}).map(([i,a])=>({name:i,...a})),o={configPath:q(),routes:r,count:r.length};e.format!=="json"&&(r.length===0?l.warn("No AI routes configured. Run: nebutra ai route set chat --policy cost-latency-balanced --fallback openai:gpt-5.4"):l.success(`Configured AI routes: ${r.map(i=>i.name).join(", ")}`)),Y(o,e.format,"ai:route:list")}function nn(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=>{let a=W(i);await bo({format:a.format,interactive:a.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)=>{let s=W(a);await vo(i,{env:s.env===!0,format:s.format,interactive:s.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)=>{let s=W(a);await wo(i,{format:s.format,interactive:s.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=>{let a=W(i);await Co({format:a.format,interactive:a.yes!==!0})}),r.command("status").description("Show local AI gateway governance status").option("--format <type>","Output format: json or plain").action(async i=>{let a=W(i);await xo({format:a.format,interactive:a.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)=>{let s=W(a);await So(i,{policy:s.policy,fallback:s.fallback,format:s.format,interactive:s.yes!==!0})}),o.command("list").description("List configured AI routes").option("--format <type>","Output format: json or plain").action(async i=>{let a=W(i);await Eo({format:a.format,interactive:a.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=>{let a=W(i);await fo({dryRun:a.dryRun===!0,format:a.format,interactive:a.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=>{let a=W(i);await go({dryRun:a.dryRun===!0,format:a.format,interactive:a.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)=>{let s=W(a);await yo(i,{dryRun:s.dryRun===!0,format:s.format,interactive:s.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=>{let a=W(i);await ho({dryRun:a.dryRun===!0,format:a.format,interactive:a.yes!==!0})})}function Ie(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 rn(e){return/^#[0-9A-Fa-f]{6}$/.test(e)}async function Ro(e){l.info("Initializing brand system...");let t=await $("brand:init",{dryRun:e.dryRun,interactive:e.interactive??!0});Ie(t,e.format,"brand:init"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||l.success("Brand system initialized successfully")}async function ko(e){l.info("Applying brand configuration...");let t=await $("brand:apply",{dryRun:e.dryRun,interactive:e.interactive??!0});Ie(t,e.format,"brand:apply"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||l.success("Brand applied successfully")}async function Oo(e){let t=[];e.primary&&(rn(e.primary)||(l.error(`Invalid primary color format: ${e.primary}. Expected hex format like #0033FE`),process.exit(c.INVALID_ARGS)),t.push(`--primary=${e.primary}`)),e.secondary&&(rn(e.secondary)||(l.error(`Invalid secondary color format: ${e.secondary}. Expected hex format like #0BF1C3`),process.exit(c.INVALID_ARGS)),t.push(`--secondary=${e.secondary}`)),t.length===0&&(l.warn("No colors specified. Usage: nebutra brand palette --primary=#HEX --secondary=#HEX"),l.info("Example: nebutra brand palette --primary=#0033FE --secondary=#0BF1C3"),process.exit(c.INVALID_ARGS)),l.info(`Generating color palette: ${t.join(" ")}`);let n=await O({command:"node",args:["scripts/generate-palette.mjs",...t],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Generate brand palette"});Ie(n,e.format,"brand:palette"),n.exitCode!==0&&process.exit(n.exitCode),e.dryRun||l.success("Color palette generated successfully")}async function Ao(e){l.info("Syncing brand tokens across packages...");let t=await $("brand:sync",{dryRun:e.dryRun,interactive:e.interactive??!0});Ie(t,e.format,"brand:sync"),t.exitCode!==0&&process.exit(t.exitCode),e.dryRun||l.success("Brand tokens synced successfully")}async function _o(e){l.info("Verifying brand token consistency...");let t=await $("brand:verify-tokens",{dryRun:e.dryRun,interactive:e.interactive??!0});Ie(t,e.format,"brand:verify-tokens"),t.exitCode!==0&&(l.error("Brand token verification failed. There are inconsistencies in your design tokens."),process.exit(t.exitCode)),e.dryRun||l.success("All brand tokens are consistent")}function on(e){let t=e.command("brand").description("Manage Nebutra brand system and design tokens");t.command("init").description("Initialize brand system configuration").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Ro({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("apply").description("Apply brand configuration to the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await ko({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("palette").description("Generate brand color palette from custom colors").option("--primary <hex>","Primary brand color (hex format, e.g. #0033FE)").option("--secondary <hex>","Secondary brand color (hex format, e.g. #0BF1C3)").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Oo({primary:n.primary,secondary:n.secondary,dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("sync").description("Synchronize brand tokens across all packages").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await Ao({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("verify").description("Verify brand token consistency across the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await _o({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})})}import{appendFileSync as Io,existsSync as No,readFileSync as Po,writeFileSync as jo}from"fs";import{homedir as Ne}from"os";import{join as xe}from"path";import*as L from"@clack/prompts";import D from"picocolors";var H={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:"Manage Nebutra Sailor commercial license activation and status",usage:"nebutra license [subcommand]",arguments:[],options:[],subcommands:[{name:"activate",description:"Activate a commercial license key for local development",usage:"nebutra license activate <key>",arguments:[{name:"key",description:"Your Nebutra Sailor commercial license key",required:!0}],options:[{flags:"--quiet",description:"Suppress output"}],examples:[{command:"nebutra license activate liz_1234567890",description:"Activate your license key globally"}]},{name:"status",description:"Check the locally configured license status",usage:"nebutra license status",arguments:[],options:[{flags:"--quiet",description:"Suppress output"}],examples:[{command:"nebutra license status",description:"Check if you have an active license"}]}]},{name:"ai",description:"Configure AI providers, gateway routing, and agent-ready defaults",usage:"nebutra ai [subcommand]"},{name:"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:"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 Ve=(H.subcommands??[]).map(e=>e.name),an={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",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"},$o=["--help","--version","--stdio","--format","--yes","--no-interactive","--no-color","--verbose","--quiet","--dry-run","--if-not-exists","--json","--no-browser","--poll"],To=Ve.map(e=>`complete -c nebutra -n "__fish_use_subcommand_only" -f -a "${e}" -d "${an[e]}"`).join(`
|
|
4
|
+
`);function sn(){return`#!/usr/bin/env bash
|
|
5
5
|
|
|
6
6
|
_nebutra_completions() {
|
|
7
7
|
local cur prev opts cmd
|
|
@@ -10,10 +10,10 @@ _nebutra_completions() {
|
|
|
10
10
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
11
11
|
|
|
12
12
|
# Subcommands
|
|
13
|
-
local subcommands="${
|
|
13
|
+
local subcommands="${Ve.join(" ")}"
|
|
14
14
|
|
|
15
15
|
# Flags
|
|
16
|
-
local flags="${
|
|
16
|
+
local flags="${$o.join(" ")}"
|
|
17
17
|
|
|
18
18
|
if [[ \${cur} == -* ]]; then
|
|
19
19
|
# Complete flags
|
|
@@ -30,11 +30,11 @@ _nebutra_completions() {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
complete -o bashdefault -o default -o nospace -F _nebutra_completions nebutra
|
|
33
|
-
`}function
|
|
33
|
+
`}function cn(){return`#compdef nebutra
|
|
34
34
|
|
|
35
35
|
_nebutra() {
|
|
36
36
|
local -a commands=(
|
|
37
|
-
${
|
|
37
|
+
${Ve.map(e=>`"${e}:${an[e]}"`).join(`
|
|
38
38
|
`)}
|
|
39
39
|
)
|
|
40
40
|
|
|
@@ -47,6 +47,11 @@ _nebutra() {
|
|
|
47
47
|
"--json[Emit machine-readable JSON]"
|
|
48
48
|
)
|
|
49
49
|
|
|
50
|
+
local -a sync_flags=(
|
|
51
|
+
"--dry-run[Preview planned additions without writing files]"
|
|
52
|
+
"--json[Emit machine-readable JSON]"
|
|
53
|
+
)
|
|
54
|
+
|
|
50
55
|
local -a mcp_flags=(
|
|
51
56
|
"--stdio[Use stdio transport (default)]"
|
|
52
57
|
)
|
|
@@ -74,6 +79,9 @@ _nebutra() {
|
|
|
74
79
|
status)
|
|
75
80
|
_arguments "*:options:($global_flags $status_flags)"
|
|
76
81
|
;;
|
|
82
|
+
sync)
|
|
83
|
+
_arguments "*:options:($global_flags $sync_flags)"
|
|
84
|
+
;;
|
|
77
85
|
*)
|
|
78
86
|
_arguments "*:options:($global_flags)"
|
|
79
87
|
;;
|
|
@@ -82,12 +90,12 @@ _nebutra() {
|
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
_nebutra
|
|
85
|
-
`}function
|
|
93
|
+
`}function ln(){return`# Fish shell completions for nebutra
|
|
86
94
|
|
|
87
|
-
complete -c nebutra -f -n "__fish_seen_subcommand_from; and not __fish_seen_subcommand_from ${
|
|
95
|
+
complete -c nebutra -f -n "__fish_seen_subcommand_from; and not __fish_seen_subcommand_from ${Ve.join(" ")}" -d "Nebutra governance CLI"
|
|
88
96
|
|
|
89
97
|
# Subcommands
|
|
90
|
-
${
|
|
98
|
+
${To}
|
|
91
99
|
|
|
92
100
|
# Flags
|
|
93
101
|
complete -c nebutra -f -a "--help" -d "Show help message"
|
|
@@ -96,6 +104,10 @@ complete -c nebutra -f -a "--version" -d "Show version"
|
|
|
96
104
|
# status command flags
|
|
97
105
|
complete -c nebutra -n "__fish_seen_subcommand_from status" -f -a "--json" -d "Emit machine-readable JSON"
|
|
98
106
|
|
|
107
|
+
# sync command flags
|
|
108
|
+
complete -c nebutra -n "__fish_seen_subcommand_from sync" -f -a "--dry-run" -d "Preview planned additions without writing files"
|
|
109
|
+
complete -c nebutra -n "__fish_seen_subcommand_from sync" -f -a "--json" -d "Emit machine-readable JSON"
|
|
110
|
+
|
|
99
111
|
# mcp command flags
|
|
100
112
|
complete -c nebutra -n "__fish_seen_subcommand_from mcp" -f -a "--stdio" -d "Use stdio transport (default)"
|
|
101
113
|
|
|
@@ -109,24 +121,24 @@ complete -c nebutra -n "__fish_seen_subcommand_from completions" -f -a "install"
|
|
|
109
121
|
complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--all" -d "Show full schema for all commands"
|
|
110
122
|
complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--list" -d "List all available command names"
|
|
111
123
|
complete -c nebutra -n "__fish_seen_subcommand_from schema" -f -a "--exit-codes" -d "Show exit codes reference"
|
|
112
|
-
`}function
|
|
124
|
+
`}function un(e){let n={bash:sn,zsh:cn,fish:ln}[e.toLowerCase()];n||(console.error(D.red(`Error: Unknown shell '${e}'. Supported: bash, zsh, fish`)),process.exit(c.INVALID_ARGS)),console.log(n())}async function Do(){let t=(process.env.SHELL||"/bin/bash").split("/").pop()||"bash";L.intro(D.bgCyan(D.black(" nebutra completions install ")));let n=L.spinner();n.start(D.cyan(`Detecting shell: ${t}...`));try{let r=null,o=null,i=null;if(t==="bash"?(r=xe(Ne(),".bashrc"),o=xe(Ne(),".bash_completion.d"),i="source ~/.bash_completion.d/nebutra"):t==="zsh"?(r=xe(Ne(),".zshrc"),o=xe(Ne(),".zsh/completions"),i="fpath=(~/.zsh/completions $fpath)"):t==="fish"?(o=xe(Ne(),".config/fish/completions"),i="# Completions installed to ~/.config/fish/completions/nebutra.fish"):(n.stop(),L.log.warn(D.yellow(`Warning: Unsupported shell '${t}'. Please install completions manually.`)),un("bash"),L.outro(D.cyan("Run 'nebutra completions bash' (or zsh/fish) to generate completions for your shell.")),process.exit(c.SUCCESS)),o){(await import("fs")).mkdirSync(o,{recursive:!0});let s=xe(o,t==="fish"?"nebutra.fish":"nebutra"),d=t==="bash"?sn():t==="zsh"?cn():ln();jo(s,d),n.stop(D.green(`Completion script installed to ${s}`))}r&&i&&t!=="fish"&&(!No(r)||!Po(r,"utf8").includes("nebutra"))&&(Io(r,(t==="bash"?`
|
|
113
125
|
# nebutra completions
|
|
114
126
|
[[ -f ~/.bash_completion.d/nebutra ]] && source ~/.bash_completion.d/nebutra`:`
|
|
115
127
|
# nebutra completions
|
|
116
128
|
fpath=(~/.zsh/completions $fpath)
|
|
117
129
|
autoload -Uz compinit && compinit`)+`
|
|
118
|
-
`),
|
|
119
|
-
`)}function
|
|
120
|
-
`)}function
|
|
121
|
-
`)}function u(e,t="info"){let n;switch(t){case"error":n=
|
|
122
|
-
`)}function
|
|
123
|
-
`),t&&Object.keys(t).length>0&&
|
|
124
|
-
`)}function
|
|
125
|
-
`),
|
|
126
|
-
`),
|
|
127
|
-
`),
|
|
128
|
-
`)}async function
|
|
129
|
-
`)}catch{process.stdout.write(t.stdout)}}else u("Failed to check migration status","error"),process.exit(t.exitCode||1)}function
|
|
130
|
+
`),L.log.info(D.green(`Added nebutra completions to ${r}`))),L.log.info(D.cyan("Completions installed! Restart your shell or run:")),L.log.info(D.dim(` source "${t==="bash"?"~/.bashrc":"~/.zshrc"}"`)),L.outro(D.green("Installation complete!"))}catch(r){n.stop(),L.log.error(D.red("Error installing completions. You can manually add them by running:")),L.log.info(D.cyan(` nebutra completions ${t} >> ~/.${t}rc`)),r instanceof Error&&console.error(D.dim(r.message)),process.exit(c.ERROR)}}function dn(e){e.command("completions [shell]").description("Generate or install shell completions").action(async t=>{!t||t==="install"?await Do():un(t)})}import*as ze from"@clack/prompts";import{stderr as ie,stdout as mn}from"process";import Z from"picocolors";function Lo(){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:Lo()?"plain":"json"}function Fo(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 d of e){let m=String(d[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 d=n.map(m=>String(s[m]??"").padEnd(r[String(m)])).join(" | ");a.push(d)}return a.join(`
|
|
131
|
+
`)}function Uo(e){return JSON.stringify(e)}function fn(e){if(!Array.isArray(e))return ht(e);if(e.length===0)return"(empty)";if(typeof e[0]!="object"||e[0]===null)return ht(e);let t=Fo(e);return Z.gray(t)}function ht(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"?ie:mn,o;switch(n){case"json":o=Uo(e);break;case"table":o=fn(e);break;default:o=ht(e);break}r.write(o+`
|
|
132
|
+
`)}function I(e,t={}){let n=pn(t.format),r;switch(n){case"json":r=JSON.stringify(e);break;case"table":r=fn(e);break;default:r=JSON.stringify(e,null,2);break}mn.write(r+`
|
|
133
|
+
`)}function u(e,t="info"){let n;switch(t){case"error":n=Z.red("\u2716");break;case"warn":n=Z.yellow("\u26A0");break;case"success":n=Z.green("\u2713");break;default:n=Z.blue("\u2139");break}let r=`${n} ${e}`;ie.write(r+`
|
|
134
|
+
`)}function x(e,t){if(!(process.env.NEBUTRA_DEBUG||process.argv.includes("--debug")))return;let r=Z.dim(`[debug] ${e}`);ie.write(r+`
|
|
135
|
+
`),t&&Object.keys(t).length>0&&ie.write(Z.dim(JSON.stringify(t,null,2))+`
|
|
136
|
+
`)}function Ke(e){ie.write(`
|
|
137
|
+
`),ie.write(Z.bold(Z.cyan(e))+`
|
|
138
|
+
`),ie.write(Z.gray("-".repeat(e.length))+`
|
|
139
|
+
`),ie.write(`
|
|
140
|
+
`)}async function Bo(e){u("Generating Prisma client"+"...","info");let n=await ee("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 bt(e,t){let n=t||{};if(e){u(`Creating migration: ${e}`,"info");let r=await ee("migrate",{args:["dev","--name",e],dryRun:n.dryRun,interactive:!n.dryRun});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 ee("migrate",{args:["deploy"],dryRun:n.dryRun,interactive:!n.dryRun});r.exitCode===0?u("All pending migrations completed","success"):(u("Migration deployment failed","error"),process.exit(r.exitCode||1))}}async function Mo(e){u("Pushing schema to database...","info");let t=await ee("db",{args:["push"],dryRun:e.dryRun,interactive:!e.dryRun});t.exitCode===0?u("Schema pushed successfully","success"):(u("Failed to push schema","error"),process.exit(t.exitCode||1))}async function Go(e){u("Seeding database...","info");let t=await ee("db",{args:["seed"],dryRun:e.dryRun,interactive:!e.dryRun});t.exitCode===0?u("Database seeded successfully","success"):(u("Failed to seed database","error"),process.exit(t.exitCode||1))}async function Vo(e){u("Launching Prisma Studio...","info");let t=await ee("studio",{dryRun:e.dryRun,interactive:!0});t.exitCode!==0&&(u("Failed to launch Prisma Studio","error"),process.exit(t.exitCode||1))}async function Ko(e){let t=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!e.yes&&!e.dryRun)if(t){let r=await ze.confirm({message:"Reset database? This will delete all data.",initialValue:!1});(ze.isCancel(r)||!r)&&(u("Reset cancelled","warn"),process.exit(c.CANCELLED))}else u("Database reset requires explicit --yes confirmation (use: nebutra db reset --yes)","error"),process.exit(c.INVALID_ARGS);u("Resetting database...","warn");let n=await ee("migrate",{args:["reset",...e.yes?["--force"]:[]],dryRun:e.dryRun,interactive:!e.dryRun});n.exitCode===0?u("Database reset successfully","success"):(u("Failed to reset database","error"),process.exit(n.exitCode||1))}async function zo(e){u("Checking migration status...","info");let t=await ee("migrate",{args:["status"],dryRun:e.dryRun,interactive:!e.dryRun});if(t.exitCode===0){if(e.format==="json")try{let n=JSON.parse(t.stdout);process.stdout.write(JSON.stringify(n,null,2)+`
|
|
141
|
+
`)}catch{process.stdout.write(t.stdout)}}else u("Failed to check migration status","error"),process.exit(t.exitCode||1)}function gn(e){e.command("db <verb> [args...]").description("Manage database schema, migrations, and state").option("--dry-run","Show what would be run without executing").option("--yes","Skip confirmations (especially for reset)").option("--format <type>","Output format: json, plain","plain").action(async(n,r,o)=>{let i=o.optsWithGlobals?.(),a={dryRun:o.dryRun||i?.dryRun,yes:o.yes||i?.yes,format:o.format||i?.format};try{switch(n){case"generate":await Bo(a);break;case"migrate":r.length>0?r[0]==="create"&&r[1]?await bt(r[1],a):await bt(r[0],a):await bt(void 0,a);break;case"push":await Mo(a);break;case"seed":await Go(a);break;case"studio":await Vo(a);break;case"reset":await Ko(a);break;case"status":await zo(a);break;default:u(`Unknown db subcommand: ${n}. Valid commands: generate, migrate, migrate create, push, seed, studio, reset, status`,"error"),process.exit(c.INVALID_ARGS)}}catch(s){let d=s instanceof Error?s.message:String(s);u(`Database command failed: ${d}`,"error"),x("Full error",{error:s}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
130
142
|
Examples:
|
|
131
143
|
nebutra db generate Generate Prisma client
|
|
132
144
|
nebutra db migrate Run all pending migrations
|
|
@@ -141,15 +153,15 @@ Flags:
|
|
|
141
153
|
--dry-run Show what would be run without executing
|
|
142
154
|
--yes Skip confirmations
|
|
143
155
|
--format <type> Output format: json, plain (default: plain)
|
|
144
|
-
`)}import*as
|
|
145
|
-
${n}`)}return t}async function
|
|
156
|
+
`)}import*as S from"@clack/prompts";import h from"picocolors";function A(e,t){let n=typeof e.optsWithGlobals=="function"?e.optsWithGlobals():void 0;return{...t&&typeof t.optsWithGlobals=="function"?t.optsWithGlobals():void 0,...n,...e}}function ae(e){if(e==="json"||e==="plain"||e==="table")return e}var Se=["landing","web","storybook","design-docs","idp","mail-preview","gateway","admin","auth","design","forge","router","pebble","typelens","sailor-docs","studio","sleptons"];function We(e){let t=`@nebutra/${e}`;if(!Se.includes(e)){let n=h.yellow(`Valid apps: ${Se.join(", ")}`);throw new Error(`Invalid app: ${h.red(e)}
|
|
157
|
+
${n}`)}return t}async function Wo(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:ae(e.format)}),process.exit(c.DRY_RUN_OK));try{if(e.app){We(e.app);let n=`@nebutra/${e.app}`;e.quiet||S.log.info(h.cyan(`Starting dev server for ${h.bold(n)}...`));let r=await re("dev",{filter:n});process.exit(r.exitCode)}e.quiet||S.log.info(h.cyan("Starting dev servers for all apps..."));let t=await re("dev");process.exit(t.exitCode)}catch(t){t instanceof Error?S.log.error(h.red(t.message)):S.log.error(h.red("Unknown error occurred")),process.exit(c.ERROR)}}async function Yo(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:ae(e.format)}),process.exit(c.DRY_RUN_OK));try{if(e.app&&e.strict&&(S.log.error(h.red("Cannot use --strict with --app filter")),process.exit(c.INVALID_ARGS)),e.app){We(e.app);let r=`@nebutra/${e.app}`;e.quiet||S.log.info(h.cyan(`Building ${h.bold(r)}...`));let o=await re("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(h.cyan(r))}let n=await $(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?S.log.error(h.red(t.message)):S.log.error(h.red("Unknown error occurred")),process.exit(c.ERROR)}}async function Jo(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:ae(e.format)}),process.exit(c.DRY_RUN_OK));try{if(e.app&&e.fix&&(S.log.error(h.red("Cannot use --fix with --app filter (lint:fix is global only)")),process.exit(c.INVALID_ARGS)),e.app){We(e.app);let r=`@nebutra/${e.app}`;e.quiet||S.log.info(h.cyan(`Linting ${h.bold(r)}...`));let o=await O({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(h.cyan(r))}let n=await $(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?S.log.error(h.red(t.message)):S.log.error(h.red("Unknown error occurred")),process.exit(c.ERROR)}}async function qo(e){e.dryRun&&(I({mode:"dry-run",timestamp:new Date().toISOString(),command:"typecheck",options:{app:e.app},task:"turbo typecheck"},{format:ae(e.format)}),process.exit(c.DRY_RUN_OK));try{if(e.app){We(e.app);let n=`@nebutra/${e.app}`;e.quiet||S.log.info(h.cyan(`Type-checking ${h.bold(n)}...`));let r=await re("typecheck",{filter:n});process.exit(r.exitCode)}e.quiet||S.log.info(h.cyan("Type-checking all packages..."));let t=await re("typecheck");process.exit(t.exitCode)}catch(t){t instanceof Error?S.log.error(h.red(t.message)):S.log.error(h.red("Unknown error occurred")),process.exit(c.ERROR)}}var yn=e=>{e.command("dev").description("Start development servers (turbo dev)").option("--app <name>",`App to run: ${Se.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async(t,n)=>{let r=A(t,n);await Wo({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,quiet:!!r.quiet,verbose:!!r.verbose,format:ae(r.format)})}),e.command("build").description("Build all apps (pnpm build or turbo build)").option("--app <name>",`App to build: ${Se.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 Yo({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,strict:!!t.strict,quiet:!!r.quiet,verbose:!!r.verbose,format:ae(r.format)})}),e.command("lint").description("Lint with Biome (pnpm lint or pnpm lint:fix)").option("--app <name>",`App to lint: ${Se.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 Jo({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,fix:!!t.fix,quiet:!!r.quiet,verbose:!!r.verbose,format:ae(r.format)})}),e.command("typecheck").description("Type-check with TypeScript (turbo typecheck)").option("--app <name>",`App to typecheck: ${Se.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async(t,n)=>{let r=A(t,n);await qo({app:typeof t.app=="string"?t.app:void 0,dryRun:!!t.dryRun,quiet:!!r.quiet,verbose:!!r.verbose,format:ae(r.format)})})};import hn from"fs";import bn from"path";import vt from"picocolors";var wt=["smoke","golden","sleptons"],Ho={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 Zo(e,t={}){wt.includes(e)||(l.error(`Invalid suite: ${vt.red(e)}. Valid: ${wt.join(", ")}`),process.exit(c.INVALID_ARGS));let n=C(),{config:r,suiteDir:o}=Ho[e],i=bn.join(n,r),a=bn.join(n,o);(!hn.existsSync(a)||!hn.existsSync(i))&&(l.error(`Suite ${vt.red(e)} not found at ${o}.
|
|
146
158
|
Expected config: ${r}
|
|
147
159
|
Remediation: scaffold the suite (analogous to 'nebutra workflow init <provider>')
|
|
148
|
-
or restore the directory from git.`),process.exit(c.NOT_FOUND)),t.dryRun&&(
|
|
149
|
-
`)){if(o.trim().startsWith("#")||!o.trim())continue;let
|
|
150
|
-
`)){if(
|
|
151
|
-
`);e.dryRun?(u("Dry-run: would write .env.local","info"),
|
|
152
|
-
`),u(`Environment template written to ${r}`,"success"))}async function
|
|
160
|
+
or restore the directory from git.`),process.exit(c.NOT_FOUND)),t.dryRun&&(I({mode:"dry-run",command:`e2e ${e}`,task:`pnpm exec playwright test --config ${r}`}),process.exit(c.DRY_RUN_OK)),t.quiet||l.info(`Running Playwright suite: ${vt.bold(e)}`);let s=await O({command:"pnpm",args:["exec","playwright","test","--config",r],interactive:!0,label:`playwright ${e}`});process.exit(s.exitCode)}var vn=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=A(n,r);await Zo(t,{dryRun:!!n.dryRun,quiet:!!o.quiet})})};import{existsSync as Ye,readFileSync as wn,writeFileSync as Xo}from"fs";import{resolve as Je}from"path";import*as me from"@clack/prompts";import Qo from"picocolors";function qe(e){let t=Je(e,".env.example");if(!Ye(t))throw new Error(`.env.example not found at ${t}`);let n=wn(t,"utf-8"),r={};for(let o of n.split(`
|
|
161
|
+
`)){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 Ct(e){let t=Je(e,".env.local"),n=Je(e,".env"),r=Ye(t)?t:n;if(!Ye(r))return{};let o=wn(r,"utf-8"),i={};for(let a of o.split(`
|
|
162
|
+
`)){if(a.trim().startsWith("#")||!a.trim())continue;let s=a.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);if(s){let[,d,m]=s;i[d]=m.replace(/^["'](.*)["']$/,"$1")}}return i}function ei(e,t){return["SECRET","KEY","TOKEN","PASSWORD"].some(o=>e.toUpperCase().includes(o))&&t.length>0}function ti(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 ni(e){u("Validating environment variables...","info");let t=C(),n=qe(t),r=Ct(t),o=process.env,i={},a=!0;for(let s of Object.keys(n)){let d=s in r||s in o;i[s]=d,d||(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{Ke("Environment Validation");let s=Object.entries(i).filter(([,d])=>!d).map(([d])=>d);if(s.length===0)u("All required environment variables are set","success");else{u(`${s.length} missing environment variable(s):`,"warn");for(let d of s)u(` - ${d}`,"warn")}}a||process.exit(c.CONFIG_ERROR)}async function ri(e){let t=C(),n=qe(t),r=Je(t,".env.local");if(Ye(r)&&!e.yes&&!e.dryRun){let d=await me.confirm({message:".env.local already exists. Overwrite?",initialValue:!1});(me.isCancel(d)||!d)&&(u("Template generation cancelled","warn"),process.exit(c.CANCELLED))}u("Generating .env.local from template...","info");let o={},i=0,a=Object.keys(n).length;for(let[d,m]of Object.entries(n))if(i++,e.yes)o[d]=m;else if(process.stdin.isTTY===!0){let p=`[${i}/${a}] ${d}`,g=await me.text({message:p,defaultValue:m});me.isCancel(g)&&(u("Template generation cancelled","warn"),process.exit(c.CANCELLED)),o[d]=g}else o[d]=m;let s=Object.entries(o).map(([d,m])=>{let g=/[\s=]/.test(m)?`"${m}"`:m;return`${d}=${g}`}).join(`
|
|
163
|
+
`);e.dryRun?(u("Dry-run: would write .env.local","info"),b(s,{format:"plain"})):(Xo(r,s+`
|
|
164
|
+
`),u(`Environment template written to ${r}`,"success"))}async function oi(e){u("Comparing environment variables...","info");let t=C(),n=qe(t),r=Ct(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(Ke("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 ii(e){u("Reading environment variables...","info");let t=C(),n=Ct(t),r=qe(t),o={};for(let i of Object.keys(r))if(i in n){let a=n[i];o[i]=ei(i,a)?ti(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{Ke("Current Environment Variables");let i=Object.entries(o).map(([a,s])=>({Variable:Qo.cyan(a),Value:s}));b(i,{format:"table"}),u("Note: Sensitive values (KEY, TOKEN, SECRET, PASSWORD) are masked","info")}}function Cn(e){e.command("env <verb>").description("Manage environment variables and configuration").option("--dry-run","Show what would be done without making changes").option("--yes","Skip confirmations and use defaults").option("--format <type>","Output format: json, plain","plain").action(async(n,r)=>{let o=r.optsWithGlobals?.(),i={dryRun:r.dryRun||o?.dryRun,yes:r.yes||o?.yes,format:r.format||o?.format};try{switch(n){case"validate":await ni(i);break;case"template":await ri(i);break;case"diff":await oi(i);break;case"show":await ii(i);break;default:u(`Unknown env subcommand: ${n}. Valid commands: validate, template, diff, show`,"error"),process.exit(c.INVALID_ARGS)}}catch(a){let s=a instanceof Error?a.message:String(a);u(`Environment command failed: ${s}`,"error"),x("Full error",{error:a}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
153
165
|
Examples:
|
|
154
166
|
nebutra env validate Check all required vars are set
|
|
155
167
|
nebutra env template Generate .env.local from .env.example with prompts
|
|
@@ -166,7 +178,7 @@ Flags:
|
|
|
166
178
|
Sensitive Variables:
|
|
167
179
|
Variables with KEY, TOKEN, SECRET, PASSWORD in their names are masked in output.
|
|
168
180
|
This prevents accidental exposure of credentials.
|
|
169
|
-
`)}import{promises as
|
|
181
|
+
`)}import{promises as xn}from"fs";import{join as pe}from"path";import*as je from"@clack/prompts";import _ from"picocolors";var Pe=["design","iam","commerce","integrations","platform","ops","ai"];function ai(e){return Pe.includes(e)}function xt(e,t){if(!t)throw new Error(`--category is required. Valid categories: ${Pe.join(", ")}. Example: nebutra generate package ${e} --category iam`);if(!ai(t))throw new Error(`Unknown category "${t}". Valid categories: ${Pe.join(", ")}.`);return`packages/${t}/${e}`}function Sn(e){let t=e.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("");return{"package.json":`{
|
|
170
182
|
"name": "@nebutra/${e}",
|
|
171
183
|
"version": "0.0.0",
|
|
172
184
|
"private": true,
|
|
@@ -251,7 +263,7 @@ export default function RootLayout({
|
|
|
251
263
|
|
|
252
264
|
@theme {
|
|
253
265
|
--color-*: var(--neutral-*);
|
|
254
|
-
}`}}function
|
|
266
|
+
}`}}function En(e){return{"package.json":`{
|
|
255
267
|
"name": "@nebutra/${e}",
|
|
256
268
|
"version": "0.0.0",
|
|
257
269
|
"type": "module",
|
|
@@ -288,7 +300,7 @@ export default function RootLayout({
|
|
|
288
300
|
|
|
289
301
|
export function hello(name: string): string {
|
|
290
302
|
return \`Hello, \${name}!\`;
|
|
291
|
-
}`}}function
|
|
303
|
+
}`}}function Rn(e){let t=e.replace(/\//g,"_").replace(/-/g,"_");return{[`src/routes/${e}.ts`]:`import { Hono } from "hono";
|
|
292
304
|
import { z } from "zod";
|
|
293
305
|
|
|
294
306
|
type Env = {
|
|
@@ -339,7 +351,7 @@ app.post("/", async (c) => {
|
|
|
339
351
|
);
|
|
340
352
|
});
|
|
341
353
|
|
|
342
|
-
export default app;`}}function
|
|
354
|
+
export default app;`}}function kn(e){let t=e.replace(/([A-Z])/g,"-$1").toLowerCase().replace(/^-/,""),n=e.split("-").map(r=>r.charAt(0).toUpperCase()+r.slice(1)).join("");return{[`src/components/${t}.tsx`]:`import { cva, type VariantProps } from "class-variance-authority";
|
|
343
355
|
import { cn } from "@nebutra/ui/utils";
|
|
344
356
|
|
|
345
357
|
const ${t}Variants = cva(
|
|
@@ -413,13 +425,13 @@ export const AllSizes: Story = {
|
|
|
413
425
|
<${n} size="lg">Large</\${pascalCase}>
|
|
414
426
|
</div>
|
|
415
427
|
),
|
|
416
|
-
};`}}async function
|
|
417
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
418
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
419
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
420
|
-
`),process.exit(c.DRY_RUN_OK)}
|
|
421
|
-
`),r.push({app:
|
|
422
|
-
Average coverage: ${i
|
|
428
|
+
};`}}async function He(e,t,n){let r=C(),o=[],i={};switch(e){case"app":i=Sn(t);break;case"package":i=En(t);break;case"route":i=Rn(t);break;case"component":i=kn(t);break;default:throw new Error(`Unknown generate type: ${e}`)}let a=e==="package"?xt(t,n.category):void 0;for(let[s,d]of Object.entries(i)){let m=e==="app"?pe(r,"apps",t,s):e==="package"?pe(r,a??`packages/${t}`,s):pe(r,"backends/gateway",s);o.push({path:m,size:Buffer.byteLength(d)})}return o}async function Ze(e,t,n={}){let r=C(),o=[],i={};switch(e){case"app":i=Sn(t);break;case"package":i=En(t);break;case"route":i=Rn(t);break;case"component":i=kn(t);break;default:throw new Error(`Unknown generate type: ${e}`)}let a=e==="package"?xt(t,n.category):void 0;for(let[s,d]of Object.entries(i)){let m;e==="app"?m=pe(r,"apps",t,s):e==="package"?m=pe(r,a??`packages/${t}`,s):e==="component"?m=pe(r,"packages/design/ui",s):m=pe(r,"backends/gateway",s);let p=m.substring(0,m.lastIndexOf("/"));await xn.mkdir(p,{recursive:!0}),await xn.writeFile(m,d,"utf-8"),o.push(m)}return o}async function si(e,t){if(t.dryRun){let r=await He("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)+`
|
|
429
|
+
`),process.exit(c.DRY_RUN_OK)}je.intro(_.bgCyan(_.black(" nebutra generate app ")));let n=l.spinner();try{n.start(`Generating app ${_.cyan(e)}...`);let r=await Ze("app",e);n.stop(`Created app ${_.cyan(e)}`,0),l.success(`App created with ${r.length} files`),l.info("Next steps:"),l.info(` cd apps/${e}`),l.info(" pnpm install"),l.info(" pnpm run dev"),process.exit(c.SUCCESS)}catch(r){n.stop(`Failed to generate app: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(c.ERROR)}}async function ci(e,t){let n;try{n=xt(e,t.category)}catch(o){l.error(o instanceof Error?o.message:String(o)),process.exit(c.INVALID_ARGS)}if(t.dryRun){let o=await He("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)+`
|
|
430
|
+
`),process.exit(c.DRY_RUN_OK)}je.intro(_.bgCyan(_.black(" nebutra generate package ")));let r=l.spinner();try{r.start(`Generating package ${_.cyan(e)} in ${_.dim(n)}...`);let o=await Ze("package",e,{category:t.category});r.stop(`Created package ${_.cyan(e)}`,0),l.success(`Package created with ${o.length} files`),l.info("Next steps:"),l.info(` Edit ${n}/src/index.ts`),l.info(` Run: pnpm --filter @nebutra/${e} build`),process.exit(c.SUCCESS)}catch(o){r.stop(`Failed to generate package: ${o instanceof Error?o.message:"Unknown error"}`,1),process.exit(c.ERROR)}}async function li(e,t){if(t.dryRun){let r=await He("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)+`
|
|
431
|
+
`),process.exit(c.DRY_RUN_OK)}je.intro(_.bgCyan(_.black(" nebutra generate route ")));let n=l.spinner();try{n.start(`Generating route ${_.cyan(e)}...`);let r=await Ze("route",e);n.stop(`Created route ${_.cyan(e)}`,0),l.success(`Route created with ${r.length} files`),l.info(`File: backends/gateway/src/routes/${e}.ts`),l.info(`Add to your main app.ts: app.route("/${e}", routes)`),process.exit(c.SUCCESS)}catch(r){n.stop(`Failed to generate route: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(c.ERROR)}}async function ui(e,t){if(t.dryRun){let r=await He("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)+`
|
|
432
|
+
`),process.exit(c.DRY_RUN_OK)}je.intro(_.bgCyan(_.black(" nebutra generate component ")));let n=l.spinner();try{n.start(`Generating component ${_.cyan(e)}...`);let r=await Ze("component",e);n.stop(`Created component ${_.cyan(e)}`,0),l.success(`Component created with ${r.length} files`),l.info("Files created in packages/design/ui/src/components/"),l.info("Next steps:"),l.info(" 1. Add export to packages/design/ui/src/components/index.ts"),l.info(" 2. pnpm --filter @nebutra/ui typecheck"),l.info(" 3. pnpm --filter @nebutra/storybook dev"),process.exit(c.SUCCESS)}catch(r){n.stop(`Failed to generate component: ${r instanceof Error?r.message:"Unknown error"}`,1),process.exit(c.ERROR)}}var On=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 si(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: ${Pe.join(", ")}`).requiredOption("--category <category>",`Package category: ${Pe.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 ci(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 li(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 ui(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 St,{lstatSync as di,readdirSync as Et,readFileSync as mi,writeFileSync as pi}from"fs";import X from"path";import{fileURLToPath as fi}from"url";import U from"picocolors";var gi=X.dirname(fi(import.meta.url));function Rt(){let e=new Map,t=X.join(gi,"../../../apps");if(!St.existsSync(t))return e;try{let n=Et(t);for(let r of n){let o=X.join(t,r),i=X.join(o,"messages");St.existsSync(i)&&di(i).isDirectory()&&e.set(r,i)}}catch(n){l.debug(`Error scanning apps directory: ${n instanceof Error?n.message:String(n)}`)}return e}function $e(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=$e(o,i);for(let s of a)n.add(s)}else n.add(i)}return n}function Te(e){try{let t=mi(e,"utf-8");return JSON.parse(t)}catch(t){return l.debug(`Failed to parse ${e}: ${t instanceof Error?t.message:String(t)}`),null}}async function yi(e){if(l.info("Syncing language files across the project..."),(await $("translate",{filter:"landing",dryRun:e.dryRun,interactive:e.interactive??!0})).exitCode!==0){l.warn("Landing page translate script not found or failed. Trying sync-lang.mjs...");let n=await O({command:"node",args:["scripts/sync-lang.mjs"],dryRun:e.dryRun,interactive:e.interactive??!0,label:"Sync language files"});n.exitCode!==0&&(l.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||l.success("Language files synced successfully")}else e.format==="json"?console.log(JSON.stringify({command:"i18n:sync",exitCode:0,success:!0},null,2)):e.dryRun||l.success("Language files synced successfully")}async function hi(e){l.info("Validating translation files...");let t=Rt();t.size===0&&(l.warn("No message directories found in the project"),process.exit(c.NOT_FOUND));let n=new Map,r=!1;for(let[o,i]of t){let a=[];try{let s=Et(i).filter(g=>g.endsWith(".json"));if(s.length===0)continue;let d=X.join(i,"en.json"),m=Te(d);if(!m){l.warn(`Could not load reference locale en.json from ${o}`);continue}let p=$e(m);for(let g of s){if(g==="en.json")continue;let y=g.replace(".json",""),f=X.join(i,g),v=Te(f);if(!v){l.debug(`Could not parse ${g}`);continue}let T=$e(v),w=Array.from(p).filter(F=>!T.has(F)),z=Array.from(T).filter(F=>!p.has(F)),Q=p.size>0?(p.size-w.length)/p.size*100:100;(w.length>0||z.length>0)&&(r=!0),a.push({locale:y,totalKeys:p.size,missingKeys:w,extraKeys:z,coverage:Math.round(Q*100)/100})}a.length>0&&n.set(o,a)}catch(s){l.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){l.info(`${U.bold(o)}:`);for(let a of i){let d=(a.coverage>=100?U.green:a.coverage>=80?U.yellow:U.red)(`${a.coverage}%`);l.info(` ${U.cyan(a.locale.padEnd(5))} ${d.padEnd(20)} [${a.totalKeys} keys]`),a.missingKeys.length>0&&l.warn(` Missing: ${a.missingKeys.slice(0,5).join(", ")}${a.missingKeys.length>5?` (+ ${a.missingKeys.length-5} more)`:""}`),a.extraKeys.length>0&&l.warn(` Extra: ${a.extraKeys.slice(0,5).join(", ")}${a.extraKeys.length>5?` (+ ${a.extraKeys.length-5} more)`:""}`)}}r&&process.exit(c.CONFLICT)}async function bi(e,t){/^[a-z]{2}(-[A-Z]{2})?$/.test(e)||(l.error(`Invalid locale format: ${e}. Use 'en', 'es', 'zh-CN', etc.`),process.exit(c.INVALID_ARGS));let n=Rt();n.size===0&&(l.error("No message directories found in the project"),process.exit(c.NOT_FOUND));let r=[],o=!1;for(let[a,s]of n){let d=X.join(s,`${e}.json`);if(St.existsSync(d)){l.warn(`Locale ${e} already exists in ${a}`);continue}try{let y=function(f){for(let v in f)typeof f[v]=="string"?f[v]="":typeof f[v]=="object"&&f[v]!==null&&y(f[v])};var i=y;let m=X.join(s,"en.json"),p=Te(m);if(!p){l.warn(`Could not load reference en.json from ${a}, skipping...`);continue}let g=JSON.parse(JSON.stringify(p));y(g),t.dryRun?l.info(`Would create ${e} locale in ${a}`):(pi(d,JSON.stringify(g,null,2)+`
|
|
433
|
+
`),r.push({app:a,path:d}),l.success(`Created ${e} locale in ${a}`))}catch(m){l.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||l.success(`Locale '${e}' added to ${r.length} app(s)`),o&&process.exit(c.ERROR)}async function vi(e){l.info("Checking translation coverage...");let t=Rt();t.size===0&&(l.warn("No message directories found in the project"),process.exit(c.NOT_FOUND));let n=new Map,r=0,o=0;for(let[i,a]of t){let s=[];try{let d=Et(a).filter(y=>y.endsWith(".json"));if(d.length===0)continue;let m=X.join(a,"en.json"),p=Te(m);if(!p){l.debug(`Could not load reference locale from ${i}`);continue}let g=$e(p);for(let y of d){let f=y.replace(".json",""),v=X.join(a,y),T=Te(v);if(!T)continue;let w=$e(T),z=Array.from(w).filter(we=>{let Be=T[we.split(".")[0]];return Be!==void 0&&Be!==""}).length,Q=g.size-z,F=g.size>0?z/g.size*100:0;s.push({locale:f,coverage:Math.round(F*100)/100,totalKeys:g.size,translatedKeys:z,missingKeys:Q})}s.length>0&&(n.set(i,s),o++,r+=s.reduce((y,f)=>y+f.coverage,0)/s.length)}catch(d){l.debug(`Error checking status for ${i}: ${d instanceof Error?d.message:String(d)}`)}}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,d])=>(a[s]=d,a),{})};console.log(JSON.stringify(i,null,2))}else{for(let[i,a]of n){l.info(`${U.bold(i)}:`);for(let s of a){let d="",p=Math.round(s.coverage/100*20);d="\u2588".repeat(p)+"\u2591".repeat(20-p);let y=(s.coverage>=100?U.green:s.coverage>=80?U.yellow:U.red)(`${s.coverage.toFixed(1)}%`);l.info(` ${U.cyan(s.locale.padEnd(5))} [${d}] ${y}`),l.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;l.info(`
|
|
434
|
+
Average coverage: ${a(i.toFixed(1)+"%")}`)}}}function An(e){let t=e.command("i18n").description("Manage internationalization and translations");t.command("sync").description("Synchronize language translations across the project").option("--dry-run","Preview changes without executing").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await yi({dryRun:n.dryRun||!1,format:n.format||r.format,interactive:r.yes!==!0})}),t.command("validate").description("Validate all locale files for missing or extra keys").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await hi({format:n.format||r.format,interactive:r.yes!==!0})}),t.command("add <locale>").description("Add a new locale with template files (e.g. 'en', 'es', 'zh-CN')").option("--dry-run","Preview what would be created without writing files").option("--format <type>","Output format: json or plain").action(async(n,r)=>{let o=r.optsWithGlobals?.()||r;await bi(n,{dryRun:r.dryRun||!1,format:r.format||o.format,interactive:o.yes!==!0})}),t.command("status").description("Show translation coverage per locale and app").option("--format <type>","Output format: json or plain").action(async n=>{let r=n.optsWithGlobals?.()||n;await vi({format:n.format||r.format,interactive:r.yes!==!0})})}import*as Xe from"@clack/prompts";async function wi(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 Ce("up -d",{file:t,profile:e.profile,dryRun:e.dryRun});r.exitCode===0?u("Infrastructure stack started successfully","success"):(u("Failed to start infrastructure stack","error"),x("Docker compose output",{stderr:r.stderr}),process.exit(r.exitCode||1))}async function Ci(e){u("Stopping infrastructure stack...","info");let t=await Ce("down",{dryRun:e.dryRun});t.exitCode===0?u("Infrastructure stack stopped","success"):(u("Failed to stop infrastructure stack","error"),x("Docker compose output",{stderr:t.stderr}),process.exit(t.exitCode||1))}async function xi(e){u("Checking infrastructure status...","info");let t=await Ce("ps --format json",{dryRun:e.dryRun});if(t.exitCode===0)try{let n=JSON.parse(t.stdout);if(e.format==="json")b(n,{format:"json"});else{let r=n.map(o=>({name:o.Name,status:o.Status,ports:o.Ports||"-"}));b(r,{format:"table"})}}catch{b(t.stdout,{format:"plain"})}else u("Failed to check infrastructure status","error"),x("Docker compose output",{stderr:t.stderr}),process.exit(t.exitCode||1)}async function Si(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 Ce(o.join(" "),{dryRun:n.dryRun});i.exitCode===0?b(i.stdout,{format:"plain"}):(u("Failed to fetch logs","error"),x("Docker compose output",{stderr:i.stderr}),process.exit(i.exitCode||1))}async function Ei(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 infrastructure? This will remove all containers and volumes.",initialValue:!1});(Xe.isCancel(r)||!r)&&(u("Reset cancelled","warn"),process.exit(c.CANCELLED))}else u("Infrastructure reset requires explicit --yes confirmation (use: nebutra infra reset --yes)","error"),process.exit(c.INVALID_ARGS);u("Resetting infrastructure stack...","warn");let n=await Ce("down -v",{dryRun:e.dryRun});n.exitCode===0?u("Infrastructure reset successfully","success"):(u("Failed to reset infrastructure","error"),x("Docker compose output",{stderr:n.stderr}),process.exit(n.exitCode||1))}function _n(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)=>{let i=o.optsWithGlobals?.(),a={dryRun:o.dryRun||i?.dryRun,yes:o.yes||i?.yes,format:o.format||i?.format,lite:o.lite||!1,profile:o.profile};try{switch(n){case"up":await wi(a);break;case"down":await Ci(a);break;case"status":await xi(a);break;case"logs":await Si(r,a);break;case"reset":await Ei(a);break;default:u(`Unknown infra subcommand: ${n}. Valid commands: up, down, status, logs, reset`,"error"),process.exit(c.INVALID_ARGS)}}catch(s){let d=s instanceof Error?s.message:String(s);u(`Infrastructure command failed: ${d}`,"error"),x("Full error",{error:s}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
423
435
|
Examples:
|
|
424
436
|
nebutra infra up Start full stack
|
|
425
437
|
nebutra infra up --lite Start lite stack (PostgreSQL + Redis)
|
|
@@ -436,14 +448,19 @@ Flags:
|
|
|
436
448
|
--lite Use docker-compose.lite.yml (PostgreSQL + Redis only)
|
|
437
449
|
--profile <name> Enable Docker Compose profile (search, notifications, etc.)
|
|
438
450
|
--format <type> Output format: json, plain (default: plain, auto-json in non-TTY)
|
|
439
|
-
`)}import
|
|
440
|
-
`),o?(l.success("Configuration saved to nebutra.config.json"),l.info('Initialization complete! Run "nebutra status" to check capability readiness.')):(await new Promise(
|
|
441
|
-
`)}async function
|
|
442
|
-
|
|
451
|
+
`)}import kt from"fs";import Ri from"path";import*as M from"@clack/prompts";import Qe from"picocolors";var ki=["auth","billing","email","storage","queue","cache","notifications","webhooks","ai","mcp"],Oi={stack:"sailor-2026-09",capabilities:[...ki]};function Ai(){return process.stdout.isTTY===!0}function _i(e){return e.yes===!0||e.dryRun===!0||!Ai()}function Ii(e,t,n){return{operation:n?"update":"create",path:e,...n&&{before:n},after:t}}async function In(e={}){let t=process.cwd(),n=Ri.join(t,"nebutra.config.json"),r=kt.existsSync(n);e.ifNotExists&&r&&(l.info("nebutra.config.json already exists, skipping initialization."),process.exit(c.SUCCESS));let o=_i(e);o||M.intro(Qe.bgCyan(Qe.black(" nebutra init "))),r&&!o&&(M.log.warn("nebutra.config.json already exists in this directory."),await M.confirm({message:"Do you want to overwrite it?",initialValue:!1})||(M.outro("Operation aborted."),process.exit(c.CANCELLED)));let i={...Oi};if(e.dryRun){let a=r?JSON.parse(kt.readFileSync(n,"utf-8")):void 0,s=Ii(n,i,a);I(s),u("Dry-run completed. No changes made."),process.exit(c.DRY_RUN_OK)}o?l.info("Initializing Nebutra CLI configuration..."):M.spinner().start("Initializing Nebutra CLI configuration..."),kt.writeFileSync(n,JSON.stringify(i,null,2)+`
|
|
452
|
+
`),o?(l.success("Configuration saved to nebutra.config.json"),l.info('Initialization complete! Run "nebutra status" to check capability readiness.')):(await new Promise(a=>setTimeout(a,800)),M.spinner().stop(Qe.green("Configuration saved to nebutra.config.json")),M.outro(Qe.cyan('Initialization complete! Run "nebutra status" to check capability readiness.'))),process.exit(c.SUCCESS)}import Ot from"fs/promises";import $i from"os";import At from"path";import*as E from"@clack/prompts";import G from"picocolors";var Ni="https://analytics.nebutra.com";function Pi(e={}){if(e.noTelemetry===!0)return!0;let t=process.env.NEBUTRA_TELEMETRY;return t==="0"||t==="false"}function ji(){return process.env.npm_package_version??"0.0.0-dev"}function et(e,t={}){Pi(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??Ni},onError:()=>{}});if(typeof r?.track!="function")return;let o=r.track("license.cli",{action:e.action,cli_version:ji(),...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 Nn=process.env.NEBUTRA_LICENSE_API_URL??"https://nebutra.com";async function Pn(){let e=$i.homedir(),t=At.join(e,".config","nebutra");try{await Ot.mkdir(t,{recursive:!0})}catch{}return t}async function Ti(e){let t=`${Nn}/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 (${Nn}). 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 Di(e){let t=e instanceof Error?e.message:String(e);return/Invalid license key format/i.test(t)?"invalid_format":/license server/i.test(t)?"network_error":/not found/i.test(t)?"not_found":/expired/i.test(t)?"expired":/not valid/i.test(t)?"invalid_key":/HTTP \d+/i.test(t)?"http_error":"unknown"}async function Li(e,t){let n=t.quiet===!0;n||E.intro(G.bgCyan(G.black(" Nebutra License Activation "))),et({action:"activate_attempted"});try{if(e.length<10)throw new Error("Invalid license key format.");n||E.log.step("Validating license key with Nebutra...");let{tier:r,type:o}=await Ti(e),i=await Pn(),a=At.join(i,"license.json");await Ot.writeFile(a,JSON.stringify({licenseKey:e,tier:r,type:o,activatedAt:new Date().toISOString()},null,2),"utf-8"),n||(E.log.success(G.green(`${r} license activated and saved to ${a}`)),E.outro(G.cyan("Premium CLI capabilities are now unlocked!"))),et({action:"activated",tier:r,type:o})}catch(r){et({action:"failed",error_code:Di(r)}),n||(E.log.error(G.red("Activation failed")),l.error(r instanceof Error?r.message:String(r)),E.outro("Please check your key and try again.")),process.exit(c.ERROR)}}async function Fi(e){let t=e.quiet===!0;t||E.intro(G.bgCyan(G.black(" Nebutra License Status ")));try{let n=await Pn(),r=At.join(n,"license.json"),o=await Ot.readFile(r,"utf-8"),i=JSON.parse(o);if(i.licenseKey)t||(E.log.success(G.green("License is active!")),E.log.info(`Key: ${i.licenseKey.substring(0,8)}...`),i.tier&&E.log.info(`Tier: ${G.bold(i.tier)}`),i.type&&E.log.info(`Type: ${i.type}`),E.log.info(`Activated: ${new Date(i.activatedAt).toLocaleString()}`),E.outro(G.cyan("You are ready to use premium features.")));else throw new Error("License key not found in config.")}catch{t||(E.log.warn(G.yellow("No active license found locally.")),E.log.message(G.dim("Run `nebutra license activate <key>` to unlock premium features.")),E.outro("")),process.exit(c.NOT_FOUND)}}function jn(e){let t=e.command("license").description("Manage your Nebutra-Sailor commercial license");t.command("activate <key>").description("Activate a commercial license key for local development").option("--quiet","Suppress output").action(async(n,r)=>{await Li(n,r)}),t.command("status").description("Check the locally configured license status").option("--quiet","Suppress output").action(async n=>{await Fi(n)})}import{existsSync as $n,readFileSync as Ui,writeFileSync as Bi}from"fs";import{dirname as Mi,join as Tn,parse as Gi}from"path";import*as tt from"@clack/prompts";function Vi(e){let t=e,{root:n}=Gi(t);for(;;){if($n(Tn(t,"package.json")))return t;if(t===n)return null;let r=Mi(t);if(r===t)return null;t=r}}function Ki(e){if(!$n(e))return{};try{let t=JSON.parse(Ui(e,"utf-8"));return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return{}}}function zi(e,t){Bi(e,JSON.stringify(t,null,2)+`
|
|
453
|
+
`)}async function Wi(e,t){let n=t.format==="json",r=Vi(process.cwd());r||(n?console.log(JSON.stringify({command:"link",success:!1,error:"No package.json found upstream"},null,2)):l.error("No package.json found in current or parent directories."),process.exit(c.NOT_FOUND));let o=e;if(!o){t.yes&&(n?console.log(JSON.stringify({command:"link",success:!1,error:"project-id required in --yes mode"},null,2)):l.error("project-id is required when running with --yes / non-interactive."),process.exit(c.INVALID_ARGS));let d=await tt.text({message:"Project ID to link to:",placeholder:"proj_abc123",validate:m=>m&&m.trim().length>0?void 0:"Project ID cannot be empty"});tt.isCancel(d)&&(l.warn("Cancelled"),process.exit(c.CANCELLED)),o=String(d).trim()}let i=Tn(r,"nebutra.config.json"),s={...Ki(i),projectId:o};zi(i,s),n?console.log(JSON.stringify({command:"link",success:!0,projectId:o,configPath:i},null,2)):l.success(`Linked to project: ${o}`)}function Dn(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 Wi(t,{yes:o.yes||!1,format:n.format||o.format})})}import{spawn as ia}from"child_process";import{platform as aa}from"os";var B={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",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 Ji}from"child_process";import{chmodSync as qi,existsSync as ke,readFileSync as Fn,rmSync as Hi,unlinkSync as _t,writeFileSync as Un}from"fs";import{mkdir as Zi}from"fs/promises";import{platform as fe}from"os";import{join as Bn}from"path";import{promisify as Xi}from"util";import{homedir as Ln,platform as Yi}from"os";import{join as nt}from"path";function Ee(){if(Yi()==="win32"){let t=process.env.LOCALAPPDATA||nt(Ln(),"AppData","Local");return nt(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?nt(e,"nebutra"):nt(Ln(),".config","nebutra")}var ge=Xi(Ji),ye="nebutra-cli",Oe="default";function Re(){return Bn(Ee(),"credentials.json")}function rt(){return Bn(Ee(),"device-pending.json")}async function Mn(){await Zi(Ee(),{recursive:!0})}async function It(e){try{return await ge(fe()==="win32"?"where":"which",[e]),!0}catch{return!1}}function Nt(){return process.env.NEBUTRA_NO_KEYCHAIN==="1"}async function Qi(e){if(Nt())return!1;try{if(fe()==="darwin")return await ge("security",["add-generic-password","-a",Oe,"-s",ye,"-w",e,"-U"]),!0;if(fe()==="linux"&&await It("secret-tool"))return await ge("secret-tool",["store","--label",`${B.name} CLI credentials`,"service",ye,"account",Oe]),!0}catch{}return!1}async function Gn(){if(Nt())return null;try{if(fe()==="darwin"){let{stdout:e}=await ge("security",["find-generic-password","-a",Oe,"-s",ye,"-w"]);return e.trim()||null}if(fe()==="linux"&&await It("secret-tool")){let{stdout:e}=await ge("secret-tool",["lookup","service",ye,"account",Oe]);return e.trim()||null}}catch{}return null}async function ea(){if(Nt())return!1;let e=!1;try{fe()==="darwin"?(await ge("security",["delete-generic-password","-a",Oe,"-s",ye]),e=!0):fe()==="linux"&&await It("secret-tool")&&(await ge("secret-tool",["clear","service",ye,"account",Oe]),e=!0)}catch{}return e}async function Vn(e){await Mn();let t=JSON.stringify(e);if(await Qi(t)){try{ke(Re())&&_t(Re())}catch{}return{storedIn:"keychain"}}Un(Re(),`${t}
|
|
454
|
+
`,{mode:384});try{qi(Re(),384)}catch{}return{storedIn:"file"}}async function ta(){let e=await Gn();if(e)try{return JSON.parse(e)}catch{return null}let t=Re();if(!ke(t))return null;try{return JSON.parse(Fn(t,"utf-8"))}catch{return null}}async function Kn(){let e=[];await ea()&&e.push(`keychain:${ye}`);let t=Re();if(ke(t))try{_t(t),e.push(t)}catch{}let n=rt();if(ke(n))try{Hi(n,{force:!0}),e.push(n)}catch{}return e}async function Pt(e){await Mn(),Un(rt(),`${JSON.stringify(e)}
|
|
455
|
+
`,{mode:384})}function zn(){let e=rt();if(!ke(e))return null;try{return JSON.parse(Fn(e,"utf-8"))}catch{return null}}function ot(){let e=rt();if(ke(e))try{_t(e)}catch{}}async function Wn(){let e=process.env.NEBUTRA_TOKEN?.trim();if(e)return{token:e,source:"env",expired:!1};let t=await ta();if(!t)return null;let n=!!t.expiresAt&&new Date(t.expiresAt).getTime()<=Date.now(),r=await Gn()!==null;return{token:t.accessToken,source:r?"keychain":"file",expired:n}}var na={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",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 it(e){let t=na.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 Yn="nebutra-cli";function at(){let e=process.env.NEBUTRA_AUTH_URL?.trim();return e?e.replace(/\/+$/,""):it("auth")}var te=class extends Error{constructor(n,r){super(r);this.code=n;this.name="DeviceAuthError"}code};async function jt(e){try{return await e.json()}catch{return{}}}async function Jn(e,t,n,r={}){let o=r.attempts??3,i=r.sleep??Hn,a;for(let s=1;s<=o;s++){try{let d=await e(t,{...n,signal:AbortSignal.timeout(r.timeoutMs??1e4)});if(d.status<500||s===o)return d;a=new Error(`HTTP ${d.status}`)}catch(d){if(!Zn(d)||s===o)throw d;a=d}await i(1e3*s)}throw a}async function qn(e,t=fetch){let n=await Jn(t,`${e}/api/auth/device/code`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({client_id:Yn})}),r=await jt(n);if(!n.ok)throw new te(typeof r.error=="string"?r.error:"request_failed",typeof r.error_description=="string"?r.error_description:`Could not reach ${B.name}'s auth center (${n.status}).`);return r}async function ra(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:Yn}),signal:AbortSignal.timeout(1e4)}),o=await jt(r);if(!r.ok)throw new te(typeof o.error=="string"?o.error:"request_failed",typeof o.error_description=="string"?o.error_description:`HTTP ${r.status}`);return o}var Hn=e=>new Promise(t=>setTimeout(t,e)),oa=30;function Zn(e){if(e instanceof te)return e.code==="request_failed";if(e instanceof TypeError)return!0;let t=e?.name;return t==="TimeoutError"||t==="AbortError"}async function Xn(e,t,n){let r=n.fetchImpl??fetch,o=n.sleep??Hn,i=n.intervalSeconds;for(;;){if(Date.now()>=n.deadline)throw new te("expired_token","The device code expired before it was approved.");n.onWaiting?.(i),await o(i*1e3);try{return await ra(e,t,r)}catch(a){if(Zn(a)){i=Math.min(i+5,oa),n.onTransientFailure?.(a);continue}if(!(a instanceof te))throw a;if(a.code==="authorization_pending")continue;if(a.code==="slow_down"){i+=5;continue}throw a}}}async function st(e,t,n=fetch){let r=await Jn(n,`${e}/api/auth/get-session`,{method:"GET",headers:{authorization:`Bearer ${t}`}});if(!r.ok)return null;let i=(await jt(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 sa(e,t){return!!e.json||e.format==="json"||t==="json"}function ca(e){try{let t=aa(),[n,r]=t==="darwin"?["open",[e]]:t==="win32"?["cmd",["/c","start","",e]]:["xdg-open",[e]],o=ia(n,r,{stdio:"ignore",detached:!0});o.on("error",()=>{}),o.unref()}catch{}}async function la(e,t){let n=new Date;await Vn({accessToken:t.access_token,tokenType:t.token_type,expiresAt:new Date(n.getTime()+t.expires_in*1e3).toISOString(),scope:t.scope,obtainedAt:n.toISOString()}),ot()}async function Qn(e,t,n){let r=0,o=await Xn(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(`
|
|
456
|
+
`),await la(e,o);let i=await st(e,o.access_token);return i?{userId:i.userId,email:i.email}:null}async function ua(e,t){let n=sa(e,t.format),r=at();if(e.poll){let s=zn();if(s&&s.expiresAt>Date.now()&&s.baseUrl===r)try{let d=await Qn(r,{deviceCode:s.deviceCode,interval:s.interval,expiresAt:s.expiresAt},n);n?console.log(JSON.stringify({command:"login",success:!0,...d},null,2)):l.success(d?.email?`Logged in as ${d.email}`:`Logged in to ${B.name}.`);return}catch(d){return ot(),$t(d,n)}}let o;try{o=await qn(r)}catch(s){return $t(s,n)}let i=Date.now()+o.expires_in*1e3;if(n&&!e.poll){await Pt({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||(l.info(`First, confirm this code matches what's shown in the browser:
|
|
457
|
+
`),l.info(` ${o.user_code}
|
|
458
|
+
`),l.info(`Visiting: ${o.verification_uri_complete}`)),e.browser!==!1&&process.stdout.isTTY!==!1&&!t.yes&&ca(o.verification_uri_complete);try{await Pt({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 Qn(r,{deviceCode:o.device_code,interval:o.interval,expiresAt:i},n);n?console.log(JSON.stringify({command:"login",success:!0,...s},null,2)):l.success(s?.email?`Logged in as ${s.email}`:`Logged in to ${B.name}.`)}catch(s){return ot(),$t(s,n)}}function $t(e,t){let n=e instanceof te?e.code:"login_failed",r=e instanceof Error?e.message:String(e),o=n==="access_denied"?c.PERMISSION_DENIED:n==="expired_token"?c.TIMEOUT:c.NETWORK_ERROR;t?console.log(JSON.stringify({command:"login",success:!1,error:n,message:r},null,2)):l.error(r),process.exit(o)}function er(e){e.command("login").description(`Authorize this machine with your ${B.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 ua(t,{format:r.format,yes:r.yes})})}import{existsSync as tr,rmSync as da,unlinkSync as ma}from"fs";import{join as pa}from"path";var fa=["auth.json","session.json","token","credentials.json"];async function ga(e){let t=Ee(),n=e.format==="json",r=0,o=[],i=await Kn();if(r+=i.length,o.push(...i),e.all){if(tr(t))try{da(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)):l.error(`Failed to clear config directory: ${s}`),process.exit(c.PERMISSION_DENIED)}}else for(let a of fa){let s=pa(t,a);if(tr(s))try{ma(s),r++,o.push(s)}catch{}}r===0&&(n?console.log(JSON.stringify({command:"logout",success:!0,alreadyLoggedOut:!0,removed:[]},null,2)):l.info("Already logged out"),process.exit(c.SUCCESS)),n?console.log(JSON.stringify({command:"logout",success:!0,removed:o,clearedAll:!!e.all},null,2)):l.success(e.all?"Cleared all nebutra config":"Logged out of nebutra.com")}function nr(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 ga({all:t.all||!1,format:t.format||r.format})})}import{spawn as ya}from"child_process";import{dirname as ha,resolve as ba}from"path";import{fileURLToPath as va}from"url";import*as J from"@clack/prompts";import ne from"picocolors";var wa=ha(va(import.meta.url));function Ca(){let e=[()=>ba(wa,"../../../mcp/dist/server/contextServer.js")];for(let t of e)try{return t()}catch(n){l.debug("MCP server binary resolution strategy failed",{error:n})}return"nebutra-mcp"}function rr(e){e.command("mcp").description("Start the Nebutra MCP server for Cursor/Windsurf").option("--stdio","Use stdio transport (default)",!0).action(async t=>{J.intro(ne.bgCyan(ne.black(" nebutra mcp ")));try{let n=Ca();J.log.info(ne.cyan("Starting Nebutra MCP server for Cursor/Windsurf integration...")),J.log.info(ne.dim(`Press Ctrl+C to stop the server.
|
|
459
|
+
`));let r=ya(process.execPath,[n],{stdio:"inherit",env:{...process.env}});r.on("close",o=>{o===0?J.outro(ne.green("MCP server stopped gracefully.")):o===null&&J.outro(ne.yellow("MCP server terminated.")),process.exit(o??0)}),r.on("error",o=>{l.error(`
|
|
443
460
|
Failed to start MCP server: ${o.message}`),o.code==="ENOENT"&&(l.warn(`
|
|
444
|
-
Tip: Ensure @nebutra/mcp is installed or available in your PATH.`),l.info(" Install globally: npm install -g @nebutra/mcp"),process.exit(c.NOT_FOUND)),process.exit(c.ERROR)}),process.on("SIGINT",()=>{
|
|
445
|
-
Shutting down MCP server...`)),r.kill("SIGINT")}),process.on("SIGTERM",()=>{
|
|
446
|
-
`)}function
|
|
461
|
+
Tip: Ensure @nebutra/mcp is installed or available in your PATH.`),l.info(" Install globally: npm install -g @nebutra/mcp"),process.exit(c.NOT_FOUND)),process.exit(c.ERROR)}),process.on("SIGINT",()=>{J.log.info(ne.yellow(`
|
|
462
|
+
Shutting down MCP server...`)),r.kill("SIGINT")}),process.on("SIGTERM",()=>{J.log.info(ne.yellow("Terminating MCP server...")),r.kill("SIGTERM")})}catch(n){J.log.error(ne.red("Error: Unable to start MCP server. Ensure @nebutra/mcp is properly installed.")),n instanceof Error&&l.error(n.message),process.exit(c.ERROR)}})}import*as N from"@clack/prompts";import V from"picocolors";var ir=Object.fromEntries(Object.entries(Zt).map(([e,t])=>[e,t]));function ar(e){return typeof e=="boolean"?"boolean":"string"}function sr(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:ar(n.default)})),examples:e.examples||[]}}function xa(e){let n=(H.subcommands||[]).find(r=>r.name===e);return n?sr(n):null}function Sa(){return(H.options??[]).map(e=>({flags:e.flags,description:e.description,required:!1,type:ar(e.default)}))}function Ea(e){let t=H.subcommands||[];return{name:H.name,version:e,description:H.description,usage:H.usage||"nebutra [command] [options]",globalOptions:Sa(),commands:t.map(sr),exitCodes:ir,timestamp:new Date().toISOString()}}function or(){return(H.subcommands||[]).map(t=>t.name)}function ct(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
463
|
+
`)}function Ra(e){return e instanceof Error&&e.message.startsWith("EXIT:")}async function ka(e,t){let n=t.quiet===!0,r="0.1.0";try{if(t.all===!0&&(ct(Ea(r)),process.exit(0)),t.list===!0&&(ct(or()),process.exit(0)),(t.exitCodes===!0||t["exit-codes"]===!0)&&(ct(ir),process.exit(0)),e){let o=xa(e);o||(n||(l.error(`Unknown command '${e}'`),l.info(`Available commands: ${or().join(", ")}`)),process.exit(c.NOT_FOUND)),ct(o),process.exit(0)}n||(N.intro(V.bgCyan(V.black(" nebutra schema "))),N.log.info(V.cyan("Agent-friendly introspection of CLI commands, arguments, and value domains.")),N.log.message(""),N.log.info(V.bold("Usage:")),N.log.message(V.dim(" nebutra schema --all Show full command tree as JSON")),N.log.message(V.dim(" nebutra schema <command> Show schema for a specific command")),N.log.message(V.dim(" nebutra schema --list List available command names")),N.log.message(V.dim(" nebutra schema --exit-codes Show exit codes reference")),N.log.message(""),N.log.info(V.bold("Example:")),N.log.message(V.dim(" nebutra schema status")),N.log.message(V.dim(" nebutra schema --all | jq '.commands[0]'")),N.outro(V.cyan("All output is JSON for easy parsing by agents and automation tools."))),process.exit(0)}catch(o){if(Ra(o))throw o;n||l.error(o instanceof Error?o.message:String(o)),process.exit(c.ERROR)}}function cr(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 ka(t,n)})}import{stdin as De}from"process";import*as lt from"@clack/prompts";import R from"picocolors";async function Oa(){return new Promise((e,t)=>{let n="";De.setEncoding("utf8"),De.on("readable",()=>{let r;for(;(r=De.read())!==null;)n+=r}),De.on("end",()=>{e(n.trim())}),De.on("error",t)})}async function Aa(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(`${R.cyan(n.key)}${R.gray(r)}`,"info"),u(` Updated: ${n.updatedAt}`,"info")}}}async function _a(e,t){e||(u("Secret key is required: nebutra secrets set <key>","error"),process.exit(c.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 Oa()}catch{u("Failed to read secret from stdin","error"),process.exit(c.ERROR)}}if(n||(u("Secret value cannot be empty","error"),process.exit(c.INVALID_ARGS)),u(`Setting secret ${R.cyan(e)}...`,"info"),t.dryRun){u(`Would encrypt and store secret ${e} for tenant ${t.tenant||"org_123"}`,"info");return}u(`Secret ${R.cyan(e)} encrypted and stored successfully`,"success"),t.description&&u(`Description: ${t.description}`,"info")}async function Ia(e,t){e||(u("Secret key is required: nebutra secrets get <key>","error"),process.exit(c.INVALID_ARGS)),u(`Retrieving secret ${R.cyan(e)}...`,"info");let r=t.unmask?"sk_live_abc123xyz789...":"***masked***";t.unmask||u(`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"}):u(`${R.cyan(e)}: ${r}`,"info")}async function Na(e,t){e||(u("Secret key is required: nebutra secrets rotate <key>","error"),process.exit(c.INVALID_ARGS));let n=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!t.yes&&!t.dryRun&&n){let r=await lt.confirm({message:`Rotate encryption key for ${R.cyan(e)}? This will re-encrypt with new key material.`,initialValue:!1});(lt.isCancel(r)||!r)&&(u("Rotation cancelled","warn"),process.exit(c.CANCELLED))}if(!t.yes&&!t.dryRun&&!n&&(u("Key rotation requires --yes confirmation","error"),process.exit(c.INVALID_ARGS)),u(`Rotating encryption key for ${R.cyan(e)}...`,"info"),t.dryRun){u(`Would rotate encryption key for ${e}`,"info");return}u(`Encryption key for ${R.cyan(e)} rotated successfully`,"success"),u(`New key version: ${R.dim("v2")}`,"info")}async function Pa(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?R.green("\u2713"):R.red("\u2716");u(`${o} ${r.timestamp} ${R.cyan(r.action)} ${r.key}`,r.success?"success":"error"),u(` Actor: ${r.actor}${r.message?` \u2014 ${r.message}`:""}`,"info")}}}async function ja(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?R.green("\u2713"):R.red("\u2716");u(`${o} ${r.name}: ${r.message}`,r.healthy?"success":"error")}let n=t.every(r=>r.healthy);u(`Overall: ${n?R.green("HEALTHY"):R.red("UNHEALTHY")}`,n?"success":"error")}}function lr(e){e.command("secrets <verb> [args...]").description("Manage encrypted secrets via @nebutra/vault (AWS KMS + HKDF envelope encryption)").option("--dry-run","Show what would be run without executing").option("--yes","Skip confirmations").option("--format <type>","Output format: json, plain, table","plain").option("--tenant <id>","Tenant ID for multi-tenant access").option("--unmask","Unmask secret values (required to view)").option("--description <text>","Description for secret").option("--value <text>","Secret value (prefer stdin for security)").option("--since <date>","Audit log start date (ISO 8601)").option("--limit <n>","Maximum audit log entries (default: 50)").action(async(n,r,o)=>{let i=o.optsWithGlobals?.(),a={dryRun:o.dryRun||i?.dryRun,yes:o.yes||i?.yes,format:o.format||i?.format,tenant:o.tenant||i?.tenant,unmask:o.unmask||!1,description:o.description,value:o.value,since:o.since,limit:o.limit??50};try{switch(n){case"list":await Aa(a);break;case"set":r.length===0&&(u("set requires a secret key: nebutra secrets set <key>","error"),process.exit(c.INVALID_ARGS)),await _a(r[0],a);break;case"get":r.length===0&&(u("get requires a secret key: nebutra secrets get <key>","error"),process.exit(c.INVALID_ARGS)),await Ia(r[0],a);break;case"rotate":r.length===0&&(u("rotate requires a secret key: nebutra secrets rotate <key>","error"),process.exit(c.INVALID_ARGS)),await Na(r[0],a);break;case"audit":await Pa(a);break;case"verify":await ja(a);break;default:u(`Unknown secrets subcommand: ${n}. Valid commands: list, set, get, rotate, audit, verify`,"error"),process.exit(c.ERROR)}}catch(s){let d=s instanceof Error?s.message:String(s);u(`Secrets command failed: ${d}`,"error"),x("Full error",{error:s}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
447
464
|
Examples:
|
|
448
465
|
nebutra secrets list List all secrets (metadata only)
|
|
449
466
|
nebutra secrets list --tenant org_456 List secrets for specific tenant
|
|
@@ -478,7 +495,7 @@ Security Notes:
|
|
|
478
495
|
- Never use --value in production (visible in shell history)
|
|
479
496
|
- Always use --unmask when viewing secrets in scripts
|
|
480
497
|
- Audit log tracks all access and modifications
|
|
481
|
-
`)}import*as
|
|
498
|
+
`)}import*as ut from"@clack/prompts";import P from"picocolors";function ur(e){switch(e.toLowerCase()){case"running":return"running";case"exited":return"exited";case"restarting":return"restarting";case"paused":return"paused";default:return"exited"}}function $a(e){switch(e){case"running":return P.green("\u25CF");case"exited":return P.red("\u25CF");case"restarting":return P.yellow("\u25CF");case"paused":return P.gray("\u25CF");default:return P.gray("\u25CF")}}async function Ta(e){u("Checking service status...","info");let t=await O({command:"docker",args:["compose","ps","--format","json"],cwd:C(),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(c.ERROR));try{let n=JSON.parse(t.stdout);if(e.format==="json"){let r=n.map(o=>({name:o.Name,status:ur(o.State),exitCode:o.ExitCode}));b(r,{format:"json"})}else{u("Service Status","info");let r=n.map(o=>({Service:$a(ur(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(c.ERROR)}}async function Da(e,t){u(`Streaming logs for ${P.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 O({command:"docker",args:n,cwd:C(),interactive:!0,label:`Docker Compose Logs (${e})`,dryRun:t.dryRun})).exitCode!==0&&(u(`Failed to stream logs for ${e}`,"error"),process.exit(c.ERROR))}async function La(e,t){let n=process.stdin.isTTY===!0&&process.stdout.isTTY===!0;if(!t.yes&&!t.dryRun&&n){let i=await ut.confirm({message:`Restart service ${P.cyan(e)}?`,initialValue:!1});(ut.isCancel(i)||!i)&&(u("Restart cancelled","warn"),process.exit(c.CANCELLED))}u(`Restarting service ${P.cyan(e)}...`,"info");let r=["compose","restart"];t.timeout&&r.push("--timeout",String(t.timeout)),r.push(e);let o=await O({command:"docker",args:r,cwd:C(),interactive:!1,label:`Docker Compose Restart (${e})`,dryRun:t.dryRun});o.exitCode===0?u(`Service ${P.cyan(e)} restarted successfully`,"success"):(u(`Failed to restart service ${e}`,"error"),x("Docker error",{stderr:o.stderr}),process.exit(c.ERROR))}async function Fa(e){u("Running deep health check...","info");let t=[],n=await O({command:"docker",args:["compose","exec","-T","postgres","pg_isready"],cwd:C(),interactive:!1,label:"PostgreSQL Health",dryRun:e.dryRun});t.push({service:"PostgreSQL",healthy:n.exitCode===0,message:n.exitCode===0?"Ready":"Not responding"});let r=await O({command:"docker",args:["compose","exec","-T","redis","redis-cli","ping"],cwd:C(),interactive:!1,label:"Redis Health",dryRun:e.dryRun});t.push({service:"Redis",healthy:r.exitCode===0,message:r.exitCode===0?"Ready":"Not responding"});let o=await O({command:"docker",args:["compose","exec","-T","clickhouse","curl","-s","http://localhost:8123/ping"],cwd:C(),interactive:!1,label:"ClickHouse Health",dryRun:e.dryRun});t.push({service:"ClickHouse",healthy:o.exitCode===0,message:o.exitCode===0?"Ready":"Not responding"});let i=await O({command:"docker",args:["compose","exec","-T","meilisearch","curl","-s","http://localhost:7700/health"],cwd:C(),interactive:!1,label:"Meilisearch Health",dryRun:e.dryRun});if(t.push({service:"Meilisearch",healthy:i.exitCode===0,message:i.exitCode===0?"Healthy":"Not responding"}),e.format==="json")b(t,{format:"json"});else{for(let s of t){let d=s.healthy?P.green("\u2713"):P.red("\u2716");u(`${d} ${s.service}: ${s.message}`,s.healthy?"success":"error")}let a=t.every(s=>s.healthy);u(`Overall health: ${a?P.green("HEALTHY"):P.red("UNHEALTHY")}`,a?"success":"error")}}async function Ua(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(c.INVALID_ARGS)),u(`Scaling ${P.cyan(e)} to ${P.cyan(String(r))} replicas...`,"info");let o=await O({command:"docker",args:["compose","up","-d","--scale",`${e}=${r}`],cwd:C(),interactive:!1,label:`Docker Compose Scale (${e})`,dryRun:n.dryRun});o.exitCode===0?u(`Service ${P.cyan(e)} scaled to ${r} replicas`,"success"):(u(`Failed to scale service ${e}`,"error"),x("Docker error",{stderr:o.stderr}),process.exit(c.ERROR))}function dr(e){e.command("services <verb> [args...]").description("Manage Docker Compose microservices (postgres, redis, clickhouse, meilisearch, novu, openfga, etc.)").option("--dry-run","Show what would be run without executing").option("--yes","Skip confirmations").option("--format <type>","Output format: json, plain, table","plain").option("--tail <n>","Number of log lines to show (default: 100)").option("--since <duration>","Show logs since duration (e.g., 10m, 1h)").option("--timeout <seconds>","Timeout for service restart").action(async(n,r,o)=>{let i=o.optsWithGlobals?.(),a={dryRun:o.dryRun||i?.dryRun,yes:o.yes||i?.yes,format:o.format||i?.format,tail:o.tail??100,since:o.since,timeout:o.timeout};try{switch(n){case"status":await Ta(a);break;case"logs":r.length===0&&(u("logs requires a service name: nebutra services logs <service>","error"),process.exit(c.INVALID_ARGS)),await Da(r[0],a);break;case"restart":r.length===0&&(u("restart requires a service name: nebutra services restart <service>","error"),process.exit(c.INVALID_ARGS)),await La(r[0],a);break;case"health":await Fa(a);break;case"scale":r.length<2&&(u("scale requires a service name and replica count: nebutra services scale <service> <count>","error"),process.exit(c.INVALID_ARGS)),await Ua(r[0],r[1],a);break;default:u(`Unknown services subcommand: ${n}. Valid commands: status, logs, restart, health, scale`,"error"),process.exit(c.ERROR)}}catch(s){let d=s instanceof Error?s.message:String(s);u(`Services command failed: ${d}`,"error"),x("Full error",{error:s}),process.exit(c.ERROR)}}).addHelpText("after",`
|
|
482
499
|
Examples:
|
|
483
500
|
nebutra services status Show all service status
|
|
484
501
|
nebutra services logs postgres Stream postgres logs
|
|
@@ -499,24 +516,34 @@ Flags:
|
|
|
499
516
|
--tail <n> Log lines to show (default: 100)
|
|
500
517
|
--since <duration> Show logs since (e.g., 10m, 1h)
|
|
501
518
|
--timeout <seconds> Timeout for restart
|
|
502
|
-
`)}import{existsSync as
|
|
503
|
-
`)}function
|
|
504
|
-
`)}function
|
|
505
|
-
`))}function
|
|
506
|
-
${n}`)}return t}async function za(e){e.dryRun&&(T({mode:"dry-run",timestamp:new Date().toISOString(),command:"test",options:{app:e.app,watch:e.watch,coverage:e.coverage},task:e.coverage?"pnpm test:coverage":e.watch?"pnpm test:watch":"pnpm test"},{format:ae(e.format)}),process.exit(c.DRY_RUN_OK));try{if(e.app&&(e.watch||e.coverage)&&(N.log.error(x.red("Cannot use --watch or --coverage with --app filter (app-level tests use turbo)")),process.exit(c.INVALID_ARGS)),e.app){Ka(e.app);let r=`@nebutra/${e.app}`;e.quiet||N.log.info(x.cyan(`Running tests for ${x.bold(r)}...`));let o=await Q("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...";N.log.info(x.cyan(r))}let n=await P(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?N.log.error(x.red(t.message)):N.log.error(x.red("Unknown error occurred")),process.exit(c.ERROR)}}async function On(e){e.dryRun&&(T({mode:"dry-run",timestamp:new Date().toISOString(),command:"test e2e",options:{ui:e.ui,ci:e.ci},task:e.ci?"pnpm e2e:ci":e.ui?"pnpm e2e:ui":"pnpm e2e"},{format:ae(e.format)}),process.exit(c.DRY_RUN_OK));try{e.ui&&e.ci&&(N.log.error(x.red("Cannot use --ui and --ci together")),process.exit(c.INVALID_ARGS));let t="e2e";if(e.ci?t="e2e:ci":e.ui&&(t="e2e:ui"),!e.quiet){let r=t==="e2e:ci"?"Running E2E tests (CI mode)...":t==="e2e:ui"?"Opening E2E tests in UI mode...":"Running E2E tests...";N.log.info(x.cyan(r))}let n=await P(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?N.log.error(x.red(t.message)):N.log.error(x.red("Unknown error occurred")),process.exit(c.ERROR)}}async function An(e){e.dryRun&&(T({mode:"dry-run",timestamp:new Date().toISOString(),command:"test size",options:{why:e.why},task:e.why?"pnpm size:why":"pnpm size"},{format:ae(e.format)}),process.exit(c.DRY_RUN_OK));try{let t=e.why?"size:why":"size";if(!e.quiet){let r=e.why?"Analyzing bundle size (showing largest assets)...":"Checking bundle size...";N.log.info(x.cyan(r))}let n=await P(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?N.log.error(x.red(t.message)):N.log.error(x.red("Unknown error occurred")),process.exit(c.ERROR)}}async function In(e){e.dryRun&&(T({mode:"dry-run",timestamp:new Date().toISOString(),command:"test arch",task:"pnpm test:arch"},{format:ae(e.format)}),process.exit(c.DRY_RUN_OK));try{e.quiet||N.log.info(x.cyan("Running architecture tests..."));let t=await P("test:arch",{interactive:!0});process.exit(t.exitCode)}catch(t){t instanceof Error?N.log.error(x.red(t.message)):N.log.error(x.red("Unknown error occurred")),process.exit(c.ERROR)}}var _n=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: ${gt.join(", ")}`).option("--dry-run","Preview what would run (exit code 10)").action(async(n,r,o)=>{let a=O(r,o),i={dryRun:!!r.dryRun,quiet:!!a.quiet,verbose:!!a.verbose,format:ae(a.format)};n==="e2e"?await On({...i,ui:!!r.ui,ci:!!r.ci}):n==="size"?await An({...i,why:!!r.why}):n==="arch"?await In(i):await za({...i,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=O(n,r);await On({ui:!!n.ui,ci:!!n.ci,dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:ae(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=O(n,r);await An({why:!!n.why,dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:ae(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=O(n,r);await In({dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:ae(o.format)})})};var Ya={$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 theme use 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 theme use 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 theme use 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 theme use 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 theme use 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 theme use 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 theme use 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 theme use 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 theme use vercel",cssImport:"@nebutra/tokens/skins/vercel.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}}]},ie=Ya,Wa=ie.languages.map(e=>e.id),yt=ie.defaultLanguage;function bt(e){return ie.languages.find(t=>t.id===e)}function Nn(e){let t=e.opts?e.opts():e,n=e.optsWithGlobals?e.optsWithGlobals():e;return t.format??n.format??qa()}function qa(){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 Ha(e="table"){let t=ie.languages;return e==="json"?JSON.stringify({version:ie.version,defaultLanguage:ie.defaultLanguage,count:t.length,languages:t},null,2):[`Nebutra design languages (${t.length}) \u2014 global product chrome swap`,`Default: ${yt} (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(`
|
|
507
|
-
`)
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
519
|
+
`)}import{join as Ha}from"path";import be from"picocolors";import{existsSync as mr,readFileSync as pr}from"fs";import{dirname as Ya,join as fr,parse as Ja}from"path";import{stderr as Ba}from"process";import se from"picocolors";function Ma(){return process.stderr.isTTY===!0&&!process.env.CI&&!process.env.NEBUTRA_NON_TTY}function Ga(e){let t=[];return t.push(se.red(`\u2716 ${e.error}`)),t.push(se.white(e.message)),e.suggestion&&(t.push(""),t.push(se.cyan("\u{1F4A1} Suggestion:")),t.push(se.gray(` ${e.suggestion}`))),process.env.NEBUTRA_DEBUG&&e.context&&Object.keys(e.context).length>0&&(t.push(""),t.push(se.dim("Debug context:")),t.push(se.dim(JSON.stringify(e.context,null,2)))),process.env.NEBUTRA_DEBUG&&e.stack&&(t.push(""),t.push(se.dim("Stack trace:")),t.push(se.dim(e.stack))),t.join(`
|
|
520
|
+
`)}function Va(e){let t={error:e.error,message:e.message,...e.suggestion&&{suggestion:e.suggestion},retryable:e.retryable,exitCode:e.exitCode,...e.context&&Object.keys(e.context).length>0&&{context:e.context},...process.env.NEBUTRA_DEBUG&&e.stack&&{stack:e.stack}};return JSON.stringify(t)}function Le(e){return{error:e.error,message:e.message,suggestion:e.suggestion,retryable:e.retryable??!1,exitCode:e.exitCode??c.ERROR,context:e.context,stack:e.cause?.stack}}function Tt(e){let t=Ma()?Ga(e):Va(e);Ba.write(t+`
|
|
521
|
+
`)}function Ka(e){Tt(e),process.exit(e.exitCode)}function Dt(e,t){Ka(Le({error:"not_found",message:e,suggestion:t??"Check the resource path and try again",exitCode:c.NOT_FOUND}))}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??c.ERROR,this.suggestion=t.suggestion,this.retryable=t.retryable??!1,this.context=t.context,this.cause=t.cause}};function za(e){return e instanceof K}function Wa(e){return za(e)?Le({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?Le({error:"unexpected_error",message:e.message,suggestion:"Re-run with NEBUTRA_DEBUG=1 for a stack trace.",exitCode:c.ERROR,cause:e}):Le({error:"unexpected_error",message:String(e),suggestion:"Re-run with NEBUTRA_DEBUG=1 for a stack trace.",exitCode:c.ERROR})}function Lt(e){let t=Wa(e);return Tt(t),t.exitCode}async function he(e){try{await e()}catch(t){process.exitCode=Lt(t)}}var Ae={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 dt(e){let t=e,{root:n}=Ja(t);for(;;){if(mr(fr(t,"nebutra.config.json")))return t;if(t===n)return null;let r=Ya(t);if(r===t)return null;t=r}}function mt(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:c.CONFIG_ERROR});return n}function qa(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 gr(e){let t={...process.env};for(let n of[".env",".env.local"]){let r=fr(e,n);if(mr(r))try{let o=qa(pr(r,"utf-8"));for(let[i,a]of Object.entries(o))process.env[i]===void 0&&(t[i]=a)}catch{}}return t}function yr(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 hr(e){let t=Object.keys(Ae),n=e.filter(a=>!Ae[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:c.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 Za(e,t){switch(e){case"live":return be.green(t);case"local-fallback":return be.yellow(t);case"missing-key":return be.red(t);default:return t}}async function Xa(e){let t=dt(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:c.CONFIG_ERROR});let n=Ha(t,"nebutra.config.json"),r=mt(n),o=Array.isArray(r.capabilities)?r.capabilities:[],i=gr(t),a=i.NEBUTRA_LOCALE||"global",s=typeof r.stack=="string"?r.stack:"unknown",d=o.map(f=>{let v=Ae[f];return v?yr(f,v,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:d},{format:"json"});return}let p=[];p.push(be.bold("nebutra status")),p.push(be.dim(`stack: ${s} locale: ${a} project: ${t}`)),p.push("");let g=Math.max(10,...d.map(f=>f.name.length)),y=Math.max(14,...d.map(f=>f.state.length));for(let f of d){let v=f.name.padEnd(g),T=Za(f.state,f.state.padEnd(y)),w=f.provider.length>0?f.provider.join(", "):"-";p.push(`${v} ${T} ${be.dim(w)}`),f.state!=="live"&&p.push(`${" ".repeat(g)} ${be.dim(`\u2192 ${f.next}`)}`)}console.log(p.join(`
|
|
522
|
+
`))}function br(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)=>he(async()=>{let r=n.optsWithGlobals?n.optsWithGlobals():{};await Xa({json:t.json,format:r.format})}))}import{existsSync as vr,readFileSync as Qa,writeFileSync as wr}from"fs";import{join as Ft}from"path";import Fe from"picocolors";var Cr=".env.example",xr=".env.local",es=["# Local secrets for this machine \u2014 never commit real values.","# `nebutra sync` created this file; add your own keys below.",""].join(`
|
|
523
|
+
`);function ts(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 ns(e,t){let n=ts(e),r=[],o=[],i=[];for(let m of t){let p=Ae[m];if(p)for(let g of p.providers){if(g.envKeys.length===0)continue;let y=[];for(let f of g.envKeys)n.has(f)?o.push(f):y.push(f);if(y.length!==0){i.push([`# ${m} (${g.id})`,...y.map(f=>`${f}=`)].join(`
|
|
524
|
+
`));for(let f of y)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}
|
|
525
|
+
|
|
526
|
+
`:""}${i.join(`
|
|
527
|
+
|
|
528
|
+
`)}
|
|
529
|
+
`,added:r,unchanged:o}}async function rs(e){let t=dt(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:c.CONFIG_ERROR});let n=Ft(t,"nebutra.config.json"),r=mt(n),o=Array.isArray(r.capabilities)?r.capabilities:[],{capabilities:i,warnings:a}=hr(o),s=Ft(t,Cr),d=vr(s),m=d?Qa(s,"utf-8"):"",p=ns(m,i),g=Ft(t,xr),y=vr(g),f={};p.added.length>0&&(f[Cr]=p.added),y||(f[xr]=[]);let v={added:f,unchanged:p.unchanged,warnings:a},T=e.json===!0||e.format==="json";if(e.dryRun){I({mode:"dry-run",command:"sync",projectRoot:t,...v},{format:T?"json":e.format});for(let w of a)u(w,"warn");process.exit(c.DRY_RUN_OK)}if((p.added.length>0||!d)&&wr(s,p.content),y||wr(g,es),T)b(v,{format:"json"});else{let w=[];w.push(Fe.bold("nebutra sync")),w.push(Fe.dim(`project: ${t}`)),w.push("");let z=Object.keys(f);if(z.length===0)w.push(Fe.dim("Env files already match declared capabilities. Nothing to do."));else for(let Q of z){let F=f[Q]??[];F.length>0?w.push(`${Fe.green("+")} ${Q}: added ${F.join(", ")}`):w.push(`${Fe.green("+")} ${Q}: created`)}console.log(w.join(`
|
|
530
|
+
`))}for(let w of a)u(w,"warn");u("Run `nebutra status` to check capability readiness.")}function Sr(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)=>he(async()=>{let r=n.optsWithGlobals?n.optsWithGlobals():{};await rs({dryRun:t.dryRun,json:t.json,format:r.format})}))}import*as j from"@clack/prompts";import k from"picocolors";function ce(e){if(e==="json"||e==="plain"||e==="table")return e}var Ut=["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 os(e){let t=`@nebutra/${e}`;if(!Ut.includes(e)){let n=k.yellow(`Valid apps: ${Ut.join(", ")}`);throw new Error(`Invalid app: ${k.red(e)}
|
|
531
|
+
${n}`)}return t}async function is(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:ce(e.format)}),process.exit(c.DRY_RUN_OK));try{if(e.app&&(e.watch||e.coverage)&&(j.log.error(k.red("Cannot use --watch or --coverage with --app filter (app-level tests use turbo)")),process.exit(c.INVALID_ARGS)),e.app){os(e.app);let r=`@nebutra/${e.app}`;e.quiet||j.log.info(k.cyan(`Running tests for ${k.bold(r)}...`));let o=await re("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...";j.log.info(k.cyan(r))}let n=await $(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?j.log.error(k.red(t.message)):j.log.error(k.red("Unknown error occurred")),process.exit(c.ERROR)}}async function Er(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:ce(e.format)}),process.exit(c.DRY_RUN_OK));try{e.ui&&e.ci&&(j.log.error(k.red("Cannot use --ui and --ci together")),process.exit(c.INVALID_ARGS));let t="e2e";if(e.ci?t="e2e:ci":e.ui&&(t="e2e:ui"),!e.quiet){let r=t==="e2e:ci"?"Running E2E tests (CI mode)...":t==="e2e:ui"?"Opening E2E tests in UI mode...":"Running E2E tests...";j.log.info(k.cyan(r))}let n=await $(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?j.log.error(k.red(t.message)):j.log.error(k.red("Unknown error occurred")),process.exit(c.ERROR)}}async function Rr(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:ce(e.format)}),process.exit(c.DRY_RUN_OK));try{let t=e.why?"size:why":"size";if(!e.quiet){let r=e.why?"Analyzing bundle size (showing largest assets)...":"Checking bundle size...";j.log.info(k.cyan(r))}let n=await $(t,{interactive:!0});process.exit(n.exitCode)}catch(t){t instanceof Error?j.log.error(k.red(t.message)):j.log.error(k.red("Unknown error occurred")),process.exit(c.ERROR)}}async function kr(e){e.dryRun&&(I({mode:"dry-run",timestamp:new Date().toISOString(),command:"test arch",task:"pnpm test:arch"},{format:ce(e.format)}),process.exit(c.DRY_RUN_OK));try{e.quiet||j.log.info(k.cyan("Running architecture tests..."));let t=await $("test:arch",{interactive:!0});process.exit(t.exitCode)}catch(t){t instanceof Error?j.log.error(k.red(t.message)):j.log.error(k.red("Unknown error occurred")),process.exit(c.ERROR)}}var Or=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: ${Ut.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:ce(i.format)};n==="e2e"?await Er({...a,ui:!!r.ui,ci:!!r.ci}):n==="size"?await Rr({...a,why:!!r.why}):n==="arch"?await kr(a):await is({...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 Er({ui:!!n.ui,ci:!!n.ci,dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:ce(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 Rr({why:!!n.why,dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:ce(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 kr({dryRun:!!n.dryRun,quiet:!!o.quiet,verbose:!!o.verbose,format:ce(o.format)})})};var as={$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 theme use 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 theme use 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 theme use 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 theme use 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 theme use 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 theme use 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 theme use 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 theme use 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 theme use vercel",cssImport:"@nebutra/tokens/skins/vercel.css"},compatibility:{tailwind:"4",cssVariables:!0,brandPackage:!0,dataBrand:!0}}]},le=as,ss=le.languages.map(e=>e.id),Bt=le.defaultLanguage;function Mt(e){return le.languages.find(t=>t.id===e)}function Ar(e){let t=e.opts?e.opts():e,n=e.optsWithGlobals?e.optsWithGlobals():e;return t.format??n.format??cs()}function cs(){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 ls(e="table"){let t=le.languages;return e==="json"?JSON.stringify({version:le.version,defaultLanguage:le.defaultLanguage,count:t.length,languages:t},null,2):[`Nebutra design languages (${t.length}) \u2014 global product chrome swap`,`Default: ${Bt} (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(`
|
|
532
|
+
`)}function us(e,t="table"){let n=Mt(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(`
|
|
533
|
+
`)}function _r(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(ls(Ar(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=us(n,Ar(r));o||(l.error(`Design language '${n}' not found. Run: nebutra theme list`),process.exit(c.NOT_FOUND)),console.log(o)})}import{existsSync as Pr,readFileSync as jr}from"fs";import{resolve as Ir,join as Gt}from"path";function $r(e={}){let t=e.manifestPath?Ir(e.manifestPath):Gt(Ir(e.root??process.cwd()),"apps","design-docs","public","agent-manifest.json");return{manifestPath:t,componentDir:Gt(t,"..","agent","components")}}function ds(e){return e.trim().toLowerCase()}function ms(e){let t=ds(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 Tr(e={}){let{manifestPath:t}=$r(e);if(!Pr(t))throw new Error(`Missing Nebutra UI agent manifest at ${t}. Run pnpm --filter @nebutra/design-docs build:registry.`);return JSON.parse(jr(t,"utf8"))}function Vt(e,t={}){let n=ms(e),{componentDir:r}=$r(t),o=Gt(r,`${n}.json`);if(!Pr(o))throw new Error(`UI component contract not found: ${n}`);return JSON.parse(jr(o,"utf8"))}function Dr(e,t="",n={}){let r=t.trim().toLowerCase(),o=Nr(n.limit??20,1,100),i=Nr(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?ps(p).includes(r):!0).map(p=>({component:p,score:fs(p,r)})).sort((p,g)=>g.score-p.score||p.component.name.localeCompare(g.component.name)).map(({component:p})=>p),d=s.slice(i,i+o),m=i+d.length;return{query:t,total:s.length,count:d.length,offset:i,limit:o,hasMore:m<s.length,...m<s.length&&{nextOffset:m},items:d}}function Kt(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 ps(e){return[e.name,e.title,e.description,e.status,e.maturity,e.layer,e.source,...e.tags].join(" ").toLowerCase()}function fs(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 Nr(e,t,n){return Number.isFinite(e)?Math.min(Math.max(Math.trunc(e),t),n):t}function pt(e){let t=e.opts?e.opts():e,n=e.optsWithGlobals?e.optsWithGlobals():e;return t.format??n.format??gs()}function gs(){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 Wt(e){let t=e.opts?e.opts():e;return{root:C(),manifestPath:t.manifest}}function Lr(e,t){if(!e)return t;let n=Number(e);return Number.isFinite(n)?n:t}function ys(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(`
|
|
534
|
+
`)}return JSON.stringify(e,null,2)}function hs(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(`
|
|
535
|
+
`):JSON.stringify(e,null,2)}function bs(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(`
|
|
511
536
|
`)}return[`${e.name}: ${e.valid?"valid":"invalid"}`,...e.errors.map(n=>`error: ${n}`),...e.warnings.map(n=>`warning: ${n}`)].join(`
|
|
512
|
-
`)}return JSON.stringify(e,null,2)}function
|
|
513
|
-
`):JSON.stringify(n,null,2)}function
|
|
514
|
-
`),t?console.log(JSON.stringify({command:"unlink",success:!0,configPath:r},null,2)):l.success("Unlinked from project")}function zn(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 pi({force:t.force||!1,format:t.format||r.format})})}import{spawnSync as Ii}from"child_process";import{existsSync as fi,readFileSync as Wn}from"fs";import{dirname as Yn,join as gi}from"path";import{fileURLToPath as yi}from"url";function bi(e=import.meta.url){let t=Yn(yi(e));for(let n=0;n<6;n+=1){let r=gi(t,"package.json");if(fi(r))try{if(JSON.parse(Wn(r,"utf8")).name==="nebutra")return r}catch{}let o=Yn(t);if(o===t)break;t=o}throw new Error("Unable to locate the nebutra CLI package.json")}function Je(e=import.meta.url){return JSON.parse(Wn(bi(e),"utf8")).version}import{promises as Ze}from"fs";import{homedir as hi}from"os";import{join as Hn}from"path";import vi from"p-timeout";import se from"picocolors";var Ci=1440*60*1e3,Jn=Hn(hi(),".nebutra"),Et=Hn(Jn,"update-check.json"),wi="https://registry.npmjs.org/nebutra",Si=5e3;async function Ei(){try{await Ze.mkdir(Jn,{recursive:!0})}catch{}}async function Ri(){try{let e=await Ze.readFile(Et,"utf-8"),t=JSON.parse(e);return Date.now()-t.timestamp<Ci?t:(await Ze.unlink(Et).catch(r=>{l.debug("Failed to remove expired update cache file",{error:r})}),null)}catch{return null}}async function xi(e){try{await Ei();let t={timestamp:Date.now(),latestVersion:e};await Ze.writeFile(Et,JSON.stringify(t,null,2),"utf-8")}catch{}}async function ki(){try{let e=new AbortController,t=await vi(fetch(wi,{signal:e.signal,headers:{Accept:"application/json"}}),{milliseconds:Si,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 qn(e){return e.split(".").map(t=>parseInt(t.replace(/[^0-9]/g,""),10)||0).slice(0,3)}function Oi(e,t){let n=qn(e),r=qn(t);for(let o=0;o<3;o++){let a=n[o]??0,i=r[o]??0;if(i>a)return!0;if(i<a)return!1}return!1}async function Rt(e){let t=await Ri(),n=null;return t?n=t.latestVersion:(n=await ki(),n&&await xi(n)),!n||!Oi(e,n)?null:{currentVersion:e,latestVersion:n,isUpdate:!0}}function Ai(e){let n=`Update available: ${e.currentVersion} \u2192 ${e.latestVersion}`,r="Run `npm i -g nebutra` to update",o=" ".repeat((43-n.length)/2),a=" ".repeat((43-r.length)/2),i=[se.cyan("\u256D"+"\u2500".repeat(41)+"\u256E"),se.cyan("\u2502")+se.yellow(o+n+o)+se.cyan("\u2502"),se.cyan("\u2502")+se.gray(a+r+a)+se.cyan("\u2502"),se.cyan("\u2570"+"\u2500".repeat(41)+"\u256F")]}async function Zn(e){let t=null;return Rt(e).then(n=>{t=n}),async()=>{!process.stdout.isTTY||process.env.CI||(t||await new Promise(n=>setTimeout(n,100)),t&&Ai(t))}}var me=Je();function _i(){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 Ni(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 Pi(e){let t=_i(),n=Ni(t),r=e.format==="json",o=await Rt(me),a=o?.latestVersion??null,i=!!o?.isUpdate;e.dryRun&&(r?console.log(JSON.stringify({command:"upgrade",dryRun:!0,installer:t,command_to_run:n,currentVersion:me,latestVersion:a,hasUpdate:i},null,2)):(l.info(`Detected installer: ${t}`),l.info(`Current: ${me}${a?` \u2192 Latest: ${a}`:""}`),l.info(`Upgrade command: ${n}`)),process.exit(c.DRY_RUN_OK)),!i&&a&&(r?console.log(JSON.stringify({command:"upgrade",success:!0,alreadyLatest:!0,version:me},null,2)):l.success(`Already on the latest version (${me})`),process.exit(c.SUCCESS)),e.yes||(r?console.log(JSON.stringify({command:"upgrade",success:!0,installer:t,command_to_run:n,currentVersion:me,latestVersion:a,requiresConfirmation:!0},null,2)):(l.info(`Current: ${me}${a?` \u2192 Latest: ${a}`:""}`),l.info("To upgrade, run:"),l.info(` ${n}`),l.info("Or re-run this command with --yes to execute it now.")),process.exit(c.SUCCESS));let[s,...d]=n.split(" ");r||l.info(`Running: ${n}`);let m=Ii(s,d,{stdio:r?"pipe":"inherit"});m.status===0&&(r?console.log(JSON.stringify({command:"upgrade",success:!0,installer:t,ran:n},null,2)):l.success("Upgrade complete"),process.exit(c.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)):l.error(p),process.exit(c.ERROR)}function Xn(e){let t=(n,r)=>{let o=r?.optsWithGlobals?.()||n;return Pi({yes:n.yes||o.yes||!1,dryRun:n.dryRun||!1,format:n.format||o.format})};e.command("upgrade").alias("self-update").description("Upgrade the nebutra CLI to the latest version").option("--yes","Execute the upgrade command without confirmation").option("--dry-run","Show the upgrade command without executing (exit 10)").option("--format <type>","Output format: json or plain").action(t)}import{existsSync as ji,mkdirSync as Ti,writeFileSync as Di}from"fs";import{homedir as tr,platform as Li}from"os";import{join as Ie}from"path";var Qn={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",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"}};var $i={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",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 er(e){let t=$i.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 xt from"picocolors";function Fi(){if(Li()==="win32"){let t=process.env.LOCALAPPDATA||Ie(tr(),"AppData","Local");return Ie(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?Ie(e,"nebutra"):Ie(tr(),".config","nebutra")}function Ui(){let e=process.env.NEBUTRA_TELEMETRY;return e==="0"||e==="false"||e==="no"}function nr(e,t){try{Ti(e,{recursive:!0}),Di(t,"")}catch{}}function rr(){if(!process.stderr.isTTY||!process.stdin.isTTY||process.env.CI)return;let e=Fi(),t=Ie(e,"first-run-acked");try{if(ji(t))return}catch{return}if(Ui()){nr(e,t);return}let r=[`${xt.cyan("\u2139")} ${Qn.name} collects anonymous usage analytics to improve the CLI.`,` Opt out at any time: ${xt.dim("export NEBUTRA_TELEMETRY=0")}`,` Privacy policy: ${xt.dim(`${er("landing")}/legal/cli-analytics`)}`,""];try{process.stderr.write(r.join(`
|
|
515
|
-
`))}catch{}
|
|
537
|
+
`)}return JSON.stringify(e,null,2)}function vs(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(`
|
|
538
|
+
`):JSON.stringify(n,null,2)}function zt(e,t){try{return Vt(e,Wt(t))}catch{Dt(`UI component contract not found: ${e}`,"Run `nebutra ui search <query> --format json` to discover available component ids.")}}function Fr(e){try{return Tr(Wt(e))}catch(t){Dt(t instanceof Error?t.message:"UI agent manifest not found","Run `pnpm --filter @nebutra/design-docs build:registry` from the Nebutra monorepo.")}}function ws(e,t){let n=e.components.map(o=>Kt(Vt(o.name,Wt(t)))),r=n.filter(o=>!o.valid).length;return{valid:r===0,total:n.length,invalid:r,results:n}}function Ur(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=Fr(r),i=Dr(o,n??"",{limit:Lr(r.limit,20),offset:Lr(r.offset,0),tag:r.tag,status:r.status,maturity:r.maturity});console.log(ys(i,pt(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=zt(n,r);console.log(hs(o,pt(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?Kt(zt(n,r)):ws(Fr(r),r);console.log(bs(o,pt(r))),!o.valid&&!r.warnOnly&&process.exit(c.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(c.INCOMPATIBLE));let o=zt(n,r);console.log(vs(o,pt(r)))})}import{existsSync as Br,readFileSync as Cs,unlinkSync as xs,writeFileSync as Ss}from"fs";import{dirname as Es,join as Mr,parse as Rs}from"path";function ks(e){let t=e,{root:n}=Rs(t);for(;;){if(Br(Mr(t,"package.json")))return t;if(t===n)return null;let r=Es(t);if(r===t)return null;t=r}}async function Os(e){let t=e.format==="json",n=ks(process.cwd())??process.cwd(),r=Mr(n,"nebutra.config.json");if(Br(r)||(t?console.log(JSON.stringify({command:"unlink",success:!0,alreadyUnlinked:!0},null,2)):l.info("Already unlinked"),process.exit(c.SUCCESS)),e.force){try{xs(r)}catch(s){let d=s instanceof Error?s.message:String(s);t?console.log(JSON.stringify({command:"unlink",success:!1,error:d},null,2)):l.error(`Failed to delete config: ${d}`),process.exit(c.PERMISSION_DENIED)}t?console.log(JSON.stringify({command:"unlink",success:!0,deletedConfig:!0,configPath:r},null,2)):l.success("Removed nebutra.config.json");return}let o={};try{o=JSON.parse(Cs(r,"utf-8"))}catch{o={}}"projectId"in o||(t?console.log(JSON.stringify({command:"unlink",success:!0,alreadyUnlinked:!0},null,2)):l.info("Already unlinked"),process.exit(c.SUCCESS));let{projectId:i,...a}=o;Ss(r,JSON.stringify(a,null,2)+`
|
|
539
|
+
`),t?console.log(JSON.stringify({command:"unlink",success:!0,configPath:r},null,2)):l.success("Unlinked from project")}function Gr(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 Os({force:t.force||!1,format:t.format||r.format})})}import{spawnSync as Vs}from"child_process";import{existsSync as As,readFileSync as Kr}from"fs";import{dirname as Vr,join as _s}from"path";import{fileURLToPath as Is}from"url";function Ns(e=import.meta.url){let t=Vr(Is(e));for(let n=0;n<6;n+=1){let r=_s(t,"package.json");if(As(r))try{if(JSON.parse(Kr(r,"utf8")).name==="nebutra")return r}catch{}let o=Vr(t);if(o===t)break;t=o}throw new Error("Unable to locate the nebutra CLI package.json")}function ft(e=import.meta.url){return JSON.parse(Kr(Ns(e),"utf8")).version}import{promises as gt}from"fs";import{homedir as Ps}from"os";import{join as Wr}from"path";import js from"p-timeout";import ue from"picocolors";var $s=1440*60*1e3,Yr=Wr(Ps(),".nebutra"),Yt=Wr(Yr,"update-check.json"),Ts="https://registry.npmjs.org/nebutra",Ds=5e3;async function Ls(){try{await gt.mkdir(Yr,{recursive:!0})}catch{}}async function Fs(){try{let e=await gt.readFile(Yt,"utf-8"),t=JSON.parse(e);return Date.now()-t.timestamp<$s?t:(await gt.unlink(Yt).catch(r=>{l.debug("Failed to remove expired update cache file",{error:r})}),null)}catch{return null}}async function Us(e){try{await Ls();let t={timestamp:Date.now(),latestVersion:e};await gt.writeFile(Yt,JSON.stringify(t,null,2),"utf-8")}catch{}}async function Bs(){try{let e=new AbortController,t=await js(fetch(Ts,{signal:e.signal,headers:{Accept:"application/json"}}),{milliseconds:Ds,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 zr(e){return e.split(".").map(t=>parseInt(t.replace(/[^0-9]/g,""),10)||0).slice(0,3)}function Ms(e,t){let n=zr(e),r=zr(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 Jt(e){let t=await Fs(),n=null;return t?n=t.latestVersion:(n=await Bs(),n&&await Us(n)),!n||!Ms(e,n)?null:{currentVersion:e,latestVersion:n,isUpdate:!0}}function Gs(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=[ue.cyan("\u256D"+"\u2500".repeat(41)+"\u256E"),ue.cyan("\u2502")+ue.yellow(o+n+o)+ue.cyan("\u2502"),ue.cyan("\u2502")+ue.gray(i+r+i)+ue.cyan("\u2502"),ue.cyan("\u2570"+"\u2500".repeat(41)+"\u256F")]}async function Jr(e){let t=null;return Jt(e).then(n=>{t=n}),async()=>{!process.stdout.isTTY||process.env.CI||(t||await new Promise(n=>setTimeout(n,100)),t&&Gs(t))}}var ve=ft();function Ks(){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 zs(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 Ws(e){let t=Ks(),n=zs(t),r=e.format==="json",o=await Jt(ve),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:ve,latestVersion:i,hasUpdate:a},null,2)):(l.info(`Detected installer: ${t}`),l.info(`Current: ${ve}${i?` \u2192 Latest: ${i}`:""}`),l.info(`Upgrade command: ${n}`)),process.exit(c.DRY_RUN_OK)),!a&&i&&(r?console.log(JSON.stringify({command:"upgrade",success:!0,alreadyLatest:!0,version:ve},null,2)):l.success(`Already on the latest version (${ve})`),process.exit(c.SUCCESS)),e.yes||(r?console.log(JSON.stringify({command:"upgrade",success:!0,installer:t,command_to_run:n,currentVersion:ve,latestVersion:i,requiresConfirmation:!0},null,2)):(l.info(`Current: ${ve}${i?` \u2192 Latest: ${i}`:""}`),l.info("To upgrade, run:"),l.info(` ${n}`),l.info("Or re-run this command with --yes to execute it now.")),process.exit(c.SUCCESS));let[s,...d]=n.split(" ");r||l.info(`Running: ${n}`);let m=Vs(s,d,{stdio:r?"pipe":"inherit"});m.status===0&&(r?console.log(JSON.stringify({command:"upgrade",success:!0,installer:t,ran:n},null,2)):l.success("Upgrade complete"),process.exit(c.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)):l.error(p),process.exit(c.ERROR)}function qr(e){let t=(n,r)=>{let o=r?.optsWithGlobals?.()||n;return Ws({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 qt(e,t){let n=t==="expired"?`Your ${B.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)):l.error(n),process.exit(c.PERMISSION_DENIED)}async function Ys(e,t){let n=!!e.json||e.format==="json"||t==="json",r=await Wn();if(!r)return qt(n,"missing");if(r.expired)return qt(n,"expired");let o=at(),i=await st(o,r.token);if(!i)return qt(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)):(l.success(i.email??i.userId),i.name&&l.info(i.name))}function Hr(e){e.command("whoami").description(`Show the ${B.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 Ys(t,r.format)})}import{existsSync as Js,mkdirSync as qs,writeFileSync as Hs}from"fs";import{homedir as Zr,platform as Zs}from"os";import{join as Ue}from"path";import Ht from"picocolors";function Xs(){if(Zs()==="win32"){let t=process.env.LOCALAPPDATA||Ue(Zr(),"AppData","Local");return Ue(t,"nebutra")}let e=process.env.XDG_CONFIG_HOME;return e?Ue(e,"nebutra"):Ue(Zr(),".config","nebutra")}function Qs(){let e=process.env.NEBUTRA_TELEMETRY;return e==="0"||e==="false"||e==="no"}function Xr(e,t){try{qs(e,{recursive:!0}),Hs(t,"")}catch{}}function Qr(){if(!process.stderr.isTTY||!process.stdin.isTTY||process.env.CI)return;let e=Xs(),t=Ue(e,"first-run-acked");try{if(Js(t))return}catch{return}if(Qs()){Xr(e,t);return}let r=[`${Ht.cyan("\u2139")} ${B.name} collects anonymous usage analytics to improve the CLI.`,` Opt out at any time: ${Ht.dim("export NEBUTRA_TELEMETRY=0")}`,` Privacy policy: ${Ht.dim(`${it("landing")}/legal/cli-analytics`)}`,""];try{process.stderr.write(r.join(`
|
|
540
|
+
`))}catch{}Xr(e,t)}var ro=ft();function tc(e){let t=new ec,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=>he(async()=>{let o=r.optsWithGlobals?r.optsWithGlobals():r;await In({dryRun:r.dryRun||!1,yes:o.yes||!1,ifNotExists:r.ifNotExists||!1})})),rr(t),cr(t),on(t),An(t),_n(t),Cn(t),jn(t),nn(t),gn(t),On(t),yn(t),Or(t),vn(t),_r(t),Ur(t),dr(t),lr(t),dn(t),er(t),nr(t),Hr(t),qr(t),Dn(t),Gr(t),br(t),Sr(t),t.command("doctor").description("Check your Nebutra project setup for common issues").action(()=>he(async()=>{let{logger:r}=await import("./logger-QMYN56HK.js"),{findMonorepoRoot:o}=await import("./delegate-GIJ3G5CK.js"),i=await import("fs"),a=await import("path");r.info("Running project health check...");let s=!1,d=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(d,"nebutra.config.json"))?r.success("nebutra.config.json found"):r.warn("nebutra.config.json missing. Try running 'nebutra init'"),i.existsSync(a.join(d,"package.json"))?r.success("package.json found"):(r.error("No package.json found in root directory"),s=!0),i.existsSync(a.join(d,".env"))||i.existsSync(a.join(d,".env.local"))?r.success("Environment variables configured"):r.warn("No .env or .env.local found. Some features may not work properly.");let y=a.join(d,"apps/api-gateway");i.existsSync(y)&&r.warn("Legacy `apps/api-gateway/` present. Expected `backends/gateway/` \u2014 see CLAUDE.md for the W2.x restructure.");let f=a.join(d,"backends/gateway");i.existsSync(f)?r.success("backends/gateway/ present"):i.existsSync(a.join(d,"backends"));let v=a.join(d,"packages");if(i.existsSync(v)){let z=new Set(["design","iam","commerce","integrations","platform","ops","ai"]);try{let Q=i.readdirSync(v,{withFileTypes:!0}),F=[];for(let we of Q){if(!we.isDirectory()||z.has(we.name))continue;let Be=a.join(v,we.name,"package.json");i.existsSync(Be)&&F.push(we.name)}F.length>0?r.warn(`Found ${F.length} uncategorized package(s) under packages/: ${F.join(", ")}. Expected packages/<category>/<name>/ \u2014 see CLAUDE.md.`):r.success("Packages follow categorized layout")}catch{}}let T=a.join(d,".nebutra"),w=a.join(T,"scaffold-meta.json");if(i.existsSync(w)?r.success(".nebutra/scaffold-meta.json present (CLI-scaffolded project)"):i.existsSync(T)&&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(d,"nebutra.config.json")))&&(r.info(""),r.info("Golden path to a running app:"),r.info(" 1. Fill .env.local (copy keys from .env.example)"),r.info(" 2. pnpm infra:up # local Postgres/Redis if needed"),r.info(" 3. pnpm db:migrate && pnpm db:seed"),r.info(" 4. pnpm dev # http://localhost:3000")),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:c.CONFIG_ERROR});r.success("Doctor is happy. Your project looks healthy!")})),t.hook("preAction",r=>{let o=r.optsWithGlobals();(!n||o.noInteractive)&&(o.yes=!0)}),t.addHelpText("after",`
|
|
516
541
|
Examples:
|
|
517
542
|
$ nebutra init Initialize a new project
|
|
518
543
|
$ nebutra status Show capability readiness (live/local-fallback/missing-key)
|
|
519
544
|
$ nebutra status --json Machine-readable capability status
|
|
545
|
+
$ nebutra sync Sync .env.example/.env.local with declared capabilities
|
|
546
|
+
$ nebutra sync --dry-run Preview env-file additions without writing
|
|
520
547
|
$ nebutra dev --app=web Start dev for the web app
|
|
521
548
|
$ nebutra db migrate Run pending database migrations
|
|
522
549
|
$ nebutra generate app blog Scaffold a new app
|
|
@@ -545,4 +572,4 @@ Environment:
|
|
|
545
572
|
NEBUTRA_OUTPUT_FORMAT Output format (json|table|plain)
|
|
546
573
|
NO_COLOR Disable colored output
|
|
547
574
|
CI Auto-enable non-interactive mode
|
|
548
|
-
`),t}async function
|
|
575
|
+
`),t}async function nc(){Qr();let e=await Jr(ro),t=tc({version:ro,isInteractive:process.stdin.isTTY!==!1&&process.stdout.isTTY!==!1});process.argv.length<=2&&(t.outputHelp(),process.exit(c.SUCCESS)),await t.parseAsync(process.argv),await e()}function rc(){if(!process.argv[1])return!1;try{return eo(no(import.meta.url))===eo(to(process.argv[1]))}catch{return no(import.meta.url)===to(process.argv[1])}}var oc=rc();oc&&nc().catch(e=>{process.exitCode=Lt(e)});export{tc as buildProgram};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nebutra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"nebutra": {
|
|
5
5
|
"status": "foundation",
|
|
6
6
|
"graph": "core",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"tsup": "^8.5.1",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
46
|
"vitest": "^4.1.4",
|
|
47
|
-
"@nebutra/
|
|
48
|
-
"@nebutra/
|
|
49
|
-
"@nebutra/ui": "
|
|
50
|
-
"@nebutra/
|
|
47
|
+
"@nebutra/brand": "3.0.0",
|
|
48
|
+
"@nebutra/theme": "3.0.0",
|
|
49
|
+
"@nebutra/ui": "3.0.0",
|
|
50
|
+
"@nebutra/preset": "0.1.4"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"dev": "tsup --watch",
|