neon 4.16.0 → 4.17.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.
@@ -1,12 +1,12 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { log } from "../log.js";
3
+ import { isClaimableEnvTarget } from "../claimable/state.js";
3
4
  import { getCliName } from "../utils/cli_name.js";
4
5
  import { readEnvFile, resolveEnvFilePath } from "../env_file.js";
5
- import { _ as functionBaseUrlKey } from "../_chunks/env_services-CAWrZTWa.js";
6
- import { isClaimableEnvTarget } from "../claimable/state.js";
6
+ import { y as functionBaseUrlKey } from "../_chunks/env_services-CbARRnq9.js";
7
7
  import { branchIdResolve } from "../utils/enrichers.js";
8
8
  import { bundleEntry } from "../utils/esbuild.js";
9
- import { a as resolveDevEnv } from "../_chunks/env-DsakkOVZ.js";
9
+ import { a as resolveDevEnv } from "../_chunks/env-B75xufe_.js";
10
10
  import { findConfigFunctionBySource, resolveFunctionsFromConfig } from "../dev/functions.js";
11
11
  import { resolveWatchInputs } from "../dev/inputs.js";
12
12
  import { existsSync, mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
@@ -95,21 +95,19 @@ const devEnvContext = (props, branchId, cwd) => {
95
95
  };
96
96
  };
97
97
  /**
98
- * Say when a run issued a branch credential.
98
+ * Say when this run obtained credential secrets it did not already hold.
99
99
  *
100
- * `dev` has nowhere to persist one — it writes no file — so on a branch with nothing to reuse
101
- * it mints per start and cannot name the previous one to revoke it. Every other command that
102
- * mints says so; this is the one that runs dozens of times a day, and the server banner listing
103
- * `NEON_AI_GATEWAY_TOKEN` reads as "fetched", not "just created, and the last one is still
104
- * live". The note names the one action that stops it, so it disappears once followed.
100
+ * `dev` writes no file. With platform defaults, each start re-reveals the same secrets until
101
+ * `env pull` persists them. In a region with no defaults, each start mints another credential
102
+ * and cannot revoke the last one. The note names the one action that stops either loop.
105
103
  */
106
104
  const reportDevCredential = (credential) => {
107
105
  if (!credential?.issued) return;
108
106
  if (credential.revoked.length > 0) {
109
- log.info("Issued a new branch credential — %s changed. Revoked the one it replaced (%s).", credential.keys.join(", "), credential.revoked.join(", "));
107
+ log.info("Wrote credential secrets — %s changed. Revoked the leftover it replaced (%s).", credential.keys.join(", "), credential.revoked.join(", "));
110
108
  return;
111
109
  }
112
- log.warning(`Issued a branch credential for this run (%s) and left any previous one live — a dev server has nowhere to keep it. Run \`${getCliName()} env pull\` once to write it to your .env, and restarts will reuse it instead of issuing another.`, credential.keys.join(", "));
110
+ log.warning(`Resolved credential secrets for this run (%s) without writing them to a file. Run \`${getCliName()} env pull\` once so restarts reuse them instead of revealing or minting again.`, credential.keys.join(", "));
113
111
  };
