neonctl 2.37.1 → 2.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +15 -700
  2. package/bin/cli.js +3 -0
  3. package/package.json +8 -75
  4. package/dist/analytics.js +0 -168
  5. package/dist/api.js +0 -720
  6. package/dist/auth.js +0 -125
  7. package/dist/callback.html +0 -60
  8. package/dist/cli.js +0 -9
  9. package/dist/commands/api.js +0 -278
  10. package/dist/commands/auth.js +0 -214
  11. package/dist/commands/bootstrap.js +0 -481
  12. package/dist/commands/branches.js +0 -488
  13. package/dist/commands/bucket.js +0 -549
  14. package/dist/commands/checkout.js +0 -321
  15. package/dist/commands/config.js +0 -626
  16. package/dist/commands/connection_string.js +0 -172
  17. package/dist/commands/data_api.js +0 -284
  18. package/dist/commands/databases.js +0 -82
  19. package/dist/commands/deploy.js +0 -26
  20. package/dist/commands/dev.js +0 -698
  21. package/dist/commands/diff.js +0 -222
  22. package/dist/commands/env.js +0 -189
  23. package/dist/commands/functions.js +0 -373
  24. package/dist/commands/index.js +0 -62
  25. package/dist/commands/init.js +0 -73
  26. package/dist/commands/inspect.js +0 -65
  27. package/dist/commands/ip_allow.js +0 -137
  28. package/dist/commands/link.js +0 -1121
  29. package/dist/commands/neon_auth.js +0 -1028
  30. package/dist/commands/operations.js +0 -28
  31. package/dist/commands/orgs.js +0 -24
  32. package/dist/commands/projects.js +0 -413
  33. package/dist/commands/psql.js +0 -62
  34. package/dist/commands/roles.js +0 -65
  35. package/dist/commands/schema_diff.js +0 -151
  36. package/dist/commands/set_context.js +0 -29
  37. package/dist/commands/snapshots.js +0 -455
  38. package/dist/commands/status.js +0 -40
  39. package/dist/commands/user.js +0 -15
  40. package/dist/commands/vpc_endpoints.js +0 -134
  41. package/dist/config.js +0 -11
  42. package/dist/config_format.js +0 -72
  43. package/dist/context.js +0 -231
  44. package/dist/current_branch_fast_path.js +0 -55
  45. package/dist/dev/env.js +0 -244
  46. package/dist/dev/functions.js +0 -70
  47. package/dist/dev/inputs.js +0 -63
  48. package/dist/dev/runtime.js +0 -146
  49. package/dist/env.js +0 -36
  50. package/dist/env_file.js +0 -159
  51. package/dist/errors.js +0 -80
  52. package/dist/functions_api.js +0 -48
  53. package/dist/help.js +0 -146
  54. package/dist/index.js +0 -240
  55. package/dist/log.js +0 -18
  56. package/dist/parameters.gen.js +0 -480
  57. package/dist/pkg.js +0 -25
  58. package/dist/psql/cli.js +0 -53
  59. package/dist/psql/command/cmd_cond.js +0 -437
  60. package/dist/psql/command/cmd_connect.js +0 -820
  61. package/dist/psql/command/cmd_copy.js +0 -1035
  62. package/dist/psql/command/cmd_describe.js +0 -1815
  63. package/dist/psql/command/cmd_format.js +0 -948
  64. package/dist/psql/command/cmd_io.js +0 -2193
  65. package/dist/psql/command/cmd_lo.js +0 -393
  66. package/dist/psql/command/cmd_meta.js +0 -969
  67. package/dist/psql/command/cmd_misc.js +0 -187
  68. package/dist/psql/command/cmd_pipeline.js +0 -1148
  69. package/dist/psql/command/cmd_restrict.js +0 -171
  70. package/dist/psql/command/cmd_show.js +0 -766
  71. package/dist/psql/command/dispatch.js +0 -343
  72. package/dist/psql/command/inputQueue.js +0 -42
  73. package/dist/psql/command/shared.js +0 -71
  74. package/dist/psql/complete/filenames.js +0 -139
  75. package/dist/psql/complete/index.js +0 -104
  76. package/dist/psql/complete/matcher.js +0 -315
  77. package/dist/psql/complete/psqlVars.js +0 -249
  78. package/dist/psql/complete/queries.js +0 -493
  79. package/dist/psql/complete/rules.js +0 -2424
  80. package/dist/psql/core/common.js +0 -1253
  81. package/dist/psql/core/help.js +0 -576
  82. package/dist/psql/core/mainloop.js +0 -1360
  83. package/dist/psql/core/prompt.js +0 -439
  84. package/dist/psql/core/settings.js +0 -686
  85. package/dist/psql/core/sqlHelp.js +0 -1066
  86. package/dist/psql/core/startup.js +0 -846
  87. package/dist/psql/core/syncVars.js +0 -116
  88. package/dist/psql/core/variables.js +0 -287
  89. package/dist/psql/describe/formatters.js +0 -1330
  90. package/dist/psql/describe/processNamePattern.js +0 -270
  91. package/dist/psql/describe/queries.js +0 -2452
  92. package/dist/psql/describe/versionGate.js +0 -44
  93. package/dist/psql/index.js +0 -2030
  94. package/dist/psql/io/history.js +0 -299
  95. package/dist/psql/io/input.js +0 -120
  96. package/dist/psql/io/lineEditor/buffer.js +0 -325
  97. package/dist/psql/io/lineEditor/complete.js +0 -227
  98. package/dist/psql/io/lineEditor/filename.js +0 -159
  99. package/dist/psql/io/lineEditor/index.js +0 -893
  100. package/dist/psql/io/lineEditor/keymap.js +0 -745
  101. package/dist/psql/io/lineEditor/vt100.js +0 -363
  102. package/dist/psql/io/pgpass.js +0 -202
  103. package/dist/psql/io/pgservice.js +0 -194
  104. package/dist/psql/io/psqlrc.js +0 -422
  105. package/dist/psql/print/aligned.js +0 -1748
  106. package/dist/psql/print/asciidoc.js +0 -230
  107. package/dist/psql/print/crosstab.js +0 -463
  108. package/dist/psql/print/csv.js +0 -76
  109. package/dist/psql/print/html.js +0 -240
  110. package/dist/psql/print/json.js +0 -96
  111. package/dist/psql/print/latex.js +0 -379
  112. package/dist/psql/print/pager.js +0 -267
  113. package/dist/psql/print/troff.js +0 -240
  114. package/dist/psql/print/unaligned.js +0 -99
  115. package/dist/psql/print/units.js +0 -188
  116. package/dist/psql/scanner/slash.js +0 -515
  117. package/dist/psql/scanner/sql.js +0 -914
  118. package/dist/psql/scanner/stringutils.js +0 -394
  119. package/dist/psql/types/backslash.js +0 -1
  120. package/dist/psql/types/connection.js +0 -1
  121. package/dist/psql/types/index.js +0 -7
  122. package/dist/psql/types/printer.js +0 -1
  123. package/dist/psql/types/repl.js +0 -1
  124. package/dist/psql/types/scanner.js +0 -24
  125. package/dist/psql/types/settings.js +0 -1
  126. package/dist/psql/types/variables.js +0 -1
  127. package/dist/psql/wire/connection.js +0 -2857
  128. package/dist/psql/wire/copy.js +0 -108
  129. package/dist/psql/wire/notify.js +0 -59
  130. package/dist/psql/wire/pipeline.js +0 -521
  131. package/dist/psql/wire/protocol.js +0 -466
  132. package/dist/psql/wire/sasl.js +0 -294
  133. package/dist/psql/wire/tls.js +0 -602
  134. package/dist/storage_api.js +0 -147
  135. package/dist/test_utils/fixtures.js +0 -121
  136. package/dist/test_utils/oauth_server.js +0 -9
  137. package/dist/types.js +0 -1
  138. package/dist/utils/ai_gateway_notice.js +0 -180
  139. package/dist/utils/api_enums.js +0 -33
  140. package/dist/utils/auth.js +0 -5
  141. package/dist/utils/branch_notice.js +0 -22
  142. package/dist/utils/branch_picker.js +0 -103
  143. package/dist/utils/compute_units.js +0 -28
  144. package/dist/utils/config_diff.js +0 -185
  145. package/dist/utils/enrichers.js +0 -161
  146. package/dist/utils/esbuild.js +0 -158
  147. package/dist/utils/formats.js +0 -18
  148. package/dist/utils/git_diff.js +0 -90
  149. package/dist/utils/inspect_db.js +0 -153
  150. package/dist/utils/inspect_queries.js +0 -372
  151. package/dist/utils/middlewares.js +0 -20
  152. package/dist/utils/openapi.js +0 -114
  153. package/dist/utils/package_manager.js +0 -68
  154. package/dist/utils/point_in_time.js +0 -56
  155. package/dist/utils/psql.js +0 -120
  156. package/dist/utils/string.js +0 -5
  157. package/dist/utils/ui.js +0 -59
  158. package/dist/utils/zip.js +0 -4
  159. package/dist/writer.js +0 -97
