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 +38 -30
- package/dist/_chunks/{auth_selection-pitMFzDe.js → auth_selection-YX5_KZgD.js} +3 -3
- package/dist/_chunks/{credential_io-D8AR-jvB.js → credential_io-ByNuojoE.js} +2 -2
- package/dist/_chunks/{profiles-CvnFEQyd.js → profiles-DIB1av2Q.js} +2 -2
- package/dist/_shared/auth_selection.js +2 -2
- package/dist/_shared/profiles.js +3 -3
- package/dist/analytics.js +1 -1
- package/dist/auth_context.js +2 -2
- package/dist/auth_recovery.js +2 -2
- package/dist/commands/api_keys.js +1 -1
- package/dist/commands/auth.js +16 -10
- package/dist/commands/checkout.js +2 -2
- package/dist/commands/claim.js +2 -2
- package/dist/commands/config.js +56 -26
- package/dist/commands/dev.js +1 -1
- package/dist/commands/git.js +2 -2
- package/dist/commands/init.js +3 -5
- package/dist/commands/manifest.js +2 -2
- package/dist/commands/mcp.js +1 -1
- package/dist/commands/profile.js +10 -6
- package/dist/commands/user.js +1 -0
- package/dist/config_template.js +10 -8
- package/dist/credential_io.js +1 -1
- package/dist/index.js +1 -1
- package/dist/init/choices.js +2 -6
- package/dist/init/copy.js +3 -3
- package/dist/init/detect.js +3 -3
- package/dist/init/plan.js +2 -3
- package/dist/init/run.js +11 -24
- package/dist/log.js +1 -1
- package/dist/mcp/mint.js +2 -2
- package/dist/neon_services.js +9 -20
- package/dist/retire_credential.js +4 -3
- package/dist/utils/config_diff.js +1 -3
- package/dist/utils/middlewares.js +1 -1
- package/package.json +5 -5
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
|
|
66
|
+
neon login
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
The `
|
|
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
|
-
|
|
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
|
-
|
|
481
|
-
|
|
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
|
-
#
|
|
662
|
-
neon config init --services
|
|
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
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
744
|
-
|
|
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.
|
|
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
|
|
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
|
|
1202
|
-
neon
|
|
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
|
|
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. `
|
|
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
|
-
`
|
|
1212
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
| [
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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
|
-
* `
|
|
36
|
-
* different meanings — `neon
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
* `
|
|
36
|
-
* different meanings — `neon
|
|
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
|
*/
|
package/dist/_shared/profiles.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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-
|
|
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";
|
package/dist/auth_context.js
CHANGED
|
@@ -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
|
|
35
|
-
return `Authentication failed: the Neon API rejected profile "${profile}"'s stored session${where}. Sign in again with \`neon
|
|
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
|
};
|
package/dist/auth_recovery.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as storeFor } from "./_chunks/credential_io-
|
|
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
|
|
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.
|
|
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
|
};
|
package/dist/commands/auth.js
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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 = "
|
|
49
|
-
const aliases = ["
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
313
|
+
log.info(missingProjectMessage);
|
|
314
314
|
const { runLink } = await prompts({
|
|
315
315
|
type: "confirm",
|
|
316
316
|
name: "runLink",
|
package/dist/commands/claim.js
CHANGED
|
@@ -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-
|
|
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
|
|
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 };
|
package/dist/commands/config.js
CHANGED
|
@@ -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,
|
|
8
|
-
import {
|
|
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: "
|
|
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: "
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
package/dist/commands/dev.js
CHANGED
|
@@ -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.
|
|
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;
|
package/dist/commands/git.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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;
|
package/dist/commands/init.js
CHANGED
|
@@ -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:
|
|
54
|
+
allowed: NEON_SERVICES,
|
|
56
55
|
describe: "Services to declare in neon.ts",
|
|
57
|
-
|
|
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: "
|
|
15
|
-
aliases: ["
|
|
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
|
},
|
package/dist/commands/mcp.js
CHANGED
|
@@ -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()}
|
|
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;
|
package/dist/commands/profile.js
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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
|
|
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)
|
|
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
|
-
|
|
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);
|
package/dist/commands/user.js
CHANGED
|
@@ -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: [
|
package/dist/config_template.js
CHANGED
|
@@ -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
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
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
|
|
31
|
-
|
|
32
|
-
|
|
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,
|
|
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 };
|
package/dist/credential_io.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as storeFor } from "./_chunks/credential_io-
|
|
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
|
|
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;
|
package/dist/init/choices.js
CHANGED
|
@@ -10,7 +10,7 @@ const resolveInitConfigChoice = (input) => {
|
|
|
10
10
|
};
|
|
11
11
|
if (input.yes) return {
|
|
12
12
|
kind: "write",
|
|
13
|
-
services: ["
|
|
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,
|
|
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
|
|
53
|
-
const YES_LINK_NEEDS_AUTH = "-y cannot sign in. Sign in with `neon
|
|
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()}
|
|
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.",
|
package/dist/init/detect.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as storeFor } from "../_chunks/credential_io-
|
|
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-
|
|
4
|
-
import { i as selectCredential, t as credentialInputs } from "../_chunks/auth_selection-
|
|
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 ?
|
|
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
|
-
|
|
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 {
|
|
10
|
-
import {
|
|
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,
|
|
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 =
|
|
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 ? ["
|
|
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 ?? ["
|
|
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 ?? ["
|
|
463
|
+
const planned = existingConfig ? void 0 : configPlan.services ?? ["postgres"];
|
|
474
464
|
if (planned !== void 0) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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(
|
|
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
|
|
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.
|
|
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
|
};
|
package/dist/neon_services.js
CHANGED
|
@@ -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
|
|
61
|
-
*
|
|
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,
|
|
65
|
-
const supported = `Supported values: ${allowed.join(", ")}
|
|
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
|
|
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
|
|
88
|
-
* command supplied one.
|
|
81
|
+
* two are never answered with the same word.
|
|
89
82
|
*/
|
|
90
|
-
const unsupportedMessage = (unsupported, flag
|
|
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,
|
|
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-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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-
|
|
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
|
|
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.
|
|
68
|
-
"@neon/
|
|
69
|
-
"@neon/
|
|
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": {
|