114
112
  const handler = async (props) => {
115
113
  if (props.source !== void 0) {
@@ -1,15 +1,15 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { log } from "../log.js";
3
3
  import { ensureGitignored } from "../context.js";
4
+ import { isClaimableEnvTarget } from "../claimable/state.js";
4
5
  import { getCliName } from "../utils/cli_name.js";
5
6
  import { deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
6
7
  import { mergeEnvFile, readEnvFile, resolveEnvFilePath } from "../env_file.js";
7
- import { a as envKeysForSelection, c as ownedEnvServiceKeys, f as NEON_ENV_VAR_KEYS, i as ENV_PULL_SERVICES, l as parseEnvPullKeys, o as envPullFlagValue, r as ENV_PULL_KEY_HELP, v as isFunctionBaseUrlKey } from "../_chunks/env_services-CAWrZTWa.js";
8
- import { isClaimableEnvTarget } from "../claimable/state.js";
9
- import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
8
+ import { a as envKeysForSelection, b as isFunctionBaseUrlKey, c as ownedEnvServiceKeys, f as NEON_ENV_VAR_KEYS, i as ENV_PULL_SERVICES, l as parseEnvPullKeys, o as envPullFlagValue, r as ENV_PULL_KEY_HELP } from "../_chunks/env_services-CbARRnq9.js";
10
9
  import { warnAiGateway } from "../utils/ai_gateway_notice.js";
11
10
  import { announceTargetBranch } from "../utils/branch_notice.js";
12
- import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-DsakkOVZ.js";
11
+ import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
12
+ import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-B75xufe_.js";
13
13
  import { existsSync } from "node:fs";
14
14
  import chalk from "chalk";
15
15
  //#region src/commands/env.ts
@@ -67,7 +67,7 @@ const builder = (argv) => argv.usage("$0 env <sub-command> [options]").options({
67
67
  " 2. neon.ts, when this directory has one — including derived function",
68
68
  " URLs (the function does not have to be deployed).",
69
69
  " 3. Otherwise everything the branch has, plus the AI Gateway —",
70
- " which mints a branch credential for it.",
70
+ " which pulls the default AI Gateway credential.",
71
71
  "",
72
72
  "On an unclaimed Claimable Neon project, neon.ts is still the source",
73
73
  "of truth when it only declares Postgres, Auth, and the Data API.",
@@ -78,8 +78,8 @@ const builder = (argv) => argv.usage("$0 env <sub-command> [options]").options({
78
78
  "and writes nothing for them.",
79
79
  "",
80
80
  "The pull bundled into link / checkout / config apply follows 2 and 3",
81
- "without the AI Gateway, so it never mints a credential you did not ask",
82
- "for. Run `env pull` to add it."
81
+ "without the AI Gateway, so it never writes a gateway token you did",
82
+ "not ask for. Run `env pull` to add it."
83
83
  ].join("\n")).example("$0 env pull", "Write the linked branch's Neon vars into .env.local (or .env if present)").example("$0 env pull --branch preview --file .env.preview", "Pull a specific branch into a specific file").example("$0 env pull -s ai-gateway -s postgres", "Pull only the AI Gateway and Postgres variables").example("$0 env pull -e DATABASE_URL", "Pull only the pooled Postgres connection string").example("$0 env pull -s auth -e DATABASE_URL", "Pull all Auth variables plus DATABASE_URL").example("$0 env pull -s functions", "Pull NEON_FUNCTION_<SLUG>_BASE_URL for every deployed function").example("$0 env pull -e NEON_FUNCTION_HELLO_BASE_URL", "Write that function's production invocation URL (no deploy required)").strict(), async (args) => {
84
84
  const rawServices = servicesFlagValue(args.service);
85
85
  const rawEnvKeys = envPullFlagValue(args.env);
@@ -164,7 +164,7 @@ const pull = async (props, opts = {}) => {
164
164
  log.info("Pulled %d Neon variable%s into %s: %s", written.length, written.length === 1 ? "" : "s", targetPath, written.join(", "));
165
165
  if (removed.length > 0) log.info("Removed %d stale Neon variable%s not enabled on this branch: %s", removed.length, removed.length === 1 ? "" : "s", removed.join(", "));
166
166
  if (credential?.issued) {
167
- log.info("Issued a new branch credential — these now hold fresh values: %s", credential.keys.join(", "));
167
+ log.info("Wrote credential secrets — these now hold fresh values: %s", credential.keys.join(", "));
168
168
  if (credential.revoked.length > 0) log.info("Revoked the credential it replaced (%s).", credential.revoked.join(", "));
169
169
  else if (credential.superseded.length > 0) log.info("Left the credential it replaced live (%s): an explicitly scoped pull can't tell which other variables still use it. Revoke it in the Neon Console if nothing does.", credential.superseded.join(", "));
170
170
  }
@@ -2,13 +2,13 @@ import { t as auth_exports } from "./auth.js";
2
2
  import { t as api_exports } from "./api.js";
3
3
  import { t as api_keys_exports } from "./api_keys.js";
4
4
  import { t as ask_exports } from "./ask.js";
5
- import { t as bootstrap_exports } from "./bootstrap.js";
6
- import { t as branches_exports } from "./branches.js";
7
- import { t as bucket_exports } from "./bucket.js";
8
5
  import { t as env_exports } from "./env.js";
9
6
  import { t as config_exports } from "./config.js";
10
7
  import { t as projects_exports } from "./projects.js";
11
8
  import { t as link_exports } from "./link.js";
9
+ import { t as bootstrap_exports } from "./bootstrap.js";
10
+ import { t as branches_exports } from "./branches.js";
11
+ import { t as bucket_exports } from "./bucket.js";
12
12
  import { t as checkout_exports } from "./checkout.js";
13
13
  import { t as claim_exports } from "./claim.js";
14
14
  import { t as connection_string_exports } from "./connection_string.js";
@@ -1,21 +1,22 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { log } from "../log.js";
3
3
  import { contextBranch, readContextFile } from "../context.js";
4
+ import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
4
5
  import { getCliName } from "../utils/cli_name.js";
5
6
  import { deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
6
7
  import { CONFIG_INIT_NONE_MEANS, CONFIG_INIT_SERVICES, CONFIG_INIT_UNAVAILABLE } from "../config_template.js";
7
- import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
8
+ import { helpCsv, helpEpilogue } from "../utils/help_text.js";
8
9
  import { fetchTemplates } from "../init/bootstrap.js";
9
10
  import { initChildEnv, spawnCliChild } from "../init/child.js";
10
11
  import { agentSetupLabel, configSummaryLabel, formatInitDone, printInitBanner, printInitDone, shouldPrintInitBanner } from "../init/chrome.js";
12
+ import { hasNeonConfigFile } from "./config.js";
13
+ import { runAuthenticatedLink } from "../init/link.js";
11
14
  import { coerceAgentFlag } from "../utils/agent_flag.js";
12
- import { INIT_NEEDS_YES_OR_TERMINAL, assertNamedAgentTooling, directoryIsEmpty, initPluginAgents, initSkillsMcpAgents, linkInputArgv, planExistingInit, resolveNamedAgents } from "../init/plan.js";
15
+ import { INIT_NEEDS_YES_OR_TERMINAL, assertNamedAgentTooling, directoryIsEmpty, initPluginAgents, initSkillsMcpAgents, planExistingInit, resolveNamedAgents } from "../init/plan.js";
13
16
  import { canPickAgentsInteractively } from "../utils/agent_picker.js";
14
- import { pickInitConfigInteractively, pickInitTemplateInteractively } from "../init/wizard.js";
17
+ import { pickInitConfigInteractively, pickInitLinkInteractively, pickInitTemplateInteractively } from "../init/wizard.js";
15
18
  import { runAgentTooling, runInitSteps } from "../init/tooling.js";
16
- import { helpCsv, helpEpilogue } from "../utils/help_text.js";
17
19
  import { handler as handler$1 } from "./bootstrap.js";
18
- import { hasNeonConfigFile } from "./config.js";
19
20
  import { INIT_CONFIG_SERVICES_CONFLICT, INIT_TEMPLATE_KEEPS_CONFIG, configPlanFromResolution, resolveInitConfigChoice, resolveInitTemplateChoice, shouldRefreshEnvAfterNewConfig } from "../init/choices.js";
20
21
  import { existsSync, readdirSync } from "node:fs";
21
22
  import { resolve } from "node:path";
@@ -34,7 +35,7 @@ const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file
34
35
  alias: "y",
35
36
  type: "boolean",
36
37
  default: false,
37
- describe: "Empty dir: scaffold the default template. --skip-template: plugin, or skills and MCP, for project folders, else the host CLI agent. Exits if none. Then link --yes and config init --services none. link --yes still asks for a project unless one is already linked"
38
+ describe: "Empty dir: scaffold the default template. --skip-template: plugin, or skills and MCP, for project folders, else the host CLI agent. Exits if none. Then link with defaults and create the bare neon.ts policy. Project selection may still be required"
38
39
  }).option("skip-template", {
39
40
  type: "boolean",
40
41
  default: false,
@@ -42,6 +43,10 @@ const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file
42
43
  }).option("template", {
43
44
  type: "string",
44
45
  describe: "Template to scaffold into an empty directory. Conflicts with --skip-template"
46
+ }).option("link", {
47
+ type: "boolean",
48
+ default: true,
49
+ describe: "Link a Neon project during setup. Use --no-link to skip without being asked"
45
50
  }).option("config", {
46
51
  type: "boolean",
47
52
  describe: "Existing app or --skip-template: create neon.ts after linking. Use --no-config to skip. Omitted in a terminal: you will be asked. Scaffolding a template keeps that template's neon.ts"
@@ -80,7 +85,7 @@ const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file
80
85
  alias: "o",
81
86
  hidden: true,
82
87
  describe: "Not supported; the commands init runs print their own output"
83
- }).example("$0 init", "Set up this directory (template picker if empty)").example("$0 init --skip-template", "Empty dir: agents, link, and neon.ts — no template files").example("$0 init -y", "Default template, or existing-app defaults").example("$0 init --agent cursor --agent claude-code", "Skip agent selection; install the plugin for those agents").epilogue(helpEpilogue("Empty directory: pick a starter template, or skip scaffolding and only set up agents, a Neon project, and neon.ts. That skip is not on `neon bootstrap`.", "Interactive agent setup: plugin (recommended), skills and MCP separately, or skip agent setup. Never both plugin and skills+MCP.", "neon.ts is optional when you skip the template or set up an existing app. Saying no skips the services picker and does not write the file. Scaffolding a template keeps that template's neon.ts.", "-y installs the plugin when Cursor, Claude Code, or Codex is in project folders, else the host CLI agent. Otherwise skills and MCP. If none are found, it exits: pass --agent <name>, run from a supported agent, or omit -y in a terminal to pick. Then link unless already linked. link --yes may still ask for a project.", "--agent / -a is forwarded to plugins, or to skills and mcp, not both. It skips agent selection, including with -y.", helpCsv("Plugin agents", initPluginAgents()), helpCsv("Skills and MCP agents", initSkillsMcpAgents()))).check((argv) => {
88
+ }).example("$0 init", "Set up this directory (template picker if empty)").example("$0 init --skip-template", "Empty dir: agents, link, and neon.ts — no template files").example("$0 init -y", "Default template, or existing-app defaults").example("$0 init --agent cursor --agent claude-code", "Skip agent selection; install the plugin for those agents").epilogue(helpEpilogue("Empty directory: pick a starter template, or skip scaffolding and only set up agents, a Neon project, and neon.ts. That skip is not on `neon bootstrap`.", "Interactive agent setup: plugin (recommended), skills and MCP separately, or skip agent setup. Never both plugin and skills+MCP.", "neon.ts is optional when you skip the template or set up an existing app. Saying no skips the services picker and does not write the file. Scaffolding a template keeps that template's neon.ts.", "-y installs the plugin when Cursor, Claude Code, or Codex is in project folders, else the host CLI agent. Otherwise skills and MCP. If none are found, it exits: pass --agent <name>, run from a supported agent, or omit -y in a terminal to pick. Then link unless already linked. Project selection may still be required.", "--agent / -a is forwarded to plugins, or to skills and mcp, not both. It skips agent selection, including with -y.", helpCsv("Plugin agents", initPluginAgents()), helpCsv("Skills and MCP agents", initSkillsMcpAgents()))).check((argv) => {
84
89
  if (argv.data !== void 0) throw new Error(removedProtocol());
85
90
  if (argv.help !== true && (argv.output === "json" || argv.output === "yaml")) throw new Error(`\`${getCliName()} init\` does not support --output. The commands it runs print their own output.`);
86
91
  return true;
@@ -101,7 +106,7 @@ const parsedInitServices = (raw) => {
101
106
  onDeprecated: (used, canonical) => log.warning(deprecatedServiceMessage(used, canonical))
102
107
  });
103
108
  };
104
- const nestedBootstrapProps = (props, cwd, contextFile, template, linkExtra) => ({
109
+ const nestedBootstrapProps = (props, cwd, contextFile, template, linkInputs) => ({
105
110
  apiClient: props.apiClient,
106
111
  apiKey: props.apiKey,
107
112
  apiHost: props.apiHost,
@@ -113,19 +118,25 @@ const nestedBootstrapProps = (props, cwd, contextFile, template, linkExtra) => (
113
118
  default: template.useDefault,
114
119
  install: true,
115
120
  git: true,
116
- link: true,
121
+ link: props.link !== false,
117
122
  printBanner: false,
118
123
  skipDoneSummary: true,
119
124
  linkNoConfig: true,
120
125
  narrate: "human",
121
126
  ...template.selected ? { selectedTemplate: template.selected } : {},
122
127
  ...!template.selected && template.id !== void 0 ? { template: template.id } : {},
123
- ...linkExtra.length > 0 ? { linkExtra } : {},
128
+ linkInputs,
124
129
  ...props.agent !== void 0 ? { agent: props.agent } : {},
125
130
  ...props.configDir ? { configDir: props.configDir } : {},
126
131
  ...props.profile ? { profile: props.profile } : {},
132
+ ...props.oauthHost ? { oauthHost: props.oauthHost } : {},
133
+ ...props.clientId ? { clientId: props.clientId } : {},
134
+ ...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
135
+ ...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {},
127
136
  ...props.analytics === false ? { analytics: false } : {},
128
137
  ...props.run ? { run: props.run } : {},
138
+ ...props.linkProject ? { linkProject: props.linkProject } : {},
139
+ ...props.pickLink ? { pickLink: props.pickLink } : {},
129
140
  ...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
130
141
  ...props.detectProjectAgents ? { detectProjectAgents: props.detectProjectAgents } : {},
131
142
  ...props.detectAgent ? { detectAgent: props.detectAgent } : {},
@@ -193,13 +204,14 @@ const handler = async (props) => {
193
204
  };
194
205
  const services = parsedInitServices(props.services);
195
206
  if (props.config === false && services !== void 0) throw new Error(INIT_CONFIG_SERVICES_CONFLICT);
196
- const linkExtra = linkInputArgv({
207
+ const linkInputs = {
197
208
  ...props.orgId ? { orgId: props.orgId } : {},
198
209
  ...props.projectId ? { projectId: props.projectId } : {},
199
210
  ...props.projectName ? { projectName: props.projectName } : {},
200
211
  ...props.regionId ? { regionId: props.regionId } : {},
201
212
  ...props.branch ? { branch: props.branch } : {}
202
- });
213
+ };
214
+ const hasExplicitLinkInputs = props.orgId !== void 0 || props.projectId !== void 0 || props.projectName !== void 0 || props.regionId !== void 0 || props.branch !== void 0;
203
215
  const templateChoice = resolveInitTemplateChoice({
204
216
  empty: directoryIsEmpty(names),
205
217
  yes,
@@ -214,7 +226,7 @@ const handler = async (props) => {
214
226
  const result = await runBootstrap(nestedBootstrapProps(props, cwd, contextFile, {
215
227
  useDefault: yes,
216
228
  ...templateChoice.kind === "template" ? { id: templateChoice.id } : {}
217
- }, linkExtra));
229
+ }, linkInputs));
218
230
  printNestedBootstrapDone(result, cwd, templateChoice.kind === "template" ? templateChoice.id : "default");
219
231
  return;
220
232
  }
@@ -228,13 +240,13 @@ const handler = async (props) => {
228
240
  const result = await runBootstrap(nestedBootstrapProps(props, cwd, contextFile, {
229
241
  selected: picked.template,
230
242
  useDefault: false
231
- }, linkExtra));
243
+ }, linkInputs));
232
244
  printNestedBootstrapDone(result, cwd, picked.template.id);
233
245
  return;
234
246
  }
235
247
  }
236
248
  const alreadyLinked = isLinked(contextFile);
237
- const shouldLink = !alreadyLinked || linkExtra.length > 0;
249
+ const shouldLink = props.link !== false && (!alreadyLinked || hasExplicitLinkInputs);
238
250
  const existingConfig = hasNeonConfigFile(cwd);
239
251
  const canAskConfig = props.pickConfig !== void 0 || canPickAgentsInteractively();
240
252
  const agentSetup = await runAgentTooling({
@@ -251,18 +263,27 @@ const handler = async (props) => {
251
263
  ...props.detectAgent ? { detectAgent: props.detectAgent } : {},
252
264
  ...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {}
253
265
  });
254
- await runInitSteps(planExistingInit({
255
- linked: !shouldLink,
266
+ const canAskLink = props.pickLink !== void 0 || canPickAgentsInteractively();
267
+ const acceptedLink = shouldLink && (yes || !canAskLink || await (props.pickLink ?? pickInitLinkInteractively)());
268
+ if (acceptedLink) await (props.linkProject ?? runAuthenticatedLink)({
269
+ apiClient: props.apiClient,
270
+ apiKey: props.apiKey,
271
+ apiHost: props.apiHost,
272
+ output: props.output,
273
+ contextFile,
256
274
  yes,
257
- agentSetup: "skip",
258
- config: { kind: "skip" },
259
- ...linkExtra.length > 0 ? { linkExtra } : {}
260
- }), {
275
+ clear: false,
276
+ checks: true,
277
+ envPull: true,
278
+ config: false,
261
279
  cwd,
262
- run,
263
- forward,
264
- authEnv,
265
- narrate: "human"
280
+ ...linkInputs,
281
+ ...props.configDir ? { configDir: props.configDir } : {},
282
+ ...props.profile ? { profile: props.profile } : {},
283
+ ...props.oauthHost ? { oauthHost: props.oauthHost } : {},
284
+ ...props.clientId ? { clientId: props.clientId } : {},
285
+ ...props.forceAuth !== void 0 ? { forceAuth: props.forceAuth } : {},
286
+ ...props.allowUnsafeTls !== void 0 ? { allowUnsafeTls: props.allowUnsafeTls } : {}
266
287
  });
267
288
  const configResolution = resolveInitConfigChoice({
268
289
  flag: props.config,
@@ -317,7 +338,7 @@ Run \`${getCliName()} env pull\` once resolved.`, message);
317
338
  label: "Project",
318
339
  value: projectRow({
319
340
  alreadyLinked,
320
- linkedNow: shouldLink
341
+ linkedNow: acceptedLink
321
342
  })
322
343
  },
323
344
  {
@@ -2,8 +2,8 @@ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { log } from "../log.js";
3
3
  import { getCliName } from "../utils/cli_name.js";
4
4
  import { writer } from "../writer.js";
5
- import { projectUpdateRequest } from "../parameters.gen.js";
6
5
  import { fillSingleProject } from "../utils/enrichers.js";
6
+ import { projectUpdateRequest } from "../parameters.gen.js";
7
7
  //#region src/commands/ip_allow.ts
8
8
  var ip_allow_exports = /* @__PURE__ */ __exportAll({
9
9
  builder: () => builder,
@@ -16,6 +16,7 @@ var link_exports = /* @__PURE__ */ __exportAll({
16
16
  command: () => command,
17
17
  describe: () => describe,
18
18
  handler: () => handler,
19
+ runLink: () => runLink,
19
20
  shouldOfferConfigInit: () => shouldOfferConfigInit
20
21
  });
21
22
  const PROJECTS_LIST_LIMIT = 100;
@@ -94,7 +95,7 @@ const builder = (argv) => argv.usage("$0 link [options]").options({
94
95
  ["$0 link --no-checks --org-id org-… --project-id polished-snowflake-12345678", "Write the context offline (no API calls, no verification)"],
95
96
  ["$0 link --clear", "Forget the current org/project/branch context"]
96
97
  ]).epilogue(helpEpilogue("Non-interactive (CI, scripts, agents):", ...nonInteractiveLinkCommands().map((command) => ` ${command}`), orgScopedKeyHint)).strict();
97
- const handler = async (props) => {
98
+ const runLink = async (props) => {
98
99
  if (props.clear) {
99
100
  clearContext(props.contextFile);
100
101
  return;
@@ -110,19 +111,16 @@ const handler = async (props) => {
110
111
  await runNonInteractive(props, inputs, existing);
111
112
  return;
112
113
  }
113
- if (!canPromptInteractively()) {
114
- log.error([
115
- "Missing inputs and no interactive terminal for prompts.",
116
- "",
117
- "Use:",
118
- nonInteractiveLinkHelp(),
119
- orgScopedKeyHint
120
- ].join("\n"));
121
- process.exit(1);
122
- return;
123
- }
114
+ if (!canPromptInteractively()) throw new LinkInputError([
115
+ "Missing inputs and no interactive terminal for prompts.",
116
+ "",
117
+ "Use:",
118
+ nonInteractiveLinkHelp(),
119
+ orgScopedKeyHint
120
+ ].join("\n"));
124
121
  await runInteractive(props, inputs);
125
122
  };
123
+ const handler = runLink;
126
124
  const parseInputs = (props) => {
127
125
  let fromParams = {};
128
126
  if (props.params !== void 0 && props.params !== "") {
@@ -430,7 +428,9 @@ const runNonInteractive = async (props, inputs, existing) => {
430
428
  }
431
429
  };
432
430
  const runInteractive = async (props, inputs) => {
433
- if (!props.yes) await confirmRelinkIfNeeded(props);
431
+ if (!props.yes) {
432
+ if (!await confirmRelinkIfNeeded(props)) return;
433
+ }
434
434
  const orgResolution = await resolveOrg(props, inputs.orgId);
435
435
  let orgId;
436
436
  if (orgResolution.kind === "resolved") {
@@ -505,7 +505,7 @@ const runInteractive = async (props, inputs) => {
505
505
  };
506
506
  const confirmRelinkIfNeeded = async (props) => {
507
507
  const existing = readContextFile(props.contextFile);
508
- if (!existing.orgId || !existing.projectId) return;
508
+ if (!existing.orgId || !existing.projectId) return true;
509
509
  const { proceed } = await prompts({
510
510
  onState: onPromptState,
511
511
  type: "confirm",
@@ -515,8 +515,9 @@ const confirmRelinkIfNeeded = async (props) => {
515
515
  });
516
516
  if (!proceed) {
517
517
  process.stdout.write("Aborted. Existing link preserved.\n");
518
- process.exit(0);
518
+ return false;
519
519
  }
520
+ return true;
520
521
  };
521
522
  const promptOrgFromList = async (orgs) => {
522
523
  if (!orgs.length) throw new Error(`You don't belong to any organizations. Create one in the Neon Console first: https://console.neon.tech/`);
@@ -734,6 +735,7 @@ const finalizeLink = async (props, summary) => {
734
735
  const { config: _offerConfig, ...rest } = props;
735
736
  await autoPullEnvAfterPin({
736
737
  ...rest,
738
+ ...props.cwd ? { cwd: props.cwd } : {},
737
739
  projectId: summary.projectId,
738
740
  branch: summary.branch,
739
741
  envPull: props.envPull
@@ -756,7 +758,7 @@ const finalizeInteractiveLink = async (props, summary) => {
756
758
  */
757
759
  const shouldOfferConfigInit = (input) => !input.hasConfig && input.offer;
758
760
  const maybeOfferConfigInit = async (props, summary) => {
759
- const cwd = process.cwd();
761
+ const cwd = props.cwd ?? process.cwd();
760
762
  if (!shouldOfferConfigInit({
761
763
  hasConfig: hasNeonConfigFile(cwd),
762
764
  offer: props.config !== false
@@ -777,6 +779,7 @@ const maybeOfferConfigInit = async (props, summary) => {
777
779
  const { config: _offerConfig, ...rest } = props;
778
780
  await autoPullEnvAfterPin({
779
781
  ...rest,
782
+ cwd,
780
783
  projectId: summary.projectId,
781
784
  branch: summary.branch,
782
785
  envPull: props.envPull
@@ -795,4 +798,4 @@ const mustString = (value, name) => {
795
798
  return value;
796
799
  };
797
800
  //#endregion
798
- export { builder, command, describe, handler, shouldOfferConfigInit, link_exports as t };
801
+ export { builder, command, describe, handler, runLink, shouldOfferConfigInit, link_exports as t };
@@ -2,12 +2,12 @@ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { log } from "../log.js";
3
3
  import { readContextFile } from "../context.js";
4
4
  import { getCliName } from "../utils/cli_name.js";
5
+ import { helpCsv, helpEpilogue } from "../utils/help_text.js";
5
6
  import { writer } from "../writer.js";
6
7
  import { noPassthrough, single } from "../utils/flags.js";
7
8
  import { NEON_MCP_CATEGORIES, NEON_MCP_URL, existingNeonApiKey, installNeonMcpServer, neonMcpUrl, parseMcpCategories, trackedProjectMcpConfig } from "../mcp/install.js";
8
9
  import { mcpInstallableAgents, resolveInstallTargets } from "../mcp/targets.js";
9
10
  import { canPickAgentsInteractively } from "../utils/agent_picker.js";
10
- import { helpCsv, helpEpilogue } from "../utils/help_text.js";
11
11
  import { mintMcpApiKey, mintedKeyRevokeCommand, withdrawMintedKey } from "../mcp/mint.js";
12
12
  import { confirmMcpInstall } from "../mcp/wizard.js";
13
13
  import { resolveMcpPlan } from "../mcp/plan.js";
@@ -1,5 +1,6 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { log } from "../log.js";
3
+ import { helpCsv, helpEpilogue } from "../utils/help_text.js";
3
4
  import { writer } from "../writer.js";
4
5
  import { noPassthrough } from "../utils/flags.js";
5
6
  import { getAgentDisplayName } from "../mcp/agents.js";
@@ -8,7 +9,6 @@ import "../init/agents.js";
8
9
  import { pluginsInstallableAgents } from "../plugins/targets.js";
9
10
  import { canPickAgentsInteractively } from "../utils/agent_picker.js";
10
11
  import { NEON_PLUGIN_NAME, PLUGIN_SKILLS, PLUGIN_SOURCE, neonPluginsRetryCommand, pluginsAddArgs, runPluginsCli } from "../plugins/run.js";
11
- import { helpCsv, helpEpilogue } from "../utils/help_text.js";
12
12
  import { resolvePluginsPlan } from "../plugins/plan.js";
13
13
  import { confirmPluginsInstall } from "../plugins/wizard.js";
14
14
  //#region src/commands/plugins.ts
@@ -1,12 +1,12 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { log } from "../log.js";
3
+ import { helpCsv, helpEpilogue } from "../utils/help_text.js";
3
4
  import { writer } from "../writer.js";
4
5
  import { noPassthrough } from "../utils/flags.js";
5
6
  import { getAgentDisplayName } from "../mcp/agents.js";
6
7
  import "../init/agents.js";
7
8
  import { mappedSkillsAgentNames, skillsInstallableAgents } from "../skills/targets.js";
8
9
  import { canPickAgentsInteractively } from "../utils/agent_picker.js";
9
- import { helpCsv, helpEpilogue } from "../utils/help_text.js";
10
10
  import { skillsHelpValues, skillsYesHelp } from "../skills/catalog.js";
11
11
  import { confirmSkillsInstall, confirmSkillsUpdate } from "../skills/wizard.js";
12
12
  import { assertSkillsCanRun, resolveSkillsPlan } from "../skills/plan.js";
package/dist/dev/env.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as resolveDevEnv, i as dropClaimableUnsupported, n as MissingBranchContextError, o as resolveNeonEnvVars, r as ServiceNotOnBranchError, t as DevEnvMismatchError } from "../_chunks/env-DsakkOVZ.js";
1
+ import { a as resolveDevEnv, i as dropClaimableUnsupported, n as MissingBranchContextError, o as resolveNeonEnvVars, r as ServiceNotOnBranchError, t as DevEnvMismatchError } from "../_chunks/env-B75xufe_.js";
2
2
  export { DevEnvMismatchError, MissingBranchContextError, ServiceNotOnBranchError, dropClaimableUnsupported, resolveDevEnv, resolveNeonEnvVars };
@@ -1,2 +1,2 @@
1
- import { a as envKeysForSelection, c as ownedEnvServiceKeys, d as servicesForEnvKeys, i as ENV_PULL_SERVICES, l as parseEnvPullKeys, n as ENV_PULL_KEYS, o as envPullFlagValue, r as ENV_PULL_KEY_HELP, s as envServiceKeys, t as BRANCH_ENV_KEY, u as serviceForEnvKey } from "./_chunks/env_services-CAWrZTWa.js";
1
+ import { a as envKeysForSelection, c as ownedEnvServiceKeys, d as servicesForEnvKeys, i as ENV_PULL_SERVICES, l as parseEnvPullKeys, n as ENV_PULL_KEYS, o as envPullFlagValue, r as ENV_PULL_KEY_HELP, s as envServiceKeys, t as BRANCH_ENV_KEY, u as serviceForEnvKey } from "./_chunks/env_services-CbARRnq9.js";
2
2
  export { BRANCH_ENV_KEY, ENV_PULL_KEYS, ENV_PULL_KEY_HELP, ENV_PULL_SERVICES, envKeysForSelection, envPullFlagValue, envServiceKeys, ownedEnvServiceKeys, parseEnvPullKeys, serviceForEnvKey, servicesForEnvKeys };