neon-init 0.14.0 → 0.16.0

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.
Files changed (111) hide show
  1. package/dist/cli.js +366 -30
  2. package/dist/cli.js.map +1 -1
  3. package/dist/index.d.ts +15 -3
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +218 -13
  6. package/dist/index.js.map +1 -1
  7. package/dist/interactive.d.ts +12 -0
  8. package/dist/interactive.d.ts.map +1 -0
  9. package/dist/interactive.js +495 -0
  10. package/dist/interactive.js.map +1 -0
  11. package/dist/lib/agents.d.ts +6 -1
  12. package/dist/lib/agents.d.ts.map +1 -1
  13. package/dist/lib/agents.js +62 -1
  14. package/dist/lib/agents.js.map +1 -1
  15. package/dist/lib/auth.d.ts +10 -3
  16. package/dist/lib/auth.d.ts.map +1 -1
  17. package/dist/lib/auth.js +21 -12
  18. package/dist/lib/auth.js.map +1 -1
  19. package/dist/lib/bootstrap.d.ts +30 -0
  20. package/dist/lib/bootstrap.d.ts.map +1 -0
  21. package/dist/lib/bootstrap.js +61 -0
  22. package/dist/lib/bootstrap.js.map +1 -0
  23. package/dist/lib/build-config.d.ts +5 -0
  24. package/dist/lib/build-config.d.ts.map +1 -0
  25. package/dist/lib/build-config.js +6 -0
  26. package/dist/lib/build-config.js.map +1 -0
  27. package/dist/lib/detect-agent.d.ts +22 -0
  28. package/dist/lib/detect-agent.d.ts.map +1 -0
  29. package/dist/lib/detect-agent.js +65 -0
  30. package/dist/lib/detect-agent.js.map +1 -0
  31. package/dist/lib/editors.d.ts.map +1 -1
  32. package/dist/lib/editors.js.map +1 -1
  33. package/dist/lib/extension.d.ts +11 -3
  34. package/dist/lib/extension.d.ts.map +1 -1
  35. package/dist/lib/extension.js +28 -7
  36. package/dist/lib/extension.js.map +1 -1
  37. package/dist/lib/inspect.d.ts +28 -0
  38. package/dist/lib/inspect.d.ts.map +1 -0
  39. package/dist/lib/inspect.js +190 -0
  40. package/dist/lib/inspect.js.map +1 -0
  41. package/dist/lib/install.d.ts +10 -4
  42. package/dist/lib/install.d.ts.map +1 -1
  43. package/dist/lib/install.js +37 -20
  44. package/dist/lib/install.js.map +1 -1
  45. package/dist/lib/neonctl.d.ts +51 -0
  46. package/dist/lib/neonctl.d.ts.map +1 -0
  47. package/dist/lib/neonctl.js +184 -0
  48. package/dist/lib/neonctl.js.map +1 -0
  49. package/dist/lib/phases/auth.d.ts +12 -0
  50. package/dist/lib/phases/auth.d.ts.map +1 -0
  51. package/dist/lib/phases/auth.js +197 -0
  52. package/dist/lib/phases/auth.js.map +1 -0
  53. package/dist/lib/phases/cleanup.d.ts +12 -0
  54. package/dist/lib/phases/cleanup.d.ts.map +1 -0
  55. package/dist/lib/phases/cleanup.js +29 -0
  56. package/dist/lib/phases/cleanup.js.map +1 -0
  57. package/dist/lib/phases/db.d.ts +17 -0
  58. package/dist/lib/phases/db.d.ts.map +1 -0
  59. package/dist/lib/phases/db.js +259 -0
  60. package/dist/lib/phases/db.js.map +1 -0
  61. package/dist/lib/phases/getting-started.d.ts +26 -0
  62. package/dist/lib/phases/getting-started.d.ts.map +1 -0
  63. package/dist/lib/phases/getting-started.js +196 -0
  64. package/dist/lib/phases/getting-started.js.map +1 -0
  65. package/dist/lib/phases/mcp.d.ts +15 -0
  66. package/dist/lib/phases/mcp.d.ts.map +1 -0
  67. package/dist/lib/phases/mcp.js +179 -0
  68. package/dist/lib/phases/mcp.js.map +1 -0
  69. package/dist/lib/phases/migrations.d.ts +14 -0
  70. package/dist/lib/phases/migrations.d.ts.map +1 -0
  71. package/dist/lib/phases/migrations.js +239 -0
  72. package/dist/lib/phases/migrations.js.map +1 -0
  73. package/dist/lib/phases/neon-auth.d.ts +13 -0
  74. package/dist/lib/phases/neon-auth.d.ts.map +1 -0
  75. package/dist/lib/phases/neon-auth.js +118 -0
  76. package/dist/lib/phases/neon-auth.js.map +1 -0
  77. package/dist/lib/phases/setup.d.ts +41 -0
  78. package/dist/lib/phases/setup.d.ts.map +1 -0
  79. package/dist/lib/phases/setup.js +689 -0
  80. package/dist/lib/phases/setup.js.map +1 -0
  81. package/dist/lib/phases/skills.d.ts +14 -0
  82. package/dist/lib/phases/skills.d.ts.map +1 -0
  83. package/dist/lib/phases/skills.js +80 -0
  84. package/dist/lib/phases/skills.js.map +1 -0
  85. package/dist/lib/phases/status.d.ts +10 -0
  86. package/dist/lib/phases/status.d.ts.map +1 -0
  87. package/dist/lib/phases/status.js +65 -0
  88. package/dist/lib/phases/status.js.map +1 -0
  89. package/dist/lib/resolve-context.d.ts +19 -0
  90. package/dist/lib/resolve-context.d.ts.map +1 -0
  91. package/dist/lib/resolve-context.js +112 -0
  92. package/dist/lib/resolve-context.js.map +1 -0
  93. package/dist/lib/route-command.d.ts +8 -0
  94. package/dist/lib/route-command.d.ts.map +1 -0
  95. package/dist/lib/route-command.js +195 -0
  96. package/dist/lib/route-command.js.map +1 -0
  97. package/dist/lib/skills.d.ts +20 -3
  98. package/dist/lib/skills.d.ts.map +1 -1
  99. package/dist/lib/skills.js +116 -12
  100. package/dist/lib/skills.js.map +1 -1
  101. package/dist/lib/types.d.ts +150 -1
  102. package/dist/lib/types.d.ts.map +1 -1
  103. package/dist/lib/vsix.d.ts +15 -0
  104. package/dist/lib/vsix.d.ts.map +1 -0
  105. package/dist/lib/vsix.js +91 -0
  106. package/dist/lib/vsix.js.map +1 -0
  107. package/dist/v2.d.ts +31 -0
  108. package/dist/v2.d.ts.map +1 -0
  109. package/dist/v2.js +147 -0
  110. package/dist/v2.js.map +1 -0
  111. package/package.json +7 -3