@@ -1,222 +0,0 @@
1
- import chalk from "chalk";
2
- import { isNeonApiError } from "../api.js";
3
- import { log } from "../log.js";
4
- import { fillSingleProject } from "../utils/enrichers.js";
5
- import { looksLikeBranchId } from "../utils/formats.js";
6
- import { renderDatabaseSchemaDiff, renderSchemaDiffReport, } from "../utils/git_diff.js";
7
- import { writer } from "../writer.js";
8
- // A top-level shortcut for `branches schema-diff`, framed like `git diff`: it
9
- // compares the branch you're on (pinned in `.neon`, or `--branch`) against the
10
- // branch you name, and prints a git-style unified schema diff. Because it has a
11
- // handler but no subcommands, `diff` is also listed in `NO_SUBCOMMANDS_VERBS`
12
- // (see index.ts) so a bare `neon diff main` isn't intercepted by the help
13
- // fallback.
14
- export const command = "diff [compare-branch]";
15
- export const describe = "Show a git-style schema diff between the current branch and another branch";
16
- export const builder = (argv) => argv
17
- .usage("$0 diff [compare-branch] [options]")
18
- .positional("compare-branch", {
19
- describe: "Branch name or id to compare against (the reference / '---' side). " +
20
- "Defaults to the current branch's parent.",
21
- type: "string",
22
- })
23
- .options({
24
- "project-id": {
25
- describe: "Project ID",
26
- type: "string",
27
- },
28
- branch: {
29
- alias: "b",
30
- describe: "The branch to review (the '+++' side). Defaults to the branch " +
31
- "pinned in the local context (.neon).",
32
- type: "string",
33
- },
34
- database: {
35
- alias: "db",
36
- describe: "Limit the diff to a single database. Defaults to every database on the current branch.",
37
- type: "string",
38
- },
39
- })
40
- .middleware(fillSingleProject)
41
- .middleware((args) => {
42
- // The positional arrives as `compare-branch`; surface it under the
43
- // camelCase name the handler reads, and mirror it to `branchId` for
44
- // analytics (same pattern as the `branches` command group).
45
- const compareBranch = args["compare-branch"];
46
- if (typeof compareBranch === "string") {
47
- args.compareBranch = compareBranch;
48
- }
49
- })
50
- .example([
51
- [
52
- "$0 diff main",
53
- "Diff the current branch's schema against the main branch",
54
- ],
55
- [
56
- "$0 diff",
57
- "Diff the current branch's schema against its parent branch",
58
- ],
59
- [
60
- "$0 diff main --branch feature/checkout",
61
- "Diff an explicit branch against main (ignoring the .neon context)",
62
- ],
63
- [
64
- "$0 diff main --db neondb",
65
- "Diff only the neondb database against main",
66
- ],
67
- ]);
68
- export const handler = async (props) => {
69
- const branches = (await props.apiClient.listProjectBranches({
70
- projectId: props.projectId,
71
- })).data.branches;
72
- const after = resolveAfterBranch(branches, props.branch);
73
- const before = resolveBeforeBranch(branches, after, props.compareBranch);
74
- if (before.branchId === after.branchId) {
75
- throw new Error(`Nothing to compare: both sides resolve to branch ${after.branchName} (${after.branchId}).`);
76
- }
77
- const databases = await resolveDatabases(props, after);
78
- const diffs = [];
79
- for (const database of databases) {
80
- const [beforeSql, afterSql] = await Promise.all([
81
- fetchSchemaSql(props, before.branchId, database),
82
- fetchSchemaSql(props, after.branchId, database),
83
- ]);
84
- diffs.push({
85
- database,
86
- before: { ...before, sql: beforeSql },
87
- after: { ...after, sql: afterSql },
88
- });
89
- }
90
- if (props.output === "json" || props.output === "yaml") {
91
- writeStructured(props, diffs);
92
- return;
93
- }
94
- log.info("%s Comparing schema %s → %s", chalk.dim("→"), chalk.red(`${before.branchName}`), chalk.green(`${after.branchName}`));
95
- const { hasChanges, text } = renderSchemaDiffReport(diffs, {
96
- color: props.color !== false,
97
- });
98
- if (!hasChanges) {
99
- log.info("No schema differences between %s and %s.", before.branchName, after.branchName);
100
- return;
101
- }
102
- writer(props).text(`${text}\n`);
103
- };
104
- /**
105
- * Resolve the branch under review (`+++` side). Prefers the explicit
106
- * `branch`/`--branch` value (name or `br-…` id), falling back to the project's
107
- * default branch. An unknown `br-…` id is trusted as-is (it may be too new to
108
- * appear in the listing); an unknown *name* is a hard error.
109
- */
110
- const resolveAfterBranch = (branches, ref) => {
111
- if (ref) {
112
- return resolveRef(branches, ref);
113
- }
114
- const def = branches.find((b) => b.default);
115
- if (!def) {
116
- throw new Error("No branch specified and no default branch found. Pass --branch <name|id>.");
117
- }
118
- return { branchId: def.id, branchName: def.name ?? def.id };
119
- };
120
- /**
121
- * Resolve the reference branch (`---` side). Uses the `compare-branch`
122
- * positional when given, otherwise the parent of the branch under review — so a
123
- * bare `neon diff` answers "what did I change since branching?".
124
- */
125
- const resolveBeforeBranch = (branches, after, ref) => {
126
- if (ref) {
127
- return resolveRef(branches, ref);
128
- }
129
- const afterBranch = branches.find((b) => b.id === after.branchId);
130
- const parentId = afterBranch?.parent_id;
131
- if (!parentId) {
132
- throw new Error(`Branch "${after.branchName}" has no parent to compare against. ` +
133
- "Pass a branch to compare with, e.g. `neon diff main`.");
134
- }
135
- const parent = branches.find((b) => b.id === parentId);
136
- return {
137
- branchId: parentId,
138
- branchName: parent?.name ?? parentId,
139
- };
140
- };
141
- /** Resolve a branch reference (name or `br-…` id) against the fetched listing. */
142
- const resolveRef = (branches, ref) => {
143
- const found = looksLikeBranchId(ref)
144
- ? branches.find((b) => b.id === ref)
145
- : branches.find((b) => b.name === ref);
146
- if (found) {
147
- return { branchId: found.id, branchName: found.name ?? found.id };
148
- }
149
- // A `br-…` id absent from the listing is still usable as an id; only an
150
- // unresolved name is a genuine error (mirrors resolveBranchRef in enrichers).
151
- if (looksLikeBranchId(ref)) {
152
- return { branchId: ref, branchName: ref };
153
- }
154
- throw new Error(`Branch ${ref} not found.\nAvailable branches: ${branches
155
- .map((b) => b.name)
156
- .join(", ")}`);
157
- };
158
- /**
159
- * The databases to diff: the one passed via `--database` (validated against the
160
- * branch under review), or every database on that branch when none is given.
161
- */
162
- const resolveDatabases = async (props, after) => {
163
- const databases = (await props.apiClient.listProjectBranchDatabases(props.projectId, after.branchId)).data.databases;
164
- if (props.database !== undefined) {
165
- if (!databases.find((d) => d.name === props.database)) {
166
- throw new Error(`Database "${props.database}" not found on branch ${after.branchName}. ` +
167
- `Available: ${databases.map((d) => d.name).join(", ")}`);
168
- }
169
- return [props.database];
170
- }
171
- if (databases.length === 0) {
172
- throw new Error(`No databases found on branch ${after.branchName} (${after.branchId}).`);
173
- }
174
- return databases.map((d) => d.name);
175
- };
176
- /**
177
- * Fetch a branch database's `CREATE …` SQL. A database absent from the branch
178
- * (404) yields an empty schema, so the diff shows it as fully added/removed
179
- * rather than failing — the natural outcome when a database exists on only one
180
- * side of the comparison.
181
- */
182
- const fetchSchemaSql = async (props, branchId, database) => {
183
- try {
184
- const { data } = await props.apiClient.getProjectBranchSchema({
185
- projectId: props.projectId,
186
- branchId,
187
- db_name: database,
188
- });
189
- return data.sql ?? "";
190
- }
191
- catch (err) {
192
- if (isNeonApiError(err) && err.status === 404) {
193
- log.debug("diff: database %s not found on branch %s; treating schema as empty", database, branchId);
194
- return "";
195
- }
196
- throw err;
197
- }
198
- };
199
- /** Machine-readable output for `--output json|yaml`: one entry per database. */
200
- const writeStructured = (props, diffs) => {
201
- const report = diffs.map((diff) => {
202
- const rendered = renderDatabaseSchemaDiff(diff, { color: false });
203
- return {
204
- database: diff.database,
205
- base_branch: diff.before.branchName,
206
- base_branch_id: diff.before.branchId,
207
- compare_branch: diff.after.branchName,
208
- compare_branch_id: diff.after.branchId,
209
- has_changes: rendered.hasChanges,
210
- diff: rendered.text,
211
- };
212
- });
213
- writer(props).end(report, {
214
- fields: [
215
- "database",
216
- "base_branch",
217
- "compare_branch",
218
- "has_changes",
219
- "diff",
220
- ],
221
- });
222
- };
@@ -1,189 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { NEON_ENV_VAR_KEYS } from "@neon/env";
3
- import chalk from "chalk";
4
- import { ensureGitignored } from "../context.js";
5
- import { resolveNeonEnvVars } from "../dev/env.js";
6
- import { mergeEnvFile, readEnvFile, resolveEnvFilePath } from "../env_file.js";
7
- import { log } from "../log.js";
8
- import { warnAiGateway } from "../utils/ai_gateway_notice.js";
9
- import { announceTargetBranch } from "../utils/branch_notice.js";
10
- import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
11
- export const command = "env";
12
- export const describe = "Manage a branch's Neon env variables locally";
13
- /**
14
- * Shown (to stderr) when `link` / `checkout` skip the bundled env pull because the user passed
15
- * `--no-env-pull`. Names the two ways to get the branch's vars without an on-disk file written
16
- * eagerly: an explicit `neonctl env pull`, or runtime injection via `neon-env run`.
17
- */
18
- export const ENV_PULL_SKIPPED_HINT = "Skipped env pull (--no-env-pull). Run `neonctl env pull` to write this branch’s env vars " +
19
- "(DATABASE_URL, …) into a local .env, or inject them at runtime with `neon-env run -- <your dev command>`.";
20
- export const builder = (argv) => argv
21
- .usage("$0 env <sub-command> [options]")
22
- .options({
23
- "project-id": { describe: "Project ID", type: "string" },
24
- branch: { describe: "Branch ID or name", type: "string" },
25
- })
26
- .middleware(fillSingleProject)
27
- .command("pull", "Write the branch's Neon env variables to a local .env file", (yargs) => yargs
28
- .usage("$0 env pull [options]")
29
- .options({
30
- file: {
31
- describe: "Target .env file to write. Defaults to an existing .env, " +
32
- "otherwise .env.local. Only Neon variables are updated; other " +
33
- "lines are preserved.",
34
- type: "string",
35
- },
36
- })
37
- .example("$0 env pull", "Write the linked branch's Neon vars into .env.local (or .env if present)")
38
- .example("$0 env pull --branch preview --file .env.preview", "Pull a specific branch into a specific file"), async (args) => {
39
- // Explicit `env pull` announces the branch it's reading from up front so the user
40
- // can catch "pulled env from the wrong branch" before it overwrites their .env. The
41
- // bundled auto-pull (link / checkout / apply) stays quiet — those already report the
42
- // branch they pinned/applied to.
43
- await pull(args, { announce: true });
44
- })
45
- .demandCommand(1);
46
- export const handler = (args) => args;
47
- /** Every OS-level env var name `@neon/env` can emit, used only for reporting. */
48
- const NEON_VAR_NAMES = Object.values(NEON_ENV_VAR_KEYS).flatMap((group) => Object.values(group));
49
- /**
50
- * The Neon env vars `env pull` *owns*, so it removes any that the branch no longer has when
51
- * it reconciles the local `.env` (see {@link pull}). Scoped to the unambiguously Neon-named
52
- * vars — the `NEON_*` aliases plus `DATABASE_URL[_UNPOOLED]` — so switching a working
53
- * directory to a project/branch without Auth / the Data API drops the now-stale
54
- * `NEON_AUTH_*` / `NEON_DATA_API_*` lines instead of leaving credentials for features that
55
- * aren't enabled.
56
- *
57
- * Deliberately **excludes** the storage vars Neon projects onto third-party SDK names
58
- * (`AWS_*`): those collide with credentials a user may set by hand, so `env pull` only ever
59
- * writes them, never prunes them. The AI Gateway is emitted solely under its Neon-branded
60
- * vars (`NEON_AI_GATEWAY_*`), which are owned and pruned.
61
- */
62
- const NEON_OWNED_ENV_KEYS = [
63
- ...Object.values(NEON_ENV_VAR_KEYS.postgres),
64
- ...Object.values(NEON_ENV_VAR_KEYS.auth),
65
- ...Object.values(NEON_ENV_VAR_KEYS.dataApi),
66
- ...Object.values(NEON_ENV_VAR_KEYS.aiGateway),
67
- ];
68
- export const pull = async (props, opts = {}) => {
69
- const cwd = props.cwd ?? process.cwd();
70
- const branch = await resolveBranchRef(props);
71
- if (opts.announce) {
72
- announceTargetBranch(props, branch, "Pulling env from branch");
73
- }
74
- const branchId = branch.branchId;
75
- // Resolve the target file first and layer its current contents under the resolver's env
76
- // source. This lets `fetchEnv` reuse one-time secrets that are already on disk — Neon Auth
77
- // keys and the unified branch credential's `api_token` / `s3_secret_access_key`, which the
78
- // API returns exactly once — instead of minting a fresh credential on every pull.
79
- const targetPath = resolveEnvFilePath(cwd, props.file);
80
- const fileExisted = existsSync(targetPath);
81
- const existingEnv = fileExisted ? readEnvFile(targetPath) : {};
82
- // Reuse `neon dev`'s tiered resolver (neon.ts policy -> plan gate -> fetchEnv, else
83
- // pullConfig -> fetchEnv). Unlike dev, an unresolved context or failure is surfaced —
84
- // `env pull` is an explicit action, so it should error rather than write nothing.
85
- const vars = await resolveNeonEnvVars({
86
- cwd,
87
- projectId: props.projectId,
88
- branchId,
89
- env: { ...process.env, ...existingEnv },
90
- ...(props.apiKey ? { apiKey: props.apiKey } : {}),
91
- ...(props.apiHost ? { apiHost: props.apiHost } : {}),
92
- ...(props.runtimeApi ? { api: props.runtimeApi } : {}),
93
- });
94
- const neonVars = pickNeonVars(vars);
95
- if (Object.keys(neonVars).length === 0) {
96
- log.info("No Neon env variables to pull for this branch (no DATABASE_URL or " +
97
- "enabled Auth / Data API).");
98
- return { status: "empty" };
99
- }
100
- // Reconcile rather than blindly merge: write the branch's current Neon vars and prune any
101
- // Neon-owned vars the branch no longer has (e.g. NEON_AUTH_* / NEON_DATA_API_* carried over
102
- // from a previous project/branch). Non-Neon lines are always preserved.
103
- const { written, removed } = mergeEnvFile(targetPath, neonVars, {
104
- managedKeys: NEON_OWNED_ENV_KEYS,
105
- });
106
- log.info("Pulled %d Neon variable%s into %s: %s", written.length, written.length === 1 ? "" : "s", targetPath, written.join(", "));
107
- if (removed.length > 0) {
108
- log.info("Removed %d stale Neon variable%s not enabled on this branch: %s", removed.length, removed.length === 1 ? "" : "s", removed.join(", "));
109
- }
110
- // A dotenv file *we* create holds live branch credentials (DATABASE_URL, Auth keys, service
111
- // tokens), so ignore it the same way the `.neon` context file is — otherwise a fresh repo is
112
- // one `git add -A` away from committing them. Only on creation: re-adding the entry on every
113
- // pull would fight a user who deliberately un-ignored a file they want to commit.
114
- if (!fileExisted) {
115
- ensureGitignored(targetPath);
116
- }
117
- // When the branch has the AI Gateway enabled, the pulled credentials always work, but
118
- // serving is plan-gated and the model set can be reduced on the beta — surface that as a
119
- // courtesy notice (best-effort; never fails the pull). The freshly pulled token lets us
120
- // probe the branch's own /v1/models to detect a reduced catalog.
121
- const gatewayBaseUrl = neonVars.NEON_AI_GATEWAY_BASE_URL;
122
- const gatewayToken = neonVars.NEON_AI_GATEWAY_TOKEN;
123
- if (gatewayBaseUrl && gatewayToken) {
124
- await warnAiGateway({
125
- apiClient: props.apiClient,
126
- projectId: props.projectId,
127
- branchId,
128
- gateway: { baseUrl: gatewayBaseUrl, token: gatewayToken },
129
- });
130
- }
131
- return { status: "written", written, file: targetPath };
132
- };
133
- /**
134
- * Pull a freshly-pinned branch's Neon env vars into a local `.env`, bundled into `link` and
135
- * `checkout` so the branch-first loop is just *link + checkout* — `env pull` runs for you.
136
- *
137
- * On by default; `--no-env-pull` opts out (e.g. when env is injected at runtime via
138
- * `neon-env run` / `neon dev`, or to keep secrets out of the working tree). The pin is the
139
- * command's primary effect and has already succeeded by the time this runs, so a pull failure
140
- * degrades to a warning rather than failing the command. Returns what happened so
141
- * `link --agent` can fold an accurate note into its JSON message.
142
- */
143
- export const autoPullEnvAfterPin = async (props) => {
144
- if (!props.envPull) {
145
- log.info(chalk.dim(ENV_PULL_SKIPPED_HINT));
146
- return { status: "skipped" };
147
- }
148
- try {
149
- return await pull(props);
150
- }
151
- catch (err) {
152
- const message = err instanceof Error ? err.message : String(err);
153
- log.warning("Branch pinned, but pulling its Neon env vars failed: %s\n" +
154
- "Run `neonctl env pull` once resolved (e.g. `neonctl deploy` if a declared service " +
155
- "is missing), or inject them at runtime with `neon-env run -- <your dev command>`.", message);
156
- return { status: "failed", message };
157
- }
158
- };
159
- /**
160
- * Render the one-line env-pull note appended to `link --agent`'s JSON `message`, so an agent
161
- * reading the structured output knows whether its branch env is already on disk.
162
- */
163
- export const renderAgentPullNote = (result) => {
164
- switch (result.status) {
165
- case "written":
166
- return ` Pulled ${result.written.length} Neon env var${result.written.length === 1 ? "" : "s"} into ${result.file}.`;
167
- case "empty":
168
- return " No Neon env vars to pull for this branch yet.";
169
- case "skipped":
170
- return (" Skipped env pull (--no-env-pull); run `neonctl env pull` later, " +
171
- "or inject env at runtime with `neon-env run -- <your dev command>`.");
172
- case "failed":
173
- return ` Could not pull env vars (${result.message}); run \`neonctl env pull\` once resolved.`;
174
- }
175
- };
176
- /**
177
- * Keep only the recognized Neon variables from the resolved set, so a stray inherited
178
- * value never lands in the user's `.env` file. (Today `resolveNeonEnvVars` only emits Neon
179
- * vars, but filtering keeps the contract explicit and future-proof.)
180
- */
181
- const pickNeonVars = (vars) => {
182
- const out = {};
183
- for (const name of NEON_VAR_NAMES) {
184
- const value = vars[name];
185
- if (value !== undefined)
186
- out[name] = value;
187
- }
188
- return out;
189
- };