neon 8.9.0 → 8.10.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.
package/README.md CHANGED
@@ -1005,6 +1005,18 @@ neon feedback --message "The branch docs were unclear" --output json
1005
1005
 
1006
1006
  Default table output is `Feedback received. Thank you!`. `--output json` and `--output yaml` print `{ "received": true }`. Messages are limited to 10,000 characters. Do not include passwords, API keys, connection strings, or other secrets.
1007
1007
 
1008
+ ## Search neon.com (`docs search`)
1009
+
1010
+ `neon docs search <query>` searches neon.com: the docs, API reference, PostgreSQL tutorial, guides, FAQs, changelog, and blog. It does not log in and does not use your Neon account. It uses the same search as the neon.com search box. The search service records the query, the CLI version, and the detected coding agent; `--no-analytics` turns that off.
1011
+
1012
+ ```bash
1013
+ neon docs search "reset a branch"
1014
+ neon docs search "create branch" --category api --category docs
1015
+ neon docs search "pg_dump" --limit 5 --output json
1016
+ ```
1017
+
1018
+ `--category` limits results to `docs`, `api`, `postgresql`, `guides`, `faqs`, `blog`, or `changelog`; repeat it for more than one. `--limit` is 1 to 40, default 10. Default table output is one row per page: title, the section that matched, and URL. `--output json` and `--output yaml` print an array of hits with `title`, `url`, `category`, `breadcrumb`, `heading`, and `excerpt`, in rank order. No match prints `No results for "<query>".` and exits 0 (`[]` in JSON).
1019
+
1008
1020
  ## Install the Neon plugin (`plugins`)
1009
1021
 
1010
1022
  `neon plugins` installs the Neon agent plugin (`neon-postgres`) by running `npx plugins add`. It does not call the Neon API.
@@ -1531,7 +1543,7 @@ Global options are supported with any Neon CLI command.
1531
1543
 
1532
1544
  - <a id="analytics"></a>`--analytics`
1533
1545
 
1534
- Analytics are enabled by default to gather information about the CLI commands and options that are used by our customers. This data collection assists in offering support, and allows for a better understanding of typical usage patterns so that we can improve user experience. Neon does not collect user-defined data, such as project IDs or command payloads, except the question passed to `neon ask --prompt`. When the CLI fails on an internal bug (a JavaScript `TypeError`, `ReferenceError`, `RangeError`, or `SyntaxError`), it also sends that error to Neon's Sentry: the error type, the message only when it is a standard JavaScript message that names nothing but code (any other message is replaced), the stack trace with file paths cut to the package (`neon/dist/...`), the CLI and Node.js versions, and whether it ran in CI or a coding agent. To opt-out of analytics data collection and error reports, specify `--no-analytics` or `--analytics false`.
1546
+ Analytics are enabled by default to gather information about the CLI commands and options that are used by our customers. This data collection assists in offering support, and allows for a better understanding of typical usage patterns so that we can improve user experience. Neon does not collect user-defined data, such as project IDs or command payloads, except the question passed to `neon ask --prompt` and the query passed to `neon docs search`. The Neon search service records each `neon docs search` query with the CLI version and the detected coding agent; with `--no-analytics` it records nothing. When the CLI fails on an internal bug (a JavaScript `TypeError`, `ReferenceError`, `RangeError`, or `SyntaxError`), it also sends that error to Neon's Sentry: the error type, the message only when it is a standard JavaScript message that names nothing but code (any other message is replaced), the stack trace with file paths cut to the package (`neon/dist/...`), the CLI and Node.js versions, and whether it ran in CI or a coding agent. To opt-out of analytics data collection and error reports, specify `--no-analytics` or `--analytics false`.
1535
1547
 
1536
1548
  - <a id="version"></a>`-v, --version`
1537
1549
 
@@ -200,6 +200,12 @@ var KeyringUnavailableError = class extends Error {
200
200
  this.name = "KeyringUnavailableError";
201
201
  }
202
202
  };
