@01.software/init
Advanced tools
+502
-90
| #!/usr/bin/env node | ||
| // src/index.ts | ||
| import pc2 from "picocolors"; | ||
| import pc3 from "picocolors"; | ||
@@ -66,5 +66,2 @@ // src/detect.ts | ||
| } | ||
| function supportsMcp(env) { | ||
| return env === "nextjs" || env === "node" || env === "edge"; | ||
| } | ||
| function getClientKeyEnvVar(env) { | ||
@@ -152,3 +149,3 @@ switch (env) { | ||
| if (env === "other") { | ||
| return { env, clientKey: "", secretKey: "", setupMcp: false, packageManager: void 0 }; | ||
| return { env, clientKey: "", secretKey: "", aiTools: [], authMethod: "skip", packageManager: void 0 }; | ||
| } | ||
@@ -191,21 +188,51 @@ let packageManager; | ||
| } | ||
| const keys = keyPrompts.length > 0 ? await prompts(keyPrompts, { onCancel }) : {}; | ||
| let setupMcp = false; | ||
| if (supportsMcp(env)) { | ||
| const result = await prompts( | ||
| const { selectedTools } = await prompts( | ||
| { | ||
| type: "multiselect", | ||
| name: "selectedTools", | ||
| message: "Connect AI tools:", | ||
| choices: [ | ||
| { title: "Claude Code", description: ".mcp.json + .claude/ docs", value: "claude" }, | ||
| { title: "Cursor", description: ".cursor/mcp.json", value: "cursor" }, | ||
| { title: "VS Code", description: ".vscode/mcp.json", value: "vscode" }, | ||
| { title: "Windsurf", description: "~/.codeium/windsurf/mcp_config.json", value: "windsurf" }, | ||
| { title: "Skip", value: "skip" } | ||
| ], | ||
| hint: "space to select" | ||
| }, | ||
| { onCancel } | ||
| ); | ||
| const aiTools = Array.isArray(selectedTools) ? selectedTools.filter((t) => t !== "skip") : []; | ||
| let authMethod = "skip"; | ||
| let keys = {}; | ||
| if (aiTools.length > 0 && env !== "vanilla") { | ||
| const { method } = await prompts( | ||
| { | ||
| type: "confirm", | ||
| name: "setupMcp", | ||
| message: "Set up MCP integration? (.mcp.json)", | ||
| initial: true | ||
| type: "select", | ||
| name: "method", | ||
| message: "API keys:", | ||
| choices: [ | ||
| { title: "Browser login (recommended)", value: "browser" }, | ||
| { title: "Enter manually", value: "manual" }, | ||
| { title: "Skip for now", value: "skip" } | ||
| ] | ||
| }, | ||
| { onCancel } | ||
| ); | ||
| setupMcp = result.setupMcp; | ||
| authMethod = method ?? "skip"; | ||
| if (authMethod === "manual") { | ||
| keys = await prompts(keyPrompts, { onCancel }); | ||
| } | ||
| } else if (env !== "vanilla") { | ||
| if (keyPrompts.length > 0) { | ||
| keys = await prompts(keyPrompts, { onCancel }); | ||
| } | ||
| authMethod = "skip"; | ||
| } | ||
| return { | ||
| env, | ||
| clientKey: keys.clientKey ?? "", | ||
| secretKey: keys.secretKey ?? "", | ||
| setupMcp, | ||
| clientKey: authMethod === "browser" ? "" : keys.clientKey ?? "", | ||
| secretKey: authMethod === "browser" ? "" : keys.secretKey ?? "", | ||
| aiTools, | ||
| authMethod, | ||
| packageManager | ||
@@ -219,3 +246,3 @@ }; | ||
| import { execSync } from "child_process"; | ||
| import pc from "picocolors"; | ||
| import pc2 from "picocolors"; | ||
@@ -316,2 +343,273 @@ // src/templates.ts | ||
| // src/browser-auth.ts | ||
| import { randomBytes } from "crypto"; | ||
| import { createServer } from "http"; | ||
| import { execFile, exec } from "child_process"; | ||
| import { platform } from "os"; | ||
| import { URL } from "url"; | ||
| import pc from "picocolors"; | ||
| var DEFAULT_WEB_URL = process.env.SOFTWARE_WEB_URL || "https://01.software"; | ||
| var TIMEOUT_MS = 5 * 60 * 1e3; | ||
| function escapeHtml(s) { | ||
| return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """); | ||
| } | ||
| function openBrowser(url) { | ||
| const os = platform(); | ||
| const onError = () => { | ||
| console.log( | ||
| pc.yellow( | ||
| `Could not open browser automatically. Open this URL manually: | ||
| ${url}` | ||
| ) | ||
| ); | ||
| }; | ||
| if (os === "win32") { | ||
| exec(`start "" "${url}"`, (err) => { | ||
| if (err) onError(); | ||
| }); | ||
| } else { | ||
| const cmd = os === "darwin" ? "open" : "xdg-open"; | ||
| execFile(cmd, [url], (err) => { | ||
| if (err) onError(); | ||
| }); | ||
| } | ||
| } | ||
| var PAGE_STYLE = `*{margin:0;box-sizing:border-box} | ||
| body{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;color:#252525} | ||
| @media(prefers-color-scheme:dark){body{background:#252525;color:#f5f5f5}} | ||
| .card{text-align:center;padding:2rem 2.5rem;border-radius:10px;max-width:380px;width:100%} | ||
| .icon{width:40px;height:40px;margin:0 auto 1rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.25rem} | ||
| .icon.ok{background:rgba(0,0,0,.05);color:#252525} | ||
| .icon.err{background:rgba(220,38,38,.08);color:#dc2626} | ||
| @media(prefers-color-scheme:dark){.icon.ok{background:rgba(255,255,255,.08);color:#f5f5f5}} | ||
| h1{font-size:.875rem;font-weight:600;margin-bottom:.375rem} | ||
| p{font-size:.75rem;color:#737373;line-height:1.5}`; | ||
| var SUCCESS_HTML = `<!DOCTYPE html> | ||
| <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login</title> | ||
| <style>${PAGE_STYLE}</style> | ||
| </head><body><div class="card"><div class="icon ok">\u2713</div><h1>Authenticated</h1><p>You can close this tab and return to the terminal.</p></div></body></html>`; | ||
| var ERROR_HTML = (msg) => `<!DOCTYPE html> | ||
| <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login Error</title> | ||
| <style>${PAGE_STYLE}</style> | ||
| </head><body><div class="card"><div class="icon err">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`; | ||
| async function startBrowserAuth(options) { | ||
| const state = randomBytes(32).toString("hex"); | ||
| const webUrl = options?.webUrl ?? DEFAULT_WEB_URL; | ||
| return new Promise((resolve, reject) => { | ||
| const server = createServer((req, res) => { | ||
| if (!req.url) { | ||
| res.writeHead(400).end(); | ||
| return; | ||
| } | ||
| const url = new URL(req.url, `http://localhost`); | ||
| if (url.pathname !== "/callback") { | ||
| res.writeHead(404).end(); | ||
| return; | ||
| } | ||
| const receivedState = url.searchParams.get("state"); | ||
| const clientKey = url.searchParams.get("clientKey"); | ||
| const secretKey = url.searchParams.get("secretKey"); | ||
| const tenant = url.searchParams.get("tenant"); | ||
| const tenantIdParam = url.searchParams.get("tenantId"); | ||
| const error = url.searchParams.get("error"); | ||
| if (error) { | ||
| res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(ERROR_HTML(error)); | ||
| console.error(pc.red(`Login failed: ${error}`)); | ||
| cleanup(new Error(`Login failed: ${error}`)); | ||
| return; | ||
| } | ||
| if (receivedState !== state) { | ||
| res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(ERROR_HTML("State mismatch \u2014 possible CSRF attack.")); | ||
| console.error(pc.red("Login failed: state mismatch.")); | ||
| cleanup(new Error("Login failed: state mismatch.")); | ||
| return; | ||
| } | ||
| if (!clientKey || !secretKey || !tenant) { | ||
| res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(ERROR_HTML("Missing credentials in callback.")); | ||
| console.error(pc.red("Login failed: missing credentials.")); | ||
| cleanup(new Error("Login failed: missing credentials.")); | ||
| return; | ||
| } | ||
| res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(SUCCESS_HTML); | ||
| console.log(pc.green(` | ||
| Logged in successfully!`)); | ||
| console.log(pc.dim(`Tenant: ${tenant}`)); | ||
| const result = { | ||
| clientKey, | ||
| secretKey, | ||
| tenantName: tenant, | ||
| ...tenantIdParam ? { tenantId: tenantIdParam } : {} | ||
| }; | ||
| cleanup(null, result); | ||
| }); | ||
| let timeout; | ||
| let completed = false; | ||
| function cleanup(err, result) { | ||
| if (completed) return; | ||
| completed = true; | ||
| clearTimeout(timeout); | ||
| server.close(() => { | ||
| if (err) { | ||
| reject(err); | ||
| } else { | ||
| resolve(result); | ||
| } | ||
| }); | ||
| } | ||
| server.listen(0, "127.0.0.1", () => { | ||
| const addr = server.address(); | ||
| if (!addr || typeof addr === "string") { | ||
| reject(new Error("Failed to start local server.")); | ||
| return; | ||
| } | ||
| const port = addr.port; | ||
| timeout = setTimeout(() => { | ||
| console.error(pc.red("\nLogin timed out (5 minutes). Please try again.")); | ||
| cleanup(new Error("Login timed out")); | ||
| }, TIMEOUT_MS); | ||
| const params = new URLSearchParams({ port: String(port), state }); | ||
| if (options?.tenantId) { | ||
| params.set("tenantId", options.tenantId); | ||
| } | ||
| const loginUrl = `${webUrl}/cli-auth?${params.toString()}`; | ||
| console.log(pc.dim("Opening browser for login...")); | ||
| console.log(pc.dim(`If the browser does not open, visit: | ||
| ${loginUrl}`)); | ||
| openBrowser(loginUrl); | ||
| }); | ||
| server.on("error", (err) => { | ||
| reject(err); | ||
| }); | ||
| }); | ||
| } | ||
| // src/ai-docs.ts | ||
| function generateClaudeMd(ctx) { | ||
| const featuresSection = ctx.features && ctx.features.length > 0 ? ctx.features.map((f) => `- ${f}`).join("\n") : "- See console"; | ||
| const collectionsSection = ctx.collections && ctx.collections.length > 0 ? ctx.collections.join(", ") : "Run `01 schema list`"; | ||
| return `# 01.software SDK \u2014 ${ctx.tenantName} | ||
| ## Connection | ||
| - Client Key: \`NEXT_PUBLIC_SOFTWARE_CLIENT_KEY\` (env) | ||
| - Server Key: \`SOFTWARE_SECRET_KEY\` (env) | ||
| - MCP: \`.mcp.json\` | ||
| ## Active Features | ||
| ${featuresSection} | ||
| ## Collections | ||
| ${collectionsSection} | ||
| ## MCP Quick Reference | ||
| | Tool | Use | | ||
| |------|-----| | ||
| | \`query-collection\` | List/filter documents | | ||
| | \`create-collection\` | Create documents | | ||
| | \`update-field-config\` | Hide unused fields | | ||
| | \`get-tenant-context\` | Show active features & collections | | ||
| ## CLI | ||
| - \`01 query <collection>\` \u2014 query data | ||
| - \`01 schema show <collection>\` \u2014 inspect fields | ||
| - \`01 schema list\` \u2014 list all collections | ||
| ## Initial Setup | ||
| Run \`/field-config\` in Claude Code to configure field visibility for your use case. | ||
| `; | ||
| } | ||
| function getSkillFiles() { | ||
| return [ | ||
| { | ||
| filename: "field-config.md", | ||
| content: `Configure field visibility for this tenant using MCP tools. | ||
| Steps: | ||
| 1. Use \`list-configurable-fields\` to see current visibility settings | ||
| 2. Identify fields/collections not needed for your use case | ||
| 3. Use \`update-field-config\` to hide them | ||
| Common setups: | ||
| - Blog only: hide \`ecommerce\`, \`customers\`, \`videos\` collections | ||
| - Store: hide \`posts\`, \`documents\`, \`galleries\`, \`flows\` collections | ||
| - Minimal: hide all except the collections you actively use | ||
| Ask me: "Show current field config" or "Hide ecommerce fields" | ||
| ` | ||
| }, | ||
| { | ||
| filename: "query.md", | ||
| content: `Query collections using the MCP \`query-collection\` tool or CLI. | ||
| MCP examples: | ||
| - List products: \`query-collection\` with collection="products", limit=10 | ||
| - Filter by status: add where={"status":{"equals":"published"}} | ||
| - Sort by date: sort="-createdAt" | ||
| - Paginate: page=2, limit=20 | ||
| CLI examples: | ||
| - \`01 query products --limit 10\` | ||
| - \`01 query orders --where '{"status":{"equals":"paid"}}'\` | ||
| - \`01 schema show products\` \u2014 inspect available fields | ||
| SDK (server): | ||
| \`\`\`typescript | ||
| const { docs } = await serverClient.collection('products').find({ | ||
| where: { status: { equals: 'published' } }, | ||
| sort: '-createdAt', | ||
| limit: 10, | ||
| }) | ||
| \`\`\` | ||
| ` | ||
| }, | ||
| { | ||
| filename: "order-flow.md", | ||
| content: `Complete order flow from creation to fulfillment. | ||
| States: pending \u2192 paid \u2192 preparing \u2192 shipped \u2192 delivered \u2192 confirmed | ||
| 1. Create order: \`create-order\` with orderNumber, customerSnapshot, orderProducts, totalAmount | ||
| 2. Mark paid: \`update-order\` with status="paid" (after payment gateway confirms) | ||
| 3. Fulfill: \`create-fulfillment\` with items and carrier/trackingNumber | ||
| 4. Returns: \`create-return\` or \`return-with-refund\` (atomic) | ||
| Free orders: omit paymentId, totalAmount=0 \u2192 auto-transitions to paid | ||
| CLI: \`01 order create --help\` for full options | ||
| ` | ||
| }, | ||
| { | ||
| filename: "schema.md", | ||
| content: `Inspect collection schemas to understand available fields. | ||
| MCP: use \`get-collection-fields\` with collectionSlug | ||
| CLI: | ||
| - \`01 schema list\` \u2014 all available collections | ||
| - \`01 schema show <collection>\` \u2014 field names, types, required status | ||
| Common collections: products, orders, customers, posts, documents, images | ||
| Use \`get-tenant-context\` to see which collections are active for this tenant. | ||
| ` | ||
| } | ||
| ]; | ||
| } | ||
| async function fetchTenantContext(clientKey, secretKey) { | ||
| try { | ||
| const apiUrl = process.env.SOFTWARE_API_URL || "https://api.01.software"; | ||
| const base64 = Buffer.from(`${clientKey}:${secretKey}`).toString("base64"); | ||
| const res = await fetch(`${apiUrl}/api/tenants/context`, { | ||
| headers: { "x-api-key": base64 } | ||
| }); | ||
| if (!res.ok) return null; | ||
| const data = await res.json(); | ||
| return { | ||
| tenantName: data.tenant?.name || "", | ||
| features: data.features || [], | ||
| collections: data.collections || [] | ||
| }; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
| // src/init.ts | ||
@@ -329,3 +627,3 @@ var SECRET_KEY_ENV_VAR = "SOFTWARE_SECRET_KEY"; | ||
| if (wantsReactQuery) deps.push("@tanstack/react-query"); | ||
| console.log(pc.dim(` Installing ${deps.join(" and ")}...`)); | ||
| console.log(pc2.dim(` Installing ${deps.join(" and ")}...`)); | ||
| const wsPatched = packageManager === "pnpm" && patchPnpmWorkspace(cwd); | ||
@@ -340,4 +638,4 @@ const pkgs = deps.join(" "); | ||
| const msg = String(err.stderr || "").trim() || String(err.stdout || "").trim() || String(error); | ||
| console.log(pc.red(" Failed to install dependencies:")); | ||
| console.log(pc.dim(` ${msg}`)); | ||
| console.log(pc2.red(" Failed to install dependencies:")); | ||
| console.log(pc2.dim(` ${msg}`)); | ||
| throw error; | ||
@@ -352,6 +650,6 @@ } finally { | ||
| if (fs2.existsSync(clientPath)) { | ||
| console.log(pc.yellow(" Skipped"), relativePath(cwd, clientPath), pc.dim("(already exists)")); | ||
| console.log(pc2.yellow(" Skipped"), relativePath(cwd, clientPath), pc2.dim("(already exists)")); | ||
| } else { | ||
| fs2.writeFileSync(clientPath, getClientTemplate(env, clientKeyEnvVar)); | ||
| console.log(pc.green(" Created"), relativePath(cwd, clientPath)); | ||
| console.log(pc2.green(" Created"), relativePath(cwd, clientPath)); | ||
| } | ||
@@ -362,6 +660,6 @@ } | ||
| if (fs2.existsSync(queryProviderPath)) { | ||
| console.log(pc.yellow(" Skipped"), relativePath(cwd, queryProviderPath), pc.dim("(already exists)")); | ||
| console.log(pc2.yellow(" Skipped"), relativePath(cwd, queryProviderPath), pc2.dim("(already exists)")); | ||
| } else { | ||
| fs2.writeFileSync(queryProviderPath, getQueryProviderTemplate(env)); | ||
| console.log(pc.green(" Created"), relativePath(cwd, queryProviderPath)); | ||
| console.log(pc2.green(" Created"), relativePath(cwd, queryProviderPath)); | ||
| } | ||
@@ -372,9 +670,9 @@ } | ||
| if (fs2.existsSync(serverPath)) { | ||
| console.log(pc.yellow(" Skipped"), relativePath(cwd, serverPath), pc.dim("(already exists)")); | ||
| console.log(pc2.yellow(" Skipped"), relativePath(cwd, serverPath), pc2.dim("(already exists)")); | ||
| } else { | ||
| fs2.writeFileSync(serverPath, getServerTemplate(env, clientKeyEnvVar, SECRET_KEY_ENV_VAR)); | ||
| console.log(pc.green(" Created"), relativePath(cwd, serverPath)); | ||
| console.log(pc2.green(" Created"), relativePath(cwd, serverPath)); | ||
| } | ||
| } | ||
| if (env !== "vanilla" && env !== "edge") { | ||
| if (env !== "vanilla" && env !== "edge" && answers.authMethod !== "browser") { | ||
| const envPath = path2.join(cwd, ".env"); | ||
@@ -390,48 +688,162 @@ const envContent = getEnvContent( | ||
| if (existing.includes(clientKeyEnvVar)) { | ||
| console.log(pc.yellow(" Skipped"), ".env", pc.dim("(keys already present)")); | ||
| console.log(pc2.yellow(" Skipped"), ".env", pc2.dim("(keys already present)")); | ||
| } else { | ||
| fs2.appendFileSync(envPath, envContent); | ||
| console.log(pc.green(" Updated"), ".env"); | ||
| console.log(pc2.green(" Updated"), ".env"); | ||
| } | ||
| } else { | ||
| fs2.writeFileSync(envPath, envContent.trimStart()); | ||
| console.log(pc.green(" Created"), ".env"); | ||
| console.log(pc2.green(" Created"), ".env"); | ||
| } | ||
| } | ||
| if (answers.setupMcp) { | ||
| const clientKey = answers.clientKey || ""; | ||
| const secretKey = answers.secretKey || ""; | ||
| const apiKey = clientKey && secretKey ? Buffer.from(`${clientKey}:${secretKey}`).toString("base64") : "YOUR_API_KEY"; | ||
| const mcpPath = path2.join(cwd, ".mcp.json"); | ||
| if (fs2.existsSync(mcpPath)) { | ||
| try { | ||
| const existing = JSON.parse(fs2.readFileSync(mcpPath, "utf-8")); | ||
| if (existing.mcpServers?.["01software"]) { | ||
| console.log(pc.yellow(" Skipped"), ".mcp.json", pc.dim("(01software already configured)")); | ||
| let clientKey = answers.clientKey; | ||
| let secretKey = answers.secretKey; | ||
| let tenantName = ""; | ||
| if (answers.authMethod === "browser" && answers.aiTools.length > 0) { | ||
| try { | ||
| console.log(); | ||
| const creds = await startBrowserAuth(); | ||
| clientKey = creds.clientKey; | ||
| secretKey = creds.secretKey; | ||
| tenantName = creds.tenantName; | ||
| if (env !== "vanilla" && env !== "edge" && clientKey) { | ||
| const envPath = path2.join(cwd, ".env"); | ||
| const envContent = getEnvContent( | ||
| clientKey, | ||
| secretKey, | ||
| clientKeyEnvVar, | ||
| wantsServer ? SECRET_KEY_ENV_VAR : null | ||
| ); | ||
| if (fs2.existsSync(envPath)) { | ||
| const existing = fs2.readFileSync(envPath, "utf-8"); | ||
| if (!existing.includes(clientKeyEnvVar)) { | ||
| fs2.appendFileSync(envPath, envContent); | ||
| console.log(pc2.green(" Updated"), ".env", pc2.dim("(added API keys)")); | ||
| } | ||
| } else { | ||
| existing.mcpServers = existing.mcpServers || {}; | ||
| existing.mcpServers["01software"] = getMcpServerEntry(apiKey); | ||
| fs2.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + "\n"); | ||
| console.log(pc.green(" Updated"), ".mcp.json"); | ||
| fs2.writeFileSync(envPath, envContent.trimStart()); | ||
| console.log(pc2.green(" Created"), ".env"); | ||
| } | ||
| } catch { | ||
| console.log(pc.yellow(" Skipped"), ".mcp.json", pc.dim("(could not parse existing file)")); | ||
| } | ||
| } else { | ||
| fs2.writeFileSync(mcpPath, getMcpConfigTemplate(apiKey)); | ||
| console.log(pc.green(" Created"), ".mcp.json"); | ||
| } catch (err) { | ||
| console.log( | ||
| pc2.yellow(" Browser auth skipped:"), | ||
| err instanceof Error ? err.message : String(err) | ||
| ); | ||
| } | ||
| const gitignorePath = path2.join(cwd, ".gitignore"); | ||
| if (fs2.existsSync(gitignorePath)) { | ||
| const gitignore = fs2.readFileSync(gitignorePath, "utf-8"); | ||
| if (!gitignore.includes(".mcp.json")) { | ||
| fs2.appendFileSync(gitignorePath, "\n# MCP config (contains API key)\n.mcp.json\n"); | ||
| console.log(pc.green(" Updated"), ".gitignore", pc.dim("(added .mcp.json)")); | ||
| } | ||
| if (answers.aiTools.length > 0) { | ||
| const apiKey = clientKey && secretKey ? Buffer.from(`${clientKey}:${secretKey}`).toString("base64") : "YOUR_API_KEY"; | ||
| for (const tool of answers.aiTools) { | ||
| writeMcpConfig(tool, cwd, apiKey); | ||
| } | ||
| addToGitignore(cwd, answers.aiTools); | ||
| if (answers.aiTools.includes("claude")) { | ||
| await writeClaudeDocs(cwd, clientKey, secretKey, tenantName); | ||
| } | ||
| } | ||
| } | ||
| function writeMcpConfig(tool, cwd, apiKey) { | ||
| let configPath; | ||
| let displayPath; | ||
| switch (tool) { | ||
| case "claude": | ||
| configPath = path2.join(cwd, ".mcp.json"); | ||
| displayPath = ".mcp.json"; | ||
| break; | ||
| case "cursor": | ||
| configPath = path2.join(cwd, ".cursor", "mcp.json"); | ||
| displayPath = ".cursor/mcp.json"; | ||
| fs2.mkdirSync(path2.dirname(configPath), { recursive: true }); | ||
| break; | ||
| case "vscode": | ||
| configPath = path2.join(cwd, ".vscode", "mcp.json"); | ||
| displayPath = ".vscode/mcp.json"; | ||
| fs2.mkdirSync(path2.dirname(configPath), { recursive: true }); | ||
| break; | ||
| case "windsurf": { | ||
| const home = process.env.HOME || process.env.USERPROFILE || ""; | ||
| if (!home) { | ||
| console.log(pc2.yellow(" Skipped windsurf"), pc2.dim("(HOME not set)")); | ||
| return; | ||
| } | ||
| } else { | ||
| fs2.writeFileSync(gitignorePath, "# MCP config (contains API key)\n.mcp.json\n"); | ||
| console.log(pc.green(" Created"), ".gitignore", pc.dim("(added .mcp.json)")); | ||
| configPath = path2.join(home, ".codeium", "windsurf", "mcp_config.json"); | ||
| displayPath = configPath; | ||
| fs2.mkdirSync(path2.dirname(configPath), { recursive: true }); | ||
| break; | ||
| } | ||
| default: | ||
| return; | ||
| } | ||
| if (fs2.existsSync(configPath)) { | ||
| try { | ||
| const existing = JSON.parse(fs2.readFileSync(configPath, "utf-8")); | ||
| if (existing.mcpServers?.["01software"]) { | ||
| console.log(pc2.yellow(" Skipped"), displayPath, pc2.dim("(01software already configured)")); | ||
| return; | ||
| } | ||
| existing.mcpServers = existing.mcpServers || {}; | ||
| existing.mcpServers["01software"] = getMcpServerEntry(apiKey); | ||
| fs2.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n"); | ||
| console.log(pc2.green(" Updated"), displayPath); | ||
| } catch { | ||
| console.log(pc2.yellow(" Skipped"), displayPath, pc2.dim("(could not parse existing file)")); | ||
| } | ||
| } else { | ||
| fs2.writeFileSync(configPath, getMcpConfigTemplate(apiKey)); | ||
| console.log(pc2.green(" Created"), displayPath); | ||
| } | ||
| } | ||
| function addToGitignore(cwd, tools) { | ||
| const entries = []; | ||
| if (tools.includes("claude")) entries.push(".mcp.json"); | ||
| if (tools.includes("cursor")) entries.push(".cursor/mcp.json"); | ||
| if (tools.includes("vscode")) entries.push(".vscode/mcp.json"); | ||
| if (entries.length === 0) return; | ||
| const gitignorePath = path2.join(cwd, ".gitignore"); | ||
| const existing = fs2.existsSync(gitignorePath) ? fs2.readFileSync(gitignorePath, "utf-8") : ""; | ||
| const toAdd = entries.filter((e) => !existing.includes(e)); | ||
| if (toAdd.length === 0) return; | ||
| const content = "\n# MCP configs (contain API key)\n" + toAdd.join("\n") + "\n"; | ||
| if (fs2.existsSync(gitignorePath)) { | ||
| fs2.appendFileSync(gitignorePath, content); | ||
| } else { | ||
| fs2.writeFileSync(gitignorePath, content.trimStart()); | ||
| } | ||
| console.log(pc2.green(" Updated"), ".gitignore", pc2.dim(`(added ${toAdd.join(", ")})`)); | ||
| } | ||
| async function writeClaudeDocs(cwd, clientKey, secretKey, tenantName) { | ||
| let ctx = { | ||
| tenantName: tenantName || "Your Tenant", | ||
| features: void 0, | ||
| collections: void 0 | ||
| }; | ||
| if (clientKey && secretKey) { | ||
| const fetched = await fetchTenantContext(clientKey, secretKey); | ||
| if (fetched) { | ||
| ctx = { | ||
| tenantName: fetched.tenantName || ctx.tenantName, | ||
| features: fetched.features, | ||
| collections: fetched.collections | ||
| }; | ||
| } | ||
| } | ||
| const claudeDir = path2.join(cwd, ".claude"); | ||
| const commandsDir = path2.join(claudeDir, "commands"); | ||
| fs2.mkdirSync(commandsDir, { recursive: true }); | ||
| const claudeMdPath = path2.join(claudeDir, "CLAUDE.md"); | ||
| if (!fs2.existsSync(claudeMdPath)) { | ||
| fs2.writeFileSync(claudeMdPath, generateClaudeMd(ctx)); | ||
| console.log(pc2.green(" Created"), ".claude/CLAUDE.md"); | ||
| } else { | ||
| console.log(pc2.yellow(" Skipped"), ".claude/CLAUDE.md", pc2.dim("(already exists)")); | ||
| } | ||
| for (const { filename, content } of getSkillFiles()) { | ||
| const skillPath = path2.join(commandsDir, filename); | ||
| if (!fs2.existsSync(skillPath)) { | ||
| fs2.writeFileSync(skillPath, content); | ||
| console.log(pc2.green(" Created"), `.claude/commands/${filename}`); | ||
| } | ||
| } | ||
| } | ||
| function relativePath(cwd, filePath) { | ||
@@ -494,4 +906,4 @@ return path2.relative(cwd, filePath); | ||
| console.log(); | ||
| console.log(pc2.bold(" @01.software/init")); | ||
| console.log(pc2.dim(" Initialize 01.software SDK in your project")); | ||
| console.log(pc3.bold(" @01.software/init")); | ||
| console.log(pc3.dim(" Initialize 01.software SDK in your project")); | ||
| console.log(); | ||
@@ -501,7 +913,7 @@ const info = detectProject(cwd); | ||
| if (info.parseError) { | ||
| console.log(pc2.red(" Could not parse package.json (invalid JSON).")); | ||
| console.log(pc2.dim(" Fix the syntax error and try again.")); | ||
| console.log(pc3.red(" Could not parse package.json (invalid JSON).")); | ||
| console.log(pc3.dim(" Fix the syntax error and try again.")); | ||
| } else { | ||
| console.log(pc2.red(" No package.json found in the current directory.")); | ||
| console.log(pc2.dim(" Run this command inside an existing project.")); | ||
| console.log(pc3.red(" No package.json found in the current directory.")); | ||
| console.log(pc3.dim(" Run this command inside an existing project.")); | ||
| } | ||
@@ -515,3 +927,3 @@ console.log(); | ||
| if (info.env !== "node") { | ||
| console.log(pc2.dim(` Detected: ${detectedParts.join(" / ")}`)); | ||
| console.log(pc3.dim(` Detected: ${detectedParts.join(" / ")}`)); | ||
| console.log(); | ||
@@ -522,7 +934,7 @@ } | ||
| if (!answers) { | ||
| console.log(pc2.yellow(" Cancelled.")); | ||
| console.log(pc3.yellow(" Cancelled.")); | ||
| process.exit(0); | ||
| } | ||
| if (answers.env === "other") { | ||
| console.log(pc2.yellow(" Manual setup required for your framework:")); | ||
| console.log(pc3.yellow(" Manual setup required for your framework:")); | ||
| console.log(OTHER_FRAMEWORK_GUIDE); | ||
@@ -538,3 +950,3 @@ process.exit(0); | ||
| console.log(); | ||
| console.log(pc2.green(" Done!")); | ||
| console.log(pc3.green(" Done!")); | ||
| console.log(); | ||
@@ -544,30 +956,30 @@ console.log(" Next steps:"); | ||
| if (env === "nextjs") { | ||
| console.log(pc2.dim(" Add QueryProvider to your root layout:")); | ||
| console.log(pc3.dim(" Add QueryProvider to your root layout:")); | ||
| console.log(); | ||
| console.log(pc2.cyan(" import { QueryProvider } from '@/lib/software/query-provider'")); | ||
| console.log(pc2.cyan(" <QueryProvider>{children}</QueryProvider>")); | ||
| console.log(pc3.cyan(" import { QueryProvider } from '@/lib/software/query-provider'")); | ||
| console.log(pc3.cyan(" <QueryProvider>{children}</QueryProvider>")); | ||
| console.log(); | ||
| } else if (env === "react-vite" || env === "react-cra") { | ||
| console.log(pc2.dim(" Wrap your app entry with QueryProvider:")); | ||
| console.log(pc3.dim(" Wrap your app entry with QueryProvider:")); | ||
| console.log(); | ||
| console.log(pc2.cyan(" import { QueryProvider } from './lib/software/query-provider'")); | ||
| console.log(pc2.cyan(" <QueryProvider><App /></QueryProvider>")); | ||
| console.log(pc3.cyan(" import { QueryProvider } from './lib/software/query-provider'")); | ||
| console.log(pc3.cyan(" <QueryProvider><App /></QueryProvider>")); | ||
| console.log(); | ||
| } else if (env === "vanilla") { | ||
| console.log(pc2.dim(" Replace YOUR_CLIENT_KEY in lib/software/client.ts")); | ||
| console.log(pc3.dim(" Replace YOUR_CLIENT_KEY in lib/software/client.ts")); | ||
| console.log(); | ||
| console.log(pc2.cyan(" import { client } from './lib/software/client'")); | ||
| console.log(pc2.cyan(" const posts = await client.from('posts').find()")); | ||
| console.log(pc3.cyan(" import { client } from './lib/software/client'")); | ||
| console.log(pc3.cyan(" const posts = await client.from('posts').find()")); | ||
| console.log(); | ||
| } else if (env === "node") { | ||
| console.log(pc2.dim(" Use the server client:")); | ||
| console.log(pc3.dim(" Use the server client:")); | ||
| console.log(); | ||
| console.log(pc2.cyan(" import { serverClient } from './lib/software/server'")); | ||
| console.log(pc2.cyan(" const posts = await serverClient.from('posts').find()")); | ||
| console.log(pc3.cyan(" import { serverClient } from './lib/software/server'")); | ||
| console.log(pc3.cyan(" const posts = await serverClient.from('posts').find()")); | ||
| console.log(); | ||
| } else if (env === "edge") { | ||
| console.log(pc2.dim(" Pass your env bindings to createEdgeClient():")); | ||
| console.log(pc3.dim(" Pass your env bindings to createEdgeClient():")); | ||
| console.log(); | ||
| console.log(pc2.cyan(" import { createEdgeClient } from './lib/software/server'")); | ||
| console.log(pc2.cyan(" const serverClient = createEdgeClient(env.CLIENT_KEY, env.SECRET_KEY)")); | ||
| console.log(pc3.cyan(" import { createEdgeClient } from './lib/software/server'")); | ||
| console.log(pc3.cyan(" const serverClient = createEdgeClient(env.CLIENT_KEY, env.SECRET_KEY)")); | ||
| console.log(); | ||
@@ -578,11 +990,11 @@ } | ||
| if (missingClientKey || missingSecretKey) { | ||
| console.log(pc2.dim(" Update .env with your API keys")); | ||
| console.log(pc3.dim(" Update .env with your API keys")); | ||
| console.log(); | ||
| } | ||
| if (answers.setupMcp && (!answers.clientKey || !answers.secretKey)) { | ||
| console.log(pc2.dim(" Update .mcp.json x-api-key with Base64(clientKey:secretKey)")); | ||
| if (answers.aiTools.length > 0 && (!answers.clientKey || !answers.secretKey)) { | ||
| console.log(pc3.dim(" Update .mcp.json x-api-key with Base64(clientKey:secretKey)")); | ||
| console.log(); | ||
| } | ||
| if (env !== "vanilla") { | ||
| console.log(pc2.cyan(` ${run} dev`)); | ||
| console.log(pc3.cyan(` ${run} dev`)); | ||
| console.log(); | ||
@@ -592,6 +1004,6 @@ } | ||
| if (error instanceof Error && error.message === "cancelled") { | ||
| console.log(pc2.yellow(" Cancelled.")); | ||
| console.log(pc3.yellow(" Cancelled.")); | ||
| process.exit(0); | ||
| } | ||
| console.error(pc2.red(" Error:"), error); | ||
| console.error(pc3.red(" Error:"), error); | ||
| process.exit(1); | ||
@@ -598,0 +1010,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/detect.ts","../src/prompts.ts","../src/init.ts","../src/templates.ts"],"sourcesContent":["import pc from 'picocolors'\nimport { detectProject } from './detect'\nimport type { ProjectEnv } from './detect'\nimport { promptUser } from './prompts'\nimport { init } from './init'\n\nconst ENV_LABELS: Record<ProjectEnv, string> = {\n nextjs: 'Next.js',\n 'react-vite': 'React + Vite',\n 'react-cra': 'React + Webpack/CRA',\n vanilla: 'Vanilla JS',\n node: 'Node.js',\n edge: 'Edge runtime',\n other: 'Other framework',\n}\n\n// Manual setup guide for unsupported frameworks\nconst OTHER_FRAMEWORK_GUIDE = `\n Astro, Remix, SvelteKit, and other meta-frameworks have their own\n environment conventions. Manual setup:\n\n 1. Install the SDK:\n npm install @01.software/sdk\n\n 2. Browser client (client islands / RSC):\n import { createClient } from '@01.software/sdk'\n export const client = createClient({ clientKey: 'YOUR_CLIENT_KEY' })\n\n 3. Server client (SSR / loaders / endpoints):\n import { createServerClient } from '@01.software/sdk'\n export const serverClient = createServerClient({\n clientKey: process.env.PUBLIC_SOFTWARE_CLIENT_KEY!, // prefix varies by framework\n secretKey: process.env.SOFTWARE_SECRET_KEY!,\n })\n\n 4. Docs: https://01.software/docs/guide/quickstart\n`\n\nasync function main() {\n const cwd = process.cwd()\n\n console.log()\n console.log(pc.bold(' @01.software/init'))\n console.log(pc.dim(' Initialize 01.software SDK in your project'))\n console.log()\n\n // 1. Detect project\n const info = detectProject(cwd)\n\n if (!info.hasPackageJson) {\n if (info.parseError) {\n console.log(pc.red(' Could not parse package.json (invalid JSON).'))\n console.log(pc.dim(' Fix the syntax error and try again.'))\n } else {\n console.log(pc.red(' No package.json found in the current directory.'))\n console.log(pc.dim(' Run this command inside an existing project.'))\n }\n console.log()\n process.exit(1)\n }\n\n // Show detected environment\n const detectedParts: string[] = [ENV_LABELS[info.env]]\n if (info.packageManager) detectedParts.push(info.packageManager)\n if (info.srcDir) detectedParts.push('src/')\n\n if (info.env !== 'node') {\n // Only show detected label when it's unambiguous\n console.log(pc.dim(` Detected: ${detectedParts.join(' / ')}`))\n console.log()\n }\n\n try {\n // 2. Prompt\n const answers = await promptUser(info.hasSdk, info.env, info.packageManager)\n if (!answers) {\n console.log(pc.yellow(' Cancelled.'))\n process.exit(0)\n }\n\n // \"Other\" framework — print guide and exit\n if (answers.env === 'other') {\n console.log(pc.yellow(' Manual setup required for your framework:'))\n console.log(OTHER_FRAMEWORK_GUIDE)\n process.exit(0)\n }\n\n // Resolve package manager from detection or user selection\n const resolvedPm = info.packageManager ?? answers.packageManager ?? 'npm'\n const resolvedInfo = { ...info, packageManager: resolvedPm }\n\n // 3. Init\n console.log()\n await init(cwd, resolvedInfo, answers)\n\n // 4. Next steps\n const env = answers.env\n const run = resolvedPm === 'npm' ? 'npm run' : resolvedPm\n\n console.log()\n console.log(pc.green(' Done!'))\n console.log()\n console.log(' Next steps:')\n console.log()\n\n if (env === 'nextjs') {\n console.log(pc.dim(' Add QueryProvider to your root layout:'))\n console.log()\n console.log(pc.cyan(\" import { QueryProvider } from '@/lib/software/query-provider'\"))\n console.log(pc.cyan(' <QueryProvider>{children}</QueryProvider>'))\n console.log()\n } else if (env === 'react-vite' || env === 'react-cra') {\n console.log(pc.dim(' Wrap your app entry with QueryProvider:'))\n console.log()\n console.log(pc.cyan(\" import { QueryProvider } from './lib/software/query-provider'\"))\n console.log(pc.cyan(' <QueryProvider><App /></QueryProvider>'))\n console.log()\n } else if (env === 'vanilla') {\n console.log(pc.dim(' Replace YOUR_CLIENT_KEY in lib/software/client.ts'))\n console.log()\n console.log(pc.cyan(\" import { client } from './lib/software/client'\"))\n console.log(pc.cyan(\" const posts = await client.from('posts').find()\"))\n console.log()\n } else if (env === 'node') {\n console.log(pc.dim(' Use the server client:'))\n console.log()\n console.log(pc.cyan(\" import { serverClient } from './lib/software/server'\"))\n console.log(pc.cyan(\" const posts = await serverClient.from('posts').find()\"))\n console.log()\n } else if (env === 'edge') {\n console.log(pc.dim(' Pass your env bindings to createEdgeClient():'))\n console.log()\n console.log(pc.cyan(\" import { createEdgeClient } from './lib/software/server'\"))\n console.log(pc.cyan(' const serverClient = createEdgeClient(env.CLIENT_KEY, env.SECRET_KEY)'))\n console.log()\n }\n\n const missingClientKey = env !== 'vanilla' && !answers.clientKey\n const missingSecretKey = (env === 'nextjs' || env === 'node') && !answers.secretKey\n if (missingClientKey || missingSecretKey) {\n console.log(pc.dim(' Update .env with your API keys'))\n console.log()\n }\n if (answers.setupMcp && (!answers.clientKey || !answers.secretKey)) {\n console.log(pc.dim(' Update .mcp.json x-api-key with Base64(clientKey:secretKey)'))\n console.log()\n }\n if (env !== 'vanilla') {\n console.log(pc.cyan(` ${run} dev`))\n console.log()\n }\n } catch (error) {\n if (error instanceof Error && error.message === 'cancelled') {\n console.log(pc.yellow(' Cancelled.'))\n process.exit(0)\n }\n console.error(pc.red(' Error:'), error)\n process.exit(1)\n }\n}\n\nmain()\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun'\n\nexport type ProjectEnv =\n | 'nextjs'\n | 'react-vite'\n | 'react-cra'\n | 'vanilla'\n | 'node'\n | 'edge'\n | 'other' // Astro, Remix, SvelteKit, etc. — print guidance only\n\nexport interface ProjectInfo {\n hasPackageJson: boolean\n parseError?: boolean\n env: ProjectEnv\n packageManager: PackageManager | null\n hasSdk: boolean\n srcDir: boolean\n}\n\nexport function detectProject(cwd: string): ProjectInfo {\n const pkgPath = path.join(cwd, 'package.json')\n const hasPackageJson = fs.existsSync(pkgPath)\n\n let env: ProjectEnv = 'node'\n let hasSdk = false\n\n if (hasPackageJson) {\n let pkg: Record<string, unknown>\n try {\n pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n } catch {\n return { hasPackageJson: false, parseError: true, env: 'node', packageManager: null, hasSdk: false, srcDir: false }\n }\n\n const deps = {\n ...((pkg.dependencies as Record<string, string>) || {}),\n ...((pkg.devDependencies as Record<string, string>) || {}),\n }\n hasSdk = '@01.software/sdk' in deps\n\n if ('next' in deps) {\n env = 'nextjs'\n } else if ('astro' in deps || '@astrojs/node' in deps) {\n env = 'other'\n } else if ('@remix-run/node' in deps || '@remix-run/react' in deps) {\n env = 'other'\n } else if ('@sveltejs/kit' in deps) {\n env = 'other'\n } else if ('react' in deps) {\n if ('vite' in deps) {\n env = 'react-vite'\n } else if ('react-scripts' in deps) {\n env = 'react-cra'\n } else {\n // React + unknown bundler (Webpack, Parcel, etc.) — leave as 'node'\n // so the prompt selector is shown\n env = 'node'\n }\n }\n // else: no react/next → 'node' (covers real Node.js, Deno, Bun, etc.)\n }\n\n // Detect package manager from lockfile\n let packageManager: PackageManager | null = null\n if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {\n packageManager = 'pnpm'\n } else if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {\n packageManager = 'yarn'\n } else if (\n fs.existsSync(path.join(cwd, 'bun.lockb')) ||\n fs.existsSync(path.join(cwd, 'bun.lock'))\n ) {\n packageManager = 'bun'\n } else if (fs.existsSync(path.join(cwd, 'package-lock.json'))) {\n packageManager = 'npm'\n }\n\n // Detect src directory structure\n const srcDir =\n env === 'nextjs'\n ? fs.existsSync(path.join(cwd, 'src', 'app'))\n : fs.existsSync(path.join(cwd, 'src'))\n\n return { hasPackageJson, env, packageManager, hasSdk, srcDir }\n}\n\n/** Environments that generate a browser client file */\nexport function needsClient(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'react-vite' || env === 'react-cra' || env === 'vanilla'\n}\n\n/** Environments that generate a server client file */\nexport function needsServer(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'node' || env === 'edge'\n}\n\n/** Environments that generate a query-provider file */\nexport function needsReactQuery(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'react-vite' || env === 'react-cra'\n}\n\n/** Environments that support MCP setup (need both client + secret key) */\nexport function supportsMcp(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'node' || env === 'edge'\n}\n\n/** Get the env var name for the public client key */\nexport function getClientKeyEnvVar(env: ProjectEnv): string {\n switch (env) {\n case 'nextjs':\n return 'NEXT_PUBLIC_SOFTWARE_CLIENT_KEY'\n case 'react-vite':\n return 'VITE_SOFTWARE_CLIENT_KEY'\n case 'react-cra':\n return 'REACT_APP_SOFTWARE_CLIENT_KEY'\n default:\n return 'SOFTWARE_CLIENT_KEY'\n }\n}\n","import prompts from 'prompts'\nimport type { PackageManager, ProjectEnv } from './detect'\nimport { supportsMcp } from './detect'\n\nexport interface InitAnswers {\n env: ProjectEnv\n clientKey: string\n secretKey: string\n setupMcp: boolean\n packageManager?: PackageManager\n}\n\n// Envs that need the user to disambiguate (couldn't be auto-detected)\nconst AMBIGUOUS_ENVS: ProjectEnv[] = ['node']\n\nexport async function promptUser(\n hasSdk: boolean,\n detectedEnv: ProjectEnv,\n detectedPm: PackageManager | null,\n): Promise<InitAnswers | null> {\n const onCancel = () => {\n throw new Error('cancelled')\n }\n\n // Confirm re-init if SDK already installed\n if (hasSdk) {\n const { proceed } = await prompts(\n {\n type: 'confirm',\n name: 'proceed',\n message: '@01.software/sdk is already installed. Re-initialize?',\n initial: false,\n },\n { onCancel },\n )\n if (!proceed) return null\n }\n\n // Ask for environment when auto-detection is ambiguous\n let env = detectedEnv\n if (AMBIGUOUS_ENVS.includes(detectedEnv)) {\n const { selectedEnv } = await prompts(\n {\n type: 'select',\n name: 'selectedEnv',\n message: 'Which environment are you using?',\n choices: [\n {\n title: 'Next.js',\n description: 'Full-stack React (client + server)',\n value: 'nextjs',\n },\n {\n title: 'React + Vite',\n description: 'Client-only SPA with Vite',\n value: 'react-vite',\n },\n {\n title: 'React + Webpack / other',\n description: 'Client-only SPA, CRA or custom bundler',\n value: 'react-cra',\n },\n {\n title: 'Vanilla JS',\n description: 'Browser app without a framework',\n value: 'vanilla',\n },\n {\n title: 'Node.js / Bun / Deno',\n description: 'Server-only, no browser client',\n value: 'node',\n },\n {\n title: 'Edge runtime',\n description: 'Cloudflare Workers, Vercel Edge Functions',\n value: 'edge',\n },\n {\n title: 'Other (Astro / Remix / SvelteKit…)',\n description: 'Print manual setup guide for your framework',\n value: 'other',\n },\n ],\n },\n { onCancel },\n )\n env = selectedEnv as ProjectEnv\n }\n\n // \"Other\" frameworks — we can't scaffold correctly; exit with guidance\n if (env === 'other') {\n return { env, clientKey: '', secretKey: '', setupMcp: false, packageManager: undefined }\n }\n\n // Ask for package manager if no lockfile detected\n let packageManager: PackageManager | undefined\n if (!detectedPm) {\n const { pm } = await prompts(\n {\n type: 'select',\n name: 'pm',\n message: 'Which package manager do you use?',\n choices: [\n { title: 'npm', value: 'npm' },\n { title: 'pnpm', value: 'pnpm' },\n { title: 'yarn', value: 'yarn' },\n { title: 'bun', value: 'bun' },\n ],\n },\n { onCancel },\n )\n packageManager = pm\n }\n\n const needsSecretKey = env === 'nextjs' || env === 'node' || env === 'edge'\n\n const keyPrompts: prompts.PromptObject[] = []\n\n // Vanilla JS doesn't use env vars — no prompts for keys\n if (env !== 'vanilla') {\n keyPrompts.push({\n type: 'text',\n name: 'clientKey',\n message: 'Client Key (optional, saved to .env)',\n initial: '',\n })\n }\n\n if (needsSecretKey) {\n keyPrompts.push({\n type: 'text',\n name: 'secretKey',\n message: 'Secret Key (optional, saved to .env)',\n initial: '',\n })\n }\n\n const keys = keyPrompts.length > 0 ? await prompts(keyPrompts, { onCancel }) : {}\n\n // MCP requires both client + secret key\n let setupMcp = false\n if (supportsMcp(env)) {\n const result = await prompts(\n {\n type: 'confirm',\n name: 'setupMcp',\n message: 'Set up MCP integration? (.mcp.json)',\n initial: true,\n },\n { onCancel },\n )\n setupMcp = result.setupMcp\n }\n\n return {\n env,\n clientKey: (keys as Record<string, string>).clientKey ?? '',\n secretKey: (keys as Record<string, string>).secretKey ?? '',\n setupMcp,\n packageManager,\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { execSync } from 'node:child_process'\nimport pc from 'picocolors'\nimport type { PackageManager, ProjectInfo } from './detect'\nimport {\n needsClient,\n needsServer,\n needsReactQuery,\n getClientKeyEnvVar,\n} from './detect'\nimport type { InitAnswers } from './prompts'\nimport {\n getClientTemplate,\n getQueryProviderTemplate,\n getServerTemplate,\n getEnvContent,\n getMcpConfigTemplate,\n getMcpServerEntry,\n} from './templates'\n\ntype ResolvedProjectInfo = Omit<ProjectInfo, 'packageManager'> & {\n packageManager: PackageManager\n}\n\nconst SECRET_KEY_ENV_VAR = 'SOFTWARE_SECRET_KEY'\n\nexport async function init(\n cwd: string,\n info: ResolvedProjectInfo,\n answers: InitAnswers,\n): Promise<void> {\n const { packageManager, srcDir } = info\n const env = answers.env\n const baseDir = srcDir ? path.join(cwd, 'src') : cwd\n\n const clientKeyEnvVar = getClientKeyEnvVar(env)\n const wantsClient = needsClient(env)\n const wantsServer = needsServer(env)\n const wantsReactQuery = needsReactQuery(env)\n\n // 1. Install dependencies\n const deps = ['@01.software/sdk']\n if (wantsReactQuery) deps.push('@tanstack/react-query')\n\n console.log(pc.dim(` Installing ${deps.join(' and ')}...`))\n\n // pnpm-workspace.yaml without packages: breaks pnpm add — patch temporarily\n const wsPatched = packageManager === 'pnpm' && patchPnpmWorkspace(cwd)\n\n const pkgs = deps.join(' ')\n const pnpmFlag = hasPnpmWorkspace(cwd) ? ' -w' : ''\n const addCmd =\n packageManager === 'pnpm'\n ? `pnpm add${pnpmFlag} ${pkgs}`\n : packageManager === 'yarn'\n ? `yarn add ${pkgs}`\n : packageManager === 'bun'\n ? `bun add ${pkgs}`\n : `npm install ${pkgs}`\n\n try {\n execSync(addCmd, { cwd, stdio: 'pipe' })\n } catch (error) {\n const err = error as { stdout?: Buffer; stderr?: Buffer }\n const msg =\n String(err.stderr || '').trim() ||\n String(err.stdout || '').trim() ||\n String(error)\n console.log(pc.red(' Failed to install dependencies:'))\n console.log(pc.dim(` ${msg}`))\n throw error\n } finally {\n if (wsPatched) restorePnpmWorkspace(cwd)\n }\n\n // 2. Write lib/software/ files\n const libDir = path.join(baseDir, 'lib', 'software')\n fs.mkdirSync(libDir, { recursive: true })\n\n // Client (browser)\n if (wantsClient) {\n const clientPath = path.join(libDir, 'client.ts')\n if (fs.existsSync(clientPath)) {\n console.log(pc.yellow(' Skipped'), relativePath(cwd, clientPath), pc.dim('(already exists)'))\n } else {\n fs.writeFileSync(clientPath, getClientTemplate(env, clientKeyEnvVar))\n console.log(pc.green(' Created'), relativePath(cwd, clientPath))\n }\n }\n\n // Query Provider (React)\n if (wantsReactQuery) {\n const queryProviderPath = path.join(libDir, 'query-provider.tsx')\n if (fs.existsSync(queryProviderPath)) {\n console.log(pc.yellow(' Skipped'), relativePath(cwd, queryProviderPath), pc.dim('(already exists)'))\n } else {\n fs.writeFileSync(queryProviderPath, getQueryProviderTemplate(env))\n console.log(pc.green(' Created'), relativePath(cwd, queryProviderPath))\n }\n }\n\n // Server\n if (wantsServer) {\n const serverPath = path.join(libDir, 'server.ts')\n if (fs.existsSync(serverPath)) {\n console.log(pc.yellow(' Skipped'), relativePath(cwd, serverPath), pc.dim('(already exists)'))\n } else {\n fs.writeFileSync(serverPath, getServerTemplate(env, clientKeyEnvVar, SECRET_KEY_ENV_VAR))\n console.log(pc.green(' Created'), relativePath(cwd, serverPath))\n }\n }\n\n // 3. Append to .env (vanilla uses inline key, no env file needed)\n if (env !== 'vanilla' && env !== 'edge') {\n const envPath = path.join(cwd, '.env')\n const envContent = getEnvContent(\n answers.clientKey || '',\n answers.secretKey || '',\n clientKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n )\n\n if (fs.existsSync(envPath)) {\n const existing = fs.readFileSync(envPath, 'utf-8')\n if (existing.includes(clientKeyEnvVar)) {\n console.log(pc.yellow(' Skipped'), '.env', pc.dim('(keys already present)'))\n } else {\n fs.appendFileSync(envPath, envContent)\n console.log(pc.green(' Updated'), '.env')\n }\n } else {\n fs.writeFileSync(envPath, envContent.trimStart())\n console.log(pc.green(' Created'), '.env')\n }\n }\n\n // 4. Write .mcp.json (MCP integration)\n if (answers.setupMcp) {\n const clientKey = answers.clientKey || ''\n const secretKey = answers.secretKey || ''\n const apiKey =\n clientKey && secretKey\n ? Buffer.from(`${clientKey}:${secretKey}`).toString('base64')\n : 'YOUR_API_KEY'\n\n const mcpPath = path.join(cwd, '.mcp.json')\n if (fs.existsSync(mcpPath)) {\n try {\n const existing = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'))\n if (existing.mcpServers?.['01software']) {\n console.log(pc.yellow(' Skipped'), '.mcp.json', pc.dim('(01software already configured)'))\n } else {\n existing.mcpServers = existing.mcpServers || {}\n existing.mcpServers['01software'] = getMcpServerEntry(apiKey)\n fs.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + '\\n')\n console.log(pc.green(' Updated'), '.mcp.json')\n }\n } catch {\n console.log(pc.yellow(' Skipped'), '.mcp.json', pc.dim('(could not parse existing file)'))\n }\n } else {\n fs.writeFileSync(mcpPath, getMcpConfigTemplate(apiKey))\n console.log(pc.green(' Created'), '.mcp.json')\n }\n\n // 5. Add .mcp.json to .gitignore (contains API key)\n const gitignorePath = path.join(cwd, '.gitignore')\n if (fs.existsSync(gitignorePath)) {\n const gitignore = fs.readFileSync(gitignorePath, 'utf-8')\n if (!gitignore.includes('.mcp.json')) {\n fs.appendFileSync(gitignorePath, '\\n# MCP config (contains API key)\\n.mcp.json\\n')\n console.log(pc.green(' Updated'), '.gitignore', pc.dim('(added .mcp.json)'))\n }\n } else {\n fs.writeFileSync(gitignorePath, '# MCP config (contains API key)\\n.mcp.json\\n')\n console.log(pc.green(' Created'), '.gitignore', pc.dim('(added .mcp.json)'))\n }\n }\n}\n\nfunction relativePath(cwd: string, filePath: string): string {\n return path.relative(cwd, filePath)\n}\n\nconst WS_FILE = 'pnpm-workspace.yaml'\nconst WS_BACKUP = 'pnpm-workspace.yaml.bak'\n\nfunction hasPnpmWorkspace(cwd: string): boolean {\n return fs.existsSync(path.join(cwd, WS_FILE))\n}\n\nfunction patchPnpmWorkspace(cwd: string): boolean {\n const wsPath = path.join(cwd, WS_FILE)\n if (!fs.existsSync(wsPath)) return false\n const content = fs.readFileSync(wsPath, 'utf-8')\n if (content.includes('packages:')) return false\n fs.copyFileSync(wsPath, path.join(cwd, WS_BACKUP))\n fs.writeFileSync(wsPath, content.trimEnd() + '\\npackages: []\\n')\n return true\n}\n\nfunction restorePnpmWorkspace(cwd: string): void {\n const backupPath = path.join(cwd, WS_BACKUP)\n if (!fs.existsSync(backupPath)) return\n fs.copyFileSync(backupPath, path.join(cwd, WS_FILE))\n fs.unlinkSync(backupPath)\n}\n","import type { ProjectEnv } from './detect'\n\n// ── Client template (browser) ────────────────────────────────────────\n\nexport function getClientTemplate(env: ProjectEnv, clientKeyEnvVar: string): string {\n if (env === 'nextjs') {\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n clientKey: process.env.${clientKeyEnvVar}!,\n})\n`\n }\n\n if (env === 'react-cra') {\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n clientKey: process.env.${clientKeyEnvVar}!,\n})\n`\n }\n\n if (env === 'vanilla') {\n return `import { createClient } from '@01.software/sdk'\n\n// Replace 'YOUR_CLIENT_KEY' with your actual client key from the 01.software console\nexport const client = createClient({\n clientKey: 'YOUR_CLIENT_KEY',\n})\n`\n }\n\n // react-vite (import.meta.env)\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n clientKey: import.meta.env.${clientKeyEnvVar},\n})\n`\n}\n\n// ── Query Provider template ──────────────────────────────────────────\n\nexport function getQueryProviderTemplate(env: ProjectEnv): string {\n const useClientDirective = env === 'nextjs' ? \"'use client'\\n\\n\" : ''\n\n return `${useClientDirective}import { QueryClientProvider } from '@tanstack/react-query'\nimport { client } from './client'\n\nexport function QueryProvider({ children }: { children: React.ReactNode }) {\n return (\n <QueryClientProvider client={client.queryClient}>\n {children}\n </QueryClientProvider>\n )\n}\n`\n}\n\n// ── Server template ──────────────────────────────────────────────────\n\nexport function getServerTemplate(\n env: ProjectEnv,\n clientKeyEnvVar: string,\n secretKeyEnvVar: string,\n): string {\n if (env === 'edge') {\n return `import { createServerClient } from '@01.software/sdk'\n\n// Edge runtime: pass your env bindings here\n// e.g. Cloudflare Workers: use env.SOFTWARE_CLIENT_KEY from the handler context\n// e.g. Vercel Edge: use process.env.${clientKeyEnvVar}\nexport function createEdgeClient(clientKey: string, secretKey: string) {\n return createServerClient({ clientKey, secretKey })\n}\n`\n }\n\n return `import { createServerClient } from '@01.software/sdk'\n\nexport const serverClient = createServerClient({\n clientKey: process.env.${clientKeyEnvVar}!,\n secretKey: process.env.${secretKeyEnvVar}!,\n})\n`\n}\n\n// ── Env file content ─────────────────────────────────────────────────\n\nexport function getEnvContent(\n clientKey: string,\n secretKey: string,\n clientKeyEnvVar: string,\n secretKeyEnvVar: string | null,\n): string {\n let content = `\\n# 01.software\\n${clientKeyEnvVar}=${clientKey}\\n`\n if (secretKeyEnvVar) {\n content += `${secretKeyEnvVar}=${secretKey}\\n`\n }\n return content\n}\n\n// ── MCP config ───────────────────────────────────────────────────────\n\nexport function getMcpServerEntry(apiKey: string) {\n return {\n type: 'http' as const,\n url: 'https://mcp.01.software/mcp',\n headers: { 'x-api-key': apiKey },\n }\n}\n\nexport function getMcpConfigTemplate(apiKey: string): string {\n return (\n JSON.stringify(\n { mcpServers: { '01software': getMcpServerEntry(apiKey) } },\n null,\n 2,\n ) + '\\n'\n )\n}\n"],"mappings":"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","names":["pc","fs","path","path","fs","pc"]} | ||
| {"version":3,"sources":["../src/index.ts","../src/detect.ts","../src/prompts.ts","../src/init.ts","../src/templates.ts","../src/browser-auth.ts","../src/ai-docs.ts"],"sourcesContent":["import pc from 'picocolors'\nimport { detectProject } from './detect'\nimport type { ProjectEnv } from './detect'\nimport { promptUser } from './prompts'\nimport { init } from './init'\n\nconst ENV_LABELS: Record<ProjectEnv, string> = {\n nextjs: 'Next.js',\n 'react-vite': 'React + Vite',\n 'react-cra': 'React + Webpack/CRA',\n vanilla: 'Vanilla JS',\n node: 'Node.js',\n edge: 'Edge runtime',\n other: 'Other framework',\n}\n\n// Manual setup guide for unsupported frameworks\nconst OTHER_FRAMEWORK_GUIDE = `\n Astro, Remix, SvelteKit, and other meta-frameworks have their own\n environment conventions. Manual setup:\n\n 1. Install the SDK:\n npm install @01.software/sdk\n\n 2. Browser client (client islands / RSC):\n import { createClient } from '@01.software/sdk'\n export const client = createClient({ clientKey: 'YOUR_CLIENT_KEY' })\n\n 3. Server client (SSR / loaders / endpoints):\n import { createServerClient } from '@01.software/sdk'\n export const serverClient = createServerClient({\n clientKey: process.env.PUBLIC_SOFTWARE_CLIENT_KEY!, // prefix varies by framework\n secretKey: process.env.SOFTWARE_SECRET_KEY!,\n })\n\n 4. Docs: https://01.software/docs/guide/quickstart\n`\n\nasync function main() {\n const cwd = process.cwd()\n\n console.log()\n console.log(pc.bold(' @01.software/init'))\n console.log(pc.dim(' Initialize 01.software SDK in your project'))\n console.log()\n\n // 1. Detect project\n const info = detectProject(cwd)\n\n if (!info.hasPackageJson) {\n if (info.parseError) {\n console.log(pc.red(' Could not parse package.json (invalid JSON).'))\n console.log(pc.dim(' Fix the syntax error and try again.'))\n } else {\n console.log(pc.red(' No package.json found in the current directory.'))\n console.log(pc.dim(' Run this command inside an existing project.'))\n }\n console.log()\n process.exit(1)\n }\n\n // Show detected environment\n const detectedParts: string[] = [ENV_LABELS[info.env]]\n if (info.packageManager) detectedParts.push(info.packageManager)\n if (info.srcDir) detectedParts.push('src/')\n\n if (info.env !== 'node') {\n // Only show detected label when it's unambiguous\n console.log(pc.dim(` Detected: ${detectedParts.join(' / ')}`))\n console.log()\n }\n\n try {\n // 2. Prompt\n const answers = await promptUser(info.hasSdk, info.env, info.packageManager)\n if (!answers) {\n console.log(pc.yellow(' Cancelled.'))\n process.exit(0)\n }\n\n // \"Other\" framework — print guide and exit\n if (answers.env === 'other') {\n console.log(pc.yellow(' Manual setup required for your framework:'))\n console.log(OTHER_FRAMEWORK_GUIDE)\n process.exit(0)\n }\n\n // Resolve package manager from detection or user selection\n const resolvedPm = info.packageManager ?? answers.packageManager ?? 'npm'\n const resolvedInfo = { ...info, packageManager: resolvedPm }\n\n // 3. Init\n console.log()\n await init(cwd, resolvedInfo, answers)\n\n // 4. Next steps\n const env = answers.env\n const run = resolvedPm === 'npm' ? 'npm run' : resolvedPm\n\n console.log()\n console.log(pc.green(' Done!'))\n console.log()\n console.log(' Next steps:')\n console.log()\n\n if (env === 'nextjs') {\n console.log(pc.dim(' Add QueryProvider to your root layout:'))\n console.log()\n console.log(pc.cyan(\" import { QueryProvider } from '@/lib/software/query-provider'\"))\n console.log(pc.cyan(' <QueryProvider>{children}</QueryProvider>'))\n console.log()\n } else if (env === 'react-vite' || env === 'react-cra') {\n console.log(pc.dim(' Wrap your app entry with QueryProvider:'))\n console.log()\n console.log(pc.cyan(\" import { QueryProvider } from './lib/software/query-provider'\"))\n console.log(pc.cyan(' <QueryProvider><App /></QueryProvider>'))\n console.log()\n } else if (env === 'vanilla') {\n console.log(pc.dim(' Replace YOUR_CLIENT_KEY in lib/software/client.ts'))\n console.log()\n console.log(pc.cyan(\" import { client } from './lib/software/client'\"))\n console.log(pc.cyan(\" const posts = await client.from('posts').find()\"))\n console.log()\n } else if (env === 'node') {\n console.log(pc.dim(' Use the server client:'))\n console.log()\n console.log(pc.cyan(\" import { serverClient } from './lib/software/server'\"))\n console.log(pc.cyan(\" const posts = await serverClient.from('posts').find()\"))\n console.log()\n } else if (env === 'edge') {\n console.log(pc.dim(' Pass your env bindings to createEdgeClient():'))\n console.log()\n console.log(pc.cyan(\" import { createEdgeClient } from './lib/software/server'\"))\n console.log(pc.cyan(' const serverClient = createEdgeClient(env.CLIENT_KEY, env.SECRET_KEY)'))\n console.log()\n }\n\n const missingClientKey = env !== 'vanilla' && !answers.clientKey\n const missingSecretKey = (env === 'nextjs' || env === 'node') && !answers.secretKey\n if (missingClientKey || missingSecretKey) {\n console.log(pc.dim(' Update .env with your API keys'))\n console.log()\n }\n if (answers.aiTools.length > 0 && (!answers.clientKey || !answers.secretKey)) {\n console.log(pc.dim(' Update .mcp.json x-api-key with Base64(clientKey:secretKey)'))\n console.log()\n }\n if (env !== 'vanilla') {\n console.log(pc.cyan(` ${run} dev`))\n console.log()\n }\n } catch (error) {\n if (error instanceof Error && error.message === 'cancelled') {\n console.log(pc.yellow(' Cancelled.'))\n process.exit(0)\n }\n console.error(pc.red(' Error:'), error)\n process.exit(1)\n }\n}\n\nmain()\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun'\n\nexport type ProjectEnv =\n | 'nextjs'\n | 'react-vite'\n | 'react-cra'\n | 'vanilla'\n | 'node'\n | 'edge'\n | 'other' // Astro, Remix, SvelteKit, etc. — print guidance only\n\nexport interface ProjectInfo {\n hasPackageJson: boolean\n parseError?: boolean\n env: ProjectEnv\n packageManager: PackageManager | null\n hasSdk: boolean\n srcDir: boolean\n}\n\nexport function detectProject(cwd: string): ProjectInfo {\n const pkgPath = path.join(cwd, 'package.json')\n const hasPackageJson = fs.existsSync(pkgPath)\n\n let env: ProjectEnv = 'node'\n let hasSdk = false\n\n if (hasPackageJson) {\n let pkg: Record<string, unknown>\n try {\n pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n } catch {\n return { hasPackageJson: false, parseError: true, env: 'node', packageManager: null, hasSdk: false, srcDir: false }\n }\n\n const deps = {\n ...((pkg.dependencies as Record<string, string>) || {}),\n ...((pkg.devDependencies as Record<string, string>) || {}),\n }\n hasSdk = '@01.software/sdk' in deps\n\n if ('next' in deps) {\n env = 'nextjs'\n } else if ('astro' in deps || '@astrojs/node' in deps) {\n env = 'other'\n } else if ('@remix-run/node' in deps || '@remix-run/react' in deps) {\n env = 'other'\n } else if ('@sveltejs/kit' in deps) {\n env = 'other'\n } else if ('react' in deps) {\n if ('vite' in deps) {\n env = 'react-vite'\n } else if ('react-scripts' in deps) {\n env = 'react-cra'\n } else {\n // React + unknown bundler (Webpack, Parcel, etc.) — leave as 'node'\n // so the prompt selector is shown\n env = 'node'\n }\n }\n // else: no react/next → 'node' (covers real Node.js, Deno, Bun, etc.)\n }\n\n // Detect package manager from lockfile\n let packageManager: PackageManager | null = null\n if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {\n packageManager = 'pnpm'\n } else if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {\n packageManager = 'yarn'\n } else if (\n fs.existsSync(path.join(cwd, 'bun.lockb')) ||\n fs.existsSync(path.join(cwd, 'bun.lock'))\n ) {\n packageManager = 'bun'\n } else if (fs.existsSync(path.join(cwd, 'package-lock.json'))) {\n packageManager = 'npm'\n }\n\n // Detect src directory structure\n const srcDir =\n env === 'nextjs'\n ? fs.existsSync(path.join(cwd, 'src', 'app'))\n : fs.existsSync(path.join(cwd, 'src'))\n\n return { hasPackageJson, env, packageManager, hasSdk, srcDir }\n}\n\n/** Environments that generate a browser client file */\nexport function needsClient(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'react-vite' || env === 'react-cra' || env === 'vanilla'\n}\n\n/** Environments that generate a server client file */\nexport function needsServer(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'node' || env === 'edge'\n}\n\n/** Environments that generate a query-provider file */\nexport function needsReactQuery(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'react-vite' || env === 'react-cra'\n}\n\n/** Environments that support MCP setup (need both client + secret key) */\nexport function supportsMcp(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'node' || env === 'edge'\n}\n\n/** Get the env var name for the public client key */\nexport function getClientKeyEnvVar(env: ProjectEnv): string {\n switch (env) {\n case 'nextjs':\n return 'NEXT_PUBLIC_SOFTWARE_CLIENT_KEY'\n case 'react-vite':\n return 'VITE_SOFTWARE_CLIENT_KEY'\n case 'react-cra':\n return 'REACT_APP_SOFTWARE_CLIENT_KEY'\n default:\n return 'SOFTWARE_CLIENT_KEY'\n }\n}\n","import prompts from 'prompts'\nimport type { PackageManager, ProjectEnv } from './detect'\n\nexport type AiTool = 'claude' | 'cursor' | 'vscode' | 'windsurf'\nexport type AuthMethod = 'browser' | 'manual' | 'skip'\n\nexport interface InitAnswers {\n env: ProjectEnv\n clientKey: string\n secretKey: string\n aiTools: AiTool[]\n authMethod: AuthMethod\n packageManager?: PackageManager\n}\n\n// Envs that need the user to disambiguate (couldn't be auto-detected)\nconst AMBIGUOUS_ENVS: ProjectEnv[] = ['node']\n\nexport async function promptUser(\n hasSdk: boolean,\n detectedEnv: ProjectEnv,\n detectedPm: PackageManager | null,\n): Promise<InitAnswers | null> {\n const onCancel = () => {\n throw new Error('cancelled')\n }\n\n // Confirm re-init if SDK already installed\n if (hasSdk) {\n const { proceed } = await prompts(\n {\n type: 'confirm',\n name: 'proceed',\n message: '@01.software/sdk is already installed. Re-initialize?',\n initial: false,\n },\n { onCancel },\n )\n if (!proceed) return null\n }\n\n // Ask for environment when auto-detection is ambiguous\n let env = detectedEnv\n if (AMBIGUOUS_ENVS.includes(detectedEnv)) {\n const { selectedEnv } = await prompts(\n {\n type: 'select',\n name: 'selectedEnv',\n message: 'Which environment are you using?',\n choices: [\n {\n title: 'Next.js',\n description: 'Full-stack React (client + server)',\n value: 'nextjs',\n },\n {\n title: 'React + Vite',\n description: 'Client-only SPA with Vite',\n value: 'react-vite',\n },\n {\n title: 'React + Webpack / other',\n description: 'Client-only SPA, CRA or custom bundler',\n value: 'react-cra',\n },\n {\n title: 'Vanilla JS',\n description: 'Browser app without a framework',\n value: 'vanilla',\n },\n {\n title: 'Node.js / Bun / Deno',\n description: 'Server-only, no browser client',\n value: 'node',\n },\n {\n title: 'Edge runtime',\n description: 'Cloudflare Workers, Vercel Edge Functions',\n value: 'edge',\n },\n {\n title: 'Other (Astro / Remix / SvelteKit…)',\n description: 'Print manual setup guide for your framework',\n value: 'other',\n },\n ],\n },\n { onCancel },\n )\n env = selectedEnv as ProjectEnv\n }\n\n // \"Other\" frameworks — we can't scaffold correctly; exit with guidance\n if (env === 'other') {\n return { env, clientKey: '', secretKey: '', aiTools: [], authMethod: 'skip', packageManager: undefined }\n }\n\n // Ask for package manager if no lockfile detected\n let packageManager: PackageManager | undefined\n if (!detectedPm) {\n const { pm } = await prompts(\n {\n type: 'select',\n name: 'pm',\n message: 'Which package manager do you use?',\n choices: [\n { title: 'npm', value: 'npm' },\n { title: 'pnpm', value: 'pnpm' },\n { title: 'yarn', value: 'yarn' },\n { title: 'bun', value: 'bun' },\n ],\n },\n { onCancel },\n )\n packageManager = pm\n }\n\n const needsSecretKey = env === 'nextjs' || env === 'node' || env === 'edge'\n\n const keyPrompts: prompts.PromptObject[] = []\n\n // Vanilla JS doesn't use env vars — no prompts for keys\n if (env !== 'vanilla') {\n keyPrompts.push({\n type: 'text',\n name: 'clientKey',\n message: 'Client Key (optional, saved to .env)',\n initial: '',\n })\n }\n\n if (needsSecretKey) {\n keyPrompts.push({\n type: 'text',\n name: 'secretKey',\n message: 'Secret Key (optional, saved to .env)',\n initial: '',\n })\n }\n\n // AI tool multi-select\n const { selectedTools } = await prompts(\n {\n type: 'multiselect',\n name: 'selectedTools',\n message: 'Connect AI tools:',\n choices: [\n { title: 'Claude Code', description: '.mcp.json + .claude/ docs', value: 'claude' },\n { title: 'Cursor', description: '.cursor/mcp.json', value: 'cursor' },\n { title: 'VS Code', description: '.vscode/mcp.json', value: 'vscode' },\n { title: 'Windsurf', description: '~/.codeium/windsurf/mcp_config.json', value: 'windsurf' },\n { title: 'Skip', value: 'skip' },\n ],\n hint: 'space to select',\n },\n { onCancel },\n )\n\n const aiTools: AiTool[] = Array.isArray(selectedTools)\n ? (selectedTools as string[]).filter((t): t is AiTool => t !== 'skip')\n : []\n\n // Auth method — only if tools selected and env supports secrets\n let authMethod: AuthMethod = 'skip'\n let keys: Record<string, string> = {}\n\n if (aiTools.length > 0 && env !== 'vanilla') {\n const { method } = await prompts(\n {\n type: 'select',\n name: 'method',\n message: 'API keys:',\n choices: [\n { title: 'Browser login (recommended)', value: 'browser' },\n { title: 'Enter manually', value: 'manual' },\n { title: 'Skip for now', value: 'skip' },\n ],\n },\n { onCancel },\n )\n authMethod = (method as AuthMethod) ?? 'skip'\n\n if (authMethod === 'manual') {\n keys = await prompts(keyPrompts, { onCancel })\n }\n } else if (env !== 'vanilla') {\n // No AI tools selected — still collect keys from the key prompts if any were defined\n if (keyPrompts.length > 0) {\n keys = await prompts(keyPrompts, { onCancel })\n }\n authMethod = 'skip'\n }\n\n return {\n env,\n clientKey: authMethod === 'browser' ? '' : (keys.clientKey ?? ''),\n secretKey: authMethod === 'browser' ? '' : (keys.secretKey ?? ''),\n aiTools,\n authMethod,\n packageManager,\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { execSync } from 'node:child_process'\nimport pc from 'picocolors'\nimport type { PackageManager, ProjectInfo } from './detect'\nimport {\n needsClient,\n needsServer,\n needsReactQuery,\n getClientKeyEnvVar,\n} from './detect'\nimport type { InitAnswers } from './prompts'\nimport {\n getClientTemplate,\n getQueryProviderTemplate,\n getServerTemplate,\n getEnvContent,\n getMcpConfigTemplate,\n getMcpServerEntry,\n} from './templates'\nimport { startBrowserAuth } from './browser-auth'\nimport {\n generateClaudeMd,\n getSkillFiles,\n fetchTenantContext,\n} from './ai-docs'\n\ntype ResolvedProjectInfo = Omit<ProjectInfo, 'packageManager'> & {\n packageManager: PackageManager\n}\n\nconst SECRET_KEY_ENV_VAR = 'SOFTWARE_SECRET_KEY'\n\nexport async function init(\n cwd: string,\n info: ResolvedProjectInfo,\n answers: InitAnswers,\n): Promise<void> {\n const { packageManager, srcDir } = info\n const env = answers.env\n const baseDir = srcDir ? path.join(cwd, 'src') : cwd\n\n const clientKeyEnvVar = getClientKeyEnvVar(env)\n const wantsClient = needsClient(env)\n const wantsServer = needsServer(env)\n const wantsReactQuery = needsReactQuery(env)\n\n // 1. Install dependencies\n const deps = ['@01.software/sdk']\n if (wantsReactQuery) deps.push('@tanstack/react-query')\n\n console.log(pc.dim(` Installing ${deps.join(' and ')}...`))\n\n // pnpm-workspace.yaml without packages: breaks pnpm add — patch temporarily\n const wsPatched = packageManager === 'pnpm' && patchPnpmWorkspace(cwd)\n\n const pkgs = deps.join(' ')\n const pnpmFlag = hasPnpmWorkspace(cwd) ? ' -w' : ''\n const addCmd =\n packageManager === 'pnpm'\n ? `pnpm add${pnpmFlag} ${pkgs}`\n : packageManager === 'yarn'\n ? `yarn add ${pkgs}`\n : packageManager === 'bun'\n ? `bun add ${pkgs}`\n : `npm install ${pkgs}`\n\n try {\n execSync(addCmd, { cwd, stdio: 'pipe' })\n } catch (error) {\n const err = error as { stdout?: Buffer; stderr?: Buffer }\n const msg =\n String(err.stderr || '').trim() ||\n String(err.stdout || '').trim() ||\n String(error)\n console.log(pc.red(' Failed to install dependencies:'))\n console.log(pc.dim(` ${msg}`))\n throw error\n } finally {\n if (wsPatched) restorePnpmWorkspace(cwd)\n }\n\n // 2. Write lib/software/ files\n const libDir = path.join(baseDir, 'lib', 'software')\n fs.mkdirSync(libDir, { recursive: true })\n\n // Client (browser)\n if (wantsClient) {\n const clientPath = path.join(libDir, 'client.ts')\n if (fs.existsSync(clientPath)) {\n console.log(pc.yellow(' Skipped'), relativePath(cwd, clientPath), pc.dim('(already exists)'))\n } else {\n fs.writeFileSync(clientPath, getClientTemplate(env, clientKeyEnvVar))\n console.log(pc.green(' Created'), relativePath(cwd, clientPath))\n }\n }\n\n // Query Provider (React)\n if (wantsReactQuery) {\n const queryProviderPath = path.join(libDir, 'query-provider.tsx')\n if (fs.existsSync(queryProviderPath)) {\n console.log(pc.yellow(' Skipped'), relativePath(cwd, queryProviderPath), pc.dim('(already exists)'))\n } else {\n fs.writeFileSync(queryProviderPath, getQueryProviderTemplate(env))\n console.log(pc.green(' Created'), relativePath(cwd, queryProviderPath))\n }\n }\n\n // Server\n if (wantsServer) {\n const serverPath = path.join(libDir, 'server.ts')\n if (fs.existsSync(serverPath)) {\n console.log(pc.yellow(' Skipped'), relativePath(cwd, serverPath), pc.dim('(already exists)'))\n } else {\n fs.writeFileSync(serverPath, getServerTemplate(env, clientKeyEnvVar, SECRET_KEY_ENV_VAR))\n console.log(pc.green(' Created'), relativePath(cwd, serverPath))\n }\n }\n\n // 3. Append to .env (vanilla uses inline key; browser auth writes keys later in step 4)\n if (env !== 'vanilla' && env !== 'edge' && answers.authMethod !== 'browser') {\n const envPath = path.join(cwd, '.env')\n const envContent = getEnvContent(\n answers.clientKey || '',\n answers.secretKey || '',\n clientKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n )\n\n if (fs.existsSync(envPath)) {\n const existing = fs.readFileSync(envPath, 'utf-8')\n if (existing.includes(clientKeyEnvVar)) {\n console.log(pc.yellow(' Skipped'), '.env', pc.dim('(keys already present)'))\n } else {\n fs.appendFileSync(envPath, envContent)\n console.log(pc.green(' Updated'), '.env')\n }\n } else {\n fs.writeFileSync(envPath, envContent.trimStart())\n console.log(pc.green(' Created'), '.env')\n }\n }\n\n // 4. Browser auth — get real credentials if user chose browser login\n let clientKey = answers.clientKey\n let secretKey = answers.secretKey\n let tenantName = ''\n\n if (answers.authMethod === 'browser' && answers.aiTools.length > 0) {\n try {\n console.log()\n const creds = await startBrowserAuth()\n clientKey = creds.clientKey\n secretKey = creds.secretKey\n tenantName = creds.tenantName\n\n // Write .env with the real keys obtained from browser\n if (env !== 'vanilla' && env !== 'edge' && clientKey) {\n const envPath = path.join(cwd, '.env')\n const envContent = getEnvContent(\n clientKey,\n secretKey,\n clientKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n )\n if (fs.existsSync(envPath)) {\n const existing = fs.readFileSync(envPath, 'utf-8')\n if (!existing.includes(clientKeyEnvVar)) {\n fs.appendFileSync(envPath, envContent)\n console.log(pc.green(' Updated'), '.env', pc.dim('(added API keys)'))\n }\n } else {\n fs.writeFileSync(envPath, envContent.trimStart())\n console.log(pc.green(' Created'), '.env')\n }\n }\n } catch (err) {\n console.log(\n pc.yellow(' Browser auth skipped:'),\n err instanceof Error ? err.message : String(err),\n )\n }\n }\n\n // 5. Write AI tool configs (MCP + Claude docs)\n if (answers.aiTools.length > 0) {\n const apiKey =\n clientKey && secretKey\n ? Buffer.from(`${clientKey}:${secretKey}`).toString('base64')\n : 'YOUR_API_KEY'\n\n for (const tool of answers.aiTools) {\n writeMcpConfig(tool, cwd, apiKey)\n }\n\n addToGitignore(cwd, answers.aiTools)\n\n if (answers.aiTools.includes('claude')) {\n await writeClaudeDocs(cwd, clientKey, secretKey, tenantName)\n }\n }\n}\n\nfunction writeMcpConfig(tool: string, cwd: string, apiKey: string): void {\n let configPath: string\n let displayPath: string\n\n switch (tool) {\n case 'claude':\n configPath = path.join(cwd, '.mcp.json')\n displayPath = '.mcp.json'\n break\n case 'cursor':\n configPath = path.join(cwd, '.cursor', 'mcp.json')\n displayPath = '.cursor/mcp.json'\n fs.mkdirSync(path.dirname(configPath), { recursive: true })\n break\n case 'vscode':\n configPath = path.join(cwd, '.vscode', 'mcp.json')\n displayPath = '.vscode/mcp.json'\n fs.mkdirSync(path.dirname(configPath), { recursive: true })\n break\n case 'windsurf': {\n const home = process.env.HOME || process.env.USERPROFILE || ''\n if (!home) {\n console.log(pc.yellow(' Skipped windsurf'), pc.dim('(HOME not set)'))\n return\n }\n configPath = path.join(home, '.codeium', 'windsurf', 'mcp_config.json')\n displayPath = configPath // global path, show absolute\n fs.mkdirSync(path.dirname(configPath), { recursive: true })\n break\n }\n default:\n return\n }\n\n if (fs.existsSync(configPath)) {\n try {\n const existing = JSON.parse(fs.readFileSync(configPath, 'utf-8'))\n if (existing.mcpServers?.['01software']) {\n console.log(pc.yellow(' Skipped'), displayPath, pc.dim('(01software already configured)'))\n return\n }\n existing.mcpServers = existing.mcpServers || {}\n existing.mcpServers['01software'] = getMcpServerEntry(apiKey)\n fs.writeFileSync(configPath, JSON.stringify(existing, null, 2) + '\\n')\n console.log(pc.green(' Updated'), displayPath)\n } catch {\n console.log(pc.yellow(' Skipped'), displayPath, pc.dim('(could not parse existing file)'))\n }\n } else {\n fs.writeFileSync(configPath, getMcpConfigTemplate(apiKey))\n console.log(pc.green(' Created'), displayPath)\n }\n}\n\nfunction addToGitignore(cwd: string, tools: string[]): void {\n const entries: string[] = []\n if (tools.includes('claude')) entries.push('.mcp.json')\n if (tools.includes('cursor')) entries.push('.cursor/mcp.json')\n if (tools.includes('vscode')) entries.push('.vscode/mcp.json')\n // windsurf is global (~/.codeium/...) — no project-level gitignore needed\n\n if (entries.length === 0) return\n\n const gitignorePath = path.join(cwd, '.gitignore')\n const existing = fs.existsSync(gitignorePath)\n ? fs.readFileSync(gitignorePath, 'utf-8')\n : ''\n const toAdd = entries.filter((e) => !existing.includes(e))\n if (toAdd.length === 0) return\n\n const content = '\\n# MCP configs (contain API key)\\n' + toAdd.join('\\n') + '\\n'\n if (fs.existsSync(gitignorePath)) {\n fs.appendFileSync(gitignorePath, content)\n } else {\n fs.writeFileSync(gitignorePath, content.trimStart())\n }\n console.log(pc.green(' Updated'), '.gitignore', pc.dim(`(added ${toAdd.join(', ')})`))\n}\n\nasync function writeClaudeDocs(\n cwd: string,\n clientKey: string,\n secretKey: string,\n tenantName: string,\n): Promise<void> {\n let ctx = {\n tenantName: tenantName || 'Your Tenant',\n features: undefined as string[] | undefined,\n collections: undefined as string[] | undefined,\n }\n\n if (clientKey && secretKey) {\n const fetched = await fetchTenantContext(clientKey, secretKey)\n if (fetched) {\n ctx = {\n tenantName: fetched.tenantName || ctx.tenantName,\n features: fetched.features,\n collections: fetched.collections,\n }\n }\n }\n\n const claudeDir = path.join(cwd, '.claude')\n const commandsDir = path.join(claudeDir, 'commands')\n fs.mkdirSync(commandsDir, { recursive: true })\n\n // CLAUDE.md\n const claudeMdPath = path.join(claudeDir, 'CLAUDE.md')\n if (!fs.existsSync(claudeMdPath)) {\n fs.writeFileSync(claudeMdPath, generateClaudeMd(ctx))\n console.log(pc.green(' Created'), '.claude/CLAUDE.md')\n } else {\n console.log(pc.yellow(' Skipped'), '.claude/CLAUDE.md', pc.dim('(already exists)'))\n }\n\n // Skill files\n for (const { filename, content } of getSkillFiles()) {\n const skillPath = path.join(commandsDir, filename)\n if (!fs.existsSync(skillPath)) {\n fs.writeFileSync(skillPath, content)\n console.log(pc.green(' Created'), `.claude/commands/${filename}`)\n }\n }\n}\n\nfunction relativePath(cwd: string, filePath: string): string {\n return path.relative(cwd, filePath)\n}\n\nconst WS_FILE = 'pnpm-workspace.yaml'\nconst WS_BACKUP = 'pnpm-workspace.yaml.bak'\n\nfunction hasPnpmWorkspace(cwd: string): boolean {\n return fs.existsSync(path.join(cwd, WS_FILE))\n}\n\nfunction patchPnpmWorkspace(cwd: string): boolean {\n const wsPath = path.join(cwd, WS_FILE)\n if (!fs.existsSync(wsPath)) return false\n const content = fs.readFileSync(wsPath, 'utf-8')\n if (content.includes('packages:')) return false\n fs.copyFileSync(wsPath, path.join(cwd, WS_BACKUP))\n fs.writeFileSync(wsPath, content.trimEnd() + '\\npackages: []\\n')\n return true\n}\n\nfunction restorePnpmWorkspace(cwd: string): void {\n const backupPath = path.join(cwd, WS_BACKUP)\n if (!fs.existsSync(backupPath)) return\n fs.copyFileSync(backupPath, path.join(cwd, WS_FILE))\n fs.unlinkSync(backupPath)\n}\n","import type { ProjectEnv } from './detect'\n\n// ── Client template (browser) ────────────────────────────────────────\n\nexport function getClientTemplate(env: ProjectEnv, clientKeyEnvVar: string): string {\n if (env === 'nextjs') {\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n clientKey: process.env.${clientKeyEnvVar}!,\n})\n`\n }\n\n if (env === 'react-cra') {\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n clientKey: process.env.${clientKeyEnvVar}!,\n})\n`\n }\n\n if (env === 'vanilla') {\n return `import { createClient } from '@01.software/sdk'\n\n// Replace 'YOUR_CLIENT_KEY' with your actual client key from the 01.software console\nexport const client = createClient({\n clientKey: 'YOUR_CLIENT_KEY',\n})\n`\n }\n\n // react-vite (import.meta.env)\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n clientKey: import.meta.env.${clientKeyEnvVar},\n})\n`\n}\n\n// ── Query Provider template ──────────────────────────────────────────\n\nexport function getQueryProviderTemplate(env: ProjectEnv): string {\n const useClientDirective = env === 'nextjs' ? \"'use client'\\n\\n\" : ''\n\n return `${useClientDirective}import { QueryClientProvider } from '@tanstack/react-query'\nimport { client } from './client'\n\nexport function QueryProvider({ children }: { children: React.ReactNode }) {\n return (\n <QueryClientProvider client={client.queryClient}>\n {children}\n </QueryClientProvider>\n )\n}\n`\n}\n\n// ── Server template ──────────────────────────────────────────────────\n\nexport function getServerTemplate(\n env: ProjectEnv,\n clientKeyEnvVar: string,\n secretKeyEnvVar: string,\n): string {\n if (env === 'edge') {\n return `import { createServerClient } from '@01.software/sdk'\n\n// Edge runtime: pass your env bindings here\n// e.g. Cloudflare Workers: use env.SOFTWARE_CLIENT_KEY from the handler context\n// e.g. Vercel Edge: use process.env.${clientKeyEnvVar}\nexport function createEdgeClient(clientKey: string, secretKey: string) {\n return createServerClient({ clientKey, secretKey })\n}\n`\n }\n\n return `import { createServerClient } from '@01.software/sdk'\n\nexport const serverClient = createServerClient({\n clientKey: process.env.${clientKeyEnvVar}!,\n secretKey: process.env.${secretKeyEnvVar}!,\n})\n`\n}\n\n// ── Env file content ─────────────────────────────────────────────────\n\nexport function getEnvContent(\n clientKey: string,\n secretKey: string,\n clientKeyEnvVar: string,\n secretKeyEnvVar: string | null,\n): string {\n let content = `\\n# 01.software\\n${clientKeyEnvVar}=${clientKey}\\n`\n if (secretKeyEnvVar) {\n content += `${secretKeyEnvVar}=${secretKey}\\n`\n }\n return content\n}\n\n// ── MCP config ───────────────────────────────────────────────────────\n\nexport function getMcpServerEntry(apiKey: string) {\n return {\n type: 'http' as const,\n url: 'https://mcp.01.software/mcp',\n headers: { 'x-api-key': apiKey },\n }\n}\n\nexport function getMcpConfigTemplate(apiKey: string): string {\n return (\n JSON.stringify(\n { mcpServers: { '01software': getMcpServerEntry(apiKey) } },\n null,\n 2,\n ) + '\\n'\n )\n}\n","import { randomBytes } from 'node:crypto'\nimport { createServer } from 'node:http'\nimport { execFile, exec } from 'node:child_process'\nimport { platform } from 'node:os'\nimport { URL } from 'node:url'\nimport pc from 'picocolors'\n\nconst DEFAULT_WEB_URL = process.env.SOFTWARE_WEB_URL || 'https://01.software'\nconst TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\nfunction openBrowser(url: string): void {\n const os = platform()\n\n const onError = () => {\n console.log(\n pc.yellow(\n `Could not open browser automatically. Open this URL manually:\\n${url}`,\n ),\n )\n }\n\n if (os === 'win32') {\n exec(`start \"\" \"${url}\"`, (err) => {\n if (err) onError()\n })\n } else {\n const cmd = os === 'darwin' ? 'open' : 'xdg-open'\n execFile(cmd, [url], (err) => {\n if (err) onError()\n })\n }\n}\n\nconst PAGE_STYLE = `*{margin:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;color:#252525}\n@media(prefers-color-scheme:dark){body{background:#252525;color:#f5f5f5}}\n.card{text-align:center;padding:2rem 2.5rem;border-radius:10px;max-width:380px;width:100%}\n.icon{width:40px;height:40px;margin:0 auto 1rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.25rem}\n.icon.ok{background:rgba(0,0,0,.05);color:#252525}\n.icon.err{background:rgba(220,38,38,.08);color:#dc2626}\n@media(prefers-color-scheme:dark){.icon.ok{background:rgba(255,255,255,.08);color:#f5f5f5}}\nh1{font-size:.875rem;font-weight:600;margin-bottom:.375rem}\np{font-size:.75rem;color:#737373;line-height:1.5}`\n\nconst SUCCESS_HTML = `<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><title>Login</title>\n<style>${PAGE_STYLE}</style>\n</head><body><div class=\"card\"><div class=\"icon ok\">\\u2713</div><h1>Authenticated</h1><p>You can close this tab and return to the terminal.</p></div></body></html>`\n\nconst ERROR_HTML = (msg: string) => `<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><title>Login Error</title>\n<style>${PAGE_STYLE}</style>\n</head><body><div class=\"card\"><div class=\"icon err\">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`\n\nexport async function startBrowserAuth(options?: {\n webUrl?: string\n tenantId?: string\n}): Promise<{ clientKey: string; secretKey: string; tenantName: string; tenantId?: string }> {\n const state = randomBytes(32).toString('hex')\n const webUrl = options?.webUrl ?? DEFAULT_WEB_URL\n\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n if (!req.url) {\n res.writeHead(400).end()\n return\n }\n\n const url = new URL(req.url, `http://localhost`)\n\n if (url.pathname !== '/callback') {\n res.writeHead(404).end()\n return\n }\n\n const receivedState = url.searchParams.get('state')\n const clientKey = url.searchParams.get('clientKey')\n const secretKey = url.searchParams.get('secretKey')\n const tenant = url.searchParams.get('tenant')\n const tenantIdParam = url.searchParams.get('tenantId')\n const error = url.searchParams.get('error')\n\n if (error) {\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n .end(ERROR_HTML(error))\n console.error(pc.red(`Login failed: ${error}`))\n cleanup(new Error(`Login failed: ${error}`))\n return\n }\n\n if (receivedState !== state) {\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n .end(ERROR_HTML('State mismatch — possible CSRF attack.'))\n console.error(pc.red('Login failed: state mismatch.'))\n cleanup(new Error('Login failed: state mismatch.'))\n return\n }\n\n if (!clientKey || !secretKey || !tenant) {\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n .end(ERROR_HTML('Missing credentials in callback.'))\n console.error(pc.red('Login failed: missing credentials.'))\n cleanup(new Error('Login failed: missing credentials.'))\n return\n }\n\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n .end(SUCCESS_HTML)\n\n console.log(pc.green(`\\nLogged in successfully!`))\n console.log(pc.dim(`Tenant: ${tenant}`))\n\n const result: { clientKey: string; secretKey: string; tenantName: string; tenantId?: string } = {\n clientKey,\n secretKey,\n tenantName: tenant,\n ...(tenantIdParam ? { tenantId: tenantIdParam } : {}),\n }\n\n cleanup(null, result)\n })\n\n let timeout: ReturnType<typeof setTimeout>\n let completed = false\n\n function cleanup(\n err: Error | null,\n result?: { clientKey: string; secretKey: string; tenantName: string; tenantId?: string },\n ) {\n if (completed) return\n completed = true\n clearTimeout(timeout)\n server.close(() => {\n if (err) {\n reject(err)\n } else {\n resolve(result!)\n }\n })\n }\n\n server.listen(0, '127.0.0.1', () => {\n const addr = server.address()\n if (!addr || typeof addr === 'string') {\n reject(new Error('Failed to start local server.'))\n return\n }\n\n const port = addr.port\n\n timeout = setTimeout(() => {\n console.error(pc.red('\\nLogin timed out (5 minutes). Please try again.'))\n cleanup(new Error('Login timed out'))\n }, TIMEOUT_MS)\n\n const params = new URLSearchParams({ port: String(port), state })\n if (options?.tenantId) {\n params.set('tenantId', options.tenantId)\n }\n const loginUrl = `${webUrl}/cli-auth?${params.toString()}`\n\n console.log(pc.dim('Opening browser for login...'))\n console.log(pc.dim(`If the browser does not open, visit:\\n${loginUrl}`))\n openBrowser(loginUrl)\n })\n\n server.on('error', (err) => {\n reject(err)\n })\n })\n}\n","export interface TenantContext {\n tenantName: string\n features?: string[]\n collections?: string[]\n}\n\n// ── CLAUDE.md ────────────────────────────────────────────────────────\n\nexport function generateClaudeMd(ctx: TenantContext): string {\n const featuresSection =\n ctx.features && ctx.features.length > 0\n ? ctx.features.map((f) => `- ${f}`).join('\\n')\n : '- See console'\n\n const collectionsSection =\n ctx.collections && ctx.collections.length > 0\n ? ctx.collections.join(', ')\n : 'Run `01 schema list`'\n\n return `# 01.software SDK — ${ctx.tenantName}\n\n## Connection\n- Client Key: \\`NEXT_PUBLIC_SOFTWARE_CLIENT_KEY\\` (env)\n- Server Key: \\`SOFTWARE_SECRET_KEY\\` (env)\n- MCP: \\`.mcp.json\\`\n\n## Active Features\n${featuresSection}\n\n## Collections\n${collectionsSection}\n\n## MCP Quick Reference\n| Tool | Use |\n|------|-----|\n| \\`query-collection\\` | List/filter documents |\n| \\`create-collection\\` | Create documents |\n| \\`update-field-config\\` | Hide unused fields |\n| \\`get-tenant-context\\` | Show active features & collections |\n\n## CLI\n- \\`01 query <collection>\\` — query data\n- \\`01 schema show <collection>\\` — inspect fields\n- \\`01 schema list\\` — list all collections\n\n## Initial Setup\nRun \\`/field-config\\` in Claude Code to configure field visibility for your use case.\n`\n}\n\n// ── Skill files ──────────────────────────────────────────────────────\n\nexport function getSkillFiles(): Array<{ filename: string; content: string }> {\n return [\n {\n filename: 'field-config.md',\n content: `Configure field visibility for this tenant using MCP tools.\n\nSteps:\n1. Use \\`list-configurable-fields\\` to see current visibility settings\n2. Identify fields/collections not needed for your use case\n3. Use \\`update-field-config\\` to hide them\n\nCommon setups:\n- Blog only: hide \\`ecommerce\\`, \\`customers\\`, \\`videos\\` collections\n- Store: hide \\`posts\\`, \\`documents\\`, \\`galleries\\`, \\`flows\\` collections\n- Minimal: hide all except the collections you actively use\n\nAsk me: \"Show current field config\" or \"Hide ecommerce fields\"\n`,\n },\n {\n filename: 'query.md',\n content: `Query collections using the MCP \\`query-collection\\` tool or CLI.\n\nMCP examples:\n- List products: \\`query-collection\\` with collection=\"products\", limit=10\n- Filter by status: add where={\"status\":{\"equals\":\"published\"}}\n- Sort by date: sort=\"-createdAt\"\n- Paginate: page=2, limit=20\n\nCLI examples:\n- \\`01 query products --limit 10\\`\n- \\`01 query orders --where '{\"status\":{\"equals\":\"paid\"}}'\\`\n- \\`01 schema show products\\` — inspect available fields\n\nSDK (server):\n\\`\\`\\`typescript\nconst { docs } = await serverClient.collection('products').find({\n where: { status: { equals: 'published' } },\n sort: '-createdAt',\n limit: 10,\n})\n\\`\\`\\`\n`,\n },\n {\n filename: 'order-flow.md',\n content: `Complete order flow from creation to fulfillment.\n\nStates: pending → paid → preparing → shipped → delivered → confirmed\n\n1. Create order: \\`create-order\\` with orderNumber, customerSnapshot, orderProducts, totalAmount\n2. Mark paid: \\`update-order\\` with status=\"paid\" (after payment gateway confirms)\n3. Fulfill: \\`create-fulfillment\\` with items and carrier/trackingNumber\n4. Returns: \\`create-return\\` or \\`return-with-refund\\` (atomic)\n\nFree orders: omit paymentId, totalAmount=0 → auto-transitions to paid\n\nCLI: \\`01 order create --help\\` for full options\n`,\n },\n {\n filename: 'schema.md',\n content: `Inspect collection schemas to understand available fields.\n\nMCP: use \\`get-collection-fields\\` with collectionSlug\n\nCLI:\n- \\`01 schema list\\` — all available collections\n- \\`01 schema show <collection>\\` — field names, types, required status\n\nCommon collections: products, orders, customers, posts, documents, images\nUse \\`get-tenant-context\\` to see which collections are active for this tenant.\n`,\n },\n ]\n}\n\n// ── Tenant context fetch ─────────────────────────────────────────────\n\nexport async function fetchTenantContext(\n clientKey: string,\n secretKey: string,\n): Promise<TenantContext | null> {\n try {\n const apiUrl = process.env.SOFTWARE_API_URL || 'https://api.01.software'\n const base64 = Buffer.from(`${clientKey}:${secretKey}`).toString('base64')\n const res = await fetch(`${apiUrl}/api/tenants/context`, {\n headers: { 'x-api-key': base64 },\n })\n if (!res.ok) return null\n const data = (await res.json()) as {\n tenant?: { name?: string }\n features?: string[]\n collections?: string[]\n }\n return {\n tenantName: data.tenant?.name || '',\n features: data.features || [],\n collections: data.collections || [],\n }\n } catch {\n return null\n }\n}\n"],"mappings":"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","names":["pc","fs","path","pc","path","pc","fs","pc"]} |
+1
-1
| { | ||
| "name": "@01.software/init", | ||
| "version": "0.1.6", | ||
| "version": "0.2.0", | ||
| "description": "Initialize 01.software SDK in your project (Next.js, React, Vanilla JS, Node.js, Edge)", | ||
@@ -5,0 +5,0 @@ "type": "module", |
Network access
Supply chain riskThis module accesses the network.
Found 2 instances
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
99456
70.37%954
69.45%8
100%4
300%