@01.software/init
Advanced tools
| #!/usr/bin/env node | ||
| import { | ||
| fetchTenantContext, | ||
| generateClaudeMd, | ||
| getSkillFiles | ||
| } from "./chunk-5K2CB2Y5.js"; | ||
| import { | ||
| chmodSecretFile, | ||
| readEnvValue, | ||
| replaceTomlMcpSection, | ||
| setEnvValue, | ||
| writeEnvFile, | ||
| writeSecretGlobalConfig | ||
| } from "./chunk-UA7WNT2F.js"; | ||
| import { | ||
| CODEX_MCP_SECTION_MARKER, | ||
| getAnalyticsTemplate, | ||
| getClientTemplate, | ||
| getCodexMcpTomlSection, | ||
| getEnvContent, | ||
| getMcpConfigTemplate, | ||
| getMcpRootKey, | ||
| getMcpServerEntry, | ||
| getQueryProviderTemplate, | ||
| getServerTemplate | ||
| } from "./chunk-TBGKXE3Q.js"; | ||
| // src/init.ts | ||
| import fs2 from "fs"; | ||
| import path2 from "path"; | ||
| import os from "os"; | ||
| import { execSync } from "child_process"; | ||
| import pc2 from "picocolors"; | ||
| import prompts from "prompts"; | ||
| // src/detect.ts | ||
| import fs from "fs"; | ||
| import path from "path"; | ||
| function detectProject(cwd) { | ||
| const pkgPath = path.join(cwd, "package.json"); | ||
| const hasPackageJson = fs.existsSync(pkgPath); | ||
| let env = "node"; | ||
| let hasSdk = false; | ||
| let hasReactQuery = false; | ||
| let parseError = false; | ||
| if (hasPackageJson) { | ||
| let pkg; | ||
| try { | ||
| pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")); | ||
| } catch { | ||
| return { | ||
| hasPackageJson: true, | ||
| parseError: true, | ||
| env: "node", | ||
| packageManager: null, | ||
| hasSdk: false, | ||
| hasReactQuery: false, | ||
| srcDir: false | ||
| }; | ||
| } | ||
| const deps = { | ||
| ...pkg.dependencies || {}, | ||
| ...pkg.devDependencies || {} | ||
| }; | ||
| hasSdk = "@01.software/sdk" in deps; | ||
| hasReactQuery = "@tanstack/react-query" in deps; | ||
| if ("next" in deps) { | ||
| env = "nextjs"; | ||
| } else if ("astro" in deps || "@astrojs/node" in deps) { | ||
| env = "other"; | ||
| } else if ("@remix-run/node" in deps || "@remix-run/react" in deps) { | ||
| env = "other"; | ||
| } else if ("@sveltejs/kit" in deps) { | ||
| env = "other"; | ||
| } else if ("react" in deps) { | ||
| if ("vite" in deps) { | ||
| env = "react-vite"; | ||
| } else if ("react-scripts" in deps) { | ||
| env = "react-cra"; | ||
| } else { | ||
| env = "node"; | ||
| } | ||
| } | ||
| } | ||
| let packageManager = null; | ||
| if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) { | ||
| packageManager = "pnpm"; | ||
| } else if (fs.existsSync(path.join(cwd, "yarn.lock"))) { | ||
| packageManager = "yarn"; | ||
| } else if (fs.existsSync(path.join(cwd, "bun.lockb")) || fs.existsSync(path.join(cwd, "bun.lock"))) { | ||
| packageManager = "bun"; | ||
| } else if (fs.existsSync(path.join(cwd, "package-lock.json"))) { | ||
| packageManager = "npm"; | ||
| } | ||
| const srcDir = env === "nextjs" ? fs.existsSync(path.join(cwd, "src", "app")) : fs.existsSync(path.join(cwd, "src")); | ||
| return { hasPackageJson, parseError, env, packageManager, hasSdk, hasReactQuery, srcDir }; | ||
| } | ||
| function needsClient(env) { | ||
| return env === "nextjs" || env === "react-vite" || env === "react-cra" || env === "vanilla"; | ||
| } | ||
| function needsServer(env) { | ||
| return env === "nextjs" || env === "node" || env === "edge"; | ||
| } | ||
| function needsReactQuery(env) { | ||
| return env === "nextjs" || env === "react-vite" || env === "react-cra"; | ||
| } | ||
| function getPublishableKeyEnvVar(env) { | ||
| switch (env) { | ||
| case "nextjs": | ||
| return "NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY"; | ||
| case "react-vite": | ||
| return "VITE_SOFTWARE_PUBLISHABLE_KEY"; | ||
| case "react-cra": | ||
| return "REACT_APP_SOFTWARE_PUBLISHABLE_KEY"; | ||
| default: | ||
| return "SOFTWARE_PUBLISHABLE_KEY"; | ||
| } | ||
| } | ||
| // 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 os2 = platform(); | ||
| const onError = () => { | ||
| console.log( | ||
| pc.yellow( | ||
| `Could not open browser automatically. Open this URL manually: | ||
| ${url}` | ||
| ) | ||
| ); | ||
| }; | ||
| if (os2 === "win32") { | ||
| exec(`start "" "${url}"`, (err) => { | ||
| if (err) onError(); | ||
| }); | ||
| } else { | ||
| const cmd = os2 === "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 exchangeCode(webUrl, code) { | ||
| const url = `${webUrl}/api/cli/exchange`; | ||
| try { | ||
| const res = await fetch(url, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ code }) | ||
| }); | ||
| if (!res.ok) { | ||
| const body = await res.text().catch(() => ""); | ||
| console.error( | ||
| pc.red( | ||
| `Exchange failed: HTTP ${res.status} from ${url}${body ? ` \u2014 ${body.slice(0, 200)}` : ""}` | ||
| ) | ||
| ); | ||
| return null; | ||
| } | ||
| const data = await res.json(); | ||
| if (typeof data.publishableKey !== "string" || typeof data.secretKey !== "string" || typeof data.tenantName !== "string" || typeof data.tenantId !== "string") { | ||
| console.error(pc.red(`Exchange failed: malformed response from ${url}`)); | ||
| return null; | ||
| } | ||
| return { | ||
| publishableKey: data.publishableKey, | ||
| secretKey: data.secretKey, | ||
| tenantName: data.tenantName, | ||
| tenantId: data.tenantId | ||
| }; | ||
| } catch (err) { | ||
| console.error( | ||
| pc.red( | ||
| `Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}` | ||
| ) | ||
| ); | ||
| return null; | ||
| } | ||
| } | ||
| 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" || req.method !== "GET") { | ||
| res.writeHead(404).end(); | ||
| return; | ||
| } | ||
| const error = url.searchParams.get("error"); | ||
| if (error) { | ||
| res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML(error)); | ||
| console.error(pc.red(`Login failed: ${error}`)); | ||
| cleanup(new Error(`Login failed: ${error}`)); | ||
| return; | ||
| } | ||
| const code = url.searchParams.get("code"); | ||
| const receivedState = url.searchParams.get("state"); | ||
| if (!code || !receivedState) { | ||
| res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Missing code or state.")); | ||
| cleanup(new Error("Login failed: missing code or state.")); | ||
| return; | ||
| } | ||
| if (receivedState !== state) { | ||
| res.writeHead(403, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("State mismatch.")); | ||
| console.error(pc.red("Login failed: state mismatch.")); | ||
| cleanup(new Error("Login failed: state mismatch.")); | ||
| return; | ||
| } | ||
| exchangeCode(webUrl, code).then((creds) => { | ||
| if (!creds) { | ||
| res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Invalid or expired code.")); | ||
| cleanup(new Error("Login failed: code exchange failed.")); | ||
| return; | ||
| } | ||
| res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(SUCCESS_HTML); | ||
| console.log(pc.green(` | ||
| Logged in successfully!`)); | ||
| console.log(pc.dim(`Tenant: ${creds.tenantName}`)); | ||
| cleanup(null, creds); | ||
| }); | ||
| }); | ||
| let timeout; | ||
| let completed = false; | ||
| function cleanup(err, result) { | ||
| if (completed) return; | ||
| completed = true; | ||
| clearTimeout(timeout); | ||
| server.closeAllConnections?.(); | ||
| 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/init.ts | ||
| var SECRET_KEY_ENV_VAR = "SOFTWARE_SECRET_KEY"; | ||
| async function init(cwd, info, answers, deps = {}) { | ||
| const { packageManager, srcDir } = info; | ||
| const env = answers.env; | ||
| const baseDir = srcDir ? path2.join(cwd, "src") : cwd; | ||
| const browserAuth = deps.startBrowserAuth ?? startBrowserAuth; | ||
| const publishableKeyEnvVar = getPublishableKeyEnvVar(env); | ||
| const wantsClient = needsClient(env); | ||
| const wantsServer = needsServer(env); | ||
| const wantsReactQuery = needsReactQuery(env); | ||
| const plan = await planConflictsAndEnv(cwd, baseDir, env, answers); | ||
| const installResult = deps.skipInstall ? { | ||
| installFailed: false, | ||
| installSkipped: true, | ||
| installCmd: buildAddCmd(packageManager, hasPnpmWorkspace(cwd), [ | ||
| "@01.software/sdk" | ||
| ]) | ||
| } : installDeps( | ||
| cwd, | ||
| packageManager, | ||
| info.hasSdk, | ||
| info.hasReactQuery, | ||
| wantsReactQuery | ||
| ); | ||
| if (wantsClient || wantsReactQuery || wantsServer) { | ||
| fs2.mkdirSync(path2.join(baseDir, "lib", "software"), { recursive: true }); | ||
| } | ||
| const libDir = path2.join(baseDir, "lib", "software"); | ||
| if (wantsClient) { | ||
| await writeFileWithPolicy( | ||
| cwd, | ||
| path2.join(libDir, "client.ts"), | ||
| getClientTemplate(env, publishableKeyEnvVar), | ||
| plan.policy | ||
| ); | ||
| await writeFileWithPolicy( | ||
| cwd, | ||
| path2.join(libDir, "analytics.ts"), | ||
| getAnalyticsTemplate(env, publishableKeyEnvVar), | ||
| plan.policy | ||
| ); | ||
| } | ||
| if (wantsReactQuery) { | ||
| await writeFileWithPolicy( | ||
| cwd, | ||
| path2.join(libDir, "query-provider.tsx"), | ||
| getQueryProviderTemplate(env), | ||
| plan.policy | ||
| ); | ||
| } | ||
| if (wantsServer) { | ||
| await writeFileWithPolicy( | ||
| cwd, | ||
| path2.join(libDir, "server.ts"), | ||
| getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR), | ||
| plan.policy | ||
| ); | ||
| } | ||
| if (plan.envFile && answers.authMethod !== "browser") { | ||
| await writeEnv( | ||
| cwd, | ||
| plan.envFile, | ||
| answers.publishableKey || "", | ||
| answers.secretKey || "", | ||
| publishableKeyEnvVar, | ||
| wantsServer ? SECRET_KEY_ENV_VAR : null, | ||
| plan.policy | ||
| ); | ||
| } | ||
| let publishableKey = answers.publishableKey; | ||
| let secretKey = answers.secretKey; | ||
| let tenantName = ""; | ||
| if (answers.authMethod === "browser" && answers.aiTools.length > 0) { | ||
| try { | ||
| console.log(); | ||
| const creds = await browserAuth(); | ||
| publishableKey = creds.publishableKey; | ||
| secretKey = creds.secretKey; | ||
| tenantName = creds.tenantName; | ||
| if (plan.envFile && publishableKey) { | ||
| await writeEnv( | ||
| cwd, | ||
| plan.envFile, | ||
| publishableKey, | ||
| secretKey, | ||
| publishableKeyEnvVar, | ||
| wantsServer ? SECRET_KEY_ENV_VAR : null, | ||
| "overwrite", | ||
| true | ||
| ); | ||
| } | ||
| } catch (err) { | ||
| console.log( | ||
| pc2.yellow(" Browser auth skipped:"), | ||
| err instanceof Error ? err.message : String(err) | ||
| ); | ||
| } | ||
| } | ||
| if (answers.aiTools.length > 0) { | ||
| for (const tool of answers.aiTools) { | ||
| await writeMcpConfig(tool, cwd); | ||
| } | ||
| addToGitignore(cwd, answers.aiTools); | ||
| if (answers.aiTools.includes("claude")) { | ||
| await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy); | ||
| } | ||
| } | ||
| return { | ||
| ...installResult, | ||
| publishableKey: publishableKey || void 0, | ||
| secretKey: secretKey || void 0, | ||
| tenantName: tenantName || void 0 | ||
| }; | ||
| } | ||
| function installDeps(cwd, pm, hasSdk, hasReactQuery, wantsReactQuery) { | ||
| const allDeps = [ | ||
| { name: "@01.software/sdk", installed: hasSdk, needed: true }, | ||
| { name: "@tanstack/react-query", installed: hasReactQuery, needed: wantsReactQuery } | ||
| ]; | ||
| const fullList = allDeps.filter((d) => d.needed).map((d) => d.name); | ||
| const toInstall = allDeps.filter((d) => d.needed && !d.installed).map((d) => d.name); | ||
| const fullCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), fullList); | ||
| if (toInstall.length === 0) { | ||
| console.log(pc2.dim(` Dependencies already installed: ${fullList.join(", ")}`)); | ||
| return { installFailed: false, installSkipped: true, installCmd: fullCmd }; | ||
| } | ||
| const addCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), toInstall); | ||
| console.log(pc2.dim(` Installing ${toInstall.join(" and ")}...`)); | ||
| const wsPatched = pm === "pnpm" && patchPnpmWorkspace(cwd); | ||
| let installFailed = false; | ||
| try { | ||
| execSync(addCmd, { cwd, stdio: "pipe" }); | ||
| console.log(pc2.green(" Installed"), toInstall.join(", ")); | ||
| } catch (error) { | ||
| installFailed = true; | ||
| const err = error; | ||
| const msg = String(err.stderr || "").trim() || String(err.stdout || "").trim() || String(error); | ||
| console.log(pc2.yellow(" Install failed \u2014 continuing with scaffolding")); | ||
| const firstLines = msg.split("\n").slice(0, 3).map((l) => ` ${l}`).join("\n"); | ||
| if (firstLines) console.log(pc2.dim(firstLines)); | ||
| console.log(pc2.dim(` Run manually: ${addCmd}`)); | ||
| } finally { | ||
| if (wsPatched) restorePnpmWorkspace(cwd); | ||
| } | ||
| return { installFailed, installSkipped: false, installCmd: addCmd }; | ||
| } | ||
| function buildAddCmd(pm, hasPnpmWs, deps) { | ||
| const pkgs = deps.join(" "); | ||
| switch (pm) { | ||
| case "pnpm": | ||
| return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`; | ||
| case "yarn": | ||
| return `yarn add ${pkgs}`; | ||
| case "bun": | ||
| return `bun add ${pkgs}`; | ||
| default: | ||
| return `npm install ${pkgs}`; | ||
| } | ||
| } | ||
| async function planConflictsAndEnv(cwd, baseDir, env, answers) { | ||
| const candidates = []; | ||
| const libDir = path2.join(baseDir, "lib", "software"); | ||
| if (needsClient(env)) candidates.push(path2.join(libDir, "client.ts")); | ||
| if (needsReactQuery(env)) candidates.push(path2.join(libDir, "query-provider.tsx")); | ||
| if (needsServer(env)) candidates.push(path2.join(libDir, "server.ts")); | ||
| if (answers.aiTools.includes("claude")) { | ||
| for (const { dirName } of getSkillFiles()) { | ||
| candidates.push(path2.join(cwd, ".claude", "skills", dirName, "SKILL.md")); | ||
| } | ||
| } | ||
| const conflicts = candidates.filter((p) => fs2.existsSync(p)); | ||
| let policy = "skip"; | ||
| if (conflicts.length > 0) { | ||
| console.log(pc2.yellow(` ${conflicts.length} file(s) already exist:`)); | ||
| for (const c of conflicts) console.log(pc2.dim(` ${path2.relative(cwd, c)}`)); | ||
| const { selected } = await prompts({ | ||
| type: "select", | ||
| name: "selected", | ||
| message: "How should I handle existing files?", | ||
| choices: [ | ||
| { title: "Keep existing (skip)", value: "skip" }, | ||
| { title: "Overwrite all", value: "overwrite" }, | ||
| { title: "Ask for each", value: "ask" } | ||
| ], | ||
| initial: 0 | ||
| }); | ||
| policy = selected ?? "skip"; | ||
| } | ||
| const envFile = env === "vanilla" || env === "edge" ? "" : await pickEnvFile(cwd, env); | ||
| return { policy, envFile }; | ||
| } | ||
| async function pickEnvFile(cwd, env) { | ||
| const candidates = [".env.local", ".env", ".env.development"]; | ||
| const existing = candidates.filter((f) => fs2.existsSync(path2.join(cwd, f))); | ||
| const preferred = env === "nextjs" ? ".env.local" : ".env"; | ||
| if (existing.length === 0) return preferred; | ||
| if (existing.length === 1 && existing[0] === preferred) return existing[0]; | ||
| const options = Array.from(/* @__PURE__ */ new Set([...existing, preferred])); | ||
| const choices = options.map((f) => ({ | ||
| title: f, | ||
| description: existing.includes(f) ? "exists" : "create", | ||
| value: f | ||
| })); | ||
| const initial = Math.max( | ||
| 0, | ||
| choices.findIndex((c) => c.value === preferred) | ||
| ); | ||
| const { file } = await prompts({ | ||
| type: "select", | ||
| name: "file", | ||
| message: "Which env file should I write SDK credentials to?", | ||
| choices, | ||
| initial | ||
| }); | ||
| return file ?? preferred; | ||
| } | ||
| async function writeFileWithPolicy(cwd, filePath, content, policy) { | ||
| const rel = path2.relative(cwd, filePath); | ||
| if (!fs2.existsSync(filePath)) { | ||
| fs2.mkdirSync(path2.dirname(filePath), { recursive: true }); | ||
| fs2.writeFileSync(filePath, content); | ||
| console.log(pc2.green(" Created"), rel); | ||
| return; | ||
| } | ||
| const existing = fs2.readFileSync(filePath, "utf-8"); | ||
| if (existing === content) { | ||
| console.log(pc2.dim(" Unchanged"), rel); | ||
| return; | ||
| } | ||
| let shouldWrite = false; | ||
| if (policy === "overwrite") { | ||
| shouldWrite = true; | ||
| } else if (policy === "ask") { | ||
| const { confirm } = await prompts({ | ||
| type: "confirm", | ||
| name: "confirm", | ||
| message: `Overwrite ${rel}?`, | ||
| initial: false | ||
| }); | ||
| shouldWrite = !!confirm; | ||
| } | ||
| if (shouldWrite) { | ||
| fs2.writeFileSync(filePath, content); | ||
| console.log(pc2.green(" Overwrote"), rel); | ||
| } else { | ||
| console.log(pc2.yellow(" Skipped"), rel, pc2.dim("(already exists)")); | ||
| } | ||
| } | ||
| async function writeEnv(cwd, envFile, publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar, policy, fromBrowserAuth = false) { | ||
| const envPath = path2.join(cwd, envFile); | ||
| const targets = [ | ||
| { name: publishableKeyEnvVar, value: publishableKey } | ||
| ]; | ||
| if (secretKeyEnvVar) { | ||
| targets.push({ name: secretKeyEnvVar, value: secretKey }); | ||
| } | ||
| if (!fs2.existsSync(envPath)) { | ||
| const initial = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar); | ||
| writeEnvFile(envPath, initial.trimStart()); | ||
| console.log(pc2.green(" Created"), envFile); | ||
| return; | ||
| } | ||
| let content = fs2.readFileSync(envPath, "utf-8"); | ||
| let modified = false; | ||
| let appendedHeader = false; | ||
| const headerAlreadyPresent = targets.some( | ||
| (t) => readEnvValue(content, t.name) !== null | ||
| ); | ||
| for (const { name, value } of targets) { | ||
| const existing = readEnvValue(content, name); | ||
| if (existing === null) { | ||
| if (!headerAlreadyPresent && !appendedHeader) { | ||
| if (content.length > 0 && !content.endsWith("\n")) content += "\n"; | ||
| content += "\n# 01.software\n"; | ||
| appendedHeader = true; | ||
| } | ||
| content = setEnvValue(content, name, value); | ||
| modified = true; | ||
| continue; | ||
| } | ||
| if (existing === value) continue; | ||
| if (!value) continue; | ||
| let shouldOverwrite = false; | ||
| if (policy === "overwrite") { | ||
| shouldOverwrite = true; | ||
| } else if (policy === "ask") { | ||
| const { confirm } = await prompts({ | ||
| type: "confirm", | ||
| name: "confirm", | ||
| message: `${name} already set in ${envFile}. Overwrite?`, | ||
| initial: fromBrowserAuth | ||
| }); | ||
| shouldOverwrite = !!confirm; | ||
| } | ||
| if (shouldOverwrite) { | ||
| content = setEnvValue(content, name, value); | ||
| modified = true; | ||
| } | ||
| } | ||
| if (modified) { | ||
| writeEnvFile(envPath, content); | ||
| console.log( | ||
| pc2.green(" Updated"), | ||
| envFile, | ||
| fromBrowserAuth ? pc2.dim("(SDK credentials)") : "" | ||
| ); | ||
| } else { | ||
| chmodSecretFile(envPath); | ||
| console.log(pc2.dim(" Unchanged"), envFile); | ||
| } | ||
| } | ||
| function resolveMcpLocation(tool, cwd) { | ||
| const home = os.homedir(); | ||
| switch (tool) { | ||
| case "claude": | ||
| return { | ||
| kind: "json", | ||
| absolutePath: path2.join(cwd, ".mcp.json"), | ||
| displayPath: ".mcp.json", | ||
| gitignoreEntry: ".mcp.json" | ||
| }; | ||
| case "cursor": | ||
| return { | ||
| kind: "json", | ||
| absolutePath: path2.join(cwd, ".cursor", "mcp.json"), | ||
| displayPath: ".cursor/mcp.json", | ||
| gitignoreEntry: ".cursor/mcp.json" | ||
| }; | ||
| case "vscode": | ||
| return { | ||
| kind: "json", | ||
| absolutePath: path2.join(cwd, ".vscode", "mcp.json"), | ||
| jsonClient: "vscode", | ||
| displayPath: ".vscode/mcp.json", | ||
| gitignoreEntry: ".vscode/mcp.json" | ||
| }; | ||
| case "windsurf": { | ||
| if (!home) return null; | ||
| const p = path2.join(home, ".codeium", "windsurf", "mcp_config.json"); | ||
| return { | ||
| kind: "json", | ||
| absolutePath: p, | ||
| jsonClient: "windsurf", | ||
| displayPath: p, | ||
| gitignoreEntry: null, | ||
| global: true | ||
| }; | ||
| } | ||
| case "codex": { | ||
| if (!home) return null; | ||
| const p = path2.join(home, ".codex", "config.toml"); | ||
| return { kind: "toml", absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }; | ||
| } | ||
| case "gemini": { | ||
| if (!home) return null; | ||
| const p = path2.join(home, ".gemini", "settings.json"); | ||
| return { kind: "json", absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }; | ||
| } | ||
| } | ||
| } | ||
| async function writeMcpConfig(tool, cwd) { | ||
| const loc = resolveMcpLocation(tool, cwd); | ||
| if (!loc) { | ||
| console.log(pc2.yellow(` Skipped ${tool}`), pc2.dim("(HOME not set)")); | ||
| return; | ||
| } | ||
| if (!loc.global) { | ||
| fs2.mkdirSync(path2.dirname(loc.absolutePath), { recursive: true }); | ||
| } | ||
| try { | ||
| if (loc.kind === "json") { | ||
| writeJsonMcp(loc); | ||
| } else { | ||
| writeTomlMcp(loc); | ||
| } | ||
| } catch (err) { | ||
| console.log( | ||
| pc2.yellow(` Skipped ${loc.displayPath}`), | ||
| pc2.dim(err instanceof Error ? err.message : String(err)) | ||
| ); | ||
| } | ||
| } | ||
| function writeJsonMcp(loc) { | ||
| const writeFile = loc.global ? (target, content) => writeSecretGlobalConfig(target, content) : (target, content) => fs2.writeFileSync(target, content); | ||
| if (!fs2.existsSync(loc.absolutePath)) { | ||
| writeFile(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient)); | ||
| console.log(pc2.green(" Created"), loc.displayPath); | ||
| return; | ||
| } | ||
| const rootKey = getMcpRootKey(loc.jsonClient); | ||
| let existing; | ||
| try { | ||
| existing = JSON.parse(fs2.readFileSync(loc.absolutePath, "utf-8")); | ||
| } catch { | ||
| console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(could not parse existing file)")); | ||
| return; | ||
| } | ||
| const nextEntry = getMcpServerEntry(loc.jsonClient); | ||
| const existingServers = existing[rootKey] ?? void 0; | ||
| if (existingServers?.["01software"] && JSON.stringify(existingServers["01software"]) === JSON.stringify(nextEntry)) { | ||
| console.log(pc2.dim(" Unchanged"), loc.displayPath); | ||
| return; | ||
| } | ||
| const servers = existing[rootKey] ?? {}; | ||
| servers["01software"] = nextEntry; | ||
| existing[rootKey] = servers; | ||
| writeFile(loc.absolutePath, JSON.stringify(existing, null, 2) + "\n"); | ||
| console.log(pc2.green(" Updated"), loc.displayPath); | ||
| } | ||
| function writeTomlMcp(loc) { | ||
| const section = getCodexMcpTomlSection(); | ||
| const writeFile = loc.global ? (target, content) => writeSecretGlobalConfig(target, content) : (target, content) => fs2.writeFileSync(target, content); | ||
| if (!fs2.existsSync(loc.absolutePath)) { | ||
| writeFile(loc.absolutePath, section.trimStart()); | ||
| console.log(pc2.green(" Created"), loc.displayPath); | ||
| return; | ||
| } | ||
| const existing = fs2.readFileSync(loc.absolutePath, "utf-8"); | ||
| if (!existing.includes(CODEX_MCP_SECTION_MARKER)) { | ||
| const sep = existing.endsWith("\n") ? "" : "\n"; | ||
| writeFile(loc.absolutePath, existing + sep + section); | ||
| console.log(pc2.green(" Updated"), loc.displayPath); | ||
| return; | ||
| } | ||
| const replaced = replaceTomlMcpSection(existing, section); | ||
| if (replaced === existing) { | ||
| console.log(pc2.dim(" Unchanged"), loc.displayPath); | ||
| return; | ||
| } | ||
| writeFile(loc.absolutePath, replaced); | ||
| console.log(pc2.green(" Updated"), loc.displayPath); | ||
| } | ||
| function addToGitignore(cwd, tools) { | ||
| const entries = []; | ||
| for (const tool of tools) { | ||
| const loc = resolveMcpLocation(tool, cwd); | ||
| if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry); | ||
| } | ||
| 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\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, publishableKey, secretKey, tenantName, policy) { | ||
| let ctx = { | ||
| tenantName: tenantName || "Your Tenant", | ||
| features: void 0, | ||
| collections: void 0 | ||
| }; | ||
| if (publishableKey && secretKey) { | ||
| const fetched = await fetchTenantContext(publishableKey, secretKey); | ||
| if (fetched) { | ||
| ctx = { | ||
| tenantName: fetched.tenantName || ctx.tenantName, | ||
| features: fetched.features, | ||
| collections: fetched.collections | ||
| }; | ||
| } | ||
| } | ||
| const claudeDir = path2.join(cwd, ".claude"); | ||
| const softwareDir = path2.join(claudeDir, "01software"); | ||
| const skillsDir = path2.join(claudeDir, "skills"); | ||
| fs2.mkdirSync(softwareDir, { recursive: true }); | ||
| fs2.mkdirSync(skillsDir, { recursive: true }); | ||
| const contextPath = path2.join(softwareDir, "context.md"); | ||
| const contextExists = fs2.existsSync(contextPath); | ||
| fs2.writeFileSync(contextPath, generateClaudeMd(ctx)); | ||
| console.log(pc2.green(contextExists ? " Updated" : " Created"), ".claude/01software/context.md"); | ||
| const claudeMdPath = path2.join(claudeDir, "CLAUDE.md"); | ||
| const importLine = "@.claude/01software/context.md"; | ||
| if (!fs2.existsSync(claudeMdPath)) { | ||
| fs2.writeFileSync(claudeMdPath, importLine + "\n"); | ||
| console.log(pc2.green(" Created"), ".claude/CLAUDE.md"); | ||
| } else { | ||
| const existing = fs2.readFileSync(claudeMdPath, "utf-8"); | ||
| if (!existing.includes(importLine)) { | ||
| const prefix = existing.endsWith("\n") ? "\n" : "\n\n"; | ||
| fs2.appendFileSync(claudeMdPath, prefix + importLine + "\n"); | ||
| console.log(pc2.green(" Updated"), ".claude/CLAUDE.md", pc2.dim("(added @import)")); | ||
| } else { | ||
| console.log(pc2.dim(" Unchanged"), ".claude/CLAUDE.md"); | ||
| } | ||
| } | ||
| for (const { dirName, content } of getSkillFiles()) { | ||
| const skillDir = path2.join(skillsDir, dirName); | ||
| const skillPath = path2.join(skillDir, "SKILL.md"); | ||
| fs2.mkdirSync(skillDir, { recursive: true }); | ||
| await writeFileWithPolicy(cwd, skillPath, content, policy); | ||
| } | ||
| } | ||
| var WS_FILE = "pnpm-workspace.yaml"; | ||
| var WS_BACKUP = "pnpm-workspace.yaml.bak"; | ||
| function hasPnpmWorkspace(cwd) { | ||
| return fs2.existsSync(path2.join(cwd, WS_FILE)); | ||
| } | ||
| function patchPnpmWorkspace(cwd) { | ||
| const wsPath = path2.join(cwd, WS_FILE); | ||
| if (!fs2.existsSync(wsPath)) return false; | ||
| const content = fs2.readFileSync(wsPath, "utf-8"); | ||
| if (content.includes("packages:")) return false; | ||
| fs2.copyFileSync(wsPath, path2.join(cwd, WS_BACKUP)); | ||
| fs2.writeFileSync(wsPath, content.trimEnd() + "\npackages: []\n"); | ||
| return true; | ||
| } | ||
| function restorePnpmWorkspace(cwd) { | ||
| const backupPath = path2.join(cwd, WS_BACKUP); | ||
| if (!fs2.existsSync(backupPath)) return; | ||
| fs2.copyFileSync(backupPath, path2.join(cwd, WS_FILE)); | ||
| fs2.unlinkSync(backupPath); | ||
| } | ||
| export { | ||
| detectProject, | ||
| init | ||
| }; | ||
| //# sourceMappingURL=chunk-2IGKOSK7.js.map |
| {"version":3,"sources":["../src/init.ts","../src/detect.ts","../src/browser-auth.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { execSync } from 'node:child_process'\nimport pc from 'picocolors'\nimport prompts from 'prompts'\nimport type { PackageManager, ProjectEnv, ProjectInfo } from './detect'\nimport {\n needsClient,\n needsServer,\n needsReactQuery,\n getPublishableKeyEnvVar,\n} from './detect'\nimport type { AiTool, InitAnswers } from './prompts'\nimport {\n getClientTemplate,\n getAnalyticsTemplate,\n getQueryProviderTemplate,\n getServerTemplate,\n getEnvContent,\n getMcpConfigTemplate,\n getMcpRootKey,\n getMcpServerEntry,\n getCodexMcpTomlSection,\n CODEX_MCP_SECTION_MARKER,\n} from './templates'\nimport { startBrowserAuth } from './browser-auth'\nimport {\n generateClaudeMd,\n getSkillFiles,\n fetchTenantContext,\n} from './ai-docs'\nimport {\n readEnvValue,\n setEnvValue,\n replaceTomlMcpSection,\n writeEnvFile,\n chmodSecretFile,\n writeSecretGlobalConfig,\n} from './file-ops'\n\ntype ResolvedProjectInfo = Omit<ProjectInfo, 'packageManager'> & {\n packageManager: PackageManager\n}\n\nconst SECRET_KEY_ENV_VAR = 'SOFTWARE_SECRET_KEY'\n\nexport type ConflictPolicy = 'overwrite' | 'skip' | 'ask'\n\nexport interface InitResult {\n installFailed: boolean\n installSkipped: boolean\n installCmd: string\n /** Set when browser-auth or manual entry produced a publishable key the\n * caller should prefer over `answers.publishableKey` when computing\n * next-step messages. */\n publishableKey?: string\n /** Set when browser-auth or manual entry produced a secret key. */\n secretKey?: string\n /** Tenant display name from browser-auth, when available. */\n tenantName?: string\n}\n\nexport interface InitDeps {\n /** Test seam — replaces the real browser-auth flow. */\n startBrowserAuth?: typeof startBrowserAuth\n /** Test seam — skips actually invoking the package manager. */\n skipInstall?: boolean\n}\n\nexport async function init(\n cwd: string,\n info: ResolvedProjectInfo,\n answers: InitAnswers,\n deps: InitDeps = {},\n): Promise<InitResult> {\n const { packageManager, srcDir } = info\n const env = answers.env\n const baseDir = srcDir ? path.join(cwd, 'src') : cwd\n const browserAuth = deps.startBrowserAuth ?? startBrowserAuth\n\n const publishableKeyEnvVar = getPublishableKeyEnvVar(env)\n const wantsClient = needsClient(env)\n const wantsServer = needsServer(env)\n const wantsReactQuery = needsReactQuery(env)\n\n // 0. Plan: scan for conflicts and pick the env file. Both prompts are\n // front-loaded so the rest of init() doesn't interleave I/O with prompts.\n const plan = await planConflictsAndEnv(cwd, baseDir, env, answers)\n\n // 1. Install dependencies — skip whatever's already in package.json\n const installResult = deps.skipInstall\n ? {\n installFailed: false,\n installSkipped: true,\n installCmd: buildAddCmd(packageManager, hasPnpmWorkspace(cwd), [\n '@01.software/sdk',\n ]),\n }\n : installDeps(\n cwd,\n packageManager,\n info.hasSdk,\n info.hasReactQuery,\n wantsReactQuery,\n )\n\n // 2. Write lib/software/ files\n if (wantsClient || wantsReactQuery || wantsServer) {\n fs.mkdirSync(path.join(baseDir, 'lib', 'software'), { recursive: true })\n }\n const libDir = path.join(baseDir, 'lib', 'software')\n\n if (wantsClient) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'client.ts'),\n getClientTemplate(env, publishableKeyEnvVar),\n plan.policy,\n )\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'analytics.ts'),\n getAnalyticsTemplate(env, publishableKeyEnvVar),\n plan.policy,\n )\n }\n if (wantsReactQuery) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'query-provider.tsx'),\n getQueryProviderTemplate(env),\n plan.policy,\n )\n }\n if (wantsServer) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'server.ts'),\n getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR),\n plan.policy,\n )\n }\n\n // 3. Append to env file (browser auth handles its own write in step 4)\n if (plan.envFile && answers.authMethod !== 'browser') {\n await writeEnv(\n cwd,\n plan.envFile,\n answers.publishableKey || '',\n answers.secretKey || '',\n publishableKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n plan.policy,\n )\n }\n\n // 4. Browser auth — get real credentials and force-write env\n let publishableKey = answers.publishableKey\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 browserAuth()\n publishableKey = creds.publishableKey\n secretKey = creds.secretKey\n tenantName = creds.tenantName\n\n // Browser auth just minted fresh credentials → policy is overwrite.\n if (plan.envFile && publishableKey) {\n await writeEnv(\n cwd,\n plan.envFile,\n publishableKey,\n secretKey,\n publishableKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n 'overwrite',\n true,\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. AI tool configs (MCP + Claude docs)\n if (answers.aiTools.length > 0) {\n for (const tool of answers.aiTools) {\n await writeMcpConfig(tool, cwd)\n }\n\n addToGitignore(cwd, answers.aiTools)\n\n if (answers.aiTools.includes('claude')) {\n await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy)\n }\n }\n\n return {\n ...installResult,\n publishableKey: publishableKey || undefined,\n secretKey: secretKey || undefined,\n tenantName: tenantName || undefined,\n }\n}\n\n// ── Install ──────────────────────────────────────────────────────────\n\ninterface DepSpec {\n name: string\n installed: boolean\n needed: boolean\n}\n\nfunction installDeps(\n cwd: string,\n pm: PackageManager,\n hasSdk: boolean,\n hasReactQuery: boolean,\n wantsReactQuery: boolean,\n): InitResult {\n const allDeps: DepSpec[] = [\n { name: '@01.software/sdk', installed: hasSdk, needed: true },\n { name: '@tanstack/react-query', installed: hasReactQuery, needed: wantsReactQuery },\n ]\n const fullList = allDeps.filter((d) => d.needed).map((d) => d.name)\n const toInstall = allDeps.filter((d) => d.needed && !d.installed).map((d) => d.name)\n const fullCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), fullList)\n\n if (toInstall.length === 0) {\n console.log(pc.dim(` Dependencies already installed: ${fullList.join(', ')}`))\n return { installFailed: false, installSkipped: true, installCmd: fullCmd }\n }\n\n const addCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), toInstall)\n console.log(pc.dim(` Installing ${toInstall.join(' and ')}...`))\n\n const wsPatched = pm === 'pnpm' && patchPnpmWorkspace(cwd)\n let installFailed = false\n\n try {\n execSync(addCmd, { cwd, stdio: 'pipe' })\n console.log(pc.green(' Installed'), toInstall.join(', '))\n } catch (error) {\n installFailed = true\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.yellow(' Install failed — continuing with scaffolding'))\n const firstLines = msg.split('\\n').slice(0, 3).map((l) => ` ${l}`).join('\\n')\n if (firstLines) console.log(pc.dim(firstLines))\n console.log(pc.dim(` Run manually: ${addCmd}`))\n } finally {\n if (wsPatched) restorePnpmWorkspace(cwd)\n }\n\n return { installFailed, installSkipped: false, installCmd: addCmd }\n}\n\nfunction buildAddCmd(\n pm: PackageManager,\n hasPnpmWs: boolean,\n deps: string[],\n): string {\n const pkgs = deps.join(' ')\n switch (pm) {\n case 'pnpm':\n return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`\n case 'yarn':\n return `yarn add ${pkgs}`\n case 'bun':\n return `bun add ${pkgs}`\n default:\n return `npm install ${pkgs}`\n }\n}\n\n// ── Conflict planning ────────────────────────────────────────────────\n\ninterface PlanResult {\n policy: ConflictPolicy\n /** Empty string when env file is not used (vanilla / edge) */\n envFile: string\n}\n\nasync function planConflictsAndEnv(\n cwd: string,\n baseDir: string,\n env: ProjectEnv,\n answers: InitAnswers,\n): Promise<PlanResult> {\n const candidates: string[] = []\n const libDir = path.join(baseDir, 'lib', 'software')\n if (needsClient(env)) candidates.push(path.join(libDir, 'client.ts'))\n if (needsReactQuery(env)) candidates.push(path.join(libDir, 'query-provider.tsx'))\n if (needsServer(env)) candidates.push(path.join(libDir, 'server.ts'))\n if (answers.aiTools.includes('claude')) {\n for (const { dirName } of getSkillFiles()) {\n candidates.push(path.join(cwd, '.claude', 'skills', dirName, 'SKILL.md'))\n }\n }\n\n const conflicts = candidates.filter((p) => fs.existsSync(p))\n\n let policy: ConflictPolicy = 'skip'\n if (conflicts.length > 0) {\n console.log(pc.yellow(` ${conflicts.length} file(s) already exist:`))\n for (const c of conflicts) console.log(pc.dim(` ${path.relative(cwd, c)}`))\n const { selected } = await prompts({\n type: 'select',\n name: 'selected',\n message: 'How should I handle existing files?',\n choices: [\n { title: 'Keep existing (skip)', value: 'skip' },\n { title: 'Overwrite all', value: 'overwrite' },\n { title: 'Ask for each', value: 'ask' },\n ],\n initial: 0,\n })\n policy = (selected as ConflictPolicy) ?? 'skip'\n }\n\n const envFile =\n env === 'vanilla' || env === 'edge' ? '' : await pickEnvFile(cwd, env)\n\n return { policy, envFile }\n}\n\nasync function pickEnvFile(cwd: string, env: ProjectEnv): Promise<string> {\n // Order matters — `.env.local` first so it's the default selection on\n // Next.js, where it's the conventional secret store.\n const candidates = ['.env.local', '.env', '.env.development']\n const existing = candidates.filter((f) => fs.existsSync(path.join(cwd, f)))\n const preferred = env === 'nextjs' ? '.env.local' : '.env'\n\n if (existing.length === 0) return preferred\n if (existing.length === 1 && existing[0] === preferred) return existing[0]\n\n // Multiple files exist OR the only existing file isn't the preferred default.\n const options = Array.from(new Set([...existing, preferred]))\n const choices = options.map((f) => ({\n title: f,\n description: existing.includes(f) ? 'exists' : 'create',\n value: f,\n }))\n const initial = Math.max(\n 0,\n choices.findIndex((c) => c.value === preferred),\n )\n const { file } = await prompts({\n type: 'select',\n name: 'file',\n message: 'Which env file should I write SDK credentials to?',\n choices,\n initial,\n })\n return file ?? preferred\n}\n\n// ── Generic file write ───────────────────────────────────────────────\n\nasync function writeFileWithPolicy(\n cwd: string,\n filePath: string,\n content: string,\n policy: ConflictPolicy,\n): Promise<void> {\n const rel = path.relative(cwd, filePath)\n if (!fs.existsSync(filePath)) {\n fs.mkdirSync(path.dirname(filePath), { recursive: true })\n fs.writeFileSync(filePath, content)\n console.log(pc.green(' Created'), rel)\n return\n }\n\n const existing = fs.readFileSync(filePath, 'utf-8')\n if (existing === content) {\n console.log(pc.dim(' Unchanged'), rel)\n return\n }\n\n let shouldWrite = false\n if (policy === 'overwrite') {\n shouldWrite = true\n } else if (policy === 'ask') {\n const { confirm } = await prompts({\n type: 'confirm',\n name: 'confirm',\n message: `Overwrite ${rel}?`,\n initial: false,\n })\n shouldWrite = !!confirm\n }\n\n if (shouldWrite) {\n fs.writeFileSync(filePath, content)\n console.log(pc.green(' Overwrote'), rel)\n } else {\n console.log(pc.yellow(' Skipped'), rel, pc.dim('(already exists)'))\n }\n}\n\n// ── Env file write ───────────────────────────────────────────────────\n\nasync function writeEnv(\n cwd: string,\n envFile: string,\n publishableKey: string,\n secretKey: string,\n publishableKeyEnvVar: string,\n secretKeyEnvVar: string | null,\n policy: ConflictPolicy,\n fromBrowserAuth = false,\n): Promise<void> {\n const envPath = path.join(cwd, envFile)\n\n const targets: { name: string; value: string }[] = [\n { name: publishableKeyEnvVar, value: publishableKey },\n ]\n if (secretKeyEnvVar) {\n targets.push({ name: secretKeyEnvVar, value: secretKey })\n }\n\n if (!fs.existsSync(envPath)) {\n const initial = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar)\n writeEnvFile(envPath, initial.trimStart())\n console.log(pc.green(' Created'), envFile)\n return\n }\n\n let content = fs.readFileSync(envPath, 'utf-8')\n let modified = false\n let appendedHeader = false\n const headerAlreadyPresent = targets.some(\n (t) => readEnvValue(content, t.name) !== null,\n )\n\n for (const { name, value } of targets) {\n const existing = readEnvValue(content, name)\n\n if (existing === null) {\n // Append. Add the `# 01.software` section header on first append, but\n // only if no other 01.software keys are already in the file.\n if (!headerAlreadyPresent && !appendedHeader) {\n if (content.length > 0 && !content.endsWith('\\n')) content += '\\n'\n content += '\\n# 01.software\\n'\n appendedHeader = true\n }\n content = setEnvValue(content, name, value)\n modified = true\n continue\n }\n\n if (existing === value) continue\n // Don't overwrite an existing real value with empty (e.g. user skipped key entry).\n if (!value) continue\n\n let shouldOverwrite = false\n if (policy === 'overwrite') {\n shouldOverwrite = true\n } else if (policy === 'ask') {\n const { confirm } = await prompts({\n type: 'confirm',\n name: 'confirm',\n message: `${name} already set in ${envFile}. Overwrite?`,\n initial: fromBrowserAuth,\n })\n shouldOverwrite = !!confirm\n }\n\n if (shouldOverwrite) {\n content = setEnvValue(content, name, value)\n modified = true\n }\n }\n\n if (modified) {\n writeEnvFile(envPath, content)\n console.log(\n pc.green(' Updated'),\n envFile,\n fromBrowserAuth ? pc.dim('(SDK credentials)') : '',\n )\n } else {\n // Tighten mode on existing files we did not modify, so re-runs harden\n // permissions on env files created by an earlier looser version.\n chmodSecretFile(envPath)\n console.log(pc.dim(' Unchanged'), envFile)\n }\n}\n\n// ── MCP targets registry ─────────────────────────────────────────────\n\ntype McpFormat = 'json' | 'toml'\ntype McpJsonClient = 'generic' | 'windsurf' | 'vscode'\ninterface McpLocation {\n kind: McpFormat\n absolutePath: string\n jsonClient?: McpJsonClient\n displayPath: string\n gitignoreEntry: string | null // null = global path, not worth ignoring\n /** Global config under user HOME — apply 0o600 mode, atomic rename, and\n * symlink/hardlink rejection per spec §\"Secret Handling Rules\". */\n global?: boolean\n}\n\nfunction resolveMcpLocation(tool: AiTool, cwd: string): McpLocation | null {\n const home = os.homedir()\n\n switch (tool) {\n case 'claude':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.mcp.json'),\n displayPath: '.mcp.json',\n gitignoreEntry: '.mcp.json',\n }\n case 'cursor':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.cursor', 'mcp.json'),\n displayPath: '.cursor/mcp.json',\n gitignoreEntry: '.cursor/mcp.json',\n }\n case 'vscode':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.vscode', 'mcp.json'),\n jsonClient: 'vscode',\n displayPath: '.vscode/mcp.json',\n gitignoreEntry: '.vscode/mcp.json',\n }\n case 'windsurf': {\n if (!home) return null\n const p = path.join(home, '.codeium', 'windsurf', 'mcp_config.json')\n return {\n kind: 'json',\n absolutePath: p,\n jsonClient: 'windsurf',\n displayPath: p,\n gitignoreEntry: null,\n global: true,\n }\n }\n case 'codex': {\n if (!home) return null\n const p = path.join(home, '.codex', 'config.toml')\n return { kind: 'toml', absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }\n }\n case 'gemini': {\n if (!home) return null\n const p = path.join(home, '.gemini', 'settings.json')\n return { kind: 'json', absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }\n }\n }\n}\n\nasync function writeMcpConfig(\n tool: AiTool,\n cwd: string,\n): Promise<void> {\n const loc = resolveMcpLocation(tool, cwd)\n if (!loc) {\n console.log(pc.yellow(` Skipped ${tool}`), pc.dim('(HOME not set)'))\n return\n }\n\n // For global secret-bearing config, parent-dir creation with 0o700 is\n // handled inside `writeSecretGlobalConfig`. Repo-local writes (Claude Code,\n // Cursor, VS Code) keep loose mkdir — they only contain OAuth/discovery URLs.\n if (!loc.global) {\n fs.mkdirSync(path.dirname(loc.absolutePath), { recursive: true })\n }\n\n try {\n if (loc.kind === 'json') {\n writeJsonMcp(loc)\n } else {\n writeTomlMcp(loc)\n }\n } catch (err) {\n console.log(\n pc.yellow(` Skipped ${loc.displayPath}`),\n pc.dim(err instanceof Error ? err.message : String(err)),\n )\n }\n}\n\nfunction writeJsonMcp(loc: McpLocation): void {\n const writeFile = loc.global\n ? (target: string, content: string) => writeSecretGlobalConfig(target, content)\n : (target: string, content: string) => fs.writeFileSync(target, content)\n\n if (!fs.existsSync(loc.absolutePath)) {\n writeFile(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient))\n console.log(pc.green(' Created'), loc.displayPath)\n return\n }\n\n // VS Code uses `servers` as the config root; all other JSON clients use `mcpServers`.\n const rootKey = getMcpRootKey(loc.jsonClient)\n\n let existing: Record<string, unknown>\n try {\n existing = JSON.parse(fs.readFileSync(loc.absolutePath, 'utf-8'))\n } catch {\n console.log(pc.yellow(' Skipped'), loc.displayPath, pc.dim('(could not parse existing file)'))\n return\n }\n\n const nextEntry = getMcpServerEntry(loc.jsonClient)\n const existingServers = (existing[rootKey] as Record<string, unknown> | undefined) ?? undefined\n if (\n existingServers?.['01software'] &&\n JSON.stringify(existingServers['01software']) === JSON.stringify(nextEntry)\n ) {\n console.log(pc.dim(' Unchanged'), loc.displayPath)\n return\n }\n\n const servers = (existing[rootKey] as Record<string, unknown> | undefined) ?? {}\n servers['01software'] = nextEntry\n existing[rootKey] = servers\n writeFile(loc.absolutePath, JSON.stringify(existing, null, 2) + '\\n')\n console.log(pc.green(' Updated'), loc.displayPath)\n}\n\nfunction writeTomlMcp(loc: McpLocation): void {\n const section = getCodexMcpTomlSection()\n const writeFile = loc.global\n ? (target: string, content: string) => writeSecretGlobalConfig(target, content)\n : (target: string, content: string) => fs.writeFileSync(target, content)\n\n if (!fs.existsSync(loc.absolutePath)) {\n writeFile(loc.absolutePath, section.trimStart())\n console.log(pc.green(' Created'), loc.displayPath)\n return\n }\n\n const existing = fs.readFileSync(loc.absolutePath, 'utf-8')\n\n if (!existing.includes(CODEX_MCP_SECTION_MARKER)) {\n const sep = existing.endsWith('\\n') ? '' : '\\n'\n // Atomic rewrite (read-modify-write) — `appendFileSync` would bypass the\n // symlink/hardlink guards and the 0o600 mode of `writeSecretGlobalConfig`.\n writeFile(loc.absolutePath, existing + sep + section)\n console.log(pc.green(' Updated'), loc.displayPath)\n return\n }\n\n const replaced = replaceTomlMcpSection(existing, section)\n if (replaced === existing) {\n console.log(pc.dim(' Unchanged'), loc.displayPath)\n return\n }\n\n writeFile(loc.absolutePath, replaced)\n console.log(pc.green(' Updated'), loc.displayPath)\n}\n\nfunction addToGitignore(cwd: string, tools: AiTool[]): void {\n const entries: string[] = []\n for (const tool of tools) {\n const loc = resolveMcpLocation(tool, cwd)\n if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry)\n }\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\\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 publishableKey: string,\n secretKey: string,\n tenantName: string,\n policy: ConflictPolicy,\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 (publishableKey && secretKey) {\n const fetched = await fetchTenantContext(publishableKey, 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 softwareDir = path.join(claudeDir, '01software')\n const skillsDir = path.join(claudeDir, 'skills')\n fs.mkdirSync(softwareDir, { recursive: true })\n fs.mkdirSync(skillsDir, { recursive: true })\n\n // context.md is intentionally always overwritten so re-running init\n // refreshes tenant data; users don't customize this file.\n const contextPath = path.join(softwareDir, 'context.md')\n const contextExists = fs.existsSync(contextPath)\n fs.writeFileSync(contextPath, generateClaudeMd(ctx))\n console.log(pc.green(contextExists ? ' Updated' : ' Created'), '.claude/01software/context.md')\n\n // CLAUDE.md — append @import line (idempotent, never overwrites user content)\n const claudeMdPath = path.join(claudeDir, 'CLAUDE.md')\n const importLine = '@.claude/01software/context.md'\n if (!fs.existsSync(claudeMdPath)) {\n fs.writeFileSync(claudeMdPath, importLine + '\\n')\n console.log(pc.green(' Created'), '.claude/CLAUDE.md')\n } else {\n const existing = fs.readFileSync(claudeMdPath, 'utf-8')\n if (!existing.includes(importLine)) {\n const prefix = existing.endsWith('\\n') ? '\\n' : '\\n\\n'\n fs.appendFileSync(claudeMdPath, prefix + importLine + '\\n')\n console.log(pc.green(' Updated'), '.claude/CLAUDE.md', pc.dim('(added @import)'))\n } else {\n console.log(pc.dim(' Unchanged'), '.claude/CLAUDE.md')\n }\n }\n\n // Skill files honour the user's conflict policy (templates the user may have\n // tuned to their workflow).\n for (const { dirName, content } of getSkillFiles()) {\n const skillDir = path.join(skillsDir, dirName)\n const skillPath = path.join(skillDir, 'SKILL.md')\n fs.mkdirSync(skillDir, { recursive: true })\n await writeFileWithPolicy(cwd, skillPath, content, policy)\n }\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 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 hasReactQuery: 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 let hasReactQuery = false\n let parseError = 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 {\n hasPackageJson: true,\n parseError: true,\n env: 'node',\n packageManager: null,\n hasSdk: false,\n hasReactQuery: false,\n srcDir: false,\n }\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 hasReactQuery = '@tanstack/react-query' 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, parseError, env, packageManager, hasSdk, hasReactQuery, 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 publishable key */\nexport function getPublishableKeyEnvVar(env: ProjectEnv): string {\n switch (env) {\n case 'nextjs':\n return 'NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY'\n case 'react-vite':\n return 'VITE_SOFTWARE_PUBLISHABLE_KEY'\n case 'react-cra':\n return 'REACT_APP_SOFTWARE_PUBLISHABLE_KEY'\n default:\n return 'SOFTWARE_PUBLISHABLE_KEY'\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\ninterface ExchangeResponse {\n publishableKey: string\n secretKey: string\n tenantName: string\n tenantId: string\n}\n\nasync function exchangeCode(\n webUrl: string,\n code: string,\n): Promise<ExchangeResponse | null> {\n const url = `${webUrl}/api/cli/exchange`\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code }),\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n console.error(\n pc.red(\n `Exchange failed: HTTP ${res.status} from ${url}${body ? ` — ${body.slice(0, 200)}` : ''}`,\n ),\n )\n return null\n }\n const data = (await res.json()) as Partial<ExchangeResponse>\n if (\n typeof data.publishableKey !== 'string' ||\n typeof data.secretKey !== 'string' ||\n typeof data.tenantName !== 'string' ||\n typeof data.tenantId !== 'string'\n ) {\n console.error(pc.red(`Exchange failed: malformed response from ${url}`))\n return null\n }\n return {\n publishableKey: data.publishableKey,\n secretKey: data.secretKey,\n tenantName: data.tenantName,\n tenantId: data.tenantId,\n }\n } catch (err) {\n console.error(\n pc.red(\n `Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`,\n ),\n )\n return null\n }\n}\n\nexport async function startBrowserAuth(options?: {\n webUrl?: string\n tenantId?: string\n}): Promise<{\n publishableKey: string\n secretKey: string\n tenantName: string\n tenantId?: string\n}> {\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' || req.method !== 'GET') {\n res.writeHead(404).end()\n return\n }\n\n const error = url.searchParams.get('error')\n if (error) {\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\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 const code = url.searchParams.get('code')\n const receivedState = url.searchParams.get('state')\n\n if (!code || !receivedState) {\n res\n .writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('Missing code or state.'))\n cleanup(new Error('Login failed: missing code or state.'))\n return\n }\n\n if (receivedState !== state) {\n res\n .writeHead(403, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('State mismatch.'))\n console.error(pc.red('Login failed: state mismatch.'))\n cleanup(new Error('Login failed: state mismatch.'))\n return\n }\n\n exchangeCode(webUrl, code).then((creds) => {\n if (!creds) {\n res\n .writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('Invalid or expired code.'))\n cleanup(new Error('Login failed: code exchange failed.'))\n return\n }\n\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(SUCCESS_HTML)\n\n console.log(pc.green(`\\nLogged in successfully!`))\n console.log(pc.dim(`Tenant: ${creds.tenantName}`))\n\n cleanup(null, creds)\n })\n })\n\n let timeout: ReturnType<typeof setTimeout>\n let completed = false\n\n function cleanup(err: Error | null, result?: ExchangeResponse) {\n if (completed) return\n completed = true\n clearTimeout(timeout)\n server.closeAllConnections?.()\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"],"mappings":"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","names":["fs","path","pc","os","path","fs","pc"]} |
| #!/usr/bin/env node | ||
| // src/ai-docs.ts | ||
| function normalizeActiveCollections(collections) { | ||
| if (Array.isArray(collections?.active)) return collections.active; | ||
| return []; | ||
| } | ||
| var HTTP_MCP_TOOLS = [ | ||
| "get-collection-schema", | ||
| "get-tenant-context", | ||
| "list-configurable-fields", | ||
| "update-field-config", | ||
| "sdk-get-recipe", | ||
| "sdk-search-docs", | ||
| "sdk-get-auth-setup", | ||
| "sdk-get-collection-pattern" | ||
| ]; | ||
| 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 | ||
| - Publishable Key: \`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\` (env) | ||
| - Secret Key: \`SOFTWARE_SECRET_KEY\` (env) | ||
| - MCP: \`.mcp.json\` | ||
| - Agent discovery: \`https://01.software/llms.txt\` | ||
| - Agent skill: \`https://docs.01.software/skill.md\` | ||
| - OpenAPI: \`https://docs.01.software/api/openapi\` | ||
| ## Active Features | ||
| ${featuresSection} | ||
| ## Active Collections | ||
| ${collectionsSection} | ||
| ## Hosted HTTP MCP Quick Reference | ||
| The hosted OAuth MCP server exposes these 8 tools: | ||
| ${HTTP_MCP_TOOLS.map((tool) => `- \`${tool}\``).join("\n")} | ||
| Use \`get-collection-schema\` for live field introspection before generating code. | ||
| Use SDK/server code or local CLI stdio for collection reads, order workflows, and cart workflows. | ||
| ## CLI | ||
| - \`01 query <collection>\` \u2014 query data | ||
| - \`01 schema show <collection>\` \u2014 inspect fields | ||
| - \`01 schema list\` \u2014 list all collections | ||
| - \`npx @01.software/cli mcp\` \u2014 start trusted local stdio MCP for full server-key workflows | ||
| ## Initial Setup | ||
| Run \`/01software-field-config\` in Claude Code to configure field visibility for your use case. | ||
| Use \`get-collection-schema\` or \`01 schema show <collection>\` for live field introspection instead of relying on this document as a schema snapshot. | ||
| `; | ||
| } | ||
| function getSkillFiles() { | ||
| return [ | ||
| { | ||
| dirName: "01software-field-config", | ||
| content: `--- | ||
| name: 01software-field-config | ||
| description: Configure field visibility for this tenant \u2014 hide unused collections and fields via MCP | ||
| disable-model-invocation: true | ||
| --- | ||
| 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 \`articles\`, \`documents\`, \`galleries\`, \`canvas\` collections | ||
| - Minimal: hide all except the collections you actively use | ||
| Ask me: "Show current field config" or "Hide ecommerce fields" | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-query", | ||
| content: `--- | ||
| name: 01software-query | ||
| description: Query 01.software collections via SDK or CLI with filter, sort, and pagination examples | ||
| --- | ||
| Hosted HTTP MCP does not expose collection query tools. Use the SDK/server client or CLI for collection queries. | ||
| HTTP MCP: | ||
| - Use \`get-collection-schema\` before assuming fields. | ||
| - Use \`sdk-get-collection-pattern\` for collection-specific code patterns. | ||
| 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.collections.from('products').find({ | ||
| where: { status: { equals: 'published' } }, | ||
| sort: '-createdAt', | ||
| limit: 10, | ||
| }) | ||
| \`\`\` | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-order-flow", | ||
| content: `--- | ||
| name: 01software-order-flow | ||
| description: Order lifecycle reference \u2014 create, pay, fulfill, and return flows for 01.software | ||
| --- | ||
| Complete order flow from creation to fulfillment. | ||
| States: pending \u2192 paid \u2192 preparing \u2192 shipped \u2192 delivered \u2192 confirmed | ||
| 1. Create the order from server code with the SDK/server client or Order API. | ||
| 2. Mark paid only after the payment gateway confirms. | ||
| 3. Create fulfillment with items and carrier/trackingNumber from a trusted server path. | ||
| 4. Handle returns and refunds from a trusted server path. | ||
| Free orders: omit paymentId, totalAmount=0 \u2192 auto-transitions to paid | ||
| Hosted HTTP MCP is for schema, tenant context, field config, and SDK guidance. | ||
| For trusted local stdio MCP workflows, run \`npx @01.software/cli mcp\`. | ||
| CLI: \`01 order create --help\` for full options | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-schema", | ||
| content: `--- | ||
| name: 01software-schema | ||
| description: Inspect 01.software collection schemas and available fields via MCP or CLI | ||
| --- | ||
| Inspect collection schemas to understand available fields. | ||
| MCP: use \`get-collection-schema\` with collection | ||
| CLI: | ||
| - \`01 schema list\` \u2014 all available collections | ||
| - \`01 schema show <collection>\` \u2014 field names, types, required status | ||
| Common collections: products, orders, customers, articles, documents, images | ||
| Use \`get-tenant-context\` to see which collections are active for this tenant. | ||
| ` | ||
| } | ||
| ]; | ||
| } | ||
| async function fetchTenantContext(publishableKey, secretKey) { | ||
| try { | ||
| const apiUrl = process.env.SOFTWARE_API_URL || "https://api.01.software"; | ||
| const res = await fetch(`${apiUrl}/api/tenants/context`, { | ||
| headers: { | ||
| "X-Publishable-Key": publishableKey, | ||
| Authorization: `Bearer ${secretKey}` | ||
| } | ||
| }); | ||
| if (!res.ok) return null; | ||
| const data = await res.json(); | ||
| return { | ||
| tenantName: data.tenant?.name || "", | ||
| features: data.features || [], | ||
| collections: normalizeActiveCollections(data.collections) | ||
| }; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
| export { | ||
| generateClaudeMd, | ||
| getSkillFiles, | ||
| fetchTenantContext | ||
| }; | ||
| //# sourceMappingURL=chunk-5K2CB2Y5.js.map |
| {"version":3,"sources":["../src/ai-docs.ts"],"sourcesContent":["export interface TenantContext {\n tenantName: string\n features?: string[]\n collections?: string[]\n}\n\ninterface TenantContextApiResponse {\n tenant?: { name?: string }\n features?: string[]\n collections?: { active?: string[]; inactive?: string[] }\n}\n\nfunction normalizeActiveCollections(\n collections: TenantContextApiResponse['collections'],\n): string[] {\n if (Array.isArray(collections?.active)) return collections.active\n return []\n}\n\nconst HTTP_MCP_TOOLS = [\n 'get-collection-schema',\n 'get-tenant-context',\n 'list-configurable-fields',\n 'update-field-config',\n 'sdk-get-recipe',\n 'sdk-search-docs',\n 'sdk-get-auth-setup',\n 'sdk-get-collection-pattern',\n] as const\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- Publishable Key: \\`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\\` (env)\n- Secret Key: \\`SOFTWARE_SECRET_KEY\\` (env)\n- MCP: \\`.mcp.json\\`\n- Agent discovery: \\`https://01.software/llms.txt\\`\n- Agent skill: \\`https://docs.01.software/skill.md\\`\n- OpenAPI: \\`https://docs.01.software/api/openapi\\`\n\n## Active Features\n${featuresSection}\n\n## Active Collections\n${collectionsSection}\n\n## Hosted HTTP MCP Quick Reference\nThe hosted OAuth MCP server exposes these 8 tools:\n\n${HTTP_MCP_TOOLS.map((tool) => `- \\`${tool}\\``).join('\\n')}\n\nUse \\`get-collection-schema\\` for live field introspection before generating code.\nUse SDK/server code or local CLI stdio for collection reads, order workflows, and cart workflows.\n\n## CLI\n- \\`01 query <collection>\\` — query data\n- \\`01 schema show <collection>\\` — inspect fields\n- \\`01 schema list\\` — list all collections\n- \\`npx @01.software/cli mcp\\` — start trusted local stdio MCP for full server-key workflows\n\n## Initial Setup\nRun \\`/01software-field-config\\` in Claude Code to configure field visibility for your use case.\nUse \\`get-collection-schema\\` or \\`01 schema show <collection>\\` for live field introspection instead of relying on this document as a schema snapshot.\n`\n}\n\n// ── Skill files ──────────────────────────────────────────────────────\n\nexport function getSkillFiles(): Array<{ dirName: string; content: string }> {\n return [\n {\n dirName: '01software-field-config',\n content: `---\nname: 01software-field-config\ndescription: Configure field visibility for this tenant — hide unused collections and fields via MCP\ndisable-model-invocation: true\n---\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 \\`articles\\`, \\`documents\\`, \\`galleries\\`, \\`canvas\\` 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 dirName: '01software-query',\n content: `---\nname: 01software-query\ndescription: Query 01.software collections via SDK or CLI with filter, sort, and pagination examples\n---\n\nHosted HTTP MCP does not expose collection query tools. Use the SDK/server client or CLI for collection queries.\n\nHTTP MCP:\n- Use \\`get-collection-schema\\` before assuming fields.\n- Use \\`sdk-get-collection-pattern\\` for collection-specific code patterns.\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.collections.from('products').find({\n where: { status: { equals: 'published' } },\n sort: '-createdAt',\n limit: 10,\n})\n\\`\\`\\`\n`,\n },\n {\n dirName: '01software-order-flow',\n content: `---\nname: 01software-order-flow\ndescription: Order lifecycle reference — create, pay, fulfill, and return flows for 01.software\n---\n\nComplete order flow from creation to fulfillment.\n\nStates: pending → paid → preparing → shipped → delivered → confirmed\n\n1. Create the order from server code with the SDK/server client or Order API.\n2. Mark paid only after the payment gateway confirms.\n3. Create fulfillment with items and carrier/trackingNumber from a trusted server path.\n4. Handle returns and refunds from a trusted server path.\n\nFree orders: omit paymentId, totalAmount=0 → auto-transitions to paid\n\nHosted HTTP MCP is for schema, tenant context, field config, and SDK guidance.\nFor trusted local stdio MCP workflows, run \\`npx @01.software/cli mcp\\`.\nCLI: \\`01 order create --help\\` for full options\n`,\n },\n {\n dirName: '01software-schema',\n content: `---\nname: 01software-schema\ndescription: Inspect 01.software collection schemas and available fields via MCP or CLI\n---\n\nInspect collection schemas to understand available fields.\n\nMCP: use \\`get-collection-schema\\` with collection\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, articles, 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 publishableKey: string,\n secretKey: string,\n): Promise<TenantContext | null> {\n try {\n const apiUrl = process.env.SOFTWARE_API_URL || 'https://api.01.software'\n // secretKey is now an opaque sk01_/pat01_ bearer token — send it directly.\n const res = await fetch(`${apiUrl}/api/tenants/context`, {\n headers: {\n 'X-Publishable-Key': publishableKey,\n Authorization: `Bearer ${secretKey}`,\n },\n })\n if (!res.ok) return null\n const data = (await res.json()) as TenantContextApiResponse\n return {\n tenantName: data.tenant?.name || '',\n features: data.features || [],\n collections: normalizeActiveCollections(data.collections),\n }\n } catch {\n return null\n }\n}\n"],"mappings":";;;AAYA,SAAS,2BACP,aACU;AACV,MAAI,MAAM,QAAQ,aAAa,MAAM,EAAG,QAAO,YAAY;AAC3D,SAAO,CAAC;AACV;AAEA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,SAAS,iBAAiB,KAA4B;AAC3D,QAAM,kBACJ,IAAI,YAAY,IAAI,SAAS,SAAS,IAClC,IAAI,SAAS,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,IAC3C;AAEN,QAAM,qBACJ,IAAI,eAAe,IAAI,YAAY,SAAS,IACxC,IAAI,YAAY,KAAK,IAAI,IACzB;AAEN,SAAO,4BAAuB,IAAI,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW5C,eAAe;AAAA;AAAA;AAAA,EAGf,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKlB,eAAe,IAAI,CAAC,SAAS,OAAO,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAe1D;AAIO,SAAS,gBAA6D;AAC3E,SAAO;AAAA,IACL;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAyBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAoBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBX;AAAA,EACF;AACF;AAIA,eAAsB,mBACpB,gBACA,WAC+B;AAC/B,MAAI;AACF,UAAM,SAAS,QAAQ,IAAI,oBAAoB;AAE/C,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,wBAAwB;AAAA,MACvD,SAAS;AAAA,QACP,qBAAqB;AAAA,QACrB,eAAe,UAAU,SAAS;AAAA,MACpC;AAAA,IACF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO;AAAA,MACL,YAAY,KAAK,QAAQ,QAAQ;AAAA,MACjC,UAAU,KAAK,YAAY,CAAC;AAAA,MAC5B,aAAa,2BAA2B,KAAK,WAAW;AAAA,IAC1D;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]} |
+1
-1
@@ -6,3 +6,3 @@ #!/usr/bin/env node | ||
| getSkillFiles | ||
| } from "./chunk-3MXIG3ZL.js"; | ||
| } from "./chunk-5K2CB2Y5.js"; | ||
| export { | ||
@@ -9,0 +9,0 @@ fetchTenantContext, |
+9
-3
@@ -5,4 +5,4 @@ #!/usr/bin/env node | ||
| init | ||
| } from "./chunk-3RQE6YVO.js"; | ||
| import "./chunk-3MXIG3ZL.js"; | ||
| } from "./chunk-2IGKOSK7.js"; | ||
| import "./chunk-5K2CB2Y5.js"; | ||
| import "./chunk-UA7WNT2F.js"; | ||
@@ -203,3 +203,3 @@ import "./chunk-TBGKXE3Q.js"; | ||
| 4. Docs: https://01.software/docs/developers/sdk/client | ||
| 4. Docs: https://docs.01.software/docs/developers/sdk/client | ||
| `; | ||
@@ -311,2 +311,8 @@ async function main() { | ||
| console.log(pc.dim(" MCP config uses OAuth discovery.")); | ||
| console.log(pc.dim(" Agent discovery flow:")); | ||
| console.log(pc.cyan(" 1. Read https://01.software/llms.txt")); | ||
| console.log(pc.cyan(" 2. Connect https://mcp.01.software/mcp")); | ||
| console.log(pc.cyan(" 3. Read https://docs.01.software/skill.md")); | ||
| console.log(pc.cyan(" 4. Inspect https://docs.01.software/api/openapi")); | ||
| console.log(pc.cyan(" 5. Run local lint, typecheck, tests, and build")); | ||
| console.log(); | ||
@@ -313,0 +319,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/prompts.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({ publishableKey: 'YOUR_PUBLISHABLE_KEY' })\n\n 3. Server client (SSR / loaders / endpoints):\n import { createServerClient } from '@01.software/sdk'\n export const serverClient = createServerClient({\n publishableKey: process.env.PUBLIC_SOFTWARE_PUBLISHABLE_KEY!, // prefix varies by framework\n secretKey: process.env.SOFTWARE_SECRET_KEY!,\n })\n\n 4. Docs: https://01.software/docs/developers/sdk/client\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 || info.parseError) {\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 const result = 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 (result.installFailed) {\n console.log(pc.yellow(' Install the SDK manually:'))\n console.log(pc.cyan(` ${result.installCmd}`))\n console.log()\n }\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 console.log(pc.dim(' Optional: start browser analytics with the generated helper:'))\n console.log()\n console.log(pc.cyan(\" import { analytics } from '@/lib/software/analytics'\"))\n console.log(pc.cyan(\" analytics.track('signup')\"))\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 console.log(pc.dim(' Optional: start browser analytics with the generated helper:'))\n console.log()\n console.log(pc.cyan(\" import { analytics } from './lib/software/analytics'\"))\n console.log(pc.cyan(\" analytics.track('signup')\"))\n console.log()\n } else if (env === 'vanilla') {\n console.log(pc.dim(' Replace YOUR_PUBLISHABLE_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 articles = await client.collections.from('articles').find()\"))\n console.log()\n console.log(pc.dim(' Optional: wire the analytics helper in lib/software/analytics.ts'))\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 articles = await serverClient.collections.from('articles').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.PUBLISHABLE_KEY, env.SECRET_KEY)'))\n console.log()\n }\n\n // Prefer keys returned by `init()` (e.g. from browser-auth) over the\n // original prompt answers. Browser-auth credentials are written directly\n // and must not leave the user looking at stale \"Update .env\" instructions.\n const effectivePublishableKey = result.publishableKey ?? answers.publishableKey\n const effectiveSecretKey = result.secretKey ?? answers.secretKey\n const missingPublishableKey = env !== 'vanilla' && !effectivePublishableKey\n const missingSecretKey = (env === 'nextjs' || env === 'node') && !effectiveSecretKey\n if (missingPublishableKey || missingSecretKey) {\n console.log(pc.dim(' Update .env with your SDK credentials'))\n console.log()\n }\n if (answers.aiTools.length > 0) {\n console.log(pc.dim(' MCP config uses OAuth discovery.'))\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 prompts from 'prompts'\nimport type { PackageManager, ProjectEnv } from './detect'\n\nexport type AiTool =\n | 'claude'\n | 'cursor'\n | 'vscode'\n | 'windsurf'\n | 'codex'\n | 'gemini'\n\nexport type AuthMethod = 'browser' | 'manual' | 'skip'\n\nexport interface InitAnswers {\n env: ProjectEnv\n publishableKey: 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, publishableKey: '', 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: 'publishableKey',\n message: 'Publishable 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 (empty selection = skip)\n const { selectedTools } = await prompts(\n {\n type: 'multiselect',\n name: 'selectedTools',\n message: 'Connect AI tools (leave empty to skip):',\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: 'Codex CLI', description: '~/.codex/config.toml', value: 'codex' },\n { title: 'Gemini CLI', description: '~/.gemini/settings.json', value: 'gemini' },\n ],\n hint: 'space to select, enter to confirm',\n },\n { onCancel },\n )\n\n const aiTools: AiTool[] = Array.isArray(selectedTools) ? (selectedTools as AiTool[]) : []\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: 'SDK credentials:',\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 publishableKey: authMethod === 'browser' ? '' : (keys.publishableKey ?? ''),\n secretKey: authMethod === 'browser' ? '' : (keys.secretKey ?? ''),\n aiTools,\n authMethod,\n packageManager,\n }\n}\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/index.ts","../src/prompts.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({ publishableKey: 'YOUR_PUBLISHABLE_KEY' })\n\n 3. Server client (SSR / loaders / endpoints):\n import { createServerClient } from '@01.software/sdk'\n export const serverClient = createServerClient({\n publishableKey: process.env.PUBLIC_SOFTWARE_PUBLISHABLE_KEY!, // prefix varies by framework\n secretKey: process.env.SOFTWARE_SECRET_KEY!,\n })\n\n 4. Docs: https://docs.01.software/docs/developers/sdk/client\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 || info.parseError) {\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 const result = 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 (result.installFailed) {\n console.log(pc.yellow(' Install the SDK manually:'))\n console.log(pc.cyan(` ${result.installCmd}`))\n console.log()\n }\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 console.log(pc.dim(' Optional: start browser analytics with the generated helper:'))\n console.log()\n console.log(pc.cyan(\" import { analytics } from '@/lib/software/analytics'\"))\n console.log(pc.cyan(\" analytics.track('signup')\"))\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 console.log(pc.dim(' Optional: start browser analytics with the generated helper:'))\n console.log()\n console.log(pc.cyan(\" import { analytics } from './lib/software/analytics'\"))\n console.log(pc.cyan(\" analytics.track('signup')\"))\n console.log()\n } else if (env === 'vanilla') {\n console.log(pc.dim(' Replace YOUR_PUBLISHABLE_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 articles = await client.collections.from('articles').find()\"))\n console.log()\n console.log(pc.dim(' Optional: wire the analytics helper in lib/software/analytics.ts'))\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 articles = await serverClient.collections.from('articles').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.PUBLISHABLE_KEY, env.SECRET_KEY)'))\n console.log()\n }\n\n // Prefer keys returned by `init()` (e.g. from browser-auth) over the\n // original prompt answers. Browser-auth credentials are written directly\n // and must not leave the user looking at stale \"Update .env\" instructions.\n const effectivePublishableKey = result.publishableKey ?? answers.publishableKey\n const effectiveSecretKey = result.secretKey ?? answers.secretKey\n const missingPublishableKey = env !== 'vanilla' && !effectivePublishableKey\n const missingSecretKey = (env === 'nextjs' || env === 'node') && !effectiveSecretKey\n if (missingPublishableKey || missingSecretKey) {\n console.log(pc.dim(' Update .env with your SDK credentials'))\n console.log()\n }\n if (answers.aiTools.length > 0) {\n console.log(pc.dim(' MCP config uses OAuth discovery.'))\n console.log(pc.dim(' Agent discovery flow:'))\n console.log(pc.cyan(' 1. Read https://01.software/llms.txt'))\n console.log(pc.cyan(' 2. Connect https://mcp.01.software/mcp'))\n console.log(pc.cyan(' 3. Read https://docs.01.software/skill.md'))\n console.log(pc.cyan(' 4. Inspect https://docs.01.software/api/openapi'))\n console.log(pc.cyan(' 5. Run local lint, typecheck, tests, and build'))\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 prompts from 'prompts'\nimport type { PackageManager, ProjectEnv } from './detect'\n\nexport type AiTool =\n | 'claude'\n | 'cursor'\n | 'vscode'\n | 'windsurf'\n | 'codex'\n | 'gemini'\n\nexport type AuthMethod = 'browser' | 'manual' | 'skip'\n\nexport interface InitAnswers {\n env: ProjectEnv\n publishableKey: 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, publishableKey: '', 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: 'publishableKey',\n message: 'Publishable 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 (empty selection = skip)\n const { selectedTools } = await prompts(\n {\n type: 'multiselect',\n name: 'selectedTools',\n message: 'Connect AI tools (leave empty to skip):',\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: 'Codex CLI', description: '~/.codex/config.toml', value: 'codex' },\n { title: 'Gemini CLI', description: '~/.gemini/settings.json', value: 'gemini' },\n ],\n hint: 'space to select, enter to confirm',\n },\n { onCancel },\n )\n\n const aiTools: AiTool[] = Array.isArray(selectedTools) ? (selectedTools as AiTool[]) : []\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: 'SDK credentials:',\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 publishableKey: authMethod === 'browser' ? '' : (keys.publishableKey ?? ''),\n secretKey: authMethod === 'browser' ? '' : (keys.secretKey ?? ''),\n aiTools,\n authMethod,\n packageManager,\n }\n}\n"],"mappings":"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","names":[]} |
+2
-2
| #!/usr/bin/env node | ||
| import { | ||
| init | ||
| } from "./chunk-3RQE6YVO.js"; | ||
| import "./chunk-3MXIG3ZL.js"; | ||
| } from "./chunk-2IGKOSK7.js"; | ||
| import "./chunk-5K2CB2Y5.js"; | ||
| import "./chunk-UA7WNT2F.js"; | ||
@@ -7,0 +7,0 @@ import "./chunk-TBGKXE3Q.js"; |
+2
-2
| { | ||
| "name": "@01.software/init", | ||
| "version": "0.8.0", | ||
| "version": "0.9.0", | ||
| "description": "Initialize 01.software SDK in your project (Next.js, React, Vanilla JS, Node.js, Edge)", | ||
@@ -24,3 +24,3 @@ "type": "module", | ||
| "typescript": "^5.9.3", | ||
| "@01.software/sdk": "0.20.0" | ||
| "@01.software/sdk": "0.26.0" | ||
| }, | ||
@@ -27,0 +27,0 @@ "author": "<office@01.works>", |
| #!/usr/bin/env node | ||
| // src/ai-docs.ts | ||
| function normalizeActiveCollections(collections) { | ||
| if (Array.isArray(collections?.active)) return collections.active; | ||
| return []; | ||
| } | ||
| 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 | ||
| - Publishable Key: \`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\` (env) | ||
| - Secret Key: \`SOFTWARE_SECRET_KEY\` (env) | ||
| - MCP: \`.mcp.json\` | ||
| ## Active Features | ||
| ${featuresSection} | ||
| ## Active Collections | ||
| ${collectionsSection} | ||
| ## MCP Quick Reference | ||
| | Tool | Use | | ||
| |------|-----| | ||
| | \`query-collection\` | List/filter documents | | ||
| | \`get-collection-schema\` | Inspect tenant-aware fields for a collection | | ||
| | \`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 \`/01software-field-config\` in Claude Code to configure field visibility for your use case. | ||
| Use \`get-collection-schema\` or \`01 schema show <collection>\` for live field introspection instead of relying on this document as a schema snapshot. | ||
| `; | ||
| } | ||
| function getSkillFiles() { | ||
| return [ | ||
| { | ||
| dirName: "01software-field-config", | ||
| content: `--- | ||
| name: 01software-field-config | ||
| description: Configure field visibility for this tenant \u2014 hide unused collections and fields via MCP | ||
| disable-model-invocation: true | ||
| --- | ||
| 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 \`articles\`, \`documents\`, \`galleries\`, \`canvas\` collections | ||
| - Minimal: hide all except the collections you actively use | ||
| Ask me: "Show current field config" or "Hide ecommerce fields" | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-query", | ||
| content: `--- | ||
| name: 01software-query | ||
| description: Query 01.software collections via MCP or CLI with filter, sort, and pagination examples | ||
| --- | ||
| 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.collections.from('products').find({ | ||
| where: { status: { equals: 'published' } }, | ||
| sort: '-createdAt', | ||
| limit: 10, | ||
| }) | ||
| \`\`\` | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-order-flow", | ||
| content: `--- | ||
| name: 01software-order-flow | ||
| description: Order lifecycle reference \u2014 create, pay, fulfill, and return flows for 01.software | ||
| --- | ||
| 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 | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-schema", | ||
| content: `--- | ||
| name: 01software-schema | ||
| description: Inspect 01.software collection schemas and available fields via MCP or CLI | ||
| --- | ||
| Inspect collection schemas to understand available fields. | ||
| MCP: use \`get-collection-schema\` with collection | ||
| CLI: | ||
| - \`01 schema list\` \u2014 all available collections | ||
| - \`01 schema show <collection>\` \u2014 field names, types, required status | ||
| Common collections: products, orders, customers, articles, documents, images | ||
| Use \`get-tenant-context\` to see which collections are active for this tenant. | ||
| ` | ||
| } | ||
| ]; | ||
| } | ||
| async function fetchTenantContext(publishableKey, secretKey) { | ||
| try { | ||
| const apiUrl = process.env.SOFTWARE_API_URL || "https://api.01.software"; | ||
| const res = await fetch(`${apiUrl}/api/tenants/context`, { | ||
| headers: { | ||
| "X-Publishable-Key": publishableKey, | ||
| Authorization: `Bearer ${secretKey}` | ||
| } | ||
| }); | ||
| if (!res.ok) return null; | ||
| const data = await res.json(); | ||
| return { | ||
| tenantName: data.tenant?.name || "", | ||
| features: data.features || [], | ||
| collections: normalizeActiveCollections(data.collections) | ||
| }; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
| export { | ||
| generateClaudeMd, | ||
| getSkillFiles, | ||
| fetchTenantContext | ||
| }; | ||
| //# sourceMappingURL=chunk-3MXIG3ZL.js.map |
| {"version":3,"sources":["../src/ai-docs.ts"],"sourcesContent":["export interface TenantContext {\n tenantName: string\n features?: string[]\n collections?: string[]\n}\n\ninterface TenantContextApiResponse {\n tenant?: { name?: string }\n features?: string[]\n collections?: { active?: string[]; inactive?: string[] }\n}\n\nfunction normalizeActiveCollections(\n collections: TenantContextApiResponse['collections'],\n): string[] {\n if (Array.isArray(collections?.active)) return collections.active\n return []\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- Publishable Key: \\`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\\` (env)\n- Secret Key: \\`SOFTWARE_SECRET_KEY\\` (env)\n- MCP: \\`.mcp.json\\`\n\n## Active Features\n${featuresSection}\n\n## Active Collections\n${collectionsSection}\n\n## MCP Quick Reference\n| Tool | Use |\n|------|-----|\n| \\`query-collection\\` | List/filter documents |\n| \\`get-collection-schema\\` | Inspect tenant-aware fields for a collection |\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 \\`/01software-field-config\\` in Claude Code to configure field visibility for your use case.\nUse \\`get-collection-schema\\` or \\`01 schema show <collection>\\` for live field introspection instead of relying on this document as a schema snapshot.\n`\n}\n\n// ── Skill files ──────────────────────────────────────────────────────\n\nexport function getSkillFiles(): Array<{ dirName: string; content: string }> {\n return [\n {\n dirName: '01software-field-config',\n content: `---\nname: 01software-field-config\ndescription: Configure field visibility for this tenant — hide unused collections and fields via MCP\ndisable-model-invocation: true\n---\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 \\`articles\\`, \\`documents\\`, \\`galleries\\`, \\`canvas\\` 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 dirName: '01software-query',\n content: `---\nname: 01software-query\ndescription: Query 01.software collections via MCP or CLI with filter, sort, and pagination examples\n---\n\nQuery 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.collections.from('products').find({\n where: { status: { equals: 'published' } },\n sort: '-createdAt',\n limit: 10,\n})\n\\`\\`\\`\n`,\n },\n {\n dirName: '01software-order-flow',\n content: `---\nname: 01software-order-flow\ndescription: Order lifecycle reference — create, pay, fulfill, and return flows for 01.software\n---\n\nComplete 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 dirName: '01software-schema',\n content: `---\nname: 01software-schema\ndescription: Inspect 01.software collection schemas and available fields via MCP or CLI\n---\n\nInspect collection schemas to understand available fields.\n\nMCP: use \\`get-collection-schema\\` with collection\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, articles, 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 publishableKey: string,\n secretKey: string,\n): Promise<TenantContext | null> {\n try {\n const apiUrl = process.env.SOFTWARE_API_URL || 'https://api.01.software'\n // secretKey is now an opaque sk01_/pat01_ bearer token — send it directly.\n const res = await fetch(`${apiUrl}/api/tenants/context`, {\n headers: {\n 'X-Publishable-Key': publishableKey,\n Authorization: `Bearer ${secretKey}`,\n },\n })\n if (!res.ok) return null\n const data = (await res.json()) as TenantContextApiResponse\n return {\n tenantName: data.tenant?.name || '',\n features: data.features || [],\n collections: normalizeActiveCollections(data.collections),\n }\n } catch {\n return null\n }\n}\n"],"mappings":";;;AAYA,SAAS,2BACP,aACU;AACV,MAAI,MAAM,QAAQ,aAAa,MAAM,EAAG,QAAO,YAAY;AAC3D,SAAO,CAAC;AACV;AAIO,SAAS,iBAAiB,KAA4B;AAC3D,QAAM,kBACJ,IAAI,YAAY,IAAI,SAAS,SAAS,IAClC,IAAI,SAAS,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,IAC3C;AAEN,QAAM,qBACJ,IAAI,eAAe,IAAI,YAAY,SAAS,IACxC,IAAI,YAAY,KAAK,IAAI,IACzB;AAEN,SAAO,4BAAuB,IAAI,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5C,eAAe;AAAA;AAAA;AAAA,EAGf,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBpB;AAIO,SAAS,gBAA6D;AAC3E,SAAO;AAAA,IACL;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2BX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBX;AAAA,EACF;AACF;AAIA,eAAsB,mBACpB,gBACA,WAC+B;AAC/B,MAAI;AACF,UAAM,SAAS,QAAQ,IAAI,oBAAoB;AAE/C,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,wBAAwB;AAAA,MACvD,SAAS;AAAA,QACP,qBAAqB;AAAA,QACrB,eAAe,UAAU,SAAS;AAAA,MACpC;AAAA,IACF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO;AAAA,MACL,YAAY,KAAK,QAAQ,QAAQ;AAAA,MACjC,UAAU,KAAK,YAAY,CAAC;AAAA,MAC5B,aAAa,2BAA2B,KAAK,WAAW;AAAA,IAC1D;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]} |
| #!/usr/bin/env node | ||
| import { | ||
| fetchTenantContext, | ||
| generateClaudeMd, | ||
| getSkillFiles | ||
| } from "./chunk-3MXIG3ZL.js"; | ||
| import { | ||
| chmodSecretFile, | ||
| readEnvValue, | ||
| replaceTomlMcpSection, | ||
| setEnvValue, | ||
| writeEnvFile, | ||
| writeSecretGlobalConfig | ||
| } from "./chunk-UA7WNT2F.js"; | ||
| import { | ||
| CODEX_MCP_SECTION_MARKER, | ||
| getAnalyticsTemplate, | ||
| getClientTemplate, | ||
| getCodexMcpTomlSection, | ||
| getEnvContent, | ||
| getMcpConfigTemplate, | ||
| getMcpRootKey, | ||
| getMcpServerEntry, | ||
| getQueryProviderTemplate, | ||
| getServerTemplate | ||
| } from "./chunk-TBGKXE3Q.js"; | ||
| // src/init.ts | ||
| import fs2 from "fs"; | ||
| import path2 from "path"; | ||
| import os from "os"; | ||
| import { execSync } from "child_process"; | ||
| import pc2 from "picocolors"; | ||
| import prompts from "prompts"; | ||
| // src/detect.ts | ||
| import fs from "fs"; | ||
| import path from "path"; | ||
| function detectProject(cwd) { | ||
| const pkgPath = path.join(cwd, "package.json"); | ||
| const hasPackageJson = fs.existsSync(pkgPath); | ||
| let env = "node"; | ||
| let hasSdk = false; | ||
| let hasReactQuery = false; | ||
| let parseError = false; | ||
| if (hasPackageJson) { | ||
| let pkg; | ||
| try { | ||
| pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")); | ||
| } catch { | ||
| return { | ||
| hasPackageJson: true, | ||
| parseError: true, | ||
| env: "node", | ||
| packageManager: null, | ||
| hasSdk: false, | ||
| hasReactQuery: false, | ||
| srcDir: false | ||
| }; | ||
| } | ||
| const deps = { | ||
| ...pkg.dependencies || {}, | ||
| ...pkg.devDependencies || {} | ||
| }; | ||
| hasSdk = "@01.software/sdk" in deps; | ||
| hasReactQuery = "@tanstack/react-query" in deps; | ||
| if ("next" in deps) { | ||
| env = "nextjs"; | ||
| } else if ("astro" in deps || "@astrojs/node" in deps) { | ||
| env = "other"; | ||
| } else if ("@remix-run/node" in deps || "@remix-run/react" in deps) { | ||
| env = "other"; | ||
| } else if ("@sveltejs/kit" in deps) { | ||
| env = "other"; | ||
| } else if ("react" in deps) { | ||
| if ("vite" in deps) { | ||
| env = "react-vite"; | ||
| } else if ("react-scripts" in deps) { | ||
| env = "react-cra"; | ||
| } else { | ||
| env = "node"; | ||
| } | ||
| } | ||
| } | ||
| let packageManager = null; | ||
| if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) { | ||
| packageManager = "pnpm"; | ||
| } else if (fs.existsSync(path.join(cwd, "yarn.lock"))) { | ||
| packageManager = "yarn"; | ||
| } else if (fs.existsSync(path.join(cwd, "bun.lockb")) || fs.existsSync(path.join(cwd, "bun.lock"))) { | ||
| packageManager = "bun"; | ||
| } else if (fs.existsSync(path.join(cwd, "package-lock.json"))) { | ||
| packageManager = "npm"; | ||
| } | ||
| const srcDir = env === "nextjs" ? fs.existsSync(path.join(cwd, "src", "app")) : fs.existsSync(path.join(cwd, "src")); | ||
| return { hasPackageJson, parseError, env, packageManager, hasSdk, hasReactQuery, srcDir }; | ||
| } | ||
| function needsClient(env) { | ||
| return env === "nextjs" || env === "react-vite" || env === "react-cra" || env === "vanilla"; | ||
| } | ||
| function needsServer(env) { | ||
| return env === "nextjs" || env === "node" || env === "edge"; | ||
| } | ||
| function needsReactQuery(env) { | ||
| return env === "nextjs" || env === "react-vite" || env === "react-cra"; | ||
| } | ||
| function getPublishableKeyEnvVar(env) { | ||
| switch (env) { | ||
| case "nextjs": | ||
| return "NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY"; | ||
| case "react-vite": | ||
| return "VITE_SOFTWARE_PUBLISHABLE_KEY"; | ||
| case "react-cra": | ||
| return "REACT_APP_SOFTWARE_PUBLISHABLE_KEY"; | ||
| default: | ||
| return "SOFTWARE_PUBLISHABLE_KEY"; | ||
| } | ||
| } | ||
| // 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 os2 = platform(); | ||
| const onError = () => { | ||
| console.log( | ||
| pc.yellow( | ||
| `Could not open browser automatically. Open this URL manually: | ||
| ${url}` | ||
| ) | ||
| ); | ||
| }; | ||
| if (os2 === "win32") { | ||
| exec(`start "" "${url}"`, (err) => { | ||
| if (err) onError(); | ||
| }); | ||
| } else { | ||
| const cmd = os2 === "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 exchangeCode(webUrl, code) { | ||
| const url = `${webUrl}/api/cli/exchange`; | ||
| try { | ||
| const res = await fetch(url, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ code }) | ||
| }); | ||
| if (!res.ok) { | ||
| const body = await res.text().catch(() => ""); | ||
| console.error( | ||
| pc.red( | ||
| `Exchange failed: HTTP ${res.status} from ${url}${body ? ` \u2014 ${body.slice(0, 200)}` : ""}` | ||
| ) | ||
| ); | ||
| return null; | ||
| } | ||
| const data = await res.json(); | ||
| if (typeof data.publishableKey !== "string" || typeof data.secretKey !== "string" || typeof data.tenantName !== "string" || typeof data.tenantId !== "string") { | ||
| console.error(pc.red(`Exchange failed: malformed response from ${url}`)); | ||
| return null; | ||
| } | ||
| return { | ||
| publishableKey: data.publishableKey, | ||
| secretKey: data.secretKey, | ||
| tenantName: data.tenantName, | ||
| tenantId: data.tenantId | ||
| }; | ||
| } catch (err) { | ||
| console.error( | ||
| pc.red( | ||
| `Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}` | ||
| ) | ||
| ); | ||
| return null; | ||
| } | ||
| } | ||
| 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" || req.method !== "GET") { | ||
| res.writeHead(404).end(); | ||
| return; | ||
| } | ||
| const error = url.searchParams.get("error"); | ||
| if (error) { | ||
| res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML(error)); | ||
| console.error(pc.red(`Login failed: ${error}`)); | ||
| cleanup(new Error(`Login failed: ${error}`)); | ||
| return; | ||
| } | ||
| const code = url.searchParams.get("code"); | ||
| const receivedState = url.searchParams.get("state"); | ||
| if (!code || !receivedState) { | ||
| res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Missing code or state.")); | ||
| cleanup(new Error("Login failed: missing code or state.")); | ||
| return; | ||
| } | ||
| if (receivedState !== state) { | ||
| res.writeHead(403, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("State mismatch.")); | ||
| console.error(pc.red("Login failed: state mismatch.")); | ||
| cleanup(new Error("Login failed: state mismatch.")); | ||
| return; | ||
| } | ||
| exchangeCode(webUrl, code).then((creds) => { | ||
| if (!creds) { | ||
| res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Invalid or expired code.")); | ||
| cleanup(new Error("Login failed: code exchange failed.")); | ||
| return; | ||
| } | ||
| res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(SUCCESS_HTML); | ||
| console.log(pc.green(` | ||
| Logged in successfully!`)); | ||
| console.log(pc.dim(`Tenant: ${creds.tenantName}`)); | ||
| cleanup(null, creds); | ||
| }); | ||
| }); | ||
| let timeout; | ||
| let completed = false; | ||
| function cleanup(err, result) { | ||
| if (completed) return; | ||
| completed = true; | ||
| clearTimeout(timeout); | ||
| server.closeAllConnections?.(); | ||
| 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/init.ts | ||
| var SECRET_KEY_ENV_VAR = "SOFTWARE_SECRET_KEY"; | ||
| async function init(cwd, info, answers, deps = {}) { | ||
| const { packageManager, srcDir } = info; | ||
| const env = answers.env; | ||
| const baseDir = srcDir ? path2.join(cwd, "src") : cwd; | ||
| const browserAuth = deps.startBrowserAuth ?? startBrowserAuth; | ||
| const publishableKeyEnvVar = getPublishableKeyEnvVar(env); | ||
| const wantsClient = needsClient(env); | ||
| const wantsServer = needsServer(env); | ||
| const wantsReactQuery = needsReactQuery(env); | ||
| const plan = await planConflictsAndEnv(cwd, baseDir, env, answers); | ||
| const installResult = deps.skipInstall ? { | ||
| installFailed: false, | ||
| installSkipped: true, | ||
| installCmd: buildAddCmd(packageManager, hasPnpmWorkspace(cwd), [ | ||
| "@01.software/sdk" | ||
| ]) | ||
| } : installDeps( | ||
| cwd, | ||
| packageManager, | ||
| info.hasSdk, | ||
| info.hasReactQuery, | ||
| wantsReactQuery | ||
| ); | ||
| if (wantsClient || wantsReactQuery || wantsServer) { | ||
| fs2.mkdirSync(path2.join(baseDir, "lib", "software"), { recursive: true }); | ||
| } | ||
| const libDir = path2.join(baseDir, "lib", "software"); | ||
| if (wantsClient) { | ||
| await writeFileWithPolicy( | ||
| cwd, | ||
| path2.join(libDir, "client.ts"), | ||
| getClientTemplate(env, publishableKeyEnvVar), | ||
| plan.policy | ||
| ); | ||
| await writeFileWithPolicy( | ||
| cwd, | ||
| path2.join(libDir, "analytics.ts"), | ||
| getAnalyticsTemplate(env, publishableKeyEnvVar), | ||
| plan.policy | ||
| ); | ||
| } | ||
| if (wantsReactQuery) { | ||
| await writeFileWithPolicy( | ||
| cwd, | ||
| path2.join(libDir, "query-provider.tsx"), | ||
| getQueryProviderTemplate(env), | ||
| plan.policy | ||
| ); | ||
| } | ||
| if (wantsServer) { | ||
| await writeFileWithPolicy( | ||
| cwd, | ||
| path2.join(libDir, "server.ts"), | ||
| getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR), | ||
| plan.policy | ||
| ); | ||
| } | ||
| if (plan.envFile && answers.authMethod !== "browser") { | ||
| await writeEnv( | ||
| cwd, | ||
| plan.envFile, | ||
| answers.publishableKey || "", | ||
| answers.secretKey || "", | ||
| publishableKeyEnvVar, | ||
| wantsServer ? SECRET_KEY_ENV_VAR : null, | ||
| plan.policy | ||
| ); | ||
| } | ||
| let publishableKey = answers.publishableKey; | ||
| let secretKey = answers.secretKey; | ||
| let tenantName = ""; | ||
| if (answers.authMethod === "browser" && answers.aiTools.length > 0) { | ||
| try { | ||
| console.log(); | ||
| const creds = await browserAuth(); | ||
| publishableKey = creds.publishableKey; | ||
| secretKey = creds.secretKey; | ||
| tenantName = creds.tenantName; | ||
| if (plan.envFile && publishableKey) { | ||
| await writeEnv( | ||
| cwd, | ||
| plan.envFile, | ||
| publishableKey, | ||
| secretKey, | ||
| publishableKeyEnvVar, | ||
| wantsServer ? SECRET_KEY_ENV_VAR : null, | ||
| "overwrite", | ||
| true | ||
| ); | ||
| } | ||
| } catch (err) { | ||
| console.log( | ||
| pc2.yellow(" Browser auth skipped:"), | ||
| err instanceof Error ? err.message : String(err) | ||
| ); | ||
| } | ||
| } | ||
| if (answers.aiTools.length > 0) { | ||
| for (const tool of answers.aiTools) { | ||
| await writeMcpConfig(tool, cwd); | ||
| } | ||
| addToGitignore(cwd, answers.aiTools); | ||
| if (answers.aiTools.includes("claude")) { | ||
| await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy); | ||
| } | ||
| } | ||
| return { | ||
| ...installResult, | ||
| publishableKey: publishableKey || void 0, | ||
| secretKey: secretKey || void 0, | ||
| tenantName: tenantName || void 0 | ||
| }; | ||
| } | ||
| function installDeps(cwd, pm, hasSdk, hasReactQuery, wantsReactQuery) { | ||
| const allDeps = [ | ||
| { name: "@01.software/sdk", installed: hasSdk, needed: true }, | ||
| { name: "@tanstack/react-query", installed: hasReactQuery, needed: wantsReactQuery } | ||
| ]; | ||
| const fullList = allDeps.filter((d) => d.needed).map((d) => d.name); | ||
| const toInstall = allDeps.filter((d) => d.needed && !d.installed).map((d) => d.name); | ||
| const fullCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), fullList); | ||
| if (toInstall.length === 0) { | ||
| console.log(pc2.dim(` Dependencies already installed: ${fullList.join(", ")}`)); | ||
| return { installFailed: false, installSkipped: true, installCmd: fullCmd }; | ||
| } | ||
| const addCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), toInstall); | ||
| console.log(pc2.dim(` Installing ${toInstall.join(" and ")}...`)); | ||
| const wsPatched = pm === "pnpm" && patchPnpmWorkspace(cwd); | ||
| let installFailed = false; | ||
| try { | ||
| execSync(addCmd, { cwd, stdio: "pipe" }); | ||
| console.log(pc2.green(" Installed"), toInstall.join(", ")); | ||
| } catch (error) { | ||
| installFailed = true; | ||
| const err = error; | ||
| const msg = String(err.stderr || "").trim() || String(err.stdout || "").trim() || String(error); | ||
| console.log(pc2.yellow(" Install failed \u2014 continuing with scaffolding")); | ||
| const firstLines = msg.split("\n").slice(0, 3).map((l) => ` ${l}`).join("\n"); | ||
| if (firstLines) console.log(pc2.dim(firstLines)); | ||
| console.log(pc2.dim(` Run manually: ${addCmd}`)); | ||
| } finally { | ||
| if (wsPatched) restorePnpmWorkspace(cwd); | ||
| } | ||
| return { installFailed, installSkipped: false, installCmd: addCmd }; | ||
| } | ||
| function buildAddCmd(pm, hasPnpmWs, deps) { | ||
| const pkgs = deps.join(" "); | ||
| switch (pm) { | ||
| case "pnpm": | ||
| return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`; | ||
| case "yarn": | ||
| return `yarn add ${pkgs}`; | ||
| case "bun": | ||
| return `bun add ${pkgs}`; | ||
| default: | ||
| return `npm install ${pkgs}`; | ||
| } | ||
| } | ||
| async function planConflictsAndEnv(cwd, baseDir, env, answers) { | ||
| const candidates = []; | ||
| const libDir = path2.join(baseDir, "lib", "software"); | ||
| if (needsClient(env)) candidates.push(path2.join(libDir, "client.ts")); | ||
| if (needsReactQuery(env)) candidates.push(path2.join(libDir, "query-provider.tsx")); | ||
| if (needsServer(env)) candidates.push(path2.join(libDir, "server.ts")); | ||
| if (answers.aiTools.includes("claude")) { | ||
| for (const { dirName } of getSkillFiles()) { | ||
| candidates.push(path2.join(cwd, ".claude", "skills", dirName, "SKILL.md")); | ||
| } | ||
| } | ||
| const conflicts = candidates.filter((p) => fs2.existsSync(p)); | ||
| let policy = "skip"; | ||
| if (conflicts.length > 0) { | ||
| console.log(pc2.yellow(` ${conflicts.length} file(s) already exist:`)); | ||
| for (const c of conflicts) console.log(pc2.dim(` ${path2.relative(cwd, c)}`)); | ||
| const { selected } = await prompts({ | ||
| type: "select", | ||
| name: "selected", | ||
| message: "How should I handle existing files?", | ||
| choices: [ | ||
| { title: "Keep existing (skip)", value: "skip" }, | ||
| { title: "Overwrite all", value: "overwrite" }, | ||
| { title: "Ask for each", value: "ask" } | ||
| ], | ||
| initial: 0 | ||
| }); | ||
| policy = selected ?? "skip"; | ||
| } | ||
| const envFile = env === "vanilla" || env === "edge" ? "" : await pickEnvFile(cwd, env); | ||
| return { policy, envFile }; | ||
| } | ||
| async function pickEnvFile(cwd, env) { | ||
| const candidates = [".env.local", ".env", ".env.development"]; | ||
| const existing = candidates.filter((f) => fs2.existsSync(path2.join(cwd, f))); | ||
| const preferred = env === "nextjs" ? ".env.local" : ".env"; | ||
| if (existing.length === 0) return preferred; | ||
| if (existing.length === 1 && existing[0] === preferred) return existing[0]; | ||
| const options = Array.from(/* @__PURE__ */ new Set([...existing, preferred])); | ||
| const choices = options.map((f) => ({ | ||
| title: f, | ||
| description: existing.includes(f) ? "exists" : "create", | ||
| value: f | ||
| })); | ||
| const initial = Math.max( | ||
| 0, | ||
| choices.findIndex((c) => c.value === preferred) | ||
| ); | ||
| const { file } = await prompts({ | ||
| type: "select", | ||
| name: "file", | ||
| message: "Which env file should I write SDK credentials to?", | ||
| choices, | ||
| initial | ||
| }); | ||
| return file ?? preferred; | ||
| } | ||
| async function writeFileWithPolicy(cwd, filePath, content, policy) { | ||
| const rel = path2.relative(cwd, filePath); | ||
| if (!fs2.existsSync(filePath)) { | ||
| fs2.mkdirSync(path2.dirname(filePath), { recursive: true }); | ||
| fs2.writeFileSync(filePath, content); | ||
| console.log(pc2.green(" Created"), rel); | ||
| return; | ||
| } | ||
| const existing = fs2.readFileSync(filePath, "utf-8"); | ||
| if (existing === content) { | ||
| console.log(pc2.dim(" Unchanged"), rel); | ||
| return; | ||
| } | ||
| let shouldWrite = false; | ||
| if (policy === "overwrite") { | ||
| shouldWrite = true; | ||
| } else if (policy === "ask") { | ||
| const { confirm } = await prompts({ | ||
| type: "confirm", | ||
| name: "confirm", | ||
| message: `Overwrite ${rel}?`, | ||
| initial: false | ||
| }); | ||
| shouldWrite = !!confirm; | ||
| } | ||
| if (shouldWrite) { | ||
| fs2.writeFileSync(filePath, content); | ||
| console.log(pc2.green(" Overwrote"), rel); | ||
| } else { | ||
| console.log(pc2.yellow(" Skipped"), rel, pc2.dim("(already exists)")); | ||
| } | ||
| } | ||
| async function writeEnv(cwd, envFile, publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar, policy, fromBrowserAuth = false) { | ||
| const envPath = path2.join(cwd, envFile); | ||
| const targets = [ | ||
| { name: publishableKeyEnvVar, value: publishableKey } | ||
| ]; | ||
| if (secretKeyEnvVar) { | ||
| targets.push({ name: secretKeyEnvVar, value: secretKey }); | ||
| } | ||
| if (!fs2.existsSync(envPath)) { | ||
| const initial = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar); | ||
| writeEnvFile(envPath, initial.trimStart()); | ||
| console.log(pc2.green(" Created"), envFile); | ||
| return; | ||
| } | ||
| let content = fs2.readFileSync(envPath, "utf-8"); | ||
| let modified = false; | ||
| let appendedHeader = false; | ||
| const headerAlreadyPresent = targets.some( | ||
| (t) => readEnvValue(content, t.name) !== null | ||
| ); | ||
| for (const { name, value } of targets) { | ||
| const existing = readEnvValue(content, name); | ||
| if (existing === null) { | ||
| if (!headerAlreadyPresent && !appendedHeader) { | ||
| if (content.length > 0 && !content.endsWith("\n")) content += "\n"; | ||
| content += "\n# 01.software\n"; | ||
| appendedHeader = true; | ||
| } | ||
| content = setEnvValue(content, name, value); | ||
| modified = true; | ||
| continue; | ||
| } | ||
| if (existing === value) continue; | ||
| if (!value) continue; | ||
| let shouldOverwrite = false; | ||
| if (policy === "overwrite") { | ||
| shouldOverwrite = true; | ||
| } else if (policy === "ask") { | ||
| const { confirm } = await prompts({ | ||
| type: "confirm", | ||
| name: "confirm", | ||
| message: `${name} already set in ${envFile}. Overwrite?`, | ||
| initial: fromBrowserAuth | ||
| }); | ||
| shouldOverwrite = !!confirm; | ||
| } | ||
| if (shouldOverwrite) { | ||
| content = setEnvValue(content, name, value); | ||
| modified = true; | ||
| } | ||
| } | ||
| if (modified) { | ||
| writeEnvFile(envPath, content); | ||
| console.log( | ||
| pc2.green(" Updated"), | ||
| envFile, | ||
| fromBrowserAuth ? pc2.dim("(SDK credentials)") : "" | ||
| ); | ||
| } else { | ||
| chmodSecretFile(envPath); | ||
| console.log(pc2.dim(" Unchanged"), envFile); | ||
| } | ||
| } | ||
| function resolveMcpLocation(tool, cwd) { | ||
| const home = os.homedir(); | ||
| switch (tool) { | ||
| case "claude": | ||
| return { | ||
| kind: "json", | ||
| absolutePath: path2.join(cwd, ".mcp.json"), | ||
| displayPath: ".mcp.json", | ||
| gitignoreEntry: ".mcp.json" | ||
| }; | ||
| case "cursor": | ||
| return { | ||
| kind: "json", | ||
| absolutePath: path2.join(cwd, ".cursor", "mcp.json"), | ||
| displayPath: ".cursor/mcp.json", | ||
| gitignoreEntry: ".cursor/mcp.json" | ||
| }; | ||
| case "vscode": | ||
| return { | ||
| kind: "json", | ||
| absolutePath: path2.join(cwd, ".vscode", "mcp.json"), | ||
| jsonClient: "vscode", | ||
| displayPath: ".vscode/mcp.json", | ||
| gitignoreEntry: ".vscode/mcp.json" | ||
| }; | ||
| case "windsurf": { | ||
| if (!home) return null; | ||
| const p = path2.join(home, ".codeium", "windsurf", "mcp_config.json"); | ||
| return { | ||
| kind: "json", | ||
| absolutePath: p, | ||
| jsonClient: "windsurf", | ||
| displayPath: p, | ||
| gitignoreEntry: null, | ||
| global: true | ||
| }; | ||
| } | ||
| case "codex": { | ||
| if (!home) return null; | ||
| const p = path2.join(home, ".codex", "config.toml"); | ||
| return { kind: "toml", absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }; | ||
| } | ||
| case "gemini": { | ||
| if (!home) return null; | ||
| const p = path2.join(home, ".gemini", "settings.json"); | ||
| return { kind: "json", absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }; | ||
| } | ||
| } | ||
| } | ||
| async function writeMcpConfig(tool, cwd) { | ||
| const loc = resolveMcpLocation(tool, cwd); | ||
| if (!loc) { | ||
| console.log(pc2.yellow(` Skipped ${tool}`), pc2.dim("(HOME not set)")); | ||
| return; | ||
| } | ||
| if (!loc.global) { | ||
| fs2.mkdirSync(path2.dirname(loc.absolutePath), { recursive: true }); | ||
| } | ||
| try { | ||
| if (loc.kind === "json") { | ||
| writeJsonMcp(loc); | ||
| } else { | ||
| writeTomlMcp(loc); | ||
| } | ||
| } catch (err) { | ||
| console.log( | ||
| pc2.yellow(` Skipped ${loc.displayPath}`), | ||
| pc2.dim(err instanceof Error ? err.message : String(err)) | ||
| ); | ||
| } | ||
| } | ||
| function writeJsonMcp(loc) { | ||
| const writeFile = loc.global ? (target, content) => writeSecretGlobalConfig(target, content) : (target, content) => fs2.writeFileSync(target, content); | ||
| if (!fs2.existsSync(loc.absolutePath)) { | ||
| writeFile(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient)); | ||
| console.log(pc2.green(" Created"), loc.displayPath); | ||
| return; | ||
| } | ||
| const rootKey = getMcpRootKey(loc.jsonClient); | ||
| let existing; | ||
| try { | ||
| existing = JSON.parse(fs2.readFileSync(loc.absolutePath, "utf-8")); | ||
| } catch { | ||
| console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(could not parse existing file)")); | ||
| return; | ||
| } | ||
| const nextEntry = getMcpServerEntry(loc.jsonClient); | ||
| const existingServers = existing[rootKey] ?? void 0; | ||
| if (existingServers?.["01software"] && JSON.stringify(existingServers["01software"]) === JSON.stringify(nextEntry)) { | ||
| console.log(pc2.dim(" Unchanged"), loc.displayPath); | ||
| return; | ||
| } | ||
| const servers = existing[rootKey] ?? {}; | ||
| servers["01software"] = nextEntry; | ||
| existing[rootKey] = servers; | ||
| writeFile(loc.absolutePath, JSON.stringify(existing, null, 2) + "\n"); | ||
| console.log(pc2.green(" Updated"), loc.displayPath); | ||
| } | ||
| function writeTomlMcp(loc) { | ||
| const section = getCodexMcpTomlSection(); | ||
| const writeFile = loc.global ? (target, content) => writeSecretGlobalConfig(target, content) : (target, content) => fs2.writeFileSync(target, content); | ||
| if (!fs2.existsSync(loc.absolutePath)) { | ||
| writeFile(loc.absolutePath, section.trimStart()); | ||
| console.log(pc2.green(" Created"), loc.displayPath); | ||
| return; | ||
| } | ||
| const existing = fs2.readFileSync(loc.absolutePath, "utf-8"); | ||
| if (!existing.includes(CODEX_MCP_SECTION_MARKER)) { | ||
| const sep = existing.endsWith("\n") ? "" : "\n"; | ||
| writeFile(loc.absolutePath, existing + sep + section); | ||
| console.log(pc2.green(" Updated"), loc.displayPath); | ||
| return; | ||
| } | ||
| const replaced = replaceTomlMcpSection(existing, section); | ||
| if (replaced === existing) { | ||
| console.log(pc2.dim(" Unchanged"), loc.displayPath); | ||
| return; | ||
| } | ||
| writeFile(loc.absolutePath, replaced); | ||
| console.log(pc2.green(" Updated"), loc.displayPath); | ||
| } | ||
| function addToGitignore(cwd, tools) { | ||
| const entries = []; | ||
| for (const tool of tools) { | ||
| const loc = resolveMcpLocation(tool, cwd); | ||
| if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry); | ||
| } | ||
| 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\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, publishableKey, secretKey, tenantName, policy) { | ||
| let ctx = { | ||
| tenantName: tenantName || "Your Tenant", | ||
| features: void 0, | ||
| collections: void 0 | ||
| }; | ||
| if (publishableKey && secretKey) { | ||
| const fetched = await fetchTenantContext(publishableKey, secretKey); | ||
| if (fetched) { | ||
| ctx = { | ||
| tenantName: fetched.tenantName || ctx.tenantName, | ||
| features: fetched.features, | ||
| collections: fetched.collections | ||
| }; | ||
| } | ||
| } | ||
| const claudeDir = path2.join(cwd, ".claude"); | ||
| const softwareDir = path2.join(claudeDir, "01software"); | ||
| const skillsDir = path2.join(claudeDir, "skills"); | ||
| fs2.mkdirSync(softwareDir, { recursive: true }); | ||
| fs2.mkdirSync(skillsDir, { recursive: true }); | ||
| const contextPath = path2.join(softwareDir, "context.md"); | ||
| const contextExists = fs2.existsSync(contextPath); | ||
| fs2.writeFileSync(contextPath, generateClaudeMd(ctx)); | ||
| console.log(pc2.green(contextExists ? " Updated" : " Created"), ".claude/01software/context.md"); | ||
| const claudeMdPath = path2.join(claudeDir, "CLAUDE.md"); | ||
| const importLine = "@.claude/01software/context.md"; | ||
| if (!fs2.existsSync(claudeMdPath)) { | ||
| fs2.writeFileSync(claudeMdPath, importLine + "\n"); | ||
| console.log(pc2.green(" Created"), ".claude/CLAUDE.md"); | ||
| } else { | ||
| const existing = fs2.readFileSync(claudeMdPath, "utf-8"); | ||
| if (!existing.includes(importLine)) { | ||
| const prefix = existing.endsWith("\n") ? "\n" : "\n\n"; | ||
| fs2.appendFileSync(claudeMdPath, prefix + importLine + "\n"); | ||
| console.log(pc2.green(" Updated"), ".claude/CLAUDE.md", pc2.dim("(added @import)")); | ||
| } else { | ||
| console.log(pc2.dim(" Unchanged"), ".claude/CLAUDE.md"); | ||
| } | ||
| } | ||
| for (const { dirName, content } of getSkillFiles()) { | ||
| const skillDir = path2.join(skillsDir, dirName); | ||
| const skillPath = path2.join(skillDir, "SKILL.md"); | ||
| fs2.mkdirSync(skillDir, { recursive: true }); | ||
| await writeFileWithPolicy(cwd, skillPath, content, policy); | ||
| } | ||
| } | ||
| var WS_FILE = "pnpm-workspace.yaml"; | ||
| var WS_BACKUP = "pnpm-workspace.yaml.bak"; | ||
| function hasPnpmWorkspace(cwd) { | ||
| return fs2.existsSync(path2.join(cwd, WS_FILE)); | ||
| } | ||
| function patchPnpmWorkspace(cwd) { | ||
| const wsPath = path2.join(cwd, WS_FILE); | ||
| if (!fs2.existsSync(wsPath)) return false; | ||
| const content = fs2.readFileSync(wsPath, "utf-8"); | ||
| if (content.includes("packages:")) return false; | ||
| fs2.copyFileSync(wsPath, path2.join(cwd, WS_BACKUP)); | ||
| fs2.writeFileSync(wsPath, content.trimEnd() + "\npackages: []\n"); | ||
| return true; | ||
| } | ||
| function restorePnpmWorkspace(cwd) { | ||
| const backupPath = path2.join(cwd, WS_BACKUP); | ||
| if (!fs2.existsSync(backupPath)) return; | ||
| fs2.copyFileSync(backupPath, path2.join(cwd, WS_FILE)); | ||
| fs2.unlinkSync(backupPath); | ||
| } | ||
| export { | ||
| detectProject, | ||
| init | ||
| }; | ||
| //# sourceMappingURL=chunk-3RQE6YVO.js.map |
| {"version":3,"sources":["../src/init.ts","../src/detect.ts","../src/browser-auth.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { execSync } from 'node:child_process'\nimport pc from 'picocolors'\nimport prompts from 'prompts'\nimport type { PackageManager, ProjectEnv, ProjectInfo } from './detect'\nimport {\n needsClient,\n needsServer,\n needsReactQuery,\n getPublishableKeyEnvVar,\n} from './detect'\nimport type { AiTool, InitAnswers } from './prompts'\nimport {\n getClientTemplate,\n getAnalyticsTemplate,\n getQueryProviderTemplate,\n getServerTemplate,\n getEnvContent,\n getMcpConfigTemplate,\n getMcpRootKey,\n getMcpServerEntry,\n getCodexMcpTomlSection,\n CODEX_MCP_SECTION_MARKER,\n} from './templates'\nimport { startBrowserAuth } from './browser-auth'\nimport {\n generateClaudeMd,\n getSkillFiles,\n fetchTenantContext,\n} from './ai-docs'\nimport {\n readEnvValue,\n setEnvValue,\n replaceTomlMcpSection,\n writeEnvFile,\n chmodSecretFile,\n writeSecretGlobalConfig,\n} from './file-ops'\n\ntype ResolvedProjectInfo = Omit<ProjectInfo, 'packageManager'> & {\n packageManager: PackageManager\n}\n\nconst SECRET_KEY_ENV_VAR = 'SOFTWARE_SECRET_KEY'\n\nexport type ConflictPolicy = 'overwrite' | 'skip' | 'ask'\n\nexport interface InitResult {\n installFailed: boolean\n installSkipped: boolean\n installCmd: string\n /** Set when browser-auth or manual entry produced a publishable key the\n * caller should prefer over `answers.publishableKey` when computing\n * next-step messages. */\n publishableKey?: string\n /** Set when browser-auth or manual entry produced a secret key. */\n secretKey?: string\n /** Tenant display name from browser-auth, when available. */\n tenantName?: string\n}\n\nexport interface InitDeps {\n /** Test seam — replaces the real browser-auth flow. */\n startBrowserAuth?: typeof startBrowserAuth\n /** Test seam — skips actually invoking the package manager. */\n skipInstall?: boolean\n}\n\nexport async function init(\n cwd: string,\n info: ResolvedProjectInfo,\n answers: InitAnswers,\n deps: InitDeps = {},\n): Promise<InitResult> {\n const { packageManager, srcDir } = info\n const env = answers.env\n const baseDir = srcDir ? path.join(cwd, 'src') : cwd\n const browserAuth = deps.startBrowserAuth ?? startBrowserAuth\n\n const publishableKeyEnvVar = getPublishableKeyEnvVar(env)\n const wantsClient = needsClient(env)\n const wantsServer = needsServer(env)\n const wantsReactQuery = needsReactQuery(env)\n\n // 0. Plan: scan for conflicts and pick the env file. Both prompts are\n // front-loaded so the rest of init() doesn't interleave I/O with prompts.\n const plan = await planConflictsAndEnv(cwd, baseDir, env, answers)\n\n // 1. Install dependencies — skip whatever's already in package.json\n const installResult = deps.skipInstall\n ? {\n installFailed: false,\n installSkipped: true,\n installCmd: buildAddCmd(packageManager, hasPnpmWorkspace(cwd), [\n '@01.software/sdk',\n ]),\n }\n : installDeps(\n cwd,\n packageManager,\n info.hasSdk,\n info.hasReactQuery,\n wantsReactQuery,\n )\n\n // 2. Write lib/software/ files\n if (wantsClient || wantsReactQuery || wantsServer) {\n fs.mkdirSync(path.join(baseDir, 'lib', 'software'), { recursive: true })\n }\n const libDir = path.join(baseDir, 'lib', 'software')\n\n if (wantsClient) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'client.ts'),\n getClientTemplate(env, publishableKeyEnvVar),\n plan.policy,\n )\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'analytics.ts'),\n getAnalyticsTemplate(env, publishableKeyEnvVar),\n plan.policy,\n )\n }\n if (wantsReactQuery) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'query-provider.tsx'),\n getQueryProviderTemplate(env),\n plan.policy,\n )\n }\n if (wantsServer) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'server.ts'),\n getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR),\n plan.policy,\n )\n }\n\n // 3. Append to env file (browser auth handles its own write in step 4)\n if (plan.envFile && answers.authMethod !== 'browser') {\n await writeEnv(\n cwd,\n plan.envFile,\n answers.publishableKey || '',\n answers.secretKey || '',\n publishableKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n plan.policy,\n )\n }\n\n // 4. Browser auth — get real credentials and force-write env\n let publishableKey = answers.publishableKey\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 browserAuth()\n publishableKey = creds.publishableKey\n secretKey = creds.secretKey\n tenantName = creds.tenantName\n\n // Browser auth just minted fresh credentials → policy is overwrite.\n if (plan.envFile && publishableKey) {\n await writeEnv(\n cwd,\n plan.envFile,\n publishableKey,\n secretKey,\n publishableKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n 'overwrite',\n true,\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. AI tool configs (MCP + Claude docs)\n if (answers.aiTools.length > 0) {\n for (const tool of answers.aiTools) {\n await writeMcpConfig(tool, cwd)\n }\n\n addToGitignore(cwd, answers.aiTools)\n\n if (answers.aiTools.includes('claude')) {\n await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy)\n }\n }\n\n return {\n ...installResult,\n publishableKey: publishableKey || undefined,\n secretKey: secretKey || undefined,\n tenantName: tenantName || undefined,\n }\n}\n\n// ── Install ──────────────────────────────────────────────────────────\n\ninterface DepSpec {\n name: string\n installed: boolean\n needed: boolean\n}\n\nfunction installDeps(\n cwd: string,\n pm: PackageManager,\n hasSdk: boolean,\n hasReactQuery: boolean,\n wantsReactQuery: boolean,\n): InitResult {\n const allDeps: DepSpec[] = [\n { name: '@01.software/sdk', installed: hasSdk, needed: true },\n { name: '@tanstack/react-query', installed: hasReactQuery, needed: wantsReactQuery },\n ]\n const fullList = allDeps.filter((d) => d.needed).map((d) => d.name)\n const toInstall = allDeps.filter((d) => d.needed && !d.installed).map((d) => d.name)\n const fullCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), fullList)\n\n if (toInstall.length === 0) {\n console.log(pc.dim(` Dependencies already installed: ${fullList.join(', ')}`))\n return { installFailed: false, installSkipped: true, installCmd: fullCmd }\n }\n\n const addCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), toInstall)\n console.log(pc.dim(` Installing ${toInstall.join(' and ')}...`))\n\n const wsPatched = pm === 'pnpm' && patchPnpmWorkspace(cwd)\n let installFailed = false\n\n try {\n execSync(addCmd, { cwd, stdio: 'pipe' })\n console.log(pc.green(' Installed'), toInstall.join(', '))\n } catch (error) {\n installFailed = true\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.yellow(' Install failed — continuing with scaffolding'))\n const firstLines = msg.split('\\n').slice(0, 3).map((l) => ` ${l}`).join('\\n')\n if (firstLines) console.log(pc.dim(firstLines))\n console.log(pc.dim(` Run manually: ${addCmd}`))\n } finally {\n if (wsPatched) restorePnpmWorkspace(cwd)\n }\n\n return { installFailed, installSkipped: false, installCmd: addCmd }\n}\n\nfunction buildAddCmd(\n pm: PackageManager,\n hasPnpmWs: boolean,\n deps: string[],\n): string {\n const pkgs = deps.join(' ')\n switch (pm) {\n case 'pnpm':\n return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`\n case 'yarn':\n return `yarn add ${pkgs}`\n case 'bun':\n return `bun add ${pkgs}`\n default:\n return `npm install ${pkgs}`\n }\n}\n\n// ── Conflict planning ────────────────────────────────────────────────\n\ninterface PlanResult {\n policy: ConflictPolicy\n /** Empty string when env file is not used (vanilla / edge) */\n envFile: string\n}\n\nasync function planConflictsAndEnv(\n cwd: string,\n baseDir: string,\n env: ProjectEnv,\n answers: InitAnswers,\n): Promise<PlanResult> {\n const candidates: string[] = []\n const libDir = path.join(baseDir, 'lib', 'software')\n if (needsClient(env)) candidates.push(path.join(libDir, 'client.ts'))\n if (needsReactQuery(env)) candidates.push(path.join(libDir, 'query-provider.tsx'))\n if (needsServer(env)) candidates.push(path.join(libDir, 'server.ts'))\n if (answers.aiTools.includes('claude')) {\n for (const { dirName } of getSkillFiles()) {\n candidates.push(path.join(cwd, '.claude', 'skills', dirName, 'SKILL.md'))\n }\n }\n\n const conflicts = candidates.filter((p) => fs.existsSync(p))\n\n let policy: ConflictPolicy = 'skip'\n if (conflicts.length > 0) {\n console.log(pc.yellow(` ${conflicts.length} file(s) already exist:`))\n for (const c of conflicts) console.log(pc.dim(` ${path.relative(cwd, c)}`))\n const { selected } = await prompts({\n type: 'select',\n name: 'selected',\n message: 'How should I handle existing files?',\n choices: [\n { title: 'Keep existing (skip)', value: 'skip' },\n { title: 'Overwrite all', value: 'overwrite' },\n { title: 'Ask for each', value: 'ask' },\n ],\n initial: 0,\n })\n policy = (selected as ConflictPolicy) ?? 'skip'\n }\n\n const envFile =\n env === 'vanilla' || env === 'edge' ? '' : await pickEnvFile(cwd, env)\n\n return { policy, envFile }\n}\n\nasync function pickEnvFile(cwd: string, env: ProjectEnv): Promise<string> {\n // Order matters — `.env.local` first so it's the default selection on\n // Next.js, where it's the conventional secret store.\n const candidates = ['.env.local', '.env', '.env.development']\n const existing = candidates.filter((f) => fs.existsSync(path.join(cwd, f)))\n const preferred = env === 'nextjs' ? '.env.local' : '.env'\n\n if (existing.length === 0) return preferred\n if (existing.length === 1 && existing[0] === preferred) return existing[0]\n\n // Multiple files exist OR the only existing file isn't the preferred default.\n const options = Array.from(new Set([...existing, preferred]))\n const choices = options.map((f) => ({\n title: f,\n description: existing.includes(f) ? 'exists' : 'create',\n value: f,\n }))\n const initial = Math.max(\n 0,\n choices.findIndex((c) => c.value === preferred),\n )\n const { file } = await prompts({\n type: 'select',\n name: 'file',\n message: 'Which env file should I write SDK credentials to?',\n choices,\n initial,\n })\n return file ?? preferred\n}\n\n// ── Generic file write ───────────────────────────────────────────────\n\nasync function writeFileWithPolicy(\n cwd: string,\n filePath: string,\n content: string,\n policy: ConflictPolicy,\n): Promise<void> {\n const rel = path.relative(cwd, filePath)\n if (!fs.existsSync(filePath)) {\n fs.mkdirSync(path.dirname(filePath), { recursive: true })\n fs.writeFileSync(filePath, content)\n console.log(pc.green(' Created'), rel)\n return\n }\n\n const existing = fs.readFileSync(filePath, 'utf-8')\n if (existing === content) {\n console.log(pc.dim(' Unchanged'), rel)\n return\n }\n\n let shouldWrite = false\n if (policy === 'overwrite') {\n shouldWrite = true\n } else if (policy === 'ask') {\n const { confirm } = await prompts({\n type: 'confirm',\n name: 'confirm',\n message: `Overwrite ${rel}?`,\n initial: false,\n })\n shouldWrite = !!confirm\n }\n\n if (shouldWrite) {\n fs.writeFileSync(filePath, content)\n console.log(pc.green(' Overwrote'), rel)\n } else {\n console.log(pc.yellow(' Skipped'), rel, pc.dim('(already exists)'))\n }\n}\n\n// ── Env file write ───────────────────────────────────────────────────\n\nasync function writeEnv(\n cwd: string,\n envFile: string,\n publishableKey: string,\n secretKey: string,\n publishableKeyEnvVar: string,\n secretKeyEnvVar: string | null,\n policy: ConflictPolicy,\n fromBrowserAuth = false,\n): Promise<void> {\n const envPath = path.join(cwd, envFile)\n\n const targets: { name: string; value: string }[] = [\n { name: publishableKeyEnvVar, value: publishableKey },\n ]\n if (secretKeyEnvVar) {\n targets.push({ name: secretKeyEnvVar, value: secretKey })\n }\n\n if (!fs.existsSync(envPath)) {\n const initial = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar)\n writeEnvFile(envPath, initial.trimStart())\n console.log(pc.green(' Created'), envFile)\n return\n }\n\n let content = fs.readFileSync(envPath, 'utf-8')\n let modified = false\n let appendedHeader = false\n const headerAlreadyPresent = targets.some(\n (t) => readEnvValue(content, t.name) !== null,\n )\n\n for (const { name, value } of targets) {\n const existing = readEnvValue(content, name)\n\n if (existing === null) {\n // Append. Add the `# 01.software` section header on first append, but\n // only if no other 01.software keys are already in the file.\n if (!headerAlreadyPresent && !appendedHeader) {\n if (content.length > 0 && !content.endsWith('\\n')) content += '\\n'\n content += '\\n# 01.software\\n'\n appendedHeader = true\n }\n content = setEnvValue(content, name, value)\n modified = true\n continue\n }\n\n if (existing === value) continue\n // Don't overwrite an existing real value with empty (e.g. user skipped key entry).\n if (!value) continue\n\n let shouldOverwrite = false\n if (policy === 'overwrite') {\n shouldOverwrite = true\n } else if (policy === 'ask') {\n const { confirm } = await prompts({\n type: 'confirm',\n name: 'confirm',\n message: `${name} already set in ${envFile}. Overwrite?`,\n initial: fromBrowserAuth,\n })\n shouldOverwrite = !!confirm\n }\n\n if (shouldOverwrite) {\n content = setEnvValue(content, name, value)\n modified = true\n }\n }\n\n if (modified) {\n writeEnvFile(envPath, content)\n console.log(\n pc.green(' Updated'),\n envFile,\n fromBrowserAuth ? pc.dim('(SDK credentials)') : '',\n )\n } else {\n // Tighten mode on existing files we did not modify, so re-runs harden\n // permissions on env files created by an earlier looser version.\n chmodSecretFile(envPath)\n console.log(pc.dim(' Unchanged'), envFile)\n }\n}\n\n// ── MCP targets registry ─────────────────────────────────────────────\n\ntype McpFormat = 'json' | 'toml'\ntype McpJsonClient = 'generic' | 'windsurf' | 'vscode'\ninterface McpLocation {\n kind: McpFormat\n absolutePath: string\n jsonClient?: McpJsonClient\n displayPath: string\n gitignoreEntry: string | null // null = global path, not worth ignoring\n /** Global config under user HOME — apply 0o600 mode, atomic rename, and\n * symlink/hardlink rejection per spec §\"Secret Handling Rules\". */\n global?: boolean\n}\n\nfunction resolveMcpLocation(tool: AiTool, cwd: string): McpLocation | null {\n const home = os.homedir()\n\n switch (tool) {\n case 'claude':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.mcp.json'),\n displayPath: '.mcp.json',\n gitignoreEntry: '.mcp.json',\n }\n case 'cursor':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.cursor', 'mcp.json'),\n displayPath: '.cursor/mcp.json',\n gitignoreEntry: '.cursor/mcp.json',\n }\n case 'vscode':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.vscode', 'mcp.json'),\n jsonClient: 'vscode',\n displayPath: '.vscode/mcp.json',\n gitignoreEntry: '.vscode/mcp.json',\n }\n case 'windsurf': {\n if (!home) return null\n const p = path.join(home, '.codeium', 'windsurf', 'mcp_config.json')\n return {\n kind: 'json',\n absolutePath: p,\n jsonClient: 'windsurf',\n displayPath: p,\n gitignoreEntry: null,\n global: true,\n }\n }\n case 'codex': {\n if (!home) return null\n const p = path.join(home, '.codex', 'config.toml')\n return { kind: 'toml', absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }\n }\n case 'gemini': {\n if (!home) return null\n const p = path.join(home, '.gemini', 'settings.json')\n return { kind: 'json', absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }\n }\n }\n}\n\nasync function writeMcpConfig(\n tool: AiTool,\n cwd: string,\n): Promise<void> {\n const loc = resolveMcpLocation(tool, cwd)\n if (!loc) {\n console.log(pc.yellow(` Skipped ${tool}`), pc.dim('(HOME not set)'))\n return\n }\n\n // For global secret-bearing config, parent-dir creation with 0o700 is\n // handled inside `writeSecretGlobalConfig`. Repo-local writes (Claude Code,\n // Cursor, VS Code) keep loose mkdir — they only contain OAuth/discovery URLs.\n if (!loc.global) {\n fs.mkdirSync(path.dirname(loc.absolutePath), { recursive: true })\n }\n\n try {\n if (loc.kind === 'json') {\n writeJsonMcp(loc)\n } else {\n writeTomlMcp(loc)\n }\n } catch (err) {\n console.log(\n pc.yellow(` Skipped ${loc.displayPath}`),\n pc.dim(err instanceof Error ? err.message : String(err)),\n )\n }\n}\n\nfunction writeJsonMcp(loc: McpLocation): void {\n const writeFile = loc.global\n ? (target: string, content: string) => writeSecretGlobalConfig(target, content)\n : (target: string, content: string) => fs.writeFileSync(target, content)\n\n if (!fs.existsSync(loc.absolutePath)) {\n writeFile(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient))\n console.log(pc.green(' Created'), loc.displayPath)\n return\n }\n\n // VS Code uses `servers` as the config root; all other JSON clients use `mcpServers`.\n const rootKey = getMcpRootKey(loc.jsonClient)\n\n let existing: Record<string, unknown>\n try {\n existing = JSON.parse(fs.readFileSync(loc.absolutePath, 'utf-8'))\n } catch {\n console.log(pc.yellow(' Skipped'), loc.displayPath, pc.dim('(could not parse existing file)'))\n return\n }\n\n const nextEntry = getMcpServerEntry(loc.jsonClient)\n const existingServers = (existing[rootKey] as Record<string, unknown> | undefined) ?? undefined\n if (\n existingServers?.['01software'] &&\n JSON.stringify(existingServers['01software']) === JSON.stringify(nextEntry)\n ) {\n console.log(pc.dim(' Unchanged'), loc.displayPath)\n return\n }\n\n const servers = (existing[rootKey] as Record<string, unknown> | undefined) ?? {}\n servers['01software'] = nextEntry\n existing[rootKey] = servers\n writeFile(loc.absolutePath, JSON.stringify(existing, null, 2) + '\\n')\n console.log(pc.green(' Updated'), loc.displayPath)\n}\n\nfunction writeTomlMcp(loc: McpLocation): void {\n const section = getCodexMcpTomlSection()\n const writeFile = loc.global\n ? (target: string, content: string) => writeSecretGlobalConfig(target, content)\n : (target: string, content: string) => fs.writeFileSync(target, content)\n\n if (!fs.existsSync(loc.absolutePath)) {\n writeFile(loc.absolutePath, section.trimStart())\n console.log(pc.green(' Created'), loc.displayPath)\n return\n }\n\n const existing = fs.readFileSync(loc.absolutePath, 'utf-8')\n\n if (!existing.includes(CODEX_MCP_SECTION_MARKER)) {\n const sep = existing.endsWith('\\n') ? '' : '\\n'\n // Atomic rewrite (read-modify-write) — `appendFileSync` would bypass the\n // symlink/hardlink guards and the 0o600 mode of `writeSecretGlobalConfig`.\n writeFile(loc.absolutePath, existing + sep + section)\n console.log(pc.green(' Updated'), loc.displayPath)\n return\n }\n\n const replaced = replaceTomlMcpSection(existing, section)\n if (replaced === existing) {\n console.log(pc.dim(' Unchanged'), loc.displayPath)\n return\n }\n\n writeFile(loc.absolutePath, replaced)\n console.log(pc.green(' Updated'), loc.displayPath)\n}\n\nfunction addToGitignore(cwd: string, tools: AiTool[]): void {\n const entries: string[] = []\n for (const tool of tools) {\n const loc = resolveMcpLocation(tool, cwd)\n if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry)\n }\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\\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 publishableKey: string,\n secretKey: string,\n tenantName: string,\n policy: ConflictPolicy,\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 (publishableKey && secretKey) {\n const fetched = await fetchTenantContext(publishableKey, 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 softwareDir = path.join(claudeDir, '01software')\n const skillsDir = path.join(claudeDir, 'skills')\n fs.mkdirSync(softwareDir, { recursive: true })\n fs.mkdirSync(skillsDir, { recursive: true })\n\n // context.md is intentionally always overwritten so re-running init\n // refreshes tenant data; users don't customize this file.\n const contextPath = path.join(softwareDir, 'context.md')\n const contextExists = fs.existsSync(contextPath)\n fs.writeFileSync(contextPath, generateClaudeMd(ctx))\n console.log(pc.green(contextExists ? ' Updated' : ' Created'), '.claude/01software/context.md')\n\n // CLAUDE.md — append @import line (idempotent, never overwrites user content)\n const claudeMdPath = path.join(claudeDir, 'CLAUDE.md')\n const importLine = '@.claude/01software/context.md'\n if (!fs.existsSync(claudeMdPath)) {\n fs.writeFileSync(claudeMdPath, importLine + '\\n')\n console.log(pc.green(' Created'), '.claude/CLAUDE.md')\n } else {\n const existing = fs.readFileSync(claudeMdPath, 'utf-8')\n if (!existing.includes(importLine)) {\n const prefix = existing.endsWith('\\n') ? '\\n' : '\\n\\n'\n fs.appendFileSync(claudeMdPath, prefix + importLine + '\\n')\n console.log(pc.green(' Updated'), '.claude/CLAUDE.md', pc.dim('(added @import)'))\n } else {\n console.log(pc.dim(' Unchanged'), '.claude/CLAUDE.md')\n }\n }\n\n // Skill files honour the user's conflict policy (templates the user may have\n // tuned to their workflow).\n for (const { dirName, content } of getSkillFiles()) {\n const skillDir = path.join(skillsDir, dirName)\n const skillPath = path.join(skillDir, 'SKILL.md')\n fs.mkdirSync(skillDir, { recursive: true })\n await writeFileWithPolicy(cwd, skillPath, content, policy)\n }\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 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 hasReactQuery: 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 let hasReactQuery = false\n let parseError = 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 {\n hasPackageJson: true,\n parseError: true,\n env: 'node',\n packageManager: null,\n hasSdk: false,\n hasReactQuery: false,\n srcDir: false,\n }\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 hasReactQuery = '@tanstack/react-query' 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, parseError, env, packageManager, hasSdk, hasReactQuery, 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 publishable key */\nexport function getPublishableKeyEnvVar(env: ProjectEnv): string {\n switch (env) {\n case 'nextjs':\n return 'NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY'\n case 'react-vite':\n return 'VITE_SOFTWARE_PUBLISHABLE_KEY'\n case 'react-cra':\n return 'REACT_APP_SOFTWARE_PUBLISHABLE_KEY'\n default:\n return 'SOFTWARE_PUBLISHABLE_KEY'\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\ninterface ExchangeResponse {\n publishableKey: string\n secretKey: string\n tenantName: string\n tenantId: string\n}\n\nasync function exchangeCode(\n webUrl: string,\n code: string,\n): Promise<ExchangeResponse | null> {\n const url = `${webUrl}/api/cli/exchange`\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code }),\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n console.error(\n pc.red(\n `Exchange failed: HTTP ${res.status} from ${url}${body ? ` — ${body.slice(0, 200)}` : ''}`,\n ),\n )\n return null\n }\n const data = (await res.json()) as Partial<ExchangeResponse>\n if (\n typeof data.publishableKey !== 'string' ||\n typeof data.secretKey !== 'string' ||\n typeof data.tenantName !== 'string' ||\n typeof data.tenantId !== 'string'\n ) {\n console.error(pc.red(`Exchange failed: malformed response from ${url}`))\n return null\n }\n return {\n publishableKey: data.publishableKey,\n secretKey: data.secretKey,\n tenantName: data.tenantName,\n tenantId: data.tenantId,\n }\n } catch (err) {\n console.error(\n pc.red(\n `Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`,\n ),\n )\n return null\n }\n}\n\nexport async function startBrowserAuth(options?: {\n webUrl?: string\n tenantId?: string\n}): Promise<{\n publishableKey: string\n secretKey: string\n tenantName: string\n tenantId?: string\n}> {\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' || req.method !== 'GET') {\n res.writeHead(404).end()\n return\n }\n\n const error = url.searchParams.get('error')\n if (error) {\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\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 const code = url.searchParams.get('code')\n const receivedState = url.searchParams.get('state')\n\n if (!code || !receivedState) {\n res\n .writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('Missing code or state.'))\n cleanup(new Error('Login failed: missing code or state.'))\n return\n }\n\n if (receivedState !== state) {\n res\n .writeHead(403, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('State mismatch.'))\n console.error(pc.red('Login failed: state mismatch.'))\n cleanup(new Error('Login failed: state mismatch.'))\n return\n }\n\n exchangeCode(webUrl, code).then((creds) => {\n if (!creds) {\n res\n .writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('Invalid or expired code.'))\n cleanup(new Error('Login failed: code exchange failed.'))\n return\n }\n\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(SUCCESS_HTML)\n\n console.log(pc.green(`\\nLogged in successfully!`))\n console.log(pc.dim(`Tenant: ${creds.tenantName}`))\n\n cleanup(null, creds)\n })\n })\n\n let timeout: ReturnType<typeof setTimeout>\n let completed = false\n\n function cleanup(err: Error | null, result?: ExchangeResponse) {\n if (completed) return\n completed = true\n clearTimeout(timeout)\n server.closeAllConnections?.()\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"],"mappings":"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","names":["fs","path","pc","os","path","fs","pc"]} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
161337
1.59%1634
1.11%