203
+ var KeyringValueTooLargeError = class extends Error {
204
+ constructor(profile, actual, maximum) {
205
+ super(`Credentials for profile "${profile}" are ${actual} characters, and this OS keyring stores at most ${maximum}. Run \`neon login --profile ${profile}\` without \`--keyring\` to keep the credential in an owner-only file. If this profile already uses the keyring, remove it first with \`neon profile remove ${profile} --yes\`.`);
206
+ this.name = "KeyringValueTooLargeError";
207
+ }
208
+ };
203
209
  var KeyringUnreadableError = class extends Error {
204
210
  constructor(profile) {
205
211
  const replace = `\`neon login --profile ${profile}\``;
@@ -248,8 +254,10 @@ const createCredentialStore = (dir, options = {}) => {
248
254
  } catch {
249
255
  previous = null;
250
256
  }
257
+ const serialized = JSON.stringify(credentials);
258
+ if (kr.maxPasswordCodeUnits !== void 0 && serialized.length > kr.maxPasswordCodeUnits) throw new KeyringValueTooLargeError(profile, serialized.length, kr.maxPasswordCodeUnits);
251
259
  try {
252
- kr.set(KEYRING_SERVICE, account, JSON.stringify(credentials));
260
+ kr.set(KEYRING_SERVICE, account, serialized);
253
261
  } catch {
254
262
  throw new KeyringUnavailableError();
255
263
  }
package/dist/analytics.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as OAUTH, t as storeFor } from "./_chunks/credential_io-ByNuojoE.js";
1
+ import { a as OAUTH, t as storeFor } from "./_chunks/credential_io-BS-0Pmwc.js";
2
2
  import { getCliAgent, getGithubEnvVars, isCi } from "./env.js";
3
3
  import { log } from "./log.js";
4
4
  import { getOrCreateAnonymousId } from "./anonymous_id.js";
package/dist/auth.js CHANGED
@@ -221,4 +221,4 @@ const auth = async ({ oauthHost, clientId, allowUnsafeTls }) => {
221
221
  });
222
222
  };
223
223
  //#endregion
224
- export { AuthRefreshError, auth, classifyRefreshFailure, defaultClientID, refreshToken, revokeToken };
224
+ export { AuthRefreshError, NEONCTL_SCOPES, auth, classifyRefreshFailure, defaultClientID, refreshToken, revokeToken };
@@ -1,4 +1,4 @@
1
- import { t as storeFor } from "./_chunks/credential_io-ByNuojoE.js";
1
+ import { t as storeFor } from "./_chunks/credential_io-BS-0Pmwc.js";
2
2
  import { log } from "./log.js";
3
3
  import { authFailureMessage, getAuthContext, locationFromContext } from "./auth_context.js";
4
4
  import { AuthRefreshError } from "./auth.js";
@@ -1,12 +1,12 @@
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-ByNuojoE.js";
2
+ import { a as OAUTH, i as API_KEY, l as interpretCredentials, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-BS-0Pmwc.js";
3
3
  import { isCi } from "../env.js";
4
4
  import { log } from "../log.js";
5
5
  import { withExclusiveLock } from "../refresh_lock.js";
6
6
  import { getApiClient, isNeonApiError } from "../api.js";
7
7
  import { setAuthContext } from "../auth_context.js";
8
8
  import { a as isOwnedCredentialPath } from "../_chunks/paths-DMq0Lt7a.js";
9
- import { currentContextFile, isAskCommand, isClaimCommand, isConfigAdd, isConfigInit, isCurrentBranchProbe, isFeedbackCommand, isGitLocalCommand, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, isUnfollowedGitHookSync, readContextFile } from "../context.js";
9
+ import { currentContextFile, isAskCommand, isClaimCommand, isConfigAdd, isConfigInit, isCurrentBranchProbe, isDocsCommand, isFeedbackCommand, isGitLocalCommand, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, isUnfollowedGitHookSync, readContextFile } from "../context.js";
10
10
  import { _ as upsertProfile, d as newProfileLocation, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, t as DEFAULT_PROFILE, u as locationOf } from "../_chunks/profiles-DIB1av2Q.js";
11
11
  import { extendTokenSet } from "../utils/auth.js";
12
12
  import { AuthRefreshError, auth, classifyRefreshFailure, refreshToken } from "../auth.js";
@@ -136,20 +136,16 @@ const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceAuth, "f
136
136
  return tokenSet.access_token || "";
137
137
  };
138
138
  /**
139
- * Persist before identity lookup so a lookup failure cannot discard the new session.
140
- * Merge the identity only while that session remains current on disk.
139
+ * One write, after the lookup: a second write that adds `user_id` could fail the keyring size
140
+ * check after the first had already replaced the previous credential.
141
141
  */
