neon 4.6.0 → 4.8.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 +48 -113
- package/dist/_chunks/{auth_selection-ktL6uoEe.js → auth_selection-pitMFzDe.js} +1 -1
- package/dist/_chunks/{credential_io-B1JxWELN.js → credential_io-YeAxg9Xn.js} +2 -2
- package/dist/_chunks/{paths-g3DRqJiD.js → paths-DMq0Lt7a.js} +1 -1
- package/dist/_chunks/{profiles-D95AeZ5v.js → profiles-CvnFEQyd.js} +1 -1
- package/dist/analytics.js +2 -2
- package/dist/auth_context.js +1 -1
- package/dist/commands/auth.js +12 -9
- package/dist/commands/bootstrap.js +72 -43
- package/dist/commands/claim.js +1 -1
- package/dist/commands/env.js +3 -20
- package/dist/commands/init.js +80 -79
- package/dist/commands/link.js +110 -292
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/plugins.js +3 -3
- package/dist/commands/profile.js +4 -4
- package/dist/config.js +1 -1
- package/dist/credential_io.js +1 -1
- package/dist/index.js +1 -1
- package/dist/init/child.js +36 -0
- package/dist/init/plan.js +47 -0
- package/dist/init/tooling.js +43 -0
- package/dist/init/wizard.js +42 -0
- package/dist/mcp/targets.js +1 -1
- package/dist/plugins/plan.js +1 -1
- package/dist/plugins/targets.js +5 -1
- package/dist/retire_credential.js +1 -1
- package/dist/utils/cli_name.js +1 -12
- package/dist/utils/middlewares.js +1 -1
- package/dist/utils/package_manager.js +10 -5
- package/package.json +13 -14
- package/dist/init/auth.js +0 -53
- package/dist/init/build_config.js +0 -4
- package/dist/init/detect_agent.js +0 -63
- package/dist/init/editors.js +0 -19
- package/dist/init/enrich_output.js +0 -61
- package/dist/init/extension.js +0 -154
- package/dist/init/inspect.js +0 -249
- package/dist/init/interactive.js +0 -529
- package/dist/init/neonctl.js +0 -139
- package/dist/init/orchestrate.js +0 -189
- package/dist/init/phases/auth.js +0 -195
- package/dist/init/phases/cleanup.js +0 -27
- package/dist/init/phases/db.js +0 -257
- package/dist/init/phases/getting_started.js +0 -218
- package/dist/init/phases/mcp.js +0 -219
- package/dist/init/phases/migrations.js +0 -250
- package/dist/init/phases/neon_auth.js +0 -116
- package/dist/init/phases/setup.js +0 -582
- package/dist/init/phases/skills.js +0 -86
- package/dist/init/phases/status.js +0 -82
- package/dist/init/profile_cli.js +0 -56
- package/dist/init/resolve_context.js +0 -105
- package/dist/init/route_command.js +0 -93
- package/dist/init/skills.js +0 -206
- package/dist/init/types.js +0 -1
- package/dist/init/vsix.js +0 -89
package/README.md
CHANGED
|
@@ -256,14 +256,14 @@ The Neon CLI supports autocompletion, which you can configure in a few easy step
|
|
|
256
256
|
`link` resolves what it can and **verifies every identifier you pass** before writing, so a `.neon` is never left half-written or pointing at something that doesn't exist:
|
|
257
257
|
|
|
258
258
|
- **org** is inferred from the project (so `--project-id` alone is enough); it's omitted only when the project has no organization (personal account).
|
|
259
|
-
- **project** is taken from `--project-id` (or chosen interactively
|
|
260
|
-
- **branch** is
|
|
259
|
+
- **project** is taken from `--project-id` (or chosen interactively).
|
|
260
|
+
- **branch** is taken from `--branch`, an existing pin for the same project, or the project's branch list: one branch is pinned automatically; several prompt in a TTY, pin the default with `-y`, or stay unpinned for [`neon checkout <branch>`](#checkout). A project with no branches is linked without a pin and says so.
|
|
261
261
|
|
|
262
262
|
When a branch ends up pinned, `link` also runs [`env pull`](#env-pull) so the branch's Neon env vars (`DATABASE_URL`, …) land in a local `.env`. With no branch pinned there is nothing to pull, so `link` instead nudges you to run `neon checkout`. Pass `--no-env-pull` to skip the pull (for example when injecting env at runtime with `neon-env run` or `neon dev`).
|
|
263
263
|
|
|
264
264
|
> **Migrating from `set-context`?** `set-context` is **deprecated** in favor of `link` (see [below](#set-context-is-deprecated)). It still works exactly as before for now (a raw write), it just prints a deprecation warning. The `.neon` `branchId` field is also superseded by `branch` (which stores the branch **name** when known); old `branchId` files are still read and are upgraded to `branch` the next time `link`/`checkout` writes the context.
|
|
265
265
|
|
|
266
|
-
There are
|
|
266
|
+
There are two modes:
|
|
267
267
|
|
|
268
268
|
**Interactive (default)** — guided prompts for humans:
|
|
269
269
|
|
|
@@ -282,22 +282,34 @@ Linked .neon:
|
|
|
282
282
|
|
|
283
283
|
When you link an **existing** project that has more than one branch, the interactive flow adds a
|
|
284
284
|
final step to pick which branch to pin — the same `+ Create a new branch…` + list selector used by
|
|
285
|
-
`neon checkout` (a single-branch project is pinned automatically, no prompt)
|
|
286
|
-
`link --project-id …` does **not** prompt or default a branch; it links org + project and leaves
|
|
287
|
-
branch selection to `neon checkout`:
|
|
285
|
+
`neon checkout` (a single-branch project is pinned automatically, no prompt):
|
|
288
286
|
|
|
289
287
|
```bash
|
|
288
|
+
$ neon link
|
|
290
289
|
? Which organization would you like to link? › Personal Org (org-abc123)
|
|
291
290
|
? Which project would you like to link? › my-app (polished-snowflake-12345678)
|
|
292
291
|
? Which branch would you like to link? › [default] main (br-main-branch-87654321)
|
|
293
292
|
```
|
|
294
293
|
|
|
294
|
+
`link --project-id …` skips org and project. One branch is pinned with no prompt. Several branches
|
|
295
|
+
in a TTY show the branch prompt; `-y` pins the default; no TTY leaves the pin empty for
|
|
296
|
+
`neon checkout`:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
$ neon link --project-id polished-snowflake-12345678
|
|
300
|
+
? Which branch would you like to link? › [default] main (br-main-branch-87654321)
|
|
301
|
+
```
|
|
302
|
+
|
|
295
303
|
**Non-interactive (flags or `--params` JSON)** — for scripts and CI:
|
|
296
304
|
|
|
297
305
|
```bash
|
|
298
|
-
# Link to an existing project (org is inferred
|
|
306
|
+
# Link to an existing project (org is inferred). Pins the only branch;
|
|
307
|
+
# several branches prompt in a TTY, or stay unpinned without one.
|
|
299
308
|
neon link --project-id polished-snowflake-12345678
|
|
300
309
|
|
|
310
|
+
# Same, pin the project's default branch when several exist
|
|
311
|
+
neon link --project-id polished-snowflake-12345678 -y
|
|
312
|
+
|
|
301
313
|
# Same, but also pin a branch (name or id — resolved and stored as its name)
|
|
302
314
|
neon link --project-id polished-snowflake-12345678 --branch main
|
|
303
315
|
|
|
@@ -322,64 +334,21 @@ neon link --no-checks --org-id org-abc123 --project-id polished-snowflake-123456
|
|
|
322
334
|
|
|
323
335
|
Every supplied identifier is checked before anything is written, with actionable errors — e.g. `Project '…' not found`, `You don't have access to project '…'`, `Organization '…' not found, or your API key doesn't have access to it`, `Project '…' belongs to organization 'A', not 'B'`, or `Branch '…' not found in project '…'. Available branches: …`.
|
|
324
336
|
|
|
325
|
-
**
|
|
337
|
+
**Agents and scripts (no TTY):** List, then link. `neon link --help` prints the same recipe.
|
|
326
338
|
|
|
327
339
|
```bash
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
"options": [
|
|
333
|
-
{ "id": "org-abc123", "name": "Personal Org" },
|
|
334
|
-
{ "id": "org-team", "name": "Team Org" }
|
|
335
|
-
],
|
|
336
|
-
"next_command_template": "neon link --agent --org-id <org_id>"
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
$ neon link --agent --org-id org-abc123
|
|
340
|
-
{
|
|
341
|
-
"status": "needs_project",
|
|
342
|
-
"instruction": "Ask the user whether to link to one of these 1 existing projects (use next_command_template with --project-id) or create a new project (use create_option.next_command_template).",
|
|
343
|
-
"options": [
|
|
344
|
-
{ "id": "polished-snowflake-12345678", "name": "my-app" }
|
|
345
|
-
],
|
|
346
|
-
"create_option": {
|
|
347
|
-
"instruction": "To create a new project, ask the user for a project name. The region can be omitted to receive a follow-up needs_project_details response that lists available regions.",
|
|
348
|
-
"next_command_template": "neon link --agent --org-id org-abc123 --project-name <name> --region-id <region_id>"
|
|
349
|
-
},
|
|
350
|
-
"next_command_template": "neon link --agent --org-id org-abc123 --project-id <project_id>"
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
$ neon link --agent --org-id org-abc123 --project-id polished-snowflake-12345678
|
|
354
|
-
{
|
|
355
|
-
"status": "linked",
|
|
356
|
-
"context_file": "/path/to/cwd/.neon",
|
|
357
|
-
"context": {
|
|
358
|
-
"orgId": "org-abc123",
|
|
359
|
-
"projectId": "polished-snowflake-12345678"
|
|
360
|
-
},
|
|
361
|
-
"project": { "id": "polished-snowflake-12345678" },
|
|
362
|
-
"message": "Linked /path/to/cwd/.neon to project polished-snowflake-12345678 (org org-abc123). No branch pinned — run `neon checkout <branch>` (omit the branch to list options) to pin one and pull its env vars."
|
|
363
|
-
}
|
|
340
|
+
neon orgs list --output json
|
|
341
|
+
neon projects list --org-id <org-id> --output json
|
|
342
|
+
neon link --project-id <project-id> [--branch <name> | -y]
|
|
343
|
+
neon link --org-id <org-id> --project-name <name> --region-id aws-us-east-2
|
|
364
344
|
```
|
|
365
345
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
**Organization-scoped API keys** (those created at the organization level rather than the user level) cannot list user organizations or call the regions endpoint. `link` handles this transparently:
|
|
346
|
+
Organization-scoped API keys cannot list user organizations (`orgs list`) or call the regions endpoint:
|
|
369
347
|
|
|
370
|
-
-
|
|
371
|
-
- If the
|
|
372
|
-
-
|
|
373
|
-
|
|
374
|
-
**Agent error contract**: any unexpected failure in `--agent` mode is reported as JSON to stdout with exit code 1, so agents can always parse the response:
|
|
375
|
-
|
|
376
|
-
```json
|
|
377
|
-
{
|
|
378
|
-
"status": "error",
|
|
379
|
-
"code": "CLIENT_ERROR",
|
|
380
|
-
"message": "user has no access to projects"
|
|
381
|
-
}
|
|
382
|
-
```
|
|
348
|
+
- Pass `--org-id` (Neon Console → Settings) or `--project-id` (org is inferred from the project).
|
|
349
|
+
- If the key is org-scoped and at least one project already exists, interactive `link` auto-detects the org from the first project and prints an informational message.
|
|
350
|
+
- If no projects exist yet, interactive `link` errors pointing at `--org-id`.
|
|
351
|
+
- When the regions endpoint is not allowed, interactive create falls back to a built-in static region list. Non-interactive create already requires `--region-id`.
|
|
383
352
|
|
|
384
353
|
**Offline writes (`--no-checks`)** — write the `.neon` with no API calls at all: no org inference, no existence/access verification, no env pull. Because nothing can be resolved offline, it requires both `--org-id` and `--project-id` (`--branch` optional, stored verbatim). Handy for scripted/CI setups or re-creating a `.neon` from values you already trust:
|
|
385
354
|
|
|
@@ -395,7 +364,7 @@ How today's `set-context` uses map onto `link`:
|
|
|
395
364
|
|
|
396
365
|
| `set-context` (deprecated) | Recommended `link` equivalent |
|
|
397
366
|
| --------------------------------------- | ----------------------------------------------------------------------------- |
|
|
398
|
-
| `neon set-context --project-id <id>` | `neon link --project-id <id>` (infers org + verifies;
|
|
367
|
+
| `neon set-context --project-id <id>` | `neon link --project-id <id>` (infers org + verifies; pins the only branch) |
|
|
399
368
|
| `neon set-context --org-id <id>` | `neon link --org-id <id>` |
|
|
400
369
|
| `neon set-context --branch-id <id>` | `neon link --branch <name\|id>` |
|
|
401
370
|
| `neon set-context` (clear) | `neon link --clear` |
|
|
@@ -700,7 +669,9 @@ When a package cannot be bundled — a native addon with no esbuild loader, or a
|
|
|
700
669
|
|
|
701
670
|
## Scaffold a project (`bootstrap`)
|
|
702
671
|
|
|
703
|
-
`neon bootstrap` copies a Neon starter template into a new (or current) directory — conceptually like `degit`, but it only pulls from a small set of templates we maintain in the public [`neondatabase/examples`](https://github.com/neondatabase/examples) repo.
|
|
672
|
+
`neon bootstrap` copies a Neon starter template into a new (or current) directory — conceptually like `degit`, but it only pulls from a small set of templates we maintain in the public [`neondatabase/examples`](https://github.com/neondatabase/examples) repo. The template copy needs no Neon login: it downloads files from GitHub.
|
|
673
|
+
|
|
674
|
+
After scaffolding, an interactive terminal offers agent tooling (the Neon plugin, or skills and MCP separately — never both) and then `neon link`. `--default` / `-y` skips the template, install, git, and agent pickers, then runs `link --yes`. `link --yes` still asks for a project unless one is already linked. `--no-agent-setup` and `--no-link` skip those. Non-interactive without `--default` prints next steps and does not install, set up agents, or link.
|
|
704
675
|
|
|
705
676
|
Pass a target directory (or `.` for the current one). In an interactive terminal you pick the template from a list; in CI / non-interactive contexts pass `--template <id>`.
|
|
706
677
|
|
|
@@ -708,7 +679,7 @@ Pass a target directory (or `.` for the current one). In an interactive terminal
|
|
|
708
679
|
# Pick a template interactively and scaffold it into ./my-app
|
|
709
680
|
$ neon bootstrap my-app
|
|
710
681
|
|
|
711
|
-
# Scaffold a specific template into the current directory (
|
|
682
|
+
# Scaffold a specific template into the current directory (skips the template picker)
|
|
712
683
|
$ neon bootstrap . --template hono
|
|
713
684
|
|
|
714
685
|
# List templates
|
|
@@ -720,64 +691,28 @@ $ neon bootstrap --list-templates --output json
|
|
|
720
691
|
|
|
721
692
|
The target directory must be empty unless you pass `--force` (a lone `.git` is ignored, so a freshly `git init`ed folder is fine). Symlinks and executable bits in the template are preserved.
|
|
722
693
|
|
|
723
|
-
## Set up a project
|
|
694
|
+
## Set up a project (`init`)
|
|
724
695
|
|
|
725
|
-
`neon init`
|
|
696
|
+
`neon init` sets up this directory for Neon.
|
|
726
697
|
|
|
727
|
-
|
|
728
|
-
$ neon init
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
Run in a terminal it prompts you through those steps. This is what the retired `neon-init` package used to do; `npx neon init` replaces it.
|
|
698
|
+
An empty directory (nothing except `.git`) runs `neon bootstrap .` and stops. With `-y` that is `neon bootstrap . --default`. Bootstrap handles scaffolding, agent tooling, and linking.
|
|
732
699
|
|
|
733
|
-
|
|
700
|
+
An existing app installs agent tooling, then `neon link` unless `.neon` already has a projectId, then `neon config init`. Interactive `config init` opens the services picker; `-y` uses `--services none` (starter policy).
|
|
734
701
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
`--agent` turns the same flow into a state machine an AI coding assistant drives. It prints **one JSON object on stdout** and nothing else: a phase response carrying a `status` and a `nextAction` telling the agent what to do next — usually another `neon init` invocation, spelled out as a `command`. The two read-only steps, `status` and `finalize`, return a snapshot instead.
|
|
702
|
+
In an interactive terminal it offers one of: the Neon plugin (`neon plugins`), skills and MCP separately (`neon skills`, then `neon mcp`), or skip agent setup. It never runs plugin and skills+MCP together.
|
|
738
703
|
|
|
739
704
|
```bash
|
|
740
|
-
$ neon init
|
|
741
|
-
|
|
742
|
-
"auth": { "authenticated": true },
|
|
743
|
-
"tooling": { "mcpServer": { "configured": true, "scope": "global" }, "skills": { "installed": false, "scope": null } },
|
|
744
|
-
"project": { "databaseUrl": false },
|
|
745
|
-
"migrations": { "tool": "prisma", "hasMigrations": false },
|
|
746
|
-
"recommendations": [
|
|
747
|
-
{ "priority": "high", "message": "No DATABASE_URL found in .env", "command": "neon init --agent --data '{\"step\":\"db\"}'" },
|
|
748
|
-
{ "priority": "medium", "message": "Neon agent skills not detected in this project", "command": "neon init --agent --data '{\"step\":\"skills\",\"install\":true}'" },
|
|
749
|
-
{ "priority": "medium", "message": "prisma detected but no migrations found", "command": "neon init --agent --data '{\"step\":\"migrations\"}'" }
|
|
750
|
-
]
|
|
751
|
-
}
|
|
705
|
+
$ neon init
|
|
706
|
+
$ neon init -y
|
|
752
707
|
```
|
|
753
708
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
`--data` takes a JSON object whose `step` selects the phase: `auth`, `db`, `setup`, `getting-started`, `mcp`, `skills`, `migrations`, `neon-auth`, `status`, or `finalize`. Remaining keys are that phase's options. Without `--data`, the orchestrator picks the next phase itself. An unrecognised `step` is refused with the full list.
|
|
757
|
-
|
|
758
|
-
**Failures are JSON too**, so an agent never has to distinguish "it broke" from "it returned nothing":
|
|
759
|
-
|
|
760
|
-
```bash
|
|
761
|
-
$ neon init --agent --data '{not json'
|
|
762
|
-
{
|
|
763
|
-
"success": false,
|
|
764
|
-
"error": "Invalid JSON in --data flag at position 1. Expected a JSON object."
|
|
765
|
-
}
|
|
766
|
-
$ echo $?
|
|
767
|
-
1
|
|
768
|
-
```
|
|
709
|
+
`-y` skips the template picker and the agent-setup offer. Empty dir: `bootstrap --default`. Existing app: plugin when a project-level plugin agent is detected (Cursor, Claude Code, Codex); otherwise skills and MCP. VS Code, GitHub Copilot CLI, and Grok only take the plugin user-level (`neon plugins --global`), so `-y` uses skills and MCP for those.
|
|
769
710
|
|
|
770
|
-
|
|
711
|
+
`-y` forwards `-y` to `plugins` or `skills`/`mcp`, `--default` to `bootstrap`, `--yes` to `link`, and `--services none` to `config init`. `link --yes` only skips the "already linked" confirmation; it still asks for a project unless one is already linked.
|
|
771
712
|
|
|
772
|
-
|
|
713
|
+
A failed step stops the rest. `--profile` and `--config-dir` are forwarded to each child. `--output json` and `--output yaml` are refused; the commands init runs print their own output.
|
|
773
714
|
|
|
774
|
-
|
|
775
|
-
| --- | --- |
|
|
776
|
-
| `--agent`, `-a` | Emit the JSON state machine instead of prompting |
|
|
777
|
-
| `--data <json>` | Route to one phase, with that phase's options |
|
|
778
|
-
| `--skip-migrations` | Leave the migrations phase out of the flow |
|
|
779
|
-
| `--preview` | Enable preview features (scaffolding a project from a template) |
|
|
780
|
-
| `--profile <name>` | Run as that stored account. See [Which credential an invocation uses](#which-credential-an-invocation-uses). |
|
|
715
|
+
`skills` needs Node.js 22.20 or newer. See [`bootstrap`](#scaffold-a-project-bootstrap), [`plugins`](#install-the-neon-plugin-plugins), [`skills`](#install-neon-agent-skills-skills), [`link`](#linking-a-project), and [`mcp`](#install-the-neon-mcp-server-mcp) for what those commands write.
|
|
781
716
|
|
|
782
717
|
## Install the Neon MCP server (`mcp`)
|
|
783
718
|
|
|
@@ -1159,7 +1094,7 @@ When both are only environment variables the key wins, which keeps a CI pipeline
|
|
|
1159
1094
|
|
|
1160
1095
|
`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.
|
|
1161
1096
|
|
|
1162
|
-
`neon init`
|
|
1097
|
+
`neon init` forwards `--profile` and `--config-dir` to the commands it runs. An explicit `--api-key` is passed to those children through `NEON_API_KEY`, not argv.
|
|
1163
1098
|
|
|
1164
1099
|
## API keys (`api-keys`)
|
|
1165
1100
|
|
|
@@ -1256,8 +1191,8 @@ Id Name Project Created At Last Used At Last
|
|
|
1256
1191
|
| open | | Open the linked project in Console |
|
|
1257
1192
|
| config | `init`, `status`, `plan`, `apply` | Drive a branch from `neon.ts` |
|
|
1258
1193
|
| deploy | | Alias for `config apply` |
|
|
1259
|
-
| bootstrap | | Scaffold a
|
|
1260
|
-
| init | |
|
|
1194
|
+
| bootstrap | | Scaffold a template, then agent tooling and link |
|
|
1195
|
+
| init | | Empty dir: bootstrap. Existing: agents, link, neon.ts |
|
|
1261
1196
|
| mcp | | Install the Neon MCP server |
|
|
1262
1197
|
| plugins | | Install the Neon plugin |
|
|
1263
1198
|
| skills | `update` | Install Neon agent skills |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as writeSecretFile } from "./secure_file-BucZj4yQ.js";
|
|
2
|
-
import {
|
|
3
|
-
import { f as profilesFilePath, v as CRED_STORAGE_FILE, y as CRED_STORAGE_KEYRING } from "./profiles-
|
|
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";
|
|
4
4
|
import { tryLoadKeyring } from "../keyring.js";
|
|
5
5
|
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
6
6
|
import { dirname } from "node:path";
|
|
@@ -148,4 +148,4 @@ const isOwnedCredentialPath = (configDirectory, file) => {
|
|
|
148
148
|
return legacy !== void 0 && isInsideConfigDir(legacy, file);
|
|
149
149
|
};
|
|
150
150
|
//#endregion
|
|
151
|
-
export {
|
|
151
|
+
export { isOwnedCredentialPath as a, isInsideConfigDir as i, credentialsPath as n, resolveConfigFile as o, defaultDir as r, CREDENTIALS_FILE as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as writeSecretFile } from "./secure_file-BucZj4yQ.js";
|
|
2
|
-
import {
|
|
2
|
+
import { n as credentialsPath, o as resolveConfigFile, r as defaultDir } from "./paths-DMq0Lt7a.js";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
5
5
|
//#region ../../internals/cli-core/dist/cli_config.js
|
package/dist/analytics.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { a as OAUTH, t as storeFor } from "./_chunks/credential_io-
|
|
1
|
+
import { a as OAUTH, t as storeFor } from "./_chunks/credential_io-YeAxg9Xn.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";
|
|
5
5
|
import { getApiClient, isNeonApiError } from "./api.js";
|
|
6
|
-
import {
|
|
6
|
+
import { n as credentialsPath } from "./_chunks/paths-DMq0Lt7a.js";
|
|
7
7
|
import "./config.js";
|
|
8
8
|
import { getAuthContext } from "./auth_context.js";
|
|
9
9
|
import { isCurrentBranchProbe } from "./context.js";
|
package/dist/auth_context.js
CHANGED
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-YeAxg9Xn.js";
|
|
3
3
|
import { isCi } from "../env.js";
|
|
4
4
|
import { log } from "../log.js";
|
|
5
5
|
import { getApiClient } from "../api.js";
|
|
6
|
-
import {
|
|
6
|
+
import { a as isOwnedCredentialPath } from "../_chunks/paths-DMq0Lt7a.js";
|
|
7
7
|
import { setAuthContext } from "../auth_context.js";
|
|
8
8
|
import { currentContextFile, isClaimCommand, isConfigInit, isCurrentBranchProbe, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, 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-CvnFEQyd.js";
|
|
10
10
|
import { extendTokenSet } from "../utils/auth.js";
|
|
11
11
|
import { auth, refreshToken } from "../auth.js";
|
|
12
|
-
import { i as selectCredential, n as displacedProfileWarning, t as credentialInputs } from "../_chunks/auth_selection-
|
|
12
|
+
import { i as selectCredential, n as displacedProfileWarning, t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
13
13
|
import { ClaimableClient, ClaimableServiceError } from "../claimable/api.js";
|
|
14
14
|
import { assertionHasExpired, claimableCredentialsPath, readClaimableCredentials, resolveClaimableContext, shouldUseClaimableCredentials } from "../claimable/state.js";
|
|
15
15
|
import { readOutgoingCredential, retirePreviousCredential } from "../retire_credential.js";
|
|
@@ -220,10 +220,17 @@ const ensureAuth = async (props) => {
|
|
|
220
220
|
if (isMcpOauth(props)) return;
|
|
221
221
|
if (props._[0] === "open") return;
|
|
222
222
|
if (isSkillsCommand(props) || isPluginsCommand(props)) return;
|
|
223
|
+
if (props._[0] === "init") {
|
|
224
|
+
selectCredential({
|
|
225
|
+
...credentialInputs(),
|
|
226
|
+
profileFlag: props.profile
|
|
227
|
+
});
|
|
228
|
+
log.debug("init: skipping global auth; child commands authenticate");
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
223
231
|
if (isClaimCommand(props)) return;
|
|
224
232
|
const isLocalDev = props._[0] === "dev";
|
|
225
233
|
const isBootstrap = props._[0] === "bootstrap";
|
|
226
|
-
const isInit = props._[0] === "init";
|
|
227
234
|
const isMcp = isMcpCommand(props);
|
|
228
235
|
if (props._[0] === "auth") {
|
|
229
236
|
props.apiClient = getApiClient({
|
|
@@ -334,10 +341,6 @@ const ensureAuth = async (props) => {
|
|
|
334
341
|
log.debug("bootstrap: no usable credentials; continuing without auth");
|
|
335
342
|
return;
|
|
336
343
|
}
|
|
337
|
-
if (isInit) {
|
|
338
|
-
log.debug("init: skipping global auth; init manages its own auth flow");
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
344
|
if (isMcp) {
|
|
342
345
|
log.debug("mcp: no usable credentials; minting requires auth or --oauth");
|
|
343
346
|
return;
|
|
@@ -2,9 +2,12 @@ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
|
2
2
|
import { isCi } from "../env.js";
|
|
3
3
|
import { log } from "../log.js";
|
|
4
4
|
import { getCliName } from "../utils/cli_name.js";
|
|
5
|
-
import { t as credentialInputs } from "../_chunks/auth_selection-
|
|
5
|
+
import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
6
6
|
import { writer } from "../writer.js";
|
|
7
7
|
import { FALLBACK_TEMPLATES, ensureTargetUsable, fetchTemplates, findTemplate, scaffoldTemplate, templateIds } from "../init/bootstrap.js";
|
|
8
|
+
import { spawnCliChild } from "../init/child.js";
|
|
9
|
+
import { projectContextFile } from "../init/plan.js";
|
|
10
|
+
import { runScaffoldFollowUp } from "../init/tooling.js";
|
|
8
11
|
import { formatInstallCommand, inferPackageManager, installArgs, installedPackageManagers, resolvePackageManager, runCommand } from "../utils/package_manager.js";
|
|
9
12
|
import { existsSync } from "node:fs";
|
|
10
13
|
import { join, relative, resolve } from "node:path";
|
|
@@ -19,7 +22,7 @@ var bootstrap_exports = /* @__PURE__ */ __exportAll({
|
|
|
19
22
|
});
|
|
20
23
|
const removedAgent = () => `\`${getCliName()} bootstrap --agent\` was removed. List templates with \`${getCliName()} bootstrap --list-templates --output json\`. Scaffold with \`${getCliName()} bootstrap <directory> --template <id>\` or \`${getCliName()} bootstrap <directory> --default\`.`;
|
|
21
24
|
const command = "bootstrap [directory]";
|
|
22
|
-
const describe = "Scaffold a new project from a Neon starter template";
|
|
25
|
+
const describe = "Scaffold a new project from a Neon starter template, then install agent tooling and link a Neon project";
|
|
23
26
|
const builder = (argv) => argv.usage("$0 bootstrap [directory] [options]").positional("directory", {
|
|
24
27
|
describe: "Directory to scaffold into. Use \".\" for the current directory. Omit to be prompted.",
|
|
25
28
|
type: "string"
|
|
@@ -45,7 +48,7 @@ const builder = (argv) => argv.usage("$0 bootstrap [directory] [options]").posit
|
|
|
45
48
|
},
|
|
46
49
|
default: {
|
|
47
50
|
alias: "y",
|
|
48
|
-
describe: "Quick start: scaffold the default template (or --template)
|
|
51
|
+
describe: "Quick start: scaffold the default template (or --template), then install, git, agent tooling, and link --yes. Skips those pickers; link --yes still asks for a project unless one is already linked",
|
|
49
52
|
type: "boolean",
|
|
50
53
|
default: false
|
|
51
54
|
},
|
|
@@ -63,8 +66,13 @@ const builder = (argv) => argv.usage("$0 bootstrap [directory] [options]").posit
|
|
|
63
66
|
describe: `Run \`${getCliName()} link\` in the scaffolded directory after installing. In interactive mode this is offered as a prompt; use --no-link to skip without being asked.`,
|
|
64
67
|
type: "boolean",
|
|
65
68
|
default: true
|
|
69
|
+
},
|
|
70
|
+
"agent-setup": {
|
|
71
|
+
type: "boolean",
|
|
72
|
+
default: true,
|
|
73
|
+
describe: "After scaffolding, install the Neon plugin or skills and MCP. Use --no-agent-setup to skip"
|
|
66
74
|
}
|
|
67
|
-
}).example("$0 bootstrap my-app", "Create ./my-app from an interactively chosen template").example("$0 bootstrap . --template hono", "Scaffold the Hono template into the current directory").example("$0 bootstrap my-app --default", "
|
|
75
|
+
}).example("$0 bootstrap my-app", "Create ./my-app from an interactively chosen template").example("$0 bootstrap . --template hono", "Scaffold the Hono template into the current directory").example("$0 bootstrap my-app --default", "Skip the pickers; link --yes still asks for a project unless one is already linked").example("$0 bootstrap --list-templates --output json", "Print the template catalog as JSON").check((argv) => {
|
|
68
76
|
if (argv.agent === true) throw new Error(removedAgent());
|
|
69
77
|
return true;
|
|
70
78
|
}).strict();
|
|
@@ -163,12 +171,6 @@ const resolveTargetDir = async (props, interactive, template) => {
|
|
|
163
171
|
return resolve(process.cwd(), dir === "." ? "" : dir);
|
|
164
172
|
};
|
|
165
173
|
const defaultDirName = (template) => template.source.subdir.split("/").pop() || template.id;
|
|
166
|
-
/**
|
|
167
|
-
* Download and materialize the template into `targetDir`. The actual
|
|
168
|
-
* download/extract/write lives in `src/init/bootstrap.ts`, shared with `neon init`
|
|
169
|
-
* (exec-bit and symlink fidelity, graceful symlink fallback); here we just
|
|
170
|
-
* frame it with progress logging. Returns the number of files written.
|
|
171
|
-
*/
|
|
172
174
|
const scaffold = async (template, targetDir) => {
|
|
173
175
|
log.info("Fetching template \"%s\" from GitHub…", template.id);
|
|
174
176
|
const filesWritten = await scaffoldTemplate(template, targetDir, { onWarn: (message) => {
|
|
@@ -198,16 +200,33 @@ const runPostScaffoldSteps = async (props, targetDir, interactive) => {
|
|
|
198
200
|
installed = await runCommand(pm, installArgs(pm), targetDir);
|
|
199
201
|
}
|
|
200
202
|
if (props.git && !isGitRepo(targetDir) && await confirm("Initialize a git repository?")) await initGitRepo(targetDir);
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
203
|
+
const skipLink = shouldSkipLinkForDeps(targetDir, installed);
|
|
204
|
+
if (props.link && skipLink) logSkippedLink(pm);
|
|
205
|
+
const kids = bootstrapChildren(props, targetDir);
|
|
206
|
+
await runScaffoldFollowUp({
|
|
207
|
+
cwd: targetDir,
|
|
208
|
+
yes: false,
|
|
209
|
+
skipAgentSetup: props.agentSetup === false,
|
|
210
|
+
shouldLink: false,
|
|
211
|
+
linkYes: false,
|
|
212
|
+
...kids,
|
|
213
|
+
...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
|
|
214
|
+
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {}
|
|
215
|
+
});
|
|
216
|
+
if (props.link && !skipLink && await confirm(`Link this project to a Neon project now? (runs ${getCliName()} link)`)) {
|
|
217
|
+
await runScaffoldFollowUp({
|
|
218
|
+
cwd: targetDir,
|
|
219
|
+
yes: false,
|
|
220
|
+
skipAgentSetup: true,
|
|
221
|
+
shouldLink: true,
|
|
222
|
+
linkYes: false,
|
|
223
|
+
...kids
|
|
224
|
+
});
|
|
225
|
+
printNextSteps(targetDir, pm, {
|
|
226
|
+
installed,
|
|
227
|
+
suggestLink: false
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
211
230
|
}
|
|
212
231
|
printNextSteps(targetDir, pm, {
|
|
213
232
|
installed,
|
|
@@ -215,20 +234,27 @@ const runPostScaffoldSteps = async (props, targetDir, interactive) => {
|
|
|
215
234
|
});
|
|
216
235
|
};
|
|
217
236
|
const installPrompt = (inferred) => inferred ? `Install dependencies with ${inferred}?` : "Install dependencies?";
|
|
218
|
-
/**
|
|
219
|
-
* `--default` quick start: run install + git init without prompting, honoring
|
|
220
|
-
* --no-install / --no-git. Linking is intentionally skipped — it needs an
|
|
221
|
-
* org/project choice we can't make non-interactively — so we point at it in the
|
|
222
|
-
* closing hint instead.
|
|
223
|
-
*/
|
|
237
|
+
/** `link --yes` still asks for a project unless one is already linked. */
|
|
224
238
|
const runDefaultSteps = async (props, targetDir, pm) => {
|
|
225
|
-
log.info("Quick start (--default):
|
|
239
|
+
log.info("Quick start (--default): skipping the template, install, git, and agent pickers. link --yes still asks for a project unless one is already linked.");
|
|
226
240
|
let installed = false;
|
|
227
241
|
if (props.install) installed = await runCommand(pm, installArgs(pm), targetDir);
|
|
228
242
|
if (props.git && !isGitRepo(targetDir)) await initGitRepo(targetDir);
|
|
243
|
+
const skipLink = shouldSkipLinkForDeps(targetDir, installed);
|
|
244
|
+
if (props.link && skipLink) logSkippedLink(pm);
|
|
245
|
+
await runScaffoldFollowUp({
|
|
246
|
+
cwd: targetDir,
|
|
247
|
+
yes: true,
|
|
248
|
+
skipAgentSetup: props.agentSetup === false,
|
|
249
|
+
shouldLink: props.link && !skipLink,
|
|
250
|
+
linkYes: true,
|
|
251
|
+
...bootstrapChildren(props, targetDir),
|
|
252
|
+
...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
|
|
253
|
+
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {}
|
|
254
|
+
});
|
|
229
255
|
printNextSteps(targetDir, pm, {
|
|
230
256
|
installed,
|
|
231
|
-
suggestLink:
|
|
257
|
+
suggestLink: !(props.link && !skipLink)
|
|
232
258
|
});
|
|
233
259
|
};
|
|
234
260
|
const isGitRepo = (dir) => existsSync(join(dir, ".git"));
|
|
@@ -239,6 +265,24 @@ const NEON_CONFIG_FILENAMES = [
|
|
|
239
265
|
"neon.mjs"
|
|
240
266
|
];
|
|
241
267
|
const hasNeonConfig = (dir) => NEON_CONFIG_FILENAMES.some((name) => existsSync(join(dir, name)));
|
|
268
|
+
const shouldSkipLinkForDeps = (dir, installed) => !installed && hasNeonConfig(dir);
|
|
269
|
+
const logSkippedLink = (pm) => {
|
|
270
|
+
log.info(`Skipping the Neon link step: \`${getCliName()} link\` reads this project's neon.ts to pull env vars, which needs its dependencies. Run \`${formatInstallCommand(pm)}\`, then \`${getCliName()} link\`.`);
|
|
271
|
+
};
|
|
272
|
+
const bootstrapChildren = (props, targetDir) => {
|
|
273
|
+
const explicitKey = props.profile ? "" : credentialInputs().apiKeyFlag;
|
|
274
|
+
return {
|
|
275
|
+
run: props.run ?? spawnCliChild,
|
|
276
|
+
forward: {
|
|
277
|
+
...props.configDir ? { configDir: props.configDir } : {},
|
|
278
|
+
...props.profile ? { profile: props.profile } : {},
|
|
279
|
+
apiHost: props.apiHost,
|
|
280
|
+
contextFile: projectContextFile(targetDir, props.contextFile),
|
|
281
|
+
...props.analytics === false ? { analytics: false } : {}
|
|
282
|
+
},
|
|
283
|
+
...explicitKey ? { authEnv: { NEON_API_KEY: explicitKey } } : {}
|
|
284
|
+
};
|
|
285
|
+
};
|
|
242
286
|
/**
|
|
243
287
|
* Initialize a git repository in the scaffolded directory. Just `git init` — we
|
|
244
288
|
* deliberately don't auto-commit, both to avoid failing on a machine with no
|
|
@@ -279,21 +323,6 @@ const selectPackageManager = async () => {
|
|
|
279
323
|
});
|
|
280
324
|
return pm ?? "npm";
|
|
281
325
|
};
|
|
282
|
-
/**
|
|
283
|
-
* Re-invoke this same CLI as `neon link` inside the scaffolded directory, so the
|
|
284
|
-
* new project's `.neon` context (and pulled `.env`) land in the right place and
|
|
285
|
-
* link's own interactive picker drives org/project/branch selection. Re-execing
|
|
286
|
-
* (rather than calling the handler in-process) keeps link running with `cwd` set
|
|
287
|
-
* to the target dir, which is where its env pull writes.
|
|
288
|
-
*/
|
|
289
|
-
const runNeonLink = async (props, targetDir) => {
|
|
290
|
-
const args = [process.argv[1], "link"];
|
|
291
|
-
if (props.configDir) args.push("--config-dir", props.configDir);
|
|
292
|
-
if (props.profile) args.push("--profile", props.profile);
|
|
293
|
-
args.push("--api-host", props.apiHost, "--output", props.output);
|
|
294
|
-
const explicitKey = props.profile ? "" : credentialInputs().apiKeyFlag;
|
|
295
|
-
await runCommand(process.execPath, args, targetDir, explicitKey ? { NEON_API_KEY: explicitKey } : void 0);
|
|
296
|
-
};
|
|
297
326
|
const printScaffolded = (template, targetDir) => {
|
|
298
327
|
log.info("");
|
|
299
328
|
log.info("Done. Scaffolded \"%s\" into %s.", template.title, isCurrentDir(targetDir) ? "the current directory" : displayDir(targetDir));
|
package/dist/commands/claim.js
CHANGED
|
@@ -5,7 +5,7 @@ import { applyContext, contextBranch, ensureGitignored, readContextFile } from "
|
|
|
5
5
|
import { declaredNeonServices } from "../config_services.js";
|
|
6
6
|
import { NEON_SERVICES, deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
|
|
7
7
|
import { mergeEnvFile, resolveEnvFilePath } from "../env_file.js";
|
|
8
|
-
import { t as credentialInputs } from "../_chunks/auth_selection-
|
|
8
|
+
import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
9
9
|
import { ClaimableClient, ClaimableServiceError } from "../claimable/api.js";
|
|
10
10
|
import { assertionHasExpired, listClaimableCredentials, readClaimableCredentials, removeClaimableCredentials, resolveClaimableContext, writeClaimableCredentials } from "../claimable/state.js";
|
|
11
11
|
import { writer } from "../writer.js";
|
package/dist/commands/env.js
CHANGED
|
@@ -19,8 +19,7 @@ var env_exports = /* @__PURE__ */ __exportAll({
|
|
|
19
19
|
command: () => "env",
|
|
20
20
|
describe: () => describe,
|
|
21
21
|
handler: () => handler,
|
|
22
|
-
pull: () => pull
|
|
23
|
-
renderAgentPullNote: () => renderAgentPullNote
|
|
22
|
+
pull: () => pull
|
|
24
23
|
});
|
|
25
24
|
const command = "env";
|
|
26
25
|
const describe = "Manage a branch's Neon env variables locally";
|
|
@@ -229,8 +228,7 @@ const pickSelectedVars = (vars, selectedKeys) => {
|
|
|
229
228
|
* On by default; `--no-env-pull` opts out (e.g. when env is injected at runtime via
|
|
230
229
|
* `neon-env run` / `neon dev`, or to keep secrets out of the working tree). The pin is the
|
|
231
230
|
* command's primary effect and has already succeeded by the time this runs, so a pull failure
|
|
232
|
-
* degrades to a warning rather than failing the command.
|
|
233
|
-
* `link --agent` can fold an accurate note into its JSON message.
|
|
231
|
+
* degrades to a warning rather than failing the command.
|
|
234
232
|
*/
|
|
235
233
|
const autoPullEnvAfterPin = async (props) => {
|
|
236
234
|
if (!props.envPull) {
|
|
@@ -250,21 +248,6 @@ Run \`${getCliName()} env pull\` once resolved (e.g. \`${getCliName()} deploy\`
|
|
|
250
248
|
}
|
|
251
249
|
};
|
|
252
250
|
/**
|
|
253
|
-
* Render the one-line env-pull note appended to `link --agent`'s JSON `message`, so an agent
|
|
254
|
-
* reading the structured output knows whether its branch env is already on disk.
|
|
255
|
-
*/
|
|
256
|
-
const renderAgentPullNote = (result) => {
|
|
257
|
-
switch (result.status) {
|
|
258
|
-
case "written": {
|
|
259
|
-
const credential = result.credential?.issued ? ` Issued a new branch credential, so ${result.credential.keys.join(", ")} changed.` : "";
|
|
260
|
-
return ` Pulled ${result.written.length} Neon env var${result.written.length === 1 ? "" : "s"} into ${result.file}.${credential}`;
|
|
261
|
-
}
|
|
262
|
-
case "empty": return " No Neon env vars to pull for this branch yet.";
|
|
263
|
-
case "skipped": return ` Skipped env pull (--no-env-pull); run \`${getCliName()} env pull\` later, or inject env at runtime with \`neon-env run -- <your dev command>\`.`;
|
|
264
|
-
case "failed": return ` Could not pull env vars (${result.message}); run \`${getCliName()} env pull\` once resolved.`;
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
/**
|
|
268
251
|
* Keep only the recognized Neon variables from the resolved set, so a stray inherited
|
|
269
252
|
* value never lands in the user's `.env` file. (Today `resolveNeonEnvVars` only emits Neon
|
|
270
253
|
* vars, but filtering keeps the contract explicit and future-proof.)
|
|
@@ -278,4 +261,4 @@ const pickNeonVars = (vars) => {
|
|
|
278
261
|
return out;
|
|
279
262
|
};
|
|
280
263
|
//#endregion
|
|
281
|
-
export { ENV_PULL_SKIPPED_HINT, autoPullEnvAfterPin, builder, command, describe, handler, pull,
|
|
264
|
+
export { ENV_PULL_SKIPPED_HINT, autoPullEnvAfterPin, builder, command, describe, handler, pull, env_exports as t };
|