neon 3.5.1 → 4.0.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.
package/README.md CHANGED
@@ -720,8 +720,7 @@ That message reports where parsing stopped and nothing more. `--data` carries wh
720
720
  | `--data <json>` | Route to one phase, with that phase's options |
721
721
  | `--skip-migrations` | Leave the migrations phase out of the flow |
722
722
  | `--preview` | Enable preview features (scaffolding a project from a template) |
723
-
724
- `neon init` refuses `--profile`; see [Which credential an invocation uses](#which-credential-an-invocation-uses).
723
+ | `--profile <name>` | Run as that stored account. See [Which credential an invocation uses](#which-credential-an-invocation-uses). |
725
724
 
726
725
  ## Snapshots (`snapshots`)
727
726
 
@@ -842,22 +841,18 @@ neon profile remove work
842
841
  ```console
843
842
  $ neon profile list
844
843
  Profiles
845
- ┌────────┬─────────┬───────────────────────┬─────────┬────────────────┬──────┬─────────┬────────────────────────┐
846
- │ Active │ Name │ Account │ Auth │ Scope │ File │ Storage │ Credentials │
847
- ├────────┼─────────┼───────────────────────┼─────────┼────────────────┼──────┼─────────┼────────────────────────┤
848
- │ * │ DEFAULT │ me@example.com │ oauth │ - │ ok │ file │ credentials.json │
849
- ├────────┼─────────┼───────────────────────┼─────────┼────────────────┼──────┼─────────┼────────────────────────┤
850
- │ │ work │ me@example.com │ api key │ account │ ok │ keyring │ keyring │
851
- ├────────┼─────────┼───────────────────────┼─────────┼────────────────┼──────┼─────────┼────────────────────────┤
852
- │ │ ci │ org-abc-123 │ api key │ project proj-1 │ ok │ file │ credentials.ci.json │
853
- └────────┴─────────┴───────────────────────┴─────────┴────────────────┴──────┴─────────┴────────────────────────┘
844
+ Active Name Account Auth Credentials Scope
845
+ * DEFAULT me@example.com oauth credentials.json account
846
+ work me@example.com api key keyring account
847
+ ci org-abc-123 api key credentials.ci.json project proj-1
854
848
  ```
855
849
 
856
- `Scope` is what a key can reach; an OAuth session has none of its own, so it shows `-`. `File`
857
- says whether the stored credential can be read — `ok`, `invalid` or `missing` for a file;
858
- `ok`, `invalid` or `unreadable` for a keyring item. A keyring get of null is `unreadable`, not missing:
859
- the addon cannot tell those apart. `Storage` is `file` or `keyring`, from the profile's
860
- pointer. The table shows the file name or `keyring`; `--output json` keeps the full path.
850
+ `Scope` is what the credential can reach. An OAuth session and an unscoped user API key both
851
+ show `account`. A project or org key names that project or org. `Credentials` is the path
852
+ relative to the config directory, or `keyring`. A path outside the config directory is shown
853
+ absolute. `--output json` keeps the absolute path, and also includes `file` (`ok`, `invalid`,
854
+ `missing`, or `unreadable`) and `storage` (`file` or `keyring`). A keyring get of null is
855
+ `unreadable`, not missing: the addon cannot tell those apart.
861
856
 
862
857
  Select one per invocation with `--profile`, or per shell with `NEON_PROFILE`. There is no `profile use` command and nothing is stored about which profile is "current", so what you type is always what runs.
863
858
 
@@ -999,7 +994,7 @@ When both are only environment variables the key wins, which keeps a CI pipeline
999
994
 
1000
995
  `neon auth` and the `profile` subcommands are outside all of this, because they read the same flags to mean something else: `neon auth --profile work` names where to write a credential, and `neon profile create work --api-key …` names one to store.
1001
996
 
1002
- `neon init` does not support `--profile` yet. It runs its own auth flow, which reads the default credentials directly and re-invokes the CLI as a subprocess without passing a profile down, so passing the flag fails instead of quietly running as the default account. `--api-key` and `NEON_API_KEY` reach `neon init` no better and are **not** refused. The flow reads the stored credential and nothing else, so a supplied key is ignored: with a credential on disk `neon init` runs silently as *that* account, and with none it sends you to a browser sign-in. The silent case is the one to watch — it is the same failure `--profile` is refused for, without the refusal. Until that is fixed, sign in as the account you want first, or use another command.
997
+ `neon init` follows the same profile selection: `--profile` and `NEON_PROFILE` pick the stored account. Every `npx neon` it runs, and every command it tells an agent to run (`npx neon …`, `neon init --agent …`), includes `--profile <name>` when a profile was named by `--profile` or `NEON_PROFILE`, and `--config-dir <path>` when you passed `--config-dir`; nothing is added when those were not set. It still ignores `--api-key` and `NEON_API_KEY` for its own credential read and runs as that stored profile (`DEFAULT` when a key was named with no profile). Those keys are also not written onto the emitted commands — putting a key in agent JSON would print it. An ambient `NEON_API_KEY` is inherited by the subprocesses.
1003
998
 
1004
999
  ## API keys (`api-keys`)
1005
1000
 
@@ -1014,16 +1009,14 @@ neon api-keys create --name agent --project-id frosty-… # can access only th
1014
1009
  neon api-keys revoke <id> [--org-id org-…]
1015
1010
  ```
1016
1011
 
1017
- The key is returned once, on create, and cannot be retrieved again. It prints on its own line below the table, so it can be selected in one gesture regardless of terminal width — and `… | tail -1` on stdout yields exactly the key, since both notices go to stderr.
1012
+ The key is returned once, on create, and cannot be retrieved again. It prints on its own line below the metadata, so it can be selected in one gesture regardless of terminal width — and `… | tail -1` on stdout yields exactly the key, since both notices go to stderr.
1018
1013
 
1019
1014
  ```console
1020
1015
  $ neon api-keys create --name agent --project-id proj-in-org
1021
1016
  API key
1022
- ┌─────┬───────┬─────────────┐
1023
- │ Id │ Name │ Project │
1024
- ├─────┼───────┼─────────────┤
1025
- │ 303 │ agent │ proj-in-org │
1026
- └─────┴───────┴─────────────┘
1017
+ Id 303
1018
+ Name agent
1019
+ Project proj-in-org
1027
1020
 
1028
1021
  napi_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1029
1022
  WARNING: Store this key now: it is not shown again.
@@ -1062,13 +1055,9 @@ NEON_API_KEY=napi_… neon deploy # then the agent, reaching only that pro
1062
1055
  ```console
1063
1056
  $ neon api-keys list --org-id org-7
1064
1057
  API keys in org-7
1065
- ┌─────┬──────────┬────────────────┬──────────────────────┬──────────────────────┬─────────────────────┐
1066
- │ Id │ Name │ Project │ Created At │ Last Used At │ Last Used From Addr │
1067
- ├─────┼──────────┼────────────────┼──────────────────────┼──────────────────────┼─────────────────────┤
1068
- │ 301 │ scoped │ proj-in-org │ 2026-01-02T00:00:00Z │ │ │
1069
- ├─────┼──────────┼────────────────┼──────────────────────┼──────────────────────┼─────────────────────┤
1070
- │ 302 │ org-wide │ (all projects) │ 2026-01-03T00:00:00Z │ 2026-02-03T00:00:00Z │ 203.0.113.9 │
1071
- └─────┴──────────┴────────────────┴──────────────────────┴──────────────────────┴─────────────────────┘
1058
+ Id Name Project Created At Last Used At Last Used From Addr
1059
+ 301 scoped proj-in-org 2026-01-02T00:00:00Z
1060
+ 302 org-wide (all projects) 2026-01-03T00:00:00Z 2026-02-03T00:00:00Z 203.0.113.9
1072
1061
  ```
1073
1062
 
1074
1063
  `last_used_at` and `last_used_from_addr` are how you spot a key worth revoking.
@@ -40,7 +40,9 @@ import "./profiles-CVXNvU2r.js";
40
40
  let inputs = {
41
41
  apiKeyFlag: "",
42
42
  apiKeyEnv: "",
43
- profileEnv: ""
43
+ profileEnv: "",
44
+ profileFlag: "",
45
+ configDir: ""
44
46
  };
45
47
  const recordCredentialInputs = (recorded) => {
46
48
  inputs = recorded;
package/dist/api.js CHANGED
@@ -796,7 +796,7 @@ const getApiClient = ({ apiKey, apiHost, requestTimeoutMs = REQUEST_TIMEOUT_MS }
796
796
  },
797
797
  body: data
798
798
  })),
