UNPKG

@bold-ai/create-product

Version:

CLI tool to create new products with Company Auth pre-configured

336 lines (331 loc) 12.8 kB
#!/usr/bin/env node import { createProduct } from "./chunk-7BZTDGOC.mjs"; // src/index.ts import { Command } from "commander"; import inquirer from "inquirer"; import chalk2 from "chalk"; import ora2 from "ora"; import { execSync as execSync2 } from "child_process"; // src/register-product.ts async function registerProduct(options) { const { name, slug, description, authServiceUrl, adminToken } = options; const response = await fetch(`${authServiceUrl}/api/admin/products`, { method: "POST", headers: { "Content-Type": "application/json", "X-Admin-Key": adminToken }, body: JSON.stringify({ name, slug, description, status: "development" }) }); if (!response.ok) { const error = await response.json().catch(() => ({ error: "Unknown error" })); throw new Error(error.error || `Failed to register product: ${response.status}`); } const data = await response.json(); return { productId: data.product.id, apiKey: data.product.api_key }; } // src/download-packages.ts import fs from "fs-extra"; import path from "path"; import { execSync } from "child_process"; import ora from "ora"; import chalk from "chalk"; var DEFAULT_PACKAGES = ["agents", "auth-sdk", "logger", "subscriptions"]; var GITHUB_REPO = "Bold-AI-Inc/bold-os-re"; var GITHUB_BRANCH = "main"; async function downloadAndBuildPackages(options) { const { workspaceRoot, githubToken, packages = DEFAULT_PACKAGES } = options; const packagesDir = path.join(workspaceRoot, "packages"); await fs.mkdir(packagesDir, { recursive: true }); console.log(chalk.cyan("\n\u{1F4E6} Downloading packages from GitHub...\n")); for (const pkg of packages) { const spinner2 = ora(`Downloading @bold/${pkg}...`).start(); try { await downloadPackage(pkg, packagesDir, githubToken); spinner2.succeed(`Downloaded @bold/${pkg}`); } catch (error) { spinner2.fail(`Failed to download @bold/${pkg}`); throw new Error(`Failed to download ${pkg}: ${error instanceof Error ? error.message : "Unknown error"}`); } } console.log(chalk.green("\n\u2705 All packages downloaded!\n")); console.log(chalk.cyan("\u{1F4E6} Installing workspace dependencies...\n")); const spinner = ora("Running pnpm install...").start(); try { execSync("pnpm install", { cwd: workspaceRoot, stdio: "pipe", encoding: "utf-8" }); spinner.succeed("Workspace dependencies installed!"); } catch (error) { spinner.fail("Failed to install workspace dependencies"); const errorMsg = error.stderr?.toString() || error.stdout?.toString() || error.message; console.log(chalk.yellow(` \u26A0\uFE0F You can try running 'pnpm install' manually `)); console.log(chalk.dim(`Error: ${errorMsg.slice(0, 200)}`)); } console.log(chalk.green("\n\u2705 Workspace setup complete!\n")); } async function downloadPackage(packageName, packagesDir, githubToken) { const packagePath = path.join(packagesDir, packageName); await fs.mkdir(packagePath, { recursive: true }); const apiUrl = `https://api.github.com/repos/${GITHUB_REPO}/contents/packages/${packageName}?ref=${GITHUB_BRANCH}`; const response = await fetch(apiUrl, { headers: { "Authorization": `token ${githubToken}`, "Accept": "application/vnd.github.v3+json", "User-Agent": "bold-create-product-cli" } }); if (!response.ok) { if (response.status === 401) { throw new Error("Invalid GitHub token. Make sure you have access to the repository."); } if (response.status === 404) { throw new Error(`Package not found. Make sure ${packageName} exists in the repository.`); } throw new Error(`GitHub API error: ${response.status} ${response.statusText}`); } const contents = await response.json(); if (!Array.isArray(contents)) { throw new Error(`Expected directory contents but got: ${typeof contents}`); } await downloadContents(contents, packagePath, githubToken); } async function downloadContents(contents, targetPath, githubToken) { for (const item of contents) { const itemPath = path.join(targetPath, item.name); if (item.type === "file") { const fileResponse = await fetch(item.download_url, { headers: { "Authorization": `token ${githubToken}`, "User-Agent": "bold-create-product-cli" } }); if (!fileResponse.ok) { throw new Error(`Failed to download ${item.name}`); } const fileContent = await fileResponse.text(); await fs.writeFile(itemPath, fileContent, "utf-8"); } else if (item.type === "dir") { await fs.mkdir(itemPath, { recursive: true }); const dirResponse = await fetch(item.url, { headers: { "Authorization": `token ${githubToken}`, "Accept": "application/vnd.github.v3+json", "User-Agent": "bold-create-product-cli" } }); if (!dirResponse.ok) { throw new Error(`Failed to fetch directory ${item.name}`); } const dirContents = await dirResponse.json(); await downloadContents(dirContents, itemPath, githubToken); } } } // src/index.ts var program = new Command(); function checkPnpm() { try { execSync2("pnpm --version", { stdio: "pipe" }); return true; } catch { return false; } } program.name("create-bold-product").description("Create a new product with Bold Auth pre-configured").version("0.2.1"); program.argument("[project-name]", "name of the project").option("-t, --template <type>", "template to use", "default").option("--skip-install", "skip installing dependencies").option("--skip-register", "skip registering product with auth service").action(async (projectName, options) => { console.log(chalk2.bold.blue("\n\u{1F680} Bold Product Creator\n")); if (!checkPnpm()) { console.log(chalk2.red("\u274C pnpm is required but not installed!\n")); console.log(chalk2.yellow("This CLI creates a pnpm workspace with multiple packages.")); console.log(chalk2.yellow("Please install pnpm first:\n")); console.log(chalk2.cyan(" npm install -g pnpm")); console.log(chalk2.dim(" or")); console.log(chalk2.cyan(" curl -fsSL https://get.pnpm.io/install.sh | sh -\n")); console.log(chalk2.dim("Learn more: https://pnpm.io/installation\n")); process.exit(1); } let answers = {}; if (!projectName) { const nameAnswer = await inquirer.prompt([ { type: "input", name: "projectName", message: "What is your project name?", default: "my-product", validate: (input) => { if (!/^[a-z0-9-]+$/.test(input)) { return "Project name must be lowercase with hyphens only"; } return true; } } ]); projectName = nameAnswer.projectName; } answers = await inquirer.prompt([ { type: "input", name: "displayName", message: "Display name for your product:", default: projectName?.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ") }, { type: "input", name: "description", message: "Short description:", default: `A product built with Company Auth` }, { type: "input", name: "authServiceUrl", message: "Company Auth Service URL:", default: "http://localhost:3000" }, { type: "input", name: "supabaseUrl", message: "Supabase URL (leave empty to set later):", default: "" }, { type: "input", name: "supabaseAnonKey", message: "Supabase Anon Key (leave empty to set later):", default: "" }, { type: "confirm", name: "installPackages", message: "Download and install Bold packages from GitHub (agents, auth-sdk, logger)?", default: true }, { type: "password", name: "githubToken", message: "GitHub Personal Access Token (for template and packages):", validate: (input) => { if (!input || input.length === 0) { return "GitHub token is required to download the template from the repository"; } return true; } }, { type: "confirm", name: "registerProduct", message: "Register product with Auth Service now?", default: !options.skipRegister, when: () => !options.skipRegister } ]); const spinner = ora2("Creating product...").start(); try { const result = await createProduct({ projectName, displayName: answers.displayName, description: answers.description, authServiceUrl: answers.authServiceUrl, supabaseUrl: answers.supabaseUrl, supabaseAnonKey: answers.supabaseAnonKey, skipInstall: options.skipInstall, githubToken: answers.githubToken }); spinner.succeed("Product created successfully!"); if (answers.installPackages && answers.githubToken) { console.log(""); try { await downloadAndBuildPackages({ workspaceRoot: result.workspaceRoot, githubToken: answers.githubToken }); } catch (error) { console.log(chalk2.red("\n\u274C Failed to download packages:")); console.log(chalk2.red(` ${error instanceof Error ? error.message : "Unknown error"}`)); console.log(chalk2.yellow("\n\u{1F4A1} You can download packages manually from the repository")); console.log(chalk2.dim(" Or run the CLI again with a valid GitHub token\n")); } } else if (answers.installPackages && !answers.githubToken) { console.log(chalk2.yellow("\n\u26A0\uFE0F Skipped package installation (no token provided)\n")); } if (answers.registerProduct) { const registrationAnswers = await inquirer.prompt([ { type: "input", name: "adminToken", message: "Auth Service admin API key (from ADMIN_API_KEY env):" } ]); spinner.start("Registering product with Auth Service..."); try { const registration = await registerProduct({ name: answers.displayName, slug: projectName, description: answers.description, authServiceUrl: answers.authServiceUrl, adminToken: registrationAnswers.adminToken }); spinner.succeed("Product registered!"); spinner.start("Updating .env.local with credentials..."); const { updateEnvWithCredentials } = await import("./create-product-AQCGMPYG.mjs"); await updateEnvWithCredentials( result.appDir, registration.productId, registration.apiKey, registrationAnswers.adminToken // Save admin token for server-side operations ); spinner.succeed(".env.local updated with real credentials!"); console.log(chalk2.green("\n\u2705 Product ID: ") + chalk2.bold(registration.productId)); console.log(chalk2.green("\u2705 API Key: ") + chalk2.bold(registration.apiKey)); console.log(chalk2.green("\u2705 Credentials saved to .env.local\n")); } catch (error) { spinner.warn("Product registration failed. You can register manually later."); console.log(chalk2.red(` Error: ${error instanceof Error ? error.message : "Unknown error"} `)); console.log(chalk2.yellow("\n\u26A0\uFE0F Your .env.local has placeholder values. Update them after manual registration.\n")); } } else { console.log(chalk2.yellow("\n\u26A0\uFE0F Product not registered. Your .env.local has placeholder values.\n")); console.log(chalk2.dim("To register later, visit the admin dashboard or use the API.\n")); } console.log(chalk2.bold.green("\n\u2728 All done!\n")); console.log(chalk2.bold("Your pnpm workspace is ready! \u{1F389}\n")); console.log("Next steps:"); console.log(chalk2.cyan(` cd ${projectName}`)); if (!answers.installPackages) { console.log(chalk2.cyan(" pnpm install # Install workspace dependencies")); } if (!answers.registerProduct) { console.log(chalk2.yellow(" # Update AGENCY_AUTH_PRODUCT_ID and AGENCY_AUTH_API_KEY in apps/${projectName}/.env.local")); } if (!answers.supabaseUrl || !answers.supabaseAnonKey) { console.log(chalk2.yellow(" # Update Supabase credentials in apps/${projectName}/.env.local")); } console.log(chalk2.cyan(" pnpm dev # Start your app")); console.log(""); console.log(chalk2.dim("Your app is in apps/" + projectName + "/")); console.log(chalk2.dim("Packages are in packages/")); console.log(""); } catch (error) { spinner.fail("Failed to create product"); console.error(chalk2.red("\nError:"), error instanceof Error ? error.message : error); process.exit(1); } }); program.parse();