neon-init 0.16.2 → 0.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +29 -12
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/interactive.js +22 -13
- package/dist/interactive.js.map +1 -1
- package/dist/lib/auth.js +2 -2
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/enrich-output.d.ts +11 -0
- package/dist/lib/enrich-output.d.ts.map +1 -0
- package/dist/lib/enrich-output.js +58 -0
- package/dist/lib/enrich-output.js.map +1 -0
- package/dist/lib/neonctl.d.ts +4 -14
- package/dist/lib/neonctl.d.ts.map +1 -1
- package/dist/lib/neonctl.js +5 -29
- package/dist/lib/neonctl.js.map +1 -1
- package/dist/lib/phases/auth.d.ts.map +1 -1
- package/dist/lib/phases/auth.js +2 -2
- package/dist/lib/phases/auth.js.map +1 -1
- package/dist/lib/phases/getting-started.js +2 -2
- package/dist/lib/phases/getting-started.js.map +1 -1
- package/dist/lib/phases/status.d.ts +1 -1
- package/dist/lib/phases/status.d.ts.map +1 -1
- package/dist/lib/phases/status.js +5 -5
- package/dist/lib/phases/status.js.map +1 -1
- package/dist/lib/route-command.d.ts +8 -1
- package/dist/lib/route-command.d.ts.map +1 -1
- package/dist/lib/route-command.js +87 -1
- package/dist/lib/route-command.js.map +1 -1
- package/dist/lib/types.d.ts +1 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/interactive.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.js","names":[],"sources":["../src/interactive.ts"],"sourcesContent":["/**\n * Interactive v2 CLI — purpose-built guided flow for humans.\n * Uses the same underlying install functions but with a clean clack-based UX.\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport {\n\tconfirm,\n\tisCancel,\n\tlog,\n\tmultiselect,\n\toutro,\n\tselect,\n\tspinner,\n} from \"@clack/prompts\";\nimport { execa } from \"execa\";\nimport { bold, dim } from \"yoctocolors\";\nimport { ALL_CONFIGURABLE_AGENTS, getAddMcpAgentId } from \"./lib/agents.js\";\nimport { ensureNeonctlAuth, isAuthenticated } from \"./lib/auth.js\";\nimport {\n\ttype BootstrapTemplate,\n\tFALLBACK_TEMPLATES,\n\tfetchTemplates,\n\ttype NeonFeature,\n} from \"./lib/bootstrap.js\";\nimport { detectAgent } from \"./lib/detect-agent.js\";\nimport { detectAvailableEditors } from \"./lib/editors.js\";\nimport {\n\tinstallExtension,\n\tisExtensionInstalled,\n\tusesExtension,\n} from \"./lib/extension.js\";\nimport { inspectProject } from \"./lib/inspect.js\";\nimport { ensureNeonctl } from \"./lib/neonctl.js\";\nimport { installAgentSkills } from \"./lib/skills.js\";\nimport type { Editor } from \"./lib/types.js\";\n\nfunction wordWrap(text: string, width: number): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.map((line) => {\n\t\t\tif (line.length <= width) return line;\n\t\t\tconst words = line.split(\" \");\n\t\t\tconst lines: string[] = [];\n\t\t\tlet current = \"\";\n\t\t\tfor (const word of words) {\n\t\t\t\tif (\n\t\t\t\t\tcurrent.length + word.length + 1 > width &&\n\t\t\t\t\tcurrent.length > 0\n\t\t\t\t) {\n\t\t\t\t\tlines.push(current);\n\t\t\t\t\tcurrent = word;\n\t\t\t\t} else {\n\t\t\t\t\tcurrent = current.length > 0 ? `${current} ${word}` : word;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (current.length > 0) lines.push(current);\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n// Patch picocolors (used by @clack/prompts) to use Neon green instead of cyan/magenta.\n// clack hardcodes picocolors.cyan() with no theme API, so this is the least invasive override.\nimport picocolors from \"picocolors\";\n\nconst neonGreenFn = (s: string) => `\\x1b[38;2;75;181;120m${s}\\x1b[39m`;\nconst originalCyan = picocolors.cyan;\nconst originalMagenta = picocolors.magenta;\n\nfunction patchClackColors(): () => void {\n\tconst pc = picocolors as unknown as Record<string, unknown>;\n\tpc.cyan = neonGreenFn;\n\tpc.magenta = neonGreenFn;\n\treturn () => {\n\t\tpc.cyan = originalCyan;\n\t\tpc.magenta = originalMagenta;\n\t};\n}\n\nexport interface InteractiveInitOptions {\n\tpreview?: boolean;\n}\n\nexport async function interactiveInit(\n\toptions: InteractiveInitOptions = {},\n): Promise<void> {\n\tconst restoreColors = patchClackColors();\n\ttry {\n\t\tawait interactiveInitInner(options);\n\t} finally {\n\t\trestoreColors();\n\t}\n}\n\nasync function interactiveInitInner(\n\toptions: InteractiveInitOptions,\n): Promise<void> {\n\tconsole.log();\n\tconsole.log(\n\t\t\"\\x1b[38;2;75;181;120m\" +\n\t\t\t[\n\t\t\t\t\" ██╗ ██╗██████╗ ██████╗ ██╗ ██╗\",\n\t\t\t\t\" ███╗ ██║██╔═══╝ ██╔═══██╗███╗ ██║\",\n\t\t\t\t\" ████╗██║██████╗ ██║ ██║████╗██║\",\n\t\t\t\t\" ██╔████║██╔═══╝ ██║ ██║██╔████║\",\n\t\t\t\t\" ██║╚███║██████╗ ╚██████╔╝██║╚███║\",\n\t\t\t\t\" ╚═╝ ╚══╝╚═════╝ ╚═════╝ ╚═╝ ╚══╝\",\n\t\t\t].join(\"\\n\") +\n\t\t\t\"\\x1b[0m\",\n\t);\n\tconsole.log(\n\t\tdim(\n\t\t\twordWrap(\n\t\t\t\t\"\\nLet's get your project set up with Neon. We'll install the MCP server, agent skills, and IDE extension, then connect your app to a database.\\n\",\n\t\t\t\tprocess.stdout.columns || 80,\n\t\t\t),\n\t\t),\n\t);\n\n\tconst detectedAgentId = detectAgent();\n\tconst detectedEditor = detectedAgentId\n\t\t? agentIdToEditor(detectedAgentId)\n\t\t: null;\n\n\t// -----------------------------------------------------------------------\n\t// Step 1: Inspect what's already in place\n\t// -----------------------------------------------------------------------\n\tconst inspectSpinner = spinner();\n\tinspectSpinner.start(\"Checking existing configuration...\");\n\tconst inspection = await inspectProject([\n\t\t{ id: \"has_app\", description: \"\", lookFor: [] },\n\t\t{ id: \"mcp_server\", description: \"\", lookFor: [] },\n\t\t{ id: \"skills\", description: \"\", lookFor: [] },\n\t\t{ id: \"connection_string\", description: \"\", lookFor: [] },\n\t\t{ id: \"project_stack\", description: \"\", lookFor: [] },\n\t\t{ id: \"migrations\", description: \"\", lookFor: [] },\n\t\t{ id: \"ide_type\", description: \"\", lookFor: [] },\n\t]);\n\tinspectSpinner.stop(dim(\"Configuration checked ✓\"));\n\n\tconst hasApp = inspection.hasApp === true;\n\tlet selectedFeatures: NeonFeature[] = [];\n\tlet selectedTemplate: BootstrapTemplate | null = null;\n\n\t// Preview mode: bootstrap from template if no app detected\n\tif (options.preview && !hasApp) {\n\t\tlet templates = FALLBACK_TEMPLATES;\n\t\ttry {\n\t\t\tconst fetched = await fetchTemplates();\n\t\t\tif (fetched && fetched.length > 0) templates = fetched;\n\t\t} catch {}\n\n\t\tconst templateResult = await select({\n\t\t\tmessage:\n\t\t\t\t\"No application detected. Would you like to scaffold a new project from a template?\",\n\t\t\toptions: [\n\t\t\t\t...templates.map((t) => ({\n\t\t\t\t\tvalue: t.id,\n\t\t\t\t\tlabel: t.title,\n\t\t\t\t\thint: t.description,\n\t\t\t\t})),\n\t\t\t\t{\n\t\t\t\t\tvalue: \"none\",\n\t\t\t\t\tlabel: \"No thanks — continue without scaffolding\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tinitialValue: templates[0]?.id ?? \"none\",\n\t\t});\n\t\tif (isCancel(templateResult)) {\n\t\t\toutro(\"Setup cancelled.\");\n\t\t\treturn;\n\t\t}\n\t\tif (templateResult !== \"none\") {\n\t\t\tselectedTemplate =\n\t\t\t\ttemplates.find((t) => t.id === templateResult) ?? null;\n\t\t\tif (selectedTemplate) {\n\t\t\t\tselectedFeatures = selectedTemplate.requires;\n\t\t\t\tconst bootstrapS = spinner();\n\t\t\t\tbootstrapS.start(\n\t\t\t\t\t`Scaffolding project from template \"${selectedTemplate.title}\"...`,\n\t\t\t\t);\n\t\t\t\ttry {\n\t\t\t\t\t// Pin @latest (and -y) so a stale globally-installed neonctl\n\t\t\t\t\t// can't be picked up by npx — bootstrap's rate-limit fix lives\n\t\t\t\t\t// in recent neonctl.\n\t\t\t\t\tawait execa(\n\t\t\t\t\t\t\"npx\",\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\"-y\",\n\t\t\t\t\t\t\t\"neonctl@latest\",\n\t\t\t\t\t\t\t\"bootstrap\",\n\t\t\t\t\t\t\t\".\",\n\t\t\t\t\t\t\t\"--template\",\n\t\t\t\t\t\t\tselectedTemplate.id,\n\t\t\t\t\t\t\t\"--force\",\n\t\t\t\t\t\t],\n\t\t\t\t\t\t{ stdio: \"pipe\", timeout: 120000 },\n\t\t\t\t\t);\n\t\t\t\t\tbootstrapS.stop(\n\t\t\t\t\t\tdim(\n\t\t\t\t\t\t\t`Scaffolded project from \"${selectedTemplate.title}\" ✓`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst msg =\n\t\t\t\t\t\terr instanceof Error ? err.message : \"Unknown error\";\n\t\t\t\t\tbootstrapS.stop(\"Failed to scaffold project\");\n\t\t\t\t\tlog.error(msg);\n\t\t\t\t\toutro(\"Setup failed.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// For brownfield flows (existing app), ask which features to enable\n\tif (!selectedTemplate && hasApp) {\n\t\tconst featuresResult = await select({\n\t\t\tmessage:\n\t\t\t\t\"Which Neon features would you like to enable for this project?\",\n\t\t\toptions: [\n\t\t\t\t{ value: \"database\", label: \"Database\" },\n\t\t\t\t{\n\t\t\t\t\tvalue: \"database,auth\",\n\t\t\t\t\tlabel: \"Database + Neon Auth (adds authentication via Neon)\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tinitialValue: \"database\",\n\t\t});\n\t\tif (isCancel(featuresResult)) {\n\t\t\toutro(\"Setup cancelled.\");\n\t\t\treturn;\n\t\t}\n\t\tselectedFeatures = (featuresResult as string).split(\n\t\t\t\",\",\n\t\t) as NeonFeature[];\n\t}\n\n\t// Write _init metadata to .neon\n\tif (selectedFeatures.length > 0) {\n\t\tconst neonPath = resolve(process.cwd(), \".neon\");\n\t\tlet existing: Record<string, unknown> = {};\n\t\tif (existsSync(neonPath)) {\n\t\t\ttry {\n\t\t\t\texisting = JSON.parse(readFileSync(neonPath, \"utf-8\"));\n\t\t\t} catch {}\n\t\t}\n\t\texisting._init = { features: selectedFeatures };\n\t\twriteFileSync(neonPath, `${JSON.stringify(existing, null, 2)}\\n`);\n\t}\n\n\tconst mcpAlready = inspection.mcpConfigured === true;\n\t// If we bootstrapped, skills come from the template\n\tconst skillsAlready =\n\t\tinspection.skillsInstalled === true || selectedTemplate !== null;\n\tconst hasNeonConnection = inspection.connectionString === true;\n\tconst needsMcp = !mcpAlready;\n\tconst needsSkills = !skillsAlready;\n\tconst needsInstall = needsMcp || needsSkills;\n\n\t// Check if .neon context file exists\n\tconst neonContextPath = resolve(process.cwd(), \".neon\");\n\tconst hasNeonContext =\n\t\texistsSync(neonContextPath) &&\n\t\t(() => {\n\t\t\ttry {\n\t\t\t\tconst content = JSON.parse(\n\t\t\t\t\treadFileSync(neonContextPath, \"utf-8\"),\n\t\t\t\t);\n\t\t\t\treturn !!content.projectId;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t})();\n\n\t// Check if Neon Auth is configured\n\tconst hasNeonAuth = (() => {\n\t\tfor (const envFile of [\".env\", \".env.local\"]) {\n\t\t\tconst envPath = resolve(process.cwd(), envFile);\n\t\t\tif (existsSync(envPath)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst content = readFileSync(envPath, \"utf-8\");\n\t\t\t\t\tif (/^NEON_AUTH_/m.test(content)) return true;\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t})();\n\n\t// Check if extension is installed for the detected editor\n\tlet extensionAlready = false;\n\tif (detectedEditor && usesExtension(detectedEditor)) {\n\t\textensionAlready = await isExtensionInstalled(detectedEditor);\n\t}\n\n\t// If tooling + database are configured, check if there's anything left to do\n\tif (mcpAlready && skillsAlready && hasNeonConnection && hasNeonContext) {\n\t\tlog.step(dim(\"Neon MCP server already configured ✓\"));\n\t\tlog.step(dim(\"Neon agent skills already installed ✓\"));\n\t\tif (extensionAlready)\n\t\t\tlog.step(dim(\"Neon editor extension installed ✓\"));\n\t\tlog.step(dim(\"Neon database connected ✓\"));\n\n\t\tif (hasNeonAuth) {\n\t\t\tlog.step(dim(\"Neon Auth configured ✓\"));\n\t\t\toutro(\n\t\t\t\tdim(\n\t\t\t\t\t\"Your project is fully configured with Neon. Nothing to do.\",\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Neon Auth not configured — ask if they want it\n\t\tconst authResult = await select({\n\t\t\tmessage:\n\t\t\t\t\"Would you like to set up Neon Auth for user authentication?\",\n\t\t\toptions: [\n\t\t\t\t{ value: \"yes\", label: \"Yes, set up Neon Auth\" },\n\t\t\t\t{ value: \"no\", label: \"No, skip for now\" },\n\t\t\t],\n\t\t\tinitialValue: \"no\",\n\t\t});\n\n\t\tif (isCancel(authResult) || authResult === \"no\") {\n\t\t\toutro(\n\t\t\t\tdim(\n\t\t\t\t\t\"Your project is configured with Neon. You can set up Neon Auth later by having your agent run: neon-init neon-auth --agent --json\",\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Read .neon for project context\n\t\tlet projectId: string | null = null;\n\t\ttry {\n\t\t\tconst neonCtx = JSON.parse(readFileSync(neonContextPath, \"utf-8\"));\n\t\t\tprojectId = neonCtx.projectId ?? null;\n\t\t} catch {}\n\n\t\tlog.step(\"Next steps\");\n\t\tconst promptLines = [\"Set up Neon Auth for this project.\"];\n\t\tif (projectId) promptLines.push(`Project ID: ${projectId}.`);\n\t\tlog.message(dim(\"Copy the following into your agent chat:\"));\n\t\tlog.message(\n\t\t\tpromptLines.map((line) => bold(neonGreenFn(line))).join(\"\\n\"),\n\t\t);\n\t\toutro(dim(\"Have feedback? Email us at feedback@neon.tech\"));\n\t\treturn;\n\t}\n\n\t// Log what's already in place\n\tif (mcpAlready) log.step(dim(\"Neon MCP server already configured ✓\"));\n\tif (skillsAlready) log.step(dim(\"Neon agent skills already installed ✓\"));\n\n\t// -----------------------------------------------------------------------\n\t// Step 3–5: Install what's missing (skip entirely if everything is configured)\n\t// -----------------------------------------------------------------------\n\tif (needsInstall) {\n\t\tconst homeDir = process.env.HOME || process.env.USERPROFILE;\n\t\tif (!homeDir) {\n\t\t\tlog.error(\"Could not determine home directory.\");\n\t\t\toutro(\"Setup failed.\");\n\t\t\treturn;\n\t\t}\n\n\t\tlet selectedEditors: Editor[];\n\t\tif (detectedEditor) {\n\t\t\tselectedEditors = [detectedEditor];\n\t\t} else {\n\t\t\tconst availableEditors = await detectAvailableEditors(homeDir);\n\t\t\tconst editorResponse = await multiselect({\n\t\t\t\tmessage: \"Which editor(s) would you like to configure?\",\n\t\t\t\toptions: ALL_CONFIGURABLE_AGENTS.map((agent) => ({\n\t\t\t\t\tvalue: agent.editor,\n\t\t\t\t\tlabel: agent.editor,\n\t\t\t\t\thint: agent.hint,\n\t\t\t\t})),\n\t\t\t\tinitialValues: availableEditors,\n\t\t\t\trequired: true,\n\t\t\t});\n\t\t\tif (isCancel(editorResponse)) {\n\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tselectedEditors = editorResponse as Editor[];\n\t\t\tif (selectedEditors.length === 0) {\n\t\t\t\tlog.warn(\"No editors selected.\");\n\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Check extension status\n\t\tconst vscodeEditors = selectedEditors.filter(usesExtension);\n\t\tlet extensionAlreadyInstalled = false;\n\t\tif (vscodeEditors.length > 0) {\n\t\t\tconst checks = await Promise.all(\n\t\t\t\tvscodeEditors.map((e) => isExtensionInstalled(e)),\n\t\t\t);\n\t\t\textensionAlreadyInstalled = checks.every(Boolean);\n\t\t\tif (extensionAlreadyInstalled) {\n\t\t\t\tlog.step(dim(\"Neon editor extension already installed ✓\"));\n\t\t\t}\n\t\t}\n\t\tlet doInstallExtension =\n\t\t\tvscodeEditors.length > 0 && !extensionAlreadyInstalled;\n\n\t\t// Build hint showing only what needs installing\n\t\tconst hintParts: string[] = [];\n\t\tif (needsMcp) hintParts.push(\"MCP server (global)\");\n\t\tif (needsSkills) hintParts.push(\"agent skills (project)\");\n\t\tif (doInstallExtension) hintParts.push(\"editor extension\");\n\n\t\t// Installation preferences\n\t\tlet mcpScope: \"global\" | \"project\" = \"global\";\n\t\tlet skillsScope: \"global\" | \"project\" = \"project\";\n\n\t\tlet modeResult: string;\n\t\twhile (true) {\n\t\t\tconst editorName = selectedEditors.join(\", \");\n\t\t\tconst result = await select({\n\t\t\t\tmessage: `Configure ${editorName} for Neon:`,\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"defaults\",\n\t\t\t\t\t\tlabel: \"Install with defaults\",\n\t\t\t\t\t\thint: hintParts.join(\", \"),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"customize\",\n\t\t\t\t\t\tlabel: \"Customize installation\",\n\t\t\t\t\t\thint: \"choose scopes and options\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"change_editor\",\n\t\t\t\t\t\tlabel: \"Configure a different editor\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tinitialValue: \"defaults\",\n\t\t\t});\n\n\t\t\tif (isCancel(result)) {\n\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (result === \"change_editor\") {\n\t\t\t\tconst availableEditors = await detectAvailableEditors(homeDir);\n\t\t\t\tconst editorResponse = await multiselect({\n\t\t\t\t\tmessage: \"Which editor(s) would you like to configure?\",\n\t\t\t\t\toptions: ALL_CONFIGURABLE_AGENTS.map((agent) => ({\n\t\t\t\t\t\tvalue: agent.editor,\n\t\t\t\t\t\tlabel: agent.editor,\n\t\t\t\t\t\thint: agent.hint,\n\t\t\t\t\t})),\n\t\t\t\t\tinitialValues: availableEditors,\n\t\t\t\t\trequired: true,\n\t\t\t\t});\n\t\t\t\tif (isCancel(editorResponse)) {\n\t\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tselectedEditors = editorResponse as Editor[];\n\t\t\t\tif (selectedEditors.length === 0) {\n\t\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmodeResult = result as string;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (modeResult === \"customize\") {\n\t\t\tif (needsMcp) {\n\t\t\t\tconst scopeResult = await select({\n\t\t\t\t\tmessage: \"Where should the Neon MCP server be configured?\",\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"global\",\n\t\t\t\t\t\t\tlabel: \"Global (available in all projects)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"project\",\n\t\t\t\t\t\t\tlabel: \"Project-level (this project only)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tif (isCancel(scopeResult)) {\n\t\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tmcpScope = scopeResult as \"global\" | \"project\";\n\t\t\t}\n\n\t\t\tif (needsSkills) {\n\t\t\t\tconst skillsScopeResult = await select({\n\t\t\t\t\tmessage: \"Where should Neon agent skills be installed?\",\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"global\",\n\t\t\t\t\t\t\tlabel: \"Global (available in all projects)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"project\",\n\t\t\t\t\t\t\tlabel: \"Project-level (this project only)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tinitialValue: \"project\",\n\t\t\t\t});\n\t\t\t\tif (isCancel(skillsScopeResult)) {\n\t\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tskillsScope = skillsScopeResult as \"global\" | \"project\";\n\t\t\t}\n\n\t\t\tif (doInstallExtension) {\n\t\t\t\tconst extResult = await confirm({\n\t\t\t\t\tmessage: `Install the Neon extension for ${vscodeEditors.join(\", \")}?`,\n\t\t\t\t});\n\t\t\t\tif (isCancel(extResult)) {\n\t\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdoInstallExtension = extResult;\n\t\t\t}\n\t\t}\n\n\t\t// Auth check before install\n\t\tconst installAuthed = await isAuthenticated();\n\t\tif (!installAuthed) {\n\t\t\tconst authS = spinner();\n\t\t\tauthS.start(\"Authenticating with Neon...\");\n\t\t\tconst authSuccess = await ensureNeonctlAuth();\n\t\t\tif (!authSuccess) {\n\t\t\t\tauthS.stop(\"Authentication failed.\");\n\t\t\t\toutro(\"Run neon-init again after signing in.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tauthS.stop(\"Authenticated.\");\n\t\t}\n\n\t\t// Ensure neonctl CLI is installed and up to date\n\t\tconst nctlS = spinner();\n\t\tnctlS.start(\"Checking neonctl CLI...\");\n\t\tconst nctlResult = await ensureNeonctl();\n\t\tswitch (nctlResult.status) {\n\t\t\tcase \"already_current\":\n\t\t\t\tnctlS.stop(\n\t\t\t\t\tdim(`neonctl CLI is up to date (v${nctlResult.version}) ✓`),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"installed\":\n\t\t\t\tnctlS.stop(\n\t\t\t\t\tdim(`Installed neonctl CLI (v${nctlResult.version}) ✓`),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"updated\":\n\t\t\t\tnctlS.stop(\n\t\t\t\t\tdim(`Updated neonctl CLI to v${nctlResult.version} ✓`),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"failed\":\n\t\t\t\tnctlS.stop(\"Failed to install neonctl CLI\");\n\t\t\t\tlog.warn(\n\t\t\t\t\t\"neonctl could not be installed automatically. The setup will continue using npx.\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// Install only what's missing\n\t\tfor (const editor of selectedEditors) {\n\t\t\tif (needsMcp) {\n\t\t\t\tconst mcpAgentId = getAddMcpAgentId(editor);\n\t\t\t\tconst mcpArgs = [\n\t\t\t\t\t\"-y\",\n\t\t\t\t\t\"add-mcp\",\n\t\t\t\t\t\"https://mcp.neon.tech/mcp\",\n\t\t\t\t\t\"-n\",\n\t\t\t\t\t\"Neon\",\n\t\t\t\t\t\"-y\",\n\t\t\t\t\t\"-a\",\n\t\t\t\t\tmcpAgentId,\n\t\t\t\t];\n\t\t\t\tif (mcpScope === \"global\") mcpArgs.splice(5, 0, \"-g\");\n\n\t\t\t\tconst mcpS = spinner();\n\t\t\t\tmcpS.start(`Installing Neon MCP server for ${editor}...`);\n\t\t\t\ttry {\n\t\t\t\t\tawait execa(\"npx\", mcpArgs, {\n\t\t\t\t\t\tstdio: \"pipe\",\n\t\t\t\t\t\ttimeout: 60000,\n\t\t\t\t\t});\n\t\t\t\t\tmcpS.stop(\n\t\t\t\t\t\tdim(\n\t\t\t\t\t\t\t`Neon MCP server configured for ${editor} (${mcpScope}) ✓`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst msg =\n\t\t\t\t\t\terr instanceof Error ? err.message : \"Unknown error\";\n\t\t\t\t\tmcpS.stop(`Failed to configure MCP server for ${editor}`);\n\t\t\t\t\tlog.error(msg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (needsSkills) {\n\t\t\t\tawait installAgentSkills([editor], { scope: skillsScope });\n\t\t\t}\n\n\t\t\tif (doInstallExtension && usesExtension(editor)) {\n\t\t\t\tconst extS = spinner();\n\t\t\t\textS.start(`Installing Neon extension for ${editor}...`);\n\t\t\t\tconst extOk = await installExtension(editor);\n\t\t\t\tif (extOk) {\n\t\t\t\t\textS.stop(dim(`Neon extension installed for ${editor} ✓`));\n\t\t\t\t} else {\n\t\t\t\t\textS.stop(\n\t\t\t\t\t\t`Extension install failed — install manually from the extensions panel.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Step 6: Done — build prompt for the agent to continue\n\t// -----------------------------------------------------------------------\n\n\t// Build the getting-started data payload (same as agent mode)\n\tconst gettingStartedData: Record<string, unknown> = {};\n\tif (hasNeonConnection) gettingStartedData.hasConnectionString = true;\n\tif (inspection.framework && inspection.framework !== \"none\")\n\t\tgettingStartedData.framework = inspection.framework;\n\tif (inspection.orm && inspection.orm !== \"none\")\n\t\tgettingStartedData.orm = inspection.orm;\n\tif (inspection.migrationTool && inspection.migrationTool !== \"none\")\n\t\tgettingStartedData.migrationTool = inspection.migrationTool;\n\tif (inspection.migrationDir && inspection.migrationDir !== \"none\")\n\t\tgettingStartedData.migrationDir = inspection.migrationDir;\n\tif (selectedFeatures.length > 0)\n\t\tgettingStartedData.features = selectedFeatures;\n\tif (options.preview) gettingStartedData.preview = true;\n\n\tconst dataJson = JSON.stringify(gettingStartedData);\n\n\t// Build a prompt for the user to paste into their agent chat\n\tconst apiHost = process.env.NEON_API_HOST;\n\tconst envPrefix = apiHost ? `NEON_API_HOST=${apiHost} ` : \"\";\n\tconst cmd = `${envPrefix}neon-init getting-started --agent --json --data '${dataJson}'`;\n\tconst cols = process.stdout.columns || 80;\n\tconst promptText = `To finish setting up Neon using Neon's agent-guided onboarding experience, have your agent run this shell command: ${cmd}`;\n\n\tlog.step(\"Next steps\");\n\tlog.message(dim(\"Copy the following into your agent chat:\"));\n\tlog.message(\n\t\twordWrap(promptText, cols)\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => bold(neonGreenFn(line)))\n\t\t\t.join(\"\\n\"),\n\t);\n\toutro(dim(\"Have feedback? Email us at feedback@neon.tech\"));\n}\n\nfunction agentIdToEditor(agentId: string): Editor | null {\n\tswitch (agentId) {\n\t\tcase \"cursor\":\n\t\t\treturn \"Cursor\";\n\t\tcase \"vscode\":\n\t\t\treturn \"VS Code\";\n\t\tcase \"claude-code\":\n\t\t\treturn \"Claude CLI\";\n\t\tcase \"windsurf\":\n\t\t\t// Windsurf not in Editor type yet — fall back to prompt\n\t\t\treturn null;\n\t\tcase \"codex\":\n\t\t\treturn \"Codex\";\n\t\tcase \"cline\":\n\t\t\treturn \"Cline\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"interactive.js","names":[],"sources":["../src/interactive.ts"],"sourcesContent":["/**\n * Interactive v2 CLI — purpose-built guided flow for humans.\n * Uses the same underlying install functions but with a clean clack-based UX.\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport {\n\tconfirm,\n\tisCancel,\n\tlog,\n\tmultiselect,\n\toutro,\n\tselect,\n\tspinner,\n} from \"@clack/prompts\";\nimport { execa } from \"execa\";\nimport { bold, dim } from \"yoctocolors\";\nimport { ALL_CONFIGURABLE_AGENTS, getAddMcpAgentId } from \"./lib/agents.js\";\nimport { ensureNeonctlAuth, isAuthenticated } from \"./lib/auth.js\";\nimport {\n\ttype BootstrapTemplate,\n\tFALLBACK_TEMPLATES,\n\tfetchTemplates,\n\ttype NeonFeature,\n} from \"./lib/bootstrap.js\";\nimport { detectAgent } from \"./lib/detect-agent.js\";\nimport { detectAvailableEditors } from \"./lib/editors.js\";\nimport {\n\tinstallExtension,\n\tisExtensionInstalled,\n\tusesExtension,\n} from \"./lib/extension.js\";\nimport { inspectProject } from \"./lib/inspect.js\";\nimport { ensureNeonctl } from \"./lib/neonctl.js\";\nimport { installAgentSkills } from \"./lib/skills.js\";\nimport type { Editor } from \"./lib/types.js\";\n\nfunction wordWrap(text: string, width: number): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.map((line) => {\n\t\t\tif (line.length <= width) return line;\n\t\t\tconst words = line.split(\" \");\n\t\t\tconst lines: string[] = [];\n\t\t\tlet current = \"\";\n\t\t\tfor (const word of words) {\n\t\t\t\tif (\n\t\t\t\t\tcurrent.length + word.length + 1 > width &&\n\t\t\t\t\tcurrent.length > 0\n\t\t\t\t) {\n\t\t\t\t\tlines.push(current);\n\t\t\t\t\tcurrent = word;\n\t\t\t\t} else {\n\t\t\t\t\tcurrent = current.length > 0 ? `${current} ${word}` : word;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (current.length > 0) lines.push(current);\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n// Patch picocolors (used by @clack/prompts) to use Neon green instead of cyan/magenta.\n// clack hardcodes picocolors.cyan() with no theme API, so this is the least invasive override.\nimport picocolors from \"picocolors\";\n\nconst neonGreenFn = (s: string) => `\\x1b[38;2;75;181;120m${s}\\x1b[39m`;\nconst originalCyan = picocolors.cyan;\nconst originalMagenta = picocolors.magenta;\n\nfunction patchClackColors(): () => void {\n\tconst pc = picocolors as unknown as Record<string, unknown>;\n\tpc.cyan = neonGreenFn;\n\tpc.magenta = neonGreenFn;\n\treturn () => {\n\t\tpc.cyan = originalCyan;\n\t\tpc.magenta = originalMagenta;\n\t};\n}\n\nexport interface InteractiveInitOptions {\n\tpreview?: boolean;\n}\n\nexport async function interactiveInit(\n\toptions: InteractiveInitOptions = {},\n): Promise<void> {\n\tconst restoreColors = patchClackColors();\n\ttry {\n\t\tawait interactiveInitInner(options);\n\t} finally {\n\t\trestoreColors();\n\t}\n}\n\nasync function interactiveInitInner(\n\toptions: InteractiveInitOptions,\n): Promise<void> {\n\tconsole.log();\n\tconsole.log(\n\t\t\"\\x1b[38;2;75;181;120m\" +\n\t\t\t[\n\t\t\t\t\" ██╗ ██╗██████╗ ██████╗ ██╗ ██╗\",\n\t\t\t\t\" ███╗ ██║██╔═══╝ ██╔═══██╗███╗ ██║\",\n\t\t\t\t\" ████╗██║██████╗ ██║ ██║████╗██║\",\n\t\t\t\t\" ██╔████║██╔═══╝ ██║ ██║██╔████║\",\n\t\t\t\t\" ██║╚███║██████╗ ╚██████╔╝██║╚███║\",\n\t\t\t\t\" ╚═╝ ╚══╝╚═════╝ ╚═════╝ ╚═╝ ╚══╝\",\n\t\t\t].join(\"\\n\") +\n\t\t\t\"\\x1b[0m\",\n\t);\n\tconsole.log(\n\t\tdim(\n\t\t\twordWrap(\n\t\t\t\t\"\\nLet's get your project set up with Neon. We'll install the MCP server, agent skills, and IDE extension, then connect your app to a database.\\n\",\n\t\t\t\tprocess.stdout.columns || 80,\n\t\t\t),\n\t\t),\n\t);\n\n\tconst detectedAgentId = detectAgent();\n\tconst detectedEditor = detectedAgentId\n\t\t? agentIdToEditor(detectedAgentId)\n\t\t: null;\n\n\t// -----------------------------------------------------------------------\n\t// Step 1: Inspect what's already in place\n\t// -----------------------------------------------------------------------\n\tconst inspectSpinner = spinner();\n\tinspectSpinner.start(\"Checking existing configuration...\");\n\tconst inspection = await inspectProject([\n\t\t{ id: \"has_app\", description: \"\", lookFor: [] },\n\t\t{ id: \"mcp_server\", description: \"\", lookFor: [] },\n\t\t{ id: \"skills\", description: \"\", lookFor: [] },\n\t\t{ id: \"connection_string\", description: \"\", lookFor: [] },\n\t\t{ id: \"project_stack\", description: \"\", lookFor: [] },\n\t\t{ id: \"migrations\", description: \"\", lookFor: [] },\n\t\t{ id: \"ide_type\", description: \"\", lookFor: [] },\n\t]);\n\tinspectSpinner.stop(dim(\"Configuration checked ✓\"));\n\n\tconst hasApp = inspection.hasApp === true;\n\tlet selectedFeatures: NeonFeature[] = [];\n\tlet selectedTemplate: BootstrapTemplate | null = null;\n\n\t// Preview mode: bootstrap from template if no app detected\n\tif (options.preview && !hasApp) {\n\t\tlet templates = FALLBACK_TEMPLATES;\n\t\ttry {\n\t\t\tconst fetched = await fetchTemplates();\n\t\t\tif (fetched && fetched.length > 0) templates = fetched;\n\t\t} catch {}\n\n\t\tconst templateResult = await select({\n\t\t\tmessage:\n\t\t\t\t\"No application detected. Would you like to scaffold a new project from a template?\",\n\t\t\toptions: [\n\t\t\t\t...templates.map((t) => ({\n\t\t\t\t\tvalue: t.id,\n\t\t\t\t\tlabel: t.title,\n\t\t\t\t\thint: t.description,\n\t\t\t\t})),\n\t\t\t\t{\n\t\t\t\t\tvalue: \"none\",\n\t\t\t\t\tlabel: \"No thanks — continue without scaffolding\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tinitialValue: templates[0]?.id ?? \"none\",\n\t\t});\n\t\tif (isCancel(templateResult)) {\n\t\t\toutro(\"Setup cancelled.\");\n\t\t\treturn;\n\t\t}\n\t\tif (templateResult !== \"none\") {\n\t\t\tselectedTemplate =\n\t\t\t\ttemplates.find((t) => t.id === templateResult) ?? null;\n\t\t\tif (selectedTemplate) {\n\t\t\t\tselectedFeatures = selectedTemplate.requires;\n\t\t\t\tconst bootstrapS = spinner();\n\t\t\t\tbootstrapS.start(\n\t\t\t\t\t`Scaffolding project from template \"${selectedTemplate.title}\"...`,\n\t\t\t\t);\n\t\t\t\ttry {\n\t\t\t\t\t// Pin @latest (and -y) so a stale globally-installed neonctl\n\t\t\t\t\t// can't be picked up by npx — bootstrap's rate-limit fix lives\n\t\t\t\t\t// in recent neonctl.\n\t\t\t\t\tawait execa(\n\t\t\t\t\t\t\"npx\",\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\"-y\",\n\t\t\t\t\t\t\t\"neonctl@latest\",\n\t\t\t\t\t\t\t\"bootstrap\",\n\t\t\t\t\t\t\t\".\",\n\t\t\t\t\t\t\t\"--template\",\n\t\t\t\t\t\t\tselectedTemplate.id,\n\t\t\t\t\t\t\t\"--force\",\n\t\t\t\t\t\t],\n\t\t\t\t\t\t{ stdio: \"pipe\", timeout: 120000 },\n\t\t\t\t\t);\n\t\t\t\t\tbootstrapS.stop(\n\t\t\t\t\t\tdim(\n\t\t\t\t\t\t\t`Scaffolded project from \"${selectedTemplate.title}\" ✓`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst msg =\n\t\t\t\t\t\terr instanceof Error ? err.message : \"Unknown error\";\n\t\t\t\t\tbootstrapS.stop(\"Failed to scaffold project\");\n\t\t\t\t\tlog.error(msg);\n\t\t\t\t\toutro(\"Setup failed.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// For brownfield flows (existing app), ask which features to enable\n\tif (!selectedTemplate && hasApp) {\n\t\tconst featuresResult = await select({\n\t\t\tmessage:\n\t\t\t\t\"Which Neon features would you like to enable for this project?\",\n\t\t\toptions: [\n\t\t\t\t{ value: \"database\", label: \"Database\" },\n\t\t\t\t{\n\t\t\t\t\tvalue: \"database,auth\",\n\t\t\t\t\tlabel: \"Database + Neon Auth (adds authentication via Neon)\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tinitialValue: \"database\",\n\t\t});\n\t\tif (isCancel(featuresResult)) {\n\t\t\toutro(\"Setup cancelled.\");\n\t\t\treturn;\n\t\t}\n\t\tselectedFeatures = (featuresResult as string).split(\n\t\t\t\",\",\n\t\t) as NeonFeature[];\n\t}\n\n\t// Write _init metadata to .neon\n\tif (selectedFeatures.length > 0) {\n\t\tconst neonPath = resolve(process.cwd(), \".neon\");\n\t\tlet existing: Record<string, unknown> = {};\n\t\tif (existsSync(neonPath)) {\n\t\t\ttry {\n\t\t\t\texisting = JSON.parse(readFileSync(neonPath, \"utf-8\"));\n\t\t\t} catch {}\n\t\t}\n\t\texisting._init = { features: selectedFeatures };\n\t\twriteFileSync(neonPath, `${JSON.stringify(existing, null, 2)}\\n`);\n\t}\n\n\tconst mcpAlready = inspection.mcpConfigured === true;\n\t// If we bootstrapped, skills come from the template\n\tconst skillsAlready =\n\t\tinspection.skillsInstalled === true || selectedTemplate !== null;\n\tconst hasNeonConnection = inspection.connectionString === true;\n\tlet needsMcp = !mcpAlready;\n\tconst needsSkills = !skillsAlready;\n\tconst needsInstall = needsMcp || needsSkills;\n\n\t// Check if .neon context file exists\n\tconst neonContextPath = resolve(process.cwd(), \".neon\");\n\tconst hasNeonContext =\n\t\texistsSync(neonContextPath) &&\n\t\t(() => {\n\t\t\ttry {\n\t\t\t\tconst content = JSON.parse(\n\t\t\t\t\treadFileSync(neonContextPath, \"utf-8\"),\n\t\t\t\t);\n\t\t\t\treturn !!content.projectId;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t})();\n\n\t// Check if Neon Auth is configured\n\tconst hasNeonAuth = (() => {\n\t\tfor (const envFile of [\".env\", \".env.local\"]) {\n\t\t\tconst envPath = resolve(process.cwd(), envFile);\n\t\t\tif (existsSync(envPath)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst content = readFileSync(envPath, \"utf-8\");\n\t\t\t\t\tif (/^NEON_AUTH_/m.test(content)) return true;\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t})();\n\n\t// Check if extension is installed for the detected editor\n\tlet extensionAlready = false;\n\tif (detectedEditor && usesExtension(detectedEditor)) {\n\t\textensionAlready = await isExtensionInstalled(detectedEditor);\n\t}\n\n\t// If tooling + database are configured, check if there's anything left to do\n\tif (mcpAlready && skillsAlready && hasNeonConnection && hasNeonContext) {\n\t\tlog.step(dim(\"Neon MCP server already configured ✓\"));\n\t\tlog.step(dim(\"Neon agent skills already installed ✓\"));\n\t\tif (extensionAlready)\n\t\t\tlog.step(dim(\"Neon editor extension installed ✓\"));\n\t\tlog.step(dim(\"Neon database connected ✓\"));\n\n\t\tif (hasNeonAuth) {\n\t\t\tlog.step(dim(\"Neon Auth configured ✓\"));\n\t\t\toutro(\n\t\t\t\tdim(\n\t\t\t\t\t\"Your project is fully configured with Neon. Nothing to do.\",\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Neon Auth not configured — ask if they want it\n\t\tconst authResult = await select({\n\t\t\tmessage:\n\t\t\t\t\"Would you like to set up Neon Auth for user authentication?\",\n\t\t\toptions: [\n\t\t\t\t{ value: \"yes\", label: \"Yes, set up Neon Auth\" },\n\t\t\t\t{ value: \"no\", label: \"No, skip for now\" },\n\t\t\t],\n\t\t\tinitialValue: \"no\",\n\t\t});\n\n\t\tif (isCancel(authResult) || authResult === \"no\") {\n\t\t\toutro(\n\t\t\t\tdim(\n\t\t\t\t\t`Your project is configured with Neon. You can set up Neon Auth later by having your agent run: neonctl init --agent --json --data '{\"step\":\"neon-auth\"}'`,\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Read .neon for project context\n\t\tlet projectId: string | null = null;\n\t\ttry {\n\t\t\tconst neonCtx = JSON.parse(readFileSync(neonContextPath, \"utf-8\"));\n\t\t\tprojectId = neonCtx.projectId ?? null;\n\t\t} catch {}\n\n\t\tlog.step(\"Next steps\");\n\t\tconst promptLines = [\"Set up Neon Auth for this project.\"];\n\t\tif (projectId) promptLines.push(`Project ID: ${projectId}.`);\n\t\tlog.message(dim(\"Copy the following into your agent chat:\"));\n\t\tlog.message(\n\t\t\tpromptLines.map((line) => bold(neonGreenFn(line))).join(\"\\n\"),\n\t\t);\n\t\toutro(dim(\"Have feedback? Email us at feedback@neon.tech\"));\n\t\treturn;\n\t}\n\n\t// Log what's already in place\n\tif (mcpAlready) log.step(dim(\"Neon MCP server already configured ✓\"));\n\tif (skillsAlready) log.step(dim(\"Neon agent skills already installed ✓\"));\n\n\t// -----------------------------------------------------------------------\n\t// Step 3–5: Install what's missing (skip entirely if everything is configured)\n\t// -----------------------------------------------------------------------\n\tif (needsInstall) {\n\t\tconst homeDir = process.env.HOME || process.env.USERPROFILE;\n\t\tif (!homeDir) {\n\t\t\tlog.error(\"Could not determine home directory.\");\n\t\t\toutro(\"Setup failed.\");\n\t\t\treturn;\n\t\t}\n\n\t\tlet selectedEditors: Editor[];\n\t\tif (detectedEditor) {\n\t\t\tselectedEditors = [detectedEditor];\n\t\t} else {\n\t\t\tconst availableEditors = await detectAvailableEditors(homeDir);\n\t\t\tconst editorResponse = await multiselect({\n\t\t\t\tmessage: \"Which editor(s) would you like to configure?\",\n\t\t\t\toptions: ALL_CONFIGURABLE_AGENTS.map((agent) => ({\n\t\t\t\t\tvalue: agent.editor,\n\t\t\t\t\tlabel: agent.editor,\n\t\t\t\t\thint: agent.hint,\n\t\t\t\t})),\n\t\t\t\tinitialValues: availableEditors,\n\t\t\t\trequired: true,\n\t\t\t});\n\t\t\tif (isCancel(editorResponse)) {\n\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tselectedEditors = editorResponse as Editor[];\n\t\t\tif (selectedEditors.length === 0) {\n\t\t\t\tlog.warn(\"No editors selected.\");\n\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Check extension status\n\t\tconst vscodeEditors = selectedEditors.filter(usesExtension);\n\t\tlet extensionAlreadyInstalled = false;\n\t\tif (vscodeEditors.length > 0) {\n\t\t\tconst checks = await Promise.all(\n\t\t\t\tvscodeEditors.map((e) => isExtensionInstalled(e)),\n\t\t\t);\n\t\t\textensionAlreadyInstalled = checks.every(Boolean);\n\t\t\tif (extensionAlreadyInstalled) {\n\t\t\t\tlog.step(dim(\"Neon editor extension already installed ✓\"));\n\t\t\t}\n\t\t}\n\t\tlet doInstallExtension =\n\t\t\tvscodeEditors.length > 0 && !extensionAlreadyInstalled;\n\n\t\t// Build hint showing only what needs installing\n\t\tconst hintParts: string[] = [];\n\t\tif (needsMcp) hintParts.push(\"MCP server (global)\");\n\t\tif (needsSkills) hintParts.push(\"agent skills (project)\");\n\t\tif (doInstallExtension) hintParts.push(\"editor extension\");\n\n\t\t// Installation preferences\n\t\tlet mcpScope: \"global\" | \"project\" | \"none\" = \"global\";\n\t\tlet skillsScope: \"global\" | \"project\" = \"project\";\n\n\t\tlet modeResult: string;\n\t\twhile (true) {\n\t\t\tconst editorName = selectedEditors.join(\", \");\n\t\t\tconst result = await select({\n\t\t\t\tmessage: `Configure ${editorName} for Neon:`,\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"defaults\",\n\t\t\t\t\t\tlabel: \"Install with defaults\",\n\t\t\t\t\t\thint: hintParts.join(\", \"),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"customize\",\n\t\t\t\t\t\tlabel: \"Customize installation\",\n\t\t\t\t\t\thint: \"choose scopes and options\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"change_editor\",\n\t\t\t\t\t\tlabel: \"Configure a different editor\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tinitialValue: \"defaults\",\n\t\t\t});\n\n\t\t\tif (isCancel(result)) {\n\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (result === \"change_editor\") {\n\t\t\t\tconst availableEditors = await detectAvailableEditors(homeDir);\n\t\t\t\tconst editorResponse = await multiselect({\n\t\t\t\t\tmessage: \"Which editor(s) would you like to configure?\",\n\t\t\t\t\toptions: ALL_CONFIGURABLE_AGENTS.map((agent) => ({\n\t\t\t\t\t\tvalue: agent.editor,\n\t\t\t\t\t\tlabel: agent.editor,\n\t\t\t\t\t\thint: agent.hint,\n\t\t\t\t\t})),\n\t\t\t\t\tinitialValues: availableEditors,\n\t\t\t\t\trequired: true,\n\t\t\t\t});\n\t\t\t\tif (isCancel(editorResponse)) {\n\t\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tselectedEditors = editorResponse as Editor[];\n\t\t\t\tif (selectedEditors.length === 0) {\n\t\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmodeResult = result as string;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (modeResult === \"customize\") {\n\t\t\tif (needsMcp) {\n\t\t\t\tconst scopeResult = await select({\n\t\t\t\t\tmessage: \"Where should the Neon MCP server be configured?\",\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"global\",\n\t\t\t\t\t\t\tlabel: \"Global (available in all projects)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"project\",\n\t\t\t\t\t\t\tlabel: \"Project-level (this project only)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"none\",\n\t\t\t\t\t\t\tlabel: \"Skip — do not install the MCP server\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tif (isCancel(scopeResult)) {\n\t\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tmcpScope = scopeResult as \"global\" | \"project\" | \"none\";\n\t\t\t\tif (mcpScope === \"none\") needsMcp = false;\n\t\t\t}\n\n\t\t\tif (needsSkills) {\n\t\t\t\tconst skillsScopeResult = await select({\n\t\t\t\t\tmessage: \"Where should Neon agent skills be installed?\",\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"global\",\n\t\t\t\t\t\t\tlabel: \"Global (available in all projects)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"project\",\n\t\t\t\t\t\t\tlabel: \"Project-level (this project only)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tinitialValue: \"project\",\n\t\t\t\t});\n\t\t\t\tif (isCancel(skillsScopeResult)) {\n\t\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tskillsScope = skillsScopeResult as \"global\" | \"project\";\n\t\t\t}\n\n\t\t\tif (doInstallExtension) {\n\t\t\t\tconst extResult = await confirm({\n\t\t\t\t\tmessage: `Install the Neon extension for ${vscodeEditors.join(\", \")}?`,\n\t\t\t\t});\n\t\t\t\tif (isCancel(extResult)) {\n\t\t\t\t\toutro(\"Setup cancelled.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdoInstallExtension = extResult;\n\t\t\t}\n\t\t}\n\n\t\t// Auth check before install\n\t\tconst installAuthed = await isAuthenticated();\n\t\tif (!installAuthed) {\n\t\t\tconst authS = spinner();\n\t\t\tauthS.start(\"Authenticating with Neon...\");\n\t\t\tconst authSuccess = await ensureNeonctlAuth();\n\t\t\tif (!authSuccess) {\n\t\t\t\tauthS.stop(\"Authentication failed.\");\n\t\t\t\toutro(\"Run neon-init again after signing in.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tauthS.stop(\"Authenticated.\");\n\t\t}\n\n\t\t// Ensure neonctl CLI is installed and up to date\n\t\tconst nctlS = spinner();\n\t\tnctlS.start(\"Checking neonctl CLI...\");\n\t\tconst nctlResult = await ensureNeonctl();\n\t\tswitch (nctlResult.status) {\n\t\t\tcase \"already_current\":\n\t\t\t\tnctlS.stop(\n\t\t\t\t\tdim(`neonctl CLI is up to date (v${nctlResult.version}) ✓`),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"installed\":\n\t\t\t\tnctlS.stop(\n\t\t\t\t\tdim(`Installed neonctl CLI (v${nctlResult.version}) ✓`),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"updated\":\n\t\t\t\tnctlS.stop(\n\t\t\t\t\tdim(`Updated neonctl CLI to v${nctlResult.version} ✓`),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"failed\":\n\t\t\t\tnctlS.stop(\"Failed to install neonctl CLI\");\n\t\t\t\tlog.warn(\n\t\t\t\t\t\"neonctl could not be installed automatically. The setup will continue using npx.\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// Install only what's missing\n\t\tfor (const editor of selectedEditors) {\n\t\t\tif (needsMcp) {\n\t\t\t\tconst mcpAgentId = getAddMcpAgentId(editor);\n\t\t\t\tconst mcpArgs = [\n\t\t\t\t\t\"-y\",\n\t\t\t\t\t\"add-mcp\",\n\t\t\t\t\t\"https://mcp.neon.tech/mcp\",\n\t\t\t\t\t\"-n\",\n\t\t\t\t\t\"Neon\",\n\t\t\t\t\t\"-y\",\n\t\t\t\t\t\"-a\",\n\t\t\t\t\tmcpAgentId,\n\t\t\t\t];\n\t\t\t\tif (mcpScope === \"global\") mcpArgs.splice(5, 0, \"-g\");\n\n\t\t\t\tconst mcpS = spinner();\n\t\t\t\tmcpS.start(`Installing Neon MCP server for ${editor}...`);\n\t\t\t\ttry {\n\t\t\t\t\tawait execa(\"npx\", mcpArgs, {\n\t\t\t\t\t\tstdio: \"pipe\",\n\t\t\t\t\t\ttimeout: 60000,\n\t\t\t\t\t});\n\t\t\t\t\tmcpS.stop(\n\t\t\t\t\t\tdim(\n\t\t\t\t\t\t\t`Neon MCP server configured for ${editor} (${mcpScope}) ✓`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst msg =\n\t\t\t\t\t\terr instanceof Error ? err.message : \"Unknown error\";\n\t\t\t\t\tmcpS.stop(`Failed to configure MCP server for ${editor}`);\n\t\t\t\t\tlog.error(msg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (needsSkills) {\n\t\t\t\tawait installAgentSkills([editor], { scope: skillsScope });\n\t\t\t}\n\n\t\t\tif (doInstallExtension && usesExtension(editor)) {\n\t\t\t\tconst extS = spinner();\n\t\t\t\textS.start(`Installing Neon extension for ${editor}...`);\n\t\t\t\tconst extOk = await installExtension(editor);\n\t\t\t\tif (extOk) {\n\t\t\t\t\textS.stop(dim(`Neon extension installed for ${editor} ✓`));\n\t\t\t\t} else {\n\t\t\t\t\textS.stop(\n\t\t\t\t\t\t`Extension install failed — install manually from the extensions panel.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Step 6: Done — build prompt for the agent to continue\n\t// -----------------------------------------------------------------------\n\n\t// Build the getting-started data payload (same as agent mode)\n\tconst gettingStartedData: Record<string, unknown> = {};\n\tif (hasNeonConnection) gettingStartedData.hasConnectionString = true;\n\tif (inspection.framework && inspection.framework !== \"none\")\n\t\tgettingStartedData.framework = inspection.framework;\n\tif (inspection.orm && inspection.orm !== \"none\")\n\t\tgettingStartedData.orm = inspection.orm;\n\tif (inspection.migrationTool && inspection.migrationTool !== \"none\")\n\t\tgettingStartedData.migrationTool = inspection.migrationTool;\n\tif (inspection.migrationDir && inspection.migrationDir !== \"none\")\n\t\tgettingStartedData.migrationDir = inspection.migrationDir;\n\tif (selectedFeatures.length > 0)\n\t\tgettingStartedData.features = selectedFeatures;\n\tif (options.preview) gettingStartedData.preview = true;\n\n\t// Build a prompt for the user to paste into their agent chat\n\tconst cmd = `neonctl init --agent --json --data '${JSON.stringify({ step: \"getting-started\", ...gettingStartedData })}'`;\n\t// Account for clack's \"│ \" prefix (3 chars) when wrapping\n\tconst cols = (process.stdout.columns || 80) - 3;\n\tconst promptText = `To finish setting up Neon using Neon's agent-guided onboarding experience, have your agent run this shell command: ${cmd}`;\n\n\tlog.step(\"Next steps\");\n\tlog.message(dim(\"Copy the following into your agent chat:\"));\n\tlog.message(\n\t\twordWrap(promptText, cols)\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => bold(neonGreenFn(line)))\n\t\t\t.join(\"\\n\"),\n\t);\n\toutro(dim(\"Have feedback? Email us at feedback@neon.tech\"));\n}\n\nfunction agentIdToEditor(agentId: string): Editor | null {\n\tswitch (agentId) {\n\t\tcase \"cursor\":\n\t\t\treturn \"Cursor\";\n\t\tcase \"vscode\":\n\t\t\treturn \"VS Code\";\n\t\tcase \"claude-code\":\n\t\t\treturn \"Claude CLI\";\n\t\tcase \"windsurf\":\n\t\t\t// Windsurf not in Editor type yet — fall back to prompt\n\t\t\treturn null;\n\t\tcase \"codex\":\n\t\t\treturn \"Codex\";\n\t\tcase \"cline\":\n\t\t\treturn \"Cline\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n"],"mappings":"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"}
|
package/dist/lib/auth.js
CHANGED
|
@@ -25,7 +25,7 @@ async function ensureNeonctlAuth(options) {
|
|
|
25
25
|
return true;
|
|
26
26
|
} catch (error) {
|
|
27
27
|
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
28
|
-
if (!quiet) if (msg.includes("interactive auth") || msg.includes("CI")) log.error("Auth requires an interactive terminal. Run
|
|
28
|
+
if (!quiet) if (msg.includes("interactive auth") || msg.includes("CI")) log.error("Auth requires an interactive terminal. Run neonctl init in your system terminal (outside the chat) to sign in.");
|
|
29
29
|
else log.error(`Authentication failed: ${msg}`);
|
|
30
30
|
return false;
|
|
31
31
|
}
|
|
@@ -62,7 +62,7 @@ async function createApiKeyFromNeonctl(options) {
|
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
64
64
|
const keyName = `neonctl-init-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, -5)}`;
|
|
65
|
-
const apiBase = process.env.NEON_API_HOST ? `${process.env.NEON_API_HOST.
|
|
65
|
+
const apiBase = process.env.NEON_API_HOST ? `${new URL(process.env.NEON_API_HOST).origin}/api/v2` : "https://console.neon.tech/api/v2";
|
|
66
66
|
const response = await fetch(`${apiBase}/api_keys`, {
|
|
67
67
|
method: "POST",
|
|
68
68
|
headers: {
|
package/dist/lib/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","names":[],"sources":["../../src/lib/auth.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { log } from \"@clack/prompts\";\nimport { execa } from \"execa\";\n\nexport interface AuthOptions {\n\tjson?: boolean;\n}\n\n/**\n * Ensures neonctl is authenticated by running a command that triggers auth if needed\n * This will automatically start the OAuth flow if the user isn't already authenticated\n */\nexport async function ensureNeonctlAuth(\n\toptions?: AuthOptions,\n): Promise<boolean> {\n\tconst quiet = options?.json === true;\n\n\t// If already authenticated (e.g. ran in a terminal before), we can proceed\n\tconst existingToken = await getNeonctlAccessToken();\n\tif (existingToken) return true;\n\n\ttry {\n\t\t// Use execa to authenticate with neonctl\n\t\tawait execa(\"npx\", [\"-y\", \"neonctl\", \"me\"], {\n\t\t\t// Shows OAuth URL and prompts to the user\n\t\t\tstdio: \"inherit\",\n\t\t\t// Unset CI so neonctl doesn't refuse to open the browser (e.g. when run from agent chat)\n\t\t\tenv: { ...process.env, CI: undefined },\n\t\t});\n\t\treturn true;\n\t} catch (error) {\n\t\tconst msg = error instanceof Error ? error.message : \"Unknown error\";\n\t\tif (!quiet) {\n\t\t\tif (msg.includes(\"interactive auth\") || msg.includes(\"CI\")) {\n\t\t\t\tlog.error(\n\t\t\t\t\t\"Auth requires an interactive terminal. Run
|
|
1
|
+
{"version":3,"file":"auth.js","names":[],"sources":["../../src/lib/auth.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { log } from \"@clack/prompts\";\nimport { execa } from \"execa\";\n\nexport interface AuthOptions {\n\tjson?: boolean;\n}\n\n/**\n * Ensures neonctl is authenticated by running a command that triggers auth if needed\n * This will automatically start the OAuth flow if the user isn't already authenticated\n */\nexport async function ensureNeonctlAuth(\n\toptions?: AuthOptions,\n): Promise<boolean> {\n\tconst quiet = options?.json === true;\n\n\t// If already authenticated (e.g. ran in a terminal before), we can proceed\n\tconst existingToken = await getNeonctlAccessToken();\n\tif (existingToken) return true;\n\n\ttry {\n\t\t// Use execa to authenticate with neonctl\n\t\tawait execa(\"npx\", [\"-y\", \"neonctl\", \"me\"], {\n\t\t\t// Shows OAuth URL and prompts to the user\n\t\t\tstdio: \"inherit\",\n\t\t\t// Unset CI so neonctl doesn't refuse to open the browser (e.g. when run from agent chat)\n\t\t\tenv: { ...process.env, CI: undefined },\n\t\t});\n\t\treturn true;\n\t} catch (error) {\n\t\tconst msg = error instanceof Error ? error.message : \"Unknown error\";\n\t\tif (!quiet) {\n\t\t\tif (msg.includes(\"interactive auth\") || msg.includes(\"CI\")) {\n\t\t\t\tlog.error(\n\t\t\t\t\t\"Auth requires an interactive terminal. Run neonctl init in your system terminal (outside the chat) to sign in.\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tlog.error(`Authentication failed: ${msg}`);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n\n/**\n * Checks whether neonctl has stored OAuth credentials.\n */\nexport async function isAuthenticated(): Promise<boolean> {\n\tconst token = await getNeonctlAccessToken();\n\treturn token !== null;\n}\n\n/**\n * Gets the OAuth access token from neonctl's stored credentials\n */\nasync function getNeonctlAccessToken(): Promise<string | null> {\n\ttry {\n\t\tconst homeDir = process.env.HOME || process.env.USERPROFILE;\n\t\tif (!homeDir) return null;\n\n\t\tconst credentialsPath = resolve(\n\t\t\thomeDir,\n\t\t\t\".config\",\n\t\t\t\"neonctl\",\n\t\t\t\"credentials.json\",\n\t\t);\n\t\tif (!existsSync(credentialsPath)) return null;\n\n\t\tconst credentials = JSON.parse(readFileSync(credentialsPath, \"utf-8\"));\n\t\treturn credentials.access_token || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Creates an API key using the Neon API with the OAuth token from neonctl\n */\nexport async function createApiKeyFromNeonctl(\n\toptions?: AuthOptions,\n): Promise<string | null> {\n\tconst quiet = options?.json === true;\n\n\ttry {\n\t\tconst accessToken = await getNeonctlAccessToken();\n\t\tif (!accessToken) {\n\t\t\tif (!quiet) log.error(\"Could not find OAuth token from neonctl\");\n\t\t\treturn null;\n\t\t}\n\n\t\t// Generate a unique key name with timestamp\n\t\tconst timestamp = new Date()\n\t\t\t.toISOString()\n\t\t\t.replace(/[:.]/g, \"-\")\n\t\t\t.slice(0, -5); // e.g., 2024-10-08T15-30-45\n\t\tconst keyName = `neonctl-init-${timestamp}`;\n\n\t\t// Call Neon API to create an API key\n\t\tconst apiBase = process.env.NEON_API_HOST\n\t\t\t? `${new URL(process.env.NEON_API_HOST).origin}/api/v2`\n\t\t\t: \"https://console.neon.tech/api/v2\";\n\t\tconst response = await fetch(`${apiBase}/api_keys`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tkey_name: keyName,\n\t\t\t}),\n\t\t\tsignal: AbortSignal.timeout(30000),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text();\n\t\t\tif (!quiet)\n\t\t\t\tlog.error(\n\t\t\t\t\t`Failed to create API key: ${response.status} ${errorText}`,\n\t\t\t\t);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst data = await response.json();\n\t\treturn data.key || null;\n\t} catch (error) {\n\t\tif (!quiet)\n\t\t\tlog.error(\n\t\t\t\t`Failed to create API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n\t\t\t);\n\t\treturn null;\n\t}\n}\n"],"mappings":";;;;;;;;;AAaA,eAAsB,kBACrB,SACmB;CACnB,MAAM,QAAQ,SAAS,SAAS;CAIhC,IAAI,MADwB,sBAAsB,GAC/B,OAAO;CAE1B,IAAI;EAEH,MAAM,MAAM,OAAO;GAAC;GAAM;GAAW;EAAI,GAAG;GAE3C,OAAO;GAEP,KAAK;IAAE,GAAG,QAAQ;IAAK,IAAI,KAAA;GAAU;EACtC,CAAC;EACD,OAAO;CACR,SAAS,OAAO;EACf,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU;EACrD,IAAI,CAAC,OACJ,IAAI,IAAI,SAAS,kBAAkB,KAAK,IAAI,SAAS,IAAI,GACxD,IAAI,MACH,gHACD;OAEA,IAAI,MAAM,0BAA0B,KAAK;EAG3C,OAAO;CACR;AACD;;;;AAKA,eAAsB,kBAAoC;CAEzD,OAAO,MADa,sBAAsB,MACzB;AAClB;;;;AAKA,eAAe,wBAAgD;CAC9D,IAAI;EACH,MAAM,UAAU,QAAQ,IAAI,QAAQ,QAAQ,IAAI;EAChD,IAAI,CAAC,SAAS,OAAO;EAErB,MAAM,kBAAkB,QACvB,SACA,WACA,WACA,kBACD;EACA,IAAI,CAAC,WAAW,eAAe,GAAG,OAAO;EAGzC,OADoB,KAAK,MAAM,aAAa,iBAAiB,OAAO,CACnD,CAAC,CAAC,gBAAgB;CACpC,QAAQ;EACP,OAAO;CACR;AACD;;;;AAKA,eAAsB,wBACrB,SACyB;CACzB,MAAM,QAAQ,SAAS,SAAS;CAEhC,IAAI;EACH,MAAM,cAAc,MAAM,sBAAsB;EAChD,IAAI,CAAC,aAAa;GACjB,IAAI,CAAC,OAAO,IAAI,MAAM,yCAAyC;GAC/D,OAAO;EACR;EAOA,MAAM,UAAU,iCAJE,IAAI,KAAK,EAAA,CACzB,YAAY,CAAC,CACb,QAAQ,SAAS,GAAG,CAAC,CACrB,MAAM,GAAG,EAC6B;EAGxC,MAAM,UAAU,QAAQ,IAAI,gBACzB,GAAG,IAAI,IAAI,QAAQ,IAAI,aAAa,CAAC,CAAC,OAAO,WAC7C;EACH,MAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,YAAY;GACnD,QAAQ;GACR,SAAS;IACR,eAAe,UAAU;IACzB,gBAAgB;GACjB;GACA,MAAM,KAAK,UAAU,EACpB,UAAU,QACX,CAAC;GACD,QAAQ,YAAY,QAAQ,GAAK;EAClC,CAAC;EAED,IAAI,CAAC,SAAS,IAAI;GACjB,MAAM,YAAY,MAAM,SAAS,KAAK;GACtC,IAAI,CAAC,OACJ,IAAI,MACH,6BAA6B,SAAS,OAAO,GAAG,WACjD;GACD,OAAO;EACR;EAGA,QAAO,MADY,SAAS,KAAK,EAAA,CACrB,OAAO;CACpB,SAAS,OAAO;EACf,IAAI,CAAC,OACJ,IAAI,MACH,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,iBACvE;EACD,OAAO;CACR;AACD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/lib/enrich-output.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Walks a phase response object and:
|
|
4
|
+
* 1. Replaces `args` arrays with `command` strings (neonctl init --data format)
|
|
5
|
+
* 2. Renames `run_neon_init` → `run_shell_command`
|
|
6
|
+
* 3. Adds a description to finalize steps
|
|
7
|
+
*/
|
|
8
|
+
declare function enrichResponse(obj: unknown): unknown;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { enrichResponse };
|
|
11
|
+
//# sourceMappingURL=enrich-output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrich-output.d.ts","names":[],"sources":["../../src/lib/enrich-output.ts"],"mappings":";;AA8CA;;;;;iBAAgB,cAAA"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//#region src/lib/enrich-output.ts
|
|
2
|
+
/**
|
|
3
|
+
* Converts neon-init args (e.g. ["neon-auth", "--json", "--setup"]) to a
|
|
4
|
+
* neonctl init --data command using the step routing pattern.
|
|
5
|
+
*/
|
|
6
|
+
function argsToCommand(args) {
|
|
7
|
+
const data = {};
|
|
8
|
+
let i = 0;
|
|
9
|
+
if (args.length > 0 && !args[0].startsWith("-")) {
|
|
10
|
+
data.step = args[0];
|
|
11
|
+
i = 1;
|
|
12
|
+
}
|
|
13
|
+
while (i < args.length) {
|
|
14
|
+
const arg = args[i];
|
|
15
|
+
if (arg.startsWith("--")) {
|
|
16
|
+
const key = arg.slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
17
|
+
if (key === "json") {
|
|
18
|
+
i += 1;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const next = args[i + 1];
|
|
22
|
+
if (next !== void 0 && !next.startsWith("-")) {
|
|
23
|
+
data[key] = next;
|
|
24
|
+
i += 2;
|
|
25
|
+
} else {
|
|
26
|
+
data[key] = true;
|
|
27
|
+
i += 1;
|
|
28
|
+
}
|
|
29
|
+
} else i += 1;
|
|
30
|
+
}
|
|
31
|
+
return `neonctl init --agent --json --data '${JSON.stringify(data)}'`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Walks a phase response object and:
|
|
35
|
+
* 1. Replaces `args` arrays with `command` strings (neonctl init --data format)
|
|
36
|
+
* 2. Renames `run_neon_init` → `run_shell_command`
|
|
37
|
+
* 3. Adds a description to finalize steps
|
|
38
|
+
*/
|
|
39
|
+
function enrichResponse(obj) {
|
|
40
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
41
|
+
if (Array.isArray(obj)) return obj.map(enrichResponse);
|
|
42
|
+
const record = obj;
|
|
43
|
+
const result = {};
|
|
44
|
+
for (const [key, value] of Object.entries(record)) result[key] = enrichResponse(value);
|
|
45
|
+
if (Array.isArray(result.args)) {
|
|
46
|
+
result.command = argsToCommand(result.args);
|
|
47
|
+
delete result.args;
|
|
48
|
+
}
|
|
49
|
+
if (result.type === "run_neon_init") {
|
|
50
|
+
result.type = "run_shell_command";
|
|
51
|
+
if (typeof result.command === "string" && result.command.includes("\"step\":\"finalize\"")) result.description = "Run this command to complete the setup. This is the final step — do not run any other neonctl init commands after this.";
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { enrichResponse };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=enrich-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrich-output.js","names":[],"sources":["../../src/lib/enrich-output.ts"],"sourcesContent":["/**\n * Converts neon-init args (e.g. [\"neon-auth\", \"--json\", \"--setup\"]) to a\n * neonctl init --data command using the step routing pattern.\n */\nfunction argsToCommand(args: string[]): string {\n\tconst data: Record<string, unknown> = {};\n\tlet i = 0;\n\n\t// First non-flag arg is the subcommand → step\n\tif (args.length > 0 && !args[0].startsWith(\"-\")) {\n\t\tdata.step = args[0];\n\t\ti = 1;\n\t}\n\n\twhile (i < args.length) {\n\t\tconst arg = args[i];\n\t\tif (arg.startsWith(\"--\")) {\n\t\t\tconst key = arg\n\t\t\t\t.slice(2)\n\t\t\t\t.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n\t\t\tif (key === \"json\") {\n\t\t\t\ti += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst next = args[i + 1];\n\t\t\tif (next !== undefined && !next.startsWith(\"-\")) {\n\t\t\t\tdata[key] = next;\n\t\t\t\ti += 2;\n\t\t\t} else {\n\t\t\t\tdata[key] = true;\n\t\t\t\ti += 1;\n\t\t\t}\n\t\t} else {\n\t\t\ti += 1;\n\t\t}\n\t}\n\n\treturn `neonctl init --agent --json --data '${JSON.stringify(data)}'`;\n}\n\n/**\n * Walks a phase response object and:\n * 1. Replaces `args` arrays with `command` strings (neonctl init --data format)\n * 2. Renames `run_neon_init` → `run_shell_command`\n * 3. Adds a description to finalize steps\n */\nexport function enrichResponse(obj: unknown): unknown {\n\tif (obj === null || typeof obj !== \"object\") return obj;\n\tif (Array.isArray(obj)) return obj.map(enrichResponse);\n\n\tconst record = obj as Record<string, unknown>;\n\tconst result: Record<string, unknown> = {};\n\n\tfor (const [key, value] of Object.entries(record)) {\n\t\tresult[key] = enrichResponse(value);\n\t}\n\n\t// Replace args with command in run_neon_init actions and responseMapping entries\n\tif (Array.isArray(result.args)) {\n\t\tresult.command = argsToCommand(result.args as string[]);\n\t\tdelete result.args;\n\t}\n\n\t// Rename run_neon_init → run_shell_command so agents don't infer subcommand patterns\n\tif (result.type === \"run_neon_init\") {\n\t\tresult.type = \"run_shell_command\";\n\t\t// Help agents understand finalize is the terminal step\n\t\tif (\n\t\t\ttypeof result.command === \"string\" &&\n\t\t\tresult.command.includes('\"step\":\"finalize\"')\n\t\t) {\n\t\t\tresult.description =\n\t\t\t\t\"Run this command to complete the setup. This is the final step — do not run any other neonctl init commands after this.\";\n\t\t}\n\t}\n\n\treturn result;\n}\n"],"mappings":";;;;;AAIA,SAAS,cAAc,MAAwB;CAC9C,MAAM,OAAgC,CAAC;CACvC,IAAI,IAAI;CAGR,IAAI,KAAK,SAAS,KAAK,CAAC,KAAK,EAAE,CAAC,WAAW,GAAG,GAAG;EAChD,KAAK,OAAO,KAAK;EACjB,IAAI;CACL;CAEA,OAAO,IAAI,KAAK,QAAQ;EACvB,MAAM,MAAM,KAAK;EACjB,IAAI,IAAI,WAAW,IAAI,GAAG;GACzB,MAAM,MAAM,IACV,MAAM,CAAC,CAAC,CACR,QAAQ,cAAc,GAAG,MAAc,EAAE,YAAY,CAAC;GACxD,IAAI,QAAQ,QAAQ;IACnB,KAAK;IACL;GACD;GACA,MAAM,OAAO,KAAK,IAAI;GACtB,IAAI,SAAS,KAAA,KAAa,CAAC,KAAK,WAAW,GAAG,GAAG;IAChD,KAAK,OAAO;IACZ,KAAK;GACN,OAAO;IACN,KAAK,OAAO;IACZ,KAAK;GACN;EACD,OACC,KAAK;CAEP;CAEA,OAAO,uCAAuC,KAAK,UAAU,IAAI,EAAE;AACpE;;;;;;;AAQA,SAAgB,eAAe,KAAuB;CACrD,IAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU,OAAO;CACpD,IAAI,MAAM,QAAQ,GAAG,GAAG,OAAO,IAAI,IAAI,cAAc;CAErD,MAAM,SAAS;CACf,MAAM,SAAkC,CAAC;CAEzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAC/C,OAAO,OAAO,eAAe,KAAK;CAInC,IAAI,MAAM,QAAQ,OAAO,IAAI,GAAG;EAC/B,OAAO,UAAU,cAAc,OAAO,IAAgB;EACtD,OAAO,OAAO;CACf;CAGA,IAAI,OAAO,SAAS,iBAAiB;EACpC,OAAO,OAAO;EAEd,IACC,OAAO,OAAO,YAAY,YAC1B,OAAO,QAAQ,SAAS,uBAAmB,GAE3C,OAAO,cACN;CAEH;CAEA,OAAO;AACR"}
|
package/dist/lib/neonctl.d.ts
CHANGED
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
//#region src/lib/neonctl.d.ts
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Returns the neonctl command prefix: "CI= npx -y neonctl".
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* --api-host https://console-stage.neon.build/api/v2
|
|
8
|
-
* --oauth-host https://oauth2-stage.neon.build
|
|
9
|
-
*
|
|
10
|
-
* The oauth host is derived by replacing the "console" prefix in the hostname
|
|
11
|
-
* with "oauth2" (e.g. console-stage.neon.build → oauth2-stage.neon.build).
|
|
12
|
-
*/
|
|
13
|
-
declare function getNeonctlApiFlags(): string;
|
|
14
|
-
/**
|
|
15
|
-
* Returns the neonctl command prefix: "CI= npx -y neonctl" with any
|
|
16
|
-
* --api-host / --oauth-host flags appended when NEON_API_HOST is set.
|
|
5
|
+
* neonctl reads NEON_API_HOST and NEON_OAUTH_HOST from the environment
|
|
6
|
+
* directly, so no extra flags are needed.
|
|
17
7
|
*
|
|
18
8
|
* Usage: `${neonctlCmd()} orgs list --output json`
|
|
19
9
|
*/
|
|
@@ -47,5 +37,5 @@ interface EnsureNeonctlResult {
|
|
|
47
37
|
*/
|
|
48
38
|
declare function ensureNeonctl(): Promise<EnsureNeonctlResult>;
|
|
49
39
|
//#endregion
|
|
50
|
-
export { EnsureNeonctlResult, checkNeonctl, detectPackageManager, ensureNeonctl,
|
|
40
|
+
export { EnsureNeonctlResult, checkNeonctl, detectPackageManager, ensureNeonctl, neonctlCmd };
|
|
51
41
|
//# sourceMappingURL=neonctl.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neonctl.d.ts","names":[],"sources":["../../src/lib/neonctl.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"neonctl.d.ts","names":[],"sources":["../../src/lib/neonctl.ts"],"mappings":";;AAUA;AAWA;AAQC;AAkDD;;;;AAA6C,iBArE7B,UAAA,CAAA,CAqE6B,EAAA,MAAA;AAiD7C;AAUA;;;;AAA8C;;iBArH9B,oBAAA,CAAA;UA6BN,aAAA;;;;;;;;;iBA6BY,YAAA,CAAA,GAAgB,QAAQ;UAiD7B,mBAAA;;;;;;;;;iBAUK,aAAA,CAAA,GAAiB,QAAQ"}
|
package/dist/lib/neonctl.js
CHANGED
|
@@ -1,39 +1,15 @@
|
|
|
1
1
|
import { execa } from "execa";
|
|
2
2
|
//#region src/lib/neonctl.ts
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Returns the neonctl command prefix: "CI= npx -y neonctl".
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* --api-host https://console-stage.neon.build/api/v2
|
|
9
|
-
* --oauth-host https://oauth2-stage.neon.build
|
|
10
|
-
*
|
|
11
|
-
* The oauth host is derived by replacing the "console" prefix in the hostname
|
|
12
|
-
* with "oauth2" (e.g. console-stage.neon.build → oauth2-stage.neon.build).
|
|
13
|
-
*/
|
|
14
|
-
function getNeonctlApiFlags() {
|
|
15
|
-
const apiHost = process.env.NEON_API_HOST;
|
|
16
|
-
if (!apiHost) return "";
|
|
17
|
-
const apiUrl = `${apiHost.replace(/\/+$/, "")}/api/v2`;
|
|
18
|
-
let oauthUrl = "";
|
|
19
|
-
try {
|
|
20
|
-
const url = new URL(apiHost);
|
|
21
|
-
url.hostname = url.hostname.replace(/^console/, "oauth2");
|
|
22
|
-
oauthUrl = url.origin;
|
|
23
|
-
} catch {}
|
|
24
|
-
const flags = [`--api-host ${apiUrl}`];
|
|
25
|
-
if (oauthUrl) flags.push(`--oauth-host ${oauthUrl}`);
|
|
26
|
-
return flags.join(" ");
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Returns the neonctl command prefix: "CI= npx -y neonctl" with any
|
|
30
|
-
* --api-host / --oauth-host flags appended when NEON_API_HOST is set.
|
|
6
|
+
* neonctl reads NEON_API_HOST and NEON_OAUTH_HOST from the environment
|
|
7
|
+
* directly, so no extra flags are needed.
|
|
31
8
|
*
|
|
32
9
|
* Usage: `${neonctlCmd()} orgs list --output json`
|
|
33
10
|
*/
|
|
34
11
|
function neonctlCmd() {
|
|
35
|
-
|
|
36
|
-
return flags ? `CI= npx -y neonctl ${flags}` : "CI= npx -y neonctl";
|
|
12
|
+
return "CI= npx -y neonctl";
|
|
37
13
|
}
|
|
38
14
|
/**
|
|
39
15
|
* Detects which package manager was used to invoke the current process.
|
|
@@ -179,6 +155,6 @@ async function ensureNeonctl() {
|
|
|
179
155
|
}
|
|
180
156
|
}
|
|
181
157
|
//#endregion
|
|
182
|
-
export { checkNeonctl, detectPackageManager, ensureNeonctl,
|
|
158
|
+
export { checkNeonctl, detectPackageManager, ensureNeonctl, neonctlCmd };
|
|
183
159
|
|
|
184
160
|
//# sourceMappingURL=neonctl.js.map
|
package/dist/lib/neonctl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neonctl.js","names":[],"sources":["../../src/lib/neonctl.ts"],"sourcesContent":["import { execa } from \"execa\";\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"neonctl.js","names":[],"sources":["../../src/lib/neonctl.ts"],"sourcesContent":["import { execa } from \"execa\";\n\n/**\n * Returns the neonctl command prefix: \"CI= npx -y neonctl\".\n *\n * neonctl reads NEON_API_HOST and NEON_OAUTH_HOST from the environment\n * directly, so no extra flags are needed.\n *\n * Usage: `${neonctlCmd()} orgs list --output json`\n */\nexport function neonctlCmd(): string {\n\treturn \"CI= npx -y neonctl\";\n}\n\n/**\n * Detects which package manager was used to invoke the current process.\n * Reads the `npm_config_user_agent` env var set by npm/pnpm/yarn/bun when\n * they spawn child processes (including via `npx`, `pnpx`, `bunx`, etc.).\n *\n * Falls back to \"npm\" if detection fails.\n */\nexport function detectPackageManager(): \"npm\" | \"pnpm\" | \"yarn\" | \"bun\" {\n\tconst ua = process.env.npm_config_user_agent;\n\tif (ua) {\n\t\tif (ua.startsWith(\"pnpm/\")) return \"pnpm\";\n\t\tif (ua.startsWith(\"yarn/\")) return \"yarn\";\n\t\tif (ua.startsWith(\"bun/\")) return \"bun\";\n\t}\n\treturn \"npm\";\n}\n\n/**\n * Returns the global install command for a given package manager.\n */\nfunction globalInstallArgs(\n\tpm: \"npm\" | \"pnpm\" | \"yarn\" | \"bun\",\n\tpkg: string,\n): { command: string; args: string[] } {\n\tswitch (pm) {\n\t\tcase \"pnpm\":\n\t\t\treturn { command: \"pnpm\", args: [\"add\", \"-g\", pkg] };\n\t\tcase \"yarn\":\n\t\t\treturn { command: \"yarn\", args: [\"global\", \"add\", pkg] };\n\t\tcase \"bun\":\n\t\t\treturn { command: \"bun\", args: [\"add\", \"-g\", pkg] };\n\t\tdefault:\n\t\t\treturn { command: \"npm\", args: [\"install\", \"-g\", pkg] };\n\t}\n}\n\ninterface NeonctlStatus {\n\tinstalled: boolean;\n\tcurrentVersion: string | null;\n\tlatestVersion: string | null;\n\tneedsUpdate: boolean;\n}\n\n/**\n * Gets the currently available neonctl version.\n * Tries the global binary first, then falls back to npx.\n */\nasync function getNeonctlVersion(): Promise<string | null> {\n\t// Try global binary first (fast path)\n\ttry {\n\t\tconst result = await execa(\"neonctl\", [\"--version\"], {\n\t\t\tstdio: \"pipe\",\n\t\t\ttimeout: 5000,\n\t\t});\n\t\tconst match = result.stdout.trim().match(/(\\d+\\.\\d+\\.\\d+)/);\n\t\tif (match) return match[1];\n\t} catch {\n\t\t// Not globally installed — that's fine\n\t}\n\treturn null;\n}\n\n/**\n * Checks whether the neonctl CLI is globally installed and whether it's up to date.\n */\nexport async function checkNeonctl(): Promise<NeonctlStatus> {\n\tconst currentVersion = await getNeonctlVersion();\n\n\tif (!currentVersion) {\n\t\treturn {\n\t\t\tinstalled: false,\n\t\t\tcurrentVersion: null,\n\t\t\tlatestVersion: null,\n\t\t\tneedsUpdate: true,\n\t\t};\n\t}\n\n\t// Check latest version from npm registry\n\tlet latestVersion: string | null = null;\n\ttry {\n\t\tconst result = await execa(\"npm\", [\"view\", \"neonctl\", \"version\"], {\n\t\t\tstdio: \"pipe\",\n\t\t\ttimeout: 10000,\n\t\t});\n\t\tlatestVersion = result.stdout.trim();\n\t} catch {\n\t\t// Can't determine latest — assume current is fine\n\t\treturn {\n\t\t\tinstalled: true,\n\t\t\tcurrentVersion,\n\t\t\tlatestVersion: null,\n\t\t\tneedsUpdate: false,\n\t\t};\n\t}\n\n\tconst needsUpdate =\n\t\tcurrentVersion !== null &&\n\t\tlatestVersion !== null &&\n\t\tcurrentVersion !== latestVersion &&\n\t\tisOlderVersion(currentVersion, latestVersion);\n\n\treturn { installed: true, currentVersion, latestVersion, needsUpdate };\n}\n\nfunction isOlderVersion(current: string, latest: string): boolean {\n\tconst c = current.split(\".\").map(Number);\n\tconst l = latest.split(\".\").map(Number);\n\tfor (let i = 0; i < 3; i++) {\n\t\tif ((c[i] ?? 0) < (l[i] ?? 0)) return true;\n\t\tif ((c[i] ?? 0) > (l[i] ?? 0)) return false;\n\t}\n\treturn false;\n}\n\nexport interface EnsureNeonctlResult {\n\tstatus: \"already_current\" | \"installed\" | \"updated\" | \"failed\";\n\tversion?: string;\n\terror?: string;\n}\n\n/**\n * Ensures neonctl is globally installed and up to date.\n * Uses the same package manager that invoked the init command.\n */\nexport async function ensureNeonctl(): Promise<EnsureNeonctlResult> {\n\tconst check = await checkNeonctl();\n\n\tif (check.installed && !check.needsUpdate) {\n\t\treturn {\n\t\t\tstatus: \"already_current\",\n\t\t\tversion: check.currentVersion ?? undefined,\n\t\t};\n\t}\n\n\tconst pm = detectPackageManager();\n\tconst { command, args } = globalInstallArgs(pm, \"neonctl\");\n\n\ttry {\n\t\tawait execa(command, args, { stdio: \"pipe\", timeout: 60000 });\n\n\t\t// Verify installation\n\t\tconst version = await getNeonctlVersion();\n\t\treturn {\n\t\t\tstatus: check.installed ? \"updated\" : \"installed\",\n\t\t\tversion: version ?? undefined,\n\t\t};\n\t} catch (err) {\n\t\treturn {\n\t\t\tstatus: \"failed\",\n\t\t\terror: err instanceof Error ? err.message : \"Unknown error\",\n\t\t};\n\t}\n}\n"],"mappings":";;;;;;;;;;AAUA,SAAgB,aAAqB;CACpC,OAAO;AACR;;;;;;;;AASA,SAAgB,uBAAwD;CACvE,MAAM,KAAK,QAAQ,IAAI;CACvB,IAAI,IAAI;EACP,IAAI,GAAG,WAAW,OAAO,GAAG,OAAO;EACnC,IAAI,GAAG,WAAW,OAAO,GAAG,OAAO;EACnC,IAAI,GAAG,WAAW,MAAM,GAAG,OAAO;CACnC;CACA,OAAO;AACR;;;;AAKA,SAAS,kBACR,IACA,KACsC;CACtC,QAAQ,IAAR;EACC,KAAK,QACJ,OAAO;GAAE,SAAS;GAAQ,MAAM;IAAC;IAAO;IAAM;GAAG;EAAE;EACpD,KAAK,QACJ,OAAO;GAAE,SAAS;GAAQ,MAAM;IAAC;IAAU;IAAO;GAAG;EAAE;EACxD,KAAK,OACJ,OAAO;GAAE,SAAS;GAAO,MAAM;IAAC;IAAO;IAAM;GAAG;EAAE;EACnD,SACC,OAAO;GAAE,SAAS;GAAO,MAAM;IAAC;IAAW;IAAM;GAAG;EAAE;CACxD;AACD;;;;;AAaA,eAAe,oBAA4C;CAE1D,IAAI;EAKH,MAAM,SAAQ,MAJO,MAAM,WAAW,CAAC,WAAW,GAAG;GACpD,OAAO;GACP,SAAS;EACV,CAAC,EAAA,CACoB,OAAO,KAAK,CAAC,CAAC,MAAM,iBAAiB;EAC1D,IAAI,OAAO,OAAO,MAAM;CACzB,QAAQ,CAER;CACA,OAAO;AACR;;;;AAKA,eAAsB,eAAuC;CAC5D,MAAM,iBAAiB,MAAM,kBAAkB;CAE/C,IAAI,CAAC,gBACJ,OAAO;EACN,WAAW;EACX,gBAAgB;EAChB,eAAe;EACf,aAAa;CACd;CAID,IAAI,gBAA+B;CACnC,IAAI;EAKH,iBAAgB,MAJK,MAAM,OAAO;GAAC;GAAQ;GAAW;EAAS,GAAG;GACjE,OAAO;GACP,SAAS;EACV,CAAC,EAAA,CACsB,OAAO,KAAK;CACpC,QAAQ;EAEP,OAAO;GACN,WAAW;GACX;GACA,eAAe;GACf,aAAa;EACd;CACD;CAEA,MAAM,cACL,mBAAmB,QACnB,kBAAkB,QAClB,mBAAmB,iBACnB,eAAe,gBAAgB,aAAa;CAE7C,OAAO;EAAE,WAAW;EAAM;EAAgB;EAAe;CAAY;AACtE;AAEA,SAAS,eAAe,SAAiB,QAAyB;CACjE,MAAM,IAAI,QAAQ,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;CACvC,MAAM,IAAI,OAAO,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;CACtC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;EAC3B,KAAK,EAAE,MAAM,MAAM,EAAE,MAAM,IAAI,OAAO;EACtC,KAAK,EAAE,MAAM,MAAM,EAAE,MAAM,IAAI,OAAO;CACvC;CACA,OAAO;AACR;;;;;AAYA,eAAsB,gBAA8C;CACnE,MAAM,QAAQ,MAAM,aAAa;CAEjC,IAAI,MAAM,aAAa,CAAC,MAAM,aAC7B,OAAO;EACN,QAAQ;EACR,SAAS,MAAM,kBAAkB,KAAA;CAClC;CAID,MAAM,EAAE,SAAS,SAAS,kBADf,qBACkC,GAAG,SAAS;CAEzD,IAAI;EACH,MAAM,MAAM,SAAS,MAAM;GAAE,OAAO;GAAQ,SAAS;EAAM,CAAC;EAG5D,MAAM,UAAU,MAAM,kBAAkB;EACxC,OAAO;GACN,QAAQ,MAAM,YAAY,YAAY;GACtC,SAAS,WAAW,KAAA;EACrB;CACD,SAAS,KAAK;EACb,OAAO;GACN,QAAQ;GACR,OAAO,eAAe,QAAQ,IAAI,UAAU;EAC7C;CACD;AACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/lib/phases/auth.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/lib/phases/auth.ts"],"mappings":";;;UAmBiB,gBAAA;;EAAA,MAAA,CAAA,EAAA,UAAgB,GAAA,KAAA;EAMX,MAAA,CAAA,EAAA,OAAA;;AACZ,iBADY,eAAA,CACZ,OAAA,EAAA,gBAAA,CAAA,EACP,OADO,CACC,aADD,CAAA"}
|
package/dist/lib/phases/auth.js
CHANGED
|
@@ -2,8 +2,7 @@ import { isAuthenticated } from "../auth.js";
|
|
|
2
2
|
import { neonctlCmd } from "../neonctl.js";
|
|
3
3
|
//#region src/lib/phases/auth.ts
|
|
4
4
|
function getSignupUrl() {
|
|
5
|
-
|
|
6
|
-
return base ? `${base}/signup` : "https://console.neon.tech/signup";
|
|
5
|
+
return process.env.NEON_API_HOST ? `${new URL(process.env.NEON_API_HOST).origin}/signup` : "https://console.neon.tech/signup";
|
|
7
6
|
}
|
|
8
7
|
function getSignupCommands() {
|
|
9
8
|
const url = getSignupUrl();
|
|
@@ -118,6 +117,7 @@ async function handleAuthPhase(options) {
|
|
|
118
117
|
status: "required",
|
|
119
118
|
nextAction: {
|
|
120
119
|
type: "ask_user",
|
|
120
|
+
instructions: "IMPORTANT: You MUST present this question to the user and WAIT for their response before proceeding. Do NOT assume the answer or auto-select an option. The user's answer determines the next step.",
|
|
121
121
|
question: "Do you have an existing Neon account, or do you need to create one?",
|
|
122
122
|
options: [{
|
|
123
123
|
value: "existing_account",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","names":[],"sources":["../../../src/lib/phases/auth.ts"],"sourcesContent":["import { isAuthenticated } from \"../auth.js\";\nimport { neonctlCmd } from \"../neonctl.js\";\nimport type { PhaseResponse } from \"../types.js\";\n\nfunction getSignupUrl(): string {\n\
|
|
1
|
+
{"version":3,"file":"auth.js","names":[],"sources":["../../../src/lib/phases/auth.ts"],"sourcesContent":["import { isAuthenticated } from \"../auth.js\";\nimport { neonctlCmd } from \"../neonctl.js\";\nimport type { PhaseResponse } from \"../types.js\";\n\nfunction getSignupUrl(): string {\n\treturn process.env.NEON_API_HOST\n\t\t? `${new URL(process.env.NEON_API_HOST).origin}/signup`\n\t\t: \"https://console.neon.tech/signup\";\n}\n\nfunction getSignupCommands(): Record<string, string> {\n\tconst url = getSignupUrl();\n\treturn {\n\t\tdarwin: `open ${url}`,\n\t\tlinux: `xdg-open ${url}`,\n\t\twin32: `start ${url}`,\n\t};\n}\n\nexport interface AuthPhaseOptions {\n\tagent?: string;\n\tmethod?: \"existing\" | \"new\";\n\tverify?: boolean;\n}\n\nexport async function handleAuthPhase(\n\toptions: AuthPhaseOptions,\n): Promise<PhaseResponse> {\n\tconst agentArgs = options.agent\n\t\t? [\"--agent\", options.agent, \"--json\"]\n\t\t: [\"--json\"];\n\n\t// --verify: just check if credentials exist\n\tif (options.verify) {\n\t\tconst authed = await isAuthenticated();\n\t\tif (authed) {\n\t\t\t// Continue the flow immediately — don't use \"complete\" which\n\t\t\t// causes agents to stop and get distracted by neonctl output.\n\t\t\treturn {\n\t\t\t\tphase: \"auth\",\n\t\t\t\tstatus: \"verified\",\n\t\t\t\tnextAction: {\n\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\targs: agentArgs,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tphase: \"auth\",\n\t\t\tstatus: \"not_authenticated\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\targs: [\"auth\", \"--json\"],\n\t\t\t},\n\t\t};\n\t}\n\n\t// Check if already authenticated\n\tconst authed = await isAuthenticated();\n\tif (authed) {\n\t\treturn {\n\t\t\tphase: \"auth\",\n\t\t\tstatus: \"verified\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\targs: agentArgs,\n\t\t\t},\n\t\t};\n\t}\n\n\t// --method new: guide through signup\n\tif (options.method === \"new\") {\n\t\tconst openCmd =\n\t\t\tgetSignupCommands()[process.platform] ?? getSignupCommands().linux;\n\t\treturn {\n\t\t\tphase: \"auth\",\n\t\t\tstatus: \"in_progress\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"agent_action\",\n\t\t\t\tsteps: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"open_signup\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Open the Neon sign-up page in the user's browser\",\n\t\t\t\t\t\tcommand: openCmd,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"wait_for_signup\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Tell the user: 'I've opened the Neon sign-up page. Create your account and verify your email, then let me know when you're ready.'\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tonComplete: {\n\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\targs: [\"auth\", \"--json\", \"--method\", \"existing\"],\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// --method existing: run OAuth flow\n\tif (options.method === \"existing\") {\n\t\treturn {\n\t\t\tphase: \"auth\",\n\t\t\tstatus: \"in_progress\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"run_command\",\n\t\t\t\tcommand: `${neonctlCmd()} auth`,\n\t\t\t\tdescription:\n\t\t\t\t\t\"This will open your browser for Neon OAuth sign-in.\",\n\t\t\t\ttimeout: 120000,\n\t\t\t\tonSuccess: {\n\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\targs: [\"auth\", \"--json\", \"--verify\"],\n\t\t\t\t},\n\t\t\t\tonFailure: {\n\t\t\t\t\t\"2\": {\n\t\t\t\t\t\ttype: \"ask_user\",\n\t\t\t\t\t\tquestion:\n\t\t\t\t\t\t\t\"The sign-in timed out. Did you complete the sign-in in your browser?\",\n\t\t\t\t\t\toptions: [\"yes_retry\", \"need_help\"],\n\t\t\t\t\t\tresponseMapping: {\n\t\t\t\t\t\t\tyes_retry: {\n\t\t\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\t\t\"auth\",\n\t\t\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\t\t\"--method\",\n\t\t\t\t\t\t\t\t\t\"existing\",\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tneed_help: {\n\t\t\t\t\t\t\t\targs: [\"auth\", \"--json\", \"--method\", \"new\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tother: {\n\t\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\t\targs: [\"auth\", \"--json\"],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// No method specified: ask the user, then launch OAuth directly for\n\t// \"existing account\" without an intermediate CLI round-trip.\n\tconst openCmd =\n\t\tgetSignupCommands()[process.platform] ?? getSignupCommands().linux;\n\treturn {\n\t\tphase: \"auth\",\n\t\tstatus: \"required\",\n\t\tnextAction: {\n\t\t\ttype: \"ask_user\",\n\t\t\tinstructions:\n\t\t\t\t\"IMPORTANT: You MUST present this question to the user and WAIT for their response before proceeding. Do NOT assume the answer or auto-select an option. The user's answer determines the next step.\",\n\t\t\tquestion:\n\t\t\t\t\"Do you have an existing Neon account, or do you need to create one?\",\n\t\t\toptions: [\n\t\t\t\t{\n\t\t\t\t\tvalue: \"existing_account\",\n\t\t\t\t\tlabel: \"I have an existing Neon account\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tvalue: \"new_account\",\n\t\t\t\t\tlabel: \"I need to create a new account\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tcontext:\n\t\t\t\t\"Neon is a serverless Postgres provider. A free account is required to continue.\",\n\t\t\tresponseMapping: {\n\t\t\t\texisting_account: {\n\t\t\t\t\taction: {\n\t\t\t\t\t\ttype: \"run_command\",\n\t\t\t\t\t\tcommand: `${neonctlCmd()} auth`,\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"This will open your browser for Neon OAuth sign-in.\",\n\t\t\t\t\t\ttimeout: 120000,\n\t\t\t\t\t\tonSuccess: {\n\t\t\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\t\t\targs: [\"auth\", \"--json\", \"--verify\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonFailure: {\n\t\t\t\t\t\t\t\"2\": {\n\t\t\t\t\t\t\t\ttype: \"ask_user\",\n\t\t\t\t\t\t\t\tquestion:\n\t\t\t\t\t\t\t\t\t\"The sign-in timed out. Did you complete the sign-in in your browser?\",\n\t\t\t\t\t\t\t\toptions: [\"yes_retry\", \"need_help\"],\n\t\t\t\t\t\t\t\tresponseMapping: {\n\t\t\t\t\t\t\t\t\tyes_retry: {\n\t\t\t\t\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\t\t\t\t\"auth\",\n\t\t\t\t\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\t\t\t\t\"--method\",\n\t\t\t\t\t\t\t\t\t\t\t\"existing\",\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tneed_help: {\n\t\t\t\t\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\t\t\t\t\"auth\",\n\t\t\t\t\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\t\t\t\t\"--method\",\n\t\t\t\t\t\t\t\t\t\t\t\"new\",\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tother: {\n\t\t\t\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\t\t\t\targs: [\"auth\", \"--json\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tnew_account: {\n\t\t\t\t\taction: {\n\t\t\t\t\t\ttype: \"agent_action\",\n\t\t\t\t\t\tsteps: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tid: \"open_signup\",\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\"Open the Neon sign-up page in the user's browser\",\n\t\t\t\t\t\t\t\tcommand: openCmd,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tid: \"wait_for_signup\",\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\"Tell the user: 'I've opened the Neon sign-up page. Create your account and verify your email, then let me know when you're ready.'\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tonComplete: {\n\t\t\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\t\t\targs: [\"auth\", \"--json\", \"--method\", \"existing\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n"],"mappings":";;;AAIA,SAAS,eAAuB;CAC/B,OAAO,QAAQ,IAAI,gBAChB,GAAG,IAAI,IAAI,QAAQ,IAAI,aAAa,CAAC,CAAC,OAAO,WAC7C;AACJ;AAEA,SAAS,oBAA4C;CACpD,MAAM,MAAM,aAAa;CACzB,OAAO;EACN,QAAQ,QAAQ;EAChB,OAAO,YAAY;EACnB,OAAO,SAAS;CACjB;AACD;AAQA,eAAsB,gBACrB,SACyB;CACzB,MAAM,YAAY,QAAQ,QACvB;EAAC;EAAW,QAAQ;EAAO;CAAQ,IACnC,CAAC,QAAQ;CAGZ,IAAI,QAAQ,QAAQ;EAEnB,IAAI,MADiB,gBAAgB,GAIpC,OAAO;GACN,OAAO;GACP,QAAQ;GACR,YAAY;IACX,MAAM;IACN,MAAM;GACP;EACD;EAED,OAAO;GACN,OAAO;GACP,QAAQ;GACR,YAAY;IACX,MAAM;IACN,MAAM,CAAC,QAAQ,QAAQ;GACxB;EACD;CACD;CAIA,IAAI,MADiB,gBAAgB,GAEpC,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,MAAM;EACP;CACD;CAID,IAAI,QAAQ,WAAW,OAGtB,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,OAAO,CACN;IACC,IAAI;IACJ,aACC;IACD,SAXH,kBAAkB,CAAC,CAAC,QAAQ,aAAa,kBAAkB,CAAC,CAAC;GAY3D,GACA;IACC,IAAI;IACJ,aACC;GACF,CACD;GACA,YAAY;IACX,MAAM;IACN,MAAM;KAAC;KAAQ;KAAU;KAAY;IAAU;GAChD;EACD;CACD;CAID,IAAI,QAAQ,WAAW,YACtB,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,SAAS,GAAG,WAAW,EAAE;GACzB,aACC;GACD,SAAS;GACT,WAAW;IACV,MAAM;IACN,MAAM;KAAC;KAAQ;KAAU;IAAU;GACpC;GACA,WAAW;IACV,KAAK;KACJ,MAAM;KACN,UACC;KACD,SAAS,CAAC,aAAa,WAAW;KAClC,iBAAiB;MAChB,WAAW,EACV,MAAM;OACL;OACA;OACA;OACA;MACD,EACD;MACA,WAAW,EACV,MAAM;OAAC;OAAQ;OAAU;OAAY;MAAK,EAC3C;KACD;IACD;IACA,OAAO;KACN,MAAM;KACN,MAAM,CAAC,QAAQ,QAAQ;IACxB;GACD;EACD;CACD;CAKD,MAAM,UACL,kBAAkB,CAAC,CAAC,QAAQ,aAAa,kBAAkB,CAAC,CAAC;CAC9D,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,cACC;GACD,UACC;GACD,SAAS,CACR;IACC,OAAO;IACP,OAAO;GACR,GACA;IACC,OAAO;IACP,OAAO;GACR,CACD;GACA,SACC;GACD,iBAAiB;IAChB,kBAAkB,EACjB,QAAQ;KACP,MAAM;KACN,SAAS,GAAG,WAAW,EAAE;KACzB,aACC;KACD,SAAS;KACT,WAAW;MACV,MAAM;MACN,MAAM;OAAC;OAAQ;OAAU;MAAU;KACpC;KACA,WAAW;MACV,KAAK;OACJ,MAAM;OACN,UACC;OACD,SAAS,CAAC,aAAa,WAAW;OAClC,iBAAiB;QAChB,WAAW,EACV,MAAM;SACL;SACA;SACA;SACA;QACD,EACD;QACA,WAAW,EACV,MAAM;SACL;SACA;SACA;SACA;QACD,EACD;OACD;MACD;MACA,OAAO;OACN,MAAM;OACN,MAAM,CAAC,QAAQ,QAAQ;MACxB;KACD;IACD,EACD;IACA,aAAa,EACZ,QAAQ;KACP,MAAM;KACN,OAAO,CACN;MACC,IAAI;MACJ,aACC;MACD,SAAS;KACV,GACA;MACC,IAAI;MACJ,aACC;KACF,CACD;KACA,YAAY;MACX,MAAM;MACN,MAAM;OAAC;OAAQ;OAAU;OAAY;MAAU;KAChD;IACD,EACD;GACD;EACD;CACD;AACD"}
|
|
@@ -25,8 +25,8 @@ async function handleGettingStartedPhase(options) {
|
|
|
25
25
|
id: "select_or_create_project",
|
|
26
26
|
description: [
|
|
27
27
|
"List existing Neon projects in the selected organization using the CLI command below (replace <org-id> with the selected org ID).",
|
|
28
|
-
"IMPORTANT: Preview features require a project in the AWS us-east-2 region created on or after 2026-06-
|
|
29
|
-
"Filter the project list to ONLY show projects where region_id is 'aws-us-east-2' AND created_at is on or after '2026-06-
|
|
28
|
+
"IMPORTANT: Preview features require a project in the AWS us-east-2 region created on or after 2026-06-15.",
|
|
29
|
+
"Filter the project list to ONLY show projects where region_id is 'aws-us-east-2' AND created_at is on or after '2026-06-15'.",
|
|
30
30
|
"If eligible projects exist, present them alongside a 'Create new project' option.",
|
|
31
31
|
"If no eligible projects exist, tell the user and proceed directly to creating a new one.",
|
|
32
32
|
"IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts."
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getting-started.js","names":[],"sources":["../../../src/lib/phases/getting-started.ts"],"sourcesContent":["import { neonctlCmd } from \"../neonctl.js\";\nimport { ensureSkillsUpToDate, SKILL_REFERENCE_URLS } from \"../skills.js\";\nimport type { PhaseResponse } from \"../types.js\";\n\nexport interface GettingStartedPhaseOptions {\n\tagent?: string;\n\thasConnectionString?: boolean;\n\tframework?: string;\n\torm?: string;\n\tmigrationTool?: string;\n\tmigrationDir?: string;\n\t/** Neon features required by the project (from .neon or template) */\n\tfeatures?: string[];\n\t/** Preview mode — restricts project creation to new projects in AWS us-east */\n\tpreview?: boolean;\n}\n\n/**\n * Initiates the \"Get started with Neon\" workflow.\n *\n * Steps are concrete and executable — each has a CLI command to run\n * or a specific file operation. The agent should attempt each step\n * in order and actually perform the action using the neonctl CLI.\n */\nexport async function handleGettingStartedPhase(\n\toptions: GettingStartedPhaseOptions,\n): Promise<PhaseResponse> {\n\t// Ensure skills are up to date (no-op if recently updated)\n\tif (options.agent) {\n\t\tawait ensureSkillsUpToDate(options.agent);\n\t}\n\tconst steps: { id: string; description: string; command?: string }[] = [];\n\n\tif (!options.hasConnectionString) {\n\t\tif (options.preview) {\n\t\t\t// Preview mode: new project in AWS us-east-2, or existing eligible project\n\t\t\tsteps.push(\n\t\t\t\t{\n\t\t\t\t\tid: \"select_org\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"List the user's Neon organizations using the CLI command below.\",\n\t\t\t\t\t\t\"If only one org exists, use it automatically.\",\n\t\t\t\t\t\t\"If multiple orgs exist, ask the user which one to use.\",\n\t\t\t\t\t\t\"Remember the selected org ID for the next steps.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} orgs list --output json`,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"select_or_create_project\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"List existing Neon projects in the selected organization using the CLI command below (replace <org-id> with the selected org ID).\",\n\t\t\t\t\t\t\"IMPORTANT: Preview features require a project in the AWS us-east-2 region created on or after 2026-06-11.\",\n\t\t\t\t\t\t\"Filter the project list to ONLY show projects where region_id is 'aws-us-east-2' AND created_at is on or after '2026-06-11'.\",\n\t\t\t\t\t\t\"If eligible projects exist, present them alongside a 'Create new project' option.\",\n\t\t\t\t\t\t\"If no eligible projects exist, tell the user and proceed directly to creating a new one.\",\n\t\t\t\t\t\t\"IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} projects list --org-id <org-id> --output json`,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"create_project_if_needed\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"If the user chose to create a new project, create it in the AWS us-east-2 region using the CLI command below (replace <org-id> and <project-name>).\",\n\t\t\t\t\t\t\"Ask the user for a project name (suggest the current directory name).\",\n\t\t\t\t\t\t\"If the user chose an existing eligible project, skip this step.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} projects create --name <project-name> --org-id <org-id> --region-id aws-us-east-2 --output json`,\n\t\t\t\t},\n\t\t\t);\n\t\t} else {\n\t\t\t// Standard mode: let user choose existing or create new\n\t\t\tsteps.push(\n\t\t\t\t{\n\t\t\t\t\tid: \"select_org\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"List the user's Neon organizations using the CLI command below.\",\n\t\t\t\t\t\t\"If only one org exists, use it automatically.\",\n\t\t\t\t\t\t\"If multiple orgs exist, ask the user which one to use.\",\n\t\t\t\t\t\t\"Remember the selected org ID for the next steps.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} orgs list --output json`,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"select_or_create_project\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"List existing Neon projects in the selected organization using the CLI command below (replace <org-id> with the selected org ID).\",\n\t\t\t\t\t\t\"Ask the user whether they want to use an existing project or create a new one.\",\n\t\t\t\t\t\t\"If creating new, ask the user for a project name (suggest the current directory name).\",\n\t\t\t\t\t\t\"IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} projects list --org-id <org-id> --output json`,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"create_project_if_needed\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"If the user chose to create a new project, create it using the CLI command below (replace <org-id> and <project-name>).\",\n\t\t\t\t\t\t\"If the user chose an existing project, skip this step.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} projects create --name <project-name> --org-id <org-id> --output json`,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// Create/update .neon context file\n\t\tsteps.push({\n\t\t\tid: \"create_neon_context\",\n\t\t\tdescription: [\n\t\t\t\t\"Update the .neon context file in the project root with the selected org and project IDs.\",\n\t\t\t\t\"IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved.\",\n\t\t\t\t\"If no .neon file exists, create one.\",\n\t\t\t\t'The file is JSON. Add/update only the orgId and projectId fields: {\"orgId\": \"<org-id>\", \"projectId\": \"<project-id>\", ...existing fields}.',\n\t\t\t\t\"This file is safe to commit — it contains no secrets.\",\n\t\t\t].join(\" \"),\n\t\t});\n\n\t\t// Install project dependencies (required before env pull — config files may import packages)\n\t\tsteps.push({\n\t\t\tid: \"install_dependencies\",\n\t\t\tdescription: [\n\t\t\t\t\"Check if node_modules exists in the project root.\",\n\t\t\t\t\"If not, install project dependencies using the appropriate package manager (check for pnpm-lock.yaml, yarn.lock, bun.lockb, or default to npm).\",\n\t\t\t\t\"This must be done before `neonctl env pull` because the project's Neon config file may import packages that need to be installed first.\",\n\t\t\t].join(\" \"),\n\t\t\tcommand: \"npm install\",\n\t\t});\n\n\t\t// Pull environment variables (connection string, etc.) from Neon\n\t\tsteps.push({\n\t\t\tid: \"pull_env\",\n\t\t\tdescription: [\n\t\t\t\t\"Now that the .neon context file is in place and dependencies are installed, run `neonctl env pull` to populate the project's environment variables.\",\n\t\t\t\t\"This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file.\",\n\t\t\t\t\"It reads the .neon context file to determine the project, and writes to the appropriate env file for the project.\",\n\t\t\t\t\"Ensure the target env file is listed in .gitignore.\",\n\t\t\t].join(\" \"),\n\t\t\tcommand: `${neonctlCmd()} env pull`,\n\t\t});\n\n\t\t// Step 6: Install Neon serverless driver if needed\n\t\tif (options.orm === \"prisma\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"install_driver\",\n\t\t\t\tdescription: [\n\t\t\t\t\t\"Install the @neondatabase/serverless driver adapter for Prisma.\",\n\t\t\t\t\t\"This enables Prisma to use Neon's serverless driver for edge/serverless deployments.\",\n\t\t\t\t].join(\" \"),\n\t\t\t\tcommand:\n\t\t\t\t\t\"npm install @neondatabase/serverless @prisma/adapter-neon\",\n\t\t\t});\n\t\t} else if (options.orm === \"drizzle\" || options.orm === \"drizzle-orm\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"install_driver\",\n\t\t\t\tdescription: \"Install the Neon serverless driver for Drizzle.\",\n\t\t\t\tcommand: \"npm install @neondatabase/serverless\",\n\t\t\t});\n\t\t} else if (!options.orm || options.orm === \"none\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"install_driver\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Install the Neon serverless driver for direct database access.\",\n\t\t\t\tcommand: \"npm install @neondatabase/serverless\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// Run migrations if applicable\n\tif (options.migrationTool && options.migrationTool !== \"none\") {\n\t\tconst tool = options.migrationTool.toLowerCase();\n\t\tconst migrationDir = options.migrationDir;\n\t\tconst hasMigrationDir = migrationDir && migrationDir !== \"none\";\n\n\t\tif (tool === \"drizzle\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"run_migrations\",\n\t\t\t\tdescription: [\n\t\t\t\t\thasMigrationDir\n\t\t\t\t\t\t? `Check if the ${migrationDir} directory contains .sql migration files.`\n\t\t\t\t\t\t: \"Check if a drizzle migrations directory exists with .sql files.\",\n\t\t\t\t\t\"If .sql files exist, apply them with `npx drizzle-kit migrate`.\",\n\t\t\t\t\t\"If the directory is empty or missing but a drizzle schema file exists (e.g. src/db/schema.ts, drizzle/schema.ts), run `npx drizzle-kit generate` first to create migrations, then `npx drizzle-kit migrate` to apply them.\",\n\t\t\t\t\t\"If neither schema nor migrations exist, skip this step.\",\n\t\t\t\t].join(\" \"),\n\t\t\t\tcommand: \"npx drizzle-kit migrate\",\n\t\t\t});\n\t\t} else if (tool === \"prisma\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"run_migrations\",\n\t\t\t\tdescription: [\n\t\t\t\t\thasMigrationDir\n\t\t\t\t\t\t? `Check if the ${migrationDir} directory contains migration folders.`\n\t\t\t\t\t\t: \"Check if prisma/migrations contains migration folders.\",\n\t\t\t\t\t\"If migrations exist, apply them with `npx prisma migrate deploy`.\",\n\t\t\t\t\t\"If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run `npx prisma migrate dev --name init` to create and apply the initial migration.\",\n\t\t\t\t\t\"If no models are defined, skip this step.\",\n\t\t\t\t].join(\" \"),\n\t\t\t\tcommand: \"npx prisma migrate deploy\",\n\t\t\t});\n\t\t} else if (tool === \"knex\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"run_migrations\",\n\t\t\t\tdescription: `Apply existing knex migrations to the Neon database.`,\n\t\t\t\tcommand: \"npx knex migrate:latest\",\n\t\t\t});\n\t\t}\n\t} else if (options.preview) {\n\t\t// Bootstrap flow: migration tool wasn't detected because the project was\n\t\t// inspected before scaffolding. Detect and run migrations from the scaffolded template.\n\t\tsteps.push({\n\t\t\tid: \"run_migrations\",\n\t\t\tdescription: [\n\t\t\t\t\"Check the scaffolded project for a migration tool and schema.\",\n\t\t\t\t\"Look for: drizzle.config.ts/js (Drizzle), prisma/schema.prisma (Prisma), or knexfile.ts/js (Knex).\",\n\t\t\t\t\"If Drizzle is found: check if a drizzle migrations directory exists with .sql files. If .sql files exist, run `npx drizzle-kit migrate`. If the directory is empty or missing but a schema file exists, run `npx drizzle-kit generate` first, then `npx drizzle-kit migrate`.\",\n\t\t\t\t\"If Prisma is found: check if prisma/migrations contains migration folders. If yes, run `npx prisma migrate deploy`. If not but models exist, run `npx prisma migrate dev --name init`.\",\n\t\t\t\t\"If no migration tool is found, skip this step.\",\n\t\t\t].join(\" \"),\n\t\t});\n\t}\n\n\t// Verify the connection\n\tsteps.push({\n\t\tid: \"verify_connection\",\n\t\tdescription: [\n\t\t\t\"Verify the database connection works by running a SQL query against the Neon database.\",\n\t\t\t\"Write and run a short script that connects using DATABASE_URL from the project's env file and executes `SELECT 1` (or queries a table from the migration if migrations were run).\",\n\t\t\t\"Do NOT use the neonctl CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity.\",\n\t\t].join(\" \"),\n\t});\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: \"getting_started\",\n\t\tnextAction: {\n\t\t\ttype: \"agent_action\",\n\t\t\tprerequisite: SKILL_REFERENCE_URLS.gettingStarted,\n\t\t\tsteps,\n\t\t\tonComplete: buildOnComplete(options),\n\t\t},\n\t};\n}\n\nfunction buildOnComplete(\n\toptions: GettingStartedPhaseOptions,\n): import(\"../types.js\").RunNeonInitAction {\n\tconst agentArgs = options.agent ? [\"--agent\", options.agent] : [];\n\tconst features = options.features ?? [];\n\tconst hasFeatureRequirements = features.length > 0;\n\n\t// If features are specified and auth is not required, go to finalize\n\tif (hasFeatureRequirements && !features.includes(\"auth\")) {\n\t\treturn {\n\t\t\ttype: \"run_neon_init\",\n\t\t\targs: [\"finalize\", \"--json\", ...agentArgs],\n\t\t};\n\t}\n\n\t// Chain to neon-auth — if user already selected auth via features, go straight to setup\n\tconst authSetup =\n\t\thasFeatureRequirements && features.includes(\"auth\") ? [\"--setup\"] : [];\n\treturn {\n\t\ttype: \"run_neon_init\",\n\t\targs: [\"neon-auth\", \"--json\", ...agentArgs, ...authSetup],\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"getting-started.js","names":[],"sources":["../../../src/lib/phases/getting-started.ts"],"sourcesContent":["import { neonctlCmd } from \"../neonctl.js\";\nimport { ensureSkillsUpToDate, SKILL_REFERENCE_URLS } from \"../skills.js\";\nimport type { PhaseResponse } from \"../types.js\";\n\nexport interface GettingStartedPhaseOptions {\n\tagent?: string;\n\thasConnectionString?: boolean;\n\tframework?: string;\n\torm?: string;\n\tmigrationTool?: string;\n\tmigrationDir?: string;\n\t/** Neon features required by the project (from .neon or template) */\n\tfeatures?: string[];\n\t/** Preview mode — restricts project creation to new projects in AWS us-east */\n\tpreview?: boolean;\n}\n\n/**\n * Initiates the \"Get started with Neon\" workflow.\n *\n * Steps are concrete and executable — each has a CLI command to run\n * or a specific file operation. The agent should attempt each step\n * in order and actually perform the action using the neonctl CLI.\n */\nexport async function handleGettingStartedPhase(\n\toptions: GettingStartedPhaseOptions,\n): Promise<PhaseResponse> {\n\t// Ensure skills are up to date (no-op if recently updated)\n\tif (options.agent) {\n\t\tawait ensureSkillsUpToDate(options.agent);\n\t}\n\tconst steps: { id: string; description: string; command?: string }[] = [];\n\n\tif (!options.hasConnectionString) {\n\t\tif (options.preview) {\n\t\t\t// Preview mode: new project in AWS us-east-2, or existing eligible project\n\t\t\tsteps.push(\n\t\t\t\t{\n\t\t\t\t\tid: \"select_org\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"List the user's Neon organizations using the CLI command below.\",\n\t\t\t\t\t\t\"If only one org exists, use it automatically.\",\n\t\t\t\t\t\t\"If multiple orgs exist, ask the user which one to use.\",\n\t\t\t\t\t\t\"Remember the selected org ID for the next steps.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} orgs list --output json`,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"select_or_create_project\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"List existing Neon projects in the selected organization using the CLI command below (replace <org-id> with the selected org ID).\",\n\t\t\t\t\t\t\"IMPORTANT: Preview features require a project in the AWS us-east-2 region created on or after 2026-06-15.\",\n\t\t\t\t\t\t\"Filter the project list to ONLY show projects where region_id is 'aws-us-east-2' AND created_at is on or after '2026-06-15'.\",\n\t\t\t\t\t\t\"If eligible projects exist, present them alongside a 'Create new project' option.\",\n\t\t\t\t\t\t\"If no eligible projects exist, tell the user and proceed directly to creating a new one.\",\n\t\t\t\t\t\t\"IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} projects list --org-id <org-id> --output json`,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"create_project_if_needed\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"If the user chose to create a new project, create it in the AWS us-east-2 region using the CLI command below (replace <org-id> and <project-name>).\",\n\t\t\t\t\t\t\"Ask the user for a project name (suggest the current directory name).\",\n\t\t\t\t\t\t\"If the user chose an existing eligible project, skip this step.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} projects create --name <project-name> --org-id <org-id> --region-id aws-us-east-2 --output json`,\n\t\t\t\t},\n\t\t\t);\n\t\t} else {\n\t\t\t// Standard mode: let user choose existing or create new\n\t\t\tsteps.push(\n\t\t\t\t{\n\t\t\t\t\tid: \"select_org\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"List the user's Neon organizations using the CLI command below.\",\n\t\t\t\t\t\t\"If only one org exists, use it automatically.\",\n\t\t\t\t\t\t\"If multiple orgs exist, ask the user which one to use.\",\n\t\t\t\t\t\t\"Remember the selected org ID for the next steps.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} orgs list --output json`,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"select_or_create_project\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"List existing Neon projects in the selected organization using the CLI command below (replace <org-id> with the selected org ID).\",\n\t\t\t\t\t\t\"Ask the user whether they want to use an existing project or create a new one.\",\n\t\t\t\t\t\t\"If creating new, ask the user for a project name (suggest the current directory name).\",\n\t\t\t\t\t\t\"IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} projects list --org-id <org-id> --output json`,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"create_project_if_needed\",\n\t\t\t\t\tdescription: [\n\t\t\t\t\t\t\"If the user chose to create a new project, create it using the CLI command below (replace <org-id> and <project-name>).\",\n\t\t\t\t\t\t\"If the user chose an existing project, skip this step.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\tcommand: `${neonctlCmd()} projects create --name <project-name> --org-id <org-id> --output json`,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// Create/update .neon context file\n\t\tsteps.push({\n\t\t\tid: \"create_neon_context\",\n\t\t\tdescription: [\n\t\t\t\t\"Update the .neon context file in the project root with the selected org and project IDs.\",\n\t\t\t\t\"IMPORTANT: If a .neon file already exists, you MUST read it first, then merge the new orgId and projectId into the existing content. Do NOT overwrite the file — other fields (like _init, branch, etc.) must be preserved.\",\n\t\t\t\t\"If no .neon file exists, create one.\",\n\t\t\t\t'The file is JSON. Add/update only the orgId and projectId fields: {\"orgId\": \"<org-id>\", \"projectId\": \"<project-id>\", ...existing fields}.',\n\t\t\t\t\"This file is safe to commit — it contains no secrets.\",\n\t\t\t].join(\" \"),\n\t\t});\n\n\t\t// Install project dependencies (required before env pull — config files may import packages)\n\t\tsteps.push({\n\t\t\tid: \"install_dependencies\",\n\t\t\tdescription: [\n\t\t\t\t\"Check if node_modules exists in the project root.\",\n\t\t\t\t\"If not, install project dependencies using the appropriate package manager (check for pnpm-lock.yaml, yarn.lock, bun.lockb, or default to npm).\",\n\t\t\t\t\"This must be done before `neonctl env pull` because the project's Neon config file may import packages that need to be installed first.\",\n\t\t\t].join(\" \"),\n\t\t\tcommand: \"npm install\",\n\t\t});\n\n\t\t// Pull environment variables (connection string, etc.) from Neon\n\t\tsteps.push({\n\t\t\tid: \"pull_env\",\n\t\t\tdescription: [\n\t\t\t\t\"Now that the .neon context file is in place and dependencies are installed, run `neonctl env pull` to populate the project's environment variables.\",\n\t\t\t\t\"This automatically writes the database connection string (and any other Neon-managed env vars) to the correct env file.\",\n\t\t\t\t\"It reads the .neon context file to determine the project, and writes to the appropriate env file for the project.\",\n\t\t\t\t\"Ensure the target env file is listed in .gitignore.\",\n\t\t\t].join(\" \"),\n\t\t\tcommand: `${neonctlCmd()} env pull`,\n\t\t});\n\n\t\t// Step 6: Install Neon serverless driver if needed\n\t\tif (options.orm === \"prisma\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"install_driver\",\n\t\t\t\tdescription: [\n\t\t\t\t\t\"Install the @neondatabase/serverless driver adapter for Prisma.\",\n\t\t\t\t\t\"This enables Prisma to use Neon's serverless driver for edge/serverless deployments.\",\n\t\t\t\t].join(\" \"),\n\t\t\t\tcommand:\n\t\t\t\t\t\"npm install @neondatabase/serverless @prisma/adapter-neon\",\n\t\t\t});\n\t\t} else if (options.orm === \"drizzle\" || options.orm === \"drizzle-orm\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"install_driver\",\n\t\t\t\tdescription: \"Install the Neon serverless driver for Drizzle.\",\n\t\t\t\tcommand: \"npm install @neondatabase/serverless\",\n\t\t\t});\n\t\t} else if (!options.orm || options.orm === \"none\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"install_driver\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Install the Neon serverless driver for direct database access.\",\n\t\t\t\tcommand: \"npm install @neondatabase/serverless\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// Run migrations if applicable\n\tif (options.migrationTool && options.migrationTool !== \"none\") {\n\t\tconst tool = options.migrationTool.toLowerCase();\n\t\tconst migrationDir = options.migrationDir;\n\t\tconst hasMigrationDir = migrationDir && migrationDir !== \"none\";\n\n\t\tif (tool === \"drizzle\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"run_migrations\",\n\t\t\t\tdescription: [\n\t\t\t\t\thasMigrationDir\n\t\t\t\t\t\t? `Check if the ${migrationDir} directory contains .sql migration files.`\n\t\t\t\t\t\t: \"Check if a drizzle migrations directory exists with .sql files.\",\n\t\t\t\t\t\"If .sql files exist, apply them with `npx drizzle-kit migrate`.\",\n\t\t\t\t\t\"If the directory is empty or missing but a drizzle schema file exists (e.g. src/db/schema.ts, drizzle/schema.ts), run `npx drizzle-kit generate` first to create migrations, then `npx drizzle-kit migrate` to apply them.\",\n\t\t\t\t\t\"If neither schema nor migrations exist, skip this step.\",\n\t\t\t\t].join(\" \"),\n\t\t\t\tcommand: \"npx drizzle-kit migrate\",\n\t\t\t});\n\t\t} else if (tool === \"prisma\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"run_migrations\",\n\t\t\t\tdescription: [\n\t\t\t\t\thasMigrationDir\n\t\t\t\t\t\t? `Check if the ${migrationDir} directory contains migration folders.`\n\t\t\t\t\t\t: \"Check if prisma/migrations contains migration folders.\",\n\t\t\t\t\t\"If migrations exist, apply them with `npx prisma migrate deploy`.\",\n\t\t\t\t\t\"If the migrations directory is empty or missing but prisma/schema.prisma has models defined, run `npx prisma migrate dev --name init` to create and apply the initial migration.\",\n\t\t\t\t\t\"If no models are defined, skip this step.\",\n\t\t\t\t].join(\" \"),\n\t\t\t\tcommand: \"npx prisma migrate deploy\",\n\t\t\t});\n\t\t} else if (tool === \"knex\") {\n\t\t\tsteps.push({\n\t\t\t\tid: \"run_migrations\",\n\t\t\t\tdescription: `Apply existing knex migrations to the Neon database.`,\n\t\t\t\tcommand: \"npx knex migrate:latest\",\n\t\t\t});\n\t\t}\n\t} else if (options.preview) {\n\t\t// Bootstrap flow: migration tool wasn't detected because the project was\n\t\t// inspected before scaffolding. Detect and run migrations from the scaffolded template.\n\t\tsteps.push({\n\t\t\tid: \"run_migrations\",\n\t\t\tdescription: [\n\t\t\t\t\"Check the scaffolded project for a migration tool and schema.\",\n\t\t\t\t\"Look for: drizzle.config.ts/js (Drizzle), prisma/schema.prisma (Prisma), or knexfile.ts/js (Knex).\",\n\t\t\t\t\"If Drizzle is found: check if a drizzle migrations directory exists with .sql files. If .sql files exist, run `npx drizzle-kit migrate`. If the directory is empty or missing but a schema file exists, run `npx drizzle-kit generate` first, then `npx drizzle-kit migrate`.\",\n\t\t\t\t\"If Prisma is found: check if prisma/migrations contains migration folders. If yes, run `npx prisma migrate deploy`. If not but models exist, run `npx prisma migrate dev --name init`.\",\n\t\t\t\t\"If no migration tool is found, skip this step.\",\n\t\t\t].join(\" \"),\n\t\t});\n\t}\n\n\t// Verify the connection\n\tsteps.push({\n\t\tid: \"verify_connection\",\n\t\tdescription: [\n\t\t\t\"Verify the database connection works by running a SQL query against the Neon database.\",\n\t\t\t\"Write and run a short script that connects using DATABASE_URL from the project's env file and executes `SELECT 1` (or queries a table from the migration if migrations were run).\",\n\t\t\t\"Do NOT use the neonctl CLI or MCP tools for this — use a direct database connection to verify end-to-end connectivity.\",\n\t\t].join(\" \"),\n\t});\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: \"getting_started\",\n\t\tnextAction: {\n\t\t\ttype: \"agent_action\",\n\t\t\tprerequisite: SKILL_REFERENCE_URLS.gettingStarted,\n\t\t\tsteps,\n\t\t\tonComplete: buildOnComplete(options),\n\t\t},\n\t};\n}\n\nfunction buildOnComplete(\n\toptions: GettingStartedPhaseOptions,\n): import(\"../types.js\").RunNeonInitAction {\n\tconst agentArgs = options.agent ? [\"--agent\", options.agent] : [];\n\tconst features = options.features ?? [];\n\tconst hasFeatureRequirements = features.length > 0;\n\n\t// If features are specified and auth is not required, go to finalize\n\tif (hasFeatureRequirements && !features.includes(\"auth\")) {\n\t\treturn {\n\t\t\ttype: \"run_neon_init\",\n\t\t\targs: [\"finalize\", \"--json\", ...agentArgs],\n\t\t};\n\t}\n\n\t// Chain to neon-auth — if user already selected auth via features, go straight to setup\n\tconst authSetup =\n\t\thasFeatureRequirements && features.includes(\"auth\") ? [\"--setup\"] : [];\n\treturn {\n\t\ttype: \"run_neon_init\",\n\t\targs: [\"neon-auth\", \"--json\", ...agentArgs, ...authSetup],\n\t};\n}\n"],"mappings":"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"}
|