neon-init 0.15.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;;AA+HsB,UApFL,WAAA,CAoFS;EAAA;OAAW,CAAA,EAlF5B,MAkF4B;;MAAc,CAAA,EAAA,OAAA;AAAO;;;;iBAApC,IAAA,WAAe,cAAc,QAAQ"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;;AAyIsB,UA7FL,WAAA,CA6FS;EAAA;OAAW,CAAA,EA3F5B,MA2F4B;;MAAc,CAAA,EAAA,OAAA;AAAO;;;;iBAApC,IAAA,WAAe,cAAc,QAAQ"}
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import { ALL_CONFIGURABLE_AGENTS } from "./lib/agents.js";
2
2
  import { isAuthenticated } from "./lib/auth.js";
3
3
  import { detectAvailableEditors } from "./lib/editors.js";
4
4
  import { usesExtension } from "./lib/extension.js";
5
+ import { getNeonctlApiFlags, neonctlCmd } from "./lib/neonctl.js";
5
6
  import { SKILL_REFERENCE_URLS, fetchSkillContent, installAgentSkills } from "./lib/skills.js";
6
7
  import { interactiveInit } from "./interactive.js";
7
8
  import { handleAuthPhase } from "./lib/phases/auth.js";
@@ -16,45 +17,54 @@ import { installNeon } from "./lib/install.js";
16
17
  import { intro, isCancel, log, multiselect, note, outro } from "@clack/prompts";
17
18
  import { bold, cyan } from "yoctocolors";
18
19
  //#region src/index.ts
