neon 2.45.0 → 2.46.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 (52) hide show
  1. package/README.md +62 -1
  2. package/dist/_shared/paths.js +3 -4
  3. package/dist/commands/bootstrap.js +12 -11
  4. package/dist/commands/checkout.js +7 -6
  5. package/dist/commands/config.js +2 -1
  6. package/dist/commands/data_api.js +4 -3
  7. package/dist/commands/env.js +5 -4
  8. package/dist/commands/functions.js +3 -2
  9. package/dist/commands/init.js +82 -37
  10. package/dist/commands/ip_allow.js +3 -2
  11. package/dist/commands/link.js +17 -16
  12. package/dist/commands/projects.js +3 -2
  13. package/dist/commands/set_context.js +5 -4
  14. package/dist/current_branch_fast_path.js +2 -1
  15. package/dist/dev/env.js +8 -7
  16. package/dist/index.js +2 -2
  17. package/dist/init/agents.js +127 -0
  18. package/dist/init/auth.js +77 -0
  19. package/dist/init/bootstrap.js +448 -0
  20. package/dist/init/build_config.js +2 -0
  21. package/dist/init/detect_agent.js +108 -0
  22. package/dist/init/editors.js +62 -0
  23. package/dist/init/enrich_output.js +71 -0
  24. package/dist/init/extension.js +191 -0
  25. package/dist/init/inspect.js +287 -0
  26. package/dist/init/interactive.js +651 -0
  27. package/dist/init/neonctl.js +184 -0
  28. package/dist/init/orchestrate.js +190 -0
  29. package/dist/init/phases/auth.js +209 -0
  30. package/dist/init/phases/cleanup.js +27 -0
  31. package/dist/init/phases/db.js +283 -0
  32. package/dist/init/phases/getting_started.js +228 -0
  33. package/dist/init/phases/mcp.js +227 -0
  34. package/dist/init/phases/migrations.js +251 -0
  35. package/dist/init/phases/neon_auth.js +135 -0
  36. package/dist/init/phases/setup.js +729 -0
  37. package/dist/init/phases/skills.js +89 -0
  38. package/dist/init/phases/status.js +70 -0
  39. package/dist/init/resolve_context.js +107 -0
  40. package/dist/init/route_command.js +100 -0
  41. package/dist/init/skills.js +248 -0
  42. package/dist/init/types.js +1 -0
  43. package/dist/init/vsix.js +111 -0
  44. package/dist/psql/command/cmd_meta.js +2 -2
  45. package/dist/psql/core/mainloop.js +1 -1
  46. package/dist/psql/core/startup.js +1 -1
  47. package/dist/psql/core/syncVars.js +3 -3
  48. package/dist/psql/index.js +1 -1
  49. package/dist/utils/cli_name.js +14 -0
  50. package/dist/utils/esbuild.js +1 -1
  51. package/dist/utils/write_sync.js +39 -0
  52. package/package.json +18 -12
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Shared VSIX download utilities for extension installation.
3
+ * Supports corporate proxy via NEON_VSX_GALLERY_URL env var.
4
+ */
5
+ import { createWriteStream } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { pipeline } from "node:stream/promises";
9
+ import { INTERNAL_VSX_GALLERY } from "./build_config.js";
10
+ export 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
+ export async function downloadVsix() {
22
+ // Runtime env var takes priority, then build-time baked value
23
+ const proxyGallery = process.env.NEON_VSX_GALLERY_URL || INTERNAL_VSX_GALLERY || "";
24
+ if (proxyGallery) {
25
+ return downloadFromGallery(proxyGallery);
26
+ }
27
+ return downloadFromOpenVsx();
28
+ }
29
+ /**
30
+ * Downloads from a VS Code marketplace-compatible gallery API (corporate proxy).
31
+ * Uses the VS Code extensionquery POST API to find the VSIX download URL.
32
+ */
33
+ async function downloadFromGallery(galleryUrl) {
34
+ const [publisher, name] = NEON_EXTENSION_ID.split(".");
35
+ const baseUrl = galleryUrl.replace(/\/+$/, "");
36
+ const queryUrl = `${baseUrl}/extensionquery`;
37
+ try {
38
+ // Query the marketplace API for the extension
39
+ const queryRes = await fetch(queryUrl, {
40
+ method: "POST",
41
+ headers: {
42
+ "Content-Type": "application/json",
43
+ Accept: "application/json;api-version=6.1-preview.1",
44
+ },
45
+ body: JSON.stringify({
46
+ filters: [
47
+ {
48
+ criteria: [
49
+ { filterType: 7, value: `${publisher}.${name}` },
50
+ ],
51
+ },
52
+ ],
53
+ flags: 914,
54
+ }),
55
+ signal: AbortSignal.timeout(15000),
56
+ });
57
+ if (!queryRes.ok)
58
+ return null;
59
+ const data = (await queryRes.json());
60
+ // Find the VSIX download URL from the response
61
+ const extension = data.results?.[0]?.extensions?.[0];
62
+ const latestVersion = extension?.versions?.[0];
63
+ const vsixFile = latestVersion?.files?.find((f) => f.assetType === "Microsoft.VisualStudio.Services.VSIXPackage");
64
+ if (!vsixFile?.source)
65
+ return null;
66
+ // Download the VSIX
67
+ const vsixRes = await fetch(vsixFile.source, {
68
+ signal: AbortSignal.timeout(30000),
69
+ redirect: "follow",
70
+ });
71
+ if (!vsixRes.ok || !vsixRes.body)
72
+ return null;
73
+ const tmpPath = join(tmpdir(), `${NEON_EXTENSION_ID}-proxy.vsix`);
74
+ const fileStream = createWriteStream(tmpPath);
75
+ await pipeline(vsixRes.body, fileStream);
76
+ return tmpPath;
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ }
82
+ /**
83
+ * Downloads from the public Open VSX API.
84
+ */
85
+ async function downloadFromOpenVsx() {
86
+ const [publisher, name] = NEON_EXTENSION_ID.split(".");
87
+ const metaUrl = `${OPEN_VSX_API}/${publisher}/${name}/latest`;
88
+ try {
89
+ const metaRes = await fetch(metaUrl, {
90
+ signal: AbortSignal.timeout(10000),
91
+ });
92
+ if (!metaRes.ok)
93
+ return null;
94
+ const meta = (await metaRes.json());
95
+ const downloadUrl = meta.files?.download;
96
+ if (!downloadUrl)
97
+ return null;
98
+ const vsixRes = await fetch(downloadUrl, {
99
+ signal: AbortSignal.timeout(30000),
100
+ });
101
+ if (!vsixRes.ok || !vsixRes.body)
102
+ return null;
103
+ const tmpPath = join(tmpdir(), `${NEON_EXTENSION_ID}-${meta.version ?? "latest"}.vsix`);
104
+ const fileStream = createWriteStream(tmpPath);
105
+ await pipeline(vsixRes.body, fileStream);
106
+ return tmpPath;
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }
@@ -754,12 +754,12 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
754
754
  * here on purpose (see the project's LICENSE file for terms).
755
755
  */
756
756
  const NEON_NOTICE = `
757
- This is an embedded psql reimplementation that ships with neonctl, the
757
+ This is an embedded psql reimplementation that ships with neon, the
758
758
  command-line interface for Neon (https://neon.tech). Neon is part of
759
759
  Databricks (https://www.databricks.com).
760
760
 
761
761
  It is an independent reimplementation of psql and is not affiliated with
762
- or endorsed by the PostgreSQL Global Development Group. See the neonctl
762
+ or endorsed by the PostgreSQL Global Development Group. See the neon
763
763
  LICENSE file for distribution terms.
764
764
  `;
765
765
  /**
@@ -246,7 +246,7 @@ const isHelpKeyword = (line) => {
246
246
  const stripped = trimmed.replace(/;+\s*$/u, "").trimEnd();
247
247
  return stripped === "help";
248
248
  };
249
- const HELP_TEXT = "You are using psql-ts, the embedded TypeScript psql in neonctl.\n" +
249
+ const HELP_TEXT = "You are using psql-ts, the embedded TypeScript psql in neon.\n" +
250
250
  "Type: \\copyright for distribution terms\n" +
251
251
  " \\h for help with SQL commands\n" +
252
252
  " \\? for help with psql commands\n" +
@@ -223,7 +223,7 @@ export const CLIENT_VERSION = "1.0.0";
223
223
  // `embedded-ts` label in place of a real PG build number since this is a
224
224
  // reimplementation, not a compiled psql. Distinct from the `:VERSION` psql
225
225
  // variable (the client-identity string seeded from CLIENT_VERSION).
226
- const VERSION_STRING = `psql (PostgreSQL) embedded-ts (neonctl ${CLIENT_VERSION})`;
226
+ const VERSION_STRING = `psql (PostgreSQL) embedded-ts (neon ${CLIENT_VERSION})`;
227
227
  const pushAction = (acts, a) => {
228
228
  acts.push(a);
229
229
  };
@@ -85,16 +85,16 @@ export const syncConnectionVars = (vars, conn) => {
85
85
  * identifier is real and traceable to the shipped binary, while keeping
86
86
  * upstream's variable *shapes*:
87
87
  *
88
- * - `VERSION` → `psql-ts (neonctl) <clientVersion>` — a banner that
88
+ * - `VERSION` → `psql-ts (neon) <clientVersion>` — a banner that
89
89
  * names the implementation so users can tell they are on the embedded
90
- * TS port, mirroring the startup banner's `psql-ts (neonctl, …)` shape.
90
+ * TS port, mirroring the startup banner's `psql-ts (neon, …)` shape.
91
91
  * - `VERSION_NAME` → `<clientVersion>` (e.g. `2.22.0`).
92
92
  * - `VERSION_NUM` → the same version mapped into PG's NNMMPP integer form
93
93
  * (`2.22.0` → `22200`) via {@link clientVersionNum}, so a script doing a
94
94
  * numeric `:VERSION_NUM` comparison gets a monotonic integer.
95
95
  */
96
96
  export const setStartupVars = (vars, clientVersion) => {
97
- vars.set("VERSION", `psql-ts (neonctl) ${clientVersion}`);
97
+ vars.set("VERSION", `psql-ts (neon) ${clientVersion}`);
98
98
  vars.set("VERSION_NAME", clientVersion);
99
99
  vars.set("VERSION_NUM", String(clientVersionNum(clientVersion)));
100
100
  };
@@ -337,7 +337,7 @@ const writeStartupBanner = (connection, out) => {
337
337
  // Client identifier. Matches upstream's `psql (18.4, server X.Y)` shape
338
338
  // but signals that this is the embedded TS implementation so users can tell
339
339
  // when they're on the fallback path.
340
- out.write(`psql-ts (neonctl, server ${serverVersion})\n`);
340
+ out.write(`psql-ts (neon, server ${serverVersion})\n`);
341
341
  const tls = connection.getTlsInfo();
342
342
  if (tls) {
343
343
  const parts = [
@@ -0,0 +1,14 @@
1
+ import { basename } from "node:path";
2
+ /**
3
+ * The name this CLI was invoked as: `neon` (current) or `neonctl` (legacy alias).
4
+ *
5
+ * Use this for any user-facing string that suggests a command to run — help text,
6
+ * error hints, and especially `--agent` `next_command_template` values, which an agent
7
+ * executes verbatim. Hardcoding `neonctl` breaks those on installs of the `neon` package,
8
+ * which no longer ships a `neonctl` binary (removed in `neon@2.38.0`).
9
+ *
10
+ * Derived from `process.argv[1]`, which is fixed for the process lifetime, so the result
11
+ * is stable whether this is called at module load or per invocation. Mirrors the name used
12
+ * for yargs `.scriptName()` in `index.ts`.
13
+ */
14
+ export const getCliName = () => basename(process.argv[1] ?? "") === "neonctl" ? "neonctl" : "neon";
@@ -3,7 +3,7 @@ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { basename, join } from "node:path";
5
5
  import which from "which";
6
- const NOT_FOUND = "esbuild not found. neonctl ships esbuild for most platforms; if you see " +
6
+ const NOT_FOUND = "esbuild not found. neon ships esbuild for most platforms; if you see " +
7
7
  "this, install esbuild and ensure it is on your PATH (e.g. `npm i -g " +
8
8
  "esbuild`), or set NEON_ESBUILD_PATH to an esbuild binary.";
9
9
  // Prepended to the ESM bundle. Bundled dependencies are frequently CommonJS, but an ESM
@@ -0,0 +1,39 @@
1
+ import { writeSync } from "node:fs";
2
+ /**
3
+ * Write `text` to a file descriptor and do not return until all of it is out.
4
+ *
5
+ * `process.stdout.write` is asynchronous when stdout is a pipe — which it is whenever the
6
+ * caller is an agent or a script capturing output — so a `process.exit()` on the next line
7
+ * discards whatever has not been flushed. The caller sees an exit code and empty stdout.
8
+ *
9
+ * Use this for any final message that is followed by `process.exit`.
10
+ */
11
+ export function writeAllSync(fd, text) {
12
+ let buffer = Buffer.from(text, "utf8");
13
+ while (buffer.length > 0) {
14
+ try {
15
+ buffer = buffer.subarray(writeSync(fd, buffer));
16
+ }
17
+ catch (err) {
18
+ const code = err.code;
19
+ if (code === "EAGAIN") {
20
+ // Wait for the reader rather than spinning on it. A pipe nobody is draining
21
+ // would otherwise burn a core until it is; failure output is small enough
22
+ // that this should never be reached, which is the reason to bound it rather
23
+ // than assume so.
24
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5);
25
+ continue;
26
+ }
27
+ // The reader closed the pipe (`neon … | head`). There is nobody left to tell,
28
+ // and this is the last thing the process does, so stop rather than throw.
29
+ if (code === "EPIPE")
30
+ return;
31
+ // Anything else means the write did not happen and the caller is about to exit
32
+ // as though it had. Swallowing it turns a lost payload into an apparent success.
33
+ throw err;
34
+ }
35
+ }
36
+ }
37
+ /** File descriptors, named so call sites read as the stream they mean. */
38
+ export const STDOUT_FD = 1;
39
+ export const STDERR_FD = 2;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon",
3
- "version": "2.45.0",
3
+ "version": "2.46.0",
4
4
  "description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
5
5
  "keywords": [
6
6
  "neon",
@@ -39,6 +39,8 @@
39
39
  "node": ">=20.19.0"
40
40
  },
41
41
  "dependencies": {
42
+ "@clack/core": "0.4.2",
43
+ "@clack/prompts": "0.10.1",
42
44
  "@hono/node-server": "2.0.4",
43
45
  "@segment/analytics-node": "1.3.0",
44
46
  "chalk": "5.3.0",
@@ -46,20 +48,22 @@
46
48
  "cli-table": "0.3.11",
47
49
  "cliui": "8.0.1",
48
50
  "diff": "5.2.0",
51
+ "execa": "^9.5.2",
49
52
  "fflate": "^0.8.3",
50
53
  "open": "^10.2.0",
51
54
  "openid-client": "6.8.1",
52
55
  "pg-protocol": "^1.14.0",
56
+ "picocolors": "^1.1.1",
53
57
  "prompts": "2.4.2",
54
58
  "undici": "^7.28.0",
55
59
  "which": "3.0.1",
56
60
  "yaml": "^2.9.0",
57
61
  "yargs": "17.7.2",
62
+ "yoctocolors": "^2.1.2",
58
63
  "@neon/sdk": "1.5.0",
59
- "@neon/config": "0.14.0",
60
- "@neon/config-runtime": "0.12.4",
61
- "neon-init": "0.20.7",
62
- "@neon/env": "0.14.0"
64
+ "@neon/config": "0.14.1",
65
+ "@neon/config-runtime": "0.12.5",
66
+ "@neon/env": "0.14.1"
63
67
  },
64
68
  "optionalDependencies": {
65
69
  "esbuild": "0.28.1"
@@ -111,15 +115,17 @@
111
115
  },
112
116
  "scripts": {
113
117
  "generateParams": "tsx generateOptionsFromSpec.ts",
118
+ "codegen": "node ../../scripts/sync-shared.mjs . && node scripts/set-vsx-gallery.mjs",
114
119
  "clean": "rm -rf dist",
115
- "build": "node ../../scripts/sync-shared.mjs . && pnpm generateParams && pnpm clean && tsc -p tsconfig.build.json && cp src/*.html ./dist",
120
+ "build": "pnpm codegen && pnpm generateParams && pnpm clean && tsc -p tsconfig.build.json && cp src/*.html ./dist",
121
+ "build:internal": "node ../../scripts/sync-shared.mjs . && node scripts/set-vsx-gallery.mjs https://cursor-vsx-proxy.cloud.databricks.com/gallery && pnpm generateParams && pnpm clean && tsc -p tsconfig.build.json && cp src/*.html ./dist",
116
122
  "bundle": "node pkg.js",
117
- "typecheck": "node ../../scripts/sync-shared.mjs . && tsc --noEmit",
118
- "lint": "node ../../scripts/sync-shared.mjs . && pnpm typecheck && biome check src",
123
+ "typecheck": "pnpm codegen && tsc --noEmit",
124
+ "lint": "pnpm codegen && pnpm typecheck && biome check src",
119
125
  "lint:fix": "pnpm typecheck && biome check src --write",
120
- "test": "node ../../scripts/sync-shared.mjs . && pnpm --filter neonctl... build && vitest run",
121
- "test:ci": "node ../../scripts/sync-shared.mjs . && pnpm build && vitest run",
122
- "test:e2e": "node ../../scripts/sync-shared.mjs . && pnpm build && vitest run --config vitest.e2e.config.ts",
123
- "test:conformance": "node ../../scripts/sync-shared.mjs . && vitest run --config tests/psql-conformance/vitest.config.ts"
126
+ "test": "pnpm codegen && pnpm --filter neonctl... build && vitest run",
127
+ "test:ci": "pnpm codegen && pnpm build && vitest run",
128
+ "test:e2e": "pnpm codegen && pnpm build && vitest run --config vitest.e2e.config.ts",
129
+ "test:conformance": "pnpm codegen && vitest run --config tests/psql-conformance/vitest.config.ts"
124
130
  }
125
131
  }