neon 8.0.12 → 8.2.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
@@ -63,10 +63,10 @@ leaves `neon` running the other install, such as npm. While duplicates remain, t
63
63
  Run the following command to authenticate a connection to Neon:
64
64
 
65
65
  ```bash
66
- neon auth
66
+ neon login
67
67
  ```
68
68
 
69
- The `auth` command launches a browser window where you can authorize the Neon CLI to access your Neon account. Running a Neon CLI command without authenticating with [neon auth](https://neon.com/docs/cli/auth) automatically launches the browser authentication process.
69
+ `neon auth` is an alias of `neon login`. The `login` command launches a browser window where you can authorize the Neon CLI to access your Neon account. Running a Neon CLI command without authenticating with [neon login](https://neon.com/docs/cli/auth) automatically launches the browser authentication process, except for `neon me`, which reports that you are not signed in.
70
70
 
71
71
  Alternatively, you can authenticate a connection with a Neon API key using the `--api-key` option when running a Neon CLI command. For example, an API key is used with the following `neon projects list` command:
72
72
 
@@ -76,7 +76,7 @@ neon projects list --api-key <neon_api_key>
76
76
 
77
77
  For information about obtaining an Neon API key, see [Authentication](https://neon.com/docs/reference/api/get-started), in the _Neon API Reference_.
78
78
 
79
- To check which account and credential a command uses, run `neon me` with the same `--profile` or `--api-key` option and environment as that command. The `Authentication` row names the source, such as `OAuth (profile DEFAULT)`, `API key (profile work)`, `API key (--api-key)`, or `API key (NEON_API_KEY)`.
79
+ To check which account and credential a command uses, run `neon me` with the same `--profile` or `--api-key` option and environment as that command. The `Authentication` row names the source, such as `OAuth (profile DEFAULT)`, `API key (profile work)`, `API key (--api-key)`, or `API key (NEON_API_KEY)`. When no credential is usable, `neon me` exits 1 with `Not signed in` and the `neon login` command to run; it never starts a sign-in itself.
80
80
 
81
81
  ## Create a project without an account
82
82
 
@@ -474,11 +474,11 @@ The resolved branch is then written (by name) to the same `.neon` file `link` us
474
474
 
475
475
  ```bash
476
476
  $ neon checkout main --project-id polished-snowflake-12345678
477
- INFO: Checked out branch br-main-branch-87654321 on project polished-snowflake-12345678. Updated /path/to/cwd/.neon.
477
+ Checked out branch br-main-branch-87654321 on project polished-snowflake-12345678. Updated /path/to/cwd/.neon.
478
478
 
479
479
  $ neon checkout dev --create --project-id polished-snowflake-12345678
480
- INFO: Created branch dev (br-dev-branch-12345678).
481
- INFO: Checked out branch br-dev-branch-12345678 on project polished-snowflake-12345678. Updated /path/to/cwd/.neon.
480
+ Created branch dev (br-dev-branch-12345678).
481
+ Checked out branch br-dev-branch-12345678 on project polished-snowflake-12345678. Updated /path/to/cwd/.neon.
482
482
 
483
483
  $ cat .neon
484
484
  {
@@ -658,13 +658,15 @@ neon config init --services data-api
658
658
  # Repeat the flag instead, and shorten it — every services flag takes all three spellings
659
659
  neon config init -s auth -s functions
660
660
 
661
- # Explicitly ask for the bare starter policy
662
- neon config init --services none
661
+ # Postgres only: the starter policy, with no prompt
662
+ neon config init --services postgres
663
663
 
664
664
  # Scaffold but print the install command instead of running it
665
665
  neon config init --no-install
666
666
  ```
667
667
 
668
+ `postgres` declares nothing, since every branch has Postgres, so it can stand alone or sit next to other services. `neon init --services` takes the same values.
669
+
668
670
  Object storage is spelled `object-storage` here, matching [`env pull --service`](#env-pull) and the rest of the CLI. The old `storage` still works and warns; it will be removed.
669
671
 
670
672
  Choosing **Functions** also writes the handler the policy points at, since `source` is only resolved when `apply` bundles it — a declared function with no file on disk fails at deploy:
@@ -696,9 +698,9 @@ Each command prints what it changed and the next step:
696
698
 
697
699
  ```
698
700
  $ neon config add function sendemail
699
- INFO: Created functions/sendemail.ts.
700
- INFO: Updated neon.ts: added functions.sendemail.
701
- INFO: Next: `neon dev` to run it locally, `neon config apply` to deploy.
701
+ Created functions/sendemail.ts.
702
+ Updated neon.ts: added functions.sendemail.
703
+ Next: `neon dev` to run it locally, `neon config apply` to deploy.
702
704
  ```
703
705
 
704
706
  - **Functions** take a slug of 1-20 lowercase letters and digits, the same rule `plan` enforces. The handler file is written first and an existing file is never overwritten, so pointing `--source` at code you already have works. A `.js` `neon.ts` gets a `.js` handler.
@@ -738,12 +740,18 @@ neon deploy
738
740
 
739
741
  The branch is chosen with `--branch <id|name>`; without it the project's default branch is used. The policy is the `neon.ts` in the project directory (next to `.neon`, or the current directory without one); pass `--config <path>` to point at another file.
740
742
 
741
- **Apply-only flags** (also available on `deploy`):
743
+ **Confirmations** (also on `deploy`). Two things need a yes before `apply` changes anything: applying to a branch Neon marks as protected, and overriding settings the branch already has (compute, TTL, `protected`, Data API settings, custom-domain targets, and disabling an existing Data API). Adding services and deploying functions is not an override, so on an unprotected branch it never asks. In a terminal, `apply` shows the settings it would override and asks once, default No. Without a terminal (CI, agents, pipes) or with `-o json|yaml`, it exits 1 before changing anything and names the flag to pass:
744
+
745
+ - `--update-existing` — override settings that differ from `neon.ts`.
746
+ - `--allow-protected` — apply to a protected branch.
747
+ - `-y`, `--yes` — both.
742
748
 
743
- - `--update-existing` — auto-confirm overriding existing remote settings on the branch. Without it, drift on settings already present remotely (compute, TTL, `protected`) is reported as a **conflict** and `apply` makes no changes until you resolve it or pass this flag.
744
- - `--allow-protected` — auto-confirm applying to a branch Neon marks as protected. Without it, `apply` refuses to touch a protected branch.
749
+ ```bash
750
+ neon deploy --env .env.prod --allow-protected # production main is protected, no drift expected
751
+ neon deploy -y # protected branch and drift, no questions
752
+ ```
745
753
 
746
- **Output**: `status` prints the project, branch, and reverse-engineered config. `plan` / `apply` render a **`git diff`-style report** (matching [`neon diff`](#diff)): service changes (Neon Auth, Data API, buckets, functions) list as green `+` additions, while **branch setting changes** (TTL, `protected`, compute) show grouped under a `~ <branch>` header, one sorted `field → value` line each. A bare `apply` that hits drift on settings already present remotely prints those as a sorted **before→after** diff (`current → desired`, old in red / new in green) and exits non-zero until you pass `--update-existing`. Pass `--output json` (or `--output yaml`) to emit the full machine-readable result (`PushResult`) instead, for piping into other tools or CI.
754
+ **Output**: `status` prints the project, branch, and reverse-engineered config. `plan` / `apply` render a **`git diff`-style report** (matching [`neon diff`](#diff)): service changes (Neon Auth, Data API, buckets, functions) list as green `+` additions, while **branch setting changes** (TTL, `protected`, compute) show grouped under a `~ <branch>` header, one sorted `field → value` line each. When `apply` would override settings already present remotely, it prints them as a sorted **before→after** diff (`current → desired`, old in red / new in green) before asking or, without a terminal, before exiting non-zero. Pass `--output json` (or `--output yaml`) to emit the full machine-readable result (`PushResult`) instead, for piping into other tools or CI.
747
755
 
748
756
  **`config status --current-branch`** (alias `neon status --current-branch`) prints _only_ the branch pinned in the local `.neon` file — no network, no auth, no analytics — and exits non-zero when none is pinned. This behavior lets it safely drive a shell prompt. Example [starship](https://starship.rs) segment:
749
757
 
@@ -831,7 +839,7 @@ The target directory must be empty unless you pass `--force` (a lone `.git` is i
831
839
 
832
840
  Recommended installs the Neon plugin for every detected agent (global config, project folders, and the host CLI). Plugin-capable agents get the plugin; the rest get skills and MCP. If none are detected, it installs the default Neon skills for Cursor and Codex in this directory (`./.agents/skills`) and does not configure MCP. It then links a project when the CLI is authenticated, and writes a default `neon.ts` (Postgres only) using the package manager already in the directory.
833
841
 
834
- Unauthenticated `-y` skips linking and prints the next step: sign up at https://neon.com/signup, then `neon auth`, `neon link`, or `neon claim create` (no account, expires in 72 hours unless claimed). `-y` never opens a browser. `-y` with `--project-id` or other account flags links when the CLI is already signed in; otherwise it errors and names `neon auth` or `--claimable`.
842
+ Unauthenticated `-y` skips linking and prints the next step: sign up at https://neon.com/signup, then `neon login`, `neon link`, or `neon claim create` (no account, expires in 72 hours unless claimed). `-y` never opens a browser. `-y` with `--project-id` or other account flags links when the CLI is already signed in; otherwise it errors and names `neon login` or `--claimable`.
835
843
 
836
844
  Custom asks how to add Neon to coding agents (plugin, skills and MCP separately, or skip), then how to get a project (sign in and link, or a claimable project when you are not signed in), then which services `neon.ts` should declare. Skills and MCP use separate agent pickers, and either can be skipped. Postgres is listed first as always included. The package manager is inferred from the directory; Custom asks only when none is detected. `--skill` selects skills (not the plugin) and skips that picker. MCP flags (`--mcp-config-location`, `--mcp-auth`, `--mcp-project-scoped`) select skills and MCP. `--mcp-config-location` is where the config is written: `global` maps to `neon mcp`, and `project` maps to `neon mcp --project`. With `-y`, the location defaults to `global`; interactive Custom asks. If some selected agents cannot write that location, init warns and skips MCP for those agents while continuing with the supported agents. It fails when none of the selected agents support the location. `--mcp-project-scoped` limits MCP tools to the linked Neon project by passing its ID to `neon mcp --project-id`. Omitted is false, including in Custom; there is no prompt. `--no-agent-setup` skips agent setup.
837
845
 
@@ -1198,18 +1206,18 @@ Credential Manager, Linux Secret Service) only when its `profiles.json` pointer
1198
1206
  That is per profile. Reads never migrate.
1199
1207
 
1200
1208
  ```bash
1201
- neon auth --keyring # sign DEFAULT into the OS keyring
1202
- neon auth --keyring --profile work # sign work into the OS keyring
1209
+ neon login --keyring # sign DEFAULT into the OS keyring
1210
+ neon login --keyring --profile work # sign work into the OS keyring
1203
1211
  neon profile create work --keyring # create a named profile in the keyring
1204
1212
  neon profile remove work --yes # drop a keyring profile, then create it again as a file
1205
1213
  ```
1206
1214
 
1207
- File to keyring is `neon auth --keyring` or `neon profile create … --keyring`: a new
1215
+ File to keyring is `neon login --keyring` or `neon profile create … --keyring`: a new
1208
1216
  sign-in, then the previous credential is revoked and the owned file is deleted.
1209
- `create` on an existing name always revokes after a successful write. `auth` revokes
1217
+ `create` on an existing name always revokes after a successful write. `login` revokes
1210
1218
  when it writes to the keyring, including a re-login that follows an existing pointer.
1211
- `auth` that overwrites a file does not. Keyring to file is `remove`, then create or
1212
- auth again. `create` and `auth` without `--keyring` follow an existing `"keyring"`
1219
+ `login` that overwrites a file does not. Keyring to file is `remove`, then create or
1220
+ log in again. `create` and `login` without `--keyring` follow an existing `"keyring"`
1213
1221
  pointer, so a keyring profile cannot leave the OS store until `remove` succeeds.
1214
1222
 
1215
1223
  `--api-key` and `NEON_API_KEY` skip both stores. A GitHub-release `neon-<platform>`
@@ -1218,7 +1226,7 @@ Use the npm-installed `neon`. Older releases treat the sentinel as a relative pa
1218
1226
 
1219
1227
  A `"keyring"` pointer whose OS item cannot be read is not treated as signed-out. Commands that
1220
1228
  would otherwise open a browser fail: could not read the OS keyring item. Unlock it and
1221
- retry, or run `neon auth --profile DEFAULT`. To reset the profile: `neon profile remove DEFAULT --yes`.
1229
+ retry, or run `neon login --profile DEFAULT`. To reset the profile: `neon profile remove DEFAULT --yes`.
1222
1230
  A missing `credentials.json` with no `profiles.json` is still
1223
1231
  signed-out, and those commands start OAuth.
1224
1232
 
@@ -1258,7 +1266,7 @@ the key never leaves the CLI.
1258
1266
  **A profile is one kind or the other, never both.** `type` in the credentials file states which:
1259
1267
 
1260
1268
  ```json
1261
- // oauth: what a plain `create` (or `neon auth --profile`) writes. An absent `type` means this.
1269
+ // oauth: what a plain `create` (or `neon login --profile`) writes. An absent `type` means this.
1262
1270
  { "access_token": "…", "refresh_token": "…", "expires_at": 1786…, "user_id": "…" }
1263
1271
 
1264
1272
  // api_key: what `--api-key` writes
@@ -1299,7 +1307,7 @@ Passing both flags fails rather than picking a winner: `--api-key` supplies a cr
1299
1307
 
1300
1308
  When both are only environment variables the key wins, which keeps a CI pipeline that injects `NEON_API_KEY` working even if a `NEON_PROFILE` leaks into the environment — but the disregarded profile is named on stderr rather than passed over silently.
1301
1309
 
1302
- `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.
1310
+ `neon login` and the `profile` subcommands are outside all of this, because they read the same flags to mean something else: `neon login --profile work` names where to write a credential, and `neon profile create work --api-key …` names one to store.
1303
1311
 
1304
1312
  `neon init` forwards `--profile` and `--config-dir` to agent and config children and uses them directly for its link flow. An explicit `--api-key` is passed to authenticated children through `NEON_API_KEY`, not argv.
1305
1313
 
@@ -1341,7 +1349,7 @@ Project proj-in-org
1341
1349
 
1342
1350
  napi_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1343
1351
  WARNING: Store this key now: it is not shown again.
1344
- INFO: Limited to proj-in-org: it cannot create projects, mint API keys, or read any other project. It can still change and delete everything inside that project.
1352
+ Limited to proj-in-org: it cannot create projects, mint API keys, or read any other project. It can still change and delete everything inside that project.
1345
1353
  ```
1346
1354
 
1347
1355
  `--org-id` and `--project-id` are mutually exclusive. A project-scoped key *is* an organization key, and its organization is looked up from the project rather than chosen separately. With neither flag you get an account key.
@@ -1387,7 +1395,7 @@ Id Name Project Created At Last Used At Last
1387
1395
 
1388
1396
  | Command | Subcommands | Description |
1389
1397
  | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------------------- |
1390
- | [auth](https://neon.com/docs/reference/cli-auth) | | Authenticate |
1398
+ | [login](https://neon.com/docs/reference/cli-auth) (`auth`) | | Authenticate |
1391
1399
  | claim (`claimable`) | `create`, `status`, `accept`, `list`, `delete` | Manage claimable projects |
1392
1400
  | profile | `list`, `create`, `rotate-key`, `remove` | Manage named sets of credentials |
1393
1401
  | api-keys | `list`, `create`, `revoke` | Manage API keys |
@@ -1449,7 +1457,7 @@ Global options are supported with any Neon CLI command.
1449
1457
 
1450
1458
  - <a id="config-dir"></a>`--config-dir`
1451
1459
 
1452
- Specifies the path to the `neon` configuration directory, which holds the `credentials.json` written by `neon auth` and, when a second profile exists or DEFAULT is keyring, `profiles.json`. The default is `$XDG_CONFIG_HOME/neon`, or `~/.config/neon`; run `neon --help` to see the resolved path. This option is only necessary if you keep your configuration somewhere else.
1460
+ Specifies the path to the `neon` configuration directory, which holds the `credentials.json` written by `neon login` and, when a second profile exists or DEFAULT is keyring, `profiles.json`. The default is `$XDG_CONFIG_HOME/neon`, or `~/.config/neon`; run `neon --help` to see the resolved path. This option is only necessary if you keep your configuration somewhere else.
1453
1461
 
1454
1462
  The directory was called `neonctl` before the CLI was renamed. An existing one is still read, and is used **in place** — nothing is moved or copied, so there is never a second credentials file to go stale. A directory you pass explicitly is used exactly as given and never falls back to the legacy name, so pointing a CI run at a scratch directory cannot pick up local credentials.
1455
1463
 
@@ -1462,7 +1470,7 @@ Global options are supported with any Neon CLI command.
1462
1470
  Selects a named set of credentials, for holding more than one Neon account at a time. A profile is a pointer to a credentials file, recorded in `profiles.json` next to it.
1463
1471
 
1464
1472
  ```bash
1465
- neon auth --profile work # create it, or sign in again
1473
+ neon login --profile work # create it, or sign in again
1466
1474
  neon profile list # names, accounts, and where each one's credentials live
1467
1475
  neon projects list --profile work
1468
1476
  NEON_PROFILE=work neon projects list
@@ -1472,7 +1480,7 @@ Global options are supported with any Neon CLI command.
1472
1480
 
1473
1481
  - <a id="api-key"></a>`--api-key`
1474
1482
 
1475
- Specifies your Neon API key. You can authenticate using a Neon API key when running a Neon CLI command instead of using `neon auth`. For information about obtaining an Neon API key, see [Authentication](https://neon.com/docs/reference/api/get-started), in the _Neon API Reference_.
1483
+ Specifies your Neon API key. You can authenticate using a Neon API key when running a Neon CLI command instead of using `neon login`. For information about obtaining an Neon API key, see [Authentication](https://neon.com/docs/reference/api/get-started), in the _Neon API Reference_.
1476
1484
 
1477
1485
  ```bash
1478
1486
  neon <command> --api-key <neon_api_key>
@@ -1,4 +1,4 @@
1
- import "./profiles-CvnFEQyd.js";
1
+ import "./profiles-DIB1av2Q.js";
2
2
  //#region ../../internals/cli-core/dist/auth_selection.js
3
3
  /**
4
4
  * # Which credential an invocation authenticates with
@@ -32,8 +32,8 @@ import "./profiles-CvnFEQyd.js";
32
32
  * the environment. It warns instead of staying silent, because a disregarded account
33
33
  * selection is precisely what nobody noticed last time.
34
34
  *
35
- * `auth` and the `profile` subcommands do not use any of this. They read the same flags with
36
- * different meanings — `neon auth --profile work` names where to *write* a credential, and
35
+ * `login` and the `profile` subcommands do not use any of this. They read the same flags with
36
+ * different meanings — `neon login --profile work` names where to *write* a credential, and
37
37
  * `neon profile create work --api-key …` names one to *store* — so their callers skip
38
38
  * selection entirely rather than passing exemptions down here.
39
39
  */
@@ -1,6 +1,6 @@
1
1
  import { t as writeSecretFile } from "./secure_file-BucZj4yQ.js";
2
2
  import { a as isOwnedCredentialPath } from "./paths-DMq0Lt7a.js";
3
- import { f as profilesFilePath, v as CRED_STORAGE_FILE, y as CRED_STORAGE_KEYRING } from "./profiles-CvnFEQyd.js";
3
+ import { f as profilesFilePath, v as CRED_STORAGE_FILE, y as CRED_STORAGE_KEYRING } from "./profiles-DIB1av2Q.js";
4
4
  import { tryLoadKeyring } from "../keyring.js";
5
5
  import { existsSync, readFileSync, rmSync } from "node:fs";
6
6
  import { dirname } from "node:path";
@@ -202,7 +202,7 @@ var KeyringUnavailableError = class extends Error {
202
202
  };
203
203
  var KeyringUnreadableError = class extends Error {
204
204
  constructor(profile) {
205
- const replace = `\`neon auth --profile ${profile}\``;
205
+ const replace = `\`neon login --profile ${profile}\``;
206
206
  super(`Could not read the OS keyring item for profile "${profile}". Unlock the keyring and retry, or run ${replace}. To reset the profile: \`neon profile remove ${profile} --yes\`.`);
207
207
  this.name = "KeyringUnreadableError";
208
208
  }
@@ -82,7 +82,7 @@ const inspectProfiles = (dir) => {
82
82
  *
83
83
  * A malformed file is reported through `onWarn` and treated as absent, because for a *read* the
84
84
  * worst case is a named profile turning up missing, which is recoverable — whereas throwing
85
- * would lock the user out of `neon auth` itself. Writing is the opposite: see
85
+ * would lock the user out of `neon login` itself. Writing is the opposite: see
86
86
  * {@link upsertProfile}, which refuses rather than rebuilding a file it cannot read.
87
87
  */
88
88
  const readProfiles = (dir, onWarn = () => {}) => {
@@ -96,7 +96,7 @@ const readProfiles = (dir, onWarn = () => {}) => {
96
96
  *
97
97
  * Call this **before** anything that writes a credential, opens a browser, or spends an API
98
98
  * call. {@link upsertProfile} refuses too, but it runs last: by then `create` has already
99
- * overwritten `credentials.<name>.json` and revoked the key it replaced, and `neon auth
99
+ * overwritten `credentials.<name>.json` and revoked the key it replaced, and `neon login
100
100
  * --profile` has already signed in over it — a refusal that arrives after the destruction it
101
101
  * exists to prevent. The path resolution itself is the unsound part, since with the metadata
102
102
  * unreadable the conventional filename is a guess about which account that file belongs to.
@@ -32,8 +32,8 @@ import "./profiles.js";
32
32
  * the environment. It warns instead of staying silent, because a disregarded account
33
33
  * selection is precisely what nobody noticed last time.
34
34
  *
35
- * `auth` and the `profile` subcommands do not use any of this. They read the same flags with
36
- * different meanings — `neon auth --profile work` names where to *write* a credential, and
35
+ * `login` and the `profile` subcommands do not use any of this. They read the same flags with
36
+ * different meanings — `neon login --profile work` names where to *write* a credential, and
37
37
  * `neon profile create work --api-key …` names one to *store* — so their callers skip
38
38
  * selection entirely rather than passing exemptions down here.
39
39
  */
@@ -13,7 +13,7 @@ import { isAbsolute, relative, resolve } from "node:path";
13
13
  * ```
14
14
  * ~/.config/neon/
15
15
  * ├── credentials.json # this IS the DEFAULT profile, not a copy of it
16
- * ├── credentials.work.json # created by `neon auth --profile work`
16
+ * ├── credentials.work.json # created by `neon login --profile work`
17
17
  * └── profiles.json # created only once a second profile exists
18
18
  * ```
19
19
  *
@@ -105,7 +105,7 @@ const inspectProfiles = (dir) => {
105
105
  *
106
106
  * A malformed file is reported through `onWarn` and treated as absent, because for a *read* the
107
107
  * worst case is a named profile turning up missing, which is recoverable — whereas throwing
108
- * would lock the user out of `neon auth` itself. Writing is the opposite: see
108
+ * would lock the user out of `neon login` itself. Writing is the opposite: see
109
109
  * {@link upsertProfile}, which refuses rather than rebuilding a file it cannot read.
110
110
  */
111
111
  const readProfiles = (dir, onWarn = () => {}) => {
@@ -119,7 +119,7 @@ const readProfiles = (dir, onWarn = () => {}) => {
119
119
  *
120
120
  * Call this **before** anything that writes a credential, opens a browser, or spends an API
121
121
  * call. {@link upsertProfile} refuses too, but it runs last: by then `create` has already
122
- * overwritten `credentials.<name>.json` and revoked the key it replaced, and `neon auth
122
+ * overwritten `credentials.<name>.json` and revoked the key it replaced, and `neon login
123
123
  * --profile` has already signed in over it — a refusal that arrives after the destruction it
124
124
  * exists to prevent. The path resolution itself is the unsound part, since with the metadata
125
125
  * unreadable the conventional filename is a guess about which account that file belongs to.
package/dist/analytics.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as OAUTH, t as storeFor } from "./_chunks/credential_io-D8AR-jvB.js";
1
+ import { a as OAUTH, t as storeFor } from "./_chunks/credential_io-ByNuojoE.js";
2
2
  import { getCliAgent, getGithubEnvVars, isCi } from "./env.js";
3
3
  import { log } from "./log.js";
4
4
  import pkg_default from "./pkg.js";
@@ -31,8 +31,8 @@ const authFailureMessage = (context) => {
31
31
  if (context?.source === "profile-api-key") return `Authentication failed: the Neon API rejected profile "${profile}"'s API key${where}. Replace it with \`neon profile create ${profile} --mint\`, or store another with \`neon profile create ${profile} --api-key -\`.`;
32
32
  if (context?.source === "claimable") return `Authentication failed: Claimable Neon rejected the linked project's short-lived access token${where}. Retry the command to exchange the saved identity assertion again; if it still fails, run \`neon claim status\`.`;
33
33
  if (context?.source === "stored-credentials") {
34
- if (context.storage === "keyring") return `Authentication failed: the Neon API rejected profile "${profile}"'s stored session (OS keyring). Sign in again with \`neon auth --profile ${profile}\`.`;
35
- return `Authentication failed: the Neon API rejected profile "${profile}"'s stored session${where}. Sign in again with \`neon auth --profile ${profile}\`.`;
34
+ if (context.storage === "keyring") return `Authentication failed: the Neon API rejected profile "${profile}"'s stored session (OS keyring). Sign in again with \`neon login --profile ${profile}\`.`;
35
+ return `Authentication failed: the Neon API rejected profile "${profile}"'s stored session${where}. Sign in again with \`neon login --profile ${profile}\`.`;
36
36
  }
37
37
  return "Authentication failed: the Neon API rejected the API key. Check --api-key or NEON_API_KEY.";
38
38
  };
@@ -1,4 +1,4 @@
1
- import { t as storeFor } from "./_chunks/credential_io-D8AR-jvB.js";
1
+ import { t as storeFor } from "./_chunks/credential_io-ByNuojoE.js";
2
2
  import { log } from "./log.js";
3
3
  import { authFailureMessage, getAuthContext, locationFromContext } from "./auth_context.js";
4
4
  import { AuthRefreshError } from "./auth.js";
@@ -49,7 +49,7 @@ const recoverFrom401 = async (canRetry) => {
49
49
  } catch (err) {
50
50
  if (err instanceof AuthRefreshError) {
51
51
  log.error(err.message);
52
- if (err.terminal) log.error(`Run \`neon auth --profile ${context.profile ?? "DEFAULT"}\` to sign in again.`);
52
+ if (err.terminal) log.error(`Run \`neon login --profile ${context.profile ?? "DEFAULT"}\` to sign in again.`);
53
53
  return false;
54
54
  }
55
55
  log.debug("Refresh after 401 failed: %s", err instanceof Error ? err.message : "unknown error");
@@ -242,7 +242,7 @@ const withdraw = async (props, orgId, keyId) => {
242
242
  const { data } = orgId ? await props.apiClient.revokeOrgApiKey(orgId, keyId) : await props.apiClient.revokeApiKey(keyId);
243
243
  return data.revoked === true && data.id === keyId;
244
244
  } catch (err) {
245
- log.error("Failed to revoke API key %d: %s", keyId, err instanceof Error ? err.message : String(err));
245
+ log.warning("Failed to revoke API key %d: %s", keyId, err instanceof Error ? err.message : String(err));
246
246
  return false;
247
247
  }
248
248
  };
@@ -1,15 +1,15 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
- import { a as OAUTH, i as API_KEY, l as interpretCredentials, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-D8AR-jvB.js";
2
+ import { a as OAUTH, i as API_KEY, l as interpretCredentials, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-ByNuojoE.js";
3
3
  import { isCi } from "../env.js";
4
4
  import { log } from "../log.js";
5
5
  import { getApiClient, isNeonApiError } from "../api.js";
6
6
  import { setAuthContext } from "../auth_context.js";
7
7
  import { a as isOwnedCredentialPath } from "../_chunks/paths-DMq0Lt7a.js";
8
8
  import { currentContextFile, isAskCommand, isClaimCommand, isConfigAdd, isConfigInit, isCurrentBranchProbe, isFeedbackCommand, isGitLocalCommand, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, isUnfollowedGitHookSync, readContextFile } from "../context.js";
9
- import { _ as upsertProfile, d as newProfileLocation, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, t as DEFAULT_PROFILE, u as locationOf } from "../_chunks/profiles-CvnFEQyd.js";
9
+ import { _ as upsertProfile, d as newProfileLocation, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, t as DEFAULT_PROFILE, u as locationOf } from "../_chunks/profiles-DIB1av2Q.js";
10
10
  import { extendTokenSet } from "../utils/auth.js";
11
11
  import { AuthRefreshError, auth, classifyRefreshFailure, refreshToken } from "../auth.js";
12
- import { i as selectCredential, n as displacedProfileWarning, t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
12
+ import { i as selectCredential, n as displacedProfileWarning, t as credentialInputs } from "../_chunks/auth_selection-YX5_KZgD.js";
13
13
  import { ClaimableClient, ClaimableServiceError } from "../claimable/api.js";
14
14
  import { assertionHasExpired, claimableCredentialsExist, claimableCredentialsPath, readLinkedClaimableCredentials, shouldUseClaimableCredentials } from "../claimable/state.js";
15
15
  import { withExclusiveLock } from "../refresh_lock.js";
@@ -45,8 +45,8 @@ const locationForAuth = (configDir, name, keyring, options = {}) => {
45
45
  if (options.create === true) return newProfileLocation(configDir, name, "file");
46
46
  return locationForName(configDir, name);
47
47
  };
48
- const command = "auth";
49
- const aliases = ["login"];
48
+ const command = "login";
49
+ const aliases = ["auth"];
50
50
  const describe = "Sign in with a browser. See --help for API keys and profiles";
51
51
  const wrapAuthHelp = (text) => wrapHelpText(text, helpWidth());
52
52
  const authHelpEpilogue = () => {
@@ -54,9 +54,9 @@ const authHelpEpilogue = () => {
54
54
  return helpEpilogue(wrapAuthHelp("Non-interactive (CI, scripts, agents):"), wrapAuthHelp("Use an existing API key or an API-key profile."), ` ${cli} projects list --profile agent`, wrapAuthHelp(`${cli} profile create <name> --mint needs a browser during setup, then stores an API key and drops the session. It cannot run in CI. Default key is account-wide; --org-id or --project-id (not both) restricts it.`), wrapAuthHelp(`A profile holds a browser session or an API key. Select it with --profile <name> or NEON_PROFILE. List profiles with ${cli} profile list.`), wrapAuthHelp("On commands that use a credential, --api-key or --profile overrides NEON_API_KEY and NEON_PROFILE. Pass either --api-key or --profile, not both. When neither flag is passed and both NEON_API_KEY and NEON_PROFILE are set, the key wins and stderr names the ignored profile. When no flag or environment variable selects a credential, DEFAULT is used."), wrapAuthHelp(`${cli} profile create <name> replaces an existing profile of that name and tries to revoke the credential it held. An imported key with no recorded id stays live. Pick a new name to keep the old profile.`), wrapAuthHelp(`See ${cli} profile --help and ${cli} profile create --help.`));
55
55
  };
56
56
  const builder = (yargs) => yargs.option("context-file", { hidden: true }).option("keyring", {
57
- describe: "Store the credential in the OS keyring. Per profile; later auth without the flag stays there. See `neon profile list`.",
57
+ describe: "Store the credential in the OS keyring. Per profile; a later login without the flag stays there. See `neon profile list`.",
58
58
  type: "boolean"
59
- }).example("$0 auth", "Sign in with a browser").example("$0 auth --profile work", "Save a browser session in profile work").example("$0 projects list --api-key \"$KEY\"", "Use an existing API key for this command").example("NEON_API_KEY=\"$KEY\" $0 projects list", "Use an existing API key from the environment").example("$0 profile create agent --api-key \"$KEY\"", "Store an existing API key").example("echo \"$KEY\" | $0 profile create agent --api-key -", "Store a key read from stdin").example("$0 profile create ci --mint", "Sign in once in a browser and store a new API key").epilogue(authHelpEpilogue());
59
+ }).example("$0 login", "Sign in with a browser").example("$0 login --profile work", "Save a browser session in profile work").example("$0 projects list --api-key \"$KEY\"", "Use an existing API key for this command").example("NEON_API_KEY=\"$KEY\" $0 projects list", "Use an existing API key from the environment").example("$0 profile create agent --api-key \"$KEY\"", "Store an existing API key").example("echo \"$KEY\" | $0 profile create agent --api-key -", "Store a key read from stdin").example("$0 profile create ci --mint", "Sign in once in a browser and store a new API key").epilogue(authHelpEpilogue());
60
60
  const handler = async (args) => {
61
61
  await authFlow(args);
62
62
  };
@@ -160,7 +160,7 @@ const fetchIdentity = async (apiClient) => {
160
160
  ...email ? { email } : {}
161
161
  };
162
162
  } catch (err) {
163
- if (isNeonApiError(err) && err.status === 401) throw new Error("Signed in, but the Neon API rejected the new access token. Try `neon auth` again.");
163
+ if (isNeonApiError(err) && err.status === 401) throw new Error("Signed in, but the Neon API rejected the new access token. Try `neon login` again.");
164
164
  log.warning("Signed in, but could not look up the account: %s", err instanceof Error ? err.message : String(err));
165
165
  return {};
166
166
  }
@@ -317,7 +317,7 @@ const ensureAuth = async (props) => {
317
317
  const isLocalDev = props._[0] === "dev";
318
318
  const isBootstrap = props._[0] === "bootstrap";
319
319
  const isMcp = isMcpCommand(props);
320
- if (props._[0] === "auth") {
320
+ if (props._[0] === "login" || props._[0] === "auth") {
321
321
  props.apiClient = getApiClient({
322
322
  apiKey: props.apiKey,
323
323
  apiHost: props.apiHost
@@ -337,7 +337,7 @@ const ensureAuth = async (props) => {
337
337
  try {
338
338
  token = await client.exchange(stored.identityAssertion);
339
339
  } catch (error) {
340
- if (error instanceof ClaimableServiceError && error.code === "project_claimed") throw new Error("This project was claimed. Run `neon claim status` to drop the local assertion, then `neon auth` or `neon link`.");
340
+ if (error instanceof ClaimableServiceError && error.code === "project_claimed") throw new Error("This project was claimed. Run `neon claim status` to drop the local assertion, then `neon login` or `neon link`.");
341
341
  throw error;
342
342
  }
343
343
  props.apiKey = token.accessToken;
@@ -449,6 +449,12 @@ const ensureAuth = async (props) => {
449
449
  log.debug("mcp: no usable credentials; minting requires auth or --oauth");
450
450
  return;
451
451
  }
452
+ if (props._[0] === "me") {
453
+ const state = loaded === null ? "has no stored credential" : "holds a session that has expired";
454
+ log.info(`Not signed in: profile "${selection.profile}" ${state}. Run \`${getCliName()} login --profile ${selection.profile}\` to sign in, or use an API key with --api-key or NEON_API_KEY.`);
455
+ process.exitCode = 1;
456
+ return;
457
+ }
452
458
  const apiKey = await authFlow({
453
459
  ...props,
454
460
  profile: selection.profile
@@ -223,7 +223,7 @@ const resolveBranchId = async (props, projectId, hookCtx) => {
223
223
  if (props.create) return createCheckoutBranch(props, projectId, ref, branches, hookCtx);
224
224
  const missingName = notFoundMessage(ref, branches, { suggestCreate: true });
225
225
  if (isCi() || !process.stdout.isTTY) throw new Error(missingName);
226
- log.error(missingName);
226
+ log.info(missingName);
227
227
  const { create } = await prompts({
228
228
  type: "confirm",
229
229
  name: "create",
@@ -310,7 +310,7 @@ const resolveProjectId = async (props) => {
310
310
  if (autoDetected) return autoDetected;
311
311
  const missingProjectMessage = `Could not determine which Neon project to check out a branch from. Provide one via the --project-id flag or a .neon file (created by \`${getCliName()} link\` / \`${getCliName()} set-context\`).`;
312
312
  if (isCi() || !process.stdout.isTTY) throw new Error(missingProjectMessage);
313
- log.error(missingProjectMessage);
313
+ log.info(missingProjectMessage);
314
314
  const { runLink } = await prompts({
315
315
  type: "confirm",
316
316
  name: "runLink",
@@ -3,7 +3,7 @@ import { isCi } from "../env.js";
3
3
  import { log } from "../log.js";
4
4
  import { getApiClient } from "../api.js";
5
5
  import { applyContext, contextBranch, readContextFile } from "../context.js";
6
- import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
6
+ import { t as credentialInputs } from "../_chunks/auth_selection-YX5_KZgD.js";
7
7
  import { ClaimableClient, ClaimableServiceError } from "../claimable/api.js";
8
8
  import { assertionHasExpired, listClaimableCredentials, readClaimableCredentials, readLinkedClaimableCredentials, removeClaimableCredentials, writeClaimableCredentials } from "../claimable/state.js";
9
9
  import { NEON_SERVICES, deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
@@ -460,7 +460,7 @@ const finishClaimedContext = (props, projectId, contextMatches) => {
460
460
  ...contextBranch(context) ? { branch: contextBranch(context) } : {}
461
461
  });
462
462
  }
463
- log.info("Dropped the local identity assertion. The next command needs `neon auth` or `neon link`.");
463
+ log.info("Dropped the local identity assertion. The next command needs `neon login` or `neon link`.");
464
464
  };
465
465
  //#endregion
466
466
  export { CLAIM_CREATE_FIELDS, CLAIM_LIST_FIELDS, aliases, builder, claimCreateFields, claimableCapabilities, command, create, describe, handler, claim_exports as t };
@@ -4,8 +4,8 @@ import { log } from "../log.js";
4
4
  import { getApiClient } from "../api.js";
5
5
  import { contextBranch, ensureDirectoryGitignored, readContextFile } from "../context.js";
6
6
  import { getCliName } from "../utils/cli_name.js";
7
- import { NEON_SERVICE_LABELS, deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
8
- import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES, CONFIG_INIT_UNAVAILABLE, FUNCTION_FILENAME, FUNCTION_SLUG, FUNCTION_TEMPLATE, REQUIRED_PACKAGES, renderFunctionSource, renderNeonConfig, renderNeonConfigFromView } from "../config_template.js";
7
+ import { NEON_SERVICES, NEON_SERVICE_LABELS, deprecatedServiceMessage, servicesFlagValue, servicesOption } from "../neon_services.js";
8
+ import { FUNCTION_FILENAME, FUNCTION_SLUG, FUNCTION_TEMPLATE, REQUIRED_PACKAGES, parseConfigInitServices, renderFunctionSource, renderNeonConfig, renderNeonConfigFromView } from "../config_template.js";
9
9
  import { editNeonConfig } from "../config_edit.js";
10
10
  import { toNeonConfigView } from "../config_format.js";
11
11
  import { declaredNeonServices } from "../config_services.js";
@@ -27,7 +27,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
27
27
  import { dirname, join, relative, resolve } from "node:path";
28
28
  import chalk from "chalk";
29
29
  import { packagesToStage, previewGaWarningForConfig, resolveConfig } from "@neon/config";
30
- import { PushConflictError, apply, assertZipWithinLimits, bundleAsIs, createBranch, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, plan, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle } from "@neon/config-runtime";
30
+ import { PushAbortedError, PushConflictError, apply, assertZipWithinLimits, bundleAsIs, createBranch, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, plan, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle } from "@neon/config-runtime";
31
31
  //#region src/commands/config.ts
32
32
  var config_exports = /* @__PURE__ */ __exportAll({
33
33
  ConfigInstallFailed: () => ConfigInstallFailed,
@@ -88,12 +88,18 @@ const envFlag = { env: {
88
88
  /** Apply-only flags, exported so `deploy` can reuse the exact same surface. */
89
89
  const applyFlags = {
90
90
  "update-existing": {
91
- describe: "Auto-confirm overriding existing remote settings on the branch",
91
+ describe: "Override branch settings that differ from neon.ts without asking",
92
92
  type: "boolean",
93
93
  default: false
94
94
  },
95
95
  "allow-protected": {
96
- describe: "Auto-confirm applying to a branch marked protected on Neon",
96
+ describe: "Apply to a branch marked protected on Neon without asking",
97
+ type: "boolean",
98
+ default: false
99
+ },
100
+ yes: {
101
+ alias: "y",
102
+ describe: "Answer yes to every confirmation: --update-existing and --allow-protected",
97
103
  type: "boolean",
98
104
  default: false
99
105
  }
@@ -152,13 +158,7 @@ const missingDependencies = (cwd) => {
152
158
  * starter policy, which is what `config init` has always written.
153
159
  */
154
160
  const resolveServices = async (props) => {
155
- if (props.services !== void 0) return parseServices(props.services, {
156
- allowed: CONFIG_INIT_SERVICES,
157
- whyUnavailable: CONFIG_INIT_UNAVAILABLE,
158
- flag: "--services",
159
- noneMeans: CONFIG_INIT_NONE_MEANS,
160
- onDeprecated: (used, canonical) => log.warning(deprecatedServiceMessage(used, canonical))
161
- });
161
+ if (props.services !== void 0) return parseConfigInitServices(props.services, (used, canonical) => log.warning(deprecatedServiceMessage(used, canonical)));
162
162
  if (props.pickServices) return props.pickServices();
163
163
  if (isCi() || !process.stdout.isTTY) return [];
164
164
  return pickServicesInteractively();
@@ -475,10 +475,9 @@ const builder = (argv) => argv.usage("$0 config <sub-command> [options]").option
475
475
  },
476
476
  services: servicesOption({
477
477
  key: "services",
478
- allowed: CONFIG_INIT_SERVICES,
479
- noneMeans: CONFIG_INIT_NONE_MEANS,
478
+ allowed: NEON_SERVICES,
480
479
  describe: "Services the scaffolded neon.ts declares",
481
- also: "Omitted: pick interactively on a terminal, starter policy in CI or without a TTY."
480
+ also: "postgres alone writes the starter policy; every branch has Postgres. Omitted: pick interactively on a terminal, starter policy in CI or without a TTY."
482
481
  }),
483
482
  "from-branch": {
484
483
  describe: "Seed neon.ts from a branch's live Neon state instead of asking. Uses the branch pinned in .neon, or --branch <name|id>, or the project's default branch. The only mode of `config init` that calls the Neon API.",
@@ -760,14 +759,8 @@ const applyCmd = async (props) => {
760
759
  ...branch.parentId ? { parentId: branch.parentId } : {},
761
760
  ...branch.expiresAt ? { expiresAt: branch.expiresAt } : {}
762
761
  };
763
- await runDeployBeforeHook({
764
- hooks,
765
- branch: hookBranch,
766
- git,
767
- event,
768
- cwd
769
- });
770
762
  let result;
763
+ let declined = false;
771
764
  try {
772
765
  result = await apply(config, {
773
766
  projectId: props.projectId,
@@ -775,15 +768,29 @@ const applyCmd = async (props) => {
775
768
  ...props.apiKey ? { apiKey: props.apiKey } : {},
776
769
  ...props.apiHost ? { apiHost: props.apiHost } : {},
777
770
  ...props.runtimeApi ? { api: props.runtimeApi } : {},
778
- ...props.updateExisting ? { updateExisting: true } : {},
779
- ...props.allowProtected ? { allowProtectedBranch: true } : {},
771
+ ...props.updateExisting || props.yes ? { updateExisting: true } : {},
772
+ ...props.allowProtected || props.yes ? { allowProtectedBranch: true } : {},
773
+ confirm: async (context) => {
774
+ if (context.overrides.length > 0) reportConflicts(props, context.overrides);
775
+ if (!canPromptForApply(props)) return false;
776
+ const proceed = await askToApply(context);
777
+ declined = !proceed;
778
+ return proceed;
779
+ },
780
+ beforeMutations: () => runDeployBeforeHook({
781
+ hooks,
782
+ branch: hookBranch,
783
+ git,
784
+ event,
785
+ cwd
786
+ }),
780
787
  bundleFunction: neonctlBundler
781
788
  });
782
789
  } catch (err) {
790
+ if (err instanceof PushAbortedError) throw new Error(declined ? `Aborted: nothing was applied to branch "${err.branchName}".` : abortedApplyMessage(err));
783
791
  if (err instanceof PushConflictError) {
784
792
  reportConflicts(props, err.conflicts);
785
- const overrideable = err.conflicts.every((c) => reasonAllowsUpdateExisting(c.reason));
786
- throw new Error(overrideable ? "Branch settings conflict with the policy. Re-run with --update-existing to apply the changes shown above." : "The policy conflicts with remote state. --update-existing will not resolve every conflict shown above.");
793
+ throw new Error("The policy conflicts with remote state. --update-existing will not resolve every conflict shown above.");
787
794
  }
788
795
  throw err;
789
796
  }
@@ -870,6 +877,29 @@ const reportPushResult = (props, result, mode, services) => {
870
877
  if (noChanges) log.info(`No changes — branch ${result.branchName} already matches the policy.`);
871
878
  out.text(`\nUtilized services: ${services.join(", ")}\n`);
872
879
  };
880
+ /** A prompt needs both ends of a terminal, and would corrupt `-o json|yaml` on stdout. */
881
+ const canPromptForApply = (props) => !isCi() && Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY) && props.output !== "json" && props.output !== "yaml";
882
+ const askToApply = async (context) => {
883
+ const branch = JSON.stringify(context.branchName);
884
+ const message = context.protectedBranch && context.overrideUpdates ? `Branch ${branch} is protected, and this overrides the settings shown above. Apply anyway?` : context.protectedBranch ? `Branch ${branch} is protected. Apply to it anyway?` : `Override the settings shown above on branch ${branch}?`;
885
+ const { default: prompts } = await import("prompts");
886
+ const { proceed } = await prompts({
887
+ type: "confirm",
888
+ name: "proceed",
889
+ message,
890
+ initial: false
891
+ });
892
+ return proceed === true;
893
+ };
894
+ /** Without a terminal there is no one to ask, so name the flags that answer the question. */
895
+ const abortedApplyMessage = (err) => {
896
+ const branch = JSON.stringify(err.branchName);
897
+ const isProtected = err.reasons.includes("protected-branch");
898
+ const overrides = err.reasons.includes("override-updates");
899
+ if (isProtected && overrides) return `Branch ${branch} is protected and its settings differ from the policy. Re-run with -y (or --allow-protected --update-existing) to apply the changes shown above.`;
900
+ if (isProtected) return `Branch ${branch} is protected. Re-run with --allow-protected (or -y) to apply to it.`;
901
+ return "Branch settings conflict with the policy. Re-run with --update-existing (or -y) to apply the changes shown above.";
902
+ };
873
903
  /**
874
904
  * Render the branch-setting {@link ConflictReport}s a bare `apply` refused to override (drift
875
905
  * without `--update-existing`) as the git-style before→after diff. JSON/YAML output emits the
@@ -550,7 +550,7 @@ const reconcileOnce = async (running, replan, ops) => {
550
550
  try {
551
551
  desired = await replan(nextSearchBase(running), keepPortsFromRunning(running));
552
552
  } catch (err) {
553
- log.info(chalk.red("neon.ts change ignored: ") + (err instanceof Error ? err.message : String(err)) + chalk.dim(" (fix it and save again)"));
553
+ log.warning("neon.ts change ignored: " + (err instanceof Error ? err.message : String(err)) + chalk.dim(" (fix it and save again)"));
554
554
  return;
555
555
  }
556
556
  if (ops.isShuttingDown()) return;
@@ -110,7 +110,7 @@ const uninstall = (_props) => {
110
110
  log.info("Removed the neon git post-checkout hook. Git → Neon sync is off.");
111
111
  break;
112
112
  case "foreign":
113
- log.warning("Left the existing `post-checkout` hook in place (not managed by neon). Git → Neon sync flag cleared.");
113
+ log.info("Left the existing `post-checkout` hook in place (not managed by neon). Git → Neon sync flag cleared.");
114
114
  break;
115
115
  case "absent": log.info("No neon git hook was installed. Git → Neon sync flag cleared.");
116
116
  }
@@ -295,7 +295,7 @@ const cleanup = async (props) => {
295
295
  const orphanNeonNames = new Set(orphaned.map(([, neonBranch]) => neonBranch));
296
296
  const branches = (await props.apiClient.listProjectBranches({ projectId: props.projectId })).data.branches;
297
297
  const { toDelete, skipped } = partitionBranchesToPrune(branches, orphanNeonNames);
298
- for (const { name, reason } of skipped) log.warning("Keeping Neon branch %s (%s) — never auto-deleted.", name, reason);
298
+ for (const { name, reason } of skipped) log.info("Keeping Neon branch %s (%s) — never auto-deleted.", name, reason);
299
299
  if (toDelete.length === 0) {
300
300
  log.info("No orphaned Neon branches to delete.");
301
301
  return;
@@ -1,8 +1,7 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { getCliName } from "../utils/cli_name.js";
3
3
  import { helpCsv, helpEpilogue } from "../utils/help_text.js";
4
- import { servicesOption } from "../neon_services.js";
5
- import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES } from "../config_template.js";
4
+ import { NEON_SERVICES, servicesOption } from "../neon_services.js";
6
5
  import { coerceAgentFlag } from "../utils/agent_flag.js";
7
6
  import { initPluginAgents, initSkillsMcpAgents } from "../init/plan.js";
8
7
  import { listSkillIds } from "../skills/catalog.js";
@@ -52,10 +51,9 @@ const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file
52
51
  describe: "Create neon.ts after linking. Use --no-config to skip. Omitted in Custom: you will be asked"
53
52
  }).option("services", servicesOption({
54
53
  key: "services",
55
- allowed: CONFIG_INIT_SERVICES,
54
+ allowed: NEON_SERVICES,
56
55
  describe: "Services to declare in neon.ts",
57
- noneMeans: CONFIG_INIT_NONE_MEANS,
58
- also: "Implies creating neon.ts. Cannot be combined with --no-config."
56
+ also: "postgres alone writes the starter policy; every branch has Postgres. Implies creating neon.ts. Cannot be combined with --no-config."
59
57
  })).option("org-id", {
60
58
  describe: "Forwarded to link: organization ID to link to",
61
59
  type: "string"
@@ -11,8 +11,8 @@ import { Parser } from "yargs/helpers";
11
11
  */
12
12
  const commandManifest = [
13
13
  {
14
- command: "auth",
15
- aliases: ["login"],
14
+ command: "login",
15
+ aliases: ["auth"],
16
16
  describe: "Sign in with a browser. See --help for API keys and profiles",
17
17
  load: () => import("./auth.js")
18
18
  },
@@ -139,7 +139,7 @@ const setupNeonMcp = async (options) => {
139
139
  cwd
140
140
  }) : void 0;
141
141
  if (plan.auth === "api-key" && !existing) {
142
- if (!options.apiClient || !options.apiKey) throw new Error(`Authentication required. Run \`${getCliName()} auth\`, pass --api-key or use --oauth to install without a Neon credential.`);
142
+ if (!options.apiClient || !options.apiKey) throw new Error(`Authentication required. Run \`${getCliName()} login\`, pass --api-key or use --oauth to install without a Neon credential.`);
143
143
  if (!canPickAgentsInteractively() && options.yes !== true && (options.agent ?? []).length === 0) throw new Error("No interactive terminal. Pass -y to mint into every detected agent, --agent <name> to name them or --oauth to install without minting.");
144
144
  }
145
145
  let auth;
@@ -1,14 +1,14 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { t as writeSecretFile } from "../_chunks/secure_file-BucZj4yQ.js";
3
- import { c as describeScope, d as scopeOf, l as interpretCredentials, n as KEYRING_SERVICE, o as apiKeyCredentials, r as keyringAccount, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-D8AR-jvB.js";
3
+ import { c as describeScope, d as scopeOf, l as interpretCredentials, n as KEYRING_SERVICE, o as apiKeyCredentials, r as keyringAccount, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-ByNuojoE.js";
4
4
  import { isCi } from "../env.js";
5
5
  import { log } from "../log.js";
6
6
  import { getApiClient, isNeonApiError } from "../api.js";
7
7
  import { setAuthContext } from "../auth_context.js";
8
8
  import { a as isOwnedCredentialPath, n as credentialsPath } from "../_chunks/paths-DMq0Lt7a.js";
9
- import { _ as upsertProfile, a as canDropProfilesFile, c as listProfiles, d as newProfileLocation, f as profilesFilePath, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, o as credentialsDisplay, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, u as locationOf, v as CRED_STORAGE_FILE, y as CRED_STORAGE_KEYRING } from "../_chunks/profiles-CvnFEQyd.js";
9
+ import { _ as upsertProfile, a as canDropProfilesFile, c as listProfiles, d as newProfileLocation, f as profilesFilePath, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, o as credentialsDisplay, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, u as locationOf, v as CRED_STORAGE_FILE, y as CRED_STORAGE_KEYRING } from "../_chunks/profiles-DIB1av2Q.js";
10
10
  import { auth, revokeToken } from "../auth.js";
11
- import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
11
+ import { t as credentialInputs } from "../_chunks/auth_selection-YX5_KZgD.js";
12
12
  import { readOutgoingCredential, retirePreviousCredential, revokeTokenSet, withdrawKey } from "../retire_credential.js";
13
13
  import { authFlow, usableCredential } from "./auth.js";
14
14
  import { identityFromAuthDetails, isApiKeyMethod, mintedKeyName, notAnApiKeyMessage } from "../profile_keys.js";
@@ -70,7 +70,7 @@ const builder = (argv) => argv.usage("$0 profile <sub-command> [options]").comma
70
70
  throw new Error(`--force is no longer a flag. \`neon profile create ${name}\` always replaces an existing profile and revokes the credential it held. Drop --force.`);
71
71
  }
72
72
  return true;
73
- }).example("$0 profile create work", "Sign in with the browser, like `neon auth --profile work`").example("$0 profile create work --api-key \"$KEY\"", "Store a key you already have").example("echo \"$KEY\" | $0 profile create work --api-key -", "Or pipe it, so it never reaches the process arguments").example("$0 profile create ci --mint --org-id org-abc-123", "One browser sign-in, then an org-scoped key and no session left behind").example("$0 profile create work --keyring", "Store the credential in the OS keyring instead of a file"), async (args) => await create(args)).command("rotate-key <name>", "Mint a fresh API key for a profile, at the same scope, and revoke the one it replaces", (y) => y.positional("name", {
73
+ }).example("$0 profile create work", "Sign in with the browser, like `neon login --profile work`").example("$0 profile create work --api-key \"$KEY\"", "Store a key you already have").example("echo \"$KEY\" | $0 profile create work --api-key -", "Or pipe it, so it never reaches the process arguments").example("$0 profile create ci --mint --org-id org-abc-123", "One browser sign-in, then an org-scoped key and no session left behind").example("$0 profile create work --keyring", "Store the credential in the OS keyring instead of a file"), async (args) => await create(args)).command("rotate-key <name>", "Mint a fresh API key for a profile, at the same scope, and revoke the one it replaces", (y) => y.positional("name", {
74
74
  describe: "Profile to rotate",
75
75
  type: "string",
76
76
  demandOption: true
@@ -466,7 +466,10 @@ const createByMinting = async (props) => {
466
466
  });
467
467
  warnAboutReach(scope);
468
468
  } finally {
469
- if (minted !== void 0 && !keyIsReachable) log.info(await withdrawKey(session, mintedScope, minted.id) ? `Revoked the key that was minted but not stored (id ${minted.id}).` : `Minted key ${minted.id} could be neither stored nor revoked, and may still be live. Remove it with: neon api-keys revoke ${minted.id}${mintedScope.orgId ? ` --org-id ${mintedScope.orgId}` : ""}`);
469
+ if (minted !== void 0 && !keyIsReachable) {
470
+ if (await withdrawKey(session, mintedScope, minted.id)) log.info(`Revoked the key that was minted but not stored (id ${minted.id}).`);
471
+ else log.warning(`Minted key ${minted.id} could be neither stored nor revoked, and may still be live. Remove it with: neon api-keys revoke ${minted.id}${mintedScope.orgId ? ` --org-id ${mintedScope.orgId}` : ""}`);
472
+ }
470
473
  const revoked = await revokeToken(oauthProps, tokenSet);
471
474
  log.info(revoked ? "Signed the browser session back out, so nothing but the key remains." : "Could not sign the browser session back out; it will expire on its own.");
472
475
  }
@@ -604,7 +607,8 @@ const remove = async (props) => {
604
607
  apiKey: stored.apiKey,
605
608
  apiHost: props.apiHost
606
609
  });
607
- log.info(await withdrawKey(client, scope, keyId) ? `Revoked the API key (id ${keyId})` : `Could not revoke the API key (id ${keyId}) — it may still be live. Remove it with: neon api-keys revoke ${keyId}${scope.orgId ? ` --org-id ${scope.orgId}` : ""}`);
610
+ if (await withdrawKey(client, scope, keyId)) log.info(`Revoked the API key (id ${keyId})`);
611
+ else log.warning(`Could not revoke the API key (id ${keyId}) — it may still be live. Remove it with: neon api-keys revoke ${keyId}${scope.orgId ? ` --org-id ${scope.orgId}` : ""}`);
608
612
  }
609
613
  } else if (stored !== null) {
610
614
  const revoked = await revokeTokenSet(stored.tokens, props);
@@ -17,6 +17,7 @@ const handler = async (args) => {
17
17
  await me(args);
18
18
  };
19
19
  const me = async (props) => {
20
+ if (!props.apiClient) return;
20
21
  const { data } = await props.apiClient.getCurrentUserInfo();
21
22
  if (props.output === "json" || props.output === "yaml") {
22
23
  writer(props).end(data, { fields: [
@@ -1,4 +1,4 @@
1
- import { NEON_SERVICES } from "./neon_services.js";
1
+ import { NEON_SERVICES, parseServices } from "./neon_services.js";
2
2
  //#region src/config_template.ts
3
3
  /**
4
4
  * The published npm packages a `neon.ts` project needs — the `@neon/*` org names.
@@ -23,13 +23,15 @@ const REQUIRED_PACKAGES = [CONFIG_PACKAGE, ENV_PACKAGE];
23
23
  */
24
24
  const CONFIG_INIT_SERVICES = NEON_SERVICES.filter((service) => service !== "postgres");
25
25
  /**
26
- * What `config init --services none` produces. One constant because it is both the help text
27
- * and what tells the parser `none` is a value here — passing the literal at each call site
28
- * lets the two drift into documenting something the parser does not accept.
26
+ * Parse `--services` on `init` and `config init` into the services to declare. `postgres` is
27
+ * accepted, alone or with others, and declares nothing: `--services postgres` is how a
28
+ * script asks for the Postgres-only starter policy.
29
29
  */
30
- const CONFIG_INIT_NONE_MEANS = "the bare starter policy";
31
- /** Why a policy cannot declare a real Neon service, for the refusal message. */
32
- const CONFIG_INIT_UNAVAILABLE = { postgres: "every branch has Postgres, so a policy has nothing to declare for it" };
30
+ const parseConfigInitServices = (raw, onDeprecated) => parseServices(raw, {
31
+ allowed: NEON_SERVICES,
32
+ flag: "--services",
33
+ ...onDeprecated ? { onDeprecated } : {}
34
+ }).filter((service) => service !== "postgres");
33
35
  /** Slug, display name, and source path of the function scaffolded for `functions`. */
34
36
  const FUNCTION_SLUG = "hello";
35
37
  const FUNCTION_NAME = "Hello World";
@@ -200,4 +202,4 @@ const renderFunctionSource = (slug, language) => {
200
202
  };
201
203
  const FUNCTION_TEMPLATE = renderFunctionSource(FUNCTION_SLUG, "ts");
202
204
  //#endregion
203
- export { BUCKET_NAME, CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES, CONFIG_INIT_UNAVAILABLE, CONFIG_PACKAGE, ENV_PACKAGE, FUNCTION_FILENAME, FUNCTION_NAME, FUNCTION_SLUG, FUNCTION_TEMPLATE, REQUIRED_PACKAGES, renderFunctionSource, renderKey, renderNeonConfig, renderNeonConfigFromView };
205
+ export { BUCKET_NAME, CONFIG_INIT_SERVICES, CONFIG_PACKAGE, ENV_PACKAGE, FUNCTION_FILENAME, FUNCTION_NAME, FUNCTION_SLUG, FUNCTION_TEMPLATE, REQUIRED_PACKAGES, parseConfigInitServices, renderFunctionSource, renderKey, renderNeonConfig, renderNeonConfigFromView };
@@ -1,2 +1,2 @@
1
- import { t as storeFor } from "./_chunks/credential_io-D8AR-jvB.js";
1
+ import { t as storeFor } from "./_chunks/credential_io-ByNuojoE.js";
2
2
  export { storeFor };
package/dist/index.js CHANGED
@@ -154,7 +154,7 @@ async function handleError(msg, err, canRetry) {
154
154
  log.error(err.message);
155
155
  if (err.terminal) {
156
156
  const context = getAuthContext();
157
- log.error(`Run \`neon auth --profile ${context?.profile ?? "DEFAULT"}\` to sign in again.`);
157
+ log.error(`Run \`neon login --profile ${context?.profile ?? "DEFAULT"}\` to sign in again.`);
158
158
  }
159
159
  sendError(err, "AUTH_FAILED");
160
160
  return false;
@@ -10,7 +10,7 @@ const resolveInitConfigChoice = (input) => {
10
10
  };
11
11
  if (input.yes) return {
12
12
  kind: "write",
13
- services: ["none"]
13
+ services: ["postgres"]
14
14
  };
15
15
  if (input.flag === true) return { kind: "write" };
16
16
  if (input.canAsk) return { kind: "ask" };
@@ -21,10 +21,6 @@ const configPlanFromResolution = (resolution, accepted) => {
21
21
  if (resolution.kind === "skip") return { kind: "skip" };
22
22
  return resolution;
23
23
  };
24
- const isBareInitServices = (services) => {
25
- if (services === void 0 || services.length === 0) return true;
26
- return services.every((service) => service === "none");
27
- };
28
24
  const shouldRefreshEnvAfterNewConfig = (input) => input.wroteNewFile && typeof input.projectId === "string" && input.projectId.length > 0 && typeof input.branch === "string" && input.branch.length > 0;
29
25
  const shouldPullEnvAfterInitConfig = (input) => !input.extraServices && !input.alreadyPulled && shouldRefreshEnvAfterNewConfig({
30
26
  wroteNewFile: input.wroteNewFile,
@@ -32,4 +28,4 @@ const shouldPullEnvAfterInitConfig = (input) => !input.extraServices && !input.a
32
28
  ...input.branch !== void 0 ? { branch: input.branch } : {}
33
29
  });
34
30
  //#endregion
35
- export { INIT_CONFIG_SERVICES_CONFLICT, configPlanFromResolution, isBareInitServices, resolveInitConfigChoice, shouldPullEnvAfterInitConfig, shouldRefreshEnvAfterNewConfig };
31
+ export { INIT_CONFIG_SERVICES_CONFLICT, configPlanFromResolution, resolveInitConfigChoice, shouldPullEnvAfterInitConfig, shouldRefreshEnvAfterNewConfig };
package/dist/init/copy.js CHANGED
@@ -49,8 +49,8 @@ const SERVICES_MESSAGE = "Which services should neon.ts declare? (space to toggl
49
49
  const PACKAGE_MANAGER_MESSAGE = "Which package manager should install the Neon dependencies?";
50
50
  const NO_AGENTS_FALLBACK_STATUS = "No coding agents detected. Installing the default Neon skills for Cursor and Codex in this directory.";
51
51
  const NO_AGENT_SETUP_CONFLICT = "--no-agent-setup cannot be combined with --agent, --skill, or MCP flags.";
52
- const NON_TTY_LINK_NEEDS_AUTH = "No interactive terminal. Sign in with `neon auth`, then re-run, or pass --claimable.";
53
- const YES_LINK_NEEDS_AUTH = "-y cannot sign in. Sign in with `neon auth`, then re-run, or pass --claimable.";
52
+ const NON_TTY_LINK_NEEDS_AUTH = "No interactive terminal. Sign in with `neon login`, then re-run, or pass --claimable.";
53
+ const YES_LINK_NEEDS_AUTH = "-y cannot sign in. Sign in with `neon login`, then re-run, or pass --claimable.";
54
54
  const MCP_SCOPED_NEEDS_PROJECT = "--mcp-project-scoped requires a linked project. Link a project first, or omit --mcp-project-scoped.";
55
55
  const namedAgentsUnavailable = (ids) => `--agent ${ids.join(", ")} cannot install this tooling. Pass agents that support it, or omit --agent.`;
56
56
  const mcpConfigLocationUnavailable = (ids, location) => location === "project" ? `--agent ${ids.join(", ")} cannot install project-level MCP config. Use --mcp-config-location global, or choose an agent that supports project config.` : `--agent ${ids.join(", ")} cannot install global MCP config. Choose an agent that supports global config.`;
@@ -80,7 +80,7 @@ const PROGRESS = {
80
80
  const unattendedUnauthedNext = () => [
81
81
  "Link a project with a Neon account and an authenticated CLI.",
82
82
  "Sign up: https://neon.com/signup",
83
- `${getCliName()} auth`,
83
+ `${getCliName()} login`,
84
84
  `${getCliName()} link`,
85
85
  "",
86
86
  "Or create a claimable project without an account. It expires in 72 hours unless claimed.",
@@ -1,7 +1,7 @@
1
- import { t as storeFor } from "../_chunks/credential_io-D8AR-jvB.js";
1
+ import { t as storeFor } from "../_chunks/credential_io-ByNuojoE.js";
2
2
  import { isCi } from "../env.js";
3
- import { h as resolveProfile, u as locationOf } from "../_chunks/profiles-CvnFEQyd.js";
4
- import { i as selectCredential, t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
3
+ import { h as resolveProfile, u as locationOf } from "../_chunks/profiles-DIB1av2Q.js";
4
+ import { i as selectCredential, t as credentialInputs } from "../_chunks/auth_selection-YX5_KZgD.js";
5
5
  import { detectInstalledAgents, uniqueAgentIds } from "../mcp/agents.js";
6
6
  import "./agents.js";
7
7
  import { directoryIsEmpty } from "./plan.js";
package/dist/init/plan.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { getCliName } from "../utils/cli_name.js";
2
- import { NO_SERVICES } from "../neon_services.js";
3
2
  import { tryResolveAddMcpAgentId, uniqueAgentIds } from "../mcp/agents.js";
4
3
  import { mcpInstallableAgents } from "../mcp/targets.js";
5
4
  import { supportsSkills } from "./agents.js";
@@ -363,13 +362,13 @@ const planConfigInitStep = (input) => {
363
362
  "config",
364
363
  "init",
365
364
  "--services",
366
- input.services.length === 0 ? NO_SERVICES : input.services.join(",")
365
+ input.services.length === 0 ? "postgres" : input.services.join(",")
367
366
  ];
368
367
  if (input.yes) return [
369
368
  "config",
370
369
  "init",
371
370
  "--services",
372
- NO_SERVICES
371
+ "postgres"
373
372
  ];
374
373
  return ["config", "init"];
375
374
  };
package/dist/init/run.js CHANGED
@@ -6,8 +6,8 @@ import { recordCommandSuccessExtras, takeCommandSuccessExtras } from "../analyti
6
6
  import "../claimable/api.js";
7
7
  import { readLinkedClaimableCredentials } from "../claimable/state.js";
8
8
  import { getCliName } from "../utils/cli_name.js";
9
- import { deprecatedServiceMessage, parseServices, servicesFlagValue } from "../neon_services.js";
10
- import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES, CONFIG_INIT_UNAVAILABLE } from "../config_template.js";
9
+ import { servicesFlagValue } from "../neon_services.js";
10
+ import { CONFIG_INIT_SERVICES, parseConfigInitServices } from "../config_template.js";
11
11
  import { hasNeonConfigFile, neonConfigFilename } from "../project.js";
12
12
  import { getAgentDisplayName } from "../mcp/agents.js";
13
13
  import { mcpInstallableAgents } from "../mcp/targets.js";
@@ -24,7 +24,7 @@ import { InitCancelled, raceSigint, restoreCursor } from "./cancelled.js";
24
24
  import { pickAgentSetupInteractively, pickInitAgentsInteractively, pickInitConfigInteractively, pickInitMcpAuthInteractively, pickInitMcpConfigLocationInteractively, pickInitModeInteractively, pickInitPackageManagerInteractively, pickInitProjectSetupInteractively, pickInitServicesInteractively, pickInitSkillsInteractively } from "./wizard.js";
25
25
  import { runToolingSteps } from "./tooling.js";
26
26
  import { create } from "../commands/claim.js";
27
- import { INIT_CONFIG_SERVICES_CONFLICT, configPlanFromResolution, isBareInitServices, resolveInitConfigChoice, shouldPullEnvAfterInitConfig } from "./choices.js";
27
+ import { INIT_CONFIG_SERVICES_CONFLICT, configPlanFromResolution, resolveInitConfigChoice, shouldPullEnvAfterInitConfig } from "./choices.js";
28
28
  import { detectInitEnvironment } from "./detect.js";
29
29
  import { createInitFunnel, emitInitStart, finishInitFunnel, initStartProperties } from "./funnel.js";
30
30
  import { assertAgentSetupFlags, hasInitMcpFlags, inferInitAgentSetup, resolveInitMode } from "./mode.js";
@@ -34,17 +34,6 @@ const isLinked = (contextFile) => {
34
34
  const projectId = readContextFile(contextFile).projectId;
35
35
  return typeof projectId === "string" && projectId.length > 0;
36
36
  };
37
- const parsedInitServices = (raw) => {
38
- const values = servicesFlagValue(raw);
39
- if (values === void 0) return;
40
- return parseServices(values, {
41
- allowed: CONFIG_INIT_SERVICES,
42
- whyUnavailable: CONFIG_INIT_UNAVAILABLE,
43
- flag: "--services",
44
- noneMeans: CONFIG_INIT_NONE_MEANS,
45
- onDeprecated: (used, canonical) => log.warning(deprecatedServiceMessage(used, canonical))
46
- });
47
- };
48
37
  const expandTelemetryServices = (services) => {
49
38
  if (services.includes("data-api") && !services.includes("auth")) return CONFIG_INIT_SERVICES.filter((service) => service === "auth" || services.includes(service));
50
39
  return [...services];
@@ -172,8 +161,9 @@ const runInit = async (props) => {
172
161
  ...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
173
162
  ...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {}
174
163
  };
175
- const servicesFlag = parsedInitServices(props.services);
164
+ const servicesFlag = servicesFlagValue(props.services);
176
165
  if (props.config === false && servicesFlag !== void 0) throw new Error(INIT_CONFIG_SERVICES_CONFLICT);
166
+ if (servicesFlag !== void 0) parseConfigInitServices(servicesFlag);
177
167
  const linkInputs = {
178
168
  ...props.orgId ? { orgId: props.orgId } : {},
179
169
  ...props.projectId ? { projectId: props.projectId } : {},
@@ -459,25 +449,22 @@ const runInit = async (props) => {
459
449
  const picked = await (props.pickServices ?? pickInitServicesInteractively)();
460
450
  configPlan = {
461
451
  kind: "write",
462
- services: picked.length === 0 ? ["none"] : picked
452
+ services: picked.length === 0 ? ["postgres"] : picked
463
453
  };
464
454
  }
465
455
  if (recommended && configPlan.kind === "write" && !existingConfig) configPlan = {
466
456
  kind: "write",
467
- services: servicesFlag ?? ["none"]
457
+ services: servicesFlag ?? ["postgres"]
468
458
  };
469
459
  let wroteNewFile = false;
470
460
  let extraServices = false;
471
461
  let selectedServices = null;
472
462
  if (configPlan.kind === "write") {
473
- const planned = existingConfig ? void 0 : configPlan.services ?? ["none"];
463
+ const planned = existingConfig ? void 0 : configPlan.services ?? ["postgres"];
474
464
  if (planned !== void 0) {
475
- extraServices = !isBareInitServices(planned);
476
- selectedServices = extraServices ? expandTelemetryServices(parseServices(planned, {
477
- allowed: CONFIG_INIT_SERVICES,
478
- flag: "--services",
479
- noneMeans: CONFIG_INIT_NONE_MEANS
480
- })) : [];
465
+ const declared = parseConfigInitServices(planned);
466
+ extraServices = declared.length > 0;
467
+ selectedServices = expandTelemetryServices(declared);
481
468
  funnel.services = selectedServices;
482
469
  wroteNewFile = true;
483
470
  } else selectedServices = null;
package/dist/log.js CHANGED
@@ -9,7 +9,7 @@ const log = {
9
9
  process.stderr.write(`WARNING: ${format(...args)}\n`);
10
10
  },
11
11
  info: (...args) => {
12
- process.stderr.write(`INFO: ${format(...args)}\n`);
12
+ process.stderr.write(`${format(...args)}\n`);
13
13
  },
14
14
  error: (...args) => {
15
15
  process.stderr.write(`ERROR: ${format(...args)}\n`);
package/dist/mcp/mint.js CHANGED
@@ -4,7 +4,7 @@ import { getCliName } from "../utils/cli_name.js";
4
4
  import { mintedKeyName } from "../profile_keys.js";
5
5
  import { orgIdForProject } from "../commands/api_keys.js";
6
6
  //#region src/mcp/mint.ts
7
- const cannotMintMessage = "This CLI credential cannot mint API keys. Organization and project-scoped keys cannot create other keys. Sign in with `neon auth` or pass a personal API key.";
7
+ const cannotMintMessage = "This CLI credential cannot mint API keys. Organization and project-scoped keys cannot create other keys. Sign in with `neon login` or pass a personal API key.";
8
8
  function mintedKeyRevokeCommand(key) {
9
9
  return key.orgId ? `${getCliName()} api-keys revoke ${key.id} --org-id ${key.orgId}` : `${getCliName()} api-keys revoke ${key.id}`;
10
10
  }
@@ -14,7 +14,7 @@ const withdraw = async (client, keyId, orgId) => {
14
14
  const { data } = orgId ? await client.revokeOrgApiKey(orgId, keyId) : await client.revokeApiKey(keyId);
15
15
  return data.revoked === true && data.id === keyId;
16
16
  } catch (err) {
17
- log.error("Failed to revoke API key %d: %s", keyId, err instanceof Error ? err.message : String(err));
17
+ log.warning("Failed to revoke API key %d: %s", keyId, err instanceof Error ? err.message : String(err));
18
18
  return false;
19
19
  }
20
20
  };
@@ -40,8 +40,6 @@ const NEON_SERVICE_LABELS = {
40
40
  * vocabulary, and absent from help text, errors, and docs so nobody learns it fresh.
41
41
  */
42
42
  const DEPRECATED_SERVICE_ALIASES = { storage: "object-storage" };
43
- /** An explicit empty selection, for commands where "declare nothing" is a real answer. */
44
- const NO_SERVICES = "none";
45
43
  /**
46
44
  * What to tell someone still using a retired spelling. A message rather than a log call, so
47
45
  * the parser stays free of the CLI's writer and each command can surface it in its own voice.
@@ -57,18 +55,14 @@ const deprecatedServiceMessage = (used, canonical) => `"${used}" is the old name
57
55
  *
58
56
  * An unrecognized name is rejected rather than dropped: a typo would otherwise act on
59
57
  * everything *except* the service that was asked for, and report success. A name that is a
60
- * real service but not one this command supports says so specifically — "postgres is on
61
- * every branch" is a different problem from a typo, and has a different fix.
58
+ * real service but not one this command supports says so, since that is a different problem
59
+ * from a typo.
62
60
  */
63
61
  const parseServices = (raw, options) => {
64
- const { allowed, flag, noneMeans, whyUnavailable = {}, onDeprecated } = options;
65
- const supported = `Supported values: ${allowed.join(", ")}${noneMeans !== void 0 ? `, ${NO_SERVICES}` : ""}.`;
62
+ const { allowed, flag, onDeprecated } = options;
63
+ const supported = `Supported values: ${allowed.join(", ")}.`;
66
64
  const names = raw.flatMap((value) => value.split(",")).map((name) => name.trim()).filter((name) => name !== "");
67
65
  if (names.length === 0) throw new Error(`${flag} needs at least one service. ${supported}`);
68
- if (noneMeans !== void 0 && names.includes("none")) {
69
- if (new Set(names).size > 1) throw new Error(`${flag} ${NO_SERVICES} cannot be combined with other services.`);
70
- return [];
71
- }
72
66
  const deprecated = /* @__PURE__ */ new Map();
73
67
  const resolved = names.map((name) => {
74
68
  const canonical = DEPRECATED_SERVICE_ALIASES[name];
@@ -77,23 +71,19 @@ const parseServices = (raw, options) => {
77
71
  return canonical;
78
72
  });
79
73
  const unsupported = resolved.filter((name) => !allowed.some((service) => service === name));
80
- if (unsupported.length > 0) throw new Error(`${unsupportedMessage(unsupported, flag, whyUnavailable)} ${supported}`);
74
+ if (unsupported.length > 0) throw new Error(`${unsupportedMessage(unsupported, flag)} ${supported}`);
81
75
  for (const [used, canonical] of deprecated) onDeprecated?.(used, canonical);
82
76
  return NEON_SERVICES.filter((service) => allowed.includes(service) && resolved.includes(service));
83
77
  };
84
78
  /**
85
79
  * The sentences explaining why a selection was refused. A real Neon service this command
86
80
  * cannot act on is a different mistake from a typo — different cause, different fix — so the
87
- * two are never answered with the same word, and each service carries its reason where the
88
- * command supplied one.
81
+ * two are never answered with the same word.
89
82
  */
90
- const unsupportedMessage = (unsupported, flag, whyUnavailable) => {
83
+ const unsupportedMessage = (unsupported, flag) => {
91
84
  const known = unsupported.filter((name) => NEON_SERVICES.some((service) => service === name));
92
85
  const unknown = unsupported.filter((name) => !known.some((service) => service === name));
93
- return [unknown.length > 0 ? `Unknown service${unknown.length === 1 ? "" : "s"} ${unknown.join(", ")}.` : void 0, ...known.map((service) => {
94
- const why = whyUnavailable[service];
95
- return `${service} is not something ${flag} can select${why ? `: ${why}` : ""}.`;
96
- })].filter((part) => part !== void 0).join(" ");
86
+ return [unknown.length > 0 ? `Unknown service${unknown.length === 1 ? "" : "s"} ${unknown.join(", ")}.` : void 0, ...known.map((service) => `${service} is not something ${flag} can select.`)].filter((part) => part !== void 0).join(" ");
97
87
  };
98
88
  /** Every spelling of the services flag, so a habit picked up on one command works on another. */
99
89
  const SERVICE_FLAG_NAMES = [
@@ -110,7 +100,6 @@ const servicesOption = (params) => ({
110
100
  alias: SERVICE_FLAG_NAMES.filter((name) => name !== params.key),
111
101
  describe: [
112
102
  `${params.describe}: ${params.allowed.join(", ")}.`,
113
- params.noneMeans !== void 0 ? `Pass "${NO_SERVICES}" for ${params.noneMeans}.` : void 0,
114
103
  "Repeat the flag or comma-separate.",
115
104
  params.also
116
105
  ].filter((part) => part !== void 0).join(" "),
@@ -124,4 +113,4 @@ const servicesOption = (params) => ({
124
113
  */
125
114
  const servicesFlagValue = (value) => Array.isArray(value) ? value.map(String) : void 0;
126
115
  //#endregion
127
- export { NEON_SERVICES, NEON_SERVICE_LABELS, NO_SERVICES, deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption };
116
+ export { NEON_SERVICES, NEON_SERVICE_LABELS, deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption };
@@ -1,4 +1,4 @@
1
- import { a as OAUTH, d as scopeOf, i as API_KEY, l as interpretCredentials, t as storeFor, u as isSameCredential } from "./_chunks/credential_io-D8AR-jvB.js";
1
+ import { a as OAUTH, d as scopeOf, i as API_KEY, l as interpretCredentials, t as storeFor, u as isSameCredential } from "./_chunks/credential_io-ByNuojoE.js";
2
2
  import { log } from "./log.js";
3
3
  import { getApiClient } from "./api.js";
4
4
  import { revokeToken } from "./auth.js";
@@ -57,7 +57,8 @@ const retirePreviousCredential = async (props, name, existing, replacementKey) =
57
57
  apiKey: existing.apiKey,
58
58
  apiHost: props.apiHost
59
59
  });
60
- log.info(await withdrawKey(client, existing.scope, existing.keyId) ? `Revoked the key it replaces (id ${existing.keyId})` : `Could not revoke the key it replaces (id ${existing.keyId}); it may still be live. Remove it with: neon api-keys revoke ${existing.keyId}`);
60
+ if (await withdrawKey(client, existing.scope, existing.keyId)) log.info(`Revoked the key it replaces (id ${existing.keyId})`);
61
+ else log.warning(`Could not revoke the key it replaces (id ${existing.keyId}); it may still be live. Remove it with: neon api-keys revoke ${existing.keyId}`);
61
62
  return;
62
63
  }
63
64
  const revoked = await revokeTokenSet(existing.tokens, props);
@@ -70,7 +71,7 @@ const withdrawKey = async (client, scope, keyId) => {
70
71
  const { data } = scope.orgId ? await client.revokeOrgApiKey(scope.orgId, keyId) : await client.revokeApiKey(keyId);
71
72
  return data.revoked === true && data.id === keyId;
72
73
  } catch (err) {
73
- log.error("Failed to revoke API key %d: %s", keyId, err instanceof Error ? err.message : String(err));
74
+ log.warning("Failed to revoke API key %d: %s", keyId, err instanceof Error ? err.message : String(err));
74
75
  return false;
75
76
  }
76
77
  };
@@ -130,7 +130,6 @@ const renderAppliedChanges = (changes, title, opts) => {
130
130
  lines.push(...renderBranchGroups(byBranch, paint));
131
131
  return lines.join("\n");
132
132
  };
133
- const isOverrideableConflict = (conflict) => conflict.reason.includes("Pass `updateExisting: true`");
134
133
  const CUSTOM_DOMAIN_REASON = /^custom domain "([^"]+)"/;
135
134
  const labeledConflictField = (conflict) => {
136
135
  if (conflict.field !== "customDomain") return conflict.field;
@@ -145,8 +144,7 @@ const renderBranchSettingConflicts = (conflicts, opts) => {
145
144
  const existing = byBranch.get(conflict.identifier) ?? [];
146
145
  byBranch.set(conflict.identifier, [...existing, ...expandField(labeledConflictField(conflict), conflict.current, conflict.desired)]);
147
146
  }
148
- const heading = conflicts.every(isOverrideableConflict) ? "Branch settings differ (re-run with --update-existing to apply)" : "Branch settings differ";
149
- return [paint.title(heading), ...renderBranchGroups(byBranch, paint)].join("\n");
147
+ return [paint.title("Branch settings differ"), ...renderBranchGroups(byBranch, paint)].join("\n");
150
148
  };
151
149
  const renderCustomDomainFollowup = (result, opts) => {
152
150
  const paint = palette(opts.color);
@@ -1,4 +1,4 @@
1
- import { r as recordCredentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
1
+ import { r as recordCredentialInputs } from "../_chunks/auth_selection-YX5_KZgD.js";
2
2
  //#region src/utils/middlewares.ts
3
3
  /**
4
4
  * Forwarding Yargs's default would turn ambient configuration into an explicit child override.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon",
3
- "version": "8.0.12",
3
+ "version": "8.2.0",
4
4
  "description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
5
5
  "keywords": [
6
6
  "neon",
@@ -64,9 +64,9 @@
64
64
  "yaml": "^2.9.0",
65
65
  "yargs": "17.7.2",
66
66
  "yoctocolors": "^2.1.2",
67
- "@neon/config": "1.8.4",
68
- "@neon/config-runtime": "1.7.6",
69
- "@neon/sdk": "7.0.1"
67
+ "@neon/config-runtime": "1.8.0",
68
+ "@neon/sdk": "7.0.1",
69
+ "@neon/config": "1.8.6"
70
70
  },
71
71
  "optionalDependencies": {
72
72
  "@napi-rs/keyring": "1.3.0",
@@ -100,8 +100,8 @@
100
100
  "typescript": "^5.9.0",
101
101
  "vitest": "^3.0.9",
102
102
  "@neon-internals/cli-core": "0.0.0",
103
- "@neon-internals/env-core": "0.0.27",
104
103
  "@neon/e2e-harness": "0.0.0",
104
+ "@neon-internals/env-core": "0.0.29",
105
105
  "@neon/functions": "0.11.0"
106
106
  },
107
107
  "publishConfig": {