neon 8.12.1 → 8.13.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.
@@ -11,9 +11,9 @@ import { looksLikeBranchId } from "../utils/formats.js";
11
11
  import { fillSingleProject, listAllProjectBranches } from "../utils/enrichers.js";
12
12
  import { buildCheckoutEvent, readGitContext } from "../utils/git.js";
13
13
  import { buildHookBranch, loadHookConfig, resolveHookEnv, runCheckoutAfterHook, runCheckoutBeforeHook, runCreateAfterHook, runCreateBeforeHook } from "../utils/hooks.js";
14
- import { n as autoPullEnvAfterPin } from "./env-5HS5qQGd.js";
15
- import { a as applyPolicyOnCreate, f as envFlag, l as createBranchFromPolicyOnCheckout } from "./config-BH_qRHiG.js";
16
- import { i as handler$1 } from "./link-BgtkOBws.js";
14
+ import { n as autoPullEnvAfterPin } from "./env-uVRY4xah.js";
15
+ import { a as applyPolicyOnCreate, f as envFlag, l as createBranchFromPolicyOnCheckout } from "./config-BnwcGpbq.js";
16
+ import { i as handler$1 } from "./link-DOM84hYj.js";
17
17
  import chalk from "chalk";
18
18
  import prompts from "prompts";
19
19
  //#region src/commands/checkout.ts
@@ -12,7 +12,7 @@ import { projectConfigPath } from "../project.js";
12
12
  import { resolveEnvFilePath } from "../env_file.js";
13
13
  import { writer } from "../writer.js";
14
14
  import { noPassthrough } from "../utils/flags.js";
15
- import { c as pull } from "./env-5HS5qQGd.js";
15
+ import { c as pull } from "./env-uVRY4xah.js";
16
16
  import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
17
17
  import open from "open";
18
18
  import prompts from "prompts";
@@ -23,7 +23,7 @@ import { readGitContext } from "../utils/git.js";
23
23
  import { resolveHookEnv, runDeployAfterHook, runDeployBeforeHook } from "../utils/hooks.js";
24
24
  import { pickServicesInteractively } from "../utils/service_picker.js";
25
25
  import { zipBundle } from "../utils/zip.js";
26
- import { n as autoPullEnvAfterPin } from "./env-5HS5qQGd.js";
26
+ import { n as autoPullEnvAfterPin } from "./env-uVRY4xah.js";
27
27
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
28
28
  import { dirname, join, relative, resolve } from "node:path";
29
29
  import chalk from "chalk";
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
2
  import { fillSingleProject } from "../utils/enrichers.js";
3
- import { f as envFlag, i as applyFlags, p as envPullFlag, r as applyCmd } from "./config-BH_qRHiG.js";
3
+ import { f as envFlag, i as applyFlags, p as envPullFlag, r as applyCmd } from "./config-BnwcGpbq.js";
4
4
  import { DEPLOY_COMMANDS_EPILOGUE } from "../commands/deploy_help.js";
5
5
  //#region src/commands/deploy.ts