142
142
  const persistNewSession = async (at, credentials, apiClient, configDir) => {
143
- const stored = oauthCredentialsFromTokenSet(credentials, null);
144
- writeOAuthCredentials(configDir, at, stored);
145
143
  const identity = await fetchIdentity(apiClient);
146
- if (identity.id) {
147
- const current = storeFor(configDir).read(at);
148
- if (current !== null && current.credentials.access_token === stored.access_token) writeOAuthCredentials(configDir, at, {
149
- ...current.credentials,
150
- user_id: identity.id
151
- });
152
- }
144
+ const stored = oauthCredentialsFromTokenSet(credentials, null);
145
+ writeOAuthCredentials(configDir, at, identity.id ? {
146
+ ...stored,
147
+ user_id: identity.id
148
+ } : stored);
153
149
  return identity;
154
150
  };
155
151
  const fetchIdentity = async (apiClient) => {
@@ -248,8 +244,8 @@ const refreshStoredCredentials = async (at, props) => {
248
244
  const oauthCredentialsFromTokenSet = (tokenSet, previous) => {
249
245
  const refreshTokenValue = typeof tokenSet.refresh_token === "string" && tokenSet.refresh_token !== "" ? tokenSet.refresh_token : previous?.refresh_token;
250
246
  const stored = {
251
- ...tokenSet,
252
247
  type: OAUTH,
248
+ ...typeof tokenSet.access_token === "string" && tokenSet.access_token !== "" ? { access_token: tokenSet.access_token } : {},
253
249
  expires_at: tokenSet.expires_at,
254
250
  ...typeof refreshTokenValue === "string" && refreshTokenValue !== "" ? { refresh_token: refreshTokenValue } : {}
255
251
  };
@@ -304,7 +300,7 @@ const ensureAuth = async (props) => {
304
300
  if (isInspectDbUrl(props)) return;
305
301
  if (props._[0] === "open") return;
306
302
  if (isSkillsCommand(props) || isPluginsCommand(props)) return;
307
- if (isAskCommand(props) || isFeedbackCommand(props)) return;
303
+ if (isAskCommand(props) || isFeedbackCommand(props) || isDocsCommand(props)) return;
308
304
  if (props._[0] === "init") {
309
305
  selectCredential({
310
306
  ...credentialInputs(),
@@ -4,12 +4,12 @@ import { isClaimableEnvTarget } from "../claimable/state.js";
4
4
  import { getCliName } from "../utils/cli_name.js";
5
5
  import { defaultDevEnvFiles, readEnvFile, resolveEnvFilePath } from "../env_file.js";
6
6
  import { l as functionBaseUrlKey } from "../_chunks/env-BpP3Dqpe.js";
7
- import { a as resolveDevEnv } from "../_chunks/env-BNm69Qh8.js";
8
- import { findConfigFunctionBySource, resolveFunctionsFromConfig } from "../dev/functions.js";
9
- import { resolveWatchInputs } from "../dev/inputs.js";
10
7
  import { branchIdResolve } from "../utils/enrichers.js";
11
8
  import { bundleEntry } from "../utils/esbuild.js";
9
+ import { a as resolveDevEnv } from "../_chunks/env-BNm69Qh8.js";
12
10
  import { envFlag } from "./config.js";
11
+ import { findConfigFunctionBySource, resolveFunctionsFromConfig } from "../dev/functions.js";
12
+ import { resolveWatchInputs } from "../dev/inputs.js";
13
13
  import { existsSync, mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
14
14
  import { basename, dirname, join, relative, resolve } from "node:path";
15
15
  import { fileURLToPath } from "node:url";
@@ -0,0 +1,174 @@
1
+ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
+ import { getCliAgent } from "../env.js";
3
+ import pkg_default from "../pkg.js";
4
+ import { isNetworkError } from "../errors.js";
5
+ import { writer } from "../writer.js";
6
+ import { single } from "../utils/flags.js";
7
+ //#region src/commands/docs.ts
8
+ var docs_exports = /* @__PURE__ */ __exportAll({
9
+ DEFAULT_SITE_SEARCH_URL: () => DEFAULT_SITE_SEARCH_URL,
10
+ MAX_LIMIT: () => 40,
11
+ MAX_QUERY_LENGTH: () => 500,
12
+ SITE_SEARCH_CATEGORIES: () => SITE_SEARCH_CATEGORIES,
13
+ builder: () => builder,
14
+ command: () => command,
15
+ describe: () => describe,
16
+ parseSearchHits: () => parseSearchHits,
17
+ resolveSiteSearchUrl: () => resolveSiteSearchUrl,
18
+ siteSearchHeaders: () => siteSearchHeaders
19
+ });
20
+ const DEFAULT_SITE_SEARCH_URL = "https://br-frosty-cell-a5smzg39-assistant.compute.c-1.us-east-2.aws.neon.tech/site-search";
21
+ const SITE_SEARCH_CATEGORIES = [
22
+ "docs",
23
+ "api",
24
+ "postgresql",
25
+ "guides",
26
+ "faqs",
27
+ "blog",
28
+ "changelog"
29
+ ];
30
+ const MAX_QUERY_LENGTH = 500;
31
+ const MAX_LIMIT = 40;
32
+ const DEFAULT_LIMIT = 10;
33
+ const SEARCH_TIMEOUT_MS = 1e4;
34
+ const command = "docs";
35
+ const describe = "Search neon.com docs, guides, and more";
36
+ const SEARCH_DESCRIBE = "Search neon.com: docs, API reference, PostgreSQL tutorial, guides, FAQs, changelog, and blog";
37
+ const builder = (argv) => argv.usage("$0 docs <sub-command> [options]").command("search <query>", SEARCH_DESCRIBE, (yargs) => yargs.positional("query", {
38
+ describe: "What to search for",
39
+ type: "string"
40
+ }).options({
41
+ category: {
42
+ describe: "Only return pages in these categories (repeatable)",
43
+ type: "string",
44
+ array: true,
45
+ choices: SITE_SEARCH_CATEGORIES
46
+ },
47
+ limit: {
48
+ describe: `Maximum number of results (1-40)`,
49
+ type: "number",
50
+ default: DEFAULT_LIMIT
51
+ },
52
+ url: {
53
+ describe: "Override the site search URL",
54
+ type: "string",
55
+ hidden: true,
56
+ coerce: single("url")
57
+ }
58
+ }).strict().check((args) => {
59
+ const query = typeof args.query === "string" ? args.query.trim() : "";
60
+ if (query === "") throw new Error("<query> needs a value");
61
+ if (query.length > 500) throw new Error(`<query> must be 500 characters or fewer`);
62
+ const limit = args.limit;
63
+ if (!Number.isInteger(limit) || limit < 1 || limit > 40) throw new Error(`--limit must be a whole number from 1 to 40`);
64
+ return true;
65
+ }).example("$0 docs search \"reset a branch\"", SEARCH_DESCRIBE).example("$0 docs search \"create branch\" --category api --category docs", "Search only the API reference and the docs"), (args) => search(args));
66
+ function resolveSiteSearchUrl(opts) {
67
+ const fromFlag = opts.url?.trim();
68
+ if (fromFlag) return fromFlag;
69
+ const fromEnv = opts.envUrl?.trim();
70
+ if (fromEnv) return fromEnv;
71
+ return DEFAULT_SITE_SEARCH_URL;
72
+ }
73
+ const search = async (props) => {
74
+ const query = props.query.trim();
75
+ const hits = await searchSite({
76
+ url: resolveSiteSearchUrl({
77
+ url: props.url,
78
+ envUrl: process.env.NEON_SITE_SEARCH_URL
79
+ }),
80
+ query,
81
+ limit: props.limit,
82
+ categories: props.category ? [...new Set(props.category)] : void 0,
83
+ headers: siteSearchHeaders({
84
+ analytics: props.analytics,
85
+ agent: getCliAgent(process.env),
86
+ version: pkg_default.version
87
+ })
88
+ });
89
+ writer(props).end(hits, {
90
+ fields: [
91
+ "title",
92
+ "heading",
93
+ "url"
94
+ ],
95
+ emptyMessage: `No results for "${query}".`,
96
+ renderColumns: { heading: (hit) => sectionOf(hit) }
97
+ });
98
+ };
99
+ function sectionOf(hit) {
100
+ const parts = hit.heading.split(" > ");
101
+ return (parts[0] === hit.title ? parts.slice(1) : parts).join(" > ");
102
+ }
103
+ function siteSearchHeaders(opts) {
104
+ return {
105
+ "Content-Type": "application/json",
106
+ "User-Agent": `neon/${opts.version}`,
107
+ "x-neon-source": "cli",
108
+ ...opts.agent ? { "x-neon-client": encodeURIComponent(opts.agent) } : {},
109
+ ...opts.analytics ? {} : { "x-neon-analytics": "off" }
110
+ };
111
+ }
112
+ function isTimeout(error) {
113
+ return error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError");
114
+ }
115
+ async function searchSite(opts) {
116
+ const signal = AbortSignal.timeout(SEARCH_TIMEOUT_MS);
117
+ try {
118
+ const response = await fetch(opts.url, {
119
+ method: "POST",
120
+ headers: opts.headers,
121
+ body: JSON.stringify({
122
+ query: opts.query,
123
+ limit: opts.limit,
124
+ ...opts.categories ? { categories: opts.categories } : {}
125
+ }),
126
+ signal
127
+ });
128
+ const raw = await response.text();
129
+ if (!response.ok) throw new Error(searchErrorMessage(raw, response.status));
130
+ return parseSearchHits(raw);
131
+ } catch (error) {
132
+ if (isTimeout(error)) throw new Error("Neon site search did not respond in time.");
133
+ if (isNetworkError(error)) throw new Error("Could not reach Neon site search. Check your internet connection and try again.");
134
+ throw error;
135
+ }
136
+ }
137
+ function parseJson(raw) {
138
+ try {
139
+ return JSON.parse(raw);
140
+ } catch {
141
+ return;
142
+ }
143
+ }
144
+ function searchErrorMessage(raw, status) {
145
+ const body = parseJson(raw);
146
+ if (status < 500 && typeof body === "object" && body !== null && "error" in body && typeof body.error === "string" && body.error.trim() !== "") return body.error;
147
+ return `Neon site search failed (HTTP ${status}).`;
148
+ }
149
+ const UNEXPECTED_RESPONSE = "Neon site search returned an unexpected response.";
150
+ function stringField(fields, key) {
151
+ const field = fields[key];
152
+ if (typeof field !== "string") throw new Error(UNEXPECTED_RESPONSE);
153
+ return field;
154
+ }
155
+ function parseSearchHits(raw) {
156
+ const body = parseJson(raw);
157
+ if (typeof body !== "object" || body === null || !("hits" in body) || !Array.isArray(body.hits)) throw new Error(UNEXPECTED_RESPONSE);
158
+ return body.hits.map((hit) => {
159
+ if (typeof hit !== "object" || hit === null) throw new Error(UNEXPECTED_RESPONSE);
160
+ const fields = Object.fromEntries(Object.entries(hit));
161
+ const breadcrumb = fields.breadcrumb;
162
+ if (!Array.isArray(breadcrumb) || !breadcrumb.every((part) => typeof part === "string")) throw new Error(UNEXPECTED_RESPONSE);
163
+ return {
164
+ title: stringField(fields, "title"),
165
+ url: stringField(fields, "url"),
166
+ category: stringField(fields, "category"),
167
+ breadcrumb,
168
+ heading: stringField(fields, "heading"),
169
+ excerpt: stringField(fields, "excerpt")
170
+ };
171
+ });
172
+ }
173
+ //#endregion
174
+ export { DEFAULT_SITE_SEARCH_URL, MAX_LIMIT, MAX_QUERY_LENGTH, SITE_SEARCH_CATEGORIES, builder, command, describe, parseSearchHits, resolveSiteSearchUrl, siteSearchHeaders, docs_exports as t };
@@ -7,9 +7,9 @@ import { deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOpt
7
7
  import { mergeEnvFile, readEnvFile, resolveEnvFilePath } from "../env_file.js";
8
8
  import { t as NEON_ENV_VAR_KEYS, u as isFunctionBaseUrlKey } from "../_chunks/env-BpP3Dqpe.js";
9
9
  import { ENV_PULL_KEY_HELP, ENV_PULL_SERVICES, envKeysForSelection, envPullFlagValue, ownedEnvServiceKeys, parseEnvPullKeys } from "../env_services.js";
10
- import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-BNm69Qh8.js";
11
10
  import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
12
11
  import { warnAiGateway } from "../utils/ai_gateway_notice.js";
12
+ import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-BNm69Qh8.js";
13
13
  import { formatPulledEnv, pulledBranch } from "./env_output.js";
14
14
  import { existsSync } from "node:fs";
15
15
  import chalk from "chalk";
@@ -21,6 +21,7 @@ import { t as databases_exports } from "./databases.js";
21
21
  import { t as deploy_exports } from "./deploy.js";
22
22
  import { t as dev_exports } from "./dev.js";
23
23
  import { t as diff_exports } from "./diff.js";
24
+ import { t as docs_exports } from "./docs.js";
24
25
  import { t as feedback_exports } from "./feedback.js";
25
26
  import { t as functions_exports } from "./functions.js";
26
27
  import { t as git_exports } from "./git.js";
@@ -75,6 +76,7 @@ var commands_default = [
75
76
  skills_exports,
76
77
  ask_exports,
77
78
  feedback_exports,
79
+ docs_exports,
78
80
  data_api_exports,
79
81
  realtime_exports,
80
82
  functions_exports,
@@ -181,6 +181,11 @@ const commandManifest = [
181
181
  describe: "Send feedback to Neon",
182
182
  load: () => import("./feedback.js")
183
183
  },
184
+ {
185
+ command: "docs",
186
+ describe: "Search neon.com docs, guides, and more",
187
+ load: () => import("./docs.js")
188
+ },
184
189
  {
185
190
  command: "data-api",
186
191
  describe: "Manage the Neon Data API for a database",
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { t as writeSecretFile } from "../_chunks/secure_file-BucZj4yQ.js";
3
- import { c as describeScope, d as scopeOf, l as interpretCredentials, n as KEYRING_SERVICE, o as apiKeyCredentials, r as keyringAccount, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-ByNuojoE.js";
3
+ import { c as describeScope, d as scopeOf, l as interpretCredentials, n as KEYRING_SERVICE, o as apiKeyCredentials, r as keyringAccount, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-BS-0Pmwc.js";
4
4
  import { isCi } from "../env.js";
5
5
  import { log } from "../log.js";
6
6
  import { getApiClient, isNeonApiError } from "../api.js";
@@ -1,6 +1,6 @@
1
1
  import { writer } from "../writer.js";
2
- import { o as resolveNeonEnvVars } from "../_chunks/env-BNm69Qh8.js";
3
2
  import { branchIdFromProps } from "../utils/enrichers.js";
3
+ import { o as resolveNeonEnvVars } from "../_chunks/env-BNm69Qh8.js";
4
4
  import YAML from "yaml";
5
5
  //#region src/commands/subscribe.ts
6
6
  const builder = (argv) => argv.usage("$0 realtime subscribe \"<query>\" [options]").positional("query", {
package/dist/context.js CHANGED
@@ -90,6 +90,8 @@ const isPluginsCommand = (args) => args._[0] === "plugins" || args._[0] === "plu
90
90
  const isAskCommand = (args) => args._[0] === "ask";
91
91
  /** Feedback is anonymous, so it must never read credentials or `.neon` context. */
92
92
  const isFeedbackCommand = (args) => args._[0] === "feedback";
93
+ /** Docs search calls public neon.com search, so it needs no credentials or `.neon` context. */
94
+ const isDocsCommand = (args) => args._[0] === "docs";
93
95
  /**
94
96
  * `inspect db --db-url` talks to Postgres with that URI. Skip only when the
95
97
  * parsed value is nonempty: empty `--db-url` still goes through project
@@ -177,7 +179,7 @@ const enrichFromContext = (args) => {
177
179
  if (isProfileCommand(args)) return;
178
180
  if (isMcpCommand(args)) return;
179
181
  if (isSkillsCommand(args) || isPluginsCommand(args)) return;
180
- if (isAskCommand(args) || isFeedbackCommand(args)) return;
182
+ if (isAskCommand(args) || isFeedbackCommand(args) || isDocsCommand(args)) return;
181
183
  if (isClaimCommand(args)) return;
182
184
  const context = readContextFile(args.contextFile);
183
185
  if (!args.orgId) args.orgId = context.orgId;
@@ -357,4 +359,4 @@ const globToRegExp = (pattern) => {
357
359
  return new RegExp(`^${source}$`);
358
360
  };
359
361
  //#endregion
360
- export { CONTEXT_FILE, applyContext, contextBranch, currentContextFile, enrichFromContext, ensureDirectoryGitignored, ensureGitignored, gitBranchMap, gitBranchMapping, isApiKeysCommand, isAskCommand, isClaimCommand, isConfigAdd, isConfigInit, isCurrentBranchProbe, isFeedbackCommand, isGitLocalCommand, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, isUnfollowedGitHookSync, readContextFile, setContext, setGitBranchMap, setGitBranchMapping, setGitFollow, updateContextFile, walkContextFile };
362
+ export { CONTEXT_FILE, applyContext, contextBranch, currentContextFile, enrichFromContext, ensureDirectoryGitignored, ensureGitignored, gitBranchMap, gitBranchMapping, isApiKeysCommand, isAskCommand, isClaimCommand, isConfigAdd, isConfigInit, isCurrentBranchProbe, isDocsCommand, isFeedbackCommand, isGitLocalCommand, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, isUnfollowedGitHookSync, readContextFile, setContext, setGitBranchMap, setGitBranchMapping, setGitFollow, updateContextFile, walkContextFile };
@@ -1 +1 @@
1
- export { t as storeFor } from "./_chunks/credential_io-ByNuojoE.js";
1
+ export { t as storeFor } from "./_chunks/credential_io-BS-0Pmwc.js";
@@ -1,4 +1,4 @@
1
- import { t as storeFor } from "../_chunks/credential_io-ByNuojoE.js";
1
+ import { t as storeFor } from "../_chunks/credential_io-BS-0Pmwc.js";
2
2
  import { isCi } from "../env.js";
3
3
  import { h as resolveProfile, u as locationOf } from "../_chunks/profiles-DIB1av2Q.js";
4
4
  import { i as selectCredential, t as credentialInputs } from "../_chunks/auth_selection-YX5_KZgD.js";
package/dist/keyring.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  //#region src/keyring.ts
3
+ /** Windows Credential Manager stores at most 2560 bytes per item: 1280 UTF-16 code units. */
4
+ const keyringPasswordLimit = (platform) => platform === "win32" ? 1280 : void 0;
3
5
  const isPackaged = () => process.pkg !== void 0;
4
6
  const isMissingItem = (err) => {
5
7
  const message = err instanceof Error ? err.message : String(err);
@@ -13,7 +15,9 @@ const tryLoadKeyring = () => {
13
15
  if (isPackaged()) return null;
14
16
  try {
15
17
  const { Entry } = createRequire(import.meta.url)(["@napi-rs", "keyring"].join("/"));
18
+ const maxPasswordCodeUnits = keyringPasswordLimit(process.platform);
16
19
  return {
20
+ ...maxPasswordCodeUnits !== void 0 ? { maxPasswordCodeUnits } : {},
17
21
  get(service, account) {
18
22
  try {
19
23
  return new Entry(service, account).getPassword();
@@ -39,4 +43,4 @@ const tryLoadKeyring = () => {
39
43
  }
40
44
  };
41
45
  //#endregion
42
- export { tryLoadKeyring };
46
+ export { keyringPasswordLimit, tryLoadKeyring };
@@ -1,4 +1,4 @@
1
- import { a as OAUTH, d as scopeOf, i as API_KEY, l as interpretCredentials, t as storeFor, u as isSameCredential } from "./_chunks/credential_io-ByNuojoE.js";
1
+ import { a as OAUTH, d as scopeOf, i as API_KEY, l as interpretCredentials, t as storeFor, u as isSameCredential } from "./_chunks/credential_io-BS-0Pmwc.js";
2
2
  import { log } from "./log.js";
3
3
  import { getApiClient } from "./api.js";
4
4
  import { revokeToken } from "./auth.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon",
3
- "version": "8.9.0",
3
+ "version": "8.10.1",
4
4
  "description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
5
5
  "keywords": [
6
6
  "neon",
@@ -71,9 +71,9 @@
71
71
  "yargs": "17.7.2",
72
72
  "yoctocolors": "^2.1.2",
73
73
  "@neon/config": "1.9.3",
74
- "@neon/config-runtime": "1.10.2",
75
74
  "@neon/realtime": "0.1.2",
76
- "@neon/sdk": "7.4.0"
75
+ "@neon/sdk": "7.4.0",
76
+ "@neon/config-runtime": "1.10.2"
77
77
  },
78
78
  "optionalDependencies": {
79
79
  "@napi-rs/keyring": "1.3.0",