799
- sendNeonAuthTestEmail: (projectId, branchId, data) => call(() => raw.sendNeonAuthTestEmail({
799
+ sendNeonAuthEmailProviderTest: (projectId, branchId, data) => call(() => raw.sendNeonAuthEmailProviderTest({
800
800
  client,
801
801
  path: {
802
802
  project_id: projectId,
@@ -171,13 +171,8 @@ const create = async (props) => {
171
171
  log.info("Limited to %s: it cannot create projects, mint API keys, or read any other project. It can still change and delete everything inside that project.", scopeTo);
172
172
  };
173
173
  /**
174
- * Print the issued key.
175
- *
176
- * In a terminal the secret goes on its own line rather than into a table cell: `cli-table`
177
- * neither wraps nor truncates, so a 50-odd character key makes the row wider than most
178
- * terminals and the wrapped remainder ends up beside box-drawing characters. Since this is
179
- * the only time the key is ever shown, it has to be selectable in one gesture. Structured
180
- * output keeps the key in the object, where a script expects it.
174
+ * The key is shown only once, so human output keeps it on a selectable line.
175
+ * Structured output keeps it in the object for scripts.
181
176
  */
182
177
  const report = (props, data, fields, tableFields) => {
183
178
  const out = writer(props);
@@ -9,7 +9,7 @@ import { setAuthContext } from "../auth_context.js";
9
9
  import { isConfigInit, isCurrentBranchProbe, isProfileCommand } from "../context.js";
10
10
  import { extendTokenSet } from "../utils/auth.js";
11
11
  import { auth, refreshToken } from "../auth.js";
12
- import { i as selectCredential, n as displacedProfileWarning, t as credentialInputs } from "../_chunks/auth_selection-CV_3n1du.js";
12
+ import { i as selectCredential, n as displacedProfileWarning, t as credentialInputs } from "../_chunks/auth_selection-Bx2NAgkk.js";
13
13
  import { readOutgoingCredential, retirePreviousCredential } from "../retire_credential.js";
14
14
  import { existsSync } from "node:fs";
15
15
  import { createHash } from "node:crypto";
@@ -2,7 +2,7 @@ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { isCi } from "../env.js";
3
3
  import { log } from "../log.js";
4
4
  import { getCliName } from "../utils/cli_name.js";
5
- import { t as credentialInputs } from "../_chunks/auth_selection-CV_3n1du.js";
5
+ import { t as credentialInputs } from "../_chunks/auth_selection-Bx2NAgkk.js";
6
6
  import { BootstrapInputError, FALLBACK_TEMPLATES, ensureTargetUsable, fetchTemplates, findTemplate, scaffoldTemplate, templateIds } from "../init/bootstrap.js";
7
7
  import { DO_NOT_SUBSTITUTE_HINT, formatInstallCommand, inferPackageManager, installArgs, installedPackageManagers, resolvePackageManager, runCommand } from "../utils/package_manager.js";
8
8
  import { existsSync } from "node:fs";
@@ -1,6 +1,5 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { closeAnalytics, sendError } from "../analytics.js";
3
- import { t as credentialInputs } from "../_chunks/auth_selection-CV_3n1du.js";
4
3
  import { detectAgent } from "../init/detect_agent.js";
5
4
  import { enrichResponse } from "../init/enrich_output.js";
6
5
  import { interactiveInit } from "../init/interactive.js";
@@ -62,11 +61,6 @@ const handler = async (argv) => {
62
61
  const agent = (argv.agent || !process.stdin.isTTY ? detectAgent() : null) || void 0;
63
62
  const isAgentMode = argv.agent || agent !== void 0;
64
63
  try {
65
- const selectedProfile = argv.profile?.trim() || credentialInputs().profileEnv.trim();
66
- if (selectedProfile) {
67
- const how = argv.profile?.trim() ? "--profile was passed, so" : "NEON_PROFILE is set, so";
68
- throw new Error(`${how} \`neon init\` would run as the default account instead of "${selectedProfile}", and it does not support profile selection yet. Run it without one, or set the project up with \`neon --profile ${selectedProfile} link\`.`);
69
- }
70
64
  if (argv.data && isAgentMode) {
71
65
  let data;
72
66
  try {
@@ -1,6 +1,7 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { codeFromBody, isNeonApiError, retryOnLock } from "../api.js";
3
3
  import { writer } from "../writer.js";
4
+ import { noPassthrough, single } from "../utils/flags.js";
4
5
  import { NeonAuthEmailVerificationMethod, NeonAuthOauthProviderId, NeonAuthOauthProviderType, NeonAuthSupportedAuthProvider } from "../utils/api_enums.js";
5
6
  import { branchIdFromProps, fillSingleProject } from "../utils/enrichers.js";
6
7
  import chalk from "chalk";
@@ -241,43 +242,17 @@ const builder = (argv) => {
241
242
  }
242
243
  }), async (args) => {
243
244
  await emailProviderUpdate(args);
244
- }).command("test", "Send a test email", (yargs) => yargs.options({
245
- "recipient-email": {
246
- describe: "Email address to send test email to",
247
- type: "string",
248
- demandOption: true
249
- },
250
- host: {
251
- describe: "SMTP host",
252
- type: "string",
253
- demandOption: true
254
- },
255
- port: {
256
- describe: "SMTP port",
257
- type: "number",
258
- demandOption: true
259
- },
260
- username: {
261
- describe: "SMTP username",
262
- type: "string",
263
- demandOption: true
264
- },
265
- password: {
266
- describe: "SMTP password",
267
- type: "string",
268
- demandOption: true
269
- },
270
- "sender-email": {
271
- describe: "Sender email address",
272
- type: "string",
273
- demandOption: true
274
- },
275
- "sender-name": {
276
- describe: "Sender display name",
277
- type: "string",
278
- demandOption: true
279
- }
280
- }), async (args) => {
245
+ }).command("test", "Send a test message through the saved custom SMTP provider", (yargs) => yargs.options({ "recipient-email": {
246
+ describe: "Email address to deliver the test message to",
247
+ type: "string",
248
+ demandOption: true,
249
+ coerce: single("recipient-email", { required: true })
250
+ } }).strict().check(noPassthrough("neon-auth config email-provider test")).epilogue([
251
+ "Save the provider first with",
252
+ "`neon neon-auth config email-provider update --type standard`.",
253
+ "A shared provider, a missing configuration, or a",
254
+ "non-Better-Auth integration is rejected by the API."
255
+ ].join("\n")), async (args) => {
281
256
  await emailProviderTest(args);
282
257
  });
283
258
  }).command("organization", "Manage organization plugin settings", (yargs) => {
@@ -663,18 +638,21 @@ const emailProviderUpdate = async (props) => {
663
638
  };
664
639
  const emailProviderTest = async (props) => {
665
640
  const branchId = await resolveBranch(props);
666
- const { data } = await props.apiClient.sendNeonAuthTestEmail(props.projectId, branchId, {
667
- recipient_email: props.recipientEmail,
668
- host: props.host,
669
- port: props.port,
670
- username: props.username,
671
- password: props.password,
672
- sender_email: props.senderEmail,
673
- sender_name: props.senderName
674
- });
675
- if (props.output === "json" || props.output === "yaml") writer(props).end(data, { fields: TEST_EMAIL_FIELDS });
676
- else if (data.success) printMessage("Test email sent successfully");
677
- else process.stdout.write(`\n${chalk.red("Test email failed")}\n ${data.error_message ?? "Unknown error"}\n\n`);
641
+ const { data } = await props.apiClient.sendNeonAuthEmailProviderTest(props.projectId, branchId, { recipient_email: props.recipientEmail });
642
+ if (props.output === "json" || props.output === "yaml") {
643
+ writer(props).end(data, { fields: TEST_EMAIL_FIELDS });
644
+ if (!data.success) throw new Error(emailProviderTestFailureMessage(props.recipientEmail, data.error_message));
645
+ return;
646
+ }
647
+ if (data.success) {
648
+ printMessage(`Test email dispatched to ${props.recipientEmail} using the custom SMTP provider saved on this branch.`);
649
+ return;
650
+ }
651
+ throw new Error(emailProviderTestFailureMessage(props.recipientEmail, data.error_message));
652
+ };
653
+ const emailProviderTestFailureMessage = (recipientEmail, errorMessage) => {
654
+ const header = `Test email could NOT be sent to ${recipientEmail} using the custom SMTP provider saved on this branch.`;
655
+ return errorMessage ? `${header}\nUpstream error: ${errorMessage}` : header;
678
656
  };
679
657
  const printOrganizationEntries = (data) => [
680
658
  ["Enabled: ", String(data.enabled)],
@@ -7,7 +7,7 @@ import { getApiClient, isNeonApiError } from "../api.js";
7
7
  import { o as isOwnedCredentialPath, r as credentialsPath } from "../_chunks/paths-g3DRqJiD.js";
8
8
  import { setAuthContext } from "../auth_context.js";
9
9
  import { auth, revokeToken } from "../auth.js";
10
- import { t as credentialInputs } from "../_chunks/auth_selection-CV_3n1du.js";
10
+ import { t as credentialInputs } from "../_chunks/auth_selection-Bx2NAgkk.js";
11
11
  import { readOutgoingCredential, retirePreviousCredential, revokeTokenSet, withdrawKey } from "../retire_credential.js";
12
12
  import { authFlow, usableCredential } from "./auth.js";
13
13
  import { writer } from "../writer.js";
@@ -15,7 +15,7 @@ import { noPassthrough, single } from "../utils/flags.js";
15
15
  import { orgIdForProject } from "./api_keys.js";
16
16
  import { identityFromAuthDetails, isApiKeyMethod, mintedKeyName, notAnApiKeyMessage } from "../profile_keys.js";
17
17
  import { existsSync, readFileSync, rmSync } from "node:fs";
18
- import { basename } from "node:path";
18
+ import { isAbsolute, relative } from "node:path";
19
19
  import prompts from "prompts";
20
20
  //#region src/commands/profile.ts
21
21
  var profile_exports = /* @__PURE__ */ __exportAll({
@@ -173,10 +173,10 @@ const list = async (props) => {
173
173
  name: p.name,
174
174
  account: p.label ?? p.userId ?? storedUserId ?? "-",
175
175
  auth,
176
- scope: stored !== null && auth === "api key" ? describeScope(scopeOf(stored)) : "-",
176
+ scope: auth === "oauth" ? describeScope({}) : stored !== null && auth === "api key" ? describeScope(scopeOf(stored)) : "-",
177
177
  file,
178
178
  storage,
179
- ...props.output === "table" ? { credentials: p.storage === "keyring" ? KEYRING_CREDENTIALS : basename(p.credentialsPath) } : { credentials: credentialsDisplay(p) }
179
+ credentials: props.output === "table" ? credentialsListValue(p, props.configDir) : credentialsDisplay(p)
180
180
  };
181
181
  });
182
182
  writer(props).end(rows, {
@@ -186,13 +186,17 @@ const list = async (props) => {
186
186
  "name",
187
187
  "account",
188
188
  "auth",
189
- "scope",
190
- "file",
191
- "storage",
192
- "credentials"
189
+ "credentials",
190
+ "scope"
193
191
  ]
194
192
  });
195
193
  };
194
+ const credentialsListValue = (profile, configDir) => {
195
+ if (profile.storage === "keyring") return KEYRING_CREDENTIALS;
196
+ const rel = relative(configDir, profile.credentialsPath);
197
+ if (rel.startsWith("..") || isAbsolute(rel)) return profile.credentialsPath;
198
+ return rel;
199
+ };
196
200
  const locationForCreate = (configDir, name, keyring) => {
197
201
  assertProfilesUsable(configDir, name);
198
202
  const declared = readProfiles(configDir)?.profiles[name];
@@ -0,0 +1,239 @@
1
+ import { isObject } from "./utils/string.js";
2
+ import chalk from "chalk";
3
+ //#region src/human_table.ts
4
+ const GUTTER = 2;
5
+ const MIN_COL = 3;
6
+ const ELLIPSIS = "...";
7
+ const ANSI_RE = /\u001b\[[0-9;]*m/g;
8
+ function resolveOutputWidth(out, override, deps = {}) {
9
+ if (override === null) return;
10
+ if (typeof override === "number" && Number.isFinite(override) && override > 0) return Math.floor(override);
11
+ if ("columns" in out) {
12
+ const columns = Reflect.get(out, "columns");
13
+ if (typeof columns === "number" && Number.isFinite(columns) && columns > 0) return Math.floor(columns);
14
+ }
15
+ const stdout = deps.stdout;
16
+ if (stdout !== void 0 && out === stdout) return parseColumns(deps.envColumns);
17
+ }
18
+ function parseColumns(value) {
19
+ if (value === void 0 || value === "") return;
20
+ if (!/^\d+$/.test(value)) return;
21
+ const n = Number(value);
22
+ if (!Number.isFinite(n) || n <= 0) return;
23
+ return n;
24
+ }
25
+ function displayWidth(s) {
26
+ const plain = s.replace(ANSI_RE, "");
27
+ let width = 0;
28
+ for (const char of plain) {
29
+ const code = char.codePointAt(0);
30
+ if (code === void 0) continue;
31
+ width += charWidth(code);
32
+ }
33
+ return width;
34
+ }
35
+ function formatHumanChunk(chunk) {
36
+ const arrayData = Array.isArray(chunk.data) ? chunk.data : [chunk.data];
37
+ const isList = Array.isArray(chunk.data);
38
+ if (!arrayData.length && chunk.emptyMessage) return `\n${chunk.emptyMessage}\n`;
39
+ let out = "";
40
+ if (chunk.title) {
41
+ if (chunk.leadingBlank) out += "\n";
42
+ out += formatTitle(chunk.title, chunk.colorTitle) + "\n";
43
+ }
44
+ if (!arrayData.length) return out;
45
+ const fields = chunk.fields.filter((field) => arrayData.some((item) => {
46
+ const value = fieldValue(item, field);
47
+ return value !== void 0 && value !== "";
48
+ }));
49
+ if (!fields.length) return out;
50
+ const headers = fields.map(titleCaseField);
51
+ const rows = arrayData.map((item) => fields.map((field) => cellText(item, field, chunk.renderColumns)));
52
+ const body = isList ? formatList(headers, rows, chunk.width) : formatStacked(headers, rows, chunk.width);
53
+ if (body) out += body + "\n";
54
+ return out;
55
+ }
56
+ function formatTitle(title, colorTitle) {
57
+ return colorTitle ? chalk.bold(title) : title;
58
+ }
59
+ function formatList(headers, rows, width) {
60
+ if (headers.length === 1) return formatOneColumn(headers[0] ?? "", rows.map((row) => row[0] ?? ""));
61
+ if (width === void 0) return formatColumns(headers, rows, naturalWidths(headers, rows));
62
+ for (let n = headers.length; n >= 2; n--) {
63
+ const subsetHeaders = headers.slice(0, n);
64
+ const subsetRows = rows.map((row) => row.slice(0, n));
65
+ const natural = naturalWidths(subsetHeaders, subsetRows);
66
+ if (rowWidth(natural) <= width) return formatColumns(subsetHeaders, subsetRows, natural);
67
+ const fitted = tryFit(subsetHeaders, subsetRows, width);
68
+ if (fitted === void 0) continue;
69
+ if (!natural.slice(0, n - 1).every((col, i) => fitted[i] === col)) continue;
70
+ return formatColumns(subsetHeaders.map((cell, i) => truncateTo(cell, fitted[i] ?? 0)), subsetRows.map((row) => row.map((cell, i) => truncateTo(cell, fitted[i] ?? 0))), fitted);
71
+ }
72
+ return formatStacked(headers, rows, width);
73
+ }
74
+ function rowWidth(widths) {
75
+ if (widths.length === 0) return 0;
76
+ return widths.reduce((sum, col) => sum + col, 0) + (widths.length - 1) * GUTTER;
77
+ }
78
+ function formatOneColumn(header, values) {
79
+ const lines = [chalk.green(header)];
80
+ for (const value of values) lines.push(value);
81
+ return lines.join("\n");
82
+ }
83
+ function formatColumns(headers, rows, widths) {
84
+ const joinRow = (cells) => cells.map((cell, i) => {
85
+ const width = widths[i] ?? 0;
86
+ return i === cells.length - 1 ? truncateTo(cell, width) : padEndWidth(cell, width);
87
+ }).join(" ".repeat(GUTTER)).trimEnd();
88
+ const lines = [chalk.green(joinRow(headers))];
89
+ for (const row of rows) lines.push(joinRow(row));
90
+ return lines.join("\n");
91
+ }
92
+ function formatStacked(headers, rows, width) {
93
+ const naturalLabel = Math.max(0, ...headers.map((header) => displayWidth(header)));
94
+ let labelWidth = naturalLabel;
95
+ if (width !== void 0) {
96
+ const maxLabel = width - GUTTER - MIN_COL;
97
+ labelWidth = maxLabel < 1 ? Math.min(naturalLabel, width) : Math.min(naturalLabel, maxLabel);
98
+ }
99
+ return rows.map((row) => headers.map((header, i) => stackedLine(header, row[i] ?? "", labelWidth)).join("\n")).join("\n\n");
100
+ }
101
+ function stackedLine(header, value, labelWidth) {
102
+ return `${padEndWidth(header, labelWidth)}${" ".repeat(GUTTER)}${value}`;
103
+ }
104
+ function tryFit(headers, rows, width) {
105
+ const n = headers.length;
106
+ if (n === 0) return [];
107
+ const gutters = (n - 1) * GUTTER;
108
+ if (width <= gutters) return;
109
+ const budget = width - gutters;
110
+ if (budget < n * MIN_COL) return;
111
+ const widths = naturalWidths(headers, rows);
112
+ let overflow = widths.reduce((sum, col) => sum + col, 0) - budget;
113
+ if (overflow <= 0) return widths;
114
+ for (let i = n - 1; i >= 0 && overflow > 0; i--) {
115
+ const current = widths[i] ?? 0;
116
+ const take = Math.min(Math.max(current - MIN_COL, 0), overflow);
117
+ widths[i] = current - take;
118
+ overflow -= take;
119
+ }
120
+ if (overflow > 0) return;
121
+ return widths;
122
+ }
123
+ function naturalWidths(headers, rows) {
124
+ return headers.map((header, i) => Math.max(displayWidth(header), ...rows.map((row) => displayWidth(row[i] ?? ""))));
125
+ }
126
+ function padEndWidth(s, width) {
127
+ const clipped = truncateTo(s, width);
128
+ const pad = width - displayWidth(clipped);
129
+ return pad > 0 ? clipped + " ".repeat(pad) : clipped;
130
+ }
131
+ function truncateTo(s, max) {
132
+ if (max <= 0) return "";
133
+ if (displayWidth(s) <= max) return s;
134
+ if (max <= 3) return ELLIPSIS.slice(0, max);
135
+ const budget = max - 3;
136
+ let out = "";
137
+ let used = 0;
138
+ for (const char of s.replace(ANSI_RE, "")) {
139
+ const code = char.codePointAt(0);
140
+ if (code === void 0) continue;
141
+ const w = charWidth(code);
142
+ if (used + w > budget) break;
143
+ out += char;
144
+ used += w;
145
+ }
146
+ return out + ELLIPSIS;
147
+ }
148
+ function cellText(item, field, renderColumns) {
149
+ if (renderColumns) {
150
+ const render = Reflect.get(renderColumns, field);
151
+ if (typeof render === "function") return flattenCell(render(item));
152
+ }
153
+ return flattenCell(fieldValue(item, field));
154
+ }
155
+ function fieldValue(item, field) {
156
+ if (item === null || typeof item !== "object") return;
157
+ return Reflect.get(item, field);
158
+ }
159
+ function flattenCell(value) {
160
+ if (value === void 0 || value === null) return "";
161
+ if (Array.isArray(value)) return value.map((entry) => flattenCell(entry)).join(", ");
162
+ if (isObject(value)) return JSON.stringify(value);
163
+ return String(value).replace(/\s+/g, " ").trim();
164
+ }
165
+ function titleCaseField(field) {
166
+ return field.split("_").map((word) => word ? word[0]?.toUpperCase() + word.slice(1) : word).join(" ");
167
+ }
168
+ const WIDE_RANGES = [
169
+ [4352, 4447],
170
+ [8986, 8987],
171
+ [9001, 9002],
172
+ [9193, 9196],
173
+ [9200, 9200],
174
+ [9203, 9203],
175
+ [9725, 9726],
176
+ [9748, 9749],
177
+ [9800, 9811],
178
+ [9855, 9855],
179
+ [9875, 9875],
180
+ [9889, 9889],
181
+ [9898, 9899],
182
+ [9917, 9918],
183
+ [9924, 9925],
184
+ [9934, 9934],
185
+ [9940, 9940],
186
+ [9962, 9962],
187
+ [9970, 9971],
188
+ [9973, 9973],
189
+ [9978, 9978],
190
+ [9981, 9981],
191
+ [9989, 9989],
192
+ [9994, 9995],
193
+ [10024, 10024],
194
+ [10060, 10060],
195
+ [10062, 10062],
196
+ [10067, 10069],
197
+ [10071, 10071],
198
+ [10133, 10135],
199
+ [10160, 10160],
200
+ [10175, 10175],
201
+ [11035, 11036],
202
+ [11088, 11088],
203
+ [11093, 11093],
204
+ [11904, 12350],
205
+ [12352, 42191],
206
+ [43360, 43391],
207
+ [44032, 55203],
208
+ [55216, 55295],
209
+ [63744, 64255],
210
+ [65040, 65049],
211
+ [65072, 65135],
212
+ [65280, 65376],
213
+ [65504, 65510],
214
+ [126976, 129791],
215
+ [131072, 196605],
216
+ [196608, 262141]
217
+ ];
218
+ function charWidth(code) {
219
+ if (code >= 768 && code <= 879) return 0;
220
+ let lo = 0;
221
+ let hi = WIDE_RANGES.length - 1;
222
+ while (lo <= hi) {
223
+ const mid = lo + hi >> 1;
224
+ const range = WIDE_RANGES[mid];
225
+ if (range === void 0) break;
226
+ if (code < range[0]) {
227
+ hi = mid - 1;
228
+ continue;
229
+ }
230
+ if (code > range[1]) {
231
+ lo = mid + 1;
232
+ continue;
233
+ }
234
+ return 2;
235
+ }
236
+ return 1;
237
+ }
238
+ //#endregion
239
+ export { displayWidth, formatHumanChunk, parseColumns, resolveOutputWidth };
package/dist/init/auth.js CHANGED
@@ -1,6 +1,6 @@
1
- import { l as locationForName, t as DEFAULT_PROFILE } from "../_chunks/profiles-CVXNvU2r.js";
1
+ import { l as locationForName } from "../_chunks/profiles-CVXNvU2r.js";
2
2
  import { l as interpretCredentials, t as storeFor } from "../_chunks/credential_io-D2vw1UOw.js";
3
- import { n as configDir } from "../_chunks/paths-g3DRqJiD.js";
3
+ import { npxNeonArgs, selectedConfigDir, selectedProfileName } from "./profile_cli.js";
4
4
  import { log } from "@clack/prompts";
5
5
  import { execa } from "execa";
6
6
  //#region src/init/auth.ts
@@ -12,11 +12,7 @@ async function ensureNeonctlAuth(options) {
12
12
  const quiet = options?.json === true;
13
13
  if (await getNeonctlAccessToken()) return true;
14
14
  try {
15
- await execa("npx", [
16
- "-y",
17
- "neon",
18
- "me"
19
- ], {
15
+ await execa("npx", npxNeonArgs(["me"]), {
20
16
  stdio: "inherit",
21
17
  env: {
22
18
  ...process.env,
@@ -44,8 +40,9 @@ async function isAuthenticated() {
44
40
  * Unreadable credentials throw so browser sign-in cannot overwrite them.
45
41
  */
46
42
  async function getNeonctlAccessToken() {
47
- const at = locationForName(configDir(), DEFAULT_PROFILE);
48
- const loaded = storeFor(configDir()).read(at);
43
+ const dir = selectedConfigDir();
44
+ const at = locationForName(dir, selectedProfileName());
45
+ const loaded = storeFor(dir).read(at);
49
46
  if (loaded === null) return null;
50
47
  const credential = interpretCredentials(loaded.credentials, at, loaded.backend);
51
48
  if (credential.kind === "api_key") return credential.apiKey;
@@ -1,3 +1,4 @@
1
+ import { neonInitAgentCmd } from "./profile_cli.js";
1
2
  //#region src/init/enrich_output.ts
2
3
  /**
3
4
  * Converts a phase's `args` (e.g. ["neon-auth", "--json", "--setup"]) into the
@@ -14,11 +15,15 @@ function argsToCommand(args) {
14
15
  const arg = args[i];
15
16
  if (arg.startsWith("--")) {
16
17
  const key = arg.slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
18
+ const next = args[i + 1];
17
19
  if (key === "json") {
18
20
  i += 1;
19
21
  continue;
20
22
  }
21
- const next = args[i + 1];
23
+ if (key === "profile" || key === "configDir") {
24
+ i += next !== void 0 && !next.startsWith("-") ? 2 : 1;
25
+ continue;
26
+ }
22
27
  if (next !== void 0 && !next.startsWith("-")) {
23
28
  data[key] = next;
24
29
  i += 2;
@@ -28,7 +33,7 @@ function argsToCommand(args) {
28
33
  }
29
34
  } else i += 1;
30
35
  }
31
- return `neon init --agent --data '${JSON.stringify(data)}'`;
36
+ return neonInitAgentCmd(data);
32
37
  }
33
38
  /**
34
39
  * Walks a phase response object and:
@@ -1,5 +1,6 @@
1
1
  import { FALLBACK_TEMPLATES, fetchTemplates, scaffoldTemplate } from "./bootstrap.js";
2
2
  import { detectAgent, detectIde } from "./detect_agent.js";
3
+ import { neonInitAgentCmd } from "./profile_cli.js";
3
4
  import { agentSupportsProjectMcp, detectInstalledAgents, getAgentDisplayName, getSkillsAgentName, mcpPickerOptions, tryResolveAddMcpAgentId } from "./agents.js";
4
5
  import { ensureNeonctlAuth, isAuthenticated } from "./auth.js";
5
6
  import { installExtension, isExtensionInstalled } from "./extension.js";
@@ -266,7 +267,7 @@ async function interactiveInitInner(options) {
266
267
  initialValue: "no"
267
268
  });
268
269
  if (isCancel(authResult) || authResult === "no") {
269
- outro(dim(`Your project is configured with Neon. You can set up Neon Auth later by having your agent run: neon init --agent --data '{"step":"neon-auth"}'`));
270
+ outro(dim(`Your project is configured with Neon. You can set up Neon Auth later by having your agent run: ${neonInitAgentCmd({ step: "neon-auth" })}`));
270
271
  return;
271
272
  }
272
273
  let projectId = null;
@@ -480,10 +481,10 @@ async function interactiveInitInner(options) {
480
481
  if (inspection.migrationDir && inspection.migrationDir !== "none") gettingStartedData.migrationDir = inspection.migrationDir;
481
482
  if (selectedFeatures.length > 0) gettingStartedData.features = selectedFeatures;
482
483
  if (options.preview) gettingStartedData.preview = true;
483
- const cmd = `neon init --agent --data '${JSON.stringify({
484
+ const cmd = neonInitAgentCmd({
484
485
  step: "getting-started",
485
486
  ...gettingStartedData
486
- })}'`;
487
+ });
487
488
  const cols = (process.stdout.columns || 80) - 3;
488
489
  const promptText = `To finish setting up Neon using Neon's agent-guided onboarding experience, have your agent run this shell command: ${cmd}`;
489
490
  log.step("Next steps");
@@ -1,21 +1,14 @@
1
1
  import { globalInstallCommand, resolveInvokingPackageManager } from "../utils/package_manager.js";
2
+ import { explicitConfigDirCli, explicitProfileCli } from "./profile_cli.js";
2
3
  import { lstatSync } from "node:fs";
3
4
  import { execa } from "execa";
4
5
  //#region src/init/neonctl.ts
5
6
  /**
6
- * Returns the Neon CLI command prefix emitted to users and agents:
7
- * `CI= npx -y neon`.
8
- *
9
- * The CLI reads NEON_API_HOST and NEON_OAUTH_HOST from the environment
10
- * directly, so no extra flags are needed. The published package is `neon`
11
- * (its binary is `neon`; the older `neonctl` alias is no longer shipped). `npx`
12
- * runs it with zero global-install assumptions, and the leading `CI=` keeps the
13
- * CLI non-interactive.
14
- *
15
- * Usage: `${neonctlCmd()} orgs list --output json`
7
+ * `npx -y neon` avoids global-install assumptions, and `CI=` keeps emitted calls
8
+ * non-interactive. API and OAuth hosts stay ambient because the CLI reads them directly.
16
9
  */
17
10
  function neonctlCmd() {
18
- return "CI= npx -y neon";
11
+ return `CI= npx -y neon${explicitProfileCli()}${explicitConfigDirCli()}`;
19
12
  }
20
13
  /**
21
14
  * The global binaries that indicate the Neon CLI is installed. The current
@@ -1,3 +1,4 @@
1
+ import { neonInitAgentCmd } from "../profile_cli.js";
1
2
  import { supportsSkills, tryResolveAddMcpAgentId } from "../agents.js";
2
3
  import { isAuthenticated } from "../auth.js";
3
4
  import { inspectProject } from "../inspect.js";
@@ -35,23 +36,26 @@ async function handleStatusPhase(options) {
35
36
  if (!authed) recommendations.push({
36
37
  priority: "high",
37
38
  message: "Not authenticated with Neon",
38
- command: `neon init --agent --data '{"step":"auth"}'`
39
+ command: neonInitAgentCmd({ step: "auth" })
39
40
  });
40
41
  if (!hasDatabaseUrl) recommendations.push({
41
42
  priority: "high",
42
43
  message: "No DATABASE_URL found in .env",
43
- command: `neon init --agent --data '{"step":"db"}'`
44
+ command: neonInitAgentCmd({ step: "db" })
44
45
  });
45
46
  const statusAgent = options.agent ? tryResolveAddMcpAgentId(options.agent) : void 0;
46
47
  if (!skillsInstalled && (!statusAgent || supportsSkills(statusAgent))) recommendations.push({
47
48
  priority: "medium",
48
49
  message: "Neon agent skills not detected in this project",
49
- command: `neon init --agent --data '{"step":"skills","install":true}'`
50
+ command: neonInitAgentCmd({
51
+ step: "skills",
52
+ install: true
53
+ })
50
54
  });
51
55
  if (migrationTool && !hasMigrations) recommendations.push({
52
56
  priority: "medium",
53
57
  message: `${migrationTool} detected but no migrations found`,
54
- command: `neon init --agent --data '{"step":"migrations"}'`
58
+ command: neonInitAgentCmd({ step: "migrations" })
55
59
  });
56
60
  return {
57
61
  auth: { authenticated: authed },
@@ -0,0 +1,56 @@
1
+ import { t as DEFAULT_PROFILE } from "../_chunks/profiles-CVXNvU2r.js";
2
+ import { n as configDir } from "../_chunks/paths-g3DRqJiD.js";
3
+ import { i as selectCredential, t as credentialInputs } from "../_chunks/auth_selection-Bx2NAgkk.js";
4
+ //#region src/init/profile_cli.ts
5
+ /**
6
+ * API-key selections fall back to DEFAULT because init authenticates from stored credentials.
7
+ */
8
+ function selectedProfileName() {
9
+ const selection = selectCredential(credentialInputs());
10
+ return selection.source === "profile" ? selection.profile : DEFAULT_PROFILE;
11
+ }
12
+ /**
13
+ * An explicit `--config-dir` must override `NEON_CONFIG_DIR` and the home path.
14
+ */
15
+ function selectedConfigDir() {
16
+ const recorded = credentialInputs().configDir;
17
+ return recorded !== "" ? recorded : configDir();
18
+ }
19
+ /**
20
+ * Omitting implicit DEFAULT preserves command compatibility; retaining explicit DEFAULT
21
+ * prevents ambient `NEON_PROFILE` from overriding it in subprocesses.
22
+ */
23
+ function explicitProfileArgs() {
24
+ const selection = selectCredential(credentialInputs());
25
+ if (selection.source === "profile" && selection.explicit) return ["--profile", selection.profile];
26
+ return [];
27
+ }
28
+ function explicitProfileCli() {
29
+ const args = explicitProfileArgs();
30
+ return args.length === 0 ? "" : ` ${args.join(" ")}`;
31
+ }
32
+ function explicitConfigDirArgs() {
33
+ const dir = credentialInputs().configDir;
34
+ return dir !== "" ? ["--config-dir", dir] : [];
35
+ }
36
+ function posixSingleQuote(value) {
37
+ return `'${value.replace(/'/g, `'\\''`)}'`;
38
+ }
39
+ function explicitConfigDirCli() {
40
+ const dir = credentialInputs().configDir;
41
+ return dir === "" ? "" : ` --config-dir ${posixSingleQuote(dir)}`;
42
+ }
43
+ function npxNeonArgs(command) {
44
+ return [
45
+ "-y",
46
+ "neon",
47
+ ...explicitProfileArgs(),
48
+ ...explicitConfigDirArgs(),
49
+ ...command
50
+ ];
51
+ }
52
+ function neonInitAgentCmd(data) {
53
+ return `neon init --agent${explicitProfileCli()}${explicitConfigDirCli()} --data '${JSON.stringify(data)}'`;
54
+ }
55
+ //#endregion
56
+ export { explicitConfigDirCli, explicitProfileArgs, explicitProfileCli, neonInitAgentCmd, npxNeonArgs, selectedConfigDir, selectedProfileName };
@@ -1,3 +1,4 @@
1
+ import { npxNeonArgs } from "./profile_cli.js";
1
2
  import { existsSync, readFileSync } from "node:fs";
2
3
  import { resolve } from "node:path";
3
4
  import { execa } from "execa";
@@ -40,14 +41,12 @@ async function resolveNeonContext(cwd) {
40
41
  if (!neonHost) return null;
41
42
  let orgs;
42
43
  try {
43
- const result = await execa("npx", [
44
- "-y",
45
- "neon",
44
+ const result = await execa("npx", npxNeonArgs([
46
45
  "orgs",
47
46
  "list",
48
47
  "--output",
49
48
  "json"
50
- ], {
49
+ ]), {
51
50
  stdio: "pipe",
52
51
  timeout: 3e4,
53
52
  env: {
@@ -63,16 +62,14 @@ async function resolveNeonContext(cwd) {
63
62
  for (const org of orgs) {
64
63
  let projects;
65
64
  try {
66
- const result = await execa("npx", [
67
- "-y",
68
- "neon",
65
+ const result = await execa("npx", npxNeonArgs([
69
66
  "projects",
70
67
  "list",
71
68
  "--org-id",
72
69
  org.id,
73
70
  "--output",
74
71
  "json"
75
- ], {
72
+ ]), {
76
73
  stdio: "pipe",
77
74
  timeout: 3e4,
78
75
  env: {
@@ -85,13 +82,11 @@ async function resolveNeonContext(cwd) {
85
82
  continue;
86
83
  }
87
84
  for (const project of projects) try {
88
- if ((await execa("npx", [
89
- "-y",
90
- "neon",
85
+ if ((await execa("npx", npxNeonArgs([
91
86
  "connection-string",
92
87
  "--project-id",
93
88
  project.id
94
- ], {
89
+ ]), {
95
90
  stdio: "pipe",
96
91
  timeout: 15e3,
97
92
  env: {
@@ -1,6 +1,13 @@
1
- import { r as recordCredentialInputs } from "../_chunks/auth_selection-CV_3n1du.js";
1
+ import { r as recordCredentialInputs } from "../_chunks/auth_selection-Bx2NAgkk.js";
2
2
  //#region src/utils/middlewares.ts
3
3
  /**
4
+ * Forwarding Yargs's default would turn ambient configuration into an explicit child override.
5
+ */
6
+ function configDirFromArgv(parsed) {
7
+ if (!process.argv.some((arg) => arg === "--config-dir" || arg.startsWith("--config-dir=")) || typeof parsed !== "string") return "";
8
+ return parsed;
9
+ }
10
+ /**
4
11
  * Resolves `--api-key` from `NEON_API_KEY` when the flag is absent, leaving it an
5
12
  * empty string when neither is set.
6
13
  *
@@ -20,7 +27,9 @@ const resolveApiKeyFromEnv = (args) => {
20
27
  recordCredentialInputs({
21
28
  apiKeyFlag: fromFlag,
22
29
  apiKeyEnv: fromEnv,
23
- profileEnv: process.env.NEON_PROFILE ?? ""
30
+ profileEnv: process.env.NEON_PROFILE ?? "",
31
+ profileFlag: typeof args.profile === "string" ? args.profile : "",
32
+ configDir: configDirFromArgv(args.configDir)
24
33
  });
25
34
  if (fromFlag !== "") return;
26
35
  args.apiKey = fromEnv;
package/dist/writer.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import { isCi } from "./env.js";
2
- import { isObject, toSnakeCase } from "./utils/string.js";
3
- import chalk from "chalk";
2
+ import { toSnakeCase } from "./utils/string.js";
3
+ import { formatHumanChunk, resolveOutputWidth } from "./human_table.js";
4
4
  import YAML from "yaml";
5
- import Table from "cli-table";
6
5
  //#region src/writer.ts
7
6
  const writeYaml = (chunks) => {
8
7
  return YAML.stringify(chunks.length === 1 ? chunks[0].data : Object.fromEntries(chunks.map(({ config, data }, idx) => [config.title ? toSnakeCase(config.title) : idx, data])), null, 2);
@@ -10,28 +9,18 @@ const writeYaml = (chunks) => {
10
9
  const writeJson = (chunks) => {
11
10
  return JSON.stringify(chunks.length === 1 ? chunks[0].data : Object.fromEntries(chunks.map(({ config, data }, idx) => [config.title ? toSnakeCase(config.title) : idx, data])), null, 2);
12
11
  };
13
- const writeTable = (chunks, out) => {
14
- chunks.forEach(({ data, config: { emptyMessage, fields, title, renderColumns = {} } }) => {
15
- const arrayData = Array.isArray(data) ? data : [data];
16
- if (!arrayData.length && emptyMessage) {
17
- out.write("\n" + emptyMessage + "\n");
18
- return;
19
- }
20
- const fieldsFiltered = fields.filter((field) => arrayData.some((item) => item[field] !== void 0 && item[field] !== ""));
21
- const table = new Table({
22
- style: { head: ["green"] },
23
- head: fieldsFiltered.map((field) => field.split("_").map((word) => word[0].toUpperCase() + word.slice(1)).join(" "))
24
- });
25
- arrayData.forEach((item) => {
26
- table.push(fieldsFiltered.map((field) => {
27
- const value = item[field];
28
- if (renderColumns[field]) return renderColumns[field]?.(item);
29
- return Array.isArray(value) ? value.join("\n") : isObject(value) ? JSON.stringify(value, null, 2) : value ?? "";
30
- }));
31
- });
32
- if (title) out.write((isCi() ? title : chalk.bold(title)) + "\n");
33
- out.write(table.toString());
34
- out.write("\n");
12
+ const writeTable = (chunks, out, width) => {
13
+ chunks.forEach(({ data, config }, i) => {
14
+ out.write(formatHumanChunk({
15
+ data,
16
+ fields: config.fields,
17
+ title: config.title,
18
+ emptyMessage: config.emptyMessage,
19
+ renderColumns: config.renderColumns,
20
+ width,
21
+ colorTitle: !isCi(),
22
+ leadingBlank: i > 0 && Boolean(config.title)
23
+ }));
35
24
  });
36
25
  };
37
26
  /**
@@ -50,6 +39,10 @@ const writeTable = (chunks, out) => {
50
39
  */
51
40
  const writer = (props) => {
52
41
  const out = props.out ?? process.stdout;
42
+ const width = resolveOutputWidth(out, props.columns, {
43
+ stdout: process.stdout,
44
+ envColumns: process.env.COLUMNS
45
+ });
53
46
  const chunks = [];
54
47
  return {
55
48
  write(data, config) {
@@ -69,7 +62,7 @@ const writer = (props) => {
69
62
  });
70
63
  if (props.output == "yaml") return out.write(writeYaml(chunks));
71
64
  if (props.output == "json") return out.write(writeJson(chunks));
72
- writeTable(chunks, out);
65
+ writeTable(chunks, out, width);
73
66
  }
74
67
  };
75
68
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon",
3
- "version": "3.5.1",
3
+ "version": "4.0.0",
4
4
  "description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
5
5
  "keywords": [
6
6
  "neon",
@@ -49,7 +49,6 @@
49
49
  "add-mcp": "2.0.0",
50
50
  "chalk": "5.3.0",
51
51
  "chokidar": "5.0.0",
52
- "cli-table": "0.3.11",
53
52
  "cliui": "8.0.1",
54
53
  "diff": "5.2.0",
55
54
  "execa": "^9.5.2",
@@ -64,9 +63,9 @@
64
63
  "yaml": "^2.9.0",
65
64
  "yargs": "17.7.2",
66
65
  "yoctocolors": "^2.1.2",
67
- "@neon/config-runtime": "1.0.1",
68
- "@neon/config": "1.0.1",
69
- "@neon/sdk": "2.1.0"
66
+ "@neon/config-runtime": "1.0.2",
67
+ "@neon/config": "1.0.2",
68
+ "@neon/sdk": "2.2.0"
70
69
  },
71
70
  "optionalDependencies": {
72
71
  "@napi-rs/keyring": "1.3.0",
@@ -78,7 +77,6 @@
78
77
  "@rollup/plugin-json": "6.1.0",
79
78
  "@rollup/plugin-node-resolve": "15.2.3",
80
79
  "@testcontainers/postgresql": "^11.14.0",
81
- "@types/cli-table": "0.3.4",
82
80
  "@types/diff": "5.2.1",
83
81
  "@types/express": "4.17.21",
84
82
  "@types/node": "^20.19.0",
@@ -98,8 +96,8 @@
98
96
  "tsx": "4.22.3",
99
97
  "typescript": "^5.9.0",
100
98
  "vitest": "^3.0.9",
101
- "@neon-internals/env-core": "0.0.1",
102
99
  "@neon-internals/cli-core": "0.0.0",
100
+ "@neon-internals/env-core": "0.0.2",
103
101
  "@neon/e2e-harness": "0.0.0"
104
102
  },
105
103
  "publishConfig": {