6
6
  var deploy_exports = /* @__PURE__ */ __exportAll({
@@ -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 "./env-BpP3Dqpe.js";
7
- import { branchIdResolve } from "../utils/enrichers.js";
8
- import { bundleEntry } from "../utils/esbuild.js";
9
7
  import { a as resolveDevEnv } from "./env-BNm69Qh8.js";
10
- import { f as envFlag } from "./config-BH_qRHiG.js";
11
8
  import { findConfigFunctionBySource, resolveFunctionsFromConfig } from "../dev/functions.js";
12
9
  import { resolveWatchInputs } from "../dev/inputs.js";
10
+ import { branchIdResolve } from "../utils/enrichers.js";
11
+ import { bundleEntry } from "../utils/esbuild.js";
12
+ import { f as envFlag } from "./config-BnwcGpbq.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";
@@ -5,8 +5,8 @@ import { CONTEXT_FILE, contextBranch, gitBranchMap, gitBranchMapping, isUnfollow
5
5
  import { writer } from "../writer.js";
6
6
  import { neonSafeBranchName } from "../utils/branch_name.js";
7
7
  import { currentGitBranch, gitPull, gitRepoRoot, hasUpstream, installPostCheckoutHook, isGitRepo, isManagedHook, localGitBranches, postCheckoutHookPath, readGitContext, removePostCheckoutHook } from "../utils/git.js";
8
- import { o as quoteFlagValue } from "./link-BgtkOBws.js";
9
- import { o as handler$1 } from "./checkout-CXIOydqZ.js";
8
+ import { o as quoteFlagValue } from "./link-DOM84hYj.js";
9
+ import { o as handler$1 } from "./checkout-BGvlLc43.js";
10
10
  import { existsSync } from "node:fs";
11
11
  import { join } from "node:path";
12
12
  import chalk from "chalk";
@@ -13,8 +13,8 @@ import { writer } from "../writer.js";
13
13
  import { createBranch, pickBranchInteractively } from "../utils/branch_picker.js";
14
14
  import { looksLikeBranchId } from "../utils/formats.js";
15
15
  import { listAllProjectBranches } from "../utils/enrichers.js";
16
- import { n as autoPullEnvAfterPin } from "./env-5HS5qQGd.js";
17
- import { h as initCmd } from "./config-BH_qRHiG.js";
16
+ import { n as autoPullEnvAfterPin } from "./env-uVRY4xah.js";
17
+ import { h as initCmd } from "./config-BnwcGpbq.js";
18
18
  import { formatLinkSummary } from "../commands/link_output.js";
19
19
  import { r as REGIONS } from "./projects-BwI2Uo0z.js";
20
20
  import prompts from "prompts";
@@ -1,4 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
+ import { log } from "../log.js";
2
3
  import { codeFromBody, isNeonApiError, retryOnLock } from "../api.js";
3
4
  import { stripAnsi } from "../human_table.js";
4
5
  import { writer } from "../writer.js";
@@ -78,6 +79,24 @@ const ORGANIZATION_FIELDS = [
78
79
  "organization_limit",
79
80
  "creator_role"
80
81
  ];
82
+ const OAUTH_SERVER_FIELDS = [
83
+ "enabled",
84
+ "login_page",
85
+ "consent_page",
86
+ "mcp",
87
+ "resources"
88
+ ];
89
+ const OAUTH_CLIENT_FIELDS = [
90
+ "client_id",
91
+ "name",
92
+ "type",
93
+ "redirect_uris",
94
+ "resources",
95
+ "skip_consent",
96
+ "disabled",
97
+ "created_at"
98
+ ];
99
+ const OAUTH_CLIENT_SECRET_FIELDS = ["client_id", "client_secret"];
81
100
  const WEBHOOK_FIELDS = [
82
101
  "enabled",
83
102
  "webhook_url",
@@ -110,7 +129,7 @@ const builder = (argv) => {
110
129
  default: false
111
130
  } }), async (args) => {
112
131
  await disable(args);
113
- }).command("oauth-provider", "Manage OAuth providers", (yargs) => {
132
+ }).command("oauth-provider", `Manage social login providers (${SUPPORTED_OAUTH_PROVIDERS.join(", ")})`, (yargs) => {
114
133
  return yargs.usage("$0 neon-auth oauth-provider <sub-command> [options]").command("list", "List OAuth providers", (yargs) => yargs, async (args) => {
115
134
  await oauthProviderList(args);
116
135
  }).command("add", "Add an OAuth provider", (yargs) => yargs.options({
@@ -155,6 +174,87 @@ const builder = (argv) => {
155
174
  } }), async (args) => {
156
175
  await oauthProviderDelete(args);
157
176
  });
177
+ }).command("oauth-client", "Manage clients of this Neon Auth OAuth server (e.g. MCP clients); configure the server with `config oauth-server`", (yargs) => {
178
+ return yargs.usage("$0 neon-auth oauth-client <sub-command> [options]").command("list", "List OAuth clients", (yargs) => yargs, async (args) => {
179
+ await oauthClientList(args);
180
+ }).command("create", "Create an OAuth client", (yargs) => yargs.options({
181
+ name: {
182
+ describe: "Display name of the client",
183
+ type: "string",
184
+ demandOption: true
185
+ },
186
+ type: {
187
+ describe: "Client type. Confidential clients receive a secret that is shown only once",
188
+ type: "string",
189
+ choices: ["confidential", "public"],
190
+ demandOption: true
191
+ },
192
+ "redirect-uris": {
193
+ describe: "Redirect URIs the client may use (https, or http on loopback when localhost is allowed)",
194
+ type: "string",
195
+ array: true,
196
+ demandOption: true
197
+ },
198
+ resources: {
199
+ describe: "Protected resource URLs to link the client to; each must be configured with `neon-auth config oauth-server resource add`",
200
+ type: "string",
201
+ array: true,
202
+ demandOption: true
203
+ },
204
+ "skip-consent": {
205
+ describe: "Skip the consent prompt (first-party clients only)",
206
+ type: "boolean"
207
+ }
208
+ }).example("$0 neon-auth oauth-client create --name 'My app' --type confidential --redirect-uris https://app.example.com/callback --resources https://api.example.com", "Create a confidential client"), async (args) => {
209
+ await oauthClientCreate(args);
210
+ }).command("get <client-id>", "Get an OAuth client", (yargs) => yargs.usage("$0 neon-auth oauth-client get <client-id> [options]").positional("client-id", {
211
+ describe: "OAuth client ID",
212
+ type: "string",
213
+ demandOption: true
214
+ }), async (args) => {
215
+ await oauthClientGet(args);
216
+ }).command("update <client-id>", "Update an OAuth client (the type cannot be changed)", (yargs) => yargs.usage("$0 neon-auth oauth-client update <client-id> [options]").positional("client-id", {
217
+ describe: "OAuth client ID",
218
+ type: "string",
219
+ demandOption: true
220
+ }).options({
221
+ name: {
222
+ describe: "Display name of the client",
223
+ type: "string"
224
+ },
225
+ "redirect-uris": {
226
+ describe: "Redirect URIs the client may use; replaces the stored list",
227
+ type: "string",
228
+ array: true
229
+ },
230
+ resources: {
231
+ describe: "Protected resource URLs to link the client to; replaces the stored list (at least one)",
232
+ type: "string",
233
+ array: true
234
+ },
235
+ "skip-consent": {
236
+ describe: "Skip the consent prompt (first-party clients only). Use --no-skip-consent or --skip-consent false to turn off",
237
+ type: "boolean"
238
+ },
239
+ disabled: {
240
+ describe: "Disable the client. Use --no-disabled or --disabled false to re-enable",
241
+ type: "boolean"
242
+ }
243
+ }), async (args) => {
244
+ await oauthClientUpdate(args);
245
+ }).command("delete <client-id>", "Delete an OAuth client", (yargs) => yargs.usage("$0 neon-auth oauth-client delete <client-id> [options]").positional("client-id", {
246
+ describe: "OAuth client ID",
247
+ type: "string",
248
+ demandOption: true
249
+ }), async (args) => {
250
+ await oauthClientDelete(args);
251
+ }).command("rotate-secret <client-id>", "Rotate the secret of a confidential client; the old secret stops working immediately", (yargs) => yargs.usage("$0 neon-auth oauth-client rotate-secret <client-id> [options]").positional("client-id", {
252
+ describe: "OAuth client ID",
253
+ type: "string",
254
+ demandOption: true
255
+ }), async (args) => {
256
+ await oauthClientRotateSecret(args);
257
+ });
158
258
  }).command("domain", "Manage redirect URI trusted domains", (yargs) => {
159
259
  return yargs.usage("$0 neon-auth domain <sub-command> [options]").command("list", "List trusted domains", (yargs) => yargs, async (args) => {
160
260
  await domainList(args);
@@ -283,6 +383,63 @@ const builder = (argv) => {
283
383
  }), async (args) => {
284
384
  await organizationUpdate(args);
285
385
  });
386
+ }).command("oauth-server", "Manage the OAuth 2.1 authorization server (API plugin: oauth_provider)", (yargs) => {
387
+ return yargs.usage("$0 neon-auth config oauth-server <sub-command> [options]").command("get", "Get OAuth server config", (yargs) => yargs, async (args) => {
388
+ await oauthServerGet(args);
389
+ }).command("update", "Update OAuth server config", (yargs) => yargs.options({
390
+ enabled: {
391
+ describe: "Enable the OAuth server. Use --no-enabled or --enabled false to disable",
392
+ type: "boolean"
393
+ },
394
+ "login-page": {
395
+ describe: "URL of the page authorization requests redirect to for sign-in",
396
+ type: "string"
397
+ },
398
+ "consent-page": {
399
+ describe: "URL of the page authorization requests redirect to for consent",
400
+ type: "string"
401
+ },
402
+ resources: {
403
+ describe: "Protected resource URLs that clients can be linked to; replaces the stored list. Use `resource add|remove` for incremental changes",
404
+ type: "string",
405
+ array: true
406
+ },
407
+ "mcp-resource": {
408
+ describe: "MCP server URL that MCP clients are linked to. Defaults to the stored value when another --mcp-* flag is given. To remove the MCP configuration, use --no-mcp",
409
+ type: "string"
410
+ },
411
+ mcp: {
412
+ describe: "Remove the MCP configuration (only --no-mcp is accepted)",
413
+ type: "boolean"
414
+ },
415
+ "mcp-client-id-metadata-documents": {
416
+ describe: "Allow MCP clients to identify themselves with a Client ID Metadata Document (CIMD) URL. Use --no-mcp-client-id-metadata-documents to disallow",
417
+ type: "boolean"
418
+ },
419
+ "mcp-dynamic-registration": {
420
+ describe: "Allow MCP clients to register themselves via dynamic client registration (deprecated in favor of CIMD)",
421
+ type: "boolean",
422
+ hidden: true
423
+ }
424
+ }).example("$0 neon-auth config oauth-server update --enabled --mcp-resource https://example.com/mcp --mcp-client-id-metadata-documents", "Enable the OAuth server for an MCP server"), async (args) => {
425
+ await oauthServerUpdate(args);
426
+ }).command("resource", "Manage the protected resources of the OAuth server. Add and remove read the stored list and write back the full list, so concurrent changes by others can be overwritten", (yargs) => yargs.usage("$0 neon-auth config oauth-server resource <sub-command> [options]").command("list", "List protected resources", (yargs) => yargs, async (args) => {
427
+ await oauthResourceList(args);
428
+ }).command("add <url...>", "Add protected resources (reads the stored list and writes back the full list; concurrent changes by others can be overwritten)", (yargs) => yargs.positional("url", {
429
+ describe: "Protected resource URLs to add",
430
+ type: "string",
431
+ array: true,
432
+ demandOption: true
433
+ }), async (args) => {
434
+ await oauthResourceAdd(args);
435
+ }).command("remove <url...>", "Remove protected resources (reads the stored list and writes back the full list; concurrent changes by others can be overwritten)", (yargs) => yargs.positional("url", {
436
+ describe: "Protected resource URLs to remove",
437
+ type: "string",
438
+ array: true,
439
+ demandOption: true
440
+ }), async (args) => {
441
+ await oauthResourceRemove(args);
442
+ }));
286
443
  }).command("webhook", "Manage webhook configuration", (yargs) => {
287
444
  return yargs.usage("$0 neon-auth config webhook <sub-command> [options]").command("get", "Get webhook config", (yargs) => yargs, async (args) => {
288
445
  await webhookGet(args);
@@ -319,7 +476,7 @@ const builder = (argv) => {
319
476
  return yargs.usage("$0 neon-auth plugins <sub-command> [options]").command("list", "List all plugin configurations", (yargs) => yargs, async (args) => {
320
477
  await pluginsList(args);
321
478
  }).command("get <plugin-name>", "Get a specific plugin configuration", (yargs) => yargs.usage("$0 neon-auth plugins get <plugin-name> [options]").positional("plugin-name", {
322
- describe: "Plugin name (e.g. organization, email_provider, email_and_password, oauth_providers, allow_localhost)",
479
+ describe: "Plugin name (e.g. organization, email_provider, email_and_password, oauth_providers, oauth_provider, allow_localhost)",
323
480
  type: "string",
324
481
  demandOption: true
325
482
  }), async (args) => {
@@ -714,6 +871,287 @@ const organizationUpdate = async (props) => {
714
871
  }
715
872
  printKvBlock("Organization configuration updated", printOrganizationEntries(data));
716
873
  };
874
+ const optionalBool = (v) => v === void 0 ? void 0 : String(v);
875
+ const printOAuthServerEntries = (data) => [
876
+ ["Enabled:", String(data.enabled)],
877
+ ["Login Page:", data.login_page],
878
+ ["Consent Page:", data.consent_page],
879
+ ["MCP Resource:", data.mcp?.resource],
880
+ ["MCP CIMD:", optionalBool(data.mcp?.client_id_metadata_documents)],
881
+ ["MCP Dynamic Reg:", optionalBool(data.mcp?.dynamic_registration)],
882
+ ["Resources:", (data.resources ?? []).map((r) => r.identifier).join(", ")]
883
+ ];
884
+ /** The API rejects an empty PATCH, so fail before any request with the flags that would do something. */
885
+ const requireUpdateFields = (provided, flags) => {
886
+ if (provided.every((value) => value === void 0)) throw new Error(`Nothing to update. Pass at least one of ${flags.join(", ")}.`);
887
+ };
888
+ const oauthServerGet = async (props) => {
889
+ const branchId = await resolveBranch(props);
890
+ const { data } = await props.apiClient.getNeonAuthOAuthProviderPlugin(props.projectId, branchId);
891
+ if (props.output === "json" || props.output === "yaml") {
892
+ writer(props).end(data, { fields: OAUTH_SERVER_FIELDS });
893
+ return;
894
+ }
895
+ printKvBlock("OAuth server configuration", printOAuthServerEntries(data));
896
+ };
897
+ /**
898
+ * The API requires `mcp.resource` whenever `mcp` is sent and keeps stored booleans that are
899
+ * omitted, so only the resource is filled in from the stored config.
900
+ */
901
+ const resolveMcpUpdate = async (props, getStoredMcp) => {
902
+ const { mcpResource, mcpDynamicRegistration: dynamicRegistration, mcpClientIdMetadataDocuments: clientIdMetadataDocuments } = props;
903
+ if (mcpResource === void 0 && dynamicRegistration === void 0 && clientIdMetadataDocuments === void 0) return;
904
+ const resource = mcpResource ?? (await getStoredMcp())?.resource;
905
+ if (!resource) throw new Error("No MCP resource is configured yet. Pass --mcp-resource <url> together with the other --mcp-* flags.");
906
+ return {
907
+ resource,
908
+ dynamic_registration: dynamicRegistration,
909
+ client_id_metadata_documents: clientIdMetadataDocuments
910
+ };
911
+ };
912
+ /**
913
+ * Order-preserving dedupe after trimming: padded input would otherwise miss stored values
914
+ * and reach the API as a distinct string, and repeats would be sent as duplicates.
915
+ */
916
+ const unique = (values) => [...new Set(values.map((value) => value.trim()))];
917
+ /** yargs negation turns `--no-<flag>` on an array option into `[false]`, which bypasses empty-array checks. */
918
+ const isNegatedList = (values) => values === false || Array.isArray(values) && values.includes(false);
919
+ const hasBlank = (values) => values?.some((value) => value.trim() === "") ?? false;
920
+ /** Fails before any request when a list flag was passed with no values or a blank one. */
921
+ const requireNonEmptyLists = (lists) => {
922
+ for (const [flag, values] of lists) {
923
+ if (isNegatedList(values)) throw new Error(`--no-${flag.slice(2)} is not supported. ${flag} must list at least one value.`);
924
+ if (values?.length === 0) throw new Error(`${flag} must list at least one value.`);
925
+ if (hasBlank(values)) throw new Error(`${flag} must not contain empty values.`);
926
+ }
927
+ };
928
+ /**
929
+ * yargs negation turns `--no-<flag>` on a string option into `false`, and `--flag ""` yields an
930
+ * empty string; both would otherwise crash on `.trim()` or be sent to the API.
931
+ */
932
+ const requireNonBlankString = (flag, value, hint) => {
933
+ if (value === false) throw new Error(`--no-${flag.slice(2)} is not supported. ${hint}`);
934
+ if (typeof value === "string" && value.trim() === "") throw new Error(`${flag} must not be empty.`);
935
+ };
936
+ const requireNonBlankUrls = (urls) => {
937
+ if (hasBlank(urls)) throw new Error("URLs must not be empty.");
938
+ };
939
+ /** The MCP resource is linked to MCP clients on its own, so it cannot also be a protected resource. */
940
+ const rejectMcpResourceCollision = (urls, mcpResource) => {
941
+ if (mcpResource && urls.includes(mcpResource)) throw new Error(`${mcpResource} is the MCP resource and cannot also be a protected resource.`);
942
+ };
943
+ /** Checked before any request so a malformed invocation never reaches the API. */
944
+ const validateOAuthServerUpdate = (props) => {
945
+ requireNonBlankString("--login-page", props.loginPage, "Pass --login-page <url>.");
946
+ requireNonBlankString("--consent-page", props.consentPage, "Pass --consent-page <url>.");
947
+ if (props.mcp === true) throw new Error("--mcp cannot be enabled. Configure MCP with --mcp-resource <url>, or remove it with --no-mcp.");
948
+ if (props.mcp === false && (props.mcpResource !== void 0 || props.mcpClientIdMetadataDocuments !== void 0 || props.mcpDynamicRegistration !== void 0)) throw new Error("--no-mcp cannot be combined with --mcp-resource, --mcp-client-id-metadata-documents or --mcp-dynamic-registration.");
949
+ if (props.mcpResource === false) throw new Error("--no-mcp-resource is not supported. To remove the MCP configuration, use --no-mcp.");
950
+ if (props.mcpResource?.trim() === "") throw new Error("--mcp-resource must not be empty. To remove the MCP configuration, use --no-mcp.");
951
+ if (isNegatedList(props.resources)) throw new Error("--no-resources is not supported. To remove resources, use `neon-auth config oauth-server resource remove <url...>`.");
952
+ if (props.resources?.length === 0) throw new Error("--resources must list at least one URL. To remove resources, use `neon-auth config oauth-server resource remove <url...>`.");
953
+ if (hasBlank(props.resources)) throw new Error("--resources must not contain empty values.");
954
+ };
955
+ const oauthServerUpdate = async (props) => {
956
+ requireUpdateFields([
957
+ props.enabled,
958
+ props.loginPage,
959
+ props.consentPage,
960
+ props.resources,
961
+ props.mcp,
962
+ props.mcpResource,
963
+ props.mcpClientIdMetadataDocuments,
964
+ props.mcpDynamicRegistration
965
+ ], [
966
+ "--enabled",
967
+ "--login-page",
968
+ "--consent-page",
969
+ "--resources",
970
+ "--no-mcp",
971
+ "--mcp-resource",
972
+ "--mcp-client-id-metadata-documents"
973
+ ]);
974
+ validateOAuthServerUpdate(props);
975
+ const loginPage = props.loginPage?.trim();
976
+ const consentPage = props.consentPage?.trim();
977
+ const mcpResource = props.mcpResource?.trim();
978
+ const branchId = await resolveBranch(props);
979
+ let storedMcp;
980
+ const getStoredMcp = () => storedMcp ??= props.apiClient.getNeonAuthOAuthProviderPlugin(props.projectId, branchId).then(({ data }) => data.mcp);
981
+ const mcp = props.mcp === false ? { resource: "" } : await resolveMcpUpdate({
982
+ ...props,
983
+ mcpResource
984
+ }, getStoredMcp);
985
+ const resources = props.resources && unique(props.resources);
986
+ if (resources && props.mcp !== false) rejectMcpResourceCollision(resources, mcp?.resource ?? (await getStoredMcp())?.resource);
987
+ if (props.mcpDynamicRegistration !== void 0) log.warning("--mcp-dynamic-registration is deprecated. Use --mcp-client-id-metadata-documents instead.");
988
+ const { data } = await props.apiClient.updateNeonAuthOAuthProviderPlugin(props.projectId, branchId, {
989
+ enabled: props.enabled,
990
+ login_page: loginPage,
991
+ consent_page: consentPage,
992
+ mcp,
993
+ resources: resources?.map((identifier) => ({ identifier }))
994
+ });
995
+ if (props.output === "json" || props.output === "yaml") {
996
+ writer(props).end(data, { fields: OAUTH_SERVER_FIELDS });
997
+ return;
998
+ }
999
+ printKvBlock("OAuth server configuration updated", printOAuthServerEntries(data));
1000
+ };
1001
+ const getStoredOAuthServer = async (props) => {
1002
+ const branchId = await resolveBranch(props);
1003
+ const { data } = await props.apiClient.getNeonAuthOAuthProviderPlugin(props.projectId, branchId);
1004
+ return {
1005
+ branchId,
1006
+ mcpResource: data.mcp?.resource,
1007
+ stored: (data.resources ?? []).map((r) => r.identifier)
1008
+ };
1009
+ };
1010
+ const reportOAuthResources = (props, resources) => {
1011
+ if (resources.length === 0 && props.output === "table") {
1012
+ printMessage("No protected resources are configured");
1013
+ return;
1014
+ }
1015
+ writer(props).end(resources, { fields: ["identifier"] });
1016
+ };
1017
+ /** The API replaces the whole list, so every change is a full-list PATCH. */
1018
+ const replaceOAuthResources = async (props, branchId, identifiers) => {
1019
+ const { data } = await props.apiClient.updateNeonAuthOAuthProviderPlugin(props.projectId, branchId, { resources: identifiers.map((identifier) => ({ identifier })) });
1020
+ reportOAuthResources(props, data.resources ?? []);
1021
+ };
1022
+ const oauthResourceList = async (props) => {
1023
+ const { stored } = await getStoredOAuthServer(props);
1024
+ reportOAuthResources(props, stored.map((identifier) => ({ identifier })));
1025
+ };
1026
+ const oauthResourceAdd = async (props) => {
1027
+ requireNonBlankUrls(props.url);
1028
+ const urls = unique(props.url);
1029
+ const { branchId, mcpResource, stored } = await getStoredOAuthServer(props);
1030
+ rejectMcpResourceCollision(urls, mcpResource);
1031
+ const existing = urls.filter((url) => stored.includes(url));
1032
+ if (existing.length > 0) log.warning(`Already configured, skipped: ${existing.join(", ")}`);
1033
+ const added = urls.filter((url) => !stored.includes(url));
1034
+ if (added.length === 0) {
1035
+ reportOAuthResources(props, stored.map((identifier) => ({ identifier })));
1036
+ return;
1037
+ }
1038
+ await replaceOAuthResources(props, branchId, [...stored, ...added]);
1039
+ };
1040
+ const oauthResourceRemove = async (props) => {
1041
+ requireNonBlankUrls(props.url);
1042
+ const requested = unique(props.url);
1043
+ const { branchId, stored } = await getStoredOAuthServer(props);
1044
+ const unknown = requested.filter((url) => !stored.includes(url));
1045
+ if (unknown.length > 0) log.warning(`Not configured, skipped: ${unknown.join(", ")}`);
1046
+ const urls = requested.filter((url) => stored.includes(url));
1047
+ if (urls.length === 0) {
1048
+ reportOAuthResources(props, stored.map((identifier) => ({ identifier })));
1049
+ return;
1050
+ }
1051
+ await replaceOAuthResources(props, branchId, stored.filter((url) => !urls.includes(url)));
1052
+ let linked;
1053
+ try {
1054
+ const { data } = await props.apiClient.listNeonAuthOAuthClients(props.projectId, branchId);
1055
+ linked = data.clients.filter((client) => client.resources.some((resource) => urls.includes(resource)));
1056
+ } catch (err) {
1057
+ log.debug("Could not look up clients linked to the removed resource: %s", err);
1058
+ log.warning("Removed, but could not check for clients still linked to it.");
1059
+ return;
1060
+ }
1061
+ if (linked.length > 0) log.warning(`Clients still linked to a removed resource: ${linked.map((client) => `"${client.name}" (${client.client_id})`).join(", ")}. Their token requests for that resource may fail until it is added again.`);
1062
+ };
1063
+ const printOAuthClientEntries = (data) => [
1064
+ ["Client ID:", data.client_id],
1065
+ ["Name:", data.name],
1066
+ ["Type:", data.type],
1067
+ ["Redirect URIs:", data.redirect_uris.join(", ")],
1068
+ ["Resources:", data.resources.join(", ")],
1069
+ ["Skip Consent:", String(data.skip_consent)],
1070
+ ["Disabled:", String(data.disabled)],
1071
+ ["Created At:", data.created_at]
1072
+ ];
1073
+ const oauthClientList = async (props) => {
1074
+ const branchId = await resolveBranch(props);
1075
+ const { data } = await props.apiClient.listNeonAuthOAuthClients(props.projectId, branchId);
1076
+ if (data.clients.length === 0 && props.output === "table") {
1077
+ printMessage("No OAuth clients are configured for this branch.");
1078
+ return;
1079
+ }
1080
+ writer(props).end(data.clients, { fields: OAUTH_CLIENT_FIELDS });
1081
+ };
1082
+ /**
1083
+ * The secret is shown only once, so human output keeps it on its own last line of stdout
1084
+ * and the notice goes to stderr. Structured output keeps it in the object for scripts.
1085
+ */
1086
+ const reportOAuthClientSecret = (props, data, fields, entries, title) => {
1087
+ if (props.output === "table") {
1088
+ printKvBlock(title, entries);
1089
+ if (data.client_secret) writeStyled(`${data.client_secret}\n`);
1090
+ } else writer(props).end(data, { fields });
1091
+ if (data.client_secret) log.warning("Store this secret now: it is not shown again.");
1092
+ };
1093
+ const oauthClientCreate = async (props) => {
1094
+ requireNonBlankString("--name", props.name, "Pass --name <name>.");
1095
+ requireNonEmptyLists([["--redirect-uris", props.redirectUris], ["--resources", props.resources]]);
1096
+ const branchId = await resolveBranch(props);
1097
+ const { data } = await props.apiClient.createNeonAuthOAuthClient(props.projectId, branchId, {
1098
+ name: props.name.trim(),
1099
+ type: props.type,
1100
+ redirect_uris: unique(props.redirectUris),
1101
+ resources: unique(props.resources),
1102
+ skip_consent: props.skipConsent
1103
+ });
1104
+ reportOAuthClientSecret(props, data, [...OAUTH_CLIENT_FIELDS, "client_secret"], printOAuthClientEntries(data), "OAuth client");
1105
+ };
1106
+ const oauthClientGet = async (props) => {
1107
+ const branchId = await resolveBranch(props);
1108
+ const { data } = await props.apiClient.getNeonAuthOAuthClient(props.projectId, branchId, props.clientId);
1109
+ if (props.output === "json" || props.output === "yaml") {
1110
+ writer(props).end(data, { fields: OAUTH_CLIENT_FIELDS });
1111
+ return;
1112
+ }
1113
+ printKvBlock("OAuth client", printOAuthClientEntries(data));
1114
+ };
1115
+ const oauthClientUpdate = async (props) => {
1116
+ requireNonBlankString("--name", props.name, "Pass --name <name>.");
1117
+ requireNonEmptyLists([["--redirect-uris", props.redirectUris], ["--resources", props.resources]]);
1118
+ requireUpdateFields([
1119
+ props.name,
1120
+ props.redirectUris,
1121
+ props.resources,
1122
+ props.skipConsent,
1123
+ props.disabled
1124
+ ], [
1125
+ "--name",
1126
+ "--redirect-uris",
1127
+ "--resources",
1128
+ "--skip-consent",
1129
+ "--disabled"
1130
+ ]);
1131
+ const branchId = await resolveBranch(props);
1132
+ const { data } = await props.apiClient.updateNeonAuthOAuthClient(props.projectId, branchId, props.clientId, {
1133
+ name: props.name?.trim(),
1134
+ redirect_uris: props.redirectUris && unique(props.redirectUris),
1135
+ resources: props.resources && unique(props.resources),
1136
+ skip_consent: props.skipConsent,
1137
+ disabled: props.disabled
1138
+ });
1139
+ if (props.output === "json" || props.output === "yaml") {
1140
+ writer(props).end(data, { fields: OAUTH_CLIENT_FIELDS });
1141
+ return;
1142
+ }
1143
+ printKvBlock("OAuth client updated", printOAuthClientEntries(data));
1144
+ };
1145
+ const oauthClientDelete = async (props) => {
1146
+ const branchId = await resolveBranch(props);
1147
+ await props.apiClient.deleteNeonAuthOAuthClient(props.projectId, branchId, props.clientId);
1148
+ printMessage(`OAuth client "${props.clientId}" deleted`);
1149
+ };
1150
+ const oauthClientRotateSecret = async (props) => {
1151
+ const branchId = await resolveBranch(props);
1152
+ const { data } = await props.apiClient.rotateNeonAuthOAuthClientSecret(props.projectId, branchId, props.clientId);
1153
+ reportOAuthClientSecret(props, data, OAUTH_CLIENT_SECRET_FIELDS, [["Client ID:", data.client_id]], "OAuth client secret");
1154
+ };
717
1155
  const printWebhookEntries = (data) => [
718
1156
  ["Enabled:", String(data.enabled)],
719
1157
  ["URL:", data.webhook_url ?? ""],
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
2
  import { fillSingleProject } from "../utils/enrichers.js";
3
- import { y as status } from "./config-BH_qRHiG.js";
3
+ import { y as status } from "./config-BnwcGpbq.js";
4
4
  //#region src/commands/status.ts
5
5
  var status_exports = /* @__PURE__ */ __exportAll({
6
6
  builder: () => builder,
package/dist/api.js CHANGED
@@ -895,6 +895,69 @@ const getApiClient = ({ apiKey, apiHost, requestTimeoutMs = REQUEST_TIMEOUT_MS }
895
895
  },
896
896
  body: data
897
897
  })),
898
+ getNeonAuthOAuthProviderPlugin: (projectId, branchId) => call(() => raw.getNeonAuthOAuthProviderPlugin({
899
+ client,
900
+ path: {
901
+ project_id: projectId,
902
+ branch_id: branchId
903
+ }
904
+ })),
905
+ updateNeonAuthOAuthProviderPlugin: (projectId, branchId, data) => call(() => raw.updateNeonAuthOAuthProviderPlugin({
906
+ client,
907
+ path: {
908
+ project_id: projectId,
909
+ branch_id: branchId
910
+ },
911
+ body: data
912
+ })),
913
+ listNeonAuthOAuthClients: (projectId, branchId) => call(() => raw.listNeonAuthOAuthClients({
914
+ client,
915
+ path: {
916
+ project_id: projectId,
917
+ branch_id: branchId
918
+ }
919
+ })),
920
+ createNeonAuthOAuthClient: (projectId, branchId, data) => call(() => raw.createNeonAuthOAuthClient({
921
+ client,
922
+ path: {
923
+ project_id: projectId,
924
+ branch_id: branchId
925
+ },
926
+ body: data
927
+ })),
928
+ getNeonAuthOAuthClient: (projectId, branchId, clientId) => call(() => raw.getNeonAuthOAuthClient({
929
+ client,
930
+ path: {
931
+ project_id: projectId,
932
+ branch_id: branchId,
933
+ client_id: clientId
934
+ }
935
+ })),
936
+ updateNeonAuthOAuthClient: (projectId, branchId, clientId, data) => call(() => raw.updateNeonAuthOAuthClient({
937
+ client,
938
+ path: {
939
+ project_id: projectId,
940
+ branch_id: branchId,
941
+ client_id: clientId
942
+ },
943
+ body: data
944
+ })),
945
+ deleteNeonAuthOAuthClient: (projectId, branchId, clientId) => call(() => raw.deleteNeonAuthOAuthClient({
946
+ client,
947
+ path: {
948
+ project_id: projectId,
949
+ branch_id: branchId,
950
+ client_id: clientId
951
+ }
952
+ })),
953
+ rotateNeonAuthOAuthClientSecret: (projectId, branchId, clientId) => call(() => raw.rotateNeonAuthOAuthClientSecret({
954
+ client,
955
+ path: {
956
+ project_id: projectId,
957
+ branch_id: branchId,
958
+ client_id: clientId
959
+ }
960
+ })),
898
961
  getNeonAuthWebhookConfig: (projectId, branchId) => call(() => raw.getNeonAuthWebhookConfig({
899
962
  client,
900
963
  path: {
@@ -1 +1 @@
1
- export { t as builder, r as command, i as describe, a as formatCheckoutPolicyFailure, o as handler } from "../_chunks/checkout-CXIOydqZ.js";
1
+ export { t as builder, r as command, i as describe, a as formatCheckoutPolicyFailure, o as handler } from "../_chunks/checkout-BGvlLc43.js";
@@ -1 +1 @@
1
- export { t as CLAIM_CREATE_FIELDS, n as CLAIM_LIST_FIELDS, r as aliases, i as builder, a as claimCreateFields, s as claimableCapabilities, c as command, l as create, u as describe, d as handler } from "../_chunks/claim-DqAvAvDm.js";
1
+ export { t as CLAIM_CREATE_FIELDS, n as CLAIM_LIST_FIELDS, r as aliases, i as builder, a as claimCreateFields, s as claimableCapabilities, c as command, l as create, u as describe, d as handler } from "../_chunks/claim-DqzLXZMN.js";
@@ -1 +1 @@
1
- export { t as ConfigInstallFailed, n as addCmd, r as applyCmd, i as applyFlags, a as applyPolicyOnCreate, o as builder, s as command, l as createBranchFromPolicyOnCheckout, u as describe, d as ensureConfigPackages, f as envFlag, p as envPullFlag, m as handler, h as initCmd, g as installCommandFrom, _ as missingDependencies, v as planCmd, y as status } from "../_chunks/config-BH_qRHiG.js";
1
+ export { t as ConfigInstallFailed, n as addCmd, r as applyCmd, i as applyFlags, a as applyPolicyOnCreate, o as builder, s as command, l as createBranchFromPolicyOnCheckout, u as describe, d as ensureConfigPackages, f as envFlag, p as envPullFlag, m as handler, h as initCmd, g as installCommandFrom, _ as missingDependencies, v as planCmd, y as status } from "../_chunks/config-BnwcGpbq.js";
@@ -1 +1 @@
1
- export { t as builder, n as command, i as describe, a as handler } from "../_chunks/deploy-D7B0Xfjg.js";
1
+ export { t as builder, n as command, i as describe, a as handler } from "../_chunks/deploy-BCVcDR1O.js";
@@ -1 +1 @@
1
- export { t as builder, n as captureShellEnv, r as command, i as describe, a as devBundleDir, o as devEnvContext, c as diffUnits, l as formatEnvFiles, u as formatEnvSummary, d as handler, f as loadDevEnvFiles, p as localFunctionUrlEnv, m as planFunctionsToUnits, h as reportDevCredential, g as sourceFunctionUrlOverlay } from "../_chunks/dev-CSvxAecE.js";
1
+ export { t as builder, n as captureShellEnv, r as command, i as describe, a as devBundleDir, o as devEnvContext, c as diffUnits, l as formatEnvFiles, u as formatEnvSummary, d as handler, f as loadDevEnvFiles, p as localFunctionUrlEnv, m as planFunctionsToUnits, h as reportDevCredential, g as sourceFunctionUrlOverlay } from "../_chunks/dev-mrJKMt2R.js";
@@ -1 +1 @@
1
- export { t as ENV_PULL_SKIPPED_HINT, n as autoPullEnvAfterPin, r as builder, i as command, a as describe, s as handler, c as pull } from "../_chunks/env-5HS5qQGd.js";
1
+ export { t as ENV_PULL_SKIPPED_HINT, n as autoPullEnvAfterPin, r as builder, i as command, a as describe, s as handler, c as pull } from "../_chunks/env-uVRY4xah.js";
@@ -8,7 +8,7 @@ import { writer } from "../writer.js";
8
8
  import { formatInitDone, printInitDone } from "../init/chrome.js";
9
9
  import { FUNCTION_SLUG_PATTERN, deriveFunctionSlug } from "../utils/function_slug.js";
10
10
  import { displayPath } from "./env_output.js";
11
- import { _ as missingDependencies, d as ensureConfigPackages, g as installCommandFrom } from "../_chunks/config-BH_qRHiG.js";
11
+ import { _ as missingDependencies, d as ensureConfigPackages, g as installCommandFrom } from "../_chunks/config-BnwcGpbq.js";
12
12
  import { recommendedOperations } from "../functions/registry.js";
13
13
  import { collidingFiles, operationsFromFlags, planConfigRegistration, planEnvExample, planScaffold, writeScaffold } from "../functions/scaffold.js";
14
14
  import { describeRegistrySource, openRegistry } from "../functions/source.js";
@@ -1 +1 @@
1
- export { t as aliases, n as builder, r as cleanup, i as command, a as describe, s as handler, c as install, l as partitionBranchesToPrune, u as status, d as sync, f as uninstall } from "../_chunks/git-H32TNt6M.js";
1
+ export { t as aliases, n as builder, r as cleanup, i as command, a as describe, s as handler, c as install, l as partitionBranchesToPrune, u as status, d as sync, f as uninstall } from "../_chunks/git-pN4kMH0d.js";
@@ -2,34 +2,34 @@ import { r as auth_exports } from "../_chunks/auth-CIUtXByB.js";
2
2
  import { t as api_exports } from "../_chunks/api-GbEaBZJ5.js";
3
3
  import { n as api_keys_exports } from "../_chunks/api_keys-CNnnugbQ.js";
4
4
  import { n as ask_exports } from "../_chunks/ask-DVwy0eUL.js";
5
- import { o as env_exports } from "../_chunks/env-5HS5qQGd.js";
6
- import { c as config_exports } from "../_chunks/config-BH_qRHiG.js";
5
+ import { o as env_exports } from "../_chunks/env-uVRY4xah.js";
6
+ import { c as config_exports } from "../_chunks/config-BnwcGpbq.js";
7
7
  import { u as projects_exports } from "../_chunks/projects-BwI2Uo0z.js";
8
- import { a as link_exports } from "../_chunks/link-BgtkOBws.js";
8
+ import { a as link_exports } from "../_chunks/link-DOM84hYj.js";
9
9
  import { o as mcp_exports } from "../_chunks/mcp-Bo-7TKc7.js";
10
10
  import { c as plugins_exports } from "../_chunks/plugins-DqD7BDO1.js";
11
11
  import { l as skills_exports } from "../_chunks/skills-BP6pIm7O.js";
12
12
  import { t as bootstrap_exports } from "../_chunks/bootstrap-wPZWa1sT.js";
13
13
  import { r as branches_exports } from "../_chunks/branches-BNa1xX1L.js";
14
14
  import { n as bucket_exports } from "../_chunks/bucket-BCdryTkD.js";
15
- import { n as checkout_exports } from "../_chunks/checkout-CXIOydqZ.js";
16
- import { o as claim_exports } from "../_chunks/claim-DqAvAvDm.js";
15
+ import { n as checkout_exports } from "../_chunks/checkout-BGvlLc43.js";
16
+ import { o as claim_exports } from "../_chunks/claim-DqzLXZMN.js";
17
17
  import { a as connection_string_exports } from "../_chunks/connection_string-DB4awPzZ.js";
18
18
  import { a as credentials_exports } from "../_chunks/credentials-BSjE9fRR.js";
19
19
  import { r as data_api_exports } from "../_chunks/data_api-Djxj5ZQD.js";
20
20
  import { o as databases_exports } from "../_chunks/databases-Dok4wru7.js";
21
- import { r as deploy_exports } from "../_chunks/deploy-D7B0Xfjg.js";
22
- import { s as dev_exports } from "../_chunks/dev-CSvxAecE.js";
21
+ import { r as deploy_exports } from "../_chunks/deploy-BCVcDR1O.js";
22
+ import { s as dev_exports } from "../_chunks/dev-mrJKMt2R.js";
23
23
  import { i as diff_exports } from "../_chunks/diff-DhxxbdQE.js";
24
24
  import { c as docs_exports } from "../_chunks/docs-CrVybkTP.js";
25
25
  import { o as feedback_exports } from "../_chunks/feedback-CMayyTa8.js";
26
26
  import { o as functions_exports } from "../_chunks/functions-Q0zuvtnk.js";
27
- import { o as git_exports } from "../_chunks/git-H32TNt6M.js";
27
+ import { o as git_exports } from "../_chunks/git-pN4kMH0d.js";
28
28
  import { a as init_exports } from "../_chunks/init-BkA6RilG.js";
29
29
  import { o as inspect_exports } from "../_chunks/inspect-g0wekylj.js";
30
30
  import { a as ip_allow_exports } from "../_chunks/ip_allow-5lLFvIuE.js";
31
31
  import { l as logs_exports } from "../_chunks/logs-DWkpjzIH.js";
32
- import { a as neon_auth_exports } from "../_chunks/neon_auth-CBs1_3qu.js";
32
+ import { a as neon_auth_exports } from "../_chunks/neon_auth-DpF83S24.js";
33
33
  import { a as open_exports } from "../_chunks/open-CX3BT3Tp.js";
34
34
  import { s as operations_exports } from "../_chunks/operations-D4I-AxVs.js";
35
35
  import { o as orgs_exports } from "../_chunks/orgs-xGfANwHQ.js";
@@ -39,7 +39,7 @@ import { s as realtime_exports } from "../_chunks/realtime-BRqIyGR-.js";
39
39
  import { l as roles_exports } from "../_chunks/roles-B4w3YT_7.js";
40
40
  import { a as set_context_exports } from "../_chunks/set_context-DbrwoImy.js";
41
41
  import { s as snapshots_exports } from "../_chunks/snapshots-RJBzW8GM.js";
42
- import { a as status_exports } from "../_chunks/status-DV8Z2kw3.js";
42
+ import { a as status_exports } from "../_chunks/status-DoNjIbKf.js";
43
43
  import { d as triggers_exports } from "../_chunks/triggers-BrRo1z4b.js";
44
44
  import { s as user_exports } from "../_chunks/user-dlOGXbX9.js";
45
45
  import { i as vpc_endpoints_exports } from "../_chunks/vpc_endpoints-CNb0YfMo.js";
@@ -1 +1 @@
1
- export { t as builder, n as command, r as describe, i as handler, o as quoteFlagValue, s as runLink, c as shouldOfferConfigInit } from "../_chunks/link-BgtkOBws.js";
1
+ export { t as builder, n as command, r as describe, i as handler, o as quoteFlagValue, s as runLink, c as shouldOfferConfigInit } from "../_chunks/link-DOM84hYj.js";
@@ -1 +1 @@
1
- export { t as builder, n as command, r as describe, i as handler } from "../_chunks/neon_auth-CBs1_3qu.js";
1
+ export { t as builder, n as command, r as describe, i as handler } from "../_chunks/neon_auth-DpF83S24.js";
@@ -1 +1 @@
1
- export { t as builder, n as command, r as describe, i as handler } from "../_chunks/status-DV8Z2kw3.js";
1
+ export { t as builder, n as command, r as describe, i as handler } from "../_chunks/status-DoNjIbKf.js";
@@ -1,6 +1,6 @@
1
1
  import { writer } from "../writer.js";
2
- import { branchIdFromProps } from "../utils/enrichers.js";
3
2
  import { o as resolveNeonEnvVars } from "../_chunks/env-BNm69Qh8.js";
3
+ import { branchIdFromProps } from "../utils/enrichers.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/init/auth.js CHANGED
@@ -4,7 +4,7 @@ import "../config.js";
4
4
  import "../auth.js";
5
5
  import { c as ensureAuth } from "../_chunks/auth-CIUtXByB.js";
6
6
  import { recoverFrom401 } from "../auth_recovery.js";
7
- import { c as pull } from "../_chunks/env-5HS5qQGd.js";
7
+ import { c as pull } from "../_chunks/env-uVRY4xah.js";
8
8
  import { c as setupNeonMcp } from "../_chunks/mcp-Bo-7TKc7.js";
9
9
  //#region src/init/auth.ts
10
10
  const isUnauthorized = (error) => isNeonApiError(error) && error.status === 401;
package/dist/init/link.js CHANGED
@@ -4,7 +4,7 @@ import "../config.js";
4
4
  import "../auth.js";
5
5
  import { c as ensureAuth } from "../_chunks/auth-CIUtXByB.js";
6
6
  import { recoverFrom401 } from "../auth_recovery.js";
7
- import { s as runLink } from "../_chunks/link-BgtkOBws.js";
7
+ import { s as runLink } from "../_chunks/link-DOM84hYj.js";
8
8
  //#region src/init/link.ts
9
9
  const isUnauthorized = (error) => isNeonApiError(error) && error.status === 401;
10
10
  const authenticationFailed = () => /* @__PURE__ */ new Error("Authentication failed.");
package/dist/init/run.js CHANGED
@@ -17,15 +17,15 @@ import { FALLBACK_SKILLS_AGENTS, assertNamedAgentTooling, funnelAgentSetup, plan
17
17
  import { inferPackageManager, resolvePackageManager } from "../utils/package_manager.js";
18
18
  import { CANCELLED_BODY, CLAIMABLE_ACCOUNT_FLAGS, CLAIMABLE_ALREADY_LINKED, CLAIMABLE_MCP_API_KEY, CLAIMABLE_NO_LINK, MCP_API_KEY_NEEDS_AUTH, MCP_SCOPED_NEEDS_PROJECT, NON_TTY_LINK_NEEDS_AUTH, NO_AGENTS_FALLBACK_STATUS, PROGRESS, PROJECT_PINNED_OFFLINE, claimableNext, claimableThenServicesNext, configInitNext, envPullFailedNext, existingConfigNext, installFailedNext, mcpAfterLinkNext, mcpConfigLocationSkipped, mcpConfigLocationUnavailable, mcpKeyFailedNext, namedAgentsUnavailable, newConfigNext, offlinePinNext, skippedLinkNext, unattendedUnauthedNext } from "./copy.js";
19
19
  import { agentsRowValue, configRowValue, formatInitDone, headingForKind, printInitBanner, printInitDone, printInitProgress, projectRowValue, shouldPrintInitBanner } from "./chrome.js";
20
- import { h as initCmd, t as ConfigInstallFailed } from "../_chunks/config-BH_qRHiG.js";
21
- import { o as quoteFlagValue, s as runLink } from "../_chunks/link-BgtkOBws.js";
20
+ import { h as initCmd, t as ConfigInstallFailed } from "../_chunks/config-BnwcGpbq.js";
21
+ import { o as quoteFlagValue, s as runLink } from "../_chunks/link-DOM84hYj.js";
22
22
  import { runAuthenticatedLink } from "./link.js";
23
23
  import { CannotMintApiKeyError } from "../mcp/mint.js";
24
24
  import { pullInitEnv } from "./auth.js";
25
25
  import { InitCancelled, raceSigint, restoreCursor } from "./cancelled.js";
26
26
  import { pickAgentSetupInteractively, pickInitAgentsInteractively, pickInitConfigInteractively, pickInitMcpAuthInteractively, pickInitMcpConfigLocationInteractively, pickInitModeInteractively, pickInitPackageManagerInteractively, pickInitProjectSetupInteractively, pickInitServicesInteractively, pickInitSkillsInteractively } from "./wizard.js";
27
27
  import { runToolingSteps } from "./tooling.js";
28
- import { l as create } from "../_chunks/claim-DqAvAvDm.js";
28
+ import { l as create } from "../_chunks/claim-DqzLXZMN.js";
29
29
  import { INIT_CONFIG_SERVICES_CONFLICT, configPlanFromResolution, initEnvPullAfterConfig, resolveInitConfigChoice } from "./choices.js";
30
30
  import { detectInitEnvironment } from "./detect.js";
31
31
  import { createInitFunnel, emitInitStart, finishInitFunnel, initStartProperties } from "./funnel.js";
@@ -63,8 +63,8 @@ const quoteNpxArg = (part) => /[\s"'\\*]/.test(part) ? `'${part.replace(/'/g, `'
63
63
  const npxCommand = (args) => `npx ${args.map(quoteNpxArg).join(" ")}`;
64
64
  const ANSI = /\u001b\[[0-9;]*[A-Za-z]/g;
65
65
  const UPDATE_BANNER = /^Checking for skill updates/i;
66
- const UPDATE_NOTHING = /No (?:project|global) skills (?:to update|to check|tracked in lock file|can be updated in place)|All global skills are up to date/i;
67
- const UPDATE_RESULT = /No (?:project|global) skills (?:to update|to check|tracked in lock file|can be updated in place)\.?|All global skills are up to date|(?:✓\s*)?Updated \d+ skills?(?:\(s\))?|(?:✗\s*)?Failed to update \d+ skills?(?:\(s\))?/i;
66
+ const UPDATE_NOTHING = /No (?:project|global) skills (?:to update|to check|tracked in lock file|can be updated in place)|All (?:project|global) skills are up to date/i;
67
+ const UPDATE_RESULT = /No (?:project|global) skills (?:to update|to check|tracked in lock file|can be updated in place)\.?|All (?:project|global) skills are up to date|(?:✓\s*)?Updated \d+ skills?(?:\(s\))?|(?:✗\s*)?Failed to update \d+ skills?(?:\(s\))?/i;
68
68
  const stripAnsi = (text) => text.replace(ANSI, "").replace(/\r/g, "");
69
69
  const skillsUpdateHadNothing = (output) => UPDATE_NOTHING.test(stripAnsi(output));
70
70
  const skillsUpdateDetail = (output) => {
@@ -48,14 +48,15 @@ const test = test$1.extend({
48
48
  let startedServer;
49
49
  await use(async (mockDir, onRequest) => {
50
50
  const app = express();
51
+ app.use(express.json());
51
52
  if (onRequest) app.use((req, _res, next) => {
52
53
  onRequest({
53
54
  method: req.method,
54
- path: req.path
55
+ path: req.path,
56
+ body: req.body
55
57
  });
56
58
  next();
57
59
  });
58
- app.use(express.json());
59
60
  app.use("/", emocks(join(process.cwd(), "mocks", mockDir), { "404": (_req, res) => res.status(404).send({ message: "Not Found" }) }));
60
61
  const server = await new Promise((resolve) => {
61
62
  const s = app.listen(0, () => {
@@ -76,7 +77,7 @@ const test = test$1.extend({
76
77
  },
77
78
  testCliCommand: async ({ runMockServer }, use) => {
78
79
  await use(async (args, options = {}) => {
79
- const apiHost = options.unreachableHost ? await reserveClosedPort() : `http://localhost:${(await runMockServer(options.mockDir || "main")).address().port}`;
80
+ const apiHost = options.unreachableHost ? await reserveClosedPort() : `http://localhost:${(await runMockServer(options.mockDir || "main", options.onRequest)).address().port}`;
80
81
  let output = "";
81
82
  let error = "";
82
83
  const cp = fork(join(process.cwd(), "./dist/index.js"), [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon",
3
- "version": "8.12.1",
3
+ "version": "8.13.0",
4
4
  "description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
5
5
  "keywords": [
6
6
  "neon",
@@ -72,10 +72,10 @@
72
72
  "yaml": "^2.9.0",
73
73
  "yargs": "17.7.2",
74
74
  "yoctocolors": "^2.1.2",
75
- "@neon/config": "1.9.4",
76
- "@neon/config-runtime": "1.10.3",
75
+ "@neon/config-runtime": "1.10.4",
77
76
  "@neon/realtime": "0.1.2",
78
- "@neon/sdk": "7.5.0"
77
+ "@neon/config": "1.9.5",
78
+ "@neon/sdk": "7.6.0"
79
79
  },
80
80
  "optionalDependencies": {
81
81
  "@napi-rs/keyring": "1.3.0",
@@ -112,9 +112,9 @@
112
112
  "typescript": "^5.9.0",
113
113
  "vitest": "^3.0.9",
114
114
  "@neon-internals/cli-core": "0.0.0",
115
- "@neon-internals/env-core": "0.0.35",
115
+ "@neon-internals/env-core": "0.0.36",
116
116
  "@neon/e2e-harness": "0.0.0",
117
- "@neon/env": "1.6.5",
117
+ "@neon/env": "1.6.6",
118
118
  "@neon/functions": "0.11.0"
119
119
  },
120
120
  "publishConfig": {
@@ -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 "./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 "./env-BNm69Qh8.js";
10
11
  import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
11
12
  import { warnAiGateway } from "../utils/ai_gateway_notice.js";
12
- import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "./env-BNm69Qh8.js";
13
13
  import { formatPulledEnv, pulledBranch } from "../commands/env_output.js";
14
14
  import { existsSync } from "node:fs";
15
15
  import chalk from "chalk";