19
- const NEONCTL_COMMANDS = {
20
- listOrgs: "npx neonctl orgs list --output json",
21
- listProjects: "npx neonctl projects list --output json",
22
- createProject: "npx neonctl projects create --name <project-name> --output json",
23
- getConnectionString: "npx neonctl connection-string --project-id <project-id>"
24
- };
25
- const AUTH_INSTRUCTIONS = [
26
- "YOU (the agent) must handle authentication. Do NOT ask the user to run commands themselves.",
27
- "Do NOT write wrapper scripts (Python, shell, etc.) — use simple shell commands only.",
28
- "IMPORTANT: Unset the CI environment variable for all neonctl commands below,",
29
- "otherwise neonctl will refuse to open the browser.",
30
- "",
31
- "Step 1: Ask the user: \"Do you already have a Neon account, or do you need to create one?\"",
32
- "",
33
- "=== NEW ACCOUNT (sign up) ===",
34
- "Step 2a: Open the Neon sign-up page in the user's browser:",
35
- " On macOS: open https://console.neon.tech/signup",
36
- " On Linux: xdg-open https://console.neon.tech/signup",
37
- "Step 3a: Tell the user: \"I've opened the Neon sign-up page in your browser.",
38
- " Create your account, then check your email for a verification link and click it.",
39
- " Let me know when you have finished signing up and verifying your email.\"",
40
- "Step 4a: Once the user confirms, run the OAuth login:",
41
- " CI= npx -y neonctl auth",
42
- " This opens a second browser tab for OAuth authorization. The user approves access.",
43
- " If exit code 0: authentication succeeded.",
44
- " If exit code 2 (timeout): ask the user to confirm they finished sign-up and",
45
- " email verification, then retry the same command.",
46
- "",
47
- "=== EXISTING ACCOUNT (sign in) ===",
48
- "Step 2b: Execute: CI= npx -y neonctl auth",
49
- " This opens the OAuth login page directly. Sign-in is fast (60-second timeout).",
50
- " If exit code 0: authentication succeeded.",
51
- " If exit code 2 (timeout): ask the user to confirm they completed sign-in, then retry.",
52
- "",
53
- "=== AFTER SUCCESSFUL AUTH ===",
54
- "Step 5: Verify by running: CI= npx -y neonctl me",
55
- " This should print the user's account info and exit with code 0.",
56
- "Step 6: Re-run neon-init with the same --agent and --json flags to complete setup."
57
- ].join("\n");
20
+ function getNeonctlCommands() {
21
+ const flags = getNeonctlApiFlags();
22
+ const base = flags ? `npx neonctl ${flags}` : "npx neonctl";
23
+ return {
24
+ listOrgs: `${base} orgs list --output json`,
25
+ listProjects: `${base} projects list --output json`,
26
+ createProject: `${base} projects create --name <project-name> --output json`,
27
+ getConnectionString: `${base} connection-string --project-id <project-id>`
28
+ };
29
+ }
30
+ function getAuthInstructions() {
31
+ const cmd = neonctlCmd();
32
+ const apiBase = process.env.NEON_API_HOST?.replace(/\/+$/, "");
33
+ const signupUrl = apiBase ? `${apiBase}/signup` : "https://console.neon.tech/signup";
34
+ return [
35
+ "YOU (the agent) must handle authentication. Do NOT ask the user to run commands themselves.",
36
+ "Do NOT write wrapper scripts (Python, shell, etc.) — use simple shell commands only.",
37
+ "IMPORTANT: Unset the CI environment variable for all neonctl commands below,",
38
+ "otherwise neonctl will refuse to open the browser.",
39
+ "",
40
+ "Step 1: Ask the user: \"Do you already have a Neon account, or do you need to create one?\"",
41
+ "",
42
+ "=== NEW ACCOUNT (sign up) ===",
43
+ "Step 2a: Open the Neon sign-up page in the user's browser:",
44
+ ` On macOS: open ${signupUrl}`,
45
+ ` On Linux: xdg-open ${signupUrl}`,
46
+ "Step 3a: Tell the user: \"I've opened the Neon sign-up page in your browser.",
47
+ " Create your account, then check your email for a verification link and click it.",
48
+ " Let me know when you have finished signing up and verifying your email.\"",
49
+ "Step 4a: Once the user confirms, run the OAuth login:",
50
+ ` ${cmd} auth`,
51
+ " This opens a second browser tab for OAuth authorization. The user approves access.",
52
+ " If exit code 0: authentication succeeded.",
53
+ " If exit code 2 (timeout): ask the user to confirm they finished sign-up and",
54
+ " email verification, then retry the same command.",
55
+ "",
56
+ "=== EXISTING ACCOUNT (sign in) ===",
57
+ `Step 2b: Execute: ${cmd} auth`,
58
+ " This opens the OAuth login page directly. Sign-in is fast (60-second timeout).",
59
+ " If exit code 0: authentication succeeded.",
60
+ " If exit code 2 (timeout): ask the user to confirm they completed sign-in, then retry.",
61
+ "",
62
+ "=== AFTER SUCCESSFUL AUTH ===",
63
+ `Step 5: Verify by running: ${cmd} me`,
64
+ " This should print the user's account info and exit with code 0.",
65
+ "Step 6: Re-run neon-init with the same --agent and --json flags to complete setup."
66
+ ].join("\n");
67
+ }
58
68
  function buildAgentInstructions(refs) {
59
69
  return [
60
70
  "IMPORTANT: Follow these steps to set up the Neon project. Do NOT skip steps.",
@@ -66,14 +76,14 @@ function buildAgentInstructions(refs) {
66
76
  "documentation pages (e.g. /docs/guides/*) or guess at import paths — the skill",
67
77
  "references are more accurate, complete, and agent-optimized.",
68
78
  "",
69
- `1. List organizations: ${NEONCTL_COMMANDS.listOrgs}`,
79
+ `1. List organizations: ${getNeonctlCommands().listOrgs}`,
70
80
  " - If one org, use it. If multiple, ask the user which to use.",
71
- `2. List projects: ${NEONCTL_COMMANDS.listProjects} (add --org-id <org-id>)`,
81
+ `2. List projects: ${getNeonctlCommands().listProjects} (add --org-id <org-id>)`,
72
82
  " - No projects: ask if they want to create a new one.",
73
83
  " - One project: ask if they want to use it or create new.",
74
84
  " - Multiple: let the user choose.",
75
- `3. Create project if needed: ${NEONCTL_COMMANDS.createProject} (add --org-id <org-id>)`,
76
- `4. Get connection string: ${NEONCTL_COMMANDS.getConnectionString}`,
85
+ `3. Create project if needed: ${getNeonctlCommands().createProject} (add --org-id <org-id>)`,
86
+ `4. Get connection string: ${getNeonctlCommands().getConnectionString}`,
77
87
  "5. Store in .env as DATABASE_URL (append, don't overwrite existing .env).",
78
88
  "6. For apps with user login/auth: STOP. Before writing any auth code, fetch and",
79
89
  ` read this skill reference: ${refs.neonAuth ?? ""}`,
@@ -101,7 +111,7 @@ async function init(options) {
101
111
  },
102
112
  neonctl: {
103
113
  authenticated: auth,
104
- commands: { ...NEONCTL_COMMANDS }
114
+ commands: { ...getNeonctlCommands() }
105
115
  },
106
116
  mcpServer: {
107
117
  configured: false,
@@ -150,20 +160,21 @@ async function init(options) {
150
160
  if (jsonMode) {
151
161
  if (!await isAuthenticated()) {
152
162
  const { gettingStarted: _gs, ...otherRefs } = SKILL_REFERENCE_URLS;
163
+ const gettingStartedContent = await fetchSkillContent();
153
164
  return {
154
165
  success: false,
155
166
  auth: false,
156
167
  authRequired: true,
157
- authInstructions: AUTH_INSTRUCTIONS,
168
+ authInstructions: getAuthInstructions(),
158
169
  editors: [],
159
170
  skills: {
160
171
  installed: false,
161
- gettingStarted: await fetchSkillContent(),
172
+ gettingStarted: gettingStartedContent,
162
173
  references: otherRefs
163
174
  },
164
175
  neonctl: {
165
176
  authenticated: false,
166
- commands: { ...NEONCTL_COMMANDS }
177
+ commands: { ...getNeonctlCommands() }
167
178
  },
168
179
  mcpServer: {
169
180
  configured: false,
@@ -201,7 +212,7 @@ async function init(options) {
201
212
  },
202
213
  neonctl: {
203
214
  authenticated: authSuccess,
204
- commands: { ...NEONCTL_COMMANDS }
215
+ commands: { ...getNeonctlCommands() }
205
216
  },
206
217
  mcpServer: {
207
218
  configured: mcpConfigured,
@@ -243,7 +254,7 @@ async function init(options) {
243
254
  },
244
255
  neonctl: {
245
256
  authenticated: authSuccess,
246
- commands: { ...NEONCTL_COMMANDS }
257
+ commands: { ...getNeonctlCommands() }
247
258
  },
248
259
  mcpServer: {
249
260
  configured: false,
@@ -266,7 +277,7 @@ async function init(options) {
266
277
  },
267
278
  neonctl: {
268
279
  authenticated: authSuccess,
269
- commands: { ...NEONCTL_COMMANDS }
280
+ commands: { ...getNeonctlCommands() }
270
281
  },
271
282
  mcpServer: {
272
283
  configured: mcpConfigured,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { intro, isCancel, log, multiselect, note, outro } from \"@clack/prompts\";\nimport { bold, cyan } from \"yoctocolors\";\nimport { ALL_CONFIGURABLE_AGENTS } from \"./lib/agents.js\";\nimport { isAuthenticated } from \"./lib/auth.js\";\nimport { detectAvailableEditors } from \"./lib/editors.js\";\nimport { usesExtension } from \"./lib/extension.js\";\nimport { installNeon } from \"./lib/install.js\";\nimport {\n\tfetchSkillContent,\n\tinstallAgentSkills,\n\tSKILL_REFERENCE_URLS,\n} from \"./lib/skills.js\";\nimport type { Editor, InitResult } from \"./lib/types.js\";\n\nexport type { InteractiveInitOptions } from \"./interactive.js\";\nexport { interactiveInit } from \"./interactive.js\";\nexport { handleAuthPhase } from \"./lib/phases/auth.js\";\nexport { handleDbPhase } from \"./lib/phases/db.js\";\nexport { handleMcpPhase } from \"./lib/phases/mcp.js\";\nexport { handleMigrationsPhase } from \"./lib/phases/migrations.js\";\nexport { handleNeonAuthPhase } from \"./lib/phases/neon-auth.js\";\nexport { handleSkillsPhase } from \"./lib/phases/skills.js\";\nexport { handleStatusPhase } from \"./lib/phases/status.js\";\n// v2 exports\nexport type {\n\tAgentActionAction,\n\tAgentCheck,\n\tAgentCheckAction,\n\tAgentStep,\n\tAskUserAction,\n\tCompleteAction,\n\tInitResult,\n\tNextAction,\n\tPhase,\n\tPhaseResponse,\n\tRestartRequiredAction,\n\tRunCommandAction,\n\tRunNeonInitAction,\n\tStatusResponse,\n} from \"./lib/types.js\";\nexport type { OrchestratorOptions } from \"./v2.js\";\nexport { orchestrate } from \"./v2.js\";\n\nexport interface InitOptions {\n\t/** When set, configures only this agent and skips the editor selection prompt. */\n\tagent?: Editor;\n\t/** When true, suppresses interactive UI and returns structured JSON-friendly output. */\n\tjson?: boolean;\n}\n\nconst NEONCTL_COMMANDS = {\n\tlistOrgs: \"npx neonctl orgs list --output json\",\n\tlistProjects: \"npx neonctl projects list --output json\",\n\tcreateProject:\n\t\t\"npx neonctl projects create --name <project-name> --output json\",\n\tgetConnectionString:\n\t\t\"npx neonctl connection-string --project-id <project-id>\",\n} as const;\n\nconst AUTH_INSTRUCTIONS = [\n\t\"YOU (the agent) must handle authentication. Do NOT ask the user to run commands themselves.\",\n\t\"Do NOT write wrapper scripts (Python, shell, etc.) — use simple shell commands only.\",\n\t\"IMPORTANT: Unset the CI environment variable for all neonctl commands below,\",\n\t\"otherwise neonctl will refuse to open the browser.\",\n\t\"\",\n\t'Step 1: Ask the user: \"Do you already have a Neon account, or do you need to create one?\"',\n\t\"\",\n\t\"=== NEW ACCOUNT (sign up) ===\",\n\t\"Step 2a: Open the Neon sign-up page in the user's browser:\",\n\t\" On macOS: open https://console.neon.tech/signup\",\n\t\" On Linux: xdg-open https://console.neon.tech/signup\",\n\t\"Step 3a: Tell the user: \\\"I've opened the Neon sign-up page in your browser.\",\n\t\" Create your account, then check your email for a verification link and click it.\",\n\t' Let me know when you have finished signing up and verifying your email.\"',\n\t\"Step 4a: Once the user confirms, run the OAuth login:\",\n\t\" CI= npx -y neonctl auth\",\n\t\" This opens a second browser tab for OAuth authorization. The user approves access.\",\n\t\" If exit code 0: authentication succeeded.\",\n\t\" If exit code 2 (timeout): ask the user to confirm they finished sign-up and\",\n\t\" email verification, then retry the same command.\",\n\t\"\",\n\t\"=== EXISTING ACCOUNT (sign in) ===\",\n\t\"Step 2b: Execute: CI= npx -y neonctl auth\",\n\t\" This opens the OAuth login page directly. Sign-in is fast (60-second timeout).\",\n\t\" If exit code 0: authentication succeeded.\",\n\t\" If exit code 2 (timeout): ask the user to confirm they completed sign-in, then retry.\",\n\t\"\",\n\t\"=== AFTER SUCCESSFUL AUTH ===\",\n\t\"Step 5: Verify by running: CI= npx -y neonctl me\",\n\t\" This should print the user's account info and exit with code 0.\",\n\t\"Step 6: Re-run neon-init with the same --agent and --json flags to complete setup.\",\n].join(\"\\n\");\n\nfunction buildAgentInstructions(refs: Record<string, string>): string {\n\treturn [\n\t\t\"IMPORTANT: Follow these steps to set up the Neon project. Do NOT skip steps.\",\n\t\t\"\",\n\t\t\"CRITICAL — SKILL REFERENCES: The URLs in skills.references are authoritative\",\n\t\t\"implementation guides with exact import paths, component patterns, route structures,\",\n\t\t\"and common mistakes to avoid. You MUST fetch and read the relevant skill reference\",\n\t\t\"URL BEFORE writing any code that uses that Neon feature. Do NOT rely on general\",\n\t\t\"documentation pages (e.g. /docs/guides/*) or guess at import paths — the skill\",\n\t\t\"references are more accurate, complete, and agent-optimized.\",\n\t\t\"\",\n\t\t`1. List organizations: ${NEONCTL_COMMANDS.listOrgs}`,\n\t\t\" - If one org, use it. If multiple, ask the user which to use.\",\n\t\t`2. List projects: ${NEONCTL_COMMANDS.listProjects} (add --org-id <org-id>)`,\n\t\t\" - No projects: ask if they want to create a new one.\",\n\t\t\" - One project: ask if they want to use it or create new.\",\n\t\t\" - Multiple: let the user choose.\",\n\t\t`3. Create project if needed: ${NEONCTL_COMMANDS.createProject} (add --org-id <org-id>)`,\n\t\t`4. Get connection string: ${NEONCTL_COMMANDS.getConnectionString}`,\n\t\t\"5. Store in .env as DATABASE_URL (append, don't overwrite existing .env).\",\n\t\t\"6. For apps with user login/auth: STOP. Before writing any auth code, fetch and\",\n\t\t` read this skill reference: ${refs.neonAuth ?? \"\"}`,\n\t\t\" It contains the exact packages, imports, component layout, route patterns,\",\n\t\t\" environment variables, and common mistakes. Follow it precisely.\",\n\t\t`7. For connection method/driver, fetch and read: ${refs.connectionMethods ?? \"\"}`,\n\t\t\"8. Refer to skills.gettingStarted for the full setup guide with additional details.\",\n\t\t\"\",\n\t\t\"Do NOT auto-create a project without asking the user first.\",\n\t].join(\"\\n\");\n}\n\n/**\n * Initialize Neon projects with MCP Server\n */\nexport async function init(options?: InitOptions): Promise<InitResult> {\n\tconst jsonMode = options?.json === true;\n\n\tconst makeFailedResult = (auth: boolean): InitResult => ({\n\t\tsuccess: false,\n\t\tauth,\n\t\teditors: [],\n\t\tskills: {\n\t\t\tinstalled: false,\n\t\t\tgettingStarted: null,\n\t\t\treferences: {},\n\t\t},\n\t\tneonctl: {\n\t\t\tauthenticated: auth,\n\t\t\tcommands: { ...NEONCTL_COMMANDS },\n\t\t},\n\t\tmcpServer: {\n\t\t\tconfigured: false,\n\t\t\trequiresRestart: false,\n\t\t},\n\t});\n\n\tif (!jsonMode) {\n\t\tif (options?.agent !== undefined) {\n\t\t\tif (usesExtension(options.agent)) {\n\t\t\t\tintro(\n\t\t\t\t\t`Adding Neon extension (includes MCP server) and agent skills for ${options.agent}`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tintro(\n\t\t\t\t\t`Adding Neon MCP server and agent skills for ${options.agent}`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tintro(\n\t\t\t\t\"Adding Neon MCP server, extension (for VS Code and Cursor) and agent skills\",\n\t\t\t);\n\t\t}\n\t}\n\n\tconst homeDir = process.env.HOME || process.env.USERPROFILE;\n\tif (!homeDir) {\n\t\tif (!jsonMode) {\n\t\t\tlog.error(\"Could not determine home directory\");\n\t\t\toutro(\"📣 Is this unexpected? Email us at feedback@neon.tech\");\n\t\t}\n\t\treturn makeFailedResult(false);\n\t}\n\n\tlet selectedEditors: Editor[];\n\n\tif (options?.agent !== undefined) {\n\t\tselectedEditors = [options.agent];\n\t} else {\n\t\tif (jsonMode) {\n\t\t\treturn makeFailedResult(false);\n\t\t}\n\n\t\tconst availableEditors = await detectAvailableEditors(homeDir);\n\n\t\tconst response = await multiselect({\n\t\t\tmessage:\n\t\t\t\t\"Which editor(s) would you like to configure? (Space to toggle each option, Enter to confirm your selection)\",\n\t\t\toptions: ALL_CONFIGURABLE_AGENTS.map((agent) => ({\n\t\t\t\tvalue: agent.editor,\n\t\t\t\tlabel: agent.editor,\n\t\t\t\thint: agent.hint,\n\t\t\t})),\n\t\t\tinitialValues: availableEditors,\n\t\t\trequired: true,\n\t\t});\n\n\t\tif (isCancel(response)) {\n\t\t\toutro(\"Installation cancelled\");\n\t\t\treturn makeFailedResult(false);\n\t\t}\n\n\t\tselectedEditors = response as Editor[];\n\t}\n\n\tif (selectedEditors.length === 0) {\n\t\tif (!jsonMode) {\n\t\t\tlog.warn(\"No editors selected.\");\n\t\t\toutro(\"Installation cancelled\");\n\t\t}\n\t\treturn makeFailedResult(false);\n\t}\n\n\t// In JSON mode, check for existing credentials before attempting OAuth.\n\t// neonctl's OAuth has a 60s timeout and email verification breaks the redirect,\n\t// so we let the agent handle auth as a separate step.\n\tif (jsonMode) {\n\t\tconst hasCredentials = await isAuthenticated();\n\t\tif (!hasCredentials) {\n\t\t\tconst { gettingStarted: _gs, ...otherRefs } = SKILL_REFERENCE_URLS;\n\t\t\tconst gettingStartedContent = await fetchSkillContent();\n\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tauth: false,\n\t\t\t\tauthRequired: true,\n\t\t\t\tauthInstructions: AUTH_INSTRUCTIONS,\n\t\t\t\teditors: [],\n\t\t\t\tskills: {\n\t\t\t\t\tinstalled: false,\n\t\t\t\t\tgettingStarted: gettingStartedContent,\n\t\t\t\t\treferences: otherRefs,\n\t\t\t\t},\n\t\t\t\tneonctl: {\n\t\t\t\t\tauthenticated: false,\n\t\t\t\t\tcommands: { ...NEONCTL_COMMANDS },\n\t\t\t\t},\n\t\t\t\tmcpServer: {\n\t\t\t\t\tconfigured: false,\n\t\t\t\t\trequiresRestart: false,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\tconst { results, authSuccess } = await installNeon(selectedEditors, {\n\t\tjson: jsonMode,\n\t});\n\n\tconst successful: Editor[] = [];\n\tconst failed: Editor[] = [];\n\n\tfor (const [editor, status] of results.entries()) {\n\t\tif (status === \"success\") {\n\t\t\tsuccessful.push(editor);\n\t\t} else {\n\t\t\tfailed.push(editor);\n\t\t}\n\t}\n\n\tlet skillsInstalled = false;\n\tif (successful.length > 0) {\n\t\tskillsInstalled = await installAgentSkills(successful, {\n\t\t\tjson: jsonMode,\n\t\t});\n\t}\n\n\t// Build the editors array for InitResult\n\tconst editorsResult: InitResult[\"editors\"] = [];\n\tfor (const [editor, status] of results.entries()) {\n\t\teditorsResult.push({\n\t\t\teditor,\n\t\t\tstatus,\n\t\t\ttype: usesExtension(editor) ? \"extension\" : \"mcp\",\n\t\t});\n\t}\n\n\tconst mcpConfigured = successful.some((e) => !usesExtension(e));\n\n\tif (jsonMode) {\n\t\tconst { gettingStarted: _gs, ...otherRefs } = SKILL_REFERENCE_URLS;\n\n\t\tconst gettingStartedContent = await fetchSkillContent();\n\n\t\treturn {\n\t\t\tsuccess: successful.length > 0,\n\t\t\tauth: authSuccess,\n\t\t\tagentInstructions: buildAgentInstructions(otherRefs),\n\t\t\teditors: editorsResult,\n\t\t\tskills: {\n\t\t\t\tinstalled: skillsInstalled,\n\t\t\t\tgettingStarted: gettingStartedContent,\n\t\t\t\treferences: otherRefs,\n\t\t\t},\n\t\t\tneonctl: {\n\t\t\t\tauthenticated: authSuccess,\n\t\t\t\tcommands: { ...NEONCTL_COMMANDS },\n\t\t\t},\n\t\t\tmcpServer: {\n\t\t\t\tconfigured: mcpConfigured,\n\t\t\t\trequiresRestart: mcpConfigured,\n\t\t\t},\n\t\t};\n\t}\n\n\t// Interactive UI output (non-json mode)\n\tconst extensionEditors = successful.filter(usesExtension);\n\tconst mcpEditors = successful.filter((e) => !usesExtension(e));\n\tconst failedExtensionEditors = failed.filter(usesExtension);\n\tconst failedMcpEditors = failed.filter((e) => !usesExtension(e));\n\n\tif (extensionEditors.length > 0) {\n\t\tconst extSuccessList = extensionEditors.join(\" / \");\n\t\tlog.step(\n\t\t\t`Neon Local Connect extension installed for ${extSuccessList}.\\n`,\n\t\t);\n\t}\n\n\tif (mcpEditors.length > 0) {\n\t\tconst mcpSuccessList = mcpEditors.join(\" / \");\n\t\tlog.step(\n\t\t\t`Neon MCP Server is now ready to use with ${mcpSuccessList}.\\n`,\n\t\t);\n\t}\n\n\tif (failedExtensionEditors.length > 0) {\n\t\tlog.info(\n\t\t\t\"Failed to install extension. For the best local development experience, install Neon Local Connect manually:\",\n\t\t);\n\t\tfor (const editor of failedExtensionEditors) {\n\t\t\tif (editor === \"VS Code\") {\n\t\t\t\tlog.info(\n\t\t\t\t\t\" • VS Code: https://marketplace.visualstudio.com/items?itemName=databricks.neon-local-connect\",\n\t\t\t\t);\n\t\t\t} else if (editor === \"Cursor\") {\n\t\t\t\tlog.info(\n\t\t\t\t\t\" • Cursor: https://open-vsx.org/extension/databricks/neon-local-connect\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (failedMcpEditors.length > 0) {\n\t\tlog.error(\n\t\t\t`Failed to configure MCP Server for ${failedMcpEditors.join(\" / \")}`,\n\t\t);\n\t\tlog.info(\n\t\t\t\"You can manually configure the MCP server by running: npx add-mcp https://mcp.neon.tech/mcp\",\n\t\t);\n\t}\n\n\tif (successful.length === 0) {\n\t\toutro(\n\t\t\t\"Installation cancelled or failed. Please check the output above and try again.\",\n\t\t);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\tauth: authSuccess,\n\t\t\teditors: editorsResult,\n\t\t\tskills: { installed: false, gettingStarted: null, references: {} },\n\t\t\tneonctl: {\n\t\t\t\tauthenticated: authSuccess,\n\t\t\t\tcommands: { ...NEONCTL_COMMANDS },\n\t\t\t},\n\t\t\tmcpServer: { configured: false, requiresRestart: false },\n\t\t};\n\t}\n\n\tif (extensionEditors.length > 0 && mcpEditors.length === 0) {\n\t\tconst extSuccessList = extensionEditors.join(\" / \");\n\t\tnote(\n\t\t\t`\\x1b[0mRestart ${extSuccessList}, open the Neon extension and type in \"${bold(cyan(\"Get started with Neon\"))}\\x1b[0m\" in your agent chat`,\n\t\t\t\"What's next?\",\n\t\t);\n\t} else if (mcpEditors.length > 0 && extensionEditors.length === 0) {\n\t\tconst mcpSuccessList = mcpEditors.join(\" / \");\n\t\tnote(\n\t\t\t`\\x1b[0mRestart ${mcpSuccessList} and type in \"${bold(cyan(\"Get started with Neon\"))}\\x1b[0m\" in the chat`,\n\t\t\t\"What's next?\",\n\t\t);\n\t} else {\n\t\tnote(\n\t\t\t`\\x1b[0mFor ${extensionEditors.join(\" / \")}: Restart, open the Neon extension and type in \"${bold(cyan(\"Get started with Neon\"))}\\x1b[0m\" in your agent chat\\n\\x1b[0mFor ${mcpEditors.join(\" / \")}: Restart and type in \"${bold(cyan(\"Get started with Neon\"))}\\x1b[0m\" in the chat`,\n\t\t\t\"What's next?\",\n\t\t);\n\t}\n\n\toutro(\"Have feedback? Email us at feedback@neon.tech\");\n\n\treturn {\n\t\tsuccess: true,\n\t\tauth: authSuccess,\n\t\teditors: editorsResult,\n\t\tskills: {\n\t\t\tinstalled: skillsInstalled,\n\t\t\tgettingStarted: null,\n\t\t\treferences: {},\n\t\t},\n\t\tneonctl: {\n\t\t\tauthenticated: authSuccess,\n\t\t\tcommands: { ...NEONCTL_COMMANDS },\n\t\t},\n\t\tmcpServer: {\n\t\t\tconfigured: mcpConfigured,\n\t\t\trequiresRestart: mcpConfigured,\n\t\t},\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { intro, isCancel, log, multiselect, note, outro } from \"@clack/prompts\";\nimport { bold, cyan } from \"yoctocolors\";\nimport { ALL_CONFIGURABLE_AGENTS } from \"./lib/agents.js\";\nimport { isAuthenticated } from \"./lib/auth.js\";\nimport { detectAvailableEditors } from \"./lib/editors.js\";\nimport { usesExtension } from \"./lib/extension.js\";\nimport { installNeon } from \"./lib/install.js\";\nimport { getNeonctlApiFlags, neonctlCmd } from \"./lib/neonctl.js\";\nimport {\n\tfetchSkillContent,\n\tinstallAgentSkills,\n\tSKILL_REFERENCE_URLS,\n} from \"./lib/skills.js\";\nimport type { Editor, InitResult } from \"./lib/types.js\";\n\nexport type { InteractiveInitOptions } from \"./interactive.js\";\nexport { interactiveInit } from \"./interactive.js\";\nexport { handleAuthPhase } from \"./lib/phases/auth.js\";\nexport { handleDbPhase } from \"./lib/phases/db.js\";\nexport { handleMcpPhase } from \"./lib/phases/mcp.js\";\nexport { handleMigrationsPhase } from \"./lib/phases/migrations.js\";\nexport { handleNeonAuthPhase } from \"./lib/phases/neon-auth.js\";\nexport { handleSkillsPhase } from \"./lib/phases/skills.js\";\nexport { handleStatusPhase } from \"./lib/phases/status.js\";\n// v2 exports\nexport type {\n\tAgentActionAction,\n\tAgentCheck,\n\tAgentCheckAction,\n\tAgentStep,\n\tAskUserAction,\n\tCompleteAction,\n\tInitResult,\n\tNextAction,\n\tPhase,\n\tPhaseResponse,\n\tRestartRequiredAction,\n\tRunCommandAction,\n\tRunNeonInitAction,\n\tStatusResponse,\n} from \"./lib/types.js\";\nexport type { OrchestratorOptions } from \"./v2.js\";\nexport { orchestrate } from \"./v2.js\";\n\nexport interface InitOptions {\n\t/** When set, configures only this agent and skips the editor selection prompt. */\n\tagent?: Editor;\n\t/** When true, suppresses interactive UI and returns structured JSON-friendly output. */\n\tjson?: boolean;\n}\n\nfunction getNeonctlCommands() {\n\tconst flags = getNeonctlApiFlags();\n\tconst base = flags ? `npx neonctl ${flags}` : \"npx neonctl\";\n\treturn {\n\t\tlistOrgs: `${base} orgs list --output json`,\n\t\tlistProjects: `${base} projects list --output json`,\n\t\tcreateProject: `${base} projects create --name <project-name> --output json`,\n\t\tgetConnectionString: `${base} connection-string --project-id <project-id>`,\n\t};\n}\n\nfunction getAuthInstructions(): string {\n\tconst cmd = neonctlCmd();\n\tconst apiBase = process.env.NEON_API_HOST?.replace(/\\/+$/, \"\");\n\tconst signupUrl = apiBase\n\t\t? `${apiBase}/signup`\n\t\t: \"https://console.neon.tech/signup\";\n\treturn [\n\t\t\"YOU (the agent) must handle authentication. Do NOT ask the user to run commands themselves.\",\n\t\t\"Do NOT write wrapper scripts (Python, shell, etc.) — use simple shell commands only.\",\n\t\t\"IMPORTANT: Unset the CI environment variable for all neonctl commands below,\",\n\t\t\"otherwise neonctl will refuse to open the browser.\",\n\t\t\"\",\n\t\t'Step 1: Ask the user: \"Do you already have a Neon account, or do you need to create one?\"',\n\t\t\"\",\n\t\t\"=== NEW ACCOUNT (sign up) ===\",\n\t\t\"Step 2a: Open the Neon sign-up page in the user's browser:\",\n\t\t` On macOS: open ${signupUrl}`,\n\t\t` On Linux: xdg-open ${signupUrl}`,\n\t\t\"Step 3a: Tell the user: \\\"I've opened the Neon sign-up page in your browser.\",\n\t\t\" Create your account, then check your email for a verification link and click it.\",\n\t\t' Let me know when you have finished signing up and verifying your email.\"',\n\t\t\"Step 4a: Once the user confirms, run the OAuth login:\",\n\t\t` ${cmd} auth`,\n\t\t\" This opens a second browser tab for OAuth authorization. The user approves access.\",\n\t\t\" If exit code 0: authentication succeeded.\",\n\t\t\" If exit code 2 (timeout): ask the user to confirm they finished sign-up and\",\n\t\t\" email verification, then retry the same command.\",\n\t\t\"\",\n\t\t\"=== EXISTING ACCOUNT (sign in) ===\",\n\t\t`Step 2b: Execute: ${cmd} auth`,\n\t\t\" This opens the OAuth login page directly. Sign-in is fast (60-second timeout).\",\n\t\t\" If exit code 0: authentication succeeded.\",\n\t\t\" If exit code 2 (timeout): ask the user to confirm they completed sign-in, then retry.\",\n\t\t\"\",\n\t\t\"=== AFTER SUCCESSFUL AUTH ===\",\n\t\t`Step 5: Verify by running: ${cmd} me`,\n\t\t\" This should print the user's account info and exit with code 0.\",\n\t\t\"Step 6: Re-run neon-init with the same --agent and --json flags to complete setup.\",\n\t].join(\"\\n\");\n}\n\nfunction buildAgentInstructions(refs: Record<string, string>): string {\n\treturn [\n\t\t\"IMPORTANT: Follow these steps to set up the Neon project. Do NOT skip steps.\",\n\t\t\"\",\n\t\t\"CRITICAL — SKILL REFERENCES: The URLs in skills.references are authoritative\",\n\t\t\"implementation guides with exact import paths, component patterns, route structures,\",\n\t\t\"and common mistakes to avoid. You MUST fetch and read the relevant skill reference\",\n\t\t\"URL BEFORE writing any code that uses that Neon feature. Do NOT rely on general\",\n\t\t\"documentation pages (e.g. /docs/guides/*) or guess at import paths — the skill\",\n\t\t\"references are more accurate, complete, and agent-optimized.\",\n\t\t\"\",\n\t\t`1. List organizations: ${getNeonctlCommands().listOrgs}`,\n\t\t\" - If one org, use it. If multiple, ask the user which to use.\",\n\t\t`2. List projects: ${getNeonctlCommands().listProjects} (add --org-id <org-id>)`,\n\t\t\" - No projects: ask if they want to create a new one.\",\n\t\t\" - One project: ask if they want to use it or create new.\",\n\t\t\" - Multiple: let the user choose.\",\n\t\t`3. Create project if needed: ${getNeonctlCommands().createProject} (add --org-id <org-id>)`,\n\t\t`4. Get connection string: ${getNeonctlCommands().getConnectionString}`,\n\t\t\"5. Store in .env as DATABASE_URL (append, don't overwrite existing .env).\",\n\t\t\"6. For apps with user login/auth: STOP. Before writing any auth code, fetch and\",\n\t\t` read this skill reference: ${refs.neonAuth ?? \"\"}`,\n\t\t\" It contains the exact packages, imports, component layout, route patterns,\",\n\t\t\" environment variables, and common mistakes. Follow it precisely.\",\n\t\t`7. For connection method/driver, fetch and read: ${refs.connectionMethods ?? \"\"}`,\n\t\t\"8. Refer to skills.gettingStarted for the full setup guide with additional details.\",\n\t\t\"\",\n\t\t\"Do NOT auto-create a project without asking the user first.\",\n\t].join(\"\\n\");\n}\n\n/**\n * Initialize Neon projects with MCP Server\n */\nexport async function init(options?: InitOptions): Promise<InitResult> {\n\tconst jsonMode = options?.json === true;\n\n\tconst makeFailedResult = (auth: boolean): InitResult => ({\n\t\tsuccess: false,\n\t\tauth,\n\t\teditors: [],\n\t\tskills: {\n\t\t\tinstalled: false,\n\t\t\tgettingStarted: null,\n\t\t\treferences: {},\n\t\t},\n\t\tneonctl: {\n\t\t\tauthenticated: auth,\n\t\t\tcommands: { ...getNeonctlCommands() },\n\t\t},\n\t\tmcpServer: {\n\t\t\tconfigured: false,\n\t\t\trequiresRestart: false,\n\t\t},\n\t});\n\n\tif (!jsonMode) {\n\t\tif (options?.agent !== undefined) {\n\t\t\tif (usesExtension(options.agent)) {\n\t\t\t\tintro(\n\t\t\t\t\t`Adding Neon extension (includes MCP server) and agent skills for ${options.agent}`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tintro(\n\t\t\t\t\t`Adding Neon MCP server and agent skills for ${options.agent}`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tintro(\n\t\t\t\t\"Adding Neon MCP server, extension (for VS Code and Cursor) and agent skills\",\n\t\t\t);\n\t\t}\n\t}\n\n\tconst homeDir = process.env.HOME || process.env.USERPROFILE;\n\tif (!homeDir) {\n\t\tif (!jsonMode) {\n\t\t\tlog.error(\"Could not determine home directory\");\n\t\t\toutro(\"📣 Is this unexpected? Email us at feedback@neon.tech\");\n\t\t}\n\t\treturn makeFailedResult(false);\n\t}\n\n\tlet selectedEditors: Editor[];\n\n\tif (options?.agent !== undefined) {\n\t\tselectedEditors = [options.agent];\n\t} else {\n\t\tif (jsonMode) {\n\t\t\treturn makeFailedResult(false);\n\t\t}\n\n\t\tconst availableEditors = await detectAvailableEditors(homeDir);\n\n\t\tconst response = await multiselect({\n\t\t\tmessage:\n\t\t\t\t\"Which editor(s) would you like to configure? (Space to toggle each option, Enter to confirm your selection)\",\n\t\t\toptions: ALL_CONFIGURABLE_AGENTS.map((agent) => ({\n\t\t\t\tvalue: agent.editor,\n\t\t\t\tlabel: agent.editor,\n\t\t\t\thint: agent.hint,\n\t\t\t})),\n\t\t\tinitialValues: availableEditors,\n\t\t\trequired: true,\n\t\t});\n\n\t\tif (isCancel(response)) {\n\t\t\toutro(\"Installation cancelled\");\n\t\t\treturn makeFailedResult(false);\n\t\t}\n\n\t\tselectedEditors = response as Editor[];\n\t}\n\n\tif (selectedEditors.length === 0) {\n\t\tif (!jsonMode) {\n\t\t\tlog.warn(\"No editors selected.\");\n\t\t\toutro(\"Installation cancelled\");\n\t\t}\n\t\treturn makeFailedResult(false);\n\t}\n\n\t// In JSON mode, check for existing credentials before attempting OAuth.\n\t// neonctl's OAuth has a 60s timeout and email verification breaks the redirect,\n\t// so we let the agent handle auth as a separate step.\n\tif (jsonMode) {\n\t\tconst hasCredentials = await isAuthenticated();\n\t\tif (!hasCredentials) {\n\t\t\tconst { gettingStarted: _gs, ...otherRefs } = SKILL_REFERENCE_URLS;\n\t\t\tconst gettingStartedContent = await fetchSkillContent();\n\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tauth: false,\n\t\t\t\tauthRequired: true,\n\t\t\t\tauthInstructions: getAuthInstructions(),\n\t\t\t\teditors: [],\n\t\t\t\tskills: {\n\t\t\t\t\tinstalled: false,\n\t\t\t\t\tgettingStarted: gettingStartedContent,\n\t\t\t\t\treferences: otherRefs,\n\t\t\t\t},\n\t\t\t\tneonctl: {\n\t\t\t\t\tauthenticated: false,\n\t\t\t\t\tcommands: { ...getNeonctlCommands() },\n\t\t\t\t},\n\t\t\t\tmcpServer: {\n\t\t\t\t\tconfigured: false,\n\t\t\t\t\trequiresRestart: false,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\tconst { results, authSuccess } = await installNeon(selectedEditors, {\n\t\tjson: jsonMode,\n\t});\n\n\tconst successful: Editor[] = [];\n\tconst failed: Editor[] = [];\n\n\tfor (const [editor, status] of results.entries()) {\n\t\tif (status === \"success\") {\n\t\t\tsuccessful.push(editor);\n\t\t} else {\n\t\t\tfailed.push(editor);\n\t\t}\n\t}\n\n\tlet skillsInstalled = false;\n\tif (successful.length > 0) {\n\t\tskillsInstalled = await installAgentSkills(successful, {\n\t\t\tjson: jsonMode,\n\t\t});\n\t}\n\n\t// Build the editors array for InitResult\n\tconst editorsResult: InitResult[\"editors\"] = [];\n\tfor (const [editor, status] of results.entries()) {\n\t\teditorsResult.push({\n\t\t\teditor,\n\t\t\tstatus,\n\t\t\ttype: usesExtension(editor) ? \"extension\" : \"mcp\",\n\t\t});\n\t}\n\n\tconst mcpConfigured = successful.some((e) => !usesExtension(e));\n\n\tif (jsonMode) {\n\t\tconst { gettingStarted: _gs, ...otherRefs } = SKILL_REFERENCE_URLS;\n\n\t\tconst gettingStartedContent = await fetchSkillContent();\n\n\t\treturn {\n\t\t\tsuccess: successful.length > 0,\n\t\t\tauth: authSuccess,\n\t\t\tagentInstructions: buildAgentInstructions(otherRefs),\n\t\t\teditors: editorsResult,\n\t\t\tskills: {\n\t\t\t\tinstalled: skillsInstalled,\n\t\t\t\tgettingStarted: gettingStartedContent,\n\t\t\t\treferences: otherRefs,\n\t\t\t},\n\t\t\tneonctl: {\n\t\t\t\tauthenticated: authSuccess,\n\t\t\t\tcommands: { ...getNeonctlCommands() },\n\t\t\t},\n\t\t\tmcpServer: {\n\t\t\t\tconfigured: mcpConfigured,\n\t\t\t\trequiresRestart: mcpConfigured,\n\t\t\t},\n\t\t};\n\t}\n\n\t// Interactive UI output (non-json mode)\n\tconst extensionEditors = successful.filter(usesExtension);\n\tconst mcpEditors = successful.filter((e) => !usesExtension(e));\n\tconst failedExtensionEditors = failed.filter(usesExtension);\n\tconst failedMcpEditors = failed.filter((e) => !usesExtension(e));\n\n\tif (extensionEditors.length > 0) {\n\t\tconst extSuccessList = extensionEditors.join(\" / \");\n\t\tlog.step(\n\t\t\t`Neon Local Connect extension installed for ${extSuccessList}.\\n`,\n\t\t);\n\t}\n\n\tif (mcpEditors.length > 0) {\n\t\tconst mcpSuccessList = mcpEditors.join(\" / \");\n\t\tlog.step(\n\t\t\t`Neon MCP Server is now ready to use with ${mcpSuccessList}.\\n`,\n\t\t);\n\t}\n\n\tif (failedExtensionEditors.length > 0) {\n\t\tlog.info(\n\t\t\t\"Failed to install extension. For the best local development experience, install Neon Local Connect manually:\",\n\t\t);\n\t\tfor (const editor of failedExtensionEditors) {\n\t\t\tif (editor === \"VS Code\") {\n\t\t\t\tlog.info(\n\t\t\t\t\t\" • VS Code: https://marketplace.visualstudio.com/items?itemName=databricks.neon-local-connect\",\n\t\t\t\t);\n\t\t\t} else if (editor === \"Cursor\") {\n\t\t\t\tlog.info(\n\t\t\t\t\t\" • Cursor: https://open-vsx.org/extension/databricks/neon-local-connect\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (failedMcpEditors.length > 0) {\n\t\tlog.error(\n\t\t\t`Failed to configure MCP Server for ${failedMcpEditors.join(\" / \")}`,\n\t\t);\n\t\tlog.info(\n\t\t\t\"You can manually configure the MCP server by running: npx add-mcp https://mcp.neon.tech/mcp\",\n\t\t);\n\t}\n\n\tif (successful.length === 0) {\n\t\toutro(\n\t\t\t\"Installation cancelled or failed. Please check the output above and try again.\",\n\t\t);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\tauth: authSuccess,\n\t\t\teditors: editorsResult,\n\t\t\tskills: { installed: false, gettingStarted: null, references: {} },\n\t\t\tneonctl: {\n\t\t\t\tauthenticated: authSuccess,\n\t\t\t\tcommands: { ...getNeonctlCommands() },\n\t\t\t},\n\t\t\tmcpServer: { configured: false, requiresRestart: false },\n\t\t};\n\t}\n\n\tif (extensionEditors.length > 0 && mcpEditors.length === 0) {\n\t\tconst extSuccessList = extensionEditors.join(\" / \");\n\t\tnote(\n\t\t\t`\\x1b[0mRestart ${extSuccessList}, open the Neon extension and type in \"${bold(cyan(\"Get started with Neon\"))}\\x1b[0m\" in your agent chat`,\n\t\t\t\"What's next?\",\n\t\t);\n\t} else if (mcpEditors.length > 0 && extensionEditors.length === 0) {\n\t\tconst mcpSuccessList = mcpEditors.join(\" / \");\n\t\tnote(\n\t\t\t`\\x1b[0mRestart ${mcpSuccessList} and type in \"${bold(cyan(\"Get started with Neon\"))}\\x1b[0m\" in the chat`,\n\t\t\t\"What's next?\",\n\t\t);\n\t} else {\n\t\tnote(\n\t\t\t`\\x1b[0mFor ${extensionEditors.join(\" / \")}: Restart, open the Neon extension and type in \"${bold(cyan(\"Get started with Neon\"))}\\x1b[0m\" in your agent chat\\n\\x1b[0mFor ${mcpEditors.join(\" / \")}: Restart and type in \"${bold(cyan(\"Get started with Neon\"))}\\x1b[0m\" in the chat`,\n\t\t\t\"What's next?\",\n\t\t);\n\t}\n\n\toutro(\"Have feedback? Email us at feedback@neon.tech\");\n\n\treturn {\n\t\tsuccess: true,\n\t\tauth: authSuccess,\n\t\teditors: editorsResult,\n\t\tskills: {\n\t\t\tinstalled: skillsInstalled,\n\t\t\tgettingStarted: null,\n\t\t\treferences: {},\n\t\t},\n\t\tneonctl: {\n\t\t\tauthenticated: authSuccess,\n\t\t\tcommands: { ...getNeonctlCommands() },\n\t\t},\n\t\tmcpServer: {\n\t\t\tconfigured: mcpConfigured,\n\t\t\trequiresRestart: mcpConfigured,\n\t\t},\n\t};\n}\n"],"mappings":"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"}
package/dist/lib/auth.js CHANGED
@@ -62,7 +62,8 @@ 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 response = await fetch("https://console.neon.tech/api/v2/api_keys", {
65
+ const apiBase = process.env.NEON_API_HOST ? `${process.env.NEON_API_HOST.replace(/\/+$/, "")}/api/v2` : "https://console.neon.tech/api/v2";
66
+ const response = await fetch(`${apiBase}/api_keys`, {
66
67
  method: "POST",
67
68
  headers: {
68
69
  Authorization: `Bearer ${accessToken}`,
@@ -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 neon-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 response = await fetch(\n\t\t\t\"https://console.neon.tech/api/v2/api_keys\",\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tkey_name: keyName,\n\t\t\t\t}),\n\t\t\t\tsignal: AbortSignal.timeout(30000),\n\t\t\t},\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,6GACD;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,WAAW,MAAM,MACtB,6CACA;GACC,QAAQ;GACR,SAAS;IACR,eAAe,UAAU;IACzB,gBAAgB;GACjB;GACA,MAAM,KAAK,UAAU,EACpB,UAAU,QACX,CAAC;GACD,QAAQ,YAAY,QAAQ,GAAK;EAClC,CACD;EAEA,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"}
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 neon-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? `${process.env.NEON_API_HOST.replace(/\\/+$/, \"\")}/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,6GACD;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,QAAQ,IAAI,cAAc,QAAQ,QAAQ,EAAE,EAAE,WACjD;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"}
@@ -1,4 +1,23 @@
1
1
  //#region src/lib/neonctl.d.ts
2
+ /**
3
+ * Derives --api-host and --oauth-host flags from the NEON_API_HOST env var.
4
+ *
5
+ * When NEON_API_HOST is set (e.g. "https://console-stage.neon.build"), neonctl
6
+ * commands need explicit flags:
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.
17
+ *
18
+ * Usage: `${neonctlCmd()} orgs list --output json`
19
+ */
20
+ declare function neonctlCmd(): string;
2
21
  /**
3
22
  * Detects which package manager was used to invoke the current process.
4
23
  * Reads the `npm_config_user_agent` env var set by npm/pnpm/yarn/bun when
@@ -28,5 +47,5 @@ interface EnsureNeonctlResult {
28
47
  */
29
48
  declare function ensureNeonctl(): Promise<EnsureNeonctlResult>;
30
49
  //#endregion
31
- export { EnsureNeonctlResult, checkNeonctl, detectPackageManager, ensureNeonctl };
50
+ export { EnsureNeonctlResult, checkNeonctl, detectPackageManager, ensureNeonctl, getNeonctlApiFlags, neonctlCmd };
32
51
  //# sourceMappingURL=neonctl.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"neonctl.d.ts","names":[],"sources":["../../src/lib/neonctl.ts"],"mappings":";;AASA;AAQC;AAkDD;;;;AAA6C,iBA1D7B,oBAAA,CAAA,CA0D6B,EAAA,KAAA,GAAA,MAAA,GAAA,MAAA,GAAA,KAAA;AAiD7C,UA9EU,aAAA,CA8EO;EAUK,SAAA,EAAA,OAAa;EAAA,cAAA,EAAA,MAAA,GAAA,IAAA;eAAY,EAAA,MAAA,GAAA,IAAA;aAAR,EAAA,OAAA;AAAO;;;;iBA3DxB,YAAA,CAAA,GAAgB,QAAQ;UAiD7B,mBAAA;;;;;;;;;iBAUK,aAAA,CAAA,GAAiB,QAAQ"}
1
+ {"version":3,"file":"neonctl.d.ts","names":[],"sources":["../../src/lib/neonctl.ts"],"mappings":";;AAaA;AA2BA;AAYA;AAQC;AAkDD;;;;AAA6C;AAiD7C;AAUsB,iBA5JN,kBAAA,CAAA,CA4JmB,EAAA,MAAA;;;;AAAW;;;iBAjI9B,UAAA,CAAA;;;;;;;;iBAYA,oBAAA,CAAA;UA6BN,aAAA;;;;;;;;;iBA6BY,YAAA,CAAA,GAAgB,QAAQ;UAiD7B,mBAAA;;;;;;;;;iBAUK,aAAA,CAAA,GAAiB,QAAQ"}
@@ -1,6 +1,41 @@
1
1
  import { execa } from "execa";
2
2
  //#region src/lib/neonctl.ts
3
3
  /**
4
+ * Derives --api-host and --oauth-host flags from the NEON_API_HOST env var.
5
+ *
6
+ * When NEON_API_HOST is set (e.g. "https://console-stage.neon.build"), neonctl
7
+ * commands need explicit flags:
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.
31
+ *
32
+ * Usage: `${neonctlCmd()} orgs list --output json`
33
+ */
34
+ function neonctlCmd() {
35
+ const flags = getNeonctlApiFlags();
36
+ return flags ? `CI= npx -y neonctl ${flags}` : "CI= npx -y neonctl";
37
+ }
38
+ /**
4
39
  * Detects which package manager was used to invoke the current process.
5
40
  * Reads the `npm_config_user_agent` env var set by npm/pnpm/yarn/bun when
6
41
  * they spawn child processes (including via `npx`, `pnpx`, `bunx`, etc.).
@@ -144,6 +179,6 @@ async function ensureNeonctl() {
144
179
  }
145
180
  }
146
181
  //#endregion
147
- export { checkNeonctl, detectPackageManager, ensureNeonctl };
182
+ export { checkNeonctl, detectPackageManager, ensureNeonctl, getNeonctlApiFlags, neonctlCmd };
148
183
 
149
184
  //# sourceMappingURL=neonctl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"neonctl.js","names":[],"sources":["../../src/lib/neonctl.ts"],"sourcesContent":["import { execa } from \"execa\";\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":";;;;;;;;;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
+ {"version":3,"file":"neonctl.js","names":[],"sources":["../../src/lib/neonctl.ts"],"sourcesContent":["import { execa } from \"execa\";\n\n/**\n * Derives --api-host and --oauth-host flags from the NEON_API_HOST env var.\n *\n * When NEON_API_HOST is set (e.g. \"https://console-stage.neon.build\"), neonctl\n * commands need explicit flags:\n * --api-host https://console-stage.neon.build/api/v2\n * --oauth-host https://oauth2-stage.neon.build\n *\n * The oauth host is derived by replacing the \"console\" prefix in the hostname\n * with \"oauth2\" (e.g. console-stage.neon.build → oauth2-stage.neon.build).\n */\nexport function getNeonctlApiFlags(): string {\n\tconst apiHost = process.env.NEON_API_HOST;\n\tif (!apiHost) return \"\";\n\n\tconst apiUrl = `${apiHost.replace(/\\/+$/, \"\")}/api/v2`;\n\n\tlet oauthUrl = \"\";\n\ttry {\n\t\tconst url = new URL(apiHost);\n\t\turl.hostname = url.hostname.replace(/^console/, \"oauth2\");\n\t\t// OAuth host is just the origin (no path)\n\t\toauthUrl = url.origin;\n\t} catch {\n\t\t// If URL parsing fails, skip oauth-host\n\t}\n\n\tconst flags = [`--api-host ${apiUrl}`];\n\tif (oauthUrl) flags.push(`--oauth-host ${oauthUrl}`);\n\treturn flags.join(\" \");\n}\n\n/**\n * Returns the neonctl command prefix: \"CI= npx -y neonctl\" with any\n * --api-host / --oauth-host flags appended when NEON_API_HOST is set.\n *\n * Usage: `${neonctlCmd()} orgs list --output json`\n */\nexport function neonctlCmd(): string {\n\tconst flags = getNeonctlApiFlags();\n\treturn flags ? `CI= npx -y neonctl ${flags}` : \"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":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/lib/phases/auth.ts"],"mappings":";;;UASiB,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"}
1
+ {"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/lib/phases/auth.ts"],"mappings":";;;UAkBiB,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"}
@@ -1,10 +1,18 @@
1
1
  import { isAuthenticated } from "../auth.js";
2
+ import { neonctlCmd } from "../neonctl.js";
2
3
  //#region src/lib/phases/auth.ts
3
- const SIGNUP_COMMANDS = {
4
- darwin: "open https://console.neon.tech/signup",
5
- linux: "xdg-open https://console.neon.tech/signup",
6
- win32: "start https://console.neon.tech/signup"
7
- };
4
+ function getSignupUrl() {
5
+ const base = process.env.NEON_API_HOST?.replace(/\/+$/, "");
6
+ return base ? `${base}/signup` : "https://console.neon.tech/signup";
7
+ }
8
+ function getSignupCommands() {
9
+ const url = getSignupUrl();
10
+ return {
11
+ darwin: `open ${url}`,
12
+ linux: `xdg-open ${url}`,
13
+ win32: `start ${url}`
14
+ };
15
+ }
8
16
  async function handleAuthPhase(options) {
9
17
  const agentArgs = options.agent ? [
10
18
  "--agent",
@@ -45,7 +53,7 @@ async function handleAuthPhase(options) {
45
53
  steps: [{
46
54
  id: "open_signup",
47
55
  description: "Open the Neon sign-up page in the user's browser",
48
- command: SIGNUP_COMMANDS[process.platform] ?? SIGNUP_COMMANDS.linux
56
+ command: getSignupCommands()[process.platform] ?? getSignupCommands().linux
49
57
  }, {
50
58
  id: "wait_for_signup",
51
59
  description: "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.'"
@@ -66,7 +74,7 @@ async function handleAuthPhase(options) {
66
74
  status: "in_progress",
67
75
  nextAction: {
68
76
  type: "run_command",
69
- command: "CI= npx -y neonctl auth",
77
+ command: `${neonctlCmd()} auth`,
70
78
  description: "This will open your browser for Neon OAuth sign-in.",
71
79
  timeout: 12e4,
72
80
  onSuccess: {
@@ -104,6 +112,7 @@ async function handleAuthPhase(options) {
104
112
  }
105
113
  }
106
114
  };
115
+ const openCmd = getSignupCommands()[process.platform] ?? getSignupCommands().linux;
107
116
  return {
108
117
  phase: "auth",
109
118
  status: "required",
@@ -121,7 +130,7 @@ async function handleAuthPhase(options) {
121
130
  responseMapping: {
122
131
  existing_account: { action: {
123
132
  type: "run_command",
124
- command: "CI= npx -y neonctl auth",
133
+ command: `${neonctlCmd()} auth`,
125
134
  description: "This will open your browser for Neon OAuth sign-in.",
126
135
  timeout: 12e4,
127
136
  onSuccess: {
@@ -163,7 +172,7 @@ async function handleAuthPhase(options) {
163
172
  steps: [{
164
173
  id: "open_signup",
165
174
  description: "Open the Neon sign-up page in the user's browser",
166
- command: SIGNUP_COMMANDS[process.platform] ?? SIGNUP_COMMANDS.linux
175
+ command: openCmd
167
176
  }, {
168
177
  id: "wait_for_signup",
169
178
  description: "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.'"
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","names":[],"sources":["../../../src/lib/phases/auth.ts"],"sourcesContent":["import { isAuthenticated } from \"../auth.js\";\nimport type { PhaseResponse } from \"../types.js\";\n\nconst SIGNUP_COMMANDS: Record<string, string> = {\n\tdarwin: \"open https://console.neon.tech/signup\",\n\tlinux: \"xdg-open https://console.neon.tech/signup\",\n\twin32: \"start https://console.neon.tech/signup\",\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\tSIGNUP_COMMANDS[process.platform] ?? SIGNUP_COMMANDS.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: \"CI= npx -y neonctl 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 = SIGNUP_COMMANDS[process.platform] ?? SIGNUP_COMMANDS.linux;\n\treturn {\n\t\tphase: \"auth\",\n\t\tstatus: \"required\",\n\t\tnextAction: {\n\t\t\ttype: \"ask_user\",\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: \"CI= npx -y neonctl 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":";;AAGA,MAAM,kBAA0C;CAC/C,QAAQ;CACR,OAAO;CACP,OAAO;AACR;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,gBAAgB,QAAQ,aAAa,gBAAgB;GAYnD,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;GACT,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;CAMD,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,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;KACT,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,SAxES,gBAAgB,QAAQ,aAAa,gBAAgB;KAyE/D,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"}
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\tconst base = process.env.NEON_API_HOST?.replace(/\\/+$/, \"\");\n\treturn base ? `${base}/signup` : \"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\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":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","names":[],"sources":["../../../src/lib/phases/db.ts"],"mappings":";;;UAeiB,cAAA;;EAAA,KAAA,CAAA,EAAA,MAAA;EAWK,SAAA,CAAA,EAAA,MAAa;EAAA,UAAA,CAAA,EAAA,MAAA;gBACzB,CAAA,EAAA,MAAA;WACC,CAAA,EAAA,MAAA;KAAR,CAAA,EAAA,MAAA;EAAO,KAAA,CAAA,EAAA,MAAA;;iBAFY,aAAA,UACZ,iBACP,QAAQ"}
1
+ {"version":3,"file":"db.d.ts","names":[],"sources":["../../../src/lib/phases/db.ts"],"mappings":";;;UAgBiB,cAAA;;EAAA,KAAA,CAAA,EAAA,MAAA;EAWK,SAAA,CAAA,EAAA,MAAa;EAAA,UAAA,CAAA,EAAA,MAAA;gBACzB,CAAA,EAAA,MAAA;WACC,CAAA,EAAA,MAAA;KAAR,CAAA,EAAA,MAAA;EAAO,KAAA,CAAA,EAAA,MAAA;;iBAFY,aAAA,UACZ,iBACP,QAAQ"}
@@ -1,3 +1,4 @@
1
+ import { neonctlCmd } from "../neonctl.js";
1
2
  import { SKILL_REFERENCE_URLS } from "../skills.js";
2
3
  //#region src/lib/phases/db.ts
3
4
  /**
@@ -46,7 +47,7 @@ async function handleDbPhase(options) {
46
47
  {
47
48
  id: "get_connection_string",
48
49
  description: "Get the database connection string",
49
- command: `CI= npx -y neonctl connection-string --project-id ${options.projectId}`
50
+ command: `${neonctlCmd()} connection-string --project-id ${options.projectId}`
50
51
  },
51
52
  {
52
53
  id: "store_env",
@@ -82,7 +83,7 @@ async function handleDbPhase(options) {
82
83
  value: "create",
83
84
  label: "Create a new project (provide a name)"
84
85
  }],
85
- context: "The agent should ask the user for a project name, then run: CI= npx -y neonctl projects create --name <name> --output json" + (options.orgId ? ` --org-id ${options.orgId}` : "") + " and pass the result back.",
86
+ context: `The agent should ask the user for a project name, then run: ${neonctlCmd()} projects create --name <name> --output json` + (options.orgId ? ` --org-id ${options.orgId}` : "") + " and pass the result back.",
86
87
  responseMapping: { create: { args: [
87
88
  "db",
88
89
  "--json",
@@ -122,7 +123,7 @@ async function handleDbPhase(options) {
122
123
  type: "ask_user",
123
124
  question: "Which Neon project would you like to use?",
124
125
  options: projectOptions,
125
- context: "If the user wants to create a new project, ask for a name then run: CI= npx -y neonctl projects create --name <name> --output json and use the returned project id.",
126
+ context: `If the user wants to create a new project, ask for a name then run: ${neonctlCmd()} projects create --name <name> --output json and use the returned project id.`,
126
127
  responseMapping
127
128
  }
128
129
  };
@@ -144,7 +145,7 @@ async function handleDbPhase(options) {
144
145
  org: { id: orgId },
145
146
  nextAction: {
146
147
  type: "run_command",
147
- command: `CI= npx -y neonctl projects list --org-id ${orgId} --output json`,
148
+ command: `${neonctlCmd()} projects list --org-id ${orgId} --output json`,
148
149
  description: "Listing Neon projects.",
149
150
  timeout: 3e4,
150
151
  onSuccess: {
@@ -198,7 +199,7 @@ async function handleDbPhase(options) {
198
199
  org: { id: options.orgId },
199
200
  nextAction: {
200
201
  type: "run_command",
201
- command: `CI= npx -y neonctl projects list --org-id ${options.orgId} --output json`,
202
+ command: `${neonctlCmd()} projects list --org-id ${options.orgId} --output json`,
202
203
  description: "Listing Neon projects.",
203
204
  timeout: 3e4,
204
205
  onSuccess: {
@@ -228,7 +229,7 @@ async function handleDbPhase(options) {
228
229
  status: "ready",
229
230
  nextAction: {
230
231
  type: "run_command",
231
- command: "CI= npx -y neonctl orgs list --output json",
232
+ command: `${neonctlCmd()} orgs list --output json`,
232
233
  description: "Listing your Neon organizations.",
233
234
  timeout: 3e4,
234
235
  onSuccess: {
@@ -1 +1 @@
1
- {"version":3,"file":"db.js","names":[],"sources":["../../../src/lib/phases/db.ts"],"sourcesContent":["import { SKILL_REFERENCE_URLS } from \"../skills.js\";\nimport type { PhaseResponse } from \"../types.js\";\n\n/**\n * Validates that an ID contains only safe characters for shell interpolation.\n * Neon org/project IDs are typically UUIDs or slug-like strings.\n */\nfunction assertSafeId(value: string, label: string): void {\n\tif (!/^[\\w.:-]+$/.test(value)) {\n\t\tthrow new Error(\n\t\t\t`Invalid ${label}: \"${value}\". Expected alphanumeric, hyphens, underscores, dots, or colons.`,\n\t\t);\n\t}\n}\n\nexport interface DbPhaseOptions {\n\tagent?: string;\n\torgId?: string;\n\tprojectId?: string;\n\torgsResult?: string;\n\tprojectsResult?: string;\n\tframework?: string;\n\torm?: string;\n\terror?: string;\n}\n\nexport async function handleDbPhase(\n\toptions: DbPhaseOptions,\n): Promise<PhaseResponse> {\n\tconst agentArgs = options.agent\n\t\t? [\"--agent\", options.agent, \"--json\"]\n\t\t: [\"--json\"];\n\n\t// Validate IDs that will be interpolated into shell commands\n\tif (options.projectId) assertSafeId(options.projectId, \"project ID\");\n\tif (options.orgId) assertSafeId(options.orgId, \"org ID\");\n\n\t// Error from a previous step\n\tif (options.error) {\n\t\treturn {\n\t\t\tphase: \"db\",\n\t\t\tstatus: \"error\",\n\t\t\terror: options.error,\n\t\t\tnextAction: {\n\t\t\t\ttype: \"ask_user\",\n\t\t\t\tquestion: `An error occurred during database setup: ${options.error}. Would you like to try again or skip this step?`,\n\t\t\t\toptions: [\n\t\t\t\t\t{ value: \"retry\", label: \"Try again\" },\n\t\t\t\t\t{ value: \"skip\", label: \"Skip database setup\" },\n\t\t\t\t],\n\t\t\t\tresponseMapping: {\n\t\t\t\t\tretry: { args: [\"db\", \"--json\"] },\n\t\t\t\t\tskip: { args: agentArgs },\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// If we have a project ID, we're in the \"wire it up\" phase\n\tif (options.projectId) {\n\t\treturn {\n\t\t\tphase: \"db\",\n\t\t\tstatus: \"project_ready\",\n\t\t\tproject: { id: options.projectId },\n\t\t\tnextAction: {\n\t\t\t\ttype: \"agent_action\",\n\t\t\t\tprerequisite: SKILL_REFERENCE_URLS.connectionMethods,\n\t\t\t\tsteps: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"get_connection_string\",\n\t\t\t\t\t\tdescription: \"Get the database connection string\",\n\t\t\t\t\t\tcommand: `CI= npx -y neonctl connection-string --project-id ${options.projectId}`,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"store_env\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Append DATABASE_URL=<connection_string> to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore.\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"detect_framework\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use.\",\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: agentArgs,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// If we have projects result, let user pick or create\n\tif (options.projectsResult) {\n\t\tlet projects: { id: string; name: string }[];\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(options.projectsResult);\n\t\t\tprojects = Array.isArray(parsed.projects)\n\t\t\t\t? parsed.projects\n\t\t\t\t: Array.isArray(parsed)\n\t\t\t\t\t? parsed\n\t\t\t\t\t: [];\n\t\t} catch {\n\t\t\tprojects = [];\n\t\t}\n\n\t\tconst orgIdArgs = options.orgId ? [\"--org-id\", options.orgId] : [];\n\n\t\tif (projects.length === 0) {\n\t\t\treturn {\n\t\t\t\tphase: \"db\",\n\t\t\t\tstatus: \"no_projects\",\n\t\t\t\tnextAction: {\n\t\t\t\t\ttype: \"ask_user\",\n\t\t\t\t\tquestion:\n\t\t\t\t\t\t\"You don't have any Neon projects yet. What would you like to name your new project?\",\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"create\",\n\t\t\t\t\t\t\tlabel: \"Create a new project (provide a name)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tcontext:\n\t\t\t\t\t\t\"The agent should ask the user for a project name, then run: CI= npx -y neonctl projects create --name <name> --output json\" +\n\t\t\t\t\t\t(options.orgId ? ` --org-id ${options.orgId}` : \"\") +\n\t\t\t\t\t\t\" and pass the result back.\",\n\t\t\t\t\tresponseMapping: {\n\t\t\t\t\t\tcreate: {\n\t\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\t\"db\",\n\t\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\t...orgIdArgs,\n\t\t\t\t\t\t\t\t\"--project-id\",\n\t\t\t\t\t\t\t\t\"<created-project-id>\",\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};\n\t\t}\n\n\t\tconst projectOptions = projects.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: `${p.name} (${p.id})`,\n\t\t}));\n\t\tprojectOptions.push({\n\t\t\tvalue: \"create_new\",\n\t\t\tlabel: \"Create a new project\",\n\t\t});\n\n\t\tconst responseMapping: Record<string, { args: string[] }> = {};\n\t\tfor (const p of projects) {\n\t\t\tresponseMapping[p.id] = {\n\t\t\t\targs: [\"db\", \"--json\", ...orgIdArgs, \"--project-id\", p.id],\n\t\t\t};\n\t\t}\n\t\tresponseMapping.create_new = {\n\t\t\targs: [\n\t\t\t\t\"db\",\n\t\t\t\t\"--json\",\n\t\t\t\t...orgIdArgs,\n\t\t\t\t\"--project-id\",\n\t\t\t\t\"<created-project-id>\",\n\t\t\t],\n\t\t};\n\n\t\treturn {\n\t\t\tphase: \"db\",\n\t\t\tstatus: \"select_project\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"ask_user\",\n\t\t\t\tquestion: \"Which Neon project would you like to use?\",\n\t\t\t\toptions: projectOptions,\n\t\t\t\tcontext:\n\t\t\t\t\t\"If the user wants to create a new project, ask for a name then run: CI= npx -y neonctl projects create --name <name> --output json and use the returned project id.\",\n\t\t\t\tresponseMapping,\n\t\t\t},\n\t\t};\n\t}\n\n\t// If we have orgs result, decide next step\n\tif (options.orgsResult) {\n\t\tlet orgs: { id: string; name: string }[];\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(options.orgsResult);\n\t\t\torgs = Array.isArray(parsed.organizations)\n\t\t\t\t? parsed.organizations\n\t\t\t\t: Array.isArray(parsed)\n\t\t\t\t\t? parsed\n\t\t\t\t\t: [];\n\t\t} catch {\n\t\t\torgs = [];\n\t\t}\n\n\t\t// Single org or org already selected: list projects\n\t\tconst orgId = options.orgId ?? (orgs.length === 1 ? orgs[0].id : null);\n\n\t\tif (orgId) {\n\t\t\tassertSafeId(orgId, \"org ID\");\n\t\t\treturn {\n\t\t\t\tphase: \"db\",\n\t\t\t\tstatus: \"org_selected\",\n\t\t\t\torg: { id: orgId },\n\t\t\t\tnextAction: {\n\t\t\t\t\ttype: \"run_command\",\n\t\t\t\t\tcommand: `CI= npx -y neonctl projects list --org-id ${orgId} --output json`,\n\t\t\t\t\tdescription: \"Listing Neon projects.\",\n\t\t\t\t\ttimeout: 30000,\n\t\t\t\t\tonSuccess: {\n\t\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\"db\",\n\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\"--org-id\",\n\t\t\t\t\t\t\torgId,\n\t\t\t\t\t\t\t\"--projects-result\",\n\t\t\t\t\t\t\t\"<stdout>\",\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tonFailure: {\n\t\t\t\t\t\tother: {\n\t\t\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\t\"db\",\n\t\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\t\"--error\",\n\t\t\t\t\t\t\t\t\"projects-list-failed\",\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};\n\t\t}\n\n\t\t// Multiple orgs: ask user to pick\n\t\tconst orgOptions = orgs.map((o) => ({\n\t\t\tvalue: o.id,\n\t\t\tlabel: `${o.name} (${o.id})`,\n\t\t}));\n\t\tconst responseMapping: Record<string, { args: string[] }> = {};\n\t\tfor (const o of orgs) {\n\t\t\tresponseMapping[o.id] = {\n\t\t\t\targs: [\"db\", \"--json\", \"--org-id\", o.id],\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tphase: \"db\",\n\t\t\tstatus: \"select_org\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"ask_user\",\n\t\t\t\tquestion: \"Which Neon organization would you like to use?\",\n\t\t\t\toptions: orgOptions,\n\t\t\t\tresponseMapping,\n\t\t\t},\n\t\t};\n\t}\n\n\t// If org-id provided but no orgs-result, list projects directly\n\tif (options.orgId) {\n\t\treturn {\n\t\t\tphase: \"db\",\n\t\t\tstatus: \"org_selected\",\n\t\t\torg: { id: options.orgId },\n\t\t\tnextAction: {\n\t\t\t\ttype: \"run_command\",\n\t\t\t\tcommand: `CI= npx -y neonctl projects list --org-id ${options.orgId} --output json`,\n\t\t\t\tdescription: \"Listing Neon projects.\",\n\t\t\t\ttimeout: 30000,\n\t\t\t\tonSuccess: {\n\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"db\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\"--org-id\",\n\t\t\t\t\t\toptions.orgId,\n\t\t\t\t\t\t\"--projects-result\",\n\t\t\t\t\t\t\"<stdout>\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tonFailure: {\n\t\t\t\t\tother: {\n\t\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\"db\",\n\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\"--error\",\n\t\t\t\t\t\t\t\"projects-list-failed\",\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\t// Default: start by listing orgs\n\treturn {\n\t\tphase: \"db\",\n\t\tstatus: \"ready\",\n\t\tnextAction: {\n\t\t\ttype: \"run_command\",\n\t\t\tcommand: \"CI= npx -y neonctl orgs list --output json\",\n\t\t\tdescription: \"Listing your Neon organizations.\",\n\t\t\ttimeout: 30000,\n\t\t\tonSuccess: {\n\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\targs: [\"db\", \"--json\", \"--orgs-result\", \"<stdout>\"],\n\t\t\t},\n\t\t\tonFailure: {\n\t\t\t\tother: {\n\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\targs: [\"db\", \"--json\", \"--error\", \"orgs-list-failed\"],\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"db.js","names":[],"sources":["../../../src/lib/phases/db.ts"],"sourcesContent":["import { neonctlCmd } from \"../neonctl.js\";\nimport { SKILL_REFERENCE_URLS } from \"../skills.js\";\nimport type { PhaseResponse } from \"../types.js\";\n\n/**\n * Validates that an ID contains only safe characters for shell interpolation.\n * Neon org/project IDs are typically UUIDs or slug-like strings.\n */\nfunction assertSafeId(value: string, label: string): void {\n\tif (!/^[\\w.:-]+$/.test(value)) {\n\t\tthrow new Error(\n\t\t\t`Invalid ${label}: \"${value}\". Expected alphanumeric, hyphens, underscores, dots, or colons.`,\n\t\t);\n\t}\n}\n\nexport interface DbPhaseOptions {\n\tagent?: string;\n\torgId?: string;\n\tprojectId?: string;\n\torgsResult?: string;\n\tprojectsResult?: string;\n\tframework?: string;\n\torm?: string;\n\terror?: string;\n}\n\nexport async function handleDbPhase(\n\toptions: DbPhaseOptions,\n): Promise<PhaseResponse> {\n\tconst agentArgs = options.agent\n\t\t? [\"--agent\", options.agent, \"--json\"]\n\t\t: [\"--json\"];\n\n\t// Validate IDs that will be interpolated into shell commands\n\tif (options.projectId) assertSafeId(options.projectId, \"project ID\");\n\tif (options.orgId) assertSafeId(options.orgId, \"org ID\");\n\n\t// Error from a previous step\n\tif (options.error) {\n\t\treturn {\n\t\t\tphase: \"db\",\n\t\t\tstatus: \"error\",\n\t\t\terror: options.error,\n\t\t\tnextAction: {\n\t\t\t\ttype: \"ask_user\",\n\t\t\t\tquestion: `An error occurred during database setup: ${options.error}. Would you like to try again or skip this step?`,\n\t\t\t\toptions: [\n\t\t\t\t\t{ value: \"retry\", label: \"Try again\" },\n\t\t\t\t\t{ value: \"skip\", label: \"Skip database setup\" },\n\t\t\t\t],\n\t\t\t\tresponseMapping: {\n\t\t\t\t\tretry: { args: [\"db\", \"--json\"] },\n\t\t\t\t\tskip: { args: agentArgs },\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// If we have a project ID, we're in the \"wire it up\" phase\n\tif (options.projectId) {\n\t\treturn {\n\t\t\tphase: \"db\",\n\t\t\tstatus: \"project_ready\",\n\t\t\tproject: { id: options.projectId },\n\t\t\tnextAction: {\n\t\t\t\ttype: \"agent_action\",\n\t\t\t\tprerequisite: SKILL_REFERENCE_URLS.connectionMethods,\n\t\t\t\tsteps: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"get_connection_string\",\n\t\t\t\t\t\tdescription: \"Get the database connection string\",\n\t\t\t\t\t\tcommand: `${neonctlCmd()} connection-string --project-id ${options.projectId}`,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"store_env\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Append DATABASE_URL=<connection_string> to .env. Create .env if it doesn't exist. Do NOT overwrite existing entries. Ensure .env is in .gitignore.\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"detect_framework\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Examine the project to determine the framework (Next.js, Remix, Express, etc.) and ORM (Prisma, Drizzle, raw SQL) in use.\",\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: agentArgs,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// If we have projects result, let user pick or create\n\tif (options.projectsResult) {\n\t\tlet projects: { id: string; name: string }[];\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(options.projectsResult);\n\t\t\tprojects = Array.isArray(parsed.projects)\n\t\t\t\t? parsed.projects\n\t\t\t\t: Array.isArray(parsed)\n\t\t\t\t\t? parsed\n\t\t\t\t\t: [];\n\t\t} catch {\n\t\t\tprojects = [];\n\t\t}\n\n\t\tconst orgIdArgs = options.orgId ? [\"--org-id\", options.orgId] : [];\n\n\t\tif (projects.length === 0) {\n\t\t\treturn {\n\t\t\t\tphase: \"db\",\n\t\t\t\tstatus: \"no_projects\",\n\t\t\t\tnextAction: {\n\t\t\t\t\ttype: \"ask_user\",\n\t\t\t\t\tquestion:\n\t\t\t\t\t\t\"You don't have any Neon projects yet. What would you like to name your new project?\",\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvalue: \"create\",\n\t\t\t\t\t\t\tlabel: \"Create a new project (provide a name)\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tcontext:\n\t\t\t\t\t\t`The agent should ask the user for a project name, then run: ${neonctlCmd()} projects create --name <name> --output json` +\n\t\t\t\t\t\t(options.orgId ? ` --org-id ${options.orgId}` : \"\") +\n\t\t\t\t\t\t\" and pass the result back.\",\n\t\t\t\t\tresponseMapping: {\n\t\t\t\t\t\tcreate: {\n\t\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\t\"db\",\n\t\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\t...orgIdArgs,\n\t\t\t\t\t\t\t\t\"--project-id\",\n\t\t\t\t\t\t\t\t\"<created-project-id>\",\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};\n\t\t}\n\n\t\tconst projectOptions = projects.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: `${p.name} (${p.id})`,\n\t\t}));\n\t\tprojectOptions.push({\n\t\t\tvalue: \"create_new\",\n\t\t\tlabel: \"Create a new project\",\n\t\t});\n\n\t\tconst responseMapping: Record<string, { args: string[] }> = {};\n\t\tfor (const p of projects) {\n\t\t\tresponseMapping[p.id] = {\n\t\t\t\targs: [\"db\", \"--json\", ...orgIdArgs, \"--project-id\", p.id],\n\t\t\t};\n\t\t}\n\t\tresponseMapping.create_new = {\n\t\t\targs: [\n\t\t\t\t\"db\",\n\t\t\t\t\"--json\",\n\t\t\t\t...orgIdArgs,\n\t\t\t\t\"--project-id\",\n\t\t\t\t\"<created-project-id>\",\n\t\t\t],\n\t\t};\n\n\t\treturn {\n\t\t\tphase: \"db\",\n\t\t\tstatus: \"select_project\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"ask_user\",\n\t\t\t\tquestion: \"Which Neon project would you like to use?\",\n\t\t\t\toptions: projectOptions,\n\t\t\t\tcontext: `If the user wants to create a new project, ask for a name then run: ${neonctlCmd()} projects create --name <name> --output json and use the returned project id.`,\n\t\t\t\tresponseMapping,\n\t\t\t},\n\t\t};\n\t}\n\n\t// If we have orgs result, decide next step\n\tif (options.orgsResult) {\n\t\tlet orgs: { id: string; name: string }[];\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(options.orgsResult);\n\t\t\torgs = Array.isArray(parsed.organizations)\n\t\t\t\t? parsed.organizations\n\t\t\t\t: Array.isArray(parsed)\n\t\t\t\t\t? parsed\n\t\t\t\t\t: [];\n\t\t} catch {\n\t\t\torgs = [];\n\t\t}\n\n\t\t// Single org or org already selected: list projects\n\t\tconst orgId = options.orgId ?? (orgs.length === 1 ? orgs[0].id : null);\n\n\t\tif (orgId) {\n\t\t\tassertSafeId(orgId, \"org ID\");\n\t\t\treturn {\n\t\t\t\tphase: \"db\",\n\t\t\t\tstatus: \"org_selected\",\n\t\t\t\torg: { id: orgId },\n\t\t\t\tnextAction: {\n\t\t\t\t\ttype: \"run_command\",\n\t\t\t\t\tcommand: `${neonctlCmd()} projects list --org-id ${orgId} --output json`,\n\t\t\t\t\tdescription: \"Listing Neon projects.\",\n\t\t\t\t\ttimeout: 30000,\n\t\t\t\t\tonSuccess: {\n\t\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\"db\",\n\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\"--org-id\",\n\t\t\t\t\t\t\torgId,\n\t\t\t\t\t\t\t\"--projects-result\",\n\t\t\t\t\t\t\t\"<stdout>\",\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tonFailure: {\n\t\t\t\t\t\tother: {\n\t\t\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\t\"db\",\n\t\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\t\"--error\",\n\t\t\t\t\t\t\t\t\"projects-list-failed\",\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};\n\t\t}\n\n\t\t// Multiple orgs: ask user to pick\n\t\tconst orgOptions = orgs.map((o) => ({\n\t\t\tvalue: o.id,\n\t\t\tlabel: `${o.name} (${o.id})`,\n\t\t}));\n\t\tconst responseMapping: Record<string, { args: string[] }> = {};\n\t\tfor (const o of orgs) {\n\t\t\tresponseMapping[o.id] = {\n\t\t\t\targs: [\"db\", \"--json\", \"--org-id\", o.id],\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tphase: \"db\",\n\t\t\tstatus: \"select_org\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"ask_user\",\n\t\t\t\tquestion: \"Which Neon organization would you like to use?\",\n\t\t\t\toptions: orgOptions,\n\t\t\t\tresponseMapping,\n\t\t\t},\n\t\t};\n\t}\n\n\t// If org-id provided but no orgs-result, list projects directly\n\tif (options.orgId) {\n\t\treturn {\n\t\t\tphase: \"db\",\n\t\t\tstatus: \"org_selected\",\n\t\t\torg: { id: options.orgId },\n\t\t\tnextAction: {\n\t\t\t\ttype: \"run_command\",\n\t\t\t\tcommand: `${neonctlCmd()} projects list --org-id ${options.orgId} --output json`,\n\t\t\t\tdescription: \"Listing Neon projects.\",\n\t\t\t\ttimeout: 30000,\n\t\t\t\tonSuccess: {\n\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"db\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\"--org-id\",\n\t\t\t\t\t\toptions.orgId,\n\t\t\t\t\t\t\"--projects-result\",\n\t\t\t\t\t\t\"<stdout>\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tonFailure: {\n\t\t\t\t\tother: {\n\t\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\"db\",\n\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t\"--error\",\n\t\t\t\t\t\t\t\"projects-list-failed\",\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\t// Default: start by listing orgs\n\treturn {\n\t\tphase: \"db\",\n\t\tstatus: \"ready\",\n\t\tnextAction: {\n\t\t\ttype: \"run_command\",\n\t\t\tcommand: `${neonctlCmd()} orgs list --output json`,\n\t\t\tdescription: \"Listing your Neon organizations.\",\n\t\t\ttimeout: 30000,\n\t\t\tonSuccess: {\n\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\targs: [\"db\", \"--json\", \"--orgs-result\", \"<stdout>\"],\n\t\t\t},\n\t\t\tonFailure: {\n\t\t\t\tother: {\n\t\t\t\t\ttype: \"run_neon_init\",\n\t\t\t\t\targs: [\"db\", \"--json\", \"--error\", \"orgs-list-failed\"],\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"getting-started.d.ts","names":[],"sources":["../../../src/lib/phases/getting-started.ts"],"mappings":";;;UAGiB,0BAAA;;EAAA,mBAAA,CAAA,EAAA,OAA0B;EAoBrB,SAAA,CAAA,EAAA,MAAA;EAAyB,GAAA,CAAA,EAAA,MAAA;eACrC,CAAA,EAAA,MAAA;cACC,CAAA,EAAA,MAAA;;EAAD,QAAA,CAAA,EAAA,MAAA,EAAA;;;;;;;;;;;iBAFY,yBAAA,UACZ,6BACP,QAAQ"}
1
+ {"version":3,"file":"getting-started.d.ts","names":[],"sources":["../../../src/lib/phases/getting-started.ts"],"mappings":";;;UAIiB,0BAAA;;EAAA,mBAAA,CAAA,EAAA,OAA0B;EAoBrB,SAAA,CAAA,EAAA,MAAA;EAAyB,GAAA,CAAA,EAAA,MAAA;eACrC,CAAA,EAAA,MAAA;cACC,CAAA,EAAA,MAAA;;EAAD,QAAA,CAAA,EAAA,MAAA,EAAA;;;;;;;;;;;iBAFY,yBAAA,UACZ,6BACP,QAAQ"}
@@ -1,3 +1,4 @@
1
+ import { neonctlCmd } from "../neonctl.js";
1
2
  import { SKILL_REFERENCE_URLS, ensureSkillsUpToDate } from "../skills.js";
2
3
  //#region src/lib/phases/getting-started.ts
3
4
  /**
@@ -19,7 +20,7 @@ async function handleGettingStartedPhase(options) {
19
20
  "If multiple orgs exist, ask the user which one to use.",
20
21
  "Remember the selected org ID for the next steps."
21
22
  ].join(" "),
22
- command: "CI= npx -y neonctl orgs list --output json"
23
+ command: `${neonctlCmd()} orgs list --output json`
23
24
  }, {
24
25
  id: "select_or_create_project",
25
26
  description: [
@@ -30,7 +31,7 @@ async function handleGettingStartedPhase(options) {
30
31
  "If no eligible projects exist, tell the user and proceed directly to creating a new one.",
31
32
  "IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts."
32
33
  ].join(" "),
33
- command: "CI= npx -y neonctl projects list --org-id <org-id> --output json"
34
+ command: `${neonctlCmd()} projects list --org-id <org-id> --output json`
34
35
  }, {
35
36
  id: "create_project_if_needed",
36
37
  description: [
@@ -38,7 +39,7 @@ async function handleGettingStartedPhase(options) {
38
39
  "Ask the user for a project name (suggest the current directory name).",
39
40
  "If the user chose an existing eligible project, skip this step."
40
41
  ].join(" "),
41
- command: "CI= npx -y neonctl projects create --name <project-name> --org-id <org-id> --region-id aws-us-east-2 --output json"
42
+ command: `${neonctlCmd()} projects create --name <project-name> --org-id <org-id> --region-id aws-us-east-2 --output json`
42
43
  });
43
44
  else steps.push({
44
45
  id: "select_org",
@@ -48,7 +49,7 @@ async function handleGettingStartedPhase(options) {
48
49
  "If multiple orgs exist, ask the user which one to use.",
49
50
  "Remember the selected org ID for the next steps."
50
51
  ].join(" "),
51
- command: "CI= npx -y neonctl orgs list --output json"
52
+ command: `${neonctlCmd()} orgs list --output json`
52
53
  }, {
53
54
  id: "select_or_create_project",
54
55
  description: [
@@ -57,11 +58,11 @@ async function handleGettingStartedPhase(options) {
57
58
  "If creating new, ask the user for a project name (suggest the current directory name).",
58
59
  "IMPORTANT: Always include --org-id when creating a project to avoid interactive prompts."
59
60
  ].join(" "),
60
- command: "CI= npx -y neonctl projects list --org-id <org-id> --output json"
61
+ command: `${neonctlCmd()} projects list --org-id <org-id> --output json`
61
62
  }, {
62
63
  id: "create_project_if_needed",
63
64
  description: ["If the user chose to create a new project, create it using the CLI command below (replace <org-id> and <project-name>).", "If the user chose an existing project, skip this step."].join(" "),
64
- command: "CI= npx -y neonctl projects create --name <project-name> --org-id <org-id> --output json"
65
+ command: `${neonctlCmd()} projects create --name <project-name> --org-id <org-id> --output json`
65
66
  });
66
67
  steps.push({
67
68
  id: "create_neon_context",
@@ -90,7 +91,7 @@ async function handleGettingStartedPhase(options) {
90
91
  "It reads the .neon context file to determine the project, and writes to the appropriate env file for the project.",
91
92
  "Ensure the target env file is listed in .gitignore."
92
93
  ].join(" "),
93
- command: "CI= npx -y neonctl env pull"
94
+ command: `${neonctlCmd()} env pull`
94
95
  });
95
96
  if (options.orm === "prisma") steps.push({
96
97
  id: "install_driver",
@@ -1 +1 @@
1
- {"version":3,"file":"getting-started.js","names":[],"sources":["../../../src/lib/phases/getting-started.ts"],"sourcesContent":["import { 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: \"CI= npx -y neonctl 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:\n\t\t\t\t\t\t\"CI= npx -y neonctl 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:\n\t\t\t\t\t\t\"CI= npx -y neonctl 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: \"CI= npx -y neonctl 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:\n\t\t\t\t\t\t\"CI= npx -y neonctl 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:\n\t\t\t\t\t\t\"CI= npx -y neonctl 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: \"CI= npx -y neonctl 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-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 +1 @@
1
- {"version":3,"file":"neon-auth.d.ts","names":[],"sources":["../../../src/lib/phases/neon-auth.ts"],"mappings":";;;UAGiB,oBAAA;;EAAA,KAAA,CAAA,EAAA,OAAA;EAOK,IAAA,CAAA,EAAA,OAAA;EAAmB,SAAA,CAAA,EAAA,MAAA;;AAE9B,iBAFW,mBAAA,CAEX,OAAA,EADD,oBACC,CAAA,EAAR,OAAQ,CAAA,aAAA,CAAA"}
1
+ {"version":3,"file":"neon-auth.d.ts","names":[],"sources":["../../../src/lib/phases/neon-auth.ts"],"mappings":";;;UAIiB,oBAAA;;EAAA,KAAA,CAAA,EAAA,OAAA;EAOK,IAAA,CAAA,EAAA,OAAA;EAAmB,SAAA,CAAA,EAAA,MAAA;;AAE9B,iBAFW,mBAAA,CAEX,OAAA,EADD,oBACC,CAAA,EAAR,OAAQ,CAAA,aAAA,CAAA"}
@@ -1,3 +1,4 @@
1
+ import { neonctlCmd } from "../neonctl.js";
1
2
  import { SKILL_REFERENCE_URLS, ensureSkillsUpToDate } from "../skills.js";
2
3
  //#region src/lib/phases/neon-auth.ts
3
4
  async function handleNeonAuthPhase(options) {
@@ -42,7 +43,7 @@ async function handleNeonAuthPhase(options) {
42
43
  steps: [
43
44
  {
44
45
  id: "provision",
45
- description: "Enable Neon Auth on the project using the neonctl CLI. Run: `CI= npx -y neonctl neon-auth enable --project-id <project-id> --output json`. You can check current status with: `CI= npx -y neonctl neon-auth status --project-id <project-id> --output json`." + (options.projectId ? ` Project ID: ${options.projectId}.` : " Determine the project ID from the .neon file in the project root, or from the DATABASE_URL in .env, or ask the user.")
46
+ description: `Enable Neon Auth on the project using the neonctl CLI. Run: \`${neonctlCmd()} neon-auth enable --project-id <project-id> --output json\`. You can check current status with: \`${neonctlCmd()} neon-auth status --project-id <project-id> --output json\`.` + (options.projectId ? ` Project ID: ${options.projectId}.` : " Determine the project ID from the .neon file in the project root, or from the DATABASE_URL in .env, or ask the user.")
46
47
  },
47
48
  {
48
49
  id: "install_packages",
@@ -54,8 +55,8 @@ async function handleNeonAuthPhase(options) {
54
55
  },
55
56
  {
56
57
  id: "pull_env",
57
- description: "Run `neonctl env pull` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.",
58
- command: "CI= npx -y neonctl env pull"
58
+ description: `Run \`${neonctlCmd()} env pull\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.`,
59
+ command: `${neonctlCmd()} env pull`
59
60
  }
60
61
  ],
61
62
  onComplete: {
@@ -1 +1 @@
1
- {"version":3,"file":"neon-auth.js","names":[],"sources":["../../../src/lib/phases/neon-auth.ts"],"sourcesContent":["import { ensureSkillsUpToDate, SKILL_REFERENCE_URLS } from \"../skills.js\";\nimport type { PhaseResponse } from \"../types.js\";\n\nexport interface NeonAuthPhaseOptions {\n\tagent?: string;\n\tsetup?: boolean;\n\tinfo?: boolean;\n\tprojectId?: string;\n}\n\nexport async function handleNeonAuthPhase(\n\toptions: NeonAuthPhaseOptions,\n): Promise<PhaseResponse> {\n\t// Validate IDs that may be interpolated into instruction strings\n\tif (options.projectId && !/^[\\w.:-]+$/.test(options.projectId)) {\n\t\tthrow new Error(\n\t\t\t`Invalid project ID: \"${options.projectId}\". Expected alphanumeric, hyphens, underscores, dots, or colons.`,\n\t\t);\n\t}\n\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\t// --info: return information about Neon Auth\n\tif (options.info) {\n\t\treturn {\n\t\t\tphase: \"neon_auth\",\n\t\t\tstatus: \"info\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"ask_user\",\n\t\t\t\tquestion:\n\t\t\t\t\t\"Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?\",\n\t\t\t\toptions: [\n\t\t\t\t\t{ value: \"yes\", label: \"Yes, set up Neon Auth\" },\n\t\t\t\t\t{ value: \"no\", label: \"No, skip for now\" },\n\t\t\t\t],\n\t\t\t\tcontext: `Full documentation: ${SKILL_REFERENCE_URLS.neonAuth}`,\n\t\t\t\tresponseMapping: {\n\t\t\t\t\tyes: {\n\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\"neon-auth\",\n\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t...(options.agent\n\t\t\t\t\t\t\t\t? [\"--agent\", options.agent]\n\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t\"--setup\",\n\t\t\t\t\t\t\t...(options.projectId\n\t\t\t\t\t\t\t\t? [\"--project-id\", options.projectId]\n\t\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\tno: {\n\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\"finalize\",\n\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t...(options.agent\n\t\t\t\t\t\t\t\t? [\"--agent\", options.agent]\n\t\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},\n\t\t};\n\t}\n\n\t// --setup: guide through Neon Auth configuration\n\tif (options.setup) {\n\t\treturn {\n\t\t\tphase: \"neon_auth\",\n\t\t\tstatus: \"in_progress\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"agent_action\",\n\t\t\t\tprerequisite: SKILL_REFERENCE_URLS.neonAuth,\n\t\t\t\tsteps: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"provision\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Enable Neon Auth on the project using the neonctl CLI. \" +\n\t\t\t\t\t\t\t\"Run: `CI= npx -y neonctl neon-auth enable --project-id <project-id> --output json`. \" +\n\t\t\t\t\t\t\t\"You can check current status with: `CI= npx -y neonctl neon-auth status --project-id <project-id> --output json`.\" +\n\t\t\t\t\t\t\t(options.projectId\n\t\t\t\t\t\t\t\t? ` Project ID: ${options.projectId}.`\n\t\t\t\t\t\t\t\t: \" Determine the project ID from the .neon file in the project root, or from the DATABASE_URL in .env, or ask the user.\"),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"install_packages\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.).\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"create_components\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Create auth components per the skill reference. Follow the exact patterns and imports specified.\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"pull_env\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Run `neonctl env pull` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.\",\n\t\t\t\t\t\tcommand: \"CI= npx -y neonctl env pull\",\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: [\n\t\t\t\t\t\t\"finalize\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...(options.agent ? [\"--agent\", options.agent] : []),\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// Default: ask if they want Neon Auth\n\treturn {\n\t\tphase: \"neon_auth\",\n\t\tstatus: \"optional\",\n\t\tnextAction: {\n\t\t\ttype: \"ask_user\",\n\t\t\tquestion:\n\t\t\t\t\"Would you like to set up Neon Auth for user authentication in your app?\",\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\t{ value: \"info\", label: \"Tell me more about Neon Auth\" },\n\t\t\t],\n\t\t\tcontext:\n\t\t\t\t\"Neon Auth provides drop-in user authentication that integrates with your Neon database.\",\n\t\t\tresponseMapping: {\n\t\t\t\tyes: {\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"neon-auth\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...(options.agent ? [\"--agent\", options.agent] : []),\n\t\t\t\t\t\t\"--setup\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tno: {\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"finalize\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...(options.agent ? [\"--agent\", options.agent] : []),\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tinfo: {\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"neon-auth\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...(options.agent ? [\"--agent\", options.agent] : []),\n\t\t\t\t\t\t\"--info\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n"],"mappings":";;AAUA,eAAsB,oBACrB,SACyB;CAEzB,IAAI,QAAQ,aAAa,CAAC,aAAa,KAAK,QAAQ,SAAS,GAC5D,MAAM,IAAI,MACT,wBAAwB,QAAQ,UAAU,iEAC3C;CAID,IAAI,QAAQ,OACX,MAAM,qBAAqB,QAAQ,KAAK;CAGzC,IAAI,QAAQ,MACX,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,UACC;GACD,SAAS,CACR;IAAE,OAAO;IAAO,OAAO;GAAwB,GAC/C;IAAE,OAAO;IAAM,OAAO;GAAmB,CAC1C;GACA,SAAS,uBAAuB,qBAAqB;GACrD,iBAAiB;IAChB,KAAK,EACJ,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QACT,CAAC,WAAW,QAAQ,KAAK,IACzB,CAAC;KACJ;KACA,GAAI,QAAQ,YACT,CAAC,gBAAgB,QAAQ,SAAS,IAClC,CAAC;IACL,EACD;IACA,IAAI,EACH,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QACT,CAAC,WAAW,QAAQ,KAAK,IACzB,CAAC;IACL,EACD;GACD;EACD;CACD;CAID,IAAI,QAAQ,OACX,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,cAAc,qBAAqB;GACnC,OAAO;IACN;KACC,IAAI;KACJ,aACC,kQAGC,QAAQ,YACN,gBAAgB,QAAQ,UAAU,KAClC;IACL;IACA;KACC,IAAI;KACJ,aACC;IACF;IACA;KACC,IAAI;KACJ,aACC;IACF;IACA;KACC,IAAI;KACJ,aACC;KACD,SAAS;IACV;GACD;GACA,YAAY;IACX,MAAM;IACN,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QAAQ,CAAC,WAAW,QAAQ,KAAK,IAAI,CAAC;IACnD;GACD;EACD;CACD;CAID,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,UACC;GACD,SAAS;IACR;KAAE,OAAO;KAAO,OAAO;IAAwB;IAC/C;KAAE,OAAO;KAAM,OAAO;IAAmB;IACzC;KAAE,OAAO;KAAQ,OAAO;IAA+B;GACxD;GACA,SACC;GACD,iBAAiB;IAChB,KAAK,EACJ,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QAAQ,CAAC,WAAW,QAAQ,KAAK,IAAI,CAAC;KAClD;IACD,EACD;IACA,IAAI,EACH,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QAAQ,CAAC,WAAW,QAAQ,KAAK,IAAI,CAAC;IACnD,EACD;IACA,MAAM,EACL,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QAAQ,CAAC,WAAW,QAAQ,KAAK,IAAI,CAAC;KAClD;IACD,EACD;GACD;EACD;CACD;AACD"}
1
+ {"version":3,"file":"neon-auth.js","names":[],"sources":["../../../src/lib/phases/neon-auth.ts"],"sourcesContent":["import { neonctlCmd } from \"../neonctl.js\";\nimport { ensureSkillsUpToDate, SKILL_REFERENCE_URLS } from \"../skills.js\";\nimport type { PhaseResponse } from \"../types.js\";\n\nexport interface NeonAuthPhaseOptions {\n\tagent?: string;\n\tsetup?: boolean;\n\tinfo?: boolean;\n\tprojectId?: string;\n}\n\nexport async function handleNeonAuthPhase(\n\toptions: NeonAuthPhaseOptions,\n): Promise<PhaseResponse> {\n\t// Validate IDs that may be interpolated into instruction strings\n\tif (options.projectId && !/^[\\w.:-]+$/.test(options.projectId)) {\n\t\tthrow new Error(\n\t\t\t`Invalid project ID: \"${options.projectId}\". Expected alphanumeric, hyphens, underscores, dots, or colons.`,\n\t\t);\n\t}\n\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\t// --info: return information about Neon Auth\n\tif (options.info) {\n\t\treturn {\n\t\t\tphase: \"neon_auth\",\n\t\t\tstatus: \"info\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"ask_user\",\n\t\t\t\tquestion:\n\t\t\t\t\t\"Neon Auth provides drop-in user authentication that integrates with your Neon database. It handles user sign-up, sign-in, and session management. Would you like to set it up?\",\n\t\t\t\toptions: [\n\t\t\t\t\t{ value: \"yes\", label: \"Yes, set up Neon Auth\" },\n\t\t\t\t\t{ value: \"no\", label: \"No, skip for now\" },\n\t\t\t\t],\n\t\t\t\tcontext: `Full documentation: ${SKILL_REFERENCE_URLS.neonAuth}`,\n\t\t\t\tresponseMapping: {\n\t\t\t\t\tyes: {\n\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\"neon-auth\",\n\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t...(options.agent\n\t\t\t\t\t\t\t\t? [\"--agent\", options.agent]\n\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t\"--setup\",\n\t\t\t\t\t\t\t...(options.projectId\n\t\t\t\t\t\t\t\t? [\"--project-id\", options.projectId]\n\t\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\tno: {\n\t\t\t\t\t\targs: [\n\t\t\t\t\t\t\t\"finalize\",\n\t\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t\t...(options.agent\n\t\t\t\t\t\t\t\t? [\"--agent\", options.agent]\n\t\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},\n\t\t};\n\t}\n\n\t// --setup: guide through Neon Auth configuration\n\tif (options.setup) {\n\t\treturn {\n\t\t\tphase: \"neon_auth\",\n\t\t\tstatus: \"in_progress\",\n\t\t\tnextAction: {\n\t\t\t\ttype: \"agent_action\",\n\t\t\t\tprerequisite: SKILL_REFERENCE_URLS.neonAuth,\n\t\t\t\tsteps: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"provision\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Enable Neon Auth on the project using the neonctl CLI. \" +\n\t\t\t\t\t\t\t`Run: \\`${neonctlCmd()} neon-auth enable --project-id <project-id> --output json\\`. ` +\n\t\t\t\t\t\t\t`You can check current status with: \\`${neonctlCmd()} neon-auth status --project-id <project-id> --output json\\`.` +\n\t\t\t\t\t\t\t(options.projectId\n\t\t\t\t\t\t\t\t? ` Project ID: ${options.projectId}.`\n\t\t\t\t\t\t\t\t: \" Determine the project ID from the .neon file in the project root, or from the DATABASE_URL in .env, or ask the user.\"),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"install_packages\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Install required packages per the skill reference. The exact packages depend on the framework (Next.js, React, etc.).\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"create_components\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Create auth components per the skill reference. Follow the exact patterns and imports specified.\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"pull_env\",\n\t\t\t\t\t\tdescription: `Run \\`${neonctlCmd()} env pull\\` to populate the NEON_AUTH_BASE_URL, NEON_AUTH_JWKS_URL, and other Neon Auth environment variables. This reads the .neon context file and writes the auth URLs to the project's env file.`,\n\t\t\t\t\t\tcommand: `${neonctlCmd()} env pull`,\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: [\n\t\t\t\t\t\t\"finalize\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...(options.agent ? [\"--agent\", options.agent] : []),\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// Default: ask if they want Neon Auth\n\treturn {\n\t\tphase: \"neon_auth\",\n\t\tstatus: \"optional\",\n\t\tnextAction: {\n\t\t\ttype: \"ask_user\",\n\t\t\tquestion:\n\t\t\t\t\"Would you like to set up Neon Auth for user authentication in your app?\",\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\t{ value: \"info\", label: \"Tell me more about Neon Auth\" },\n\t\t\t],\n\t\t\tcontext:\n\t\t\t\t\"Neon Auth provides drop-in user authentication that integrates with your Neon database.\",\n\t\t\tresponseMapping: {\n\t\t\t\tyes: {\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"neon-auth\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...(options.agent ? [\"--agent\", options.agent] : []),\n\t\t\t\t\t\t\"--setup\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tno: {\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"finalize\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...(options.agent ? [\"--agent\", options.agent] : []),\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tinfo: {\n\t\t\t\t\targs: [\n\t\t\t\t\t\t\"neon-auth\",\n\t\t\t\t\t\t\"--json\",\n\t\t\t\t\t\t...(options.agent ? [\"--agent\", options.agent] : []),\n\t\t\t\t\t\t\"--info\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n"],"mappings":";;;AAWA,eAAsB,oBACrB,SACyB;CAEzB,IAAI,QAAQ,aAAa,CAAC,aAAa,KAAK,QAAQ,SAAS,GAC5D,MAAM,IAAI,MACT,wBAAwB,QAAQ,UAAU,iEAC3C;CAID,IAAI,QAAQ,OACX,MAAM,qBAAqB,QAAQ,KAAK;CAGzC,IAAI,QAAQ,MACX,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,UACC;GACD,SAAS,CACR;IAAE,OAAO;IAAO,OAAO;GAAwB,GAC/C;IAAE,OAAO;IAAM,OAAO;GAAmB,CAC1C;GACA,SAAS,uBAAuB,qBAAqB;GACrD,iBAAiB;IAChB,KAAK,EACJ,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QACT,CAAC,WAAW,QAAQ,KAAK,IACzB,CAAC;KACJ;KACA,GAAI,QAAQ,YACT,CAAC,gBAAgB,QAAQ,SAAS,IAClC,CAAC;IACL,EACD;IACA,IAAI,EACH,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QACT,CAAC,WAAW,QAAQ,KAAK,IACzB,CAAC;IACL,EACD;GACD;EACD;CACD;CAID,IAAI,QAAQ,OACX,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,cAAc,qBAAqB;GACnC,OAAO;IACN;KACC,IAAI;KACJ,aACC,iEACU,WAAW,EAAE,oGACiB,WAAW,EAAE,iEACpD,QAAQ,YACN,gBAAgB,QAAQ,UAAU,KAClC;IACL;IACA;KACC,IAAI;KACJ,aACC;IACF;IACA;KACC,IAAI;KACJ,aACC;IACF;IACA;KACC,IAAI;KACJ,aAAa,SAAS,WAAW,EAAE;KACnC,SAAS,GAAG,WAAW,EAAE;IAC1B;GACD;GACA,YAAY;IACX,MAAM;IACN,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QAAQ,CAAC,WAAW,QAAQ,KAAK,IAAI,CAAC;IACnD;GACD;EACD;CACD;CAID,OAAO;EACN,OAAO;EACP,QAAQ;EACR,YAAY;GACX,MAAM;GACN,UACC;GACD,SAAS;IACR;KAAE,OAAO;KAAO,OAAO;IAAwB;IAC/C;KAAE,OAAO;KAAM,OAAO;IAAmB;IACzC;KAAE,OAAO;KAAQ,OAAO;IAA+B;GACxD;GACA,SACC;GACD,iBAAiB;IAChB,KAAK,EACJ,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QAAQ,CAAC,WAAW,QAAQ,KAAK,IAAI,CAAC;KAClD;IACD,EACD;IACA,IAAI,EACH,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QAAQ,CAAC,WAAW,QAAQ,KAAK,IAAI,CAAC;IACnD,EACD;IACA,MAAM,EACL,MAAM;KACL;KACA;KACA,GAAI,QAAQ,QAAQ,CAAC,WAAW,QAAQ,KAAK,IAAI,CAAC;KAClD;IACD,EACD;GACD;EACD;CACD;AACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon-init",
3
- "version": "0.15.0",
3
+ "version": "0.16.1",
4
4
  "description": "Initialize Neon projects",
5
5
  "keywords": [
6
6
  "neon",