@@ -0,0 +1,91 @@
1
+ import { createWriteStream } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { tmpdir } from "node:os";
4
+ import { pipeline } from "node:stream/promises";
5
+ //#region src/lib/vsix.ts
6
+ /**
7
+ * Shared VSIX download utilities for extension installation.
8
+ * Supports corporate proxy via NEON_VSX_GALLERY_URL env var.
9
+ */
10
+ const NEON_EXTENSION_ID = "databricks.neon-local-connect";
11
+ const OPEN_VSX_API = "https://open-vsx.org/api";
12
+ /**
13
+ * Downloads a .vsix file for the Neon extension.
14
+ *
15
+ * Strategy:
16
+ * 1. If NEON_VSX_GALLERY_URL is set, download from the corporate proxy gallery
17
+ * 2. Otherwise, download from the public Open VSX API
18
+ *
19
+ * Returns the path to the temp .vsix file, or null on failure.
20
+ */
21
+ async function downloadVsix() {
22
+ const { INTERNAL_VSX_GALLERY } = await import("./build-config.js");
23
+ const proxyGallery = process.env.NEON_VSX_GALLERY_URL || INTERNAL_VSX_GALLERY || "";
24
+ if (proxyGallery) return downloadFromGallery(proxyGallery);
25
+ return downloadFromOpenVsx();
26
+ }
27
+ /**
28
+ * Downloads from a VS Code marketplace-compatible gallery API (corporate proxy).
29
+ * Uses the VS Code extensionquery POST API to find the VSIX download URL.
30
+ */
31
+ async function downloadFromGallery(galleryUrl) {
32
+ const [publisher, name] = NEON_EXTENSION_ID.split(".");
33
+ const queryUrl = `${galleryUrl.replace(/\/+$/, "")}/extensionquery`;
34
+ try {
35
+ const queryRes = await fetch(queryUrl, {
36
+ method: "POST",
37
+ headers: {
38
+ "Content-Type": "application/json",
39
+ Accept: "application/json;api-version=6.1-preview.1"
40
+ },
41
+ body: JSON.stringify({
42
+ filters: [{ criteria: [{
43
+ filterType: 7,
44
+ value: `${publisher}.${name}`
45
+ }] }],
46
+ flags: 914
47
+ }),
48
+ signal: AbortSignal.timeout(15e3)
49
+ });
50
+ if (!queryRes.ok) return null;
51
+ const vsixFile = (((await queryRes.json()).results?.[0]?.extensions?.[0])?.versions?.[0])?.files?.find((f) => f.assetType === "Microsoft.VisualStudio.Services.VSIXPackage");
52
+ if (!vsixFile?.source) return null;
53
+ const vsixRes = await fetch(vsixFile.source, {
54
+ signal: AbortSignal.timeout(3e4),
55
+ redirect: "follow"
56
+ });
57
+ if (!vsixRes.ok || !vsixRes.body) return null;
58
+ const tmpPath = join(tmpdir(), `${NEON_EXTENSION_ID}-proxy.vsix`);
59
+ const fileStream = createWriteStream(tmpPath);
60
+ await pipeline(vsixRes.body, fileStream);
61
+ return tmpPath;
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+ /**
67
+ * Downloads from the public Open VSX API.
68
+ */
69
+ async function downloadFromOpenVsx() {
70
+ const [publisher, name] = NEON_EXTENSION_ID.split(".");
71
+ const metaUrl = `${OPEN_VSX_API}/${publisher}/${name}/latest`;
72
+ try {
73
+ const metaRes = await fetch(metaUrl, { signal: AbortSignal.timeout(1e4) });
74
+ if (!metaRes.ok) return null;
75
+ const meta = await metaRes.json();
76
+ const downloadUrl = meta.files?.download;
77
+ if (!downloadUrl) return null;
78
+ const vsixRes = await fetch(downloadUrl, { signal: AbortSignal.timeout(3e4) });
79
+ if (!vsixRes.ok || !vsixRes.body) return null;
80
+ const tmpPath = join(tmpdir(), `${NEON_EXTENSION_ID}-${meta.version ?? "latest"}.vsix`);
81
+ const fileStream = createWriteStream(tmpPath);
82
+ await pipeline(vsixRes.body, fileStream);
83
+ return tmpPath;
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+ //#endregion
89
+ export { NEON_EXTENSION_ID, downloadVsix };
90
+
91
+ //# sourceMappingURL=vsix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vsix.js","names":[],"sources":["../../src/lib/vsix.ts"],"sourcesContent":["/**\n * Shared VSIX download utilities for extension installation.\n * Supports corporate proxy via NEON_VSX_GALLERY_URL env var.\n */\nimport { createWriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\n\nexport const NEON_EXTENSION_ID = \"databricks.neon-local-connect\";\nconst OPEN_VSX_API = \"https://open-vsx.org/api\";\n\n/**\n * Downloads a .vsix file for the Neon extension.\n *\n * Strategy:\n * 1. If NEON_VSX_GALLERY_URL is set, download from the corporate proxy gallery\n * 2. Otherwise, download from the public Open VSX API\n *\n * Returns the path to the temp .vsix file, or null on failure.\n */\nexport async function downloadVsix(): Promise<string | null> {\n\t// Runtime env var takes priority, then build-time baked value\n\tconst { INTERNAL_VSX_GALLERY } = await import(\"./build-config.js\");\n\tconst proxyGallery =\n\t\tprocess.env.NEON_VSX_GALLERY_URL || INTERNAL_VSX_GALLERY || \"\";\n\tif (proxyGallery) {\n\t\treturn downloadFromGallery(proxyGallery);\n\t}\n\treturn downloadFromOpenVsx();\n}\n\n/**\n * Downloads from a VS Code marketplace-compatible gallery API (corporate proxy).\n * Uses the VS Code extensionquery POST API to find the VSIX download URL.\n */\nasync function downloadFromGallery(galleryUrl: string): Promise<string | null> {\n\tconst [publisher, name] = NEON_EXTENSION_ID.split(\".\");\n\tconst baseUrl = galleryUrl.replace(/\\/+$/, \"\");\n\tconst queryUrl = `${baseUrl}/extensionquery`;\n\n\ttry {\n\t\t// Query the marketplace API for the extension\n\t\tconst queryRes = await fetch(queryUrl, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\tAccept: \"application/json;api-version=6.1-preview.1\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tfilters: [\n\t\t\t\t\t{\n\t\t\t\t\t\tcriteria: [\n\t\t\t\t\t\t\t{ filterType: 7, value: `${publisher}.${name}` },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tflags: 914,\n\t\t\t}),\n\t\t\tsignal: AbortSignal.timeout(15000),\n\t\t});\n\n\t\tif (!queryRes.ok) return null;\n\n\t\tconst data = (await queryRes.json()) as {\n\t\t\tresults?: {\n\t\t\t\textensions?: {\n\t\t\t\t\tversions?: {\n\t\t\t\t\t\tfiles?: { assetType: string; source: string }[];\n\t\t\t\t\t}[];\n\t\t\t\t}[];\n\t\t\t}[];\n\t\t};\n\n\t\t// Find the VSIX download URL from the response\n\t\tconst extension = data.results?.[0]?.extensions?.[0];\n\t\tconst latestVersion = extension?.versions?.[0];\n\t\tconst vsixFile = latestVersion?.files?.find(\n\t\t\t(f) =>\n\t\t\t\tf.assetType === \"Microsoft.VisualStudio.Services.VSIXPackage\",\n\t\t);\n\n\t\tif (!vsixFile?.source) return null;\n\n\t\t// Download the VSIX\n\t\tconst vsixRes = await fetch(vsixFile.source, {\n\t\t\tsignal: AbortSignal.timeout(30000),\n\t\t\tredirect: \"follow\",\n\t\t});\n\t\tif (!vsixRes.ok || !vsixRes.body) return null;\n\n\t\tconst tmpPath = join(tmpdir(), `${NEON_EXTENSION_ID}-proxy.vsix`);\n\t\tconst fileStream = createWriteStream(tmpPath);\n\t\tawait pipeline(\n\t\t\tvsixRes.body as unknown as NodeJS.ReadableStream,\n\t\t\tfileStream,\n\t\t);\n\t\treturn tmpPath;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Downloads from the public Open VSX API.\n */\nasync function downloadFromOpenVsx(): Promise<string | null> {\n\tconst [publisher, name] = NEON_EXTENSION_ID.split(\".\");\n\tconst metaUrl = `${OPEN_VSX_API}/${publisher}/${name}/latest`;\n\n\ttry {\n\t\tconst metaRes = await fetch(metaUrl, {\n\t\t\tsignal: AbortSignal.timeout(10000),\n\t\t});\n\t\tif (!metaRes.ok) return null;\n\n\t\tconst meta = (await metaRes.json()) as {\n\t\t\tfiles?: { download?: string };\n\t\t\tversion?: string;\n\t\t};\n\t\tconst downloadUrl = meta.files?.download;\n\t\tif (!downloadUrl) return null;\n\n\t\tconst vsixRes = await fetch(downloadUrl, {\n\t\t\tsignal: AbortSignal.timeout(30000),\n\t\t});\n\t\tif (!vsixRes.ok || !vsixRes.body) return null;\n\n\t\tconst tmpPath = join(\n\t\t\ttmpdir(),\n\t\t\t`${NEON_EXTENSION_ID}-${meta.version ?? \"latest\"}.vsix`,\n\t\t);\n\t\tconst fileStream = createWriteStream(tmpPath);\n\t\tawait pipeline(\n\t\t\tvsixRes.body as unknown as NodeJS.ReadableStream,\n\t\t\tfileStream,\n\t\t);\n\t\treturn tmpPath;\n\t} catch {\n\t\treturn null;\n\t}\n}\n"],"mappings":";;;;;;;;;AASA,MAAa,oBAAoB;AACjC,MAAM,eAAe;;;;;;;;;;AAWrB,eAAsB,eAAuC;CAE5D,MAAM,EAAE,yBAAyB,MAAM,OAAO;CAC9C,MAAM,eACL,QAAQ,IAAI,wBAAwB,wBAAwB;CAC7D,IAAI,cACH,OAAO,oBAAoB,YAAY;CAExC,OAAO,oBAAoB;AAC5B;;;;;AAMA,eAAe,oBAAoB,YAA4C;CAC9E,MAAM,CAAC,WAAW,QAAQ,kBAAkB,MAAM,GAAG;CAErD,MAAM,WAAW,GADD,WAAW,QAAQ,QAAQ,EACjB,EAAE;CAE5B,IAAI;EAEH,MAAM,WAAW,MAAM,MAAM,UAAU;GACtC,QAAQ;GACR,SAAS;IACR,gBAAgB;IAChB,QAAQ;GACT;GACA,MAAM,KAAK,UAAU;IACpB,SAAS,CACR,EACC,UAAU,CACT;KAAE,YAAY;KAAG,OAAO,GAAG,UAAU,GAAG;IAAO,CAChD,EACD,CACD;IACA,OAAO;GACR,CAAC;GACD,QAAQ,YAAY,QAAQ,IAAK;EAClC,CAAC;EAED,IAAI,CAAC,SAAS,IAAI,OAAO;EAezB,MAAM,cAFY,MAXE,SAAS,KAAK,EAAA,CAWX,UAAU,EAAE,EAAE,aAAa,EAAE,GACnB,WAAW,EAAE,GACd,OAAO,MACrC,MACA,EAAE,cAAc,6CAClB;EAEA,IAAI,CAAC,UAAU,QAAQ,OAAO;EAG9B,MAAM,UAAU,MAAM,MAAM,SAAS,QAAQ;GAC5C,QAAQ,YAAY,QAAQ,GAAK;GACjC,UAAU;EACX,CAAC;EACD,IAAI,CAAC,QAAQ,MAAM,CAAC,QAAQ,MAAM,OAAO;EAEzC,MAAM,UAAU,KAAK,OAAO,GAAG,GAAG,kBAAkB,YAAY;EAChE,MAAM,aAAa,kBAAkB,OAAO;EAC5C,MAAM,SACL,QAAQ,MACR,UACD;EACA,OAAO;CACR,QAAQ;EACP,OAAO;CACR;AACD;;;;AAKA,eAAe,sBAA8C;CAC5D,MAAM,CAAC,WAAW,QAAQ,kBAAkB,MAAM,GAAG;CACrD,MAAM,UAAU,GAAG,aAAa,GAAG,UAAU,GAAG,KAAK;CAErD,IAAI;EACH,MAAM,UAAU,MAAM,MAAM,SAAS,EACpC,QAAQ,YAAY,QAAQ,GAAK,EAClC,CAAC;EACD,IAAI,CAAC,QAAQ,IAAI,OAAO;EAExB,MAAM,OAAQ,MAAM,QAAQ,KAAK;EAIjC,MAAM,cAAc,KAAK,OAAO;EAChC,IAAI,CAAC,aAAa,OAAO;EAEzB,MAAM,UAAU,MAAM,MAAM,aAAa,EACxC,QAAQ,YAAY,QAAQ,GAAK,EAClC,CAAC;EACD,IAAI,CAAC,QAAQ,MAAM,CAAC,QAAQ,MAAM,OAAO;EAEzC,MAAM,UAAU,KACf,OAAO,GACP,GAAG,kBAAkB,GAAG,KAAK,WAAW,SAAS,MAClD;EACA,MAAM,aAAa,kBAAkB,OAAO;EAC5C,MAAM,SACL,QAAQ,MACR,UACD;EACA,OAAO;CACR,QAAQ;EACP,OAAO;CACR;AACD"}
package/dist/v2.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ import { PhaseResponse } from "./lib/types.js";
2
+
3
+ //#region src/v2.d.ts
4
+ interface OrchestratorOptions {
5
+ agent?: string;
6
+ skipNeonAuth?: boolean;
7
+ skipMigrations?: boolean;
8
+ /** Enable preview features (e.g. project bootstrapping from templates) */
9
+ preview?: boolean;
10
+ }
11
+ /**
12
+ * v2 orchestrator: checks phases in order and returns the first that needs attention.
13
+ *
14
+ * Phase order:
15
+ * auth -> setup (if tooling not installed)
16
+ * -> getting-started (if tooling installed but no Neon connection string)
17
+ * -> resolve .neon context (if connection string exists but no .neon file)
18
+ * -> neon_auth (optional) -> complete
19
+ *
20
+ * Each call is stateless — it re-checks everything from the file system and credentials.
21
+ *
22
+ * The orchestrator uses filesystem inspection to decide what to do:
23
+ * - No app detected → bootstrap phase (scaffold from template)
24
+ * - MCP not configured → full setup flow (inspect → install → getting-started)
25
+ * - MCP configured, no connection string → skip install, go to getting-started
26
+ * - MCP configured + connection string → fall through to neon-auth/migrations/complete
27
+ */
28
+ declare function orchestrate(options: OrchestratorOptions): Promise<PhaseResponse>;
29
+ //#endregion
30
+ export { OrchestratorOptions, orchestrate };
31
+ //# sourceMappingURL=v2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v2.d.ts","names":[],"sources":["../src/v2.ts"],"mappings":";;;UAYiB,mBAAA;;EAAA,YAAA,CAAA,EAAA,OAAmB;EAyBd,cAAW,CAAA,EAAA,OAAA;EAAA;SACvB,CAAA,EAAA,OAAA;;;AACA;;;;;;;;;;;;;;;;iBAFY,WAAA,UACZ,sBACP,QAAQ"}
package/dist/v2.js ADDED
@@ -0,0 +1,147 @@
1
+ import { isAuthenticated } from "./lib/auth.js";
2
+ import { inspectProject } from "./lib/inspect.js";
3
+ import { handleAuthPhase } from "./lib/phases/auth.js";
4
+ import { handleGettingStartedPhase } from "./lib/phases/getting-started.js";
5
+ import { handleMigrationsPhase } from "./lib/phases/migrations.js";
6
+ import { handleNeonAuthPhase } from "./lib/phases/neon-auth.js";
7
+ import { handleSetupPhase } from "./lib/phases/setup.js";
8
+ import { resolveNeonContext } from "./lib/resolve-context.js";
9
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
10
+ import { resolve } from "node:path";
11
+ //#region src/v2.ts
12
+ /**
13
+ * v2 orchestrator: checks phases in order and returns the first that needs attention.
14
+ *
15
+ * Phase order:
16
+ * auth -> setup (if tooling not installed)
17
+ * -> getting-started (if tooling installed but no Neon connection string)
18
+ * -> resolve .neon context (if connection string exists but no .neon file)
19
+ * -> neon_auth (optional) -> complete
20
+ *
21
+ * Each call is stateless — it re-checks everything from the file system and credentials.
22
+ *
23
+ * The orchestrator uses filesystem inspection to decide what to do:
24
+ * - No app detected → bootstrap phase (scaffold from template)
25
+ * - MCP not configured → full setup flow (inspect → install → getting-started)
26
+ * - MCP configured, no connection string → skip install, go to getting-started
27
+ * - MCP configured + connection string → fall through to neon-auth/migrations/complete
28
+ */
29
+ async function orchestrate(options) {
30
+ if (!await isAuthenticated()) return handleAuthPhase({ agent: options.agent });
31
+ const cwd = process.cwd();
32
+ const inspection = await inspectProject([
33
+ {
34
+ id: "has_app",
35
+ description: "",
36
+ lookFor: []
37
+ },
38
+ {
39
+ id: "mcp_server",
40
+ description: "",
41
+ lookFor: []
42
+ },
43
+ {
44
+ id: "skills",
45
+ description: "",
46
+ lookFor: []
47
+ },
48
+ {
49
+ id: "connection_string",
50
+ description: "",
51
+ lookFor: []
52
+ },
53
+ {
54
+ id: "project_stack",
55
+ description: "",
56
+ lookFor: []
57
+ },
58
+ {
59
+ id: "migrations",
60
+ description: "",
61
+ lookFor: []
62
+ }
63
+ ]);
64
+ const hasApp = options.preview ? inspection.hasApp === true : true;
65
+ const toolingInstalled = inspection.mcpConfigured && inspection.skillsInstalled;
66
+ const hasNeonConnection = inspection.connectionString === true;
67
+ if (!hasApp || !toolingInstalled) {
68
+ cleanupInitState(resolve(cwd, ".neon"));
69
+ return handleSetupPhase({
70
+ agent: options.agent,
71
+ hasApp
72
+ });
73
+ }
74
+ const neonContextPath = resolve(cwd, ".neon");
75
+ const neonContext = readNeonContext(neonContextPath);
76
+ const initState = typeof neonContext._init === "object" && neonContext._init !== null ? neonContext._init : {};
77
+ const features = Array.isArray(initState.features) ? initState.features : [];
78
+ if (!hasNeonConnection) return handleGettingStartedPhase({
79
+ agent: options.agent,
80
+ hasConnectionString: false,
81
+ framework: inspection.framework,
82
+ orm: inspection.orm,
83
+ migrationTool: inspection.migrationTool,
84
+ migrationDir: inspection.migrationDir,
85
+ features,
86
+ preview: options.preview
87
+ });
88
+ if (!neonContext.projectId) try {
89
+ const resolved = await resolveNeonContext(cwd);
90
+ if (resolved) {
91
+ const merged = { ...neonContext };
92
+ if (resolved.orgId) merged.orgId = resolved.orgId;
93
+ if (resolved.projectId) merged.projectId = resolved.projectId;
94
+ writeFileSync(neonContextPath, `${JSON.stringify(merged, null, 2)}\n`);
95
+ }
96
+ } catch {}
97
+ const hasFeatureRequirements = features.length > 0;
98
+ if (hasFeatureRequirements ? features.includes("auth") : !options.skipNeonAuth) {
99
+ if (!checkNeonAuth(cwd)) return handleNeonAuthPhase({
100
+ agent: options.agent,
101
+ setup: hasFeatureRequirements
102
+ });
103
+ }
104
+ if (!options.skipMigrations) return handleMigrationsPhase({ agent: options.agent });
105
+ cleanupInitState(neonContextPath);
106
+ return {
107
+ phase: "setup",
108
+ status: "complete",
109
+ nextAction: {
110
+ type: "complete",
111
+ message: "Neon setup is complete. Your database is configured and your agent has the Neon MCP server and skills available."
112
+ }
113
+ };
114
+ }
115
+ /** Remove the ephemeral _init key from .neon, preserving other fields. */
116
+ function cleanupInitState(neonContextPath) {
117
+ if (!existsSync(neonContextPath)) return;
118
+ try {
119
+ const context = JSON.parse(readFileSync(neonContextPath, "utf-8"));
120
+ if (context._init !== void 0) {
121
+ delete context._init;
122
+ writeFileSync(neonContextPath, `${JSON.stringify(context, null, 2)}\n`);
123
+ }
124
+ } catch {}
125
+ }
126
+ function readNeonContext(neonContextPath) {
127
+ if (!existsSync(neonContextPath)) return {};
128
+ try {
129
+ return JSON.parse(readFileSync(neonContextPath, "utf-8"));
130
+ } catch {
131
+ return {};
132
+ }
133
+ }
134
+ function checkNeonAuth(cwd) {
135
+ const envPath = resolve(cwd, ".env");
136
+ if (!existsSync(envPath)) return false;
137
+ try {
138
+ const content = readFileSync(envPath, "utf-8");
139
+ return /^NEON_AUTH_/m.test(content);
140
+ } catch {
141
+ return false;
142
+ }
143
+ }
144
+ //#endregion
145
+ export { orchestrate };
146
+
147
+ //# sourceMappingURL=v2.js.map
package/dist/v2.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v2.js","names":[],"sources":["../src/v2.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { isAuthenticated } from \"./lib/auth.js\";\nimport { inspectProject } from \"./lib/inspect.js\";\nimport { handleAuthPhase } from \"./lib/phases/auth.js\";\nimport { handleGettingStartedPhase } from \"./lib/phases/getting-started.js\";\nimport { handleMigrationsPhase } from \"./lib/phases/migrations.js\";\nimport { handleNeonAuthPhase } from \"./lib/phases/neon-auth.js\";\nimport { handleSetupPhase } from \"./lib/phases/setup.js\";\nimport { resolveNeonContext } from \"./lib/resolve-context.js\";\nimport type { PhaseResponse } from \"./lib/types.js\";\n\nexport interface OrchestratorOptions {\n\tagent?: string;\n\tskipNeonAuth?: boolean;\n\tskipMigrations?: boolean;\n\t/** Enable preview features (e.g. project bootstrapping from templates) */\n\tpreview?: boolean;\n}\n\n/**\n * v2 orchestrator: checks phases in order and returns the first that needs attention.\n *\n * Phase order:\n * auth -> setup (if tooling not installed)\n * -> getting-started (if tooling installed but no Neon connection string)\n * -> resolve .neon context (if connection string exists but no .neon file)\n * -> neon_auth (optional) -> complete\n *\n * Each call is stateless — it re-checks everything from the file system and credentials.\n *\n * The orchestrator uses filesystem inspection to decide what to do:\n * - No app detected → bootstrap phase (scaffold from template)\n * - MCP not configured → full setup flow (inspect → install → getting-started)\n * - MCP configured, no connection string → skip install, go to getting-started\n * - MCP configured + connection string → fall through to neon-auth/migrations/complete\n */\nexport async function orchestrate(\n\toptions: OrchestratorOptions,\n): Promise<PhaseResponse> {\n\t// Phase 1: Auth\n\tconst authed = await isAuthenticated();\n\tif (!authed) {\n\t\treturn handleAuthPhase({ agent: options.agent });\n\t}\n\n\tconst cwd = process.cwd();\n\n\t// Phase 2: Inspect what's already in place\n\tconst inspection = await inspectProject([\n\t\t{ id: \"has_app\", description: \"\", lookFor: [] },\n\t\t{ id: \"mcp_server\", description: \"\", lookFor: [] },\n\t\t{ id: \"skills\", description: \"\", lookFor: [] },\n\t\t{ id: \"connection_string\", description: \"\", lookFor: [] },\n\t\t{ id: \"project_stack\", description: \"\", lookFor: [] },\n\t\t{ id: \"migrations\", description: \"\", lookFor: [] },\n\t]);\n\n\t// Only detect empty projects when --preview is enabled\n\tconst hasApp = options.preview ? inspection.hasApp === true : true;\n\tconst toolingInstalled =\n\t\tinspection.mcpConfigured && inspection.skillsInstalled;\n\tconst hasNeonConnection = inspection.connectionString === true;\n\n\t// Phase 3a: No app or tooling not installed → setup flow\n\t// When !hasApp (preview mode), setup will offer template selection before asking about tooling.\n\t// Clean up any stale _init state from a previous run.\n\tif (!hasApp || !toolingInstalled) {\n\t\tcleanupInitState(resolve(cwd, \".neon\"));\n\t\treturn handleSetupPhase({ agent: options.agent, hasApp });\n\t}\n\n\t// Read .neon context early — needed for feature-based routing\n\tconst neonContextPath = resolve(cwd, \".neon\");\n\tconst neonContext = readNeonContext(neonContextPath);\n\tconst initState =\n\t\ttypeof neonContext._init === \"object\" && neonContext._init !== null\n\t\t\t? (neonContext._init as Record<string, unknown>)\n\t\t\t: {};\n\tconst features: string[] = Array.isArray(initState.features)\n\t\t? initState.features\n\t\t: [];\n\n\t// Phase 3b: Tooling installed but no Neon connection string → getting-started\n\tif (!hasNeonConnection) {\n\t\treturn handleGettingStartedPhase({\n\t\t\tagent: options.agent,\n\t\t\thasConnectionString: false,\n\t\t\tframework: inspection.framework as string | undefined,\n\t\t\torm: inspection.orm as string | undefined,\n\t\t\tmigrationTool: inspection.migrationTool as string | undefined,\n\t\t\tmigrationDir: inspection.migrationDir as string | undefined,\n\t\t\tfeatures,\n\t\t\tpreview: options.preview,\n\t\t});\n\t}\n\n\t// Phase 3c: Neon connection exists but no .neon context file → resolve project\n\n\tif (!neonContext.projectId) {\n\t\t// Resolve the project from the connection string and merge into .neon\n\t\ttry {\n\t\t\tconst resolved = await resolveNeonContext(cwd);\n\t\t\tif (resolved) {\n\t\t\t\tconst merged = { ...neonContext };\n\t\t\t\tif (resolved.orgId) merged.orgId = resolved.orgId;\n\t\t\t\tif (resolved.projectId) merged.projectId = resolved.projectId;\n\t\t\t\twriteFileSync(\n\t\t\t\t\tneonContextPath,\n\t\t\t\t\t`${JSON.stringify(merged, null, 2)}\\n`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Continue the flow regardless — missing .neon is not a blocker\n\t\t}\n\t}\n\n\t// Phase 4: Neon Auth — skip if template/user doesn't require it\n\tconst hasFeatureRequirements = features.length > 0;\n\tconst needsAuth = hasFeatureRequirements\n\t\t? features.includes(\"auth\")\n\t\t: !options.skipNeonAuth;\n\tif (needsAuth) {\n\t\tconst hasNeonAuth = checkNeonAuth(cwd);\n\t\tif (!hasNeonAuth) {\n\t\t\t// If auth was already selected via features, go straight to setup\n\t\t\t// (don't re-ask the user)\n\t\t\treturn handleNeonAuthPhase({\n\t\t\t\tagent: options.agent,\n\t\t\t\tsetup: hasFeatureRequirements,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Phase 5: Migrations\n\tif (!options.skipMigrations) {\n\t\treturn handleMigrationsPhase({ agent: options.agent });\n\t}\n\n\t// All done — clean up ephemeral _init state from .neon\n\tcleanupInitState(neonContextPath);\n\n\treturn {\n\t\tphase: \"setup\",\n\t\tstatus: \"complete\",\n\t\tnextAction: {\n\t\t\ttype: \"complete\",\n\t\t\tmessage:\n\t\t\t\t\"Neon setup is complete. Your database is configured and your agent has the Neon MCP server and skills available.\",\n\t\t},\n\t};\n}\n\n/** Remove the ephemeral _init key from .neon, preserving other fields. */\nfunction cleanupInitState(neonContextPath: string): void {\n\tif (!existsSync(neonContextPath)) return;\n\ttry {\n\t\tconst context = JSON.parse(readFileSync(neonContextPath, \"utf-8\"));\n\t\tif (context._init !== undefined) {\n\t\t\tdelete context._init;\n\t\t\twriteFileSync(\n\t\t\t\tneonContextPath,\n\t\t\t\t`${JSON.stringify(context, null, 2)}\\n`,\n\t\t\t);\n\t\t}\n\t} catch {}\n}\n\nfunction readNeonContext(neonContextPath: string): Record<string, unknown> {\n\tif (!existsSync(neonContextPath)) return {};\n\ttry {\n\t\treturn JSON.parse(readFileSync(neonContextPath, \"utf-8\"));\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction checkNeonAuth(cwd: string): boolean {\n\tconst envPath = resolve(cwd, \".env\");\n\tif (!existsSync(envPath)) return false;\n\ttry {\n\t\tconst content = readFileSync(envPath, \"utf-8\");\n\t\treturn /^NEON_AUTH_/m.test(content);\n\t} catch {\n\t\treturn false;\n\t}\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon-init",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Initialize Neon projects",
5
5
  "keywords": [
6
6
  "neon",
@@ -46,6 +46,8 @@
46
46
  "@clack/prompts": "0.10.1",
47
47
  "add-mcp": "^1.5.1",
48
48
  "execa": "^9.5.2",
49
+ "picocolors": "^1.1.1",
50
+ "yaml": "^2.9.0",
49
51
  "yargs": "^18.0.0",
50
52
  "yoctocolors": "^2.1.2"
51
53
  },
@@ -56,9 +58,11 @@
56
58
  "provenance": false
57
59
  },
58
60
  "scripts": {
59
- "build": "tsc --noEmit && tsdown",
61
+ "build": "node scripts/set-vsx-gallery.mjs && tsc --noEmit && tsdown",
62
+ "build:internal": "node scripts/set-vsx-gallery.mjs https://cursor-vsx-proxy.cloud.databricks.com/gallery && tsc --noEmit && tsdown",
63
+ "pretest": "node scripts/set-vsx-gallery.mjs",
60
64
  "test": "vitest --passWithNoTests",
61
65
  "test:ci": "vitest run --passWithNoTests",
62
- "tsc": "tsc"
66
+ "tsc": "node scripts/set-vsx-gallery.mjs && tsc"
63
67
  }
